diff options
Diffstat (limited to 'sway/input/keyboard.c')
-rw-r--r-- | sway/input/keyboard.c | 411 |
1 files changed, 154 insertions, 257 deletions
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index e873eea3..e59d9c03 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c | |||
@@ -9,88 +9,93 @@ | |||
9 | #include "sway/commands.h" | 9 | #include "sway/commands.h" |
10 | #include "log.h" | 10 | #include "log.h" |
11 | 11 | ||
12 | static bool keysym_is_modifier(xkb_keysym_t keysym) { | 12 | /** |
13 | switch (keysym) { | 13 | * Update the shortcut model state in response to new input |
14 | case XKB_KEY_Shift_L: case XKB_KEY_Shift_R: | 14 | */ |
15 | case XKB_KEY_Control_L: case XKB_KEY_Control_R: | 15 | static void update_shortcut_model(struct sway_shortcut_state* state, |
16 | case XKB_KEY_Caps_Lock: | 16 | struct wlr_event_keyboard_key * event, |
17 | case XKB_KEY_Shift_Lock: | 17 | uint32_t new_key, |
18 | case XKB_KEY_Meta_L: case XKB_KEY_Meta_R: | 18 | bool last_key_was_a_modifier) { |
19 | case XKB_KEY_Alt_L: case XKB_KEY_Alt_R: | 19 | if (event->state == WLR_KEY_PRESSED) { |
20 | case XKB_KEY_Super_L: case XKB_KEY_Super_R: | 20 | if (last_key_was_a_modifier && state->last_key_index >= 0) { |
21 | case XKB_KEY_Hyper_L: case XKB_KEY_Hyper_R: | 21 | // Last pressed key before this one was a modifier. We nullify |
22 | return true; | 22 | // the key id but not the keycode (as that is used for erasure |
23 | default: | 23 | // on release) |
24 | return false; | 24 | state->pressed_keys[state->last_key_index] = 0; |
25 | } | 25 | state->last_key_index = -1; |
26 | } | ||
27 | |||
28 | static size_t pressed_keysyms_length(xkb_keysym_t *pressed_keysyms) { | ||
29 | size_t n = 0; | ||
30 | for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP; ++i) { | ||
31 | if (pressed_keysyms[i] != XKB_KEY_NoSymbol) { | ||
32 | ++n; | ||
33 | } | 26 | } |
34 | } | ||
35 | return n; | ||
36 | } | ||
37 | 27 | ||
38 | static ssize_t pressed_keysyms_index(xkb_keysym_t *pressed_keysyms, | 28 | // Add current key to set; there may be duplicates |
39 | xkb_keysym_t keysym) { | 29 | for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYS_CAP; i++) { |
40 | for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP; ++i) { | 30 | if (!state->pressed_keys[i]) { |
41 | if (pressed_keysyms[i] == keysym) { | 31 | state->pressed_keys[i] = new_key; |
42 | return i; | 32 | state->pressed_keycodes[i] = event->keycode; |
33 | state->last_key_index = i; | ||
34 | break; | ||
35 | } | ||
43 | } | 36 | } |
44 | } | 37 | } else { |
45 | return -1; | 38 | for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYS_CAP; i++) { |
46 | } | 39 | // The same keycode may match multiple keysyms. |
47 | 40 | if (state->pressed_keycodes[i] == event->keycode) { | |
48 | static void pressed_keysyms_add(xkb_keysym_t *pressed_keysyms, | 41 | state->pressed_keys[i] = 0; |
49 | xkb_keysym_t keysym) { | 42 | state->pressed_keycodes[i] = 0; |
50 | ssize_t i = pressed_keysyms_index(pressed_keysyms, keysym); | 43 | } |
51 | if (i < 0) { | ||
52 | i = pressed_keysyms_index(pressed_keysyms, XKB_KEY_NoSymbol); | ||
53 | if (i >= 0) { | ||
54 | pressed_keysyms[i] = keysym; | ||
55 | } | 44 | } |
56 | } | 45 | } |
57 | } | 46 | } |
58 | 47 | ||
59 | static void pressed_keysyms_remove(xkb_keysym_t *pressed_keysyms, | 48 | /** |
60 | xkb_keysym_t keysym) { | 49 | * |
61 | ssize_t i = pressed_keysyms_index(pressed_keysyms, keysym); | 50 | * Returns a binding which matches the shortcut model state (ignoring the |
62 | if (i >= 0) { | 51 | * `release` flag). |
63 | pressed_keysyms[i] = XKB_KEY_NoSymbol; | 52 | */ |
53 | static struct sway_binding* check_shortcut_model( | ||
54 | struct sway_shortcut_state* state, list_t* bindings, | ||
55 | uint32_t modifiers, bool locked) { | ||
56 | int npressed_keys = 0; | ||
57 | for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYS_CAP; i++) { | ||
58 | if (state->pressed_keys[i]) { | ||
59 | ++npressed_keys; | ||
60 | } | ||
64 | } | 61 | } |
65 | } | 62 | for (int i = 0; i < bindings->length; ++i) { |
63 | struct sway_binding *binding = bindings->items[i]; | ||
66 | 64 | ||
67 | static void pressed_keysyms_update(xkb_keysym_t *pressed_keysyms, | 65 | if (modifiers ^ binding->modifiers || |
68 | const xkb_keysym_t *keysyms, size_t keysyms_len, | 66 | npressed_keys != binding->keys->length || |
69 | enum wlr_key_state state) { | 67 | locked > binding->locked) { |
70 | for (size_t i = 0; i < keysyms_len; ++i) { | ||
71 | if (keysym_is_modifier(keysyms[i])) { | ||
72 | continue; | 68 | continue; |
73 | } | 69 | } |
74 | if (state == WLR_KEY_PRESSED) { | 70 | |
75 | pressed_keysyms_add(pressed_keysyms, keysyms[i]); | 71 | bool match = true; |
76 | } else { // WLR_KEY_RELEASED | 72 | for (int j = 0; j < binding->keys->length; ++j) { |
77 | pressed_keysyms_remove(pressed_keysyms, keysyms[i]); | 73 | uint32_t key = *(uint32_t*)binding->keys->items[j]; |
74 | |||
75 | bool key_found = false; | ||
76 | for (int k = 0; k < SWAY_KEYBOARD_PRESSED_KEYS_CAP; k++) { | ||
77 | if (state->pressed_keys[k] == key) { | ||
78 | key_found = true; | ||
79 | break; | ||
80 | } | ||
81 | } | ||
82 | if (!key_found) { | ||
83 | match = false; | ||
84 | break; | ||
85 | } | ||
78 | } | 86 | } |
79 | } | ||
80 | } | ||
81 | 87 | ||
82 | static bool binding_matches_key_state(struct sway_binding *binding, | 88 | if (match) { |
83 | enum wlr_key_state key_state) { | 89 | return binding; |
84 | if (key_state == WLR_KEY_PRESSED && !binding->release) { | 90 | } |
85 | return true; | ||
86 | } | ||
87 | if (key_state == WLR_KEY_RELEASED && binding->release) { | ||
88 | return true; | ||
89 | } | 91 | } |
90 | 92 | ||
91 | return false; | 93 | return NULL; |
92 | } | 94 | } |
93 | 95 | ||
96 | /** | ||
97 | * Execute the command associated to a binding | ||
98 | */ | ||
94 | static void keyboard_execute_command(struct sway_keyboard *keyboard, | 99 | static void keyboard_execute_command(struct sway_keyboard *keyboard, |
95 | struct sway_binding *binding) { | 100 | struct sway_binding *binding) { |
96 | wlr_log(L_DEBUG, "running command for binding: %s", | 101 | wlr_log(L_DEBUG, "running command for binding: %s", |
@@ -113,7 +118,7 @@ static void keyboard_execute_command(struct sway_keyboard *keyboard, | |||
113 | * should be propagated to clients. | 118 | * should be propagated to clients. |
114 | */ | 119 | */ |
115 | static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard, | 120 | static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard, |
116 | xkb_keysym_t *pressed_keysyms, uint32_t modifiers, size_t keysyms_len) { | 121 | const xkb_keysym_t *pressed_keysyms, uint32_t modifiers, size_t keysyms_len) { |
117 | for (size_t i = 0; i < keysyms_len; ++i) { | 122 | for (size_t i = 0; i < keysyms_len; ++i) { |
118 | xkb_keysym_t keysym = pressed_keysyms[i]; | 123 | xkb_keysym_t keysym = pressed_keysyms[i]; |
119 | if (keysym >= XKB_KEY_XF86Switch_VT_1 && | 124 | if (keysym >= XKB_KEY_XF86Switch_VT_1 && |
@@ -134,157 +139,6 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard, | |||
134 | } | 139 | } |
135 | 140 | ||
136 | /** | 141 | /** |
137 | * Execute keyboard bindings bound with `bindysm` for the given keyboard state. | ||
138 | * | ||
139 | * Returns true if the keysym was handled by a binding and false if the event | ||
140 | * should be propagated to clients. | ||
141 | */ | ||
142 | static bool keyboard_execute_bindsym(struct sway_keyboard *keyboard, | ||
143 | xkb_keysym_t *pressed_keysyms, uint32_t modifiers, | ||
144 | enum wlr_key_state key_state, bool locked) { | ||
145 | // configured bindings | ||
146 | int n = pressed_keysyms_length(pressed_keysyms); | ||
147 | list_t *keysym_bindings = config->current_mode->keysym_bindings; | ||
148 | for (int i = 0; i < keysym_bindings->length; ++i) { | ||
149 | struct sway_binding *binding = keysym_bindings->items[i]; | ||
150 | if (!binding_matches_key_state(binding, key_state) || | ||
151 | modifiers ^ binding->modifiers || | ||
152 | n != binding->keys->length || locked > binding->locked) { | ||
153 | continue; | ||
154 | } | ||
155 | |||
156 | bool match = true; | ||
157 | for (int j = 0; j < binding->keys->length; ++j) { | ||
158 | match = | ||
159 | pressed_keysyms_index(pressed_keysyms, | ||
160 | *(int*)binding->keys->items[j]) >= 0; | ||
161 | |||
162 | if (!match) { | ||
163 | break; | ||
164 | } | ||
165 | } | ||
166 | |||
167 | if (match) { | ||
168 | keyboard_execute_command(keyboard, binding); | ||
169 | return true; | ||
170 | } | ||
171 | } | ||
172 | |||
173 | return false; | ||
174 | } | ||
175 | |||
176 | static bool binding_matches_keycodes(struct wlr_keyboard *keyboard, | ||
177 | struct sway_binding *binding, struct wlr_event_keyboard_key *event, bool locked) { | ||
178 | assert(binding->bindcode); | ||
179 | |||
180 | uint32_t keycode = event->keycode + 8; | ||
181 | |||
182 | if (!binding_matches_key_state(binding, event->state)) { | ||
183 | return false; | ||
184 | } | ||
185 | |||
186 | if (locked > binding->locked) { | ||
187 | return false; | ||
188 | } | ||
189 | |||
190 | uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard); | ||
191 | if (modifiers ^ binding->modifiers) { | ||
192 | return false; | ||
193 | } | ||
194 | |||
195 | // on release, the released key must be in the binding | ||
196 | if (event->state == WLR_KEY_RELEASED) { | ||
197 | bool found = false; | ||
198 | for (int i = 0; i < binding->keys->length; ++i) { | ||
199 | uint32_t binding_keycode = *(uint32_t*)binding->keys->items[i] + 8; | ||
200 | if (binding_keycode == keycode) { | ||
201 | found = true; | ||
202 | break; | ||
203 | } | ||
204 | } | ||
205 | if (!found) { | ||
206 | return false; | ||
207 | } | ||
208 | } | ||
209 | |||
210 | // every keycode in the binding must be present in the pressed keys on the | ||
211 | // keyboard | ||
212 | for (int i = 0; i < binding->keys->length; ++i) { | ||
213 | uint32_t binding_keycode = *(uint32_t*)binding->keys->items[i] + 8; | ||
214 | if (event->state == WLR_KEY_RELEASED && keycode == binding_keycode) { | ||
215 | continue; | ||
216 | } | ||
217 | |||
218 | bool found = false; | ||
219 | for (size_t j = 0; j < keyboard->num_keycodes; ++j) { | ||
220 | xkb_keycode_t keycode = keyboard->keycodes[j] + 8; | ||
221 | if (keycode == binding_keycode) { | ||
222 | found = true; | ||
223 | break; | ||
224 | } | ||
225 | } | ||
226 | |||
227 | if (!found) { | ||
228 | return false; | ||
229 | } | ||
230 | } | ||
231 | |||
232 | // every keycode pressed on the keyboard must be present within the binding | ||
233 | // keys (unless it is a modifier) | ||
234 | for (size_t i = 0; i < keyboard->num_keycodes; ++i) { | ||
235 | xkb_keycode_t keycode = keyboard->keycodes[i] + 8; | ||
236 | bool found = false; | ||
237 | for (int j = 0; j < binding->keys->length; ++j) { | ||
238 | uint32_t binding_keycode = *(uint32_t*)binding->keys->items[j] + 8; | ||
239 | if (binding_keycode == keycode) { | ||
240 | found = true; | ||
241 | break; | ||
242 | } | ||
243 | } | ||
244 | |||
245 | if (!found) { | ||
246 | if (!binding->modifiers) { | ||
247 | return false; | ||
248 | } | ||
249 | |||
250 | // check if it is a modifier, which we know matched from the check | ||
251 | // above | ||
252 | const xkb_keysym_t *keysyms; | ||
253 | int num_keysyms = | ||
254 | xkb_state_key_get_syms(keyboard->xkb_state, | ||
255 | keycode, &keysyms); | ||
256 | if (num_keysyms != 1 || !keysym_is_modifier(keysyms[0])) { | ||
257 | return false; | ||
258 | } | ||
259 | } | ||
260 | } | ||
261 | |||
262 | return true; | ||
263 | } | ||
264 | |||
265 | /** | ||
266 | * Execute keyboard bindings bound with `bindcode` for the given keyboard state. | ||
267 | * | ||
268 | * Returns true if the keysym was handled by a binding and false if the event | ||
269 | * should be propagated to clients. | ||
270 | */ | ||
271 | static bool keyboard_execute_bindcode(struct sway_keyboard *keyboard, | ||
272 | struct wlr_event_keyboard_key *event, bool locked) { | ||
273 | struct wlr_keyboard *wlr_keyboard = | ||
274 | keyboard->seat_device->input_device->wlr_device->keyboard; | ||
275 | list_t *keycode_bindings = config->current_mode->keycode_bindings; | ||
276 | for (int i = 0; i < keycode_bindings->length; ++i) { | ||
277 | struct sway_binding *binding = keycode_bindings->items[i]; | ||
278 | if (binding_matches_keycodes(wlr_keyboard, binding, event, locked)) { | ||
279 | keyboard_execute_command(keyboard, binding); | ||
280 | return true; | ||
281 | } | ||
282 | } | ||
283 | |||
284 | return false; | ||
285 | } | ||
286 | |||
287 | /** | ||
288 | * Get keysyms and modifiers from the keyboard as xkb sees them. | 142 | * Get keysyms and modifiers from the keyboard as xkb sees them. |
289 | * | 143 | * |
290 | * This uses the xkb keysyms translation based on pressed modifiers and clears | 144 | * This uses the xkb keysyms translation based on pressed modifiers and clears |
@@ -339,61 +193,100 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { | |||
339 | struct wlr_event_keyboard_key *event = data; | 193 | struct wlr_event_keyboard_key *event = data; |
340 | bool input_inhibited = keyboard->seat_device->sway_seat->exclusive_client != NULL; | 194 | bool input_inhibited = keyboard->seat_device->sway_seat->exclusive_client != NULL; |
341 | 195 | ||
196 | // Identify new keycode, raw keysym(s), and translated keysym(s) | ||
342 | xkb_keycode_t keycode = event->keycode + 8; | 197 | xkb_keycode_t keycode = event->keycode + 8; |
343 | bool handled = false; | ||
344 | |||
345 | // handle keycodes | ||
346 | handled = keyboard_execute_bindcode(keyboard, event, input_inhibited); | ||
347 | 198 | ||
348 | // handle translated keysyms | ||
349 | if (!handled && event->state == WLR_KEY_RELEASED) { | ||
350 | handled = keyboard_execute_bindsym(keyboard, | ||
351 | keyboard->pressed_keysyms_translated, | ||
352 | keyboard->modifiers_translated, | ||
353 | event->state, input_inhibited); | ||
354 | } | ||
355 | const xkb_keysym_t *translated_keysyms; | 199 | const xkb_keysym_t *translated_keysyms; |
200 | uint32_t translated_modifiers; | ||
356 | size_t translated_keysyms_len = | 201 | size_t translated_keysyms_len = |
357 | keyboard_keysyms_translated(keyboard, keycode, &translated_keysyms, | 202 | keyboard_keysyms_translated(keyboard, keycode, &translated_keysyms, |
358 | &keyboard->modifiers_translated); | 203 | &translated_modifiers); |
359 | pressed_keysyms_update(keyboard->pressed_keysyms_translated, | ||
360 | translated_keysyms, translated_keysyms_len, event->state); | ||
361 | if (!handled && event->state == WLR_KEY_PRESSED) { | ||
362 | handled = keyboard_execute_bindsym(keyboard, | ||
363 | keyboard->pressed_keysyms_translated, | ||
364 | keyboard->modifiers_translated, | ||
365 | event->state, input_inhibited); | ||
366 | } | ||
367 | 204 | ||
368 | // Handle raw keysyms | ||
369 | if (!handled && event->state == WLR_KEY_RELEASED) { | ||
370 | handled = keyboard_execute_bindsym(keyboard, | ||
371 | keyboard->pressed_keysyms_raw, keyboard->modifiers_raw, | ||
372 | event->state, input_inhibited); | ||
373 | } | ||
374 | const xkb_keysym_t *raw_keysyms; | 205 | const xkb_keysym_t *raw_keysyms; |
206 | uint32_t raw_modifiers; | ||
375 | size_t raw_keysyms_len = | 207 | size_t raw_keysyms_len = |
376 | keyboard_keysyms_raw(keyboard, keycode, &raw_keysyms, &keyboard->modifiers_raw); | 208 | keyboard_keysyms_raw(keyboard, keycode, &raw_keysyms, &raw_modifiers); |
377 | pressed_keysyms_update(keyboard->pressed_keysyms_raw, raw_keysyms, | 209 | |
378 | raw_keysyms_len, event->state); | 210 | struct wlr_input_device *device = |
379 | if (!handled && event->state == WLR_KEY_PRESSED) { | 211 | keyboard->seat_device->input_device->wlr_device; |
380 | handled = keyboard_execute_bindsym(keyboard, | 212 | uint32_t code_modifiers = wlr_keyboard_get_modifiers(device->keyboard); |
381 | keyboard->pressed_keysyms_raw, keyboard->modifiers_raw, | 213 | |
382 | event->state, input_inhibited); | 214 | bool last_key_was_a_modifier = code_modifiers != keyboard->last_modifiers; |
215 | keyboard->last_modifiers = code_modifiers; | ||
216 | |||
217 | // Update shortcut models | ||
218 | update_shortcut_model(&keyboard->state_keycodes, event, | ||
219 | (uint32_t)keycode, last_key_was_a_modifier); | ||
220 | for (size_t i=0;i<translated_keysyms_len;i++) { | ||
221 | update_shortcut_model(&keyboard->state_keysyms_translated, | ||
222 | event, (uint32_t)translated_keysyms[i], | ||
223 | last_key_was_a_modifier); | ||
224 | } | ||
225 | for (size_t i=0;i<raw_keysyms_len;i++) { | ||
226 | update_shortcut_model(&keyboard->state_keysyms_raw, | ||
227 | event, (uint32_t)raw_keysyms[i], | ||
228 | last_key_was_a_modifier); | ||
229 | } | ||
230 | |||
231 | // identify which binding should be executed. | ||
232 | struct sway_binding *binding = | ||
233 | check_shortcut_model(&keyboard->state_keycodes, | ||
234 | config->current_mode->keycode_bindings, | ||
235 | code_modifiers, input_inhibited); | ||
236 | for (size_t i=0;i<translated_keysyms_len;i++) { | ||
237 | struct sway_binding *translated_binding = | ||
238 | check_shortcut_model(&keyboard->state_keysyms_translated, | ||
239 | config->current_mode->keysym_bindings, | ||
240 | translated_modifiers, input_inhibited); | ||
241 | if (translated_binding && !binding) { | ||
242 | binding = translated_binding; | ||
243 | } else if (binding && translated_binding && binding != translated_binding) { | ||
244 | wlr_log(L_DEBUG, "encountered duplicate bindings %d and %d", | ||
245 | binding->order, translated_binding->order); | ||
246 | } | ||
247 | } | ||
248 | for (size_t i=0;i<raw_keysyms_len;i++) { | ||
249 | struct sway_binding *raw_binding = | ||
250 | check_shortcut_model(&keyboard->state_keysyms_raw, | ||
251 | config->current_mode->keysym_bindings, | ||
252 | raw_modifiers, input_inhibited); | ||
253 | if (raw_binding && !binding) { | ||
254 | binding = raw_binding; | ||
255 | } else if (binding && raw_binding && binding != raw_binding) { | ||
256 | wlr_log(L_DEBUG, "encountered duplicate bindings %d and %d", | ||
257 | binding->order, raw_binding->order); | ||
258 | } | ||
259 | } | ||
260 | |||
261 | bool handled = false; | ||
262 | |||
263 | // Execute the identified binding if need be. | ||
264 | if (keyboard->held_binding && binding != keyboard->held_binding && | ||
265 | event->state == WLR_KEY_RELEASED) { | ||
266 | keyboard_execute_command(keyboard, keyboard->held_binding); | ||
267 | handled = true; | ||
268 | } | ||
269 | if (binding != keyboard->held_binding) { | ||
270 | keyboard->held_binding = NULL; | ||
271 | } | ||
272 | if (binding && event->state == WLR_KEY_PRESSED) { | ||
273 | if (binding->release) { | ||
274 | keyboard->held_binding = binding; | ||
275 | } else { | ||
276 | keyboard_execute_command(keyboard, binding); | ||
277 | handled = true; | ||
278 | } | ||
383 | } | 279 | } |
384 | 280 | ||
385 | // Compositor bindings | 281 | // Compositor bindings |
386 | if (!handled && event->state == WLR_KEY_PRESSED) { | 282 | if (!handled && event->state == WLR_KEY_PRESSED) { |
387 | handled = | 283 | handled = keyboard_execute_compositor_binding( |
388 | keyboard_execute_compositor_binding(keyboard, | 284 | keyboard, translated_keysyms, translated_modifiers, |
389 | keyboard->pressed_keysyms_translated, | ||
390 | keyboard->modifiers_translated, | ||
391 | translated_keysyms_len); | 285 | translated_keysyms_len); |
392 | } | 286 | } |
393 | if (!handled && event->state == WLR_KEY_PRESSED) { | 287 | if (!handled && event->state == WLR_KEY_PRESSED) { |
394 | handled = | 288 | handled = keyboard_execute_compositor_binding( |
395 | keyboard_execute_compositor_binding(keyboard, | 289 | keyboard, raw_keysyms, raw_modifiers, |
396 | keyboard->pressed_keysyms_raw, keyboard->modifiers_raw, | ||
397 | raw_keysyms_len); | 290 | raw_keysyms_len); |
398 | } | 291 | } |
399 | 292 | ||
@@ -429,6 +322,10 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, | |||
429 | wl_list_init(&keyboard->keyboard_key.link); | 322 | wl_list_init(&keyboard->keyboard_key.link); |
430 | wl_list_init(&keyboard->keyboard_modifiers.link); | 323 | wl_list_init(&keyboard->keyboard_modifiers.link); |
431 | 324 | ||
325 | keyboard->state_keycodes.last_key_index = -1; | ||
326 | keyboard->state_keysyms_raw.last_key_index = -1; | ||
327 | keyboard->state_keysyms_translated.last_key_index = -1; | ||
328 | |||
432 | return keyboard; | 329 | return keyboard; |
433 | } | 330 | } |
434 | 331 | ||