uterm: move uterm_internal.h to uterm_input.h
[platform/upstream/kmscon.git] / src / uterm_input_uxkb.c
1 /*
2  * uterm - Linux User-Space Terminal
3  *
4  * Copyright (c) 2011 Ran Benita <ran234@gmail.com>
5  * Copyright (c) 2012 David Herrmann <dh.herrmann@googlemail.com>
6  *
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:
14  *
15  * The above copyright notice and this permission notice shall be included
16  * in all copies or substantial portions of the Software.
17  *
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.
25  */
26
27 #include <errno.h>
28 #include <inttypes.h>
29 #include <linux/input.h>
30 #include <stdbool.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <xkbcommon/xkbcommon.h>
34 #include "log.h"
35 #include "uterm.h"
36 #include "uterm_input.h"
37
38 #define LOG_SUBSYSTEM "input_uxkb"
39
40 static void uxkb_dev_ref(struct kbd_dev *kbd)
41 {
42         if (!kbd || !kbd->ref)
43                 return;
44
45         ++kbd->ref;
46 }
47
48 static void uxkb_dev_unref(struct kbd_dev *kbd)
49 {
50         if (!kbd || !kbd->ref || --kbd->ref)
51                 return;
52
53         xkb_state_unref(kbd->uxkb.state);
54         kbd_desc_unref(kbd->desc);
55         free(kbd);
56 }
57
58 #define EVDEV_KEYCODE_OFFSET 8
59 enum {
60         KEY_RELEASED = 0,
61         KEY_PRESSED = 1,
62         KEY_REPEATED = 2,
63 };
64
65 static unsigned int get_effective_modmask(struct xkb_state *state)
66 {
67         unsigned int mods = 0;
68
69         if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_SHIFT,
70                                                 XKB_STATE_EFFECTIVE))
71             mods |= UTERM_SHIFT_MASK;
72         if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CAPS,
73                                                 XKB_STATE_EFFECTIVE))
74             mods |= UTERM_LOCK_MASK;
75         if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CTRL,
76                                                 XKB_STATE_EFFECTIVE))
77             mods |= UTERM_CONTROL_MASK;
78         if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_ALT,
79                                                 XKB_STATE_EFFECTIVE))
80             mods |= UTERM_MOD1_MASK;
81         if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_LOGO,
82                                                 XKB_STATE_EFFECTIVE))
83             mods |= UTERM_MOD4_MASK;
84
85         return mods;
86 }
87
88 static int uxkb_dev_process(struct kbd_dev *kbd,
89                             uint16_t key_state,
90                             uint16_t code,
91                             struct uterm_input_event *out)
92 {
93         struct xkb_state *state;
94         struct xkb_keymap *keymap;
95         xkb_keycode_t keycode;
96         const xkb_keysym_t *keysyms;
97         int num_keysyms;
98
99         if (!kbd)
100                 return -EINVAL;
101
102         state = kbd->uxkb.state;
103         keymap = xkb_state_get_map(state);
104         keycode = code + EVDEV_KEYCODE_OFFSET;
105
106         num_keysyms = xkb_key_get_syms(state, keycode, &keysyms);
107
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);
112
113         if (key_state == KEY_RELEASED)
114                 return -ENOKEY;
115
116         if (key_state == KEY_REPEATED && !xkb_key_repeats(keymap, keycode))
117                 return -ENOKEY;
118
119         if (num_keysyms <= 0)
120                 return -ENOKEY;
121
122         /*
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.
126          */
127         out->keycode = code;
128         out->keysym = keysyms[0];
129         out->mods = get_effective_modmask(state);
130         out->unicode = xkb_keysym_to_utf32(out->keysym) ? : UTERM_INPUT_INVALID;
131
132         return 0;
133 }
134
135 /*
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.
139  */
140 static void uxkb_dev_reset(struct kbd_dev *kbd, const unsigned long *ledbits)
141 {
142         unsigned int i;
143         struct xkb_state *state;
144         static const struct {
145                 int led;
146                 const char *name;
147         } led_names[] = {
148                 { LED_NUML, XKB_LED_NAME_NUM },
149                 { LED_CAPSL, XKB_LED_NAME_CAPS },
150                 { LED_SCROLLL, XKB_LED_NAME_SCROLL },
151         };
152
153         if (!kbd)
154                 return;
155
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);
164         if (!state) {
165                 log_warning("cannot recreate xkb-state");
166                 return;
167         }
168         xkb_state_unref(kbd->uxkb.state);
169         kbd->uxkb.state = state;
170
171         for (i = 0; i < sizeof(led_names) / sizeof(*led_names); i++) {
172                 if (!input_bit_is_set(ledbits, led_names[i].led))
173                         continue;
174
175                 /*
176                  * TODO: Add support in xkbcommon for setting the led state,
177                  * and updating the modifier state accordingly. E.g., something
178                  * like this:
179                  *      xkb_state_led_name_set_active(state, led_names[i].led);
180                  */
181         }
182 }
183
184 static int uxkb_desc_init(struct kbd_desc **out,
185                           const char *layout,
186                           const char *variant,
187                           const char *options)
188 {
189         int ret;
190         struct kbd_desc *desc;
191         struct xkb_rule_names rmlvo = {
192                 .rules = "evdev",
193                 .model = "evdev",
194                 .layout = layout,
195                 .variant = variant,
196                 .options = options,
197         };
198
199         if (!out)
200                 return -EINVAL;
201
202         desc = malloc(sizeof(*desc));
203         if (!desc)
204                 return -ENOMEM;
205
206         memset(desc, 0, sizeof(*desc));
207         desc->ref = 1;
208         desc->ops = &uxkb_desc_ops;
209
210         desc->uxkb.ctx = xkb_context_new(0);
211         if (!desc->uxkb.ctx) {
212                 ret = -ENOMEM;
213                 goto err_desc;
214         }
215
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);
221
222                 rmlvo.layout = "us";
223                 rmlvo.variant = "";
224                 rmlvo.options = "";
225
226                 desc->uxkb.keymap = xkb_map_new_from_names(desc->uxkb.ctx,
227                                                            &rmlvo, 0);
228                 if (!desc->uxkb.keymap) {
229                         log_warn("failed to create keymap");
230                         ret = -EFAULT;
231                         goto err_ctx;
232                 }
233         }
234
235         log_debug("new keyboard description (%s, %s, %s)",
236                         layout, variant, options);
237         *out = desc;
238         return 0;
239
240 err_ctx:
241         xkb_context_unref(desc->uxkb.ctx);
242 err_desc:
243         free(desc);
244         return ret;
245 }
246
247 static void uxkb_desc_ref(struct kbd_desc *desc)
248 {
249         if (!desc || !desc->ref)
250                 return;
251
252         ++desc->ref;
253 }
254
255 static void uxkb_desc_unref(struct kbd_desc *desc)
256 {
257         if (!desc || !desc->ref || --desc->ref)
258                 return;
259
260         log_debug("destroying keyboard description");
261         xkb_map_unref(desc->uxkb.keymap);
262         xkb_context_unref(desc->uxkb.ctx);
263         free(desc);
264 }
265
266 static int uxkb_desc_alloc(struct kbd_desc *desc, struct kbd_dev **out)
267 {
268         struct kbd_dev *kbd;
269
270         kbd = malloc(sizeof(*kbd));
271         if (!kbd)
272                 return -ENOMEM;
273
274         memset(kbd, 0, sizeof(*kbd));
275         kbd->ref = 1;
276         kbd->desc = desc;
277         kbd->ops = &uxkb_dev_ops;
278
279         kbd->uxkb.state = xkb_state_new(desc->uxkb.keymap);
280         if (!kbd->uxkb.state) {
281                 free(kbd);
282                 return -ENOMEM;
283         }
284
285         kbd_desc_ref(desc);
286         *out = kbd;
287         return 0;
288 }
289
290 static void uxkb_keysym_to_string(uint32_t keysym, char *str, size_t size)
291 {
292         xkb_keysym_get_name(keysym, str, size);
293 }
294
295 int uxkb_string_to_keysym(const char *n, uint32_t *out)
296 {
297         uint32_t keysym;
298
299         /* TODO: fix xkbcommon upstream to be case-insensitive if case-sensitive
300          * match fails. */
301         keysym = xkb_keysym_from_name(n);
302         if (!keysym)
303                 return -EFAULT;
304
305         *out = keysym;
306         return 0;
307 }
308
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,
316 };
317
318 const struct kbd_dev_ops uxkb_dev_ops = {
319         .ref = uxkb_dev_ref,
320         .unref = uxkb_dev_unref,
321         .reset = uxkb_dev_reset,
322         .process = uxkb_dev_process,
323 };