2 * uterm - Linux User-Space Terminal
4 * Copyright (c) 2011 Ran Benita <ran234@gmail.com>
5 * Copyright (c) 2012 David Herrmann <dh.herrmann@googlemail.com>
7 * Permission is hereby granted, free of charge, to any person obtaining
8 * a copy of this software and associated documentation files
9 * (the "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sublicense, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
15 * The above copyright notice and this permission notice shall be included
16 * in all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 #include <linux/input.h>
33 #include <xkbcommon/xkbcommon.h>
36 #include "uterm_internal.h"
38 #define LOG_SUBSYSTEM "input_uxkb"
40 static void uxkb_dev_ref(struct kbd_dev *kbd)
42 if (!kbd || !kbd->ref)
48 static void uxkb_dev_unref(struct kbd_dev *kbd)
50 if (!kbd || !kbd->ref || --kbd->ref)
53 xkb_state_unref(kbd->uxkb.state);
54 kbd_desc_unref(kbd->desc);
58 #define EVDEV_KEYCODE_OFFSET 8
65 static unsigned int get_effective_modmask(struct xkb_state *state)
67 unsigned int mods = 0;
69 if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_SHIFT,
71 mods |= UTERM_SHIFT_MASK;
72 if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CAPS,
74 mods |= UTERM_LOCK_MASK;
75 if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CTRL,
77 mods |= UTERM_CONTROL_MASK;
78 if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_ALT,
80 mods |= UTERM_MOD1_MASK;
81 if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_LOGO,
83 mods |= UTERM_MOD4_MASK;
88 static int uxkb_dev_process(struct kbd_dev *kbd,
91 struct uterm_input_event *out)
93 struct xkb_state *state;
94 struct xkb_keymap *keymap;
95 xkb_keycode_t keycode;
96 const xkb_keysym_t *keysyms;
102 state = kbd->uxkb.state;
103 keymap = xkb_state_get_map(state);
104 keycode = code + EVDEV_KEYCODE_OFFSET;
106 num_keysyms = xkb_key_get_syms(state, keycode, &keysyms);
108 if (key_state == KEY_PRESSED)
109 xkb_state_update_key(state, keycode, XKB_KEY_DOWN);
110 else if (key_state == KEY_RELEASED)
111 xkb_state_update_key(state, keycode, XKB_KEY_UP);
113 if (key_state == KEY_RELEASED)
116 if (key_state == KEY_REPEATED && !xkb_key_repeats(keymap, keycode))
119 if (num_keysyms <= 0)
123 * TODO: xkbcommon actually supports multiple keysyms
124 * per key press. Here we're just using the first one,
125 * but we might want to support this feature.
128 out->keysym = keysyms[0];
129 out->mods = get_effective_modmask(state);
130 out->unicode = xkb_keysym_to_utf32(out->keysym) ? : UTERM_INPUT_INVALID;
136 * Call this when we regain control of the keyboard after losing it.
137 * We don't reset the locked group, this should survive a VT switch, etc. The
138 * locked modifiers are reset according to the keyboard LEDs.
140 static void uxkb_dev_reset(struct kbd_dev *kbd, const unsigned long *ledbits)
143 struct xkb_state *state;
144 static const struct {
148 { LED_NUML, XKB_LED_NAME_NUM },
149 { LED_CAPSL, XKB_LED_NAME_CAPS },
150 { LED_SCROLLL, XKB_LED_NAME_SCROLL },
156 /* TODO: Urghs, while the input device was closed we might have missed
157 * some events that affect internal state. As xkbcommon does not provide
158 * a way to reset the internal state, we simply recreate the state. This
159 * should have the same effect.
160 * It also has a bug that if the CTRL-Release event is skipped, then
161 * every further release will never perform a _real_ release. Kind of
162 * buggy so we should fix it upstream. */
163 state = xkb_state_new(kbd->desc->uxkb.keymap);
165 log_warning("cannot recreate xkb-state");
168 xkb_state_unref(kbd->uxkb.state);
169 kbd->uxkb.state = state;
171 for (i = 0; i < sizeof(led_names) / sizeof(*led_names); i++) {
172 if (!input_bit_is_set(ledbits, led_names[i].led))
176 * TODO: Add support in xkbcommon for setting the led state,
177 * and updating the modifier state accordingly. E.g., something
179 * xkb_state_led_name_set_active(state, led_names[i].led);
184 static int uxkb_desc_init(struct kbd_desc **out,
190 struct kbd_desc *desc;
191 struct xkb_rule_names rmlvo = {
202 desc = malloc(sizeof(*desc));
206 memset(desc, 0, sizeof(*desc));
208 desc->ops = &uxkb_desc_ops;
210 desc->uxkb.ctx = xkb_context_new(0);
211 if (!desc->uxkb.ctx) {
216 desc->uxkb.keymap = xkb_map_new_from_names(desc->uxkb.ctx, &rmlvo, 0);
217 if (!desc->uxkb.keymap) {
218 log_warn("failed to create keymap (%s, %s, %s), "
219 "reverting to default US keymap",
220 layout, variant, options);
226 desc->uxkb.keymap = xkb_map_new_from_names(desc->uxkb.ctx,
228 if (!desc->uxkb.keymap) {
229 log_warn("failed to create keymap");
235 log_debug("new keyboard description (%s, %s, %s)",
236 layout, variant, options);
241 xkb_context_unref(desc->uxkb.ctx);
247 static void uxkb_desc_ref(struct kbd_desc *desc)
249 if (!desc || !desc->ref)
255 static void uxkb_desc_unref(struct kbd_desc *desc)
257 if (!desc || !desc->ref || --desc->ref)
260 log_debug("destroying keyboard description");
261 xkb_map_unref(desc->uxkb.keymap);
262 xkb_context_unref(desc->uxkb.ctx);
266 static int uxkb_desc_alloc(struct kbd_desc *desc, struct kbd_dev **out)
270 kbd = malloc(sizeof(*kbd));
274 memset(kbd, 0, sizeof(*kbd));
277 kbd->ops = &uxkb_dev_ops;
279 kbd->uxkb.state = xkb_state_new(desc->uxkb.keymap);
280 if (!kbd->uxkb.state) {
290 static void uxkb_keysym_to_string(uint32_t keysym, char *str, size_t size)
292 xkb_keysym_get_name(keysym, str, size);
295 int uxkb_string_to_keysym(const char *n, uint32_t *out)
299 /* TODO: fix xkbcommon upstream to be case-insensitive if case-sensitive
301 keysym = xkb_keysym_from_name(n);
309 const struct kbd_desc_ops uxkb_desc_ops = {
310 .init = uxkb_desc_init,
311 .ref = uxkb_desc_ref,
312 .unref = uxkb_desc_unref,
313 .alloc = uxkb_desc_alloc,
314 .keysym_to_string = uxkb_keysym_to_string,
315 .string_to_keysym = uxkb_string_to_keysym,
318 const struct kbd_dev_ops uxkb_dev_ops = {
320 .unref = uxkb_dev_unref,
321 .reset = uxkb_dev_reset,
322 .process = uxkb_dev_process,