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