xkb_unix.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. // SPDX-License-Identifier: Unlicense OR MIT
  2. //go:build (linux && !android) || freebsd || openbsd
  3. // +build linux,!android freebsd openbsd
  4. // Package xkb implements a Go interface for the X Keyboard Extension library.
  5. package xkb
  6. import (
  7. "errors"
  8. "fmt"
  9. "os"
  10. "syscall"
  11. "unicode"
  12. "unicode/utf8"
  13. "unsafe"
  14. "gioui.org/io/event"
  15. "gioui.org/io/key"
  16. )
  17. /*
  18. #cgo linux pkg-config: xkbcommon
  19. #cgo freebsd openbsd CFLAGS: -I/usr/local/include
  20. #cgo freebsd openbsd LDFLAGS: -L/usr/local/lib -lxkbcommon
  21. #include <stdlib.h>
  22. #include <xkbcommon/xkbcommon.h>
  23. #include <xkbcommon/xkbcommon-compose.h>
  24. */
  25. import "C"
  26. type Context struct {
  27. Ctx *C.struct_xkb_context
  28. keyMap *C.struct_xkb_keymap
  29. state *C.struct_xkb_state
  30. compTable *C.struct_xkb_compose_table
  31. compState *C.struct_xkb_compose_state
  32. utf8Buf []byte
  33. }
  34. var (
  35. _XKB_MOD_NAME_CTRL = []byte("Control\x00")
  36. _XKB_MOD_NAME_SHIFT = []byte("Shift\x00")
  37. _XKB_MOD_NAME_ALT = []byte("Mod1\x00")
  38. _XKB_MOD_NAME_LOGO = []byte("Mod4\x00")
  39. )
  40. func (x *Context) Destroy() {
  41. if x.compState != nil {
  42. C.xkb_compose_state_unref(x.compState)
  43. x.compState = nil
  44. }
  45. if x.compTable != nil {
  46. C.xkb_compose_table_unref(x.compTable)
  47. x.compTable = nil
  48. }
  49. x.DestroyKeymapState()
  50. if x.Ctx != nil {
  51. C.xkb_context_unref(x.Ctx)
  52. x.Ctx = nil
  53. }
  54. }
  55. func New() (*Context, error) {
  56. ctx := &Context{
  57. Ctx: C.xkb_context_new(C.XKB_CONTEXT_NO_FLAGS),
  58. }
  59. if ctx.Ctx == nil {
  60. return nil, errors.New("newXKB: xkb_context_new failed")
  61. }
  62. locale := os.Getenv("LC_ALL")
  63. if locale == "" {
  64. locale = os.Getenv("LC_CTYPE")
  65. }
  66. if locale == "" {
  67. locale = os.Getenv("LANG")
  68. }
  69. if locale == "" {
  70. locale = "C"
  71. }
  72. cloc := C.CString(locale)
  73. defer C.free(unsafe.Pointer(cloc))
  74. ctx.compTable = C.xkb_compose_table_new_from_locale(ctx.Ctx, cloc, C.XKB_COMPOSE_COMPILE_NO_FLAGS)
  75. if ctx.compTable == nil {
  76. ctx.Destroy()
  77. return nil, errors.New("newXKB: xkb_compose_table_new_from_locale failed")
  78. }
  79. ctx.compState = C.xkb_compose_state_new(ctx.compTable, C.XKB_COMPOSE_STATE_NO_FLAGS)
  80. if ctx.compState == nil {
  81. ctx.Destroy()
  82. return nil, errors.New("newXKB: xkb_compose_state_new failed")
  83. }
  84. return ctx, nil
  85. }
  86. func (x *Context) DestroyKeymapState() {
  87. if x.state != nil {
  88. C.xkb_state_unref(x.state)
  89. x.state = nil
  90. }
  91. if x.keyMap != nil {
  92. C.xkb_keymap_unref(x.keyMap)
  93. x.keyMap = nil
  94. }
  95. }
  96. // SetKeymap sets the keymap and state. The context takes ownership of the
  97. // keymap and state and frees them in Destroy.
  98. func (x *Context) SetKeymap(xkbKeyMap, xkbState unsafe.Pointer) {
  99. x.DestroyKeymapState()
  100. x.keyMap = (*C.struct_xkb_keymap)(xkbKeyMap)
  101. x.state = (*C.struct_xkb_state)(xkbState)
  102. }
  103. func (x *Context) LoadKeymap(format int, fd int, size int) error {
  104. x.DestroyKeymapState()
  105. mapData, err := syscall.Mmap(int(fd), 0, int(size), syscall.PROT_READ, syscall.MAP_SHARED)
  106. if err != nil {
  107. return fmt.Errorf("newXKB: mmap of keymap failed: %v", err)
  108. }
  109. defer syscall.Munmap(mapData)
  110. keyMap := C.xkb_keymap_new_from_buffer(x.Ctx, (*C.char)(unsafe.Pointer(&mapData[0])), C.size_t(size-1), C.XKB_KEYMAP_FORMAT_TEXT_V1, C.XKB_KEYMAP_COMPILE_NO_FLAGS)
  111. if keyMap == nil {
  112. return errors.New("newXKB: xkb_keymap_new_from_buffer failed")
  113. }
  114. state := C.xkb_state_new(keyMap)
  115. if state == nil {
  116. C.xkb_keymap_unref(keyMap)
  117. return errors.New("newXKB: xkb_state_new failed")
  118. }
  119. x.keyMap = keyMap
  120. x.state = state
  121. return nil
  122. }
  123. func (x *Context) Modifiers() key.Modifiers {
  124. var mods key.Modifiers
  125. if x.state == nil {
  126. return mods
  127. }
  128. 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 {
  129. mods |= key.ModCtrl
  130. }
  131. 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 {
  132. mods |= key.ModShift
  133. }
  134. if C.xkb_state_mod_name_is_active(x.state, (*C.char)(unsafe.Pointer(&_XKB_MOD_NAME_ALT[0])), C.XKB_STATE_MODS_EFFECTIVE) == 1 {
  135. mods |= key.ModAlt
  136. }
  137. if C.xkb_state_mod_name_is_active(x.state, (*C.char)(unsafe.Pointer(&_XKB_MOD_NAME_LOGO[0])), C.XKB_STATE_MODS_EFFECTIVE) == 1 {
  138. mods |= key.ModSuper
  139. }
  140. return mods
  141. }
  142. func (x *Context) DispatchKey(keyCode uint32, state key.State) (events []event.Event) {
  143. if x.state == nil {
  144. return
  145. }
  146. kc := C.xkb_keycode_t(keyCode)
  147. if len(x.utf8Buf) == 0 {
  148. x.utf8Buf = make([]byte, 1)
  149. }
  150. sym := C.xkb_state_key_get_one_sym(x.state, kc)
  151. if name, ok := convertKeysym(sym); ok {
  152. cmd := key.Event{
  153. Name: name,
  154. Modifiers: x.Modifiers(),
  155. State: state,
  156. }
  157. // Ensure that a physical backtab key is translated to
  158. // Shift-Tab.
  159. if sym == C.XKB_KEY_ISO_Left_Tab {
  160. cmd.Modifiers |= key.ModShift
  161. }
  162. events = append(events, cmd)
  163. }
  164. C.xkb_compose_state_feed(x.compState, sym)
  165. var str []byte
  166. switch C.xkb_compose_state_get_status(x.compState) {
  167. case C.XKB_COMPOSE_CANCELLED, C.XKB_COMPOSE_COMPOSING:
  168. return
  169. case C.XKB_COMPOSE_COMPOSED:
  170. size := C.xkb_compose_state_get_utf8(x.compState, (*C.char)(unsafe.Pointer(&x.utf8Buf[0])), C.size_t(len(x.utf8Buf)))
  171. if int(size) >= len(x.utf8Buf) {
  172. x.utf8Buf = make([]byte, size+1)
  173. size = C.xkb_compose_state_get_utf8(x.compState, (*C.char)(unsafe.Pointer(&x.utf8Buf[0])), C.size_t(len(x.utf8Buf)))
  174. }
  175. C.xkb_compose_state_reset(x.compState)
  176. str = x.utf8Buf[:size]
  177. case C.XKB_COMPOSE_NOTHING:
  178. mod := x.Modifiers()
  179. if mod&(key.ModCtrl|key.ModAlt|key.ModSuper) == 0 {
  180. str = x.charsForKeycode(kc)
  181. }
  182. }
  183. // Report only printable runes.
  184. var n int
  185. for n < len(str) {
  186. r, s := utf8.DecodeRune(str)
  187. if unicode.IsPrint(r) {
  188. n += s
  189. } else {
  190. copy(str[n:], str[n+s:])
  191. str = str[:len(str)-s]
  192. }
  193. }
  194. if state == key.Press && len(str) > 0 {
  195. events = append(events, key.EditEvent{Text: string(str)})
  196. }
  197. return
  198. }
  199. func (x *Context) charsForKeycode(keyCode C.xkb_keycode_t) []byte {
  200. size := C.xkb_state_key_get_utf8(x.state, keyCode, (*C.char)(unsafe.Pointer(&x.utf8Buf[0])), C.size_t(len(x.utf8Buf)))
  201. if int(size) >= len(x.utf8Buf) {
  202. x.utf8Buf = make([]byte, size+1)
  203. size = C.xkb_state_key_get_utf8(x.state, keyCode, (*C.char)(unsafe.Pointer(&x.utf8Buf[0])), C.size_t(len(x.utf8Buf)))
  204. }
  205. return x.utf8Buf[:size]
  206. }
  207. func (x *Context) IsRepeatKey(keyCode uint32) bool {
  208. if x.state == nil {
  209. return false
  210. }
  211. kc := C.xkb_keycode_t(keyCode)
  212. return C.xkb_keymap_key_repeats(x.keyMap, kc) == 1
  213. }
  214. func (x *Context) UpdateMask(depressed, latched, locked, depressedGroup, latchedGroup, lockedGroup uint32) {
  215. if x.state == nil {
  216. return
  217. }
  218. C.xkb_state_update_mask(x.state, C.xkb_mod_mask_t(depressed), C.xkb_mod_mask_t(latched), C.xkb_mod_mask_t(locked),
  219. C.xkb_layout_index_t(depressedGroup), C.xkb_layout_index_t(latchedGroup), C.xkb_layout_index_t(lockedGroup))
  220. }
  221. func convertKeysym(s C.xkb_keysym_t) (key.Name, bool) {
  222. if 'a' <= s && s <= 'z' {
  223. return key.Name(rune(s - 'a' + 'A')), true
  224. }
  225. if C.XKB_KEY_KP_0 <= s && s <= C.XKB_KEY_KP_9 {
  226. return key.Name(rune(s - C.XKB_KEY_KP_0 + '0')), true
  227. }
  228. if ' ' < s && s <= '~' {
  229. return key.Name(rune(s)), true
  230. }
  231. var n key.Name
  232. switch s {
  233. case C.XKB_KEY_Escape:
  234. n = key.NameEscape
  235. case C.XKB_KEY_Left:
  236. n = key.NameLeftArrow
  237. case C.XKB_KEY_Right:
  238. n = key.NameRightArrow
  239. case C.XKB_KEY_Return:
  240. n = key.NameReturn
  241. case C.XKB_KEY_Up:
  242. n = key.NameUpArrow
  243. case C.XKB_KEY_Down:
  244. n = key.NameDownArrow
  245. case C.XKB_KEY_Home:
  246. n = key.NameHome
  247. case C.XKB_KEY_End:
  248. n = key.NameEnd
  249. case C.XKB_KEY_BackSpace:
  250. n = key.NameDeleteBackward
  251. case C.XKB_KEY_Delete:
  252. n = key.NameDeleteForward
  253. case C.XKB_KEY_Page_Up:
  254. n = key.NamePageUp
  255. case C.XKB_KEY_Page_Down:
  256. n = key.NamePageDown
  257. case C.XKB_KEY_F1:
  258. n = key.NameF1
  259. case C.XKB_KEY_F2:
  260. n = key.NameF2
  261. case C.XKB_KEY_F3:
  262. n = key.NameF3
  263. case C.XKB_KEY_F4:
  264. n = key.NameF4
  265. case C.XKB_KEY_F5:
  266. n = key.NameF5
  267. case C.XKB_KEY_F6:
  268. n = key.NameF6
  269. case C.XKB_KEY_F7:
  270. n = key.NameF7
  271. case C.XKB_KEY_F8:
  272. n = key.NameF8
  273. case C.XKB_KEY_F9:
  274. n = key.NameF9
  275. case C.XKB_KEY_F10:
  276. n = key.NameF10
  277. case C.XKB_KEY_F11:
  278. n = key.NameF11
  279. case C.XKB_KEY_F12:
  280. n = key.NameF12
  281. case C.XKB_KEY_Tab, C.XKB_KEY_ISO_Left_Tab:
  282. n = key.NameTab
  283. case 0x20:
  284. n = key.NameSpace
  285. case C.XKB_KEY_Control_L, C.XKB_KEY_Control_R:
  286. n = key.NameCtrl
  287. case C.XKB_KEY_Shift_L, C.XKB_KEY_Shift_R:
  288. n = key.NameShift
  289. case C.XKB_KEY_Alt_L, C.XKB_KEY_Alt_R:
  290. n = key.NameAlt
  291. case C.XKB_KEY_Super_L, C.XKB_KEY_Super_R:
  292. n = key.NameSuper
  293. case C.XKB_KEY_KP_Space:
  294. n = key.NameSpace
  295. case C.XKB_KEY_KP_Tab:
  296. n = key.NameTab
  297. case C.XKB_KEY_KP_Enter:
  298. n = key.NameEnter
  299. case C.XKB_KEY_KP_F1:
  300. n = key.NameF1
  301. case C.XKB_KEY_KP_F2:
  302. n = key.NameF2
  303. case C.XKB_KEY_KP_F3:
  304. n = key.NameF3
  305. case C.XKB_KEY_KP_F4:
  306. n = key.NameF4
  307. case C.XKB_KEY_KP_Home:
  308. n = key.NameHome
  309. case C.XKB_KEY_KP_Left:
  310. n = key.NameLeftArrow
  311. case C.XKB_KEY_KP_Up:
  312. n = key.NameUpArrow
  313. case C.XKB_KEY_KP_Right:
  314. n = key.NameRightArrow
  315. case C.XKB_KEY_KP_Down:
  316. n = key.NameDownArrow
  317. case C.XKB_KEY_KP_Prior:
  318. // not supported
  319. return "", false
  320. case C.XKB_KEY_KP_Next:
  321. // not supported
  322. return "", false
  323. case C.XKB_KEY_KP_End:
  324. n = key.NameEnd
  325. case C.XKB_KEY_KP_Begin:
  326. n = key.NameHome
  327. case C.XKB_KEY_KP_Insert:
  328. // not supported
  329. return "", false
  330. case C.XKB_KEY_KP_Delete:
  331. n = key.NameDeleteForward
  332. case C.XKB_KEY_KP_Multiply:
  333. n = "*"
  334. case C.XKB_KEY_KP_Add:
  335. n = "+"
  336. case C.XKB_KEY_KP_Separator:
  337. // not supported
  338. return "", false
  339. case C.XKB_KEY_KP_Subtract:
  340. n = "-"
  341. case C.XKB_KEY_KP_Decimal:
  342. // TODO(dh): does a German keyboard layout also translate the numpad key to XKB_KEY_KP_DECIMAL? Because in
  343. // German, the decimal is a comma, not a period.
  344. n = "."
  345. case C.XKB_KEY_KP_Divide:
  346. n = "/"
  347. case C.XKB_KEY_KP_Equal:
  348. n = "="
  349. default:
  350. return "", false
  351. }
  352. return n, true
  353. }