]> git.proxmox.com Git - systemd.git/blame - src/libsystemd-terminal/idev-keyboard.c
Imported Upstream version 221
[systemd.git] / src / libsystemd-terminal / idev-keyboard.c
CommitLineData
5eef597e
MP
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
5eef597e
MP
22#include <stdbool.h>
23#include <stdlib.h>
5eef597e 24#include <xkbcommon/xkbcommon.h>
f47781d8 25#include <xkbcommon/xkbcommon-compose.h>
86f210e9
MP
26#include "sd-bus.h"
27#include "sd-event.h"
5eef597e 28#include "hashmap.h"
86f210e9
MP
29#include "macro.h"
30#include "util.h"
31#include "bus-util.h"
5eef597e
MP
32#include "idev.h"
33#include "idev-internal.h"
f47781d8 34#include "term-internal.h"
5eef597e 35
f47781d8 36typedef struct kbdtbl kbdtbl;
5eef597e
MP
37typedef struct kbdmap kbdmap;
38typedef struct kbdctx kbdctx;
39typedef struct idev_keyboard idev_keyboard;
40
f47781d8
MP
41struct kbdtbl {
42 unsigned long ref;
43 struct xkb_compose_table *xkb_compose_table;
44};
45
5eef597e
MP
46struct kbdmap {
47 unsigned long ref;
48 struct xkb_keymap *xkb_keymap;
49 xkb_mod_index_t modmap[IDEV_KBDMOD_CNT];
50 xkb_led_index_t ledmap[IDEV_KBDLED_CNT];
51};
52
53struct kbdctx {
54 unsigned long ref;
55 idev_context *context;
56 struct xkb_context *xkb_context;
57 struct kbdmap *kbdmap;
f47781d8 58 struct kbdtbl *kbdtbl;
5eef597e
MP
59
60 sd_bus_slot *slot_locale_props_changed;
61 sd_bus_slot *slot_locale_get_all;
62
f47781d8 63 char *locale_lang;
5eef597e
MP
64 char *locale_x11_model;
65 char *locale_x11_layout;
66 char *locale_x11_variant;
67 char *locale_x11_options;
68 char *last_x11_model;
69 char *last_x11_layout;
70 char *last_x11_variant;
71 char *last_x11_options;
72};
73
74struct idev_keyboard {
75 idev_device device;
76 kbdctx *kbdctx;
77 kbdmap *kbdmap;
f47781d8 78 kbdtbl *kbdtbl;
5eef597e
MP
79
80 struct xkb_state *xkb_state;
f47781d8 81 struct xkb_compose_state *xkb_compose;
5eef597e
MP
82
83 usec_t repeat_delay;
84 usec_t repeat_rate;
85 sd_event_source *repeat_timer;
86
87 uint32_t n_syms;
88 idev_data evdata;
89 idev_data repdata;
f47781d8 90 uint32_t *compose_res;
5eef597e
MP
91
92 bool repeating : 1;
93};
94
95#define keyboard_from_device(_d) container_of((_d), idev_keyboard, device)
96
97#define KBDCTX_KEY "keyboard.context" /* hashmap key for global kbdctx */
98#define KBDXKB_SHIFT (8) /* xkb shifts evdev key-codes by 8 */
99#define KBDKEY_UP (0) /* KEY UP event value */
100#define KBDKEY_DOWN (1) /* KEY DOWN event value */
101#define KBDKEY_REPEAT (2) /* KEY REPEAT event value */
102
103static const idev_device_vtable keyboard_vtable;
104
105static int keyboard_update_kbdmap(idev_keyboard *k);
f47781d8
MP
106static int keyboard_update_kbdtbl(idev_keyboard *k);
107
108/*
109 * Keyboard Compose Tables
110 */
111
112static kbdtbl *kbdtbl_ref(kbdtbl *kt) {
113 if (kt) {
114 assert_return(kt->ref > 0, NULL);
115 ++kt->ref;
116 }
117
118 return kt;
119}
120
121static kbdtbl *kbdtbl_unref(kbdtbl *kt) {
122 if (!kt)
123 return NULL;
124
125 assert_return(kt->ref > 0, NULL);
126
127 if (--kt->ref > 0)
128 return NULL;
129
130 xkb_compose_table_unref(kt->xkb_compose_table);
131 free(kt);
132
133 return 0;
134}
135
136DEFINE_TRIVIAL_CLEANUP_FUNC(kbdtbl*, kbdtbl_unref);
137
138static int kbdtbl_new_from_locale(kbdtbl **out, kbdctx *kc, const char *locale) {
139 _cleanup_(kbdtbl_unrefp) kbdtbl *kt = NULL;
140
141 assert_return(out, -EINVAL);
142 assert_return(locale, -EINVAL);
143
144 kt = new0(kbdtbl, 1);
145 if (!kt)
146 return -ENOMEM;
147
148 kt->ref = 1;
149
150 kt->xkb_compose_table = xkb_compose_table_new_from_locale(kc->xkb_context,
151 locale,
152 XKB_COMPOSE_COMPILE_NO_FLAGS);
153 if (!kt->xkb_compose_table)
154 return errno > 0 ? -errno : -EFAULT;
155
156 *out = kt;
157 kt = NULL;
158 return 0;
159}
5eef597e
MP
160
161/*
162 * Keyboard Keymaps
163 */
164
165static const char * const kbdmap_modmap[IDEV_KBDMOD_CNT] = {
166 [IDEV_KBDMOD_IDX_SHIFT] = XKB_MOD_NAME_SHIFT,
167 [IDEV_KBDMOD_IDX_CTRL] = XKB_MOD_NAME_CTRL,
168 [IDEV_KBDMOD_IDX_ALT] = XKB_MOD_NAME_ALT,
169 [IDEV_KBDMOD_IDX_LINUX] = XKB_MOD_NAME_LOGO,
170 [IDEV_KBDMOD_IDX_CAPS] = XKB_MOD_NAME_CAPS,
171};
172
173static const char * const kbdmap_ledmap[IDEV_KBDLED_CNT] = {
174 [IDEV_KBDLED_IDX_NUM] = XKB_LED_NAME_NUM,
175 [IDEV_KBDLED_IDX_CAPS] = XKB_LED_NAME_CAPS,
176 [IDEV_KBDLED_IDX_SCROLL] = XKB_LED_NAME_SCROLL,
177};
178
179static kbdmap *kbdmap_ref(kbdmap *km) {
180 assert_return(km, NULL);
181 assert_return(km->ref > 0, NULL);
182
183 ++km->ref;
184 return km;
185}
186
187static kbdmap *kbdmap_unref(kbdmap *km) {
188 if (!km)
189 return NULL;
190
191 assert_return(km->ref > 0, NULL);
192
193 if (--km->ref > 0)
194 return NULL;
195
196 xkb_keymap_unref(km->xkb_keymap);
197 free(km);
198
199 return 0;
200}
201
202DEFINE_TRIVIAL_CLEANUP_FUNC(kbdmap*, kbdmap_unref);
203
204static int kbdmap_new_from_names(kbdmap **out,
205 kbdctx *kc,
206 const char *model,
207 const char *layout,
208 const char *variant,
209 const char *options) {
210 _cleanup_(kbdmap_unrefp) kbdmap *km = NULL;
211 struct xkb_rule_names rmlvo = { };
212 unsigned int i;
213
214 assert_return(out, -EINVAL);
215
216 km = new0(kbdmap, 1);
217 if (!km)
218 return -ENOMEM;
219
220 km->ref = 1;
221
222 rmlvo.rules = "evdev";
223 rmlvo.model = model;
224 rmlvo.layout = layout;
225 rmlvo.variant = variant;
226 rmlvo.options = options;
227
228 errno = 0;
229 km->xkb_keymap = xkb_keymap_new_from_names(kc->xkb_context, &rmlvo, 0);
230 if (!km->xkb_keymap)
231 return errno > 0 ? -errno : -EFAULT;
232
233 for (i = 0; i < IDEV_KBDMOD_CNT; ++i) {
234 const char *t = kbdmap_modmap[i];
235
236 if (t)
237 km->modmap[i] = xkb_keymap_mod_get_index(km->xkb_keymap, t);
238 else
239 km->modmap[i] = XKB_MOD_INVALID;
240 }
241
242 for (i = 0; i < IDEV_KBDLED_CNT; ++i) {
243 const char *t = kbdmap_ledmap[i];
244
245 if (t)
246 km->ledmap[i] = xkb_keymap_led_get_index(km->xkb_keymap, t);
247 else
248 km->ledmap[i] = XKB_LED_INVALID;
249 }
250
251 *out = km;
252 km = NULL;
253 return 0;
254}
255
256/*
257 * Keyboard Context
258 */
259
f47781d8
MP
260static int kbdctx_refresh_compose_table(kbdctx *kc, const char *lang) {
261 kbdtbl *kt;
262 idev_session *s;
263 idev_device *d;
264 Iterator i, j;
265 int r;
266
267 if (!lang)
268 lang = "C";
269
270 if (streq_ptr(kc->locale_lang, lang))
271 return 0;
272
273 r = free_and_strdup(&kc->locale_lang, lang);
274 if (r < 0)
275 return r;
276
277 log_debug("idev-keyboard: new default compose table: [ %s ]", lang);
278
279 r = kbdtbl_new_from_locale(&kt, kc, lang);
280 if (r < 0) {
281 /* TODO: We need to catch the case where no compose-file is
282 * available. xkb doesn't tell us so far.. so we must not treat
283 * it as a hard-failure but just continue. Preferably, we want
284 * xkb to tell us exactly whether compilation failed or whether
285 * there is no compose file available for this locale. */
286 log_debug_errno(r, "idev-keyboard: cannot load compose-table for '%s': %m",
287 lang);
288 r = 0;
289 kt = NULL;
290 }
291
292 kbdtbl_unref(kc->kbdtbl);
293 kc->kbdtbl = kt;
294
295 HASHMAP_FOREACH(s, kc->context->session_map, i)
296 HASHMAP_FOREACH(d, s->device_map, j)
297 if (idev_is_keyboard(d))
298 keyboard_update_kbdtbl(keyboard_from_device(d));
299
300 return 0;
301}
302
5eef597e
MP
303static void move_str(char **dest, char **src) {
304 free(*dest);
305 *dest = *src;
306 *src = NULL;
307}
308
309static int kbdctx_refresh_keymap(kbdctx *kc) {
310 idev_session *s;
311 idev_device *d;
312 Iterator i, j;
313 kbdmap *km;
314 int r;
315
316 if (kc->kbdmap &&
317 streq_ptr(kc->locale_x11_model, kc->last_x11_model) &&
318 streq_ptr(kc->locale_x11_layout, kc->last_x11_layout) &&
319 streq_ptr(kc->locale_x11_variant, kc->last_x11_variant) &&
320 streq_ptr(kc->locale_x11_options, kc->last_x11_options))
321 return 0 ;
322
323 move_str(&kc->last_x11_model, &kc->locale_x11_model);
324 move_str(&kc->last_x11_layout, &kc->locale_x11_layout);
325 move_str(&kc->last_x11_variant, &kc->locale_x11_variant);
326 move_str(&kc->last_x11_options, &kc->locale_x11_options);
327
328 log_debug("idev-keyboard: new default keymap: [%s / %s / %s / %s]",
329 kc->last_x11_model, kc->last_x11_layout, kc->last_x11_variant, kc->last_x11_options);
330
331 /* TODO: add a fallback keymap that's compiled-in */
332 r = kbdmap_new_from_names(&km, kc, kc->last_x11_model, kc->last_x11_layout,
333 kc->last_x11_variant, kc->last_x11_options);
f47781d8
MP
334 if (r < 0)
335 return log_debug_errno(r, "idev-keyboard: cannot create keymap from locale1: %m");
5eef597e
MP
336
337 kbdmap_unref(kc->kbdmap);
338 kc->kbdmap = km;
339
340 HASHMAP_FOREACH(s, kc->context->session_map, i)
341 HASHMAP_FOREACH(d, s->device_map, j)
342 if (idev_is_keyboard(d))
343 keyboard_update_kbdmap(keyboard_from_device(d));
344
345 return 0;
346}
347
f47781d8
MP
348static int kbdctx_set_locale(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
349 kbdctx *kc = userdata;
350 const char *s, *ctype = NULL, *lang = NULL;
351 int r;
352
353 r = sd_bus_message_enter_container(m, 'a', "s");
354 if (r < 0)
355 goto error;
356
357 while ((r = sd_bus_message_read(m, "s", &s)) > 0) {
358 if (!ctype)
359 ctype = startswith(s, "LC_CTYPE=");
360 if (!lang)
361 lang = startswith(s, "LANG=");
362 }
363
364 if (r < 0)
365 goto error;
366
367 r = sd_bus_message_exit_container(m);
368 if (r < 0)
369 goto error;
370
371 kbdctx_refresh_compose_table(kc, ctype ? : lang);
372 r = 0;
373
374error:
375 if (r < 0)
376 log_debug_errno(r, "idev-keyboard: cannot parse locale property from locale1: %m");
377
378 return r;
379}
380
5eef597e 381static const struct bus_properties_map kbdctx_locale_map[] = {
f47781d8 382 { "Locale", "as", kbdctx_set_locale, 0 },
5eef597e
MP
383 { "X11Model", "s", NULL, offsetof(kbdctx, locale_x11_model) },
384 { "X11Layout", "s", NULL, offsetof(kbdctx, locale_x11_layout) },
385 { "X11Variant", "s", NULL, offsetof(kbdctx, locale_x11_variant) },
386 { "X11Options", "s", NULL, offsetof(kbdctx, locale_x11_options) },
e3bff60a 387 { },
5eef597e
MP
388};
389
e3bff60a 390static int kbdctx_locale_get_all_fn(sd_bus_message *m,
5eef597e
MP
391 void *userdata,
392 sd_bus_error *ret_err) {
393 kbdctx *kc = userdata;
394 int r;
395
396 kc->slot_locale_get_all = sd_bus_slot_unref(kc->slot_locale_get_all);
397
398 if (sd_bus_message_is_method_error(m, NULL)) {
399 const sd_bus_error *error = sd_bus_message_get_error(m);
400
401 log_debug("idev-keyboard: GetAll() on locale1 failed: %s: %s",
402 error->name, error->message);
403 return 0;
404 }
405
e3bff60a 406 r = bus_message_map_all_properties(m, kbdctx_locale_map, kc);
5eef597e
MP
407 if (r < 0) {
408 log_debug("idev-keyboard: erroneous GetAll() reply from locale1");
409 return 0;
410 }
411
412 kbdctx_refresh_keymap(kc);
413 return 0;
414}
415
416static int kbdctx_query_locale(kbdctx *kc) {
417 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
418 int r;
419
420 kc->slot_locale_get_all = sd_bus_slot_unref(kc->slot_locale_get_all);
421
422 r = sd_bus_message_new_method_call(kc->context->sysbus,
423 &m,
424 "org.freedesktop.locale1",
425 "/org/freedesktop/locale1",
426 "org.freedesktop.DBus.Properties",
427 "GetAll");
428 if (r < 0)
429 goto error;
430
431 r = sd_bus_message_append(m, "s", "org.freedesktop.locale1");
432 if (r < 0)
433 goto error;
434
435 r = sd_bus_call_async(kc->context->sysbus,
436 &kc->slot_locale_get_all,
437 m,
438 kbdctx_locale_get_all_fn,
439 kc,
440 0);
441 if (r < 0)
442 goto error;
443
444 return 0;
445
446error:
f47781d8 447 return log_debug_errno(r, "idev-keyboard: cannot send GetAll to locale1: %m");
5eef597e
MP
448}
449
e3bff60a 450static int kbdctx_locale_props_changed_fn(sd_bus_message *signal,
5eef597e
MP
451 void *userdata,
452 sd_bus_error *ret_err) {
453 kbdctx *kc = userdata;
454 int r;
455
456 kc->slot_locale_get_all = sd_bus_slot_unref(kc->slot_locale_get_all);
457
458 /* skip interface name */
459 r = sd_bus_message_skip(signal, "s");
460 if (r < 0)
461 goto error;
462
e3bff60a 463 r = bus_message_map_properties_changed(signal, kbdctx_locale_map, kc);
5eef597e
MP
464 if (r < 0)
465 goto error;
466
467 if (r > 0) {
468 r = kbdctx_query_locale(kc);
469 if (r < 0)
470 return r;
471 }
472
473 kbdctx_refresh_keymap(kc);
474 return 0;
475
476error:
f47781d8 477 return log_debug_errno(r, "idev-keyboard: cannot handle PropertiesChanged from locale1: %m");
5eef597e
MP
478}
479
480static int kbdctx_setup_bus(kbdctx *kc) {
481 int r;
482
483 r = sd_bus_add_match(kc->context->sysbus,
484 &kc->slot_locale_props_changed,
485 "type='signal',"
486 "sender='org.freedesktop.locale1',"
487 "interface='org.freedesktop.DBus.Properties',"
488 "member='PropertiesChanged',"
489 "path='/org/freedesktop/locale1'",
490 kbdctx_locale_props_changed_fn,
491 kc);
f47781d8
MP
492 if (r < 0)
493 return log_debug_errno(r, "idev-keyboard: cannot setup locale1 link: %m");
5eef597e
MP
494
495 return kbdctx_query_locale(kc);
496}
497
f47781d8
MP
498static void kbdctx_log_fn(struct xkb_context *ctx, enum xkb_log_level lvl, const char *format, va_list args) {
499 char buf[LINE_MAX];
500 int sd_lvl;
501
502 if (lvl >= XKB_LOG_LEVEL_DEBUG)
503 sd_lvl = LOG_DEBUG;
504 else if (lvl >= XKB_LOG_LEVEL_INFO)
505 sd_lvl = LOG_INFO;
506 else if (lvl >= XKB_LOG_LEVEL_WARNING)
507 sd_lvl = LOG_INFO; /* most XKB warnings really are informational */
f47781d8 508 else
e3bff60a
MP
509 /* XKB_LOG_LEVEL_ERROR and worse */
510 sd_lvl = LOG_ERR;
f47781d8
MP
511
512 snprintf(buf, sizeof(buf), "idev-xkb: %s", format);
513 log_internalv(sd_lvl, 0, __FILE__, __LINE__, __func__, buf, args);
514}
515
5eef597e
MP
516static kbdctx *kbdctx_ref(kbdctx *kc) {
517 assert_return(kc, NULL);
518 assert_return(kc->ref > 0, NULL);
519
520 ++kc->ref;
521 return kc;
522}
523
524static kbdctx *kbdctx_unref(kbdctx *kc) {
525 if (!kc)
526 return NULL;
527
528 assert_return(kc->ref > 0, NULL);
529
530 if (--kc->ref > 0)
531 return NULL;
532
533 free(kc->last_x11_options);
534 free(kc->last_x11_variant);
535 free(kc->last_x11_layout);
536 free(kc->last_x11_model);
537 free(kc->locale_x11_options);
538 free(kc->locale_x11_variant);
539 free(kc->locale_x11_layout);
540 free(kc->locale_x11_model);
f47781d8 541 free(kc->locale_lang);
5eef597e
MP
542 kc->slot_locale_get_all = sd_bus_slot_unref(kc->slot_locale_get_all);
543 kc->slot_locale_props_changed = sd_bus_slot_unref(kc->slot_locale_props_changed);
f47781d8 544 kc->kbdtbl = kbdtbl_unref(kc->kbdtbl);
5eef597e
MP
545 kc->kbdmap = kbdmap_unref(kc->kbdmap);
546 xkb_context_unref(kc->xkb_context);
547 hashmap_remove_value(kc->context->data_map, KBDCTX_KEY, kc);
548 free(kc);
549
550 return NULL;
551}
552
553DEFINE_TRIVIAL_CLEANUP_FUNC(kbdctx*, kbdctx_unref);
554
555static int kbdctx_new(kbdctx **out, idev_context *c) {
556 _cleanup_(kbdctx_unrefp) kbdctx *kc = NULL;
557 int r;
558
559 assert_return(out, -EINVAL);
560 assert_return(c, -EINVAL);
561
562 kc = new0(kbdctx, 1);
563 if (!kc)
564 return -ENOMEM;
565
566 kc->ref = 1;
567 kc->context = c;
568
569 errno = 0;
f47781d8 570 kc->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
5eef597e
MP
571 if (!kc->xkb_context)
572 return errno > 0 ? -errno : -EFAULT;
573
f47781d8
MP
574 xkb_context_set_log_fn(kc->xkb_context, kbdctx_log_fn);
575 xkb_context_set_log_level(kc->xkb_context, XKB_LOG_LEVEL_DEBUG);
576
5eef597e
MP
577 r = kbdctx_refresh_keymap(kc);
578 if (r < 0)
579 return r;
580
f47781d8
MP
581 r = kbdctx_refresh_compose_table(kc, NULL);
582 if (r < 0)
583 return r;
584
5eef597e
MP
585 if (c->sysbus) {
586 r = kbdctx_setup_bus(kc);
587 if (r < 0)
588 return r;
589 }
590
591 r = hashmap_put(c->data_map, KBDCTX_KEY, kc);
592 if (r < 0)
593 return r;
594
595 *out = kc;
596 kc = NULL;
597 return 0;
598}
599
600static int get_kbdctx(idev_context *c, kbdctx **out) {
601 kbdctx *kc;
602
603 assert_return(c, -EINVAL);
604 assert_return(out, -EINVAL);
605
606 kc = hashmap_get(c->data_map, KBDCTX_KEY);
607 if (kc) {
608 *out = kbdctx_ref(kc);
609 return 0;
610 }
611
612 return kbdctx_new(out, c);
613}
614
615/*
616 * Keyboard Devices
617 */
618
619bool idev_is_keyboard(idev_device *d) {
620 return d && d->vtable == &keyboard_vtable;
621}
622
623idev_device *idev_find_keyboard(idev_session *s, const char *name) {
624 char *kname;
625
626 assert_return(s, NULL);
627 assert_return(name, NULL);
628
e735f4d4 629 kname = strjoina("keyboard/", name);
5eef597e
MP
630 return hashmap_get(s->device_map, kname);
631}
632
633static int keyboard_raise_data(idev_keyboard *k, idev_data *data) {
634 idev_device *d = &k->device;
635 int r;
636
637 r = idev_session_raise_device_data(d->session, d, data);
638 if (r < 0)
f47781d8
MP
639 log_debug_errno(r, "idev-keyboard: %s/%s: error while raising data event: %m",
640 d->session->name, d->name);
5eef597e
MP
641
642 return r;
643}
644
f47781d8
MP
645static int keyboard_resize_bufs(idev_keyboard *k, uint32_t n_syms) {
646 uint32_t *t;
647
648 if (n_syms <= k->n_syms)
649 return 0;
650
651 t = realloc(k->compose_res, sizeof(*t) * n_syms);
652 if (!t)
653 return -ENOMEM;
654 k->compose_res = t;
655
656 t = realloc(k->evdata.keyboard.keysyms, sizeof(*t) * n_syms);
657 if (!t)
658 return -ENOMEM;
659 k->evdata.keyboard.keysyms = t;
660
661 t = realloc(k->evdata.keyboard.codepoints, sizeof(*t) * n_syms);
662 if (!t)
663 return -ENOMEM;
664 k->evdata.keyboard.codepoints = t;
665
666 t = realloc(k->repdata.keyboard.keysyms, sizeof(*t) * n_syms);
667 if (!t)
668 return -ENOMEM;
669 k->repdata.keyboard.keysyms = t;
670
671 t = realloc(k->repdata.keyboard.codepoints, sizeof(*t) * n_syms);
672 if (!t)
673 return -ENOMEM;
674 k->repdata.keyboard.codepoints = t;
675
676 k->n_syms = n_syms;
677 return 0;
678}
679
680static unsigned int keyboard_read_compose(idev_keyboard *k, const xkb_keysym_t **out) {
681 _cleanup_free_ char *t = NULL;
682 term_utf8 u8 = { };
683 char buf[256], *p;
684 size_t flen = 0;
685 int i, r;
686
687 r = xkb_compose_state_get_utf8(k->xkb_compose, buf, sizeof(buf));
688 if (r >= (int)sizeof(buf)) {
689 t = malloc(r + 1);
690 if (!t)
691 return 0;
692
693 xkb_compose_state_get_utf8(k->xkb_compose, t, r + 1);
694 p = t;
695 } else {
696 p = buf;
697 }
698
699 for (i = 0; i < r; ++i) {
700 uint32_t *ucs;
701 size_t len, j;
702
703 len = term_utf8_decode(&u8, &ucs, p[i]);
704 if (len > 0) {
705 r = keyboard_resize_bufs(k, flen + len);
706 if (r < 0)
707 return 0;
708
709 for (j = 0; j < len; ++j)
710 k->compose_res[flen++] = ucs[j];
711 }
712 }
713
714 *out = k->compose_res;
715 return flen;
716}
717
5eef597e
MP
718static void keyboard_arm(idev_keyboard *k, usec_t usecs) {
719 int r;
720
721 if (usecs != 0) {
722 usecs += now(CLOCK_MONOTONIC);
723 r = sd_event_source_set_time(k->repeat_timer, usecs);
724 if (r >= 0)
725 sd_event_source_set_enabled(k->repeat_timer, SD_EVENT_ONESHOT);
726 } else {
727 sd_event_source_set_enabled(k->repeat_timer, SD_EVENT_OFF);
728 }
729}
730
731static int keyboard_repeat_timer_fn(sd_event_source *source, uint64_t usec, void *userdata) {
732 idev_keyboard *k = userdata;
733
f47781d8
MP
734 /* never feed REPEAT keys into COMPOSE */
735
5eef597e
MP
736 keyboard_arm(k, k->repeat_rate);
737 return keyboard_raise_data(k, &k->repdata);
738}
739
740int idev_keyboard_new(idev_device **out, idev_session *s, const char *name) {
741 _cleanup_(idev_device_freep) idev_device *d = NULL;
742 idev_keyboard *k;
743 char *kname;
744 int r;
745
746 assert_return(out, -EINVAL);
747 assert_return(s, -EINVAL);
748 assert_return(name, -EINVAL);
749
750 k = new0(idev_keyboard, 1);
751 if (!k)
752 return -ENOMEM;
753
754 d = &k->device;
755 k->device = IDEV_DEVICE_INIT(&keyboard_vtable, s);
756 k->repeat_delay = 250 * USEC_PER_MSEC;
757 k->repeat_rate = 30 * USEC_PER_MSEC;
758
759 /* TODO: add key-repeat configuration */
760
761 r = get_kbdctx(s->context, &k->kbdctx);
762 if (r < 0)
763 return r;
764
765 r = keyboard_update_kbdmap(k);
766 if (r < 0)
767 return r;
768
f47781d8
MP
769 r = keyboard_update_kbdtbl(k);
770 if (r < 0)
771 return r;
772
773 r = keyboard_resize_bufs(k, 8);
774 if (r < 0)
775 return r;
776
5eef597e
MP
777 r = sd_event_add_time(s->context->event,
778 &k->repeat_timer,
779 CLOCK_MONOTONIC,
780 0,
781 10 * USEC_PER_MSEC,
782 keyboard_repeat_timer_fn,
783 k);
784 if (r < 0)
785 return r;
786
787 r = sd_event_source_set_enabled(k->repeat_timer, SD_EVENT_OFF);
788 if (r < 0)
789 return r;
790
e735f4d4 791 kname = strjoina("keyboard/", name);
5eef597e
MP
792 r = idev_device_add(d, kname);
793 if (r < 0)
794 return r;
795
796 if (out)
797 *out = d;
798 d = NULL;
799 return 0;
800}
801
802static void keyboard_free(idev_device *d) {
803 idev_keyboard *k = keyboard_from_device(d);
804
f47781d8 805 xkb_compose_state_unref(k->xkb_compose);
5eef597e
MP
806 xkb_state_unref(k->xkb_state);
807 free(k->repdata.keyboard.codepoints);
808 free(k->repdata.keyboard.keysyms);
809 free(k->evdata.keyboard.codepoints);
810 free(k->evdata.keyboard.keysyms);
f47781d8 811 free(k->compose_res);
5eef597e 812 k->repeat_timer = sd_event_source_unref(k->repeat_timer);
f47781d8 813 k->kbdtbl = kbdtbl_unref(k->kbdtbl);
5eef597e
MP
814 k->kbdmap = kbdmap_unref(k->kbdmap);
815 k->kbdctx = kbdctx_unref(k->kbdctx);
816 free(k);
817}
818
819static int8_t guess_ascii(struct xkb_state *state, uint32_t code, uint32_t n_syms, const uint32_t *syms) {
820 xkb_layout_index_t n_lo, lo;
821 xkb_level_index_t lv;
822 struct xkb_keymap *keymap;
823 const xkb_keysym_t *s;
824 int num;
825
826 if (n_syms == 1 && syms[0] < 128 && syms[0] > 0)
827 return syms[0];
828
829 keymap = xkb_state_get_keymap(state);
830 n_lo = xkb_keymap_num_layouts_for_key(keymap, code + KBDXKB_SHIFT);
831
832 for (lo = 0; lo < n_lo; ++lo) {
833 lv = xkb_state_key_get_level(state, code + KBDXKB_SHIFT, lo);
834 num = xkb_keymap_key_get_syms_by_level(keymap, code + KBDXKB_SHIFT, lo, lv, &s);
835 if (num == 1 && s[0] < 128 && s[0] > 0)
836 return s[0];
837 }
838
839 return -1;
840}
841
842static int keyboard_fill(idev_keyboard *k,
843 idev_data *dst,
844 bool resync,
845 uint16_t code,
846 uint32_t value,
847 uint32_t n_syms,
848 const uint32_t *keysyms) {
849 idev_data_keyboard *kev;
850 uint32_t i;
f47781d8 851 int r;
5eef597e
MP
852
853 assert(dst == &k->evdata || dst == &k->repdata);
854
f47781d8
MP
855 r = keyboard_resize_bufs(k, n_syms);
856 if (r < 0)
857 return r;
5eef597e
MP
858
859 dst->type = IDEV_DATA_KEYBOARD;
860 dst->resync = resync;
861 kev = &dst->keyboard;
862 kev->ascii = guess_ascii(k->xkb_state, code, n_syms, keysyms);
863 kev->value = value;
864 kev->keycode = code;
865 kev->mods = 0;
866 kev->consumed_mods = 0;
867 kev->n_syms = n_syms;
868 memcpy(kev->keysyms, keysyms, sizeof(*keysyms) * n_syms);
869
870 for (i = 0; i < n_syms; ++i) {
871 kev->codepoints[i] = xkb_keysym_to_utf32(keysyms[i]);
872 if (!kev->codepoints[i])
873 kev->codepoints[i] = 0xffffffffUL;
874 }
875
876 for (i = 0; i < IDEV_KBDMOD_CNT; ++i) {
5eef597e
MP
877 if (k->kbdmap->modmap[i] == XKB_MOD_INVALID)
878 continue;
879
880 r = xkb_state_mod_index_is_active(k->xkb_state, k->kbdmap->modmap[i], XKB_STATE_MODS_EFFECTIVE);
881 if (r > 0)
882 kev->mods |= 1 << i;
883
884 r = xkb_state_mod_index_is_consumed(k->xkb_state, code + KBDXKB_SHIFT, k->kbdmap->modmap[i]);
885 if (r > 0)
886 kev->consumed_mods |= 1 << i;
887 }
888
889 return 0;
890}
891
892static void keyboard_repeat(idev_keyboard *k) {
893 idev_data *evdata = &k->evdata;
894 idev_data *repdata = &k->repdata;
895 idev_data_keyboard *evkbd = &evdata->keyboard;
896 idev_data_keyboard *repkbd = &repdata->keyboard;
897 const xkb_keysym_t *keysyms;
898 idev_device *d = &k->device;
899 bool repeats;
900 int r, num;
901
902 if (evdata->resync) {
903 /*
904 * We received a re-sync event. During re-sync, any number of
905 * key-events may have been lost and sync-events may be
906 * re-ordered. Always disable key-repeat for those events. Any
907 * following event will trigger it again.
908 */
909
910 k->repeating = false;
911 keyboard_arm(k, 0);
912 return;
913 }
914
915 repeats = xkb_keymap_key_repeats(k->kbdmap->xkb_keymap, evkbd->keycode + KBDXKB_SHIFT);
916
917 if (k->repeating && repkbd->keycode == evkbd->keycode) {
918 /*
919 * We received an event for the key we currently repeat. If it
920 * was released, stop key-repeat. Otherwise, ignore the event.
921 */
922
923 if (evkbd->value == KBDKEY_UP) {
924 k->repeating = false;
925 keyboard_arm(k, 0);
926 }
927 } else if (evkbd->value == KBDKEY_DOWN && repeats) {
928 /*
929 * We received a key-down event for a key that repeats. The
930 * previous condition caught keys we already repeat, so we know
931 * this is a different key or no key-repeat is running. Start
932 * new key-repeat.
933 */
934
935 errno = 0;
936 num = xkb_state_key_get_syms(k->xkb_state, evkbd->keycode + KBDXKB_SHIFT, &keysyms);
937 if (num < 0)
938 r = errno > 0 ? errno : -EFAULT;
939 else
940 r = keyboard_fill(k, repdata, false, evkbd->keycode, KBDKEY_REPEAT, num, keysyms);
941
942 if (r < 0) {
f47781d8
MP
943 log_debug_errno(r, "idev-keyboard: %s/%s: cannot set key-repeat: %m",
944 d->session->name, d->name);
5eef597e
MP
945 k->repeating = false;
946 keyboard_arm(k, 0);
947 } else {
948 k->repeating = true;
949 keyboard_arm(k, k->repeat_delay);
950 }
951 } else if (k->repeating && !repeats) {
952 /*
953 * We received an event for a key that does not repeat, but we
954 * currently repeat a previously received key. The new key is
955 * usually a modifier, but might be any kind of key. In this
956 * case, we continue repeating the old key, but update the
957 * symbols according to the new state.
958 */
959
960 errno = 0;
961 num = xkb_state_key_get_syms(k->xkb_state, repkbd->keycode + KBDXKB_SHIFT, &keysyms);
962 if (num < 0)
963 r = errno > 0 ? errno : -EFAULT;
964 else
965 r = keyboard_fill(k, repdata, false, repkbd->keycode, KBDKEY_REPEAT, num, keysyms);
966
967 if (r < 0) {
f47781d8
MP
968 log_debug_errno(r, "idev-keyboard: %s/%s: cannot update key-repeat: %m",
969 d->session->name, d->name);
5eef597e
MP
970 k->repeating = false;
971 keyboard_arm(k, 0);
972 }
973 }
974}
975
976static int keyboard_feed_evdev(idev_keyboard *k, idev_data *data) {
977 struct input_event *ev = &data->evdev.event;
978 enum xkb_state_component compch;
f47781d8 979 enum xkb_compose_status cstatus;
5eef597e
MP
980 const xkb_keysym_t *keysyms;
981 idev_device *d = &k->device;
982 int num, r;
983
984 if (ev->type != EV_KEY || ev->value > KBDKEY_DOWN)
985 return 0;
986
987 /* TODO: We should audit xkb-actions, whether they need @resync as
988 * flag. Most actions should just be executed, however, there might
989 * be actions that depend on modifier-orders. Those should be
990 * suppressed. */
991
992 num = xkb_state_key_get_syms(k->xkb_state, ev->code + KBDXKB_SHIFT, &keysyms);
993 compch = xkb_state_update_key(k->xkb_state, ev->code + KBDXKB_SHIFT, ev->value);
994
995 if (compch & XKB_STATE_LEDS) {
996 /* TODO: update LEDs */
997 }
998
999 if (num < 0) {
1000 r = num;
1001 goto error;
1002 }
1003
f47781d8
MP
1004 if (k->xkb_compose && ev->value == KBDKEY_DOWN) {
1005 if (num == 1 && !data->resync) {
1006 xkb_compose_state_feed(k->xkb_compose, keysyms[0]);
1007 cstatus = xkb_compose_state_get_status(k->xkb_compose);
1008 } else {
1009 cstatus = XKB_COMPOSE_CANCELLED;
1010 }
1011
1012 switch (cstatus) {
1013 case XKB_COMPOSE_NOTHING:
1014 /* keep produced keysyms and forward unchanged */
1015 break;
1016 case XKB_COMPOSE_COMPOSING:
1017 /* consumed by compose-state, drop keysym */
1018 keysyms = NULL;
1019 num = 0;
1020 break;
1021 case XKB_COMPOSE_COMPOSED:
1022 /* compose-state produced sth, replace keysym */
1023 num = keyboard_read_compose(k, &keysyms);
1024 xkb_compose_state_reset(k->xkb_compose);
1025 break;
1026 case XKB_COMPOSE_CANCELLED:
1027 /* canceled compose, reset, forward cancellation sym */
1028 xkb_compose_state_reset(k->xkb_compose);
1029 break;
1030 }
1031 } else if (k->xkb_compose &&
1032 num == 1 &&
1033 keysyms[0] == XKB_KEY_Multi_key &&
1034 !data->resync &&
1035 ev->value == KBDKEY_UP) {
1036 /* Reset compose state on Multi-Key UP events. This effectively
1037 * requires you to hold the key during the whole sequence. I
1038 * think it's pretty handy to avoid accidental
1039 * Compose-sequences, but this may break Compose for disabled
1040 * people. We really need to make this opional! (TODO) */
1041 xkb_compose_state_reset(k->xkb_compose);
1042 }
1043
1044 if (ev->value == KBDKEY_UP) {
1045 /* never produce keysyms for UP */
1046 keysyms = NULL;
1047 num = 0;
1048 }
1049
5eef597e
MP
1050 r = keyboard_fill(k, &k->evdata, data->resync, ev->code, ev->value, num, keysyms);
1051 if (r < 0)
1052 goto error;
1053
1054 keyboard_repeat(k);
1055 return keyboard_raise_data(k, &k->evdata);
1056
1057error:
f47781d8
MP
1058 log_debug_errno(r, "idev-keyboard: %s/%s: cannot handle event: %m",
1059 d->session->name, d->name);
5eef597e
MP
1060 k->repeating = false;
1061 keyboard_arm(k, 0);
1062 return 0;
1063}
1064
1065static int keyboard_feed(idev_device *d, idev_data *data) {
1066 idev_keyboard *k = keyboard_from_device(d);
1067
1068 switch (data->type) {
1069 case IDEV_DATA_RESYNC:
1070 /*
1071 * If the underlying device is re-synced, key-events might be
1072 * sent re-ordered. Thus, we don't know which key was pressed
1073 * last. Key-repeat might get confused, hence, disable it
1074 * during re-syncs. The first following event will enable it
1075 * again.
1076 */
1077
1078 k->repeating = false;
1079 keyboard_arm(k, 0);
1080 return 0;
1081 case IDEV_DATA_EVDEV:
1082 return keyboard_feed_evdev(k, data);
1083 default:
1084 return 0;
1085 }
1086}
1087
1088static int keyboard_update_kbdmap(idev_keyboard *k) {
1089 idev_device *d = &k->device;
1090 struct xkb_state *state;
1091 kbdmap *km;
1092 int r;
1093
1094 assert(k);
1095
1096 km = k->kbdctx->kbdmap;
1097 if (km == k->kbdmap)
1098 return 0;
1099
1100 errno = 0;
1101 state = xkb_state_new(km->xkb_keymap);
1102 if (!state) {
1103 r = errno > 0 ? -errno : -EFAULT;
1104 goto error;
1105 }
1106
1107 kbdmap_unref(k->kbdmap);
1108 k->kbdmap = kbdmap_ref(km);
1109 xkb_state_unref(k->xkb_state);
1110 k->xkb_state = state;
1111
1112 /* TODO: On state-change, we should trigger a resync so the whole
1113 * event-state is flushed into the new xkb-state. libevdev currently
1114 * does not support that, though. */
1115
1116 return 0;
1117
1118error:
f47781d8
MP
1119 return log_debug_errno(r, "idev-keyboard: %s/%s: cannot adopt new keymap: %m",
1120 d->session->name, d->name);
1121}
1122
1123static int keyboard_update_kbdtbl(idev_keyboard *k) {
1124 idev_device *d = &k->device;
1125 struct xkb_compose_state *compose = NULL;
1126 kbdtbl *kt;
1127 int r;
1128
1129 assert(k);
1130
1131 kt = k->kbdctx->kbdtbl;
1132 if (kt == k->kbdtbl)
1133 return 0;
1134
1135 if (kt) {
1136 errno = 0;
1137 compose = xkb_compose_state_new(kt->xkb_compose_table, XKB_COMPOSE_STATE_NO_FLAGS);
1138 if (!compose) {
1139 r = errno > 0 ? -errno : -EFAULT;
1140 goto error;
1141 }
1142 }
1143
1144 kbdtbl_unref(k->kbdtbl);
1145 k->kbdtbl = kbdtbl_ref(kt);
1146 xkb_compose_state_unref(k->xkb_compose);
1147 k->xkb_compose = compose;
1148
1149 return 0;
1150
1151error:
1152 return log_debug_errno(r, "idev-keyboard: %s/%s: cannot adopt new compose table: %m",
1153 d->session->name, d->name);
5eef597e
MP
1154}
1155
1156static const idev_device_vtable keyboard_vtable = {
1157 .free = keyboard_free,
1158 .feed = keyboard_feed,
1159};