aboutsummaryrefslogtreecommitdiffstats
path: root/sway/input/keyboard.c
diff options
context:
space:
mode:
authorLibravatar frsfnrrg <frsfnrrg@users.noreply.github.com>2018-05-31 19:35:17 -0400
committerLibravatar frsfnrrg <frsfnrrg@users.noreply.github.com>2018-06-01 18:52:36 -0400
commita056419ad720e20fdc04f3a7d05bce62fa639755 (patch)
tree5248788d8b24cbcea07897b884c243700997e460 /sway/input/keyboard.c
parentUse XKB keycode numbering for bindcode (diff)
downloadsway-a056419ad720e20fdc04f3a7d05bce62fa639755.tar.gz
sway-a056419ad720e20fdc04f3a7d05bce62fa639755.tar.zst
sway-a056419ad720e20fdc04f3a7d05bce62fa639755.zip
Rewrite shortcut handling code to avoid hardcoded values
The same shortcut algorithm is now used for keycodes, raw keysyms, and translated keysyms. Pressed keysyms are now stored in association with the keycodes that generated them. Modifier keycodes (and associated keysyms) are identified retroactively by the subsequent change to the modifier flags.
Diffstat (limited to 'sway/input/keyboard.c')
-rw-r--r--sway/input/keyboard.c411
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
12static 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: 15static 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
28static 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
38static 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) {
48static 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
59static 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 */
53static 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
67static 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
82static 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 */
94static void keyboard_execute_command(struct sway_keyboard *keyboard, 99static 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 */
115static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard, 120static 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 */
142static 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
176static 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 */
271static 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