keymap.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. /* See LICENSE file for copyright and license details. */
  2. #include <err.h>
  3. #include <stdio.h>
  4. #include <ctype.h>
  5. #include <string.h>
  6. #include <stdlib.h>
  7. #include <unistd.h>
  8. #include <inttypes.h>
  9. #if USE_X
  10. # include <xcb/xkb.h>
  11. # include "../X.h"
  12. #endif
  13. #include "../lib/util.h"
  14. #include "../aslstatus.h"
  15. #include "../components_config.h"
  16. #ifndef KEYMAP_NUMLOCK
  17. # define KEYMAP_NUMLOCK ""
  18. #endif
  19. #if USE_X && USE_XKB
  20. struct layout {
  21. char group[3];
  22. uint8_t lock_mask;
  23. };
  24. enum lock_mask {
  25. CAPS = 1 << 0,
  26. NUM = 1 << 1,
  27. };
  28. static void init_events(xcb_connection_t *c);
  29. static uint8_t init_xkb_extension(xcb_connection_t *);
  30. static uint8_t get_layout_struct(xcb_connection_t *, struct layout *);
  31. void
  32. keymap(char *layout,
  33. const char __unused *_a,
  34. uint32_t __unused _i,
  35. static_data_t *static_data)
  36. {
  37. void *ev = NULL;
  38. struct layout state;
  39. uint8_t *event_initialized = static_data->data;
  40. if (!*event_initialized) {
  41. if (!init_xkb_extension(X_CONNECTION)) {
  42. warnx("xcb: failed to initialize xkb extension");
  43. ERRRET(layout);
  44. }
  45. init_events(X_CONNECTION);
  46. *event_initialized = !0;
  47. } else {
  48. if (!(ev = xcb_wait_for_event(X_CONNECTION))) {
  49. warnx("xcb: failed to get xkb event");
  50. ERRRET(layout);
  51. }
  52. free(ev);
  53. }
  54. if (!!get_layout_struct(X_CONNECTION, &state)) ERRRET(layout);
  55. if (state.lock_mask & CAPS) {
  56. SAFE_ASSIGN(state.group[0], toupper(state.group[0]));
  57. SAFE_ASSIGN(state.group[1], toupper(state.group[1]));
  58. }
  59. bprintf(layout,
  60. "%s%s",
  61. state.group,
  62. (state.lock_mask & NUM) ? KEYMAP_NUMLOCK : "");
  63. }
  64. static uint8_t
  65. init_xkb_extension(xcb_connection_t *c)
  66. {
  67. uint8_t supported;
  68. xcb_xkb_use_extension_reply_t *reply;
  69. reply = xcb_xkb_use_extension_reply(
  70. c,
  71. xcb_xkb_use_extension(c,
  72. XCB_XKB_MAJOR_VERSION,
  73. XCB_XKB_MINOR_VERSION),
  74. NULL);
  75. supported = reply->supported;
  76. free(reply);
  77. return !!supported;
  78. }
  79. static void
  80. init_events(xcb_connection_t *c)
  81. {
  82. # define clone_field(S, F) typeof_field(S, F) F
  83. struct {
  84. clone_field(xcb_xkb_select_events_details_t, affectState);
  85. clone_field(xcb_xkb_select_events_details_t, stateDetails);
  86. clone_field(xcb_xkb_select_events_details_t,
  87. affectIndicatorState);
  88. clone_field(xcb_xkb_select_events_details_t,
  89. indicatorStateDetails);
  90. } details = {
  91. TWICE(1 << XCB_XKB_EVENT_TYPE_STATE_NOTIFY),
  92. TWICE(0xFFF /* all events */),
  93. };
  94. xcb_xkb_select_events(c,
  95. XCB_XKB_ID_USE_CORE_KBD,
  96. XCB_XKB_EVENT_TYPE_STATE_NOTIFY
  97. | XCB_XKB_EVENT_TYPE_INDICATOR_STATE_NOTIFY,
  98. TWICE(TWICE(0)),
  99. &details);
  100. xcb_flush(c);
  101. }
  102. static inline uint8_t
  103. valid_layout_or_variant(char *sym)
  104. {
  105. size_t i;
  106. /* invalid symbols from xkb rules config */
  107. static const char *invalid[] = { "evdev", "inet", "pc", "base" };
  108. for (i = 0; i < LEN(invalid); i++)
  109. if (!strcmp(sym, invalid[i])) return 0;
  110. return !0;
  111. }
  112. static inline char *
  113. get_layout(char *syms, uint8_t grp_num)
  114. {
  115. __typeof__(grp_num) grp;
  116. char *layout = NULL;
  117. char *tok = strtok(syms, "+:");
  118. for (grp = 0; tok && grp <= grp_num; tok = strtok(NULL, "+:")) {
  119. if (!valid_layout_or_variant(tok)
  120. /* ignore :2, :3, :4 (additional layout groups) */
  121. || (strlen(tok) == 1 && isdigit(*tok)))
  122. continue;
  123. layout = tok;
  124. grp++;
  125. }
  126. return layout;
  127. }
  128. static uint8_t
  129. get_layout_struct(xcb_connection_t *c, struct layout *ret)
  130. {
  131. # define test_reply(R, F, C, COOKIE, CODE, END) \
  132. do { \
  133. xcb_generic_error_t *_err = NULL; \
  134. \
  135. R = F((C), (COOKIE), &_err); \
  136. \
  137. if (_err) { \
  138. warnx("xcb: %s: error %" PRIu8, \
  139. #F, \
  140. _err->error_code); \
  141. CODE = !0; \
  142. goto END; \
  143. } \
  144. } while (0)
  145. char *layout;
  146. uint8_t error = 0;
  147. xcb_xkb_get_names_value_list_t names;
  148. xcb_get_atom_name_reply_t *atom_name = NULL;
  149. xcb_xkb_get_state_reply_t *state_reply = NULL;
  150. xcb_xkb_get_names_reply_t *names_reply = NULL;
  151. test_reply(state_reply,
  152. xcb_xkb_get_state_reply,
  153. c,
  154. xcb_xkb_get_state(c, XCB_XKB_ID_USE_CORE_KBD),
  155. error,
  156. end);
  157. test_reply(names_reply,
  158. xcb_xkb_get_names_reply,
  159. c,
  160. xcb_xkb_get_names(c,
  161. XCB_XKB_ID_USE_CORE_KBD,
  162. XCB_XKB_NAME_DETAIL_SYMBOLS),
  163. error,
  164. end);
  165. xcb_xkb_get_names_value_list_unpack(
  166. xcb_xkb_get_names_value_list(names_reply),
  167. names_reply->nTypes,
  168. names_reply->indicators,
  169. names_reply->virtualMods,
  170. names_reply->groupNames,
  171. names_reply->nKeys,
  172. names_reply->nKeyAliases,
  173. names_reply->nRadioGroups,
  174. names_reply->which,
  175. &names);
  176. test_reply(atom_name,
  177. xcb_get_atom_name_reply,
  178. c,
  179. xcb_get_atom_name(c, names.symbolsName),
  180. error,
  181. end);
  182. layout =
  183. get_layout(xcb_get_atom_name_name(atom_name), state_reply->group);
  184. if (strnlen(layout, 2) < 2) {
  185. warnx("%s: invalid layout length", __func__);
  186. error = !0;
  187. goto end;
  188. }
  189. ret->group[0] = layout[0];
  190. ret->group[1] = layout[1];
  191. ret->group[2] = '\0';
  192. ret->lock_mask = 0;
  193. /*
  194. * LM - Lock Mask
  195. * MM - Mod Mask
  196. */
  197. # define check_mask(LM, MM) \
  198. (__typeof__(ret->lock_mask))((LM) \
  199. * !!(state_reply->lockedMods \
  200. & (MM)))
  201. ret->lock_mask |= check_mask(CAPS, XCB_MOD_MASK_LOCK);
  202. ret->lock_mask |= check_mask(NUM, XCB_MOD_MASK_2);
  203. end:
  204. free(atom_name);
  205. free(state_reply);
  206. free(names_reply);
  207. return error;
  208. }
  209. #endif /* USE_X && USE_XKB */