chiark / gitweb /
terminal: free xkb state on keyboard destruction
[elogind.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 };