gioui.org/ui@v0.0.0-20190926171558-ce74bc0cbaea/app/xkb_linux.go (about) 1 // SPDX-License-Identifier: Unlicense OR MIT 2 3 // +build !android 4 5 package app 6 7 /* 8 #cgo LDFLAGS: -lxkbcommon 9 10 #include <stdlib.h> 11 #include <xkbcommon/xkbcommon.h> 12 #include <xkbcommon/xkbcommon-compose.h> 13 */ 14 import "C" 15 16 import ( 17 "errors" 18 "fmt" 19 "os" 20 "syscall" 21 "unicode" 22 "unicode/utf8" 23 "unsafe" 24 25 "gioui.org/ui/key" 26 ) 27 28 type xkb struct { 29 ctx *C.struct_xkb_context 30 keyMap *C.struct_xkb_keymap 31 state *C.struct_xkb_state 32 compTable *C.struct_xkb_compose_table 33 compState *C.struct_xkb_compose_state 34 utf8Buf []byte 35 } 36 37 var ( 38 _XKB_MOD_NAME_CTRL = []byte("Control\x00") 39 _XKB_MOD_NAME_SHIFT = []byte("Shift\x00") 40 ) 41 42 func (x *xkb) Destroy() { 43 if x.state != nil { 44 C.xkb_compose_state_unref(x.compState) 45 x.compState = nil 46 } 47 if x.compTable != nil { 48 C.xkb_compose_table_unref(x.compTable) 49 x.compTable = nil 50 } 51 if x.state != nil { 52 C.xkb_state_unref(x.state) 53 x.state = nil 54 } 55 if x.keyMap != nil { 56 C.xkb_keymap_unref(x.keyMap) 57 x.keyMap = nil 58 } 59 if x.ctx != nil { 60 C.xkb_context_unref(x.ctx) 61 x.ctx = nil 62 } 63 } 64 65 func newXKB(format C.uint32_t, fd C.int32_t, size C.uint32_t) (*xkb, error) { 66 xkb := &xkb{ 67 ctx: C.xkb_context_new(C.XKB_CONTEXT_NO_FLAGS), 68 } 69 if xkb.ctx == nil { 70 return nil, errors.New("newXKB: xkb_context_new failed") 71 } 72 locale := os.Getenv("LC_ALL") 73 if locale == "" { 74 locale = os.Getenv("LC_CTYPE") 75 } 76 if locale == "" { 77 locale = os.Getenv("LANG") 78 } 79 if locale == "" { 80 locale = "C" 81 } 82 cloc := C.CString(locale) 83 defer C.free(unsafe.Pointer(cloc)) 84 xkb.compTable = C.xkb_compose_table_new_from_locale(xkb.ctx, cloc, C.XKB_COMPOSE_COMPILE_NO_FLAGS) 85 if xkb.compTable == nil { 86 xkb.Destroy() 87 return nil, errors.New("newXKB: xkb_compose_table_new_from_locale failed") 88 } 89 xkb.compState = C.xkb_compose_state_new(xkb.compTable, C.XKB_COMPOSE_STATE_NO_FLAGS) 90 if xkb.compState == nil { 91 xkb.Destroy() 92 return nil, errors.New("newXKB: xkb_compose_state_new failed") 93 } 94 mapData, err := syscall.Mmap(int(fd), 0, int(size), syscall.PROT_READ, syscall.MAP_SHARED) 95 if err != nil { 96 xkb.Destroy() 97 return nil, fmt.Errorf("newXKB: mmap of keymap failed: %v", err) 98 } 99 defer syscall.Munmap(mapData) 100 xkb.keyMap = C.xkb_keymap_new_from_buffer(xkb.ctx, (*C.char)(unsafe.Pointer(&mapData[0])), C.size_t(size-1), C.XKB_KEYMAP_FORMAT_TEXT_V1, C.XKB_KEYMAP_COMPILE_NO_FLAGS) 101 if xkb.keyMap == nil { 102 xkb.Destroy() 103 return nil, errors.New("newXKB: xkb_keymap_new_from_buffer failed") 104 } 105 xkb.state = C.xkb_state_new(xkb.keyMap) 106 if xkb.state == nil { 107 xkb.Destroy() 108 return nil, errors.New("newXKB: xkb_state_new failed") 109 } 110 return xkb, nil 111 } 112 113 func (x *xkb) dispatchKey(w *Window, keyCode C.uint32_t) { 114 keyCode = mapXKBKeyCode(keyCode) 115 if len(x.utf8Buf) == 0 { 116 x.utf8Buf = make([]byte, 1) 117 } 118 sym := C.xkb_state_key_get_one_sym(x.state, C.xkb_keycode_t(keyCode)) 119 if n, ok := convertKeysym(sym); ok { 120 cmd := key.Event{Name: n} 121 if C.xkb_state_mod_name_is_active(x.state, (*C.char)(unsafe.Pointer(&_XKB_MOD_NAME_CTRL[0])), C.XKB_STATE_MODS_EFFECTIVE) == 1 { 122 cmd.Modifiers |= key.ModCommand 123 } 124 if C.xkb_state_mod_name_is_active(x.state, (*C.char)(unsafe.Pointer(&_XKB_MOD_NAME_SHIFT[0])), C.XKB_STATE_MODS_EFFECTIVE) == 1 { 125 cmd.Modifiers |= key.ModShift 126 } 127 w.event(cmd) 128 } 129 C.xkb_compose_state_feed(x.compState, sym) 130 var size C.int 131 switch C.xkb_compose_state_get_status(x.compState) { 132 case C.XKB_COMPOSE_CANCELLED, C.XKB_COMPOSE_COMPOSING: 133 return 134 case C.XKB_COMPOSE_COMPOSED: 135 size = C.xkb_compose_state_get_utf8(x.compState, (*C.char)(unsafe.Pointer(&x.utf8Buf[0])), C.size_t(len(x.utf8Buf))) 136 if int(size) >= len(x.utf8Buf) { 137 x.utf8Buf = make([]byte, size+1) 138 size = C.xkb_compose_state_get_utf8(x.compState, (*C.char)(unsafe.Pointer(&x.utf8Buf[0])), C.size_t(len(x.utf8Buf))) 139 } 140 C.xkb_compose_state_reset(x.compState) 141 case C.XKB_COMPOSE_NOTHING: 142 size = C.xkb_state_key_get_utf8(x.state, C.xkb_keycode_t(keyCode), (*C.char)(unsafe.Pointer(&x.utf8Buf[0])), C.size_t(len(x.utf8Buf))) 143 if int(size) >= len(x.utf8Buf) { 144 x.utf8Buf = make([]byte, size+1) 145 size = C.xkb_state_key_get_utf8(x.state, C.xkb_keycode_t(keyCode), (*C.char)(unsafe.Pointer(&x.utf8Buf[0])), C.size_t(len(x.utf8Buf))) 146 } 147 } 148 // Report only printable runes. 149 str := x.utf8Buf[:size] 150 var n int 151 for n < len(str) { 152 r, s := utf8.DecodeRune(str) 153 if unicode.IsPrint(r) { 154 n += s 155 } else { 156 copy(str[n:], str[n+s:]) 157 str = str[:len(str)-s] 158 } 159 } 160 if len(str) > 0 { 161 w.event(key.EditEvent{Text: string(str)}) 162 } 163 } 164 165 func (x *xkb) isRepeatKey(keyCode C.uint32_t) bool { 166 keyCode = mapXKBKeyCode(keyCode) 167 return C.xkb_keymap_key_repeats(conn.xkb.keyMap, C.xkb_keycode_t(keyCode)) == 1 168 } 169 170 func (x *xkb) updateMask(depressed, latched, locked, group C.uint32_t) { 171 xkbGrp := C.xkb_layout_index_t(group) 172 C.xkb_state_update_mask(conn.xkb.state, C.xkb_mod_mask_t(depressed), C.xkb_mod_mask_t(latched), C.xkb_mod_mask_t(locked), xkbGrp, xkbGrp, xkbGrp) 173 } 174 175 func mapXKBKeyCode(keyCode C.uint32_t) C.uint32_t { 176 // According to the xkb_v1 spec: "to determine the xkb keycode, clients must add 8 to the key event keycode." 177 return keyCode + 8 178 } 179 180 func convertKeysym(s C.xkb_keysym_t) (rune, bool) { 181 if '0' <= s && s <= '9' || 'A' <= s && s <= 'Z' { 182 return rune(s), true 183 } 184 if 'a' <= s && s <= 'z' { 185 return rune(s - 0x20), true 186 } 187 var n rune 188 switch s { 189 case C.XKB_KEY_Escape: 190 n = key.NameEscape 191 case C.XKB_KEY_Left: 192 n = key.NameLeftArrow 193 case C.XKB_KEY_Right: 194 n = key.NameRightArrow 195 case C.XKB_KEY_Return: 196 n = key.NameReturn 197 case C.XKB_KEY_KP_Enter: 198 n = key.NameEnter 199 case C.XKB_KEY_Up: 200 n = key.NameUpArrow 201 case C.XKB_KEY_Down: 202 n = key.NameDownArrow 203 case C.XKB_KEY_Home: 204 n = key.NameHome 205 case C.XKB_KEY_End: 206 n = key.NameEnd 207 case C.XKB_KEY_BackSpace: 208 n = key.NameDeleteBackward 209 case C.XKB_KEY_Delete: 210 n = key.NameDeleteForward 211 case C.XKB_KEY_Page_Up: 212 n = key.NamePageUp 213 case C.XKB_KEY_Page_Down: 214 n = key.NamePageDown 215 default: 216 return 0, false 217 } 218 return n, true 219 }