tools: move the remaining tools from test to here
[platform/upstream/libxkbcommon.git] / tools / interactive-x11.c
1 /*
2  * Copyright © 2013 Ran Benita <ran234@gmail.com>
3  *
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:
10  *
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
13  * Software.
14  *
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.
22  */
23
24 #include "config.h"
25
26 #include <locale.h>
27
28 #include <xcb/xkb.h>
29
30 #include "xkbcommon/xkbcommon-x11.h"
31 #include "tools-common.h"
32
33 /*
34  * Note: This program only handles the core keyboard device for now.
35  * It should be straigtforward to change struct keyboard to a list of
36  * keyboards with device IDs, as in tools/interactive-evdev.c. This would
37  * require:
38  *
39  * - Initially listing the keyboard devices.
40  * - Listening to device changes.
41  * - Matching events to their devices.
42  *
43  * XKB itself knows about xinput1 devices, and most requests and events are
44  * device-specific.
45  *
46  * In order to list the devices and react to changes, you need xinput1/2.
47  * You also need xinput for the key press/release event, since the core
48  * protocol key press event does not carry a device ID to match on.
49  */
50
51 struct keyboard {
52     xcb_connection_t *conn;
53     uint8_t first_xkb_event;
54     struct xkb_context *ctx;
55
56     struct xkb_keymap *keymap;
57     struct xkb_state *state;
58     int32_t device_id;
59 };
60
61 static bool terminate;
62
63 static int
64 select_xkb_events_for_device(xcb_connection_t *conn, int32_t device_id)
65 {
66     enum {
67         required_events =
68             (XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY |
69              XCB_XKB_EVENT_TYPE_MAP_NOTIFY |
70              XCB_XKB_EVENT_TYPE_STATE_NOTIFY),
71
72         required_nkn_details =
73             (XCB_XKB_NKN_DETAIL_KEYCODES),
74
75         required_map_parts =
76             (XCB_XKB_MAP_PART_KEY_TYPES |
77              XCB_XKB_MAP_PART_KEY_SYMS |
78              XCB_XKB_MAP_PART_MODIFIER_MAP |
79              XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS |
80              XCB_XKB_MAP_PART_KEY_ACTIONS |
81              XCB_XKB_MAP_PART_VIRTUAL_MODS |
82              XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP),
83
84         required_state_details =
85             (XCB_XKB_STATE_PART_MODIFIER_BASE |
86              XCB_XKB_STATE_PART_MODIFIER_LATCH |
87              XCB_XKB_STATE_PART_MODIFIER_LOCK |
88              XCB_XKB_STATE_PART_GROUP_BASE |
89              XCB_XKB_STATE_PART_GROUP_LATCH |
90              XCB_XKB_STATE_PART_GROUP_LOCK),
91     };
92
93     static const xcb_xkb_select_events_details_t details = {
94         .affectNewKeyboard = required_nkn_details,
95         .newKeyboardDetails = required_nkn_details,
96         .affectState = required_state_details,
97         .stateDetails = required_state_details,
98     };
99
100     xcb_void_cookie_t cookie =
101         xcb_xkb_select_events_aux_checked(conn,
102                                           device_id,
103                                           required_events,    /* affectWhich */
104                                           0,                  /* clear */
105                                           0,                  /* selectAll */
106                                           required_map_parts, /* affectMap */
107                                           required_map_parts, /* map */
108                                           &details);          /* details */
109
110     xcb_generic_error_t *error = xcb_request_check(conn, cookie);
111     if (error) {
112         free(error);
113         return -1;
114     }
115
116     return 0;
117 }
118
119 static int
120 update_keymap(struct keyboard *kbd)
121 {
122     struct xkb_keymap *new_keymap;
123     struct xkb_state *new_state;
124
125     new_keymap = xkb_x11_keymap_new_from_device(kbd->ctx, kbd->conn,
126                                                 kbd->device_id,
127                                                 XKB_KEYMAP_COMPILE_NO_FLAGS);
128     if (!new_keymap)
129         goto err_out;
130
131     new_state = xkb_x11_state_new_from_device(new_keymap, kbd->conn,
132                                               kbd->device_id);
133     if (!new_state)
134         goto err_keymap;
135
136     if (kbd->keymap)
137         printf("Keymap updated!\n");
138
139     xkb_state_unref(kbd->state);
140     xkb_keymap_unref(kbd->keymap);
141     kbd->keymap = new_keymap;
142     kbd->state = new_state;
143     return 0;
144
145 err_keymap:
146     xkb_keymap_unref(new_keymap);
147 err_out:
148     return -1;
149 }
150
151 static int
152 init_kbd(struct keyboard *kbd, xcb_connection_t *conn, uint8_t first_xkb_event,
153          int32_t device_id, struct xkb_context *ctx)
154 {
155     int ret;
156
157     kbd->conn = conn;
158     kbd->first_xkb_event = first_xkb_event;
159     kbd->ctx = ctx;
160     kbd->keymap = NULL;
161     kbd->state = NULL;
162     kbd->device_id = device_id;
163
164     ret = update_keymap(kbd);
165     if (ret)
166         goto err_out;
167
168     ret = select_xkb_events_for_device(conn, device_id);
169     if (ret)
170         goto err_state;
171
172     return 0;
173
174 err_state:
175     xkb_state_unref(kbd->state);
176     xkb_keymap_unref(kbd->keymap);
177 err_out:
178     return -1;
179 }
180
181 static void
182 deinit_kbd(struct keyboard *kbd)
183 {
184     xkb_state_unref(kbd->state);
185     xkb_keymap_unref(kbd->keymap);
186 }
187
188 static void
189 process_xkb_event(xcb_generic_event_t *gevent, struct keyboard *kbd)
190 {
191     union xkb_event {
192         struct {
193             uint8_t response_type;
194             uint8_t xkbType;
195             uint16_t sequence;
196             xcb_timestamp_t time;
197             uint8_t deviceID;
198         } any;
199         xcb_xkb_new_keyboard_notify_event_t new_keyboard_notify;
200         xcb_xkb_map_notify_event_t map_notify;
201         xcb_xkb_state_notify_event_t state_notify;
202     } *event = (union xkb_event *) gevent;
203
204     if (event->any.deviceID != kbd->device_id)
205         return;
206
207     /*
208      * XkbNewKkdNotify and XkbMapNotify together capture all sorts of keymap
209      * updates (e.g. xmodmap, xkbcomp, setxkbmap), with minimal redundent
210      * recompilations.
211      */
212     switch (event->any.xkbType) {
213     case XCB_XKB_NEW_KEYBOARD_NOTIFY:
214         if (event->new_keyboard_notify.changed & XCB_XKB_NKN_DETAIL_KEYCODES)
215             update_keymap(kbd);
216         break;
217
218     case XCB_XKB_MAP_NOTIFY:
219         update_keymap(kbd);
220         break;
221
222     case XCB_XKB_STATE_NOTIFY:
223         xkb_state_update_mask(kbd->state,
224                               event->state_notify.baseMods,
225                               event->state_notify.latchedMods,
226                               event->state_notify.lockedMods,
227                               event->state_notify.baseGroup,
228                               event->state_notify.latchedGroup,
229                               event->state_notify.lockedGroup);
230         break;
231     }
232 }
233
234 static void
235 process_event(xcb_generic_event_t *gevent, struct keyboard *kbd)
236 {
237     switch (gevent->response_type) {
238     case XCB_KEY_PRESS: {
239         xcb_key_press_event_t *event = (xcb_key_press_event_t *) gevent;
240         xkb_keycode_t keycode = event->detail;
241
242         tools_print_keycode_state(kbd->state, NULL, keycode,
243                                   XKB_CONSUMED_MODE_XKB);
244
245         /* Exit on ESC. */
246         if (keycode == 9)
247             terminate = true;
248         break;
249     }
250     default:
251         if (gevent->response_type == kbd->first_xkb_event)
252             process_xkb_event(gevent, kbd);
253         break;
254     }
255 }
256
257 static int
258 loop(xcb_connection_t *conn, struct keyboard *kbd)
259 {
260     while (!terminate) {
261         xcb_generic_event_t *event;
262
263         switch (xcb_connection_has_error(conn)) {
264         case 0:
265             break;
266         case XCB_CONN_ERROR:
267             fprintf(stderr,
268                     "Closed connection to X server: connection error\n");
269             return -1;
270         case XCB_CONN_CLOSED_EXT_NOTSUPPORTED:
271             fprintf(stderr,
272                     "Closed connection to X server: extension not supported\n");
273             return -1;
274         default:
275             fprintf(stderr,
276                     "Closed connection to X server: error code %d\n",
277                     xcb_connection_has_error(conn));
278             return -1;
279         }
280
281         event = xcb_wait_for_event(conn);
282         if (!event) {
283             continue;
284         }
285
286         process_event(event, kbd);
287         free(event);
288     }
289
290     return 0;
291 }
292
293 static int
294 create_capture_window(xcb_connection_t *conn)
295 {
296     xcb_generic_error_t *error;
297     xcb_void_cookie_t cookie;
298     xcb_screen_t *screen =
299         xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
300     xcb_window_t window = xcb_generate_id(conn);
301     uint32_t values[2] = {
302         screen->white_pixel,
303         XCB_EVENT_MASK_KEY_PRESS,
304     };
305
306     cookie = xcb_create_window_checked(conn, XCB_COPY_FROM_PARENT,
307                                        window, screen->root,
308                                        10, 10, 100, 100, 1,
309                                        XCB_WINDOW_CLASS_INPUT_OUTPUT,
310                                        screen->root_visual,
311                                        XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK,
312                                        values);
313     if ((error = xcb_request_check(conn, cookie)) != NULL) {
314         free(error);
315         return -1;
316     }
317
318     cookie = xcb_map_window_checked(conn, window);
319     if ((error = xcb_request_check(conn, cookie)) != NULL) {
320         free(error);
321         return -1;
322     }
323
324     return 0;
325 }
326
327 int
328 main(int argc, char *argv[])
329 {
330     int ret;
331     xcb_connection_t *conn;
332     uint8_t first_xkb_event;
333     int32_t core_kbd_device_id;
334     struct xkb_context *ctx;
335     struct keyboard core_kbd;
336
337     setlocale(LC_ALL, "");
338
339     conn = xcb_connect(NULL, NULL);
340     if (!conn || xcb_connection_has_error(conn)) {
341         fprintf(stderr, "Couldn't connect to X server: error code %d\n",
342                 conn ? xcb_connection_has_error(conn) : -1);
343         ret = -1;
344         goto err_out;
345     }
346
347     ret = xkb_x11_setup_xkb_extension(conn,
348                                       XKB_X11_MIN_MAJOR_XKB_VERSION,
349                                       XKB_X11_MIN_MINOR_XKB_VERSION,
350                                       XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS,
351                                       NULL, NULL, &first_xkb_event, NULL);
352     if (!ret) {
353         fprintf(stderr, "Couldn't setup XKB extension\n");
354         goto err_conn;
355     }
356
357     ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
358     if (!ctx) {
359         ret = -1;
360         fprintf(stderr, "Couldn't create xkb context\n");
361         goto err_conn;
362     }
363
364     core_kbd_device_id = xkb_x11_get_core_keyboard_device_id(conn);
365     if (core_kbd_device_id == -1) {
366         ret = -1;
367         fprintf(stderr, "Couldn't find core keyboard device\n");
368         goto err_ctx;
369     }
370
371     ret = init_kbd(&core_kbd, conn, first_xkb_event, core_kbd_device_id, ctx);
372     if (ret) {
373         fprintf(stderr, "Couldn't initialize core keyboard device\n");
374         goto err_ctx;
375     }
376
377     ret = create_capture_window(conn);
378     if (ret) {
379         fprintf(stderr, "Couldn't create a capture window\n");
380         goto err_core_kbd;
381     }
382
383     tools_disable_stdin_echo();
384     ret = loop(conn, &core_kbd);
385     tools_enable_stdin_echo();
386
387 err_core_kbd:
388     deinit_kbd(&core_kbd);
389 err_ctx:
390     xkb_context_unref(ctx);
391 err_conn:
392     xcb_disconnect(conn);
393 err_out:
394     exit(ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
395 }