]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-terminal/idev-keyboard.c
terminal: free xkb state on keyboard destruction
[thirdparty/systemd.git] / src / libsystemd-terminal / idev-keyboard.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <inttypes.h>
23 #include <stdbool.h>
24 #include <stdlib.h>
25 #include <systemd/sd-bus.h>
26 #include <systemd/sd-event.h>
27 #include <xkbcommon/xkbcommon.h>
28 #include "bus-util.h"
29 #include "hashmap.h"
30 #include "idev.h"
31 #include "idev-internal.h"
32 #include "macro.h"
33 #include "util.h"
34
35 typedef struct kbdmap kbdmap;
36 typedef struct kbdctx kbdctx;
37 typedef struct idev_keyboard idev_keyboard;
38
39 struct kbdmap {
40 unsigned long ref;
41 struct xkb_keymap *xkb_keymap;
42 xkb_mod_index_t modmap[IDEV_KBDMOD_CNT];
43 xkb_led_index_t ledmap[IDEV_KBDLED_CNT];
44 };
45
46 struct kbdctx {
47 unsigned long ref;
48 idev_context *context;
49 struct xkb_context *xkb_context;
50 struct kbdmap *kbdmap;
51
52 sd_bus_slot *slot_locale_props_changed;
53 sd_bus_slot *slot_locale_get_all;
54
55 char *locale_x11_model;
56 char *locale_x11_layout;
57 char *locale_x11_variant;
58 char *locale_x11_options;
59 char *last_x11_model;
60 char *last_x11_layout;
61 char *last_x11_variant;
62 char *last_x11_options;
63 };
64
65 struct idev_keyboard {
66 idev_device device;
67 kbdctx *kbdctx;
68 kbdmap *kbdmap;
69
70 struct xkb_state *xkb_state;
71
72 usec_t repeat_delay;
73 usec_t repeat_rate;
74 sd_event_source *repeat_timer;
75
76 uint32_t n_syms;
77 idev_data evdata;
78 idev_data repdata;
79
80 bool repeating : 1;
81 };
82
83 #define keyboard_from_device(_d) container_of((_d), idev_keyboard, device)
84
85 #define KBDCTX_KEY "keyboard.context" /* hashmap key for global kbdctx */
86 #define KBDXKB_SHIFT (8) /* xkb shifts evdev key-codes by 8 */
87 #define KBDKEY_UP (0) /* KEY UP event value */
88 #define KBDKEY_DOWN (1) /* KEY DOWN event value */
89 #define KBDKEY_REPEAT (2) /* KEY REPEAT event value */
90
91 static const idev_device_vtable keyboard_vtable;
92
93 static int keyboard_update_kbdmap(idev_keyboard *k);
94
95 /*
96 * Keyboard Keymaps
97 */
98
99 static const char * const kbdmap_modmap[IDEV_KBDMOD_CNT] = {
100 [IDEV_KBDMOD_IDX_SHIFT] = XKB_MOD_NAME_SHIFT,
101 [IDEV_KBDMOD_IDX_CTRL] = XKB_MOD_NAME_CTRL,
102 [IDEV_KBDMOD_IDX_ALT] = XKB_MOD_NAME_ALT,
103 [IDEV_KBDMOD_IDX_LINUX] = XKB_MOD_NAME_LOGO,
104 [IDEV_KBDMOD_IDX_CAPS] = XKB_MOD_NAME_CAPS,
105 };
106
107 static const char * const kbdmap_ledmap[IDEV_KBDLED_CNT] = {
108 [IDEV_KBDLED_IDX_NUM] = XKB_LED_NAME_NUM,
109 [IDEV_KBDLED_IDX_CAPS] = XKB_LED_NAME_CAPS,
110 [IDEV_KBDLED_IDX_SCROLL] = XKB_LED_NAME_SCROLL,
111 };
112
113 static kbdmap *kbdmap_ref(kbdmap *km) {
114 assert_return(km, NULL);
115 assert_return(km->ref > 0, NULL);
116
117 ++km->ref;
118 return km;
119 }
120
121 static kbdmap *kbdmap_unref(kbdmap *km) {
122 if (!km)
123 return NULL;
124
125 assert_return(km->ref > 0, NULL);
126
127 if (--km->ref > 0)
128 return NULL;
129
130 xkb_keymap_unref(km->xkb_keymap);
131 free(km);
132
133 return 0;
134 }
135
136 DEFINE_TRIVIAL_CLEANUP_FUNC(kbdmap*, kbdmap_unref);
137
138 static int kbdmap_new_from_names(kbdmap **out,
139 kbdctx *kc,
140 const char *model,
141 const char *layout,
142 const char *variant,
143 const char *options) {
144 _cleanup_(kbdmap_unrefp) kbdmap *km = NULL;
145 struct xkb_rule_names rmlvo = { };
146 unsigned int i;
147
148 assert_return(out, -EINVAL);
149
150 km = new0(kbdmap, 1);
151 if (!km)
152 return -ENOMEM;
153
154 km->ref = 1;
155
156 rmlvo.rules = "evdev";
157 rmlvo.model = model;
158 rmlvo.layout = layout;
159 rmlvo.variant = variant;
160 rmlvo.options = options;
161
162 errno = 0;
163 km->xkb_keymap = xkb_keymap_new_from_names(kc->xkb_context, &rmlvo, 0);
164 if (!km->xkb_keymap)
165 return errno > 0 ? -errno : -EFAULT;
166
167 for (i = 0; i < IDEV_KBDMOD_CNT; ++i) {
168 const char *t = kbdmap_modmap[i];
169
170 if (t)
171 km->modmap[i] = xkb_keymap_mod_get_index(km->xkb_keymap, t);
172 else
173 km->modmap[i] = XKB_MOD_INVALID;
174 }
175
176 for (i = 0; i < IDEV_KBDLED_CNT; ++i) {
177 const char *t = kbdmap_ledmap[i];
178
179 if (t)
180 km->ledmap[i] = xkb_keymap_led_get_index(km->xkb_keymap, t);
181 else
182 km->ledmap[i] = XKB_LED_INVALID;
183 }
184
185 *out = km;
186 km = NULL;
187 return 0;
188 }
189
190 /*
191 * Keyboard Context
192 */
193
194 static void move_str(char **dest, char **src) {
195 free(*dest);
196 *dest = *src;
197 *src = NULL;
198 }
199
200 static int kbdctx_refresh_keymap(kbdctx *kc) {
201 idev_session *s;
202 idev_device *d;
203 Iterator i, j;
204 kbdmap *km;
205 int r;
206
207 if (kc->kbdmap &&
208 streq_ptr(kc->locale_x11_model, kc->last_x11_model) &&
209 streq_ptr(kc->locale_x11_layout, kc->last_x11_layout) &&
210 streq_ptr(kc->locale_x11_variant, kc->last_x11_variant) &&
211 streq_ptr(kc->locale_x11_options, kc->last_x11_options))
212 return 0 ;
213
214 move_str(&kc->last_x11_model, &kc->locale_x11_model);
215 move_str(&kc->last_x11_layout, &kc->locale_x11_layout);
216 move_str(&kc->last_x11_variant, &kc->locale_x11_variant);
217 move_str(&kc->last_x11_options, &kc->locale_x11_options);
218
219 log_debug("idev-keyboard: new default keymap: [%s / %s / %s / %s]",
220 kc->last_x11_model, kc->last_x11_layout, kc->last_x11_variant, kc->last_x11_options);
221
222 /* TODO: add a fallback keymap that's compiled-in */
223 r = kbdmap_new_from_names(&km, kc, kc->last_x11_model, kc->last_x11_layout,
224 kc->last_x11_variant, kc->last_x11_options);
225 if (r < 0) {
226 log_debug("idev-keyboard: cannot create keymap from locale1: %s",
227 strerror(-r));
228 return r;
229 }
230
231 kbdmap_unref(kc->kbdmap);
232 kc->kbdmap = km;
233
234 HASHMAP_FOREACH(s, kc->context->session_map, i)
235 HASHMAP_FOREACH(d, s->device_map, j)
236 if (idev_is_keyboard(d))
237 keyboard_update_kbdmap(keyboard_from_device(d));
238
239 return 0;
240 }
241
242 static const struct bus_properties_map kbdctx_locale_map[] = {
243 { "X11Model", "s", NULL, offsetof(kbdctx, locale_x11_model) },
244 { "X11Layout", "s", NULL, offsetof(kbdctx, locale_x11_layout) },
245 { "X11Variant", "s", NULL, offsetof(kbdctx, locale_x11_variant) },
246 { "X11Options", "s", NULL, offsetof(kbdctx, locale_x11_options) },
247 };
248
249 static int kbdctx_locale_get_all_fn(sd_bus *bus,
250 sd_bus_message *m,
251 void *userdata,
252 sd_bus_error *ret_err) {
253 kbdctx *kc = userdata;
254 int r;
255
256 kc->slot_locale_get_all = sd_bus_slot_unref(kc->slot_locale_get_all);
257
258 if (sd_bus_message_is_method_error(m, NULL)) {
259 const sd_bus_error *error = sd_bus_message_get_error(m);
260
261 log_debug("idev-keyboard: GetAll() on locale1 failed: %s: %s",
262 error->name, error->message);
263 return 0;
264 }
265
266 r = bus_message_map_all_properties(bus, m, kbdctx_locale_map, kc);
267 if (r < 0) {
268 log_debug("idev-keyboard: erroneous GetAll() reply from locale1");
269 return 0;
270 }
271
272 kbdctx_refresh_keymap(kc);
273 return 0;
274 }
275
276 static int kbdctx_query_locale(kbdctx *kc) {
277 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
278 int r;
279
280 kc->slot_locale_get_all = sd_bus_slot_unref(kc->slot_locale_get_all);
281
282 r = sd_bus_message_new_method_call(kc->context->sysbus,
283 &m,
284 "org.freedesktop.locale1",
285 "/org/freedesktop/locale1",
286 "org.freedesktop.DBus.Properties",
287 "GetAll");
288 if (r < 0)
289 goto error;
290
291 r = sd_bus_message_append(m, "s", "org.freedesktop.locale1");
292 if (r < 0)
293 goto error;
294
295 r = sd_bus_call_async(kc->context->sysbus,
296 &kc->slot_locale_get_all,
297 m,
298 kbdctx_locale_get_all_fn,
299 kc,
300 0);
301 if (r < 0)
302 goto error;
303
304 return 0;
305
306 error:
307 log_debug("idev-keyboard: cannot send GetAll to locale1: %s", strerror(-r));
308 return r;
309 }
310
311 static int kbdctx_locale_props_changed_fn(sd_bus *bus,
312 sd_bus_message *signal,
313 void *userdata,
314 sd_bus_error *ret_err) {
315 kbdctx *kc = userdata;
316 int r;
317
318 kc->slot_locale_get_all = sd_bus_slot_unref(kc->slot_locale_get_all);
319
320 r = bus_message_map_properties_changed(bus, signal, kbdctx_locale_map, kc);
321 if (r < 0) {
322 log_debug("idev-keyboard: cannot handle PropertiesChanged from locale1: %s", strerror(-r));
323 return r;
324 }
325
326 if (r > 0) {
327 r = kbdctx_query_locale(kc);
328 if (r < 0)
329 return r;
330 }
331
332 kbdctx_refresh_keymap(kc);
333 return 0;
334 }
335
336 static int kbdctx_setup_bus(kbdctx *kc) {
337 int r;
338
339 r = sd_bus_add_match(kc->context->sysbus,
340 &kc->slot_locale_props_changed,
341 "type='signal',"
342 "sender='org.freedesktop.locale1',"
343 "interface='org.freedesktop.DBus.Properties',"
344 "member='PropertiesChanged',"
345 "path='/org/freedesktop/locale1'",
346 kbdctx_locale_props_changed_fn,
347 kc);
348 if (r < 0) {
349 log_debug("idev-keyboard: cannot setup locale1 link: %s", strerror(-r));
350 return r;
351 }
352
353 return kbdctx_query_locale(kc);
354 }
355
356 static kbdctx *kbdctx_ref(kbdctx *kc) {
357 assert_return(kc, NULL);
358 assert_return(kc->ref > 0, NULL);
359
360 ++kc->ref;
361 return kc;
362 }
363
364 static kbdctx *kbdctx_unref(kbdctx *kc) {
365 if (!kc)
366 return NULL;
367
368 assert_return(kc->ref > 0, NULL);
369
370 if (--kc->ref > 0)
371 return NULL;
372
373 free(kc->last_x11_options);
374 free(kc->last_x11_variant);
375 free(kc->last_x11_layout);
376 free(kc->last_x11_model);
377 free(kc->locale_x11_options);
378 free(kc->locale_x11_variant);
379 free(kc->locale_x11_layout);
380 free(kc->locale_x11_model);
381 kc->slot_locale_get_all = sd_bus_slot_unref(kc->slot_locale_get_all);
382 kc->slot_locale_props_changed = sd_bus_slot_unref(kc->slot_locale_props_changed);
383 kc->kbdmap = kbdmap_unref(kc->kbdmap);
384 xkb_context_unref(kc->xkb_context);
385 hashmap_remove_value(kc->context->data_map, KBDCTX_KEY, kc);
386 free(kc);
387
388 return NULL;
389 }
390
391 DEFINE_TRIVIAL_CLEANUP_FUNC(kbdctx*, kbdctx_unref);
392
393 static int kbdctx_new(kbdctx **out, idev_context *c) {
394 _cleanup_(kbdctx_unrefp) kbdctx *kc = NULL;
395 int r;
396
397 assert_return(out, -EINVAL);
398 assert_return(c, -EINVAL);
399
400 kc = new0(kbdctx, 1);
401 if (!kc)
402 return -ENOMEM;
403
404 kc->ref = 1;
405 kc->context = c;
406
407 errno = 0;
408 kc->xkb_context = xkb_context_new(0);
409 if (!kc->xkb_context)
410 return errno > 0 ? -errno : -EFAULT;
411
412 r = kbdctx_refresh_keymap(kc);
413 if (r < 0)
414 return r;
415
416 if (c->sysbus) {
417 r = kbdctx_setup_bus(kc);
418 if (r < 0)
419 return r;
420 }
421
422 r = hashmap_put(c->data_map, KBDCTX_KEY, kc);
423 if (r < 0)
424 return r;
425
426 *out = kc;
427 kc = NULL;
428 return 0;
429 }
430
431 static int get_kbdctx(idev_context *c, kbdctx **out) {
432 kbdctx *kc;
433
434 assert_return(c, -EINVAL);
435 assert_return(out, -EINVAL);
436
437 kc = hashmap_get(c->data_map, KBDCTX_KEY);
438 if (kc) {
439 *out = kbdctx_ref(kc);
440 return 0;
441 }
442
443 return kbdctx_new(out, c);
444 }
445
446 /*
447 * Keyboard Devices
448 */
449
450 bool idev_is_keyboard(idev_device *d) {
451 return d && d->vtable == &keyboard_vtable;
452 }
453
454 idev_device *idev_find_keyboard(idev_session *s, const char *name) {
455 char *kname;
456
457 assert_return(s, NULL);
458 assert_return(name, NULL);
459
460 kname = strappenda("keyboard/", name);
461 return hashmap_get(s->device_map, kname);
462 }
463
464 static int keyboard_raise_data(idev_keyboard *k, idev_data *data) {
465 idev_device *d = &k->device;
466 int r;
467
468 r = idev_session_raise_device_data(d->session, d, data);
469 if (r < 0)
470 log_debug("idev-keyboard: %s/%s: error while raising data event: %s",
471 d->session->name, d->name, strerror(-r));
472
473 return r;
474 }
475
476 static void keyboard_arm(idev_keyboard *k, usec_t usecs) {
477 int r;
478
479 if (usecs != 0) {
480 usecs += now(CLOCK_MONOTONIC);
481 r = sd_event_source_set_time(k->repeat_timer, usecs);
482 if (r >= 0)
483 sd_event_source_set_enabled(k->repeat_timer, SD_EVENT_ONESHOT);
484 } else {
485 sd_event_source_set_enabled(k->repeat_timer, SD_EVENT_OFF);
486 }
487 }
488
489 static int keyboard_repeat_timer_fn(sd_event_source *source, uint64_t usec, void *userdata) {
490 idev_keyboard *k = userdata;
491
492 keyboard_arm(k, k->repeat_rate);
493 return keyboard_raise_data(k, &k->repdata);
494 }
495
496 int idev_keyboard_new(idev_device **out, idev_session *s, const char *name) {
497 _cleanup_(idev_device_freep) idev_device *d = NULL;
498 idev_keyboard *k;
499 char *kname;
500 int r;
501
502 assert_return(out, -EINVAL);
503 assert_return(s, -EINVAL);
504 assert_return(name, -EINVAL);
505
506 k = new0(idev_keyboard, 1);
507 if (!k)
508 return -ENOMEM;
509
510 d = &k->device;
511 k->device = IDEV_DEVICE_INIT(&keyboard_vtable, s);
512 k->repeat_delay = 250 * USEC_PER_MSEC;
513 k->repeat_rate = 30 * USEC_PER_MSEC;
514
515 /* TODO: add key-repeat configuration */
516
517 r = get_kbdctx(s->context, &k->kbdctx);
518 if (r < 0)
519 return r;
520
521 r = keyboard_update_kbdmap(k);
522 if (r < 0)
523 return r;
524
525 r = sd_event_add_time(s->context->event,
526 &k->repeat_timer,
527 CLOCK_MONOTONIC,
528 0,
529 10 * USEC_PER_MSEC,
530 keyboard_repeat_timer_fn,
531 k);
532 if (r < 0)
533 return r;
534
535 r = sd_event_source_set_enabled(k->repeat_timer, SD_EVENT_OFF);
536 if (r < 0)
537 return r;
538
539 kname = strappenda("keyboard/", name);
540 r = idev_device_add(d, kname);
541 if (r < 0)
542 return r;
543
544 if (out)
545 *out = d;
546 d = NULL;
547 return 0;
548 }
549
550 static void keyboard_free(idev_device *d) {
551 idev_keyboard *k = keyboard_from_device(d);
552
553 xkb_state_unref(k->xkb_state);
554 free(k->repdata.keyboard.codepoints);
555 free(k->repdata.keyboard.keysyms);
556 free(k->evdata.keyboard.codepoints);
557 free(k->evdata.keyboard.keysyms);
558 k->repeat_timer = sd_event_source_unref(k->repeat_timer);
559 k->kbdmap = kbdmap_unref(k->kbdmap);
560 k->kbdctx = kbdctx_unref(k->kbdctx);
561 free(k);
562 }
563
564 static int8_t guess_ascii(struct xkb_state *state, uint32_t code, uint32_t n_syms, const uint32_t *syms) {
565 xkb_layout_index_t n_lo, lo;
566 xkb_level_index_t lv;
567 struct xkb_keymap *keymap;
568 const xkb_keysym_t *s;
569 int num;
570
571 if (n_syms == 1 && syms[0] < 128)
572 return syms[0];
573
574 keymap = xkb_state_get_keymap(state);
575 n_lo = xkb_keymap_num_layouts_for_key(keymap, code + KBDXKB_SHIFT);
576
577 for (lo = 0; lo < n_lo; ++lo) {
578 lv = xkb_state_key_get_level(state, code + KBDXKB_SHIFT, lo);
579 num = xkb_keymap_key_get_syms_by_level(keymap, code + KBDXKB_SHIFT, lo, lv, &s);
580 if (num == 1 && s[0] < 128)
581 return s[0];
582 }
583
584 return -1;
585 }
586
587 static int keyboard_fill(idev_keyboard *k,
588 idev_data *dst,
589 bool resync,
590 uint16_t code,
591 uint32_t value,
592 uint32_t n_syms,
593 const uint32_t *keysyms) {
594 idev_data_keyboard *kev;
595 uint32_t i;
596
597 assert(dst == &k->evdata || dst == &k->repdata);
598
599 if (n_syms > k->n_syms) {
600 uint32_t *t;
601
602 t = realloc(k->evdata.keyboard.keysyms, sizeof(*t) * n_syms);
603 if (!t)
604 return -ENOMEM;
605 k->evdata.keyboard.keysyms = t;
606
607 t = realloc(k->evdata.keyboard.codepoints, sizeof(*t) * n_syms);
608 if (!t)
609 return -ENOMEM;
610 k->evdata.keyboard.codepoints = t;
611
612 t = realloc(k->repdata.keyboard.keysyms, sizeof(*t) * n_syms);
613 if (!t)
614 return -ENOMEM;
615 k->repdata.keyboard.keysyms = t;
616
617 t = realloc(k->repdata.keyboard.codepoints, sizeof(*t) * n_syms);
618 if (!t)
619 return -ENOMEM;
620 k->repdata.keyboard.codepoints = t;
621
622 k->n_syms = n_syms;
623 }
624
625 dst->type = IDEV_DATA_KEYBOARD;
626 dst->resync = resync;
627 kev = &dst->keyboard;
628 kev->ascii = guess_ascii(k->xkb_state, code, n_syms, keysyms);
629 kev->value = value;
630 kev->keycode = code;
631 kev->mods = 0;
632 kev->consumed_mods = 0;
633 kev->n_syms = n_syms;
634 memcpy(kev->keysyms, keysyms, sizeof(*keysyms) * n_syms);
635
636 for (i = 0; i < n_syms; ++i) {
637 kev->codepoints[i] = xkb_keysym_to_utf32(keysyms[i]);
638 if (!kev->codepoints[i])
639 kev->codepoints[i] = 0xffffffffUL;
640 }
641
642 for (i = 0; i < IDEV_KBDMOD_CNT; ++i) {
643 int r;
644
645 if (k->kbdmap->modmap[i] == XKB_MOD_INVALID)
646 continue;
647
648 r = xkb_state_mod_index_is_active(k->xkb_state, k->kbdmap->modmap[i], XKB_STATE_MODS_EFFECTIVE);
649 if (r > 0)
650 kev->mods |= 1 << i;
651
652 r = xkb_state_mod_index_is_consumed(k->xkb_state, code + KBDXKB_SHIFT, k->kbdmap->modmap[i]);
653 if (r > 0)
654 kev->consumed_mods |= 1 << i;
655 }
656
657 return 0;
658 }
659
660 static void keyboard_repeat(idev_keyboard *k) {
661 idev_data *evdata = &k->evdata;
662 idev_data *repdata = &k->repdata;
663 idev_data_keyboard *evkbd = &evdata->keyboard;
664 idev_data_keyboard *repkbd = &repdata->keyboard;
665 const xkb_keysym_t *keysyms;
666 idev_device *d = &k->device;
667 bool repeats;
668 int r, num;
669
670 if (evdata->resync) {
671 /*
672 * We received a re-sync event. During re-sync, any number of
673 * key-events may have been lost and sync-events may be
674 * re-ordered. Always disable key-repeat for those events. Any
675 * following event will trigger it again.
676 */
677
678 k->repeating = false;
679 keyboard_arm(k, 0);
680 return;
681 }
682
683 repeats = xkb_keymap_key_repeats(k->kbdmap->xkb_keymap, evkbd->keycode + KBDXKB_SHIFT);
684
685 if (k->repeating && repkbd->keycode == evkbd->keycode) {
686 /*
687 * We received an event for the key we currently repeat. If it
688 * was released, stop key-repeat. Otherwise, ignore the event.
689 */
690
691 if (evkbd->value == KBDKEY_UP) {
692 k->repeating = false;
693 keyboard_arm(k, 0);
694 }
695 } else if (evkbd->value == KBDKEY_DOWN && repeats) {
696 /*
697 * We received a key-down event for a key that repeats. The
698 * previous condition caught keys we already repeat, so we know
699 * this is a different key or no key-repeat is running. Start
700 * new key-repeat.
701 */
702
703 errno = 0;
704 num = xkb_state_key_get_syms(k->xkb_state, evkbd->keycode + KBDXKB_SHIFT, &keysyms);
705 if (num < 0)
706 r = errno > 0 ? errno : -EFAULT;
707 else
708 r = keyboard_fill(k, repdata, false, evkbd->keycode, KBDKEY_REPEAT, num, keysyms);
709
710 if (r < 0) {
711 log_debug("idev-keyboard: %s/%s: cannot set key-repeat: %s",
712 d->session->name, d->name, strerror(-r));
713 k->repeating = false;
714 keyboard_arm(k, 0);
715 } else {
716 k->repeating = true;
717 keyboard_arm(k, k->repeat_delay);
718 }
719 } else if (k->repeating && !repeats) {
720 /*
721 * We received an event for a key that does not repeat, but we
722 * currently repeat a previously received key. The new key is
723 * usually a modifier, but might be any kind of key. In this
724 * case, we continue repeating the old key, but update the
725 * symbols according to the new state.
726 */
727
728 errno = 0;
729 num = xkb_state_key_get_syms(k->xkb_state, repkbd->keycode + KBDXKB_SHIFT, &keysyms);
730 if (num < 0)
731 r = errno > 0 ? errno : -EFAULT;
732 else
733 r = keyboard_fill(k, repdata, false, repkbd->keycode, KBDKEY_REPEAT, num, keysyms);
734
735 if (r < 0) {
736 log_debug("idev-keyboard: %s/%s: cannot update key-repeat: %s",
737 d->session->name, d->name, strerror(-r));
738 k->repeating = false;
739 keyboard_arm(k, 0);
740 }
741 }
742 }
743
744 static int keyboard_feed_evdev(idev_keyboard *k, idev_data *data) {
745 struct input_event *ev = &data->evdev.event;
746 enum xkb_state_component compch;
747 const xkb_keysym_t *keysyms;
748 idev_device *d = &k->device;
749 int num, r;
750
751 if (ev->type != EV_KEY || ev->value > KBDKEY_DOWN)
752 return 0;
753
754 /* TODO: We should audit xkb-actions, whether they need @resync as
755 * flag. Most actions should just be executed, however, there might
756 * be actions that depend on modifier-orders. Those should be
757 * suppressed. */
758
759 num = xkb_state_key_get_syms(k->xkb_state, ev->code + KBDXKB_SHIFT, &keysyms);
760 compch = xkb_state_update_key(k->xkb_state, ev->code + KBDXKB_SHIFT, ev->value);
761
762 if (compch & XKB_STATE_LEDS) {
763 /* TODO: update LEDs */
764 }
765
766 if (num < 0)
767 goto error;
768
769 r = keyboard_fill(k, &k->evdata, data->resync, ev->code, ev->value, num, keysyms);
770 if (r < 0)
771 goto error;
772
773 keyboard_repeat(k);
774 return keyboard_raise_data(k, &k->evdata);
775
776 error:
777 log_debug("idev-keyboard: %s/%s: cannot handle event: %s",
778 d->session->name, d->name, strerror(-r));
779 k->repeating = false;
780 keyboard_arm(k, 0);
781 return 0;
782 }
783
784 static int keyboard_feed(idev_device *d, idev_data *data) {
785 idev_keyboard *k = keyboard_from_device(d);
786
787 switch (data->type) {
788 case IDEV_DATA_RESYNC:
789 /*
790 * If the underlying device is re-synced, key-events might be
791 * sent re-ordered. Thus, we don't know which key was pressed
792 * last. Key-repeat might get confused, hence, disable it
793 * during re-syncs. The first following event will enable it
794 * again.
795 */
796
797 k->repeating = false;
798 keyboard_arm(k, 0);
799 return 0;
800 case IDEV_DATA_EVDEV:
801 return keyboard_feed_evdev(k, data);
802 default:
803 return 0;
804 }
805 }
806
807 static int keyboard_update_kbdmap(idev_keyboard *k) {
808 idev_device *d = &k->device;
809 struct xkb_state *state;
810 kbdmap *km;
811 int r;
812
813 assert(k);
814
815 km = k->kbdctx->kbdmap;
816 if (km == k->kbdmap)
817 return 0;
818
819 errno = 0;
820 state = xkb_state_new(km->xkb_keymap);
821 if (!state) {
822 r = errno > 0 ? -errno : -EFAULT;
823 goto error;
824 }
825
826 kbdmap_unref(k->kbdmap);
827 k->kbdmap = kbdmap_ref(km);
828 xkb_state_unref(k->xkb_state);
829 k->xkb_state = state;
830
831 /* TODO: On state-change, we should trigger a resync so the whole
832 * event-state is flushed into the new xkb-state. libevdev currently
833 * does not support that, though. */
834
835 return 0;
836
837 error:
838 log_debug("idev-keyboard: %s/%s: cannot adopt new keymap: %s",
839 d->session->name, d->name, strerror(-r));
840 return r;
841 }
842
843 static const idev_device_vtable keyboard_vtable = {
844 .free = keyboard_free,
845 .feed = keyboard_feed,
846 };