2 * Copyright © 2013 Ran Benita <ran234@gmail.com>
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
32 #include "xkbcommon/xkbcommon-x11.h"
33 #include "tools-common.h"
36 * Note: This program only handles the core keyboard device for now.
37 * It should be straigtforward to change struct keyboard to a list of
38 * keyboards with device IDs, as in tools/interactive-evdev.c. This would
41 * - Initially listing the keyboard devices.
42 * - Listening to device changes.
43 * - Matching events to their devices.
45 * XKB itself knows about xinput1 devices, and most requests and events are
48 * In order to list the devices and react to changes, you need xinput1/2.
49 * You also need xinput for the key press/release event, since the core
50 * protocol key press event does not carry a device ID to match on.
54 xcb_connection_t *conn;
55 uint8_t first_xkb_event;
56 struct xkb_context *ctx;
58 struct xkb_keymap *keymap;
59 struct xkb_state *state;
63 static bool terminate;
66 select_xkb_events_for_device(xcb_connection_t *conn, int32_t device_id)
70 (XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY |
71 XCB_XKB_EVENT_TYPE_MAP_NOTIFY |
72 XCB_XKB_EVENT_TYPE_STATE_NOTIFY),
74 required_nkn_details =
75 (XCB_XKB_NKN_DETAIL_KEYCODES),
78 (XCB_XKB_MAP_PART_KEY_TYPES |
79 XCB_XKB_MAP_PART_KEY_SYMS |
80 XCB_XKB_MAP_PART_MODIFIER_MAP |
81 XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS |
82 XCB_XKB_MAP_PART_KEY_ACTIONS |
83 XCB_XKB_MAP_PART_VIRTUAL_MODS |
84 XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP),
86 required_state_details =
87 (XCB_XKB_STATE_PART_MODIFIER_BASE |
88 XCB_XKB_STATE_PART_MODIFIER_LATCH |
89 XCB_XKB_STATE_PART_MODIFIER_LOCK |
90 XCB_XKB_STATE_PART_GROUP_BASE |
91 XCB_XKB_STATE_PART_GROUP_LATCH |
92 XCB_XKB_STATE_PART_GROUP_LOCK),
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,
102 xcb_void_cookie_t cookie =
103 xcb_xkb_select_events_aux_checked(conn,
105 required_events, /* affectWhich */
108 required_map_parts, /* affectMap */
109 required_map_parts, /* map */
110 &details); /* details */
112 xcb_generic_error_t *error = xcb_request_check(conn, cookie);
122 update_keymap(struct keyboard *kbd)
124 struct xkb_keymap *new_keymap;
125 struct xkb_state *new_state;
127 new_keymap = xkb_x11_keymap_new_from_device(kbd->ctx, kbd->conn,
129 XKB_KEYMAP_COMPILE_NO_FLAGS);
133 new_state = xkb_x11_state_new_from_device(new_keymap, kbd->conn,
139 printf("Keymap updated!\n");
141 xkb_state_unref(kbd->state);
142 xkb_keymap_unref(kbd->keymap);
143 kbd->keymap = new_keymap;
144 kbd->state = new_state;
148 xkb_keymap_unref(new_keymap);
154 init_kbd(struct keyboard *kbd, xcb_connection_t *conn, uint8_t first_xkb_event,
155 int32_t device_id, struct xkb_context *ctx)
160 kbd->first_xkb_event = first_xkb_event;
164 kbd->device_id = device_id;
166 ret = update_keymap(kbd);
170 ret = select_xkb_events_for_device(conn, device_id);
177 xkb_state_unref(kbd->state);
178 xkb_keymap_unref(kbd->keymap);
184 deinit_kbd(struct keyboard *kbd)
186 xkb_state_unref(kbd->state);
187 xkb_keymap_unref(kbd->keymap);
191 process_xkb_event(xcb_generic_event_t *gevent, struct keyboard *kbd)
195 uint8_t response_type;
198 xcb_timestamp_t time;
201 xcb_xkb_new_keyboard_notify_event_t new_keyboard_notify;
202 xcb_xkb_map_notify_event_t map_notify;
203 xcb_xkb_state_notify_event_t state_notify;
204 } *event = (union xkb_event *) gevent;
206 if (event->any.deviceID != kbd->device_id)
210 * XkbNewKkdNotify and XkbMapNotify together capture all sorts of keymap
211 * updates (e.g. xmodmap, xkbcomp, setxkbmap), with minimal redundent
214 switch (event->any.xkbType) {
215 case XCB_XKB_NEW_KEYBOARD_NOTIFY:
216 if (event->new_keyboard_notify.changed & XCB_XKB_NKN_DETAIL_KEYCODES)
220 case XCB_XKB_MAP_NOTIFY:
224 case XCB_XKB_STATE_NOTIFY:
225 xkb_state_update_mask(kbd->state,
226 event->state_notify.baseMods,
227 event->state_notify.latchedMods,
228 event->state_notify.lockedMods,
229 event->state_notify.baseGroup,
230 event->state_notify.latchedGroup,
231 event->state_notify.lockedGroup);
237 process_event(xcb_generic_event_t *gevent, struct keyboard *kbd)
239 switch (gevent->response_type) {
240 case XCB_KEY_PRESS: {
241 xcb_key_press_event_t *event = (xcb_key_press_event_t *) gevent;
242 xkb_keycode_t keycode = event->detail;
244 tools_print_keycode_state(kbd->state, NULL, keycode,
245 XKB_CONSUMED_MODE_XKB);
253 if (gevent->response_type == kbd->first_xkb_event)
254 process_xkb_event(gevent, kbd);
260 loop(xcb_connection_t *conn, struct keyboard *kbd)
263 xcb_generic_event_t *event;
265 switch (xcb_connection_has_error(conn)) {
270 "Closed connection to X server: connection error\n");
272 case XCB_CONN_CLOSED_EXT_NOTSUPPORTED:
274 "Closed connection to X server: extension not supported\n");
278 "Closed connection to X server: error code %d\n",
279 xcb_connection_has_error(conn));
283 event = xcb_wait_for_event(conn);
288 process_event(event, kbd);
296 create_capture_window(xcb_connection_t *conn)
298 xcb_generic_error_t *error;
299 xcb_void_cookie_t cookie;
300 xcb_screen_t *screen =
301 xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
302 xcb_window_t window = xcb_generate_id(conn);
303 uint32_t values[2] = {
305 XCB_EVENT_MASK_KEY_PRESS,
308 cookie = xcb_create_window_checked(conn, XCB_COPY_FROM_PARENT,
309 window, screen->root,
311 XCB_WINDOW_CLASS_INPUT_OUTPUT,
313 XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK,
315 if ((error = xcb_request_check(conn, cookie)) != NULL) {
320 cookie = xcb_map_window_checked(conn, window);
321 if ((error = xcb_request_check(conn, cookie)) != NULL) {
330 main(int argc, char *argv[])
333 xcb_connection_t *conn;
334 uint8_t first_xkb_event;
335 int32_t core_kbd_device_id;
336 struct xkb_context *ctx;
337 struct keyboard core_kbd;
339 setlocale(LC_ALL, "");
341 conn = xcb_connect(NULL, NULL);
342 if (!conn || xcb_connection_has_error(conn)) {
343 fprintf(stderr, "Couldn't connect to X server: error code %d\n",
344 conn ? xcb_connection_has_error(conn) : -1);
349 ret = xkb_x11_setup_xkb_extension(conn,
350 XKB_X11_MIN_MAJOR_XKB_VERSION,
351 XKB_X11_MIN_MINOR_XKB_VERSION,
352 XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS,
353 NULL, NULL, &first_xkb_event, NULL);
355 fprintf(stderr, "Couldn't setup XKB extension\n");
359 ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
362 fprintf(stderr, "Couldn't create xkb context\n");
366 core_kbd_device_id = xkb_x11_get_core_keyboard_device_id(conn);
367 if (core_kbd_device_id == -1) {
369 fprintf(stderr, "Couldn't find core keyboard device\n");
373 ret = init_kbd(&core_kbd, conn, first_xkb_event, core_kbd_device_id, ctx);
375 fprintf(stderr, "Couldn't initialize core keyboard device\n");
379 ret = create_capture_window(conn);
381 fprintf(stderr, "Couldn't create a capture window\n");
385 tools_disable_stdin_echo();
386 ret = loop(conn, &core_kbd);
387 tools_enable_stdin_echo();
390 deinit_kbd(&core_kbd);
392 xkb_context_unref(ctx);
394 xcb_disconnect(conn);
396 exit(ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE);