chiark / gitweb /
7ab4db2cf79ed8ec71af8a25279cd76ae731ddf9
[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         free(k->repdata.keyboard.codepoints);
554         free(k->repdata.keyboard.keysyms);
555         free(k->evdata.keyboard.codepoints);
556         free(k->evdata.keyboard.keysyms);
557         k->repeat_timer = sd_event_source_unref(k->repeat_timer);
558         k->kbdmap = kbdmap_unref(k->kbdmap);
559         k->kbdctx = kbdctx_unref(k->kbdctx);
560         free(k);
561 }
562
563 static int8_t guess_ascii(struct xkb_state *state, uint32_t code, uint32_t n_syms, const uint32_t *syms) {
564         xkb_layout_index_t n_lo, lo;
565         xkb_level_index_t lv;
566         struct xkb_keymap *keymap;
567         const xkb_keysym_t *s;
568         int num;
569
570         if (n_syms == 1 && syms[0] < 128)
571                 return syms[0];
572
573         keymap = xkb_state_get_keymap(state);
574         n_lo = xkb_keymap_num_layouts_for_key(keymap, code + KBDXKB_SHIFT);
575
576         for (lo = 0; lo < n_lo; ++lo) {
577                 lv = xkb_state_key_get_level(state, code + KBDXKB_SHIFT, lo);
578                 num = xkb_keymap_key_get_syms_by_level(keymap, code + KBDXKB_SHIFT, lo, lv, &s);
579                 if (num == 1 && s[0] < 128)
580                         return s[0];
581         }
582
583         return -1;
584 }
585
586 static int keyboard_fill(idev_keyboard *k,
587                          idev_data *dst,
588                          bool resync,
589                          uint16_t code,
590                          uint32_t value,
591                          uint32_t n_syms,
592                          const uint32_t *keysyms) {
593         idev_data_keyboard *kev;
594         uint32_t i;
595
596         assert(dst == &k->evdata || dst == &k->repdata);
597
598         if (n_syms > k->n_syms) {
599                 uint32_t *t;
600
601                 t = realloc(k->evdata.keyboard.keysyms, sizeof(*t) * n_syms);
602                 if (!t)
603                         return -ENOMEM;
604                 k->evdata.keyboard.keysyms = t;
605
606                 t = realloc(k->evdata.keyboard.codepoints, sizeof(*t) * n_syms);
607                 if (!t)
608                         return -ENOMEM;
609                 k->evdata.keyboard.codepoints = t;
610
611                 t = realloc(k->repdata.keyboard.keysyms, sizeof(*t) * n_syms);
612                 if (!t)
613                         return -ENOMEM;
614                 k->repdata.keyboard.keysyms = t;
615
616                 t = realloc(k->repdata.keyboard.codepoints, sizeof(*t) * n_syms);
617                 if (!t)
618                         return -ENOMEM;
619                 k->repdata.keyboard.codepoints = t;
620
621                 k->n_syms = n_syms;
622         }
623
624         dst->type = IDEV_DATA_KEYBOARD;
625         dst->resync = resync;
626         kev = &dst->keyboard;
627         kev->ascii = guess_ascii(k->xkb_state, code, n_syms, keysyms);
628         kev->value = value;
629         kev->keycode = code;
630         kev->mods = 0;
631         kev->consumed_mods = 0;
632         kev->n_syms = n_syms;
633         memcpy(kev->keysyms, keysyms, sizeof(*keysyms) * n_syms);
634
635         for (i = 0; i < n_syms; ++i) {
636                 kev->codepoints[i] = xkb_keysym_to_utf32(keysyms[i]);
637                 if (!kev->codepoints[i])
638                         kev->codepoints[i] = 0xffffffffUL;
639         }
640
641         for (i = 0; i < IDEV_KBDMOD_CNT; ++i) {
642                 int r;
643
644                 if (k->kbdmap->modmap[i] == XKB_MOD_INVALID)
645                         continue;
646
647                 r = xkb_state_mod_index_is_active(k->xkb_state, k->kbdmap->modmap[i], XKB_STATE_MODS_EFFECTIVE);
648                 if (r > 0)
649                         kev->mods |= 1 << i;
650
651                 r = xkb_state_mod_index_is_consumed(k->xkb_state, code + KBDXKB_SHIFT, k->kbdmap->modmap[i]);
652                 if (r > 0)
653                         kev->consumed_mods |= 1 << i;
654         }
655
656         return 0;
657 }
658
659 static void keyboard_repeat(idev_keyboard *k) {
660         idev_data *evdata = &k->evdata;
661         idev_data *repdata = &k->repdata;
662         idev_data_keyboard *evkbd = &evdata->keyboard;
663         idev_data_keyboard *repkbd = &repdata->keyboard;
664         const xkb_keysym_t *keysyms;
665         idev_device *d = &k->device;
666         bool repeats;
667         int r, num;
668
669         if (evdata->resync) {
670                 /*
671                  * We received a re-sync event. During re-sync, any number of
672                  * key-events may have been lost and sync-events may be
673                  * re-ordered. Always disable key-repeat for those events. Any
674                  * following event will trigger it again.
675                  */
676
677                 k->repeating = false;
678                 keyboard_arm(k, 0);
679                 return;
680         }
681
682         repeats = xkb_keymap_key_repeats(k->kbdmap->xkb_keymap, evkbd->keycode + KBDXKB_SHIFT);
683
684         if (k->repeating && repkbd->keycode == evkbd->keycode) {
685                 /*
686                  * We received an event for the key we currently repeat. If it
687                  * was released, stop key-repeat. Otherwise, ignore the event.
688                  */
689
690                 if (evkbd->value == KBDKEY_UP) {
691                         k->repeating = false;
692                         keyboard_arm(k, 0);
693                 }
694         } else if (evkbd->value == KBDKEY_DOWN && repeats) {
695                 /*
696                  * We received a key-down event for a key that repeats. The
697                  * previous condition caught keys we already repeat, so we know
698                  * this is a different key or no key-repeat is running. Start
699                  * new key-repeat.
700                  */
701
702                 errno = 0;
703                 num = xkb_state_key_get_syms(k->xkb_state, evkbd->keycode + KBDXKB_SHIFT, &keysyms);
704                 if (num < 0)
705                         r = errno > 0 ? errno : -EFAULT;
706                 else
707                         r = keyboard_fill(k, repdata, false, evkbd->keycode, KBDKEY_REPEAT, num, keysyms);
708
709                 if (r < 0) {
710                         log_debug("idev-keyboard: %s/%s: cannot set key-repeat: %s",
711                                   d->session->name, d->name, strerror(-r));
712                         k->repeating = false;
713                         keyboard_arm(k, 0);
714                 } else {
715                         k->repeating = true;
716                         keyboard_arm(k, k->repeat_delay);
717                 }
718         } else if (k->repeating && !repeats) {
719                 /*
720                  * We received an event for a key that does not repeat, but we
721                  * currently repeat a previously received key. The new key is
722                  * usually a modifier, but might be any kind of key. In this
723                  * case, we continue repeating the old key, but update the
724                  * symbols according to the new state.
725                  */
726
727                 errno = 0;
728                 num = xkb_state_key_get_syms(k->xkb_state, repkbd->keycode + KBDXKB_SHIFT, &keysyms);
729                 if (num < 0)
730                         r = errno > 0 ? errno : -EFAULT;
731                 else
732                         r = keyboard_fill(k, repdata, false, repkbd->keycode, KBDKEY_REPEAT, num, keysyms);
733
734                 if (r < 0) {
735                         log_debug("idev-keyboard: %s/%s: cannot update key-repeat: %s",
736                                   d->session->name, d->name, strerror(-r));
737                         k->repeating = false;
738                         keyboard_arm(k, 0);
739                 }
740         }
741 }
742
743 static int keyboard_feed_evdev(idev_keyboard *k, idev_data *data) {
744         struct input_event *ev = &data->evdev.event;
745         enum xkb_state_component compch;
746         const xkb_keysym_t *keysyms;
747         idev_device *d = &k->device;
748         int num, r;
749
750         if (ev->type != EV_KEY || ev->value > KBDKEY_DOWN)
751                 return 0;
752
753         /* TODO: We should audit xkb-actions, whether they need @resync as
754          * flag. Most actions should just be executed, however, there might
755          * be actions that depend on modifier-orders. Those should be
756          * suppressed. */
757
758         num = xkb_state_key_get_syms(k->xkb_state, ev->code + KBDXKB_SHIFT, &keysyms);
759         compch = xkb_state_update_key(k->xkb_state, ev->code + KBDXKB_SHIFT, ev->value);
760
761         if (compch & XKB_STATE_LEDS) {
762                 /* TODO: update LEDs */
763         }
764
765         if (num < 0)
766                 goto error;
767
768         r = keyboard_fill(k, &k->evdata, data->resync, ev->code, ev->value, num, keysyms);
769         if (r < 0)
770                 goto error;
771
772         keyboard_repeat(k);
773         return keyboard_raise_data(k, &k->evdata);
774
775 error:
776         log_debug("idev-keyboard: %s/%s: cannot handle event: %s",
777                   d->session->name, d->name, strerror(-r));
778         k->repeating = false;
779         keyboard_arm(k, 0);
780         return 0;
781 }
782
783 static int keyboard_feed(idev_device *d, idev_data *data) {
784         idev_keyboard *k = keyboard_from_device(d);
785
786         switch (data->type) {
787         case IDEV_DATA_RESYNC:
788                 /*
789                  * If the underlying device is re-synced, key-events might be
790                  * sent re-ordered. Thus, we don't know which key was pressed
791                  * last. Key-repeat might get confused, hence, disable it
792                  * during re-syncs. The first following event will enable it
793                  * again.
794                  */
795
796                 k->repeating = false;
797                 keyboard_arm(k, 0);
798                 return 0;
799         case IDEV_DATA_EVDEV:
800                 return keyboard_feed_evdev(k, data);
801         default:
802                 return 0;
803         }
804 }
805
806 static int keyboard_update_kbdmap(idev_keyboard *k) {
807         idev_device *d = &k->device;
808         struct xkb_state *state;
809         kbdmap *km;
810         int r;
811
812         assert(k);
813
814         km = k->kbdctx->kbdmap;
815         if (km == k->kbdmap)
816                 return 0;
817
818         errno = 0;
819         state = xkb_state_new(km->xkb_keymap);
820         if (!state) {
821                 r = errno > 0 ? -errno : -EFAULT;
822                 goto error;
823         }
824
825         kbdmap_unref(k->kbdmap);
826         k->kbdmap = kbdmap_ref(km);
827         xkb_state_unref(k->xkb_state);
828         k->xkb_state = state;
829
830         /* TODO: On state-change, we should trigger a resync so the whole
831          * event-state is flushed into the new xkb-state. libevdev currently
832          * does not support that, though. */
833
834         return 0;
835
836 error:
837         log_debug("idev-keyboard: %s/%s: cannot adopt new keymap: %s",
838                   d->session->name, d->name, strerror(-r));
839         return r;
840 }
841
842 static const idev_device_vtable keyboard_vtable = {
843         .free                   = keyboard_free,
844         .feed                   = keyboard_feed,
845 };