Refactor input backend-system
[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_internal.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         state = kbd->uxkb.state;
157
158         for (i = 0; i < sizeof(led_names) / sizeof(*led_names); i++) {
159                 if (!input_bit_is_set(ledbits, led_names[i].led))
160                         continue;
161
162                 /*
163                  * TODO: Add support in xkbcommon for setting the led state,
164                  * and updating the modifier state accordingly. E.g., something
165                  * like this:
166                  *      xkb_state_led_name_set_active(state, led_names[i].led);
167                  */
168         }
169
170         (void)state;
171 }
172
173 static int uxkb_desc_init(struct kbd_desc **out,
174                           const char *layout,
175                           const char *variant,
176                           const char *options)
177 {
178         int ret;
179         struct kbd_desc *desc;
180         struct xkb_rule_names rmlvo = {
181                 .rules = "evdev",
182                 .model = "evdev",
183                 .layout = layout,
184                 .variant = variant,
185                 .options = options,
186         };
187
188         if (!out)
189                 return -EINVAL;
190
191         desc = malloc(sizeof(*desc));
192         if (!desc)
193                 return -ENOMEM;
194
195         memset(desc, 0, sizeof(*desc));
196         desc->ref = 1;
197         desc->ops = &uxkb_desc_ops;
198
199         desc->uxkb.ctx = xkb_context_new(0);
200         if (!desc->uxkb.ctx) {
201                 ret = -ENOMEM;
202                 goto err_desc;
203         }
204
205         desc->uxkb.keymap = xkb_map_new_from_names(desc->uxkb.ctx, &rmlvo, 0);
206         if (!desc->uxkb.keymap) {
207                 log_warn("failed to create keymap (%s, %s, %s), "
208                          "reverting to default US keymap",
209                          layout, variant, options);
210
211                 rmlvo.layout = "us";
212                 rmlvo.variant = "";
213                 rmlvo.options = "";
214
215                 desc->uxkb.keymap = xkb_map_new_from_names(desc->uxkb.ctx,
216                                                            &rmlvo, 0);
217                 if (!desc->uxkb.keymap) {
218                         log_warn("failed to create keymap");
219                         ret = -EFAULT;
220                         goto err_ctx;
221                 }
222         }
223
224         log_debug("new keyboard description (%s, %s, %s)",
225                         layout, variant, options);
226         *out = desc;
227         return 0;
228
229 err_ctx:
230         xkb_context_unref(desc->uxkb.ctx);
231 err_desc:
232         free(desc);
233         return ret;
234 }
235
236 static void uxkb_desc_ref(struct kbd_desc *desc)
237 {
238         if (!desc || !desc->ref)
239                 return;
240
241         ++desc->ref;
242 }
243
244 static void uxkb_desc_unref(struct kbd_desc *desc)
245 {
246         if (!desc || !desc->ref || --desc->ref)
247                 return;
248
249         log_debug("destroying keyboard description");
250         xkb_map_unref(desc->uxkb.keymap);
251         xkb_context_unref(desc->uxkb.ctx);
252         free(desc);
253 }
254
255 static int uxkb_desc_alloc(struct kbd_desc *desc, struct kbd_dev **out)
256 {
257         struct kbd_dev *kbd;
258
259         kbd = malloc(sizeof(*kbd));
260         if (!kbd)
261                 return -ENOMEM;
262
263         memset(kbd, 0, sizeof(*kbd));
264         kbd->ref = 1;
265         kbd->desc = desc;
266         kbd->ops = &uxkb_dev_ops;
267
268         kbd->uxkb.state = xkb_state_new(desc->uxkb.keymap);
269         if (!kbd->uxkb.state) {
270                 free(kbd);
271                 return -ENOMEM;
272         }
273
274         kbd_desc_ref(desc);
275         *out = kbd;
276         return 0;
277 }
278
279 static void uxkb_keysym_to_string(uint32_t keysym, char *str, size_t size)
280 {
281         xkb_keysym_get_name(keysym, str, size);
282 }
283
284 const struct kbd_desc_ops uxkb_desc_ops = {
285         .init = uxkb_desc_init,
286         .ref = uxkb_desc_ref,
287         .unref = uxkb_desc_unref,
288         .alloc = uxkb_desc_alloc,
289         .keysym_to_string = uxkb_keysym_to_string,
290 };
291
292 const struct kbd_dev_ops uxkb_dev_ops = {
293         .ref = uxkb_dev_ref,
294         .unref = uxkb_dev_unref,
295         .reset = uxkb_dev_reset,
296         .process = uxkb_dev_process,
297 };