1
0

interactive-x11.c 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958
  1. /*
  2. * Copyright © 2013 Ran Benita <ran234@gmail.com>
  3. * SPDX-License-Identifier: MIT
  4. */
  5. #include "config.h"
  6. #include <assert.h>
  7. #include <getopt.h>
  8. #include <locale.h>
  9. #include <stdbool.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <unistd.h>
  13. #include <xcb/xkb.h>
  14. #include <xcb/xproto.h>
  15. #include "xkbcommon/xkbcommon.h"
  16. #include "xkbcommon/xkbcommon-x11.h"
  17. #include "xkbcommon/xkbcommon-compose.h"
  18. #include "src/keymap-formats.h"
  19. #include "src/utils.h"
  20. #include "tools-common.h"
  21. /*
  22. * Note: This program only handles the core keyboard device for now.
  23. * It should be straigtforward to change struct keyboard to a list of
  24. * keyboards with device IDs, as in tools/interactive-evdev.c. This would
  25. * require:
  26. *
  27. * - Initially listing the keyboard devices.
  28. * - Listening to device changes.
  29. * - Matching events to their devices.
  30. *
  31. * XKB itself knows about xinput1 devices, and most requests and events are
  32. * device-specific.
  33. *
  34. * In order to list the devices and react to changes, you need xinput1/2.
  35. * You also need xinput for the key press/release event, since the core
  36. * protocol key press event does not carry a device ID to match on.
  37. */
  38. struct keyboard {
  39. xcb_connection_t *conn;
  40. uint8_t first_xkb_event;
  41. struct xkb_context *ctx;
  42. struct xkb_keymap *keymap;
  43. struct xkb_state *state;
  44. struct xkb_machine *machine;
  45. struct xkb_events *events;
  46. struct xkb_compose_state *compose_state;
  47. xkb_keycode_t repeated_key;
  48. int32_t device_id;
  49. };
  50. static bool terminate;
  51. static enum xkb_keymap_compile_flags compile_flags =
  52. (enum xkb_keymap_compile_flags) DEFAULT_KEYMAP_COMPILE_FLAGS;
  53. #ifdef KEYMAP_DUMP
  54. static_assert(DEFAULT_OUTPUT_KEYMAP_FORMAT == XKB_KEYMAP_USE_ORIGINAL_FORMAT,
  55. "Out of sync usage()");
  56. static enum xkb_keymap_format keymap_format = DEFAULT_OUTPUT_KEYMAP_FORMAT;
  57. static enum xkb_keymap_serialize_flags serialize_flags =
  58. (enum xkb_keymap_serialize_flags) DEFAULT_KEYMAP_SERIALIZE_FLAGS;
  59. #else
  60. static bool detect_repeat = false;
  61. static bool use_events_api = true;
  62. static enum xkb_consumed_mode consumed_mode = XKB_CONSUMED_MODE_XKB;
  63. static enum print_state_options print_options = DEFAULT_PRINT_OPTIONS;
  64. static bool report_state_changes = true;
  65. static bool use_local_state = false;
  66. static struct xkb_machine_options machine_options = xkb_machine_options_new();
  67. static struct xkb_keymap *custom_keymap = NULL;
  68. #endif
  69. static int
  70. select_xkb_events_for_device(xcb_connection_t *conn, int32_t device_id)
  71. {
  72. enum {
  73. required_events =
  74. (XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY |
  75. XCB_XKB_EVENT_TYPE_MAP_NOTIFY |
  76. XCB_XKB_EVENT_TYPE_STATE_NOTIFY),
  77. required_nkn_details =
  78. (XCB_XKB_NKN_DETAIL_KEYCODES),
  79. required_map_parts =
  80. (XCB_XKB_MAP_PART_KEY_TYPES |
  81. XCB_XKB_MAP_PART_KEY_SYMS |
  82. XCB_XKB_MAP_PART_MODIFIER_MAP |
  83. XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS |
  84. XCB_XKB_MAP_PART_KEY_ACTIONS |
  85. XCB_XKB_MAP_PART_VIRTUAL_MODS |
  86. XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP),
  87. required_state_details =
  88. (XCB_XKB_STATE_PART_MODIFIER_BASE |
  89. XCB_XKB_STATE_PART_MODIFIER_LATCH |
  90. XCB_XKB_STATE_PART_MODIFIER_LOCK |
  91. XCB_XKB_STATE_PART_GROUP_BASE |
  92. XCB_XKB_STATE_PART_GROUP_LATCH |
  93. XCB_XKB_STATE_PART_GROUP_LOCK),
  94. };
  95. static const xcb_xkb_select_events_details_t details = {
  96. .affectNewKeyboard = required_nkn_details,
  97. .newKeyboardDetails = required_nkn_details,
  98. .affectState = required_state_details,
  99. .stateDetails = required_state_details,
  100. };
  101. xcb_void_cookie_t cookie =
  102. xcb_xkb_select_events_aux_checked(conn,
  103. device_id,
  104. required_events, /* affectWhich */
  105. 0, /* clear */
  106. 0, /* selectAll */
  107. required_map_parts, /* affectMap */
  108. required_map_parts, /* map */
  109. &details); /* details */
  110. xcb_generic_error_t *error = xcb_request_check(conn, cookie);
  111. if (error) {
  112. free(error);
  113. return -1;
  114. }
  115. return 0;
  116. }
  117. static int
  118. update_keymap(struct keyboard *kbd)
  119. {
  120. #ifndef KEYMAP_DUMP
  121. if (custom_keymap) {
  122. if (!kbd->keymap) {
  123. /* Keymap is only parsed the one time, then we just reference it. */
  124. kbd->keymap = xkb_keymap_ref(custom_keymap);
  125. }
  126. } else {
  127. #endif
  128. const bool update = !!kbd->keymap && !!kbd->state;
  129. xkb_keymap_unref(kbd->keymap);
  130. kbd->keymap = xkb_x11_keymap_new_from_device(kbd->ctx, kbd->conn,
  131. kbd->device_id,
  132. XKB_KEYMAP_COMPILE_NO_FLAGS);
  133. if (!kbd->keymap)
  134. return -1;
  135. if (update)
  136. printf("Keymap updated!\n");
  137. #ifndef KEYMAP_DUMP
  138. }
  139. if (!use_local_state) {
  140. #endif
  141. /* Reset state on keymap reset */
  142. xkb_state_unref(kbd->state);
  143. kbd->state = xkb_x11_state_new_from_device(kbd->keymap, kbd->conn,
  144. kbd->device_id);
  145. if (!kbd->state)
  146. return -1;
  147. #ifndef KEYMAP_DUMP
  148. } else {
  149. /* Ignore state from server and reset only if state if undefined. */
  150. if (!kbd->state) {
  151. kbd->state = xkb_state_new(kbd->keymap);
  152. if (!kbd->state)
  153. return -1;
  154. }
  155. const struct xkb_state_components_update components_update = {
  156. .size = sizeof(components_update),
  157. .components = XKB_STATE_CONTROLS,
  158. .affect_controls = machine_options.controls.boolean.affect,
  159. .controls = machine_options.controls.boolean.flags,
  160. };
  161. const struct xkb_state_update update = {
  162. .size = sizeof(update),
  163. .components = &components_update,
  164. };
  165. if (use_events_api) {
  166. if (!kbd->machine) {
  167. struct xkb_machine_builder * machine_builder =
  168. xkb_machine_builder_new_from_options(kbd->keymap, &machine_options);
  169. if (!machine_builder) {
  170. fprintf(stderr, "ERROR: Couldn't create xkb state machine builder\n");
  171. return - 1;
  172. }
  173. kbd->machine = xkb_machine_new(machine_builder);
  174. xkb_machine_builder_destroy(machine_builder);
  175. if (!kbd->machine)
  176. return -1;
  177. }
  178. if (!kbd->events) {
  179. kbd->events = xkb_events_new_batch(kbd->ctx,
  180. XKB_EVENTS_NO_FLAGS);
  181. if (!kbd->events)
  182. return -1;
  183. }
  184. const int ret = xkb_machine_process_synthetic(kbd->machine,
  185. &update, kbd->events);
  186. if (ret)
  187. return ret;
  188. const struct xkb_event *event;
  189. while ((event = xkb_events_next(kbd->events))) {
  190. xkb_state_update_event(kbd->state, event);
  191. }
  192. } else {
  193. return xkb_state_update_synthetic(kbd->state, &update, NULL);
  194. }
  195. }
  196. #endif
  197. return 0;
  198. }
  199. static int
  200. init_kbd(struct keyboard *kbd, xcb_connection_t *conn, uint8_t first_xkb_event,
  201. int32_t device_id, struct xkb_context *ctx,
  202. struct xkb_compose_table *compose_table)
  203. {
  204. int ret;
  205. kbd->conn = conn;
  206. kbd->first_xkb_event = first_xkb_event;
  207. kbd->ctx = ctx;
  208. kbd->keymap = NULL;
  209. kbd->state = NULL;
  210. kbd->compose_state = NULL;
  211. kbd->repeated_key = XKB_KEYCODE_INVALID;
  212. kbd->device_id = device_id;
  213. ret = update_keymap(kbd);
  214. if (ret)
  215. goto err_out;
  216. #ifdef KEYMAP_DUMP
  217. /* Dump the keymap */
  218. char *dump = xkb_keymap_get_as_string2(kbd->keymap, keymap_format,
  219. serialize_flags);
  220. fprintf(stdout, "%s", dump);
  221. free(dump);
  222. terminate = true;
  223. return 0;
  224. #endif
  225. if (compose_table)
  226. kbd->compose_state = xkb_compose_state_new(compose_table,
  227. XKB_COMPOSE_STATE_NO_FLAGS);
  228. ret = select_xkb_events_for_device(conn, device_id);
  229. if (ret)
  230. goto err_state;
  231. return 0;
  232. err_state:
  233. xkb_state_unref(kbd->state);
  234. xkb_machine_unref(kbd->machine);
  235. xkb_events_destroy(kbd->events);
  236. xkb_compose_state_unref(kbd->compose_state);
  237. xkb_keymap_unref(kbd->keymap);
  238. err_out:
  239. return -1;
  240. }
  241. static void
  242. deinit_kbd(struct keyboard *kbd)
  243. {
  244. xkb_state_unref(kbd->state);
  245. xkb_machine_unref(kbd->machine);
  246. xkb_events_destroy(kbd->events);
  247. xkb_compose_state_unref(kbd->compose_state);
  248. xkb_keymap_unref(kbd->keymap);
  249. }
  250. #ifndef KEYMAP_DUMP
  251. static void
  252. process_xkb_event(xcb_generic_event_t *gevent, struct keyboard *kbd)
  253. {
  254. union xkb_event {
  255. struct {
  256. uint8_t response_type;
  257. uint8_t xkbType;
  258. uint16_t sequence;
  259. xcb_timestamp_t time;
  260. uint8_t deviceID;
  261. } any;
  262. xcb_xkb_new_keyboard_notify_event_t new_keyboard_notify;
  263. xcb_xkb_map_notify_event_t map_notify;
  264. xcb_xkb_state_notify_event_t state_notify;
  265. } *event = (union xkb_event *) gevent;
  266. if (event->any.deviceID != kbd->device_id)
  267. return;
  268. /*
  269. * XkbNewKkdNotify and XkbMapNotify together capture all sorts of keymap
  270. * updates (e.g. xmodmap, xkbcomp, setxkbmap), with minimal redundent
  271. * recompilations.
  272. */
  273. switch (event->any.xkbType) {
  274. case XCB_XKB_NEW_KEYBOARD_NOTIFY:
  275. if (event->new_keyboard_notify.changed & XCB_XKB_NKN_DETAIL_KEYCODES)
  276. update_keymap(kbd);
  277. break;
  278. case XCB_XKB_MAP_NOTIFY:
  279. update_keymap(kbd);
  280. break;
  281. case XCB_XKB_STATE_NOTIFY: {
  282. if (use_local_state) {
  283. /* Ignore state update if using a local state machine */
  284. break;
  285. }
  286. const enum xkb_state_component changed =
  287. xkb_state_update_mask(kbd->state,
  288. event->state_notify.baseMods,
  289. event->state_notify.latchedMods,
  290. event->state_notify.lockedMods,
  291. event->state_notify.baseGroup,
  292. event->state_notify.latchedGroup,
  293. event->state_notify.lockedGroup);
  294. if (report_state_changes)
  295. tools_print_state_changes(NULL, kbd->state, changed, print_options);
  296. break;
  297. }
  298. default:
  299. /* Ignore */
  300. break;
  301. }
  302. }
  303. static void
  304. process_event(xcb_generic_event_t *gevent, struct keyboard *kbd)
  305. {
  306. switch (gevent->response_type) {
  307. case XCB_KEY_PRESS: {
  308. xcb_key_press_event_t *event = (xcb_key_press_event_t *) gevent;
  309. const xkb_keycode_t keycode = event->detail;
  310. const bool repeated = detect_repeat && kbd->repeated_key == keycode;
  311. if (!repeated)
  312. kbd->repeated_key = keycode;
  313. const enum xkb_key_direction direction = (repeated)
  314. ? XKB_KEY_REPEATED
  315. : XKB_KEY_DOWN;
  316. if (use_local_state && use_events_api) {
  317. /* Run our local state machine with the event API */
  318. const int ret = xkb_machine_process_key(kbd->machine,
  319. keycode, direction,
  320. kbd->events);
  321. if (ret) {
  322. fprintf(stderr, "ERROR: could not update the state machine\n");
  323. // TODO: better error handling
  324. } else {
  325. tools_print_events(NULL, kbd->state, kbd->events,
  326. kbd->compose_state, consumed_mode,
  327. print_options, report_state_changes);
  328. }
  329. } else {
  330. if (kbd->compose_state) {
  331. xkb_keysym_t keysym = xkb_state_key_get_one_sym(kbd->state,
  332. keycode);
  333. xkb_compose_state_feed(kbd->compose_state, keysym);
  334. }
  335. tools_print_keycode_state(NULL, kbd->state, kbd->compose_state,
  336. keycode, direction,
  337. consumed_mode, print_options);
  338. if (kbd->compose_state) {
  339. enum xkb_compose_status status =
  340. xkb_compose_state_get_status(kbd->compose_state);
  341. if (status == XKB_COMPOSE_CANCELLED ||
  342. status == XKB_COMPOSE_COMPOSED)
  343. xkb_compose_state_reset(kbd->compose_state);
  344. }
  345. if (use_local_state) {
  346. /* Run our local state machine with the legacy API */
  347. const enum xkb_state_component changed =
  348. xkb_state_update_key(kbd->state, keycode, direction);
  349. if (changed && report_state_changes)
  350. tools_print_state_changes(NULL, kbd->state,
  351. changed, print_options);
  352. }
  353. }
  354. /* Exit on ESC. */
  355. if (xkb_state_key_get_one_sym(kbd->state, keycode) == XKB_KEY_Escape)
  356. terminate = true;
  357. break;
  358. }
  359. case XCB_KEY_RELEASE: {
  360. xcb_key_press_event_t *event = (xcb_key_press_event_t *) gevent;
  361. const xkb_keycode_t keycode = event->detail;
  362. if (kbd->repeated_key == keycode)
  363. kbd->repeated_key = XKB_KEYCODE_INVALID;
  364. if (use_local_state && use_events_api) {
  365. /* Run our local state machine */
  366. const int ret = xkb_machine_process_key(kbd->machine,
  367. keycode, XKB_KEY_UP,
  368. kbd->events);
  369. if (ret) {
  370. fprintf(stderr, "ERROR: could not update the state machine\n");
  371. // TODO: better error handling
  372. } else {
  373. tools_print_events(NULL, kbd->state, kbd->events,
  374. kbd->compose_state, consumed_mode,
  375. print_options, report_state_changes);
  376. }
  377. } else {
  378. tools_print_keycode_state(NULL, kbd->state, kbd->compose_state,
  379. keycode, XKB_KEY_UP, consumed_mode,
  380. print_options);
  381. if (use_local_state) {
  382. /* Run our local state machine with the legacy API */
  383. const enum xkb_state_component changed =
  384. xkb_state_update_key(kbd->state, keycode, XKB_KEY_UP);
  385. if (changed && report_state_changes)
  386. tools_print_state_changes(NULL, kbd->state,
  387. changed, print_options);
  388. }
  389. }
  390. break;
  391. }
  392. default:
  393. if (gevent->response_type == kbd->first_xkb_event)
  394. process_xkb_event(gevent, kbd);
  395. break;
  396. }
  397. }
  398. static int
  399. loop(xcb_connection_t *conn, struct keyboard *kbd)
  400. {
  401. while (!terminate) {
  402. xcb_generic_event_t *event;
  403. switch (xcb_connection_has_error(conn)) {
  404. case 0:
  405. break;
  406. case XCB_CONN_ERROR:
  407. fprintf(stderr,
  408. "Closed connection to X server: connection error\n");
  409. return -1;
  410. case XCB_CONN_CLOSED_EXT_NOTSUPPORTED:
  411. fprintf(stderr,
  412. "Closed connection to X server: extension not supported\n");
  413. return -1;
  414. default:
  415. fprintf(stderr,
  416. "Closed connection to X server: error code %d\n",
  417. xcb_connection_has_error(conn));
  418. return -1;
  419. }
  420. event = xcb_wait_for_event(conn);
  421. if (!event) {
  422. continue;
  423. }
  424. process_event(event, kbd);
  425. free(event);
  426. }
  427. return 0;
  428. }
  429. static int
  430. create_capture_window(xcb_connection_t *conn)
  431. {
  432. xcb_generic_error_t *error;
  433. xcb_void_cookie_t cookie;
  434. xcb_screen_t *screen =
  435. xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
  436. xcb_window_t window = xcb_generate_id(conn);
  437. uint32_t values[2] = {
  438. screen->white_pixel,
  439. XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE,
  440. };
  441. cookie = xcb_create_window_checked(conn, XCB_COPY_FROM_PARENT,
  442. window, screen->root,
  443. 10, 10, 100, 100, 1,
  444. XCB_WINDOW_CLASS_INPUT_OUTPUT,
  445. screen->root_visual,
  446. XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK,
  447. values);
  448. if ((error = xcb_request_check(conn, cookie)) != NULL) {
  449. free(error);
  450. return -1;
  451. }
  452. cookie = xcb_map_window_checked(conn, window);
  453. if ((error = xcb_request_check(conn, cookie)) != NULL) {
  454. free(error);
  455. return -1;
  456. }
  457. return 0;
  458. }
  459. #endif
  460. static void
  461. usage(FILE *fp, char *progname)
  462. {
  463. fprintf(fp,
  464. "Usage: %s [--help] [--version] [--verbose]"
  465. #ifndef KEYMAP_DUMP
  466. " [--uniline] [--multiline] [--consumed-mode={xkb|gtk}] [--no-state-report]"
  467. #endif
  468. " [--format FORMAT] [--strict]"
  469. #ifdef KEYMAP_DUMP
  470. " [--no-pretty] [--drop-unused]"
  471. #else
  472. " [--enable-compose]"
  473. " [--local-state] [--legacy-state-api true|false]"
  474. " [--controls CONTROLS] [--modifiers-mapping MAPPING]"
  475. " [--shortcuts-mask MASK] [--shortcuts-mapping]"
  476. " [--keymap FILE]"
  477. #endif
  478. "\n",
  479. progname);
  480. fprintf(fp,
  481. #ifndef KEYMAP_DUMP
  482. " --enable-compose enable Compose\n"
  483. " --local-state enable local state handling and ignore modifiers/layouts\n"
  484. " state updates from the X11 server\n"
  485. " --legacy-state-api [=true|false]\n"
  486. " use the legacy state API instead of the event API.\n"
  487. " It implies --local-state if explicitly disabled.\n"
  488. " Default: false.\n"
  489. " --controls [<CONTROLS>]\n"
  490. " use the given keyboard controls; available values are:\n"
  491. " sticky-keys, sticky-keys-no-simultaneous,\n"
  492. " sticky-keys-latch-to-lock, latch-simultaneous and overlay{1-2}.\n"
  493. " It implies --local-state and --legacy-state-api=false.\n"
  494. " --modifiers-mapping <MAPPING>\n"
  495. " use the given modifiers mapping.\n"
  496. " <MAPPING> is a comma-separated list of modifiers masks\n"
  497. " mappings with format \"source:target\", e.g.\n"
  498. " \"Control+Alt:LevelThree,Alt:Meta\".\n"
  499. " It implies --local-state and --legacy-state-api=false.\n"
  500. " --shortcuts-mask <MASK>\n"
  501. " use the given modifier mask to enable selecting a specific\n"
  502. " layout (see --shortcuts-mapping) when some of these modifiers\n"
  503. " are active. The modifier mask is a plus-separated list of\n"
  504. " modifiers names, e.g. \"Control+Alt+Super\".\n"
  505. " It implies --local-state and --legacy-state-api=false.\n"
  506. " --shortcuts-mapping <MAPPING>\n"
  507. " use the given layout mapping to enable selecting a specific\n"
  508. " layout when some modifiers are active (see --shortcuts-mask).\n"
  509. " <MAPPING> is a comma-separated list of 1-indexed layout\n"
  510. " indices mappings with format \"source:target\", e.g. \"2:1,3:1\".\n"
  511. " It implies --local-state and --legacy-state-api=false.\n"
  512. " --keymap [<FILE>] use the given keymap instead of the keymap from the compositor.\n"
  513. " It implies --local-state.\n"
  514. " If <FILE> is \"-\" or missing, then load from stdin.\n"
  515. #endif
  516. " --format <FORMAT> use keymap format <FORMAT> (default: '%s')\n"
  517. " --strict parse using strict mode\n"
  518. #ifdef KEYMAP_DUMP
  519. " --no-pretty do not pretty-print when serializing a keymap\n"
  520. " --drop-unused disable unused bits serialization\n"
  521. #else
  522. " -1, --uniline enable uniline event output\n"
  523. " -*, --multiline enable multiline event output\n"
  524. " --consumed-mode={xkb|gtk}\n"
  525. " select the consumed modifiers mode (default: xkb)\n"
  526. " --no-state-report do not report changes to the state\n"
  527. #endif
  528. " --verbose enable verbose debugging output\n"
  529. " --help display this help and exit\n"
  530. " --version print version information and exit\n",
  531. xkb_keymap_get_format_label(DEFAULT_INPUT_KEYMAP_FORMAT)
  532. );
  533. }
  534. int
  535. main(int argc, char *argv[])
  536. {
  537. int ret;
  538. bool verbose = false;
  539. xcb_connection_t *conn;
  540. uint8_t first_xkb_event;
  541. int32_t core_kbd_device_id;
  542. struct xkb_context *ctx;
  543. struct keyboard core_kbd = {0};
  544. struct xkb_compose_table *compose_table = NULL;
  545. #ifndef KEYMAP_DUMP
  546. bool with_keymap_file = false;
  547. enum xkb_keymap_format keymap_format = DEFAULT_INPUT_KEYMAP_FORMAT;
  548. const char *keymap_path = NULL;
  549. bool with_compose = false;
  550. const char *locale;
  551. #endif
  552. enum options {
  553. OPT_VERBOSE,
  554. OPT_UNILINE,
  555. OPT_MULTILINE,
  556. OPT_CONSUMED_MODE,
  557. OPT_NO_STATE_REPORT,
  558. OPT_COMPOSE,
  559. OPT_LOCAL_STATE,
  560. OPT_LEGACY_STATE_API,
  561. OPT_CONTROLS,
  562. OPT_MODIFIERS_TWEAK_MAPPING,
  563. OPT_SHORTCUTS_TWEAK_MASK,
  564. OPT_SHORTCUTS_TWEAK_MAPPING,
  565. OPT_KEYMAP_FORMAT,
  566. OPT_KEYMAP_STRICT_PARSER,
  567. OPT_KEYMAP_NO_PRETTY,
  568. OPT_KEYMAP_DROP_UNUSED,
  569. OPT_KEYMAP,
  570. };
  571. static struct option opts[] = {
  572. {"help", no_argument, 0, 'h'},
  573. {"version", no_argument, 0, 'V'},
  574. {"verbose", no_argument, 0, OPT_VERBOSE},
  575. {"format", required_argument, 0, OPT_KEYMAP_FORMAT},
  576. {"strict", no_argument, 0, OPT_KEYMAP_STRICT_PARSER},
  577. #ifdef KEYMAP_DUMP
  578. {"no-pretty", no_argument, 0, OPT_KEYMAP_NO_PRETTY},
  579. {"drop-unused", no_argument, 0, OPT_KEYMAP_DROP_UNUSED},
  580. #else
  581. {"uniline", no_argument, 0, OPT_UNILINE},
  582. {"multiline", no_argument, 0, OPT_MULTILINE},
  583. {"consumed-mode", required_argument, 0, OPT_CONSUMED_MODE},
  584. {"no-state-report", no_argument, 0, OPT_NO_STATE_REPORT},
  585. {"enable-compose", no_argument, 0, OPT_COMPOSE},
  586. {"local-state", no_argument, 0, OPT_LOCAL_STATE},
  587. {"legacy-state-api", optional_argument, 0, OPT_LEGACY_STATE_API},
  588. {"controls", required_argument, 0, OPT_CONTROLS},
  589. {"modifiers-mapping", required_argument, 0, OPT_MODIFIERS_TWEAK_MAPPING},
  590. {"shortcuts-mask", required_argument, 0, OPT_SHORTCUTS_TWEAK_MASK},
  591. {"shortcuts-mapping", required_argument, 0, OPT_SHORTCUTS_TWEAK_MAPPING},
  592. {"keymap", optional_argument, 0, OPT_KEYMAP},
  593. #endif
  594. {0, 0, 0, 0},
  595. };
  596. setlocale(LC_ALL, "");
  597. #ifndef KEYMAP_DUMP
  598. /* Ensure synced with usage() and man page */
  599. assert(use_events_api);
  600. assert(consumed_mode == XKB_CONSUMED_MODE_XKB);
  601. #endif
  602. while (1) {
  603. int opt;
  604. int option_index = 0;
  605. opt = getopt_long(argc, argv, "*1hV", opts, &option_index);
  606. if (opt == -1)
  607. break;
  608. switch (opt) {
  609. case OPT_VERBOSE:
  610. verbose = true;
  611. break;
  612. case OPT_KEYMAP_FORMAT:
  613. /*
  614. * This is either:
  615. * - the input format for the interactive tool
  616. * - the output format for the dump tool
  617. */
  618. keymap_format = xkb_keymap_parse_format(optarg);
  619. if (!keymap_format) {
  620. fprintf(stderr, "ERROR: invalid --format \"%s\"\n", optarg);
  621. goto invalid_usage;
  622. }
  623. break;
  624. case OPT_KEYMAP_STRICT_PARSER:
  625. compile_flags |= XKB_KEYMAP_COMPILE_STRICT_MODE;
  626. break;
  627. #ifdef KEYMAP_DUMP
  628. case OPT_KEYMAP_NO_PRETTY:
  629. serialize_flags &= ~XKB_KEYMAP_SERIALIZE_PRETTY;
  630. break;
  631. case OPT_KEYMAP_DROP_UNUSED:
  632. serialize_flags &= ~XKB_KEYMAP_SERIALIZE_KEEP_UNUSED;
  633. break;
  634. #else
  635. case OPT_COMPOSE:
  636. with_compose = true;
  637. break;
  638. case OPT_LOCAL_STATE:
  639. local_state:
  640. use_local_state = true;
  641. break;
  642. case OPT_LEGACY_STATE_API: {
  643. bool legacy_api = true;
  644. if (!tools_parse_bool(optarg, TOOLS_ARG_OPTIONAL, &legacy_api))
  645. goto invalid_usage;
  646. use_events_api = !legacy_api;
  647. if (use_events_api)
  648. goto local_state;
  649. break;
  650. }
  651. case OPT_CONTROLS:
  652. if (!tools_parse_controls(optarg, &machine_options))
  653. goto invalid_usage;
  654. /* --local-state and --legacy-state-api=false are implied */
  655. use_events_api = true;
  656. goto local_state;
  657. case OPT_MODIFIERS_TWEAK_MAPPING:
  658. if (!tools_parse_modifiers_mappings(optarg, &machine_options))
  659. goto invalid_usage;
  660. /* --local-state and --legacy-state-api=false are implied */
  661. use_events_api = true;
  662. goto local_state;
  663. case OPT_SHORTCUTS_TWEAK_MASK:
  664. if (!tools_parse_shortcuts_mask(optarg, &machine_options))
  665. goto invalid_usage;
  666. /* --local-state and --legacy-state-api=false are implied */
  667. use_events_api = true;
  668. goto local_state;
  669. case OPT_SHORTCUTS_TWEAK_MAPPING:
  670. if (!tools_parse_shortcuts_mappings(optarg, &machine_options))
  671. goto invalid_usage;
  672. /* --local-state and --legacy-state-api=false are implied */
  673. use_events_api = true;
  674. goto local_state;
  675. case OPT_KEYMAP:
  676. with_keymap_file = true;
  677. /* Optional arguments require `=`, but we want to make this
  678. * requirement optional too, so that both `--keymap=xxx` and
  679. * `--keymap xxx` work. */
  680. if (!optarg && argv[optind] &&
  681. (argv[optind][0] != '-' || strcmp(argv[optind], "-") == 0 )) {
  682. keymap_path = argv[optind++];
  683. } else {
  684. keymap_path = optarg;
  685. }
  686. /* --local-state is implied */
  687. goto local_state;
  688. case '1':
  689. case OPT_UNILINE:
  690. print_options |= PRINT_UNILINE;
  691. break;
  692. case '*':
  693. case OPT_MULTILINE:
  694. print_options &= ~PRINT_UNILINE;
  695. break;
  696. case OPT_CONSUMED_MODE:
  697. if (strcmp(optarg, "gtk") == 0) {
  698. consumed_mode = XKB_CONSUMED_MODE_GTK;
  699. } else if (strcmp(optarg, "xkb") == 0) {
  700. consumed_mode = XKB_CONSUMED_MODE_XKB;
  701. } else {
  702. fprintf(stderr, "ERROR: invalid --consumed-mode \"%s\"\n",
  703. optarg);
  704. usage(stderr, argv[0]);
  705. ret = EXIT_INVALID_USAGE;
  706. goto error_parse_args;
  707. }
  708. break;
  709. case OPT_NO_STATE_REPORT:
  710. report_state_changes = false;
  711. break;
  712. #endif
  713. case 'h':
  714. usage(stdout, argv[0]);
  715. ret = EXIT_SUCCESS;
  716. goto error_parse_args;
  717. case 'V':
  718. printf("%s\n", LIBXKBCOMMON_VERSION);
  719. ret = EXIT_SUCCESS;
  720. goto error_parse_args;
  721. default:
  722. invalid_usage:
  723. usage(stderr, argv[0]);
  724. ret = EXIT_INVALID_USAGE;
  725. goto error_parse_args;
  726. }
  727. }
  728. #ifndef KEYMAP_DUMP
  729. if (optind < argc && !isempty(argv[optind])) {
  730. /* Some positional arguments left: use as a keymap input */
  731. if (keymap_path)
  732. goto too_much_arguments;
  733. keymap_path = argv[optind++];
  734. if (optind < argc) {
  735. /* Further positional arguments is an error */
  736. too_much_arguments:
  737. fprintf(stderr, "ERROR: Too many positional arguments\n");
  738. goto invalid_usage;
  739. }
  740. with_keymap_file = true;
  741. } else if (is_pipe_or_regular_file(STDIN_FILENO) && !with_keymap_file) {
  742. /* No positional argument: piping detected */
  743. with_keymap_file = true;
  744. }
  745. if (with_keymap_file) {
  746. /* --local-state is implied with custom keymap */
  747. use_local_state = true;
  748. }
  749. if (isempty(keymap_path) || strcmp(keymap_path, "-") == 0)
  750. keymap_path = NULL;
  751. #endif
  752. conn = xcb_connect(NULL, NULL);
  753. if (!conn || xcb_connection_has_error(conn)) {
  754. fprintf(stderr, "Couldn't connect to X server: error code %d\n",
  755. conn ? xcb_connection_has_error(conn) : -1);
  756. ret = -1;
  757. goto err_out;
  758. }
  759. ret = xkb_x11_setup_xkb_extension(conn,
  760. XKB_X11_MIN_MAJOR_XKB_VERSION,
  761. XKB_X11_MIN_MINOR_XKB_VERSION,
  762. XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS,
  763. NULL, NULL, &first_xkb_event, NULL);
  764. if (!ret) {
  765. fprintf(stderr, "Couldn't setup XKB extension\n");
  766. goto err_conn;
  767. }
  768. #ifndef KEYMAP_DUMP
  769. /*
  770. * Enable detection of repeated keys
  771. *
  772. * The X server usually generates both press and release events whenever an
  773. * autorepeating key is held down. With the following change, client will
  774. * receive: Press → Press → Press → Release
  775. * instead of: Press → Release → Press → Release → Press → Release
  776. *
  777. * See: https://www.x.org/releases/X11R7.7/doc/kbproto/xkbproto.html#Detectable_Autorepeat
  778. */
  779. xcb_xkb_per_client_flags_cookie_t pcf = xcb_xkb_per_client_flags(
  780. conn, XCB_XKB_ID_USE_CORE_KBD,
  781. XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT,
  782. XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT,
  783. 0, 0, 0
  784. );
  785. xcb_xkb_per_client_flags_reply_t *pcf_reply;
  786. if (!(pcf_reply = xcb_xkb_per_client_flags_reply(conn, pcf, NULL))) {
  787. fprintf(stderr, "ERROR: Couldn't set X11 detectable auto-repeat\n");
  788. } else {
  789. detect_repeat =
  790. (pcf_reply->value & XCB_XKB_PER_CLIENT_FLAG_DETECTABLE_AUTO_REPEAT);
  791. if (!detect_repeat)
  792. fprintf(stderr,
  793. "WARNING: Detectable auto-repeat is not supported by "
  794. "the X server\n");
  795. free(pcf_reply);
  796. }
  797. #endif
  798. ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
  799. if (!ctx) {
  800. ret = -1;
  801. fprintf(stderr, "Couldn't create xkb context\n");
  802. goto err_conn;
  803. }
  804. if (verbose)
  805. tools_enable_verbose_logging(ctx);
  806. #ifndef KEYMAP_DUMP
  807. if (with_keymap_file) {
  808. FILE *file = NULL;
  809. if (keymap_path) {
  810. /* Read from regular file */
  811. file = fopen(keymap_path, "rb");
  812. } else {
  813. /* Read from stdin */
  814. file = tools_read_stdin();
  815. }
  816. if (!file) {
  817. fprintf(stderr, "ERROR: Failed to open keymap file \"%s\": %s\n",
  818. keymap_path ? keymap_path : "stdin", strerror(errno));
  819. xkb_context_unref(ctx);
  820. goto err_out;
  821. }
  822. custom_keymap = xkb_keymap_new_from_file(ctx, file, keymap_format,
  823. compile_flags);
  824. if (!custom_keymap)
  825. goto err_keymap;
  826. fclose(file);
  827. }
  828. if (with_compose) {
  829. locale = setlocale(LC_CTYPE, NULL);
  830. compose_table =
  831. xkb_compose_table_new_from_locale(ctx, locale,
  832. XKB_COMPOSE_COMPILE_NO_FLAGS);
  833. if (!compose_table) {
  834. fprintf(stderr, "Couldn't create compose from locale\n");
  835. goto err_compose;
  836. }
  837. } else {
  838. compose_table = NULL;
  839. }
  840. #endif
  841. core_kbd_device_id = xkb_x11_get_core_keyboard_device_id(conn);
  842. if (core_kbd_device_id == -1) {
  843. ret = -1;
  844. fprintf(stderr, "Couldn't find core keyboard device\n");
  845. goto err_ctx;
  846. }
  847. ret = init_kbd(&core_kbd, conn, first_xkb_event, core_kbd_device_id,
  848. ctx, compose_table);
  849. if (ret) {
  850. fprintf(stderr, "Couldn't initialize core keyboard device\n");
  851. goto err_ctx;
  852. }
  853. #ifndef KEYMAP_DUMP
  854. ret = create_capture_window(conn);
  855. if (ret) {
  856. fprintf(stderr, "Couldn't create a capture window\n");
  857. goto err_window;
  858. }
  859. tools_disable_stdin_echo();
  860. ret = loop(conn, &core_kbd);
  861. tools_enable_stdin_echo();
  862. err_window:
  863. xkb_compose_table_unref(compose_table);
  864. err_compose:
  865. xkb_keymap_unref(custom_keymap);
  866. err_keymap:
  867. #endif
  868. deinit_kbd(&core_kbd);
  869. err_ctx:
  870. xkb_context_unref(ctx);
  871. err_conn:
  872. xcb_disconnect(conn);
  873. err_out:
  874. ret = (ret >= 0) ? EXIT_SUCCESS : EXIT_FAILURE;
  875. error_parse_args:
  876. #ifndef KEYMAP_DUMP
  877. xkb_machine_options_free(&machine_options);
  878. #endif
  879. exit(ret);
  880. }