github.com/rajveermalviya/gamen@v0.1.2-0.20220930195403-9be15877c1aa/internal/xkbcommon/xkbcommon.go (about) 1 //go:build linux && !android 2 3 package xkbcommon 4 5 import ( 6 "errors" 7 "fmt" 8 "os" 9 "unsafe" 10 ) 11 12 /* 13 14 #include <stdlib.h> 15 16 #include <X11/Xlib-xcb.h> 17 #include <xcb/xkb.h> 18 19 #include <xkbcommon/xkbcommon.h> 20 #include <xkbcommon/xkbcommon-x11.h> 21 #include <xkbcommon/xkbcommon-compose.h> 22 23 */ 24 import "C" 25 26 var locale = func() string { 27 locale := os.Getenv("LC_ALL") 28 if locale == "" { 29 locale = os.Getenv("LC_CTYPE") 30 } 31 if locale == "" { 32 locale = os.Getenv("LANG") 33 } 34 if locale == "" { 35 locale = "C" 36 } 37 return locale 38 }() 39 40 type Xkb struct { 41 l *xkbcommon_library 42 context *C.struct_xkb_context 43 keymap *C.struct_xkb_keymap 44 state *C.struct_xkb_state 45 46 composeTable *C.struct_xkb_compose_table 47 composeState *C.struct_xkb_compose_state 48 } 49 50 func New() (xkb *Xkb, err error) { 51 xkb = &Xkb{} 52 53 xkb.l, err = open_xkbcommon_library() 54 if err != nil { 55 return nil, err 56 } 57 58 xkb.context = xkb.l.xkb_context_new(C.XKB_CONTEXT_NO_FLAGS) 59 if xkb.context == nil { 60 return nil, errors.New("failed to create xkb context") 61 } 62 63 localStr := C.CString(locale) 64 defer C.free(unsafe.Pointer(localStr)) 65 66 xkb.composeTable = xkb.l.xkb_compose_table_new_from_locale(xkb.context, localStr, C.XKB_COMPOSE_COMPILE_NO_FLAGS) 67 if xkb.composeTable == nil { 68 return 69 } 70 71 xkb.composeState = xkb.l.xkb_compose_state_new(xkb.composeTable, C.XKB_COMPOSE_STATE_NO_FLAGS) 72 if xkb.composeState == nil { 73 return 74 } 75 76 return 77 } 78 79 type XcbConnection = C.xcb_connection_t 80 81 func NewFromXcb(conn *XcbConnection) (xkb *Xkb, deviceId int32, firstEvent uint8, err error) { 82 xkb = &Xkb{} 83 84 xkb.l, err = open_xkbcommon_library() 85 if err != nil { 86 return nil, 0, 0, err 87 } 88 89 var firstXkbEvent C.uint8_t 90 ret := xkb.l.xkb_x11_setup_xkb_extension(conn, 91 C.XKB_X11_MIN_MAJOR_XKB_VERSION, 92 C.XKB_X11_MIN_MINOR_XKB_VERSION, 93 C.XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS, 94 nil, nil, &firstXkbEvent, nil) 95 if ret == 0 { 96 return nil, 0, 0, errors.New("failed to setup xkb extension") 97 } 98 firstEvent = uint8(firstXkbEvent) 99 100 deviceId = int32(xkb.l.xkb_x11_get_core_keyboard_device_id((*C.xcb_connection_t)(conn))) 101 if deviceId == -1 { 102 return nil, 0, 0, errors.New("unable to find core keyboard device") 103 } 104 105 xkb.context = xkb.l.xkb_context_new(C.XKB_CONTEXT_NO_FLAGS) 106 if xkb.context == nil { 107 return nil, 0, 0, errors.New("failed to create xkb context") 108 } 109 110 err = xkb.UpdateKeymap(conn, deviceId) 111 if err != nil { 112 xkb.l.xkb_context_unref(xkb.context) 113 return nil, 0, 0, fmt.Errorf("failed to create keymap: %w", err) 114 } 115 116 err = selectXkbEvents(xkb.l, (*C.xcb_connection_t)(conn), deviceId) 117 if err != nil { 118 xkb.l.xkb_context_unref(xkb.context) 119 return nil, 0, 0, fmt.Errorf("failed to select xcb-xkb events: %w", err) 120 } 121 122 localStr := C.CString(locale) 123 defer C.free(unsafe.Pointer(localStr)) 124 125 xkb.composeTable = xkb.l.xkb_compose_table_new_from_locale(xkb.context, localStr, C.XKB_COMPOSE_COMPILE_NO_FLAGS) 126 if xkb.composeTable == nil { 127 return 128 } 129 130 xkb.composeState = xkb.l.xkb_compose_state_new(xkb.composeTable, C.XKB_COMPOSE_STATE_NO_FLAGS) 131 if xkb.composeState == nil { 132 return 133 } 134 135 return 136 } 137 138 func selectXkbEvents(l *xkbcommon_library, conn *C.xcb_connection_t, deviceID int32) error { 139 requiredEvents := C.XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY | 140 C.XCB_XKB_EVENT_TYPE_MAP_NOTIFY | 141 C.XCB_XKB_EVENT_TYPE_STATE_NOTIFY 142 143 requiredNknDetails := C.XCB_XKB_NKN_DETAIL_KEYCODES 144 145 requiredMapParts := C.XCB_XKB_MAP_PART_KEY_TYPES | 146 C.XCB_XKB_MAP_PART_KEY_SYMS | 147 C.XCB_XKB_MAP_PART_MODIFIER_MAP | 148 C.XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS | 149 C.XCB_XKB_MAP_PART_KEY_ACTIONS | 150 C.XCB_XKB_MAP_PART_VIRTUAL_MODS | 151 C.XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP 152 153 requiredStateDetails := C.XCB_XKB_STATE_PART_MODIFIER_BASE | 154 C.XCB_XKB_STATE_PART_MODIFIER_LATCH | 155 C.XCB_XKB_STATE_PART_MODIFIER_LOCK | 156 C.XCB_XKB_STATE_PART_GROUP_BASE | 157 C.XCB_XKB_STATE_PART_GROUP_LATCH | 158 C.XCB_XKB_STATE_PART_GROUP_LOCK 159 160 details := &C.xcb_xkb_select_events_details_t{ 161 affectNewKeyboard: C.uint16_t(requiredNknDetails), 162 newKeyboardDetails: C.uint16_t(requiredNknDetails), 163 affectState: C.uint16_t(requiredStateDetails), 164 stateDetails: C.uint16_t(requiredStateDetails), 165 } 166 167 cookie := l.xcb_xkb_select_events_aux_checked( 168 conn, 169 C.xcb_xkb_device_spec_t(deviceID), 170 C.uint16_t(requiredEvents), 171 0, 172 0, 173 C.uint16_t(requiredMapParts), 174 C.uint16_t(requiredMapParts), 175 details, 176 ) 177 178 err := l.xcb_request_check(conn, cookie) 179 if err != nil { 180 C.free(unsafe.Pointer(err)) 181 return errors.New("unable to bind events") 182 } 183 return nil 184 } 185 186 func (xkb *Xkb) KeymapFromBuffer(buf []byte) error { 187 keymap := xkb.l.xkb_keymap_new_from_buffer( 188 xkb.context, 189 (*C.char)(unsafe.Pointer(&buf[0])), 190 C.size_t(len(buf)-1), 191 C.XKB_KEYMAP_FORMAT_TEXT_V1, 192 C.XKB_KEYMAP_COMPILE_NO_FLAGS, 193 ) 194 if keymap == nil { 195 return errors.New("unable to create keymap from buffer") 196 } 197 198 state := xkb.l.xkb_state_new(keymap) 199 if state == nil { 200 xkb.l.xkb_keymap_unref(keymap) 201 return errors.New("unable to create new state") 202 } 203 204 xkb.keymap = keymap 205 xkb.state = state 206 return nil 207 } 208 209 func (xkb *Xkb) UpdateKeymap(conn *XcbConnection, deviceID int32) error { 210 keymap := xkb.l.xkb_x11_keymap_new_from_device( 211 xkb.context, 212 conn, 213 C.int32_t(deviceID), 214 C.XKB_KEYMAP_COMPILE_NO_FLAGS) 215 if keymap == nil { 216 return errors.New("unable to create keymap from device") 217 } 218 219 state := xkb.l.xkb_x11_state_new_from_device(keymap, conn, C.int32_t(deviceID)) 220 if state == nil { 221 xkb.l.xkb_keymap_unref(keymap) 222 return errors.New("unable to create state from device") 223 } 224 225 xkb.keymap = keymap 226 xkb.state = state 227 return nil 228 } 229 230 type ( 231 KeyCode = C.xkb_keycode_t 232 KeySym = C.xkb_keysym_t 233 ) 234 235 func (xkb *Xkb) KeyRepeats(key KeyCode) bool { 236 if xkb.keymap == nil { 237 return false 238 } 239 return xkb.l.xkb_keymap_key_repeats(xkb.keymap, key) != 0 240 } 241 242 func (xkb *Xkb) GetOneSym(key KeyCode) C.xkb_keysym_t { 243 return xkb.l.xkb_state_key_get_one_sym(xkb.state, key) 244 } 245 246 func (xkb *Xkb) GetUtf8(key KeyCode, sym KeySym) string { 247 if xkb.composeState == nil { 248 size := xkb.l.xkb_state_key_get_utf8(xkb.state, C.xkb_keycode_t(key), nil, 0) + 1 249 if size > 1 { 250 buf := (*C.char)(C.malloc(C.size_t(size))) 251 defer C.free(unsafe.Pointer(buf)) 252 xkb.l.xkb_state_key_get_utf8(xkb.state, C.xkb_keycode_t(key), buf, C.size_t(size)) 253 return C.GoString(buf) 254 } 255 return "" 256 } 257 258 feedResult := xkb.l.xkb_compose_state_feed(xkb.composeState, C.xkb_keysym_t(sym)) 259 if feedResult == C.XKB_COMPOSE_FEED_ACCEPTED { 260 status := xkb.l.xkb_compose_state_get_status(xkb.composeState) 261 switch status { 262 case C.XKB_COMPOSE_COMPOSED: 263 size := xkb.l.xkb_compose_state_get_utf8(xkb.composeState, nil, 0) + 1 264 if size > 1 { 265 buf := (*C.char)(C.malloc(C.size_t(size))) 266 defer C.free(unsafe.Pointer(buf)) 267 xkb.l.xkb_compose_state_get_utf8(xkb.composeState, buf, C.size_t(size)) 268 return C.GoString(buf) 269 } 270 case C.XKB_COMPOSE_NOTHING: 271 size := xkb.l.xkb_state_key_get_utf8(xkb.state, C.xkb_keycode_t(key), nil, 0) + 1 272 if size > 1 { 273 buf := (*C.char)(C.malloc(C.size_t(size))) 274 defer C.free(unsafe.Pointer(buf)) 275 xkb.l.xkb_state_key_get_utf8(xkb.state, C.xkb_keycode_t(key), buf, C.size_t(size)) 276 return C.GoString(buf) 277 } 278 } 279 } 280 281 return "" 282 } 283 284 type ( 285 ModMask = C.xkb_mod_mask_t 286 LayoutIndex = C.xkb_layout_index_t 287 ) 288 289 func (xkb *Xkb) UpdateMask( 290 depressed_mods ModMask, 291 latched_mods ModMask, 292 locked_mods ModMask, 293 depressed_layout LayoutIndex, 294 latched_layout LayoutIndex, 295 locked_layout LayoutIndex, 296 ) bool { 297 return xkb.l.xkb_state_update_mask(xkb.state, 298 depressed_mods, 299 latched_mods, 300 locked_mods, 301 depressed_layout, 302 latched_layout, 303 locked_layout, 304 )&C.XKB_STATE_MODS_EFFECTIVE == 0 305 } 306 307 var ( 308 XKB_MOD_NAME_SHIFT = (*C.char)(unsafe.Pointer(&[]byte("Shift\x00")[0])) 309 XKB_MOD_NAME_CTRL = (*C.char)(unsafe.Pointer(&[]byte("Control\x00")[0])) 310 XKB_MOD_NAME_ALT = (*C.char)(unsafe.Pointer(&[]byte("Mod1\x00")[0])) 311 XKB_MOD_NAME_LOGO = (*C.char)(unsafe.Pointer(&[]byte("Mod4\x00")[0])) 312 ) 313 314 func (xkb *Xkb) ModIsShift() bool { 315 return xkb.l.xkb_state_mod_name_is_active(xkb.state, XKB_MOD_NAME_SHIFT, C.XKB_STATE_MODS_EFFECTIVE) == 1 316 } 317 318 func (xkb *Xkb) ModIsCtrl() bool { 319 return xkb.l.xkb_state_mod_name_is_active(xkb.state, XKB_MOD_NAME_CTRL, C.XKB_STATE_MODS_EFFECTIVE) == 1 320 } 321 322 func (xkb *Xkb) ModIsAlt() bool { 323 return xkb.l.xkb_state_mod_name_is_active(xkb.state, XKB_MOD_NAME_ALT, C.XKB_STATE_MODS_EFFECTIVE) == 1 324 } 325 326 func (xkb *Xkb) ModIsLogo() bool { 327 return xkb.l.xkb_state_mod_name_is_active(xkb.state, XKB_MOD_NAME_LOGO, C.XKB_STATE_MODS_EFFECTIVE) == 1 328 } 329 330 func (xkb *Xkb) Destroy() { 331 if xkb.state != nil { 332 xkb.l.xkb_state_unref(xkb.state) 333 xkb.state = nil 334 } 335 if xkb.keymap != nil { 336 xkb.l.xkb_keymap_unref(xkb.keymap) 337 xkb.keymap = nil 338 } 339 if xkb.composeState != nil { 340 xkb.l.xkb_compose_state_unref(xkb.composeState) 341 xkb.composeState = nil 342 } 343 if xkb.composeTable != nil { 344 xkb.l.xkb_compose_table_unref(xkb.composeTable) 345 xkb.composeTable = nil 346 } 347 if xkb.context != nil { 348 xkb.l.xkb_context_unref(xkb.context) 349 xkb.context = nil 350 } 351 if xkb.l != nil { 352 xkb.l.close() 353 xkb.l = nil 354 } 355 }