github.com/jmigpin/editor@v1.6.0/driver/xdriver/xinput/kmap.go (about) 1 package xinput 2 3 import ( 4 "fmt" 5 "strings" 6 "unicode" 7 8 "github.com/BurntSushi/xgb" 9 "github.com/BurntSushi/xgb/xproto" 10 "github.com/jmigpin/editor/util/uiutil/event" 11 ) 12 13 // $ man keymaps 14 // https://tronche.com/gui/x/xlib/input/XGetKeyboardMapping.html 15 // https://tronche.com/gui/x/xlib/input/keyboard-encoding.html 16 // http://wiki.linuxquestions.org/wiki/List_of_Keysyms_Recognised_by_Xmodmap 17 // https://www.x.org/releases/X11R7.7/doc/libX11/i18n/compose/iso8859-2.html 18 19 // Keyboard mapping 20 type KMap struct { 21 si *xproto.SetupInfo 22 reply *xproto.GetKeyboardMappingReply 23 conn *xgb.Conn 24 } 25 26 func NewKMap(conn *xgb.Conn) (*KMap, error) { 27 km := &KMap{conn: conn} 28 err := km.ReadTable() 29 if err != nil { 30 return nil, err 31 } 32 return km, nil 33 } 34 35 //---------- 36 37 func (km *KMap) ReadTable() error { 38 si := xproto.Setup(km.conn) 39 count := byte(si.MaxKeycode - si.MinKeycode + 1) 40 if count == 0 { 41 return fmt.Errorf("count is 0") 42 } 43 cookie := xproto.GetKeyboardMapping(km.conn, si.MinKeycode, count) 44 reply, err := cookie.Reply() 45 if err != nil { 46 return err 47 } 48 if reply.KeysymsPerKeycode < 2 { 49 return fmt.Errorf("keysyms per keycode < 2") 50 } 51 km.reply = reply 52 km.si = si 53 54 //log.Printf("%v", km.KeysymTable()) 55 56 return nil 57 } 58 59 //---------- 60 61 func (km *KMap) KeysymTable() string { 62 // some symbols are not present, like "~" and "^", and their X11 constant is present instead 63 o := "keysym table\n" 64 table := km.reply.Keysyms 65 width := int(km.reply.KeysymsPerKeycode) 66 for y := 0; y*width < len(table); y++ { 67 var u []string 68 for x := 0; x < width; x++ { 69 xks := table[y*width+x] 70 evs := translateXKeysymToEventKeySym(xks) 71 s2 := fmt.Sprintf("%v", evs) 72 if evs == event.KSymNone { 73 s2 = "-" 74 } 75 u = append(u, fmt.Sprintf("(%c,%v)", rune(xks), s2)) 76 } 77 kc := xproto.Keycode(y) + km.si.MinKeycode 78 o += fmt.Sprintf("kc=%v: %v\n", kc, strings.Join(u, ", ")) 79 } 80 return o 81 } 82 83 //---------- 84 85 func (km *KMap) keysymRow(keycode xproto.Keycode) []xproto.Keysym { 86 y := int(keycode - km.si.MinKeycode) 87 width := int(km.reply.KeysymsPerKeycode) // usually ~7 88 return km.reply.Keysyms[y*width : y*width+width] 89 } 90 91 //---------- 92 93 func (km *KMap) printKeysyms(keycode xproto.Keycode) { 94 keysyms := km.keysymRow(keycode) 95 //fmt.Printf("%v\n", keysyms) 96 97 { 98 u := []string{} 99 for _, ks := range keysyms { 100 u = append(u, string(rune(ks))) 101 } 102 fmt.Printf("[%v]\n", strings.Join(u, " ")) 103 } 104 { 105 u := []string{} 106 for _, ks := range keysyms { 107 u = append(u, fmt.Sprintf("%x", ks)) 108 } 109 fmt.Printf("[%v]\n", strings.Join(u, " ")) 110 } 111 } 112 113 //---------- 114 115 func isKeypad(ks xproto.Keysym) bool { 116 return (0xFF80 <= ks && ks <= 0xFFBD) || 117 (0x11000000 <= ks && ks <= 0x1100FFFF) 118 } 119 120 //---------- 121 122 // xproto.Keycode is a physical key. 123 // xproto.Keysym is the encoding of a symbol on the cap of a key. 124 // A list of keysyms is associated with each keycode. 125 126 func (km *KMap) keysym(krow []xproto.Keysym, m uint16) xproto.Keysym { 127 bitIsSet := func(v uint16) bool { return m&v > 0 } 128 hasShift := bitIsSet(xproto.KeyButMaskShift) 129 hasCaps := bitIsSet(xproto.KeyButMaskLock) 130 hasCtrl := bitIsSet(xproto.KeyButMaskControl) 131 hasNum := bitIsSet(xproto.KeyButMaskMod2) 132 hasAltGr := bitIsSet(xproto.KeyButMaskMod5) 133 134 // keysym group 135 group := 0 136 if hasCtrl { 137 group = 1 138 } else if hasAltGr { 139 group = 2 140 } 141 142 // each group has two symbols 143 i1 := group * 2 144 i2 := i1 + 1 145 if i1 >= len(krow) { 146 return 0 147 } 148 if i2 >= len(krow) { 149 i2 = i1 150 } 151 ks1, ks2 := krow[i1], krow[i2] 152 if ks2 == 0 { 153 ks2 = ks1 154 } 155 156 // keypad 157 if hasNum && isKeypad(ks2) { 158 if hasShift { 159 return ks1 160 } else { 161 return ks2 162 } 163 } 164 165 r1 := rune(ks1) 166 hasLower := unicode.IsLower(unicode.ToLower(r1)) 167 168 if hasLower { 169 shifted := (hasShift && !hasCaps) || (!hasShift && hasCaps) 170 if shifted { 171 return ks2 172 } 173 return ks1 174 } 175 176 if hasShift { 177 return ks2 178 } 179 return ks1 180 } 181 182 //---------- 183 184 func (km *KMap) Lookup(keycode xproto.Keycode, kmods uint16) (event.KeySym, rune) { 185 // keysym 186 ksRow := km.keysymRow(keycode) // keycode to keysyms 187 xks := km.keysym(ksRow, kmods) // keysyms to keysym 188 eks := translateXKeysymToEventKeySym(xks) // keysym to event.keysym 189 // rune 190 ru := keySymsRune(xks, eks) 191 return eks, ru 192 }