From d77681ea3d84f1f02164f3cb0a29a42616afd23b Mon Sep 17 00:00:00 2001 From: frsfnrrg Date: Thu, 31 May 2018 15:16:01 -0400 Subject: [PATCH 01/11] Share common code between bindsym and bindcode commands --- sway/commands/bind.c | 172 +++++++++++++++---------------------------- 1 file changed, 61 insertions(+), 111 deletions(-) diff --git a/sway/commands/bind.c b/sway/commands/bind.c index c6b3368ad..f3bf72ad9 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -69,15 +69,17 @@ bool binding_key_compare(struct sway_binding *binding_a, return true; } -struct cmd_results *cmd_bindsym(int argc, char **argv) { +static struct cmd_results * cmd_bindsym_or_bindcode(int argc, char **argv, bool bindcode) { + const char* bindtype = bindcode ? "bindcode" : "bindsym"; + struct cmd_results *error = NULL; - if ((error = checkarg(argc, "bindsym", EXPECTED_MORE_THAN, 1))) { + if ((error = checkarg(argc, bindtype, EXPECTED_MORE_THAN, 1))) { return error; } struct sway_binding *binding = calloc(1, sizeof(struct sway_binding)); if (!binding) { - return cmd_results_new(CMD_FAILURE, "bindsym", + return cmd_results_new(CMD_FAILURE, bindtype, "Unable to allocate binding"); } binding->keys = create_list(); @@ -100,9 +102,9 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) { } if (argc < 2) { free_sway_binding(binding); - return cmd_results_new(CMD_FAILURE, "bindsym", - "Invalid bindsym command " - "(expected at least 2 non-option arguments, got %d)", argc); + return cmd_results_new(CMD_FAILURE, bindtype, + "Invalid %s command " + "(expected at least 2 non-option arguments, got %d)", bindtype, argc); } binding->command = join_args(argv + 1, argc - 1); @@ -115,36 +117,63 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) { binding->modifiers |= mod; continue; } - // Check for xkb key - xkb_keysym_t sym = xkb_keysym_from_name(split->items[i], - XKB_KEYSYM_CASE_INSENSITIVE); - // Check for mouse binding - if (strncasecmp(split->items[i], "button", strlen("button")) == 0 && - strlen(split->items[i]) == strlen("button0")) { - sym = ((char *)split->items[i])[strlen("button")] - '1' + BTN_LEFT; + xkb_keycode_t keycode; + xkb_keysym_t keysym; + if (bindcode) { + // parse keycode + keycode = (int)strtol(split->items[i], NULL, 10); + if (!xkb_keycode_is_legal_ext(keycode)) { + error = + cmd_results_new(CMD_INVALID, "bindcode", + "Invalid keycode '%s'", (char *)split->items[i]); + free_sway_binding(binding); + list_free(split); + return error; + } + } else { + // Check for xkb key + keysym = xkb_keysym_from_name(split->items[i], + XKB_KEYSYM_CASE_INSENSITIVE); + + // Check for mouse binding + if (strncasecmp(split->items[i], "button", strlen("button")) == 0 && + strlen(split->items[i]) == strlen("button0")) { + keysym = ((char *)split->items[i])[strlen("button")] - '1' + BTN_LEFT; + } + if (!keysym) { + struct cmd_results *ret = cmd_results_new(CMD_INVALID, "bindsym", + "Unknown key '%s'", (char *)split->items[i]); + free_sway_binding(binding); + free_flat_list(split); + return ret; + } } - if (!sym) { - struct cmd_results *ret = cmd_results_new(CMD_INVALID, "bindsym", - "Unknown key '%s'", (char *)split->items[i]); - free_sway_binding(binding); - free_flat_list(split); - return ret; - } - xkb_keysym_t *key = calloc(1, sizeof(xkb_keysym_t)); + uint32_t *key = calloc(1, sizeof(xkb_keysym_t)); if (!key) { free_sway_binding(binding); free_flat_list(split); - return cmd_results_new(CMD_FAILURE, "bindsym", + return cmd_results_new(CMD_FAILURE, bindtype, "Unable to allocate binding"); } - *key = sym; + + if (bindcode) { + *key = (uint32_t) (keycode - 8); + } else { + *key = (uint32_t) keysym; + } + list_add(binding->keys, key); } free_flat_list(split); binding->order = binding_order++; - list_t *mode_bindings = config->current_mode->keysym_bindings; + list_t *mode_bindings; + if (bindcode) { + mode_bindings = config->current_mode->keycode_bindings; + } else { + mode_bindings = config->current_mode->keysym_bindings; + } // overwrite the binding if it already exists bool overwritten = false; @@ -163,95 +192,16 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) { list_add(mode_bindings, binding); } - wlr_log(L_DEBUG, "bindsym - Bound %s to command %s", - argv[0], binding->command); + wlr_log(L_DEBUG, "%s - Bound %s to command %s", + bindtype, argv[0], binding->command); return cmd_results_new(CMD_SUCCESS, NULL, NULL); + +} + +struct cmd_results *cmd_bindsym(int argc, char **argv) { + return cmd_bindsym_or_bindcode(argc, argv, false); } struct cmd_results *cmd_bindcode(int argc, char **argv) { - struct cmd_results *error = NULL; - if ((error = checkarg(argc, "bindcode", EXPECTED_MORE_THAN, 1))) { - return error; - } - - struct sway_binding *binding = calloc(1, sizeof(struct sway_binding)); - if (!binding) { - return cmd_results_new(CMD_FAILURE, "bindsym", - "Unable to allocate binding"); - } - binding->keys = create_list(); - binding->modifiers = 0; - binding->release = false; - binding->locked = false; - binding->bindcode = true; - - // Handle --release and --locked - while (argc > 0) { - if (strcmp("--release", argv[0]) == 0) { - binding->release = true; - } else if (strcmp("--locked", argv[0]) == 0) { - binding->locked = true; - } else { - break; - } - argv++; - argc--; - } - if (argc < 2) { - free_sway_binding(binding); - return cmd_results_new(CMD_FAILURE, "bindcode", - "Invalid bindcode command " - "(expected at least 2 non-option arguments, got %d)", argc); - } - - binding->command = join_args(argv + 1, argc - 1); - - list_t *split = split_string(argv[0], "+"); - for (int i = 0; i < split->length; ++i) { - // Check for a modifier key - uint32_t mod; - if ((mod = get_modifier_mask_by_name(split->items[i])) > 0) { - binding->modifiers |= mod; - continue; - } - // parse keycode - xkb_keycode_t keycode = (int)strtol(split->items[i], NULL, 10); - if (!xkb_keycode_is_legal_ext(keycode)) { - error = - cmd_results_new(CMD_INVALID, "bindcode", - "Invalid keycode '%s'", (char *)split->items[i]); - free_sway_binding(binding); - list_free(split); - return error; - } - xkb_keycode_t *key = calloc(1, sizeof(xkb_keycode_t)); - *key = keycode - 8; - list_add(binding->keys, key); - } - free_flat_list(split); - - binding->order = binding_order++; - - list_t *mode_bindings = config->current_mode->keycode_bindings; - - // overwrite the binding if it already exists - bool overwritten = false; - for (int i = 0; i < mode_bindings->length; ++i) { - struct sway_binding *config_binding = mode_bindings->items[i]; - if (binding_key_compare(binding, config_binding)) { - wlr_log(L_DEBUG, "overwriting old binding with command '%s'", - config_binding->command); - free_sway_binding(config_binding); - mode_bindings->items[i] = binding; - overwritten = true; - } - } - - if (!overwritten) { - list_add(mode_bindings, binding); - } - - wlr_log(L_DEBUG, "bindcode - Bound %s to command %s", - argv[0], binding->command); - return cmd_results_new(CMD_SUCCESS, NULL, NULL); + return cmd_bindsym_or_bindcode(argc, argv, true); } From f5ed65e633b6ec3afe4a5f654a48fd96f187b282 Mon Sep 17 00:00:00 2001 From: frsfnrrg Date: Thu, 31 May 2018 19:33:38 -0400 Subject: [PATCH 02/11] Use XKB keycode numbering for bindcode --- sway/commands/bind.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/commands/bind.c b/sway/commands/bind.c index f3bf72ad9..f963001bb 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -158,7 +158,7 @@ static struct cmd_results * cmd_bindsym_or_bindcode(int argc, char **argv, bool } if (bindcode) { - *key = (uint32_t) (keycode - 8); + *key = (uint32_t) keycode; } else { *key = (uint32_t) keysym; } From a056419ad720e20fdc04f3a7d05bce62fa639755 Mon Sep 17 00:00:00 2001 From: frsfnrrg Date: Thu, 31 May 2018 19:35:17 -0400 Subject: [PATCH 03/11] 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. --- include/sway/input/keyboard.h | 18 +- sway/input/keyboard.c | 415 +++++++++++++--------------------- 2 files changed, 168 insertions(+), 265 deletions(-) diff --git a/include/sway/input/keyboard.h b/include/sway/input/keyboard.h index 8ec3eb356..c9a8d514b 100644 --- a/include/sway/input/keyboard.h +++ b/include/sway/input/keyboard.h @@ -3,7 +3,13 @@ #include "sway/input/seat.h" -#define SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP 32 +#define SWAY_KEYBOARD_PRESSED_KEYS_CAP 32 + +struct sway_shortcut_state { + uint32_t pressed_keys[SWAY_KEYBOARD_PRESSED_KEYS_CAP]; + uint32_t pressed_keycodes[SWAY_KEYBOARD_PRESSED_KEYS_CAP]; + int last_key_index; +}; struct sway_keyboard { struct sway_seat_device *seat_device; @@ -13,11 +19,11 @@ struct sway_keyboard { struct wl_listener keyboard_key; struct wl_listener keyboard_modifiers; - xkb_keysym_t pressed_keysyms_translated[SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP]; - uint32_t modifiers_translated; - - xkb_keysym_t pressed_keysyms_raw[SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP]; - uint32_t modifiers_raw; + struct sway_shortcut_state state_keysyms_translated; + struct sway_shortcut_state state_keysyms_raw; + struct sway_shortcut_state state_keycodes; + struct sway_binding *held_binding; + uint32_t last_modifiers; }; struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index e873eea31..e59d9c03b 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -9,88 +9,93 @@ #include "sway/commands.h" #include "log.h" -static bool keysym_is_modifier(xkb_keysym_t keysym) { - switch (keysym) { - case XKB_KEY_Shift_L: case XKB_KEY_Shift_R: - case XKB_KEY_Control_L: case XKB_KEY_Control_R: - case XKB_KEY_Caps_Lock: - case XKB_KEY_Shift_Lock: - case XKB_KEY_Meta_L: case XKB_KEY_Meta_R: - case XKB_KEY_Alt_L: case XKB_KEY_Alt_R: - case XKB_KEY_Super_L: case XKB_KEY_Super_R: - case XKB_KEY_Hyper_L: case XKB_KEY_Hyper_R: - return true; - default: - return false; - } -} - -static size_t pressed_keysyms_length(xkb_keysym_t *pressed_keysyms) { - size_t n = 0; - for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP; ++i) { - if (pressed_keysyms[i] != XKB_KEY_NoSymbol) { - ++n; +/** + * Update the shortcut model state in response to new input + */ +static void update_shortcut_model(struct sway_shortcut_state* state, + struct wlr_event_keyboard_key * event, + uint32_t new_key, + bool last_key_was_a_modifier) { + if (event->state == WLR_KEY_PRESSED) { + if (last_key_was_a_modifier && state->last_key_index >= 0) { + // Last pressed key before this one was a modifier. We nullify + // the key id but not the keycode (as that is used for erasure + // on release) + state->pressed_keys[state->last_key_index] = 0; + state->last_key_index = -1; } - } - return n; -} -static ssize_t pressed_keysyms_index(xkb_keysym_t *pressed_keysyms, - xkb_keysym_t keysym) { - for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYSYMS_CAP; ++i) { - if (pressed_keysyms[i] == keysym) { - return i; + // Add current key to set; there may be duplicates + for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYS_CAP; i++) { + if (!state->pressed_keys[i]) { + state->pressed_keys[i] = new_key; + state->pressed_keycodes[i] = event->keycode; + state->last_key_index = i; + break; + } } - } - return -1; -} - -static void pressed_keysyms_add(xkb_keysym_t *pressed_keysyms, - xkb_keysym_t keysym) { - ssize_t i = pressed_keysyms_index(pressed_keysyms, keysym); - if (i < 0) { - i = pressed_keysyms_index(pressed_keysyms, XKB_KEY_NoSymbol); - if (i >= 0) { - pressed_keysyms[i] = keysym; + } else { + for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYS_CAP; i++) { + // The same keycode may match multiple keysyms. + if (state->pressed_keycodes[i] == event->keycode) { + state->pressed_keys[i] = 0; + state->pressed_keycodes[i] = 0; + } } } } -static void pressed_keysyms_remove(xkb_keysym_t *pressed_keysyms, - xkb_keysym_t keysym) { - ssize_t i = pressed_keysyms_index(pressed_keysyms, keysym); - if (i >= 0) { - pressed_keysyms[i] = XKB_KEY_NoSymbol; +/** + * + * Returns a binding which matches the shortcut model state (ignoring the + * `release` flag). + */ +static struct sway_binding* check_shortcut_model( + struct sway_shortcut_state* state, list_t* bindings, + uint32_t modifiers, bool locked) { + int npressed_keys = 0; + for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYS_CAP; i++) { + if (state->pressed_keys[i]) { + ++npressed_keys; + } } -} + for (int i = 0; i < bindings->length; ++i) { + struct sway_binding *binding = bindings->items[i]; -static void pressed_keysyms_update(xkb_keysym_t *pressed_keysyms, - const xkb_keysym_t *keysyms, size_t keysyms_len, - enum wlr_key_state state) { - for (size_t i = 0; i < keysyms_len; ++i) { - if (keysym_is_modifier(keysyms[i])) { + if (modifiers ^ binding->modifiers || + npressed_keys != binding->keys->length || + locked > binding->locked) { continue; } - if (state == WLR_KEY_PRESSED) { - pressed_keysyms_add(pressed_keysyms, keysyms[i]); - } else { // WLR_KEY_RELEASED - pressed_keysyms_remove(pressed_keysyms, keysyms[i]); + + bool match = true; + for (int j = 0; j < binding->keys->length; ++j) { + uint32_t key = *(uint32_t*)binding->keys->items[j]; + + bool key_found = false; + for (int k = 0; k < SWAY_KEYBOARD_PRESSED_KEYS_CAP; k++) { + if (state->pressed_keys[k] == key) { + key_found = true; + break; + } + } + if (!key_found) { + match = false; + break; + } + } + + if (match) { + return binding; } } + + return NULL; } -static bool binding_matches_key_state(struct sway_binding *binding, - enum wlr_key_state key_state) { - if (key_state == WLR_KEY_PRESSED && !binding->release) { - return true; - } - if (key_state == WLR_KEY_RELEASED && binding->release) { - return true; - } - - return false; -} - +/** + * Execute the command associated to a binding + */ static void keyboard_execute_command(struct sway_keyboard *keyboard, struct sway_binding *binding) { wlr_log(L_DEBUG, "running command for binding: %s", @@ -113,7 +118,7 @@ static void keyboard_execute_command(struct sway_keyboard *keyboard, * should be propagated to clients. */ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard, - xkb_keysym_t *pressed_keysyms, uint32_t modifiers, size_t keysyms_len) { + const xkb_keysym_t *pressed_keysyms, uint32_t modifiers, size_t keysyms_len) { for (size_t i = 0; i < keysyms_len; ++i) { xkb_keysym_t keysym = pressed_keysyms[i]; if (keysym >= XKB_KEY_XF86Switch_VT_1 && @@ -133,157 +138,6 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard, return false; } -/** - * Execute keyboard bindings bound with `bindysm` for the given keyboard state. - * - * Returns true if the keysym was handled by a binding and false if the event - * should be propagated to clients. - */ -static bool keyboard_execute_bindsym(struct sway_keyboard *keyboard, - xkb_keysym_t *pressed_keysyms, uint32_t modifiers, - enum wlr_key_state key_state, bool locked) { - // configured bindings - int n = pressed_keysyms_length(pressed_keysyms); - list_t *keysym_bindings = config->current_mode->keysym_bindings; - for (int i = 0; i < keysym_bindings->length; ++i) { - struct sway_binding *binding = keysym_bindings->items[i]; - if (!binding_matches_key_state(binding, key_state) || - modifiers ^ binding->modifiers || - n != binding->keys->length || locked > binding->locked) { - continue; - } - - bool match = true; - for (int j = 0; j < binding->keys->length; ++j) { - match = - pressed_keysyms_index(pressed_keysyms, - *(int*)binding->keys->items[j]) >= 0; - - if (!match) { - break; - } - } - - if (match) { - keyboard_execute_command(keyboard, binding); - return true; - } - } - - return false; -} - -static bool binding_matches_keycodes(struct wlr_keyboard *keyboard, - struct sway_binding *binding, struct wlr_event_keyboard_key *event, bool locked) { - assert(binding->bindcode); - - uint32_t keycode = event->keycode + 8; - - if (!binding_matches_key_state(binding, event->state)) { - return false; - } - - if (locked > binding->locked) { - return false; - } - - uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard); - if (modifiers ^ binding->modifiers) { - return false; - } - - // on release, the released key must be in the binding - if (event->state == WLR_KEY_RELEASED) { - bool found = false; - for (int i = 0; i < binding->keys->length; ++i) { - uint32_t binding_keycode = *(uint32_t*)binding->keys->items[i] + 8; - if (binding_keycode == keycode) { - found = true; - break; - } - } - if (!found) { - return false; - } - } - - // every keycode in the binding must be present in the pressed keys on the - // keyboard - for (int i = 0; i < binding->keys->length; ++i) { - uint32_t binding_keycode = *(uint32_t*)binding->keys->items[i] + 8; - if (event->state == WLR_KEY_RELEASED && keycode == binding_keycode) { - continue; - } - - bool found = false; - for (size_t j = 0; j < keyboard->num_keycodes; ++j) { - xkb_keycode_t keycode = keyboard->keycodes[j] + 8; - if (keycode == binding_keycode) { - found = true; - break; - } - } - - if (!found) { - return false; - } - } - - // every keycode pressed on the keyboard must be present within the binding - // keys (unless it is a modifier) - for (size_t i = 0; i < keyboard->num_keycodes; ++i) { - xkb_keycode_t keycode = keyboard->keycodes[i] + 8; - bool found = false; - for (int j = 0; j < binding->keys->length; ++j) { - uint32_t binding_keycode = *(uint32_t*)binding->keys->items[j] + 8; - if (binding_keycode == keycode) { - found = true; - break; - } - } - - if (!found) { - if (!binding->modifiers) { - return false; - } - - // check if it is a modifier, which we know matched from the check - // above - const xkb_keysym_t *keysyms; - int num_keysyms = - xkb_state_key_get_syms(keyboard->xkb_state, - keycode, &keysyms); - if (num_keysyms != 1 || !keysym_is_modifier(keysyms[0])) { - return false; - } - } - } - - return true; -} - -/** - * Execute keyboard bindings bound with `bindcode` for the given keyboard state. - * - * Returns true if the keysym was handled by a binding and false if the event - * should be propagated to clients. - */ -static bool keyboard_execute_bindcode(struct sway_keyboard *keyboard, - struct wlr_event_keyboard_key *event, bool locked) { - struct wlr_keyboard *wlr_keyboard = - keyboard->seat_device->input_device->wlr_device->keyboard; - list_t *keycode_bindings = config->current_mode->keycode_bindings; - for (int i = 0; i < keycode_bindings->length; ++i) { - struct sway_binding *binding = keycode_bindings->items[i]; - if (binding_matches_keycodes(wlr_keyboard, binding, event, locked)) { - keyboard_execute_command(keyboard, binding); - return true; - } - } - - return false; -} - /** * Get keysyms and modifiers from the keyboard as xkb sees them. * @@ -339,61 +193,100 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { struct wlr_event_keyboard_key *event = data; bool input_inhibited = keyboard->seat_device->sway_seat->exclusive_client != NULL; + // Identify new keycode, raw keysym(s), and translated keysym(s) xkb_keycode_t keycode = event->keycode + 8; - bool handled = false; - // handle keycodes - handled = keyboard_execute_bindcode(keyboard, event, input_inhibited); - - // handle translated keysyms - if (!handled && event->state == WLR_KEY_RELEASED) { - handled = keyboard_execute_bindsym(keyboard, - keyboard->pressed_keysyms_translated, - keyboard->modifiers_translated, - event->state, input_inhibited); - } const xkb_keysym_t *translated_keysyms; + uint32_t translated_modifiers; size_t translated_keysyms_len = keyboard_keysyms_translated(keyboard, keycode, &translated_keysyms, - &keyboard->modifiers_translated); - pressed_keysyms_update(keyboard->pressed_keysyms_translated, - translated_keysyms, translated_keysyms_len, event->state); - if (!handled && event->state == WLR_KEY_PRESSED) { - handled = keyboard_execute_bindsym(keyboard, - keyboard->pressed_keysyms_translated, - keyboard->modifiers_translated, - event->state, input_inhibited); + &translated_modifiers); + + const xkb_keysym_t *raw_keysyms; + uint32_t raw_modifiers; + size_t raw_keysyms_len = + keyboard_keysyms_raw(keyboard, keycode, &raw_keysyms, &raw_modifiers); + + struct wlr_input_device *device = + keyboard->seat_device->input_device->wlr_device; + uint32_t code_modifiers = wlr_keyboard_get_modifiers(device->keyboard); + + bool last_key_was_a_modifier = code_modifiers != keyboard->last_modifiers; + keyboard->last_modifiers = code_modifiers; + + // Update shortcut models + update_shortcut_model(&keyboard->state_keycodes, event, + (uint32_t)keycode, last_key_was_a_modifier); + for (size_t i=0;istate_keysyms_translated, + event, (uint32_t)translated_keysyms[i], + last_key_was_a_modifier); + } + for (size_t i=0;istate_keysyms_raw, + event, (uint32_t)raw_keysyms[i], + last_key_was_a_modifier); } - // Handle raw keysyms - if (!handled && event->state == WLR_KEY_RELEASED) { - handled = keyboard_execute_bindsym(keyboard, - keyboard->pressed_keysyms_raw, keyboard->modifiers_raw, - event->state, input_inhibited); + // identify which binding should be executed. + struct sway_binding *binding = + check_shortcut_model(&keyboard->state_keycodes, + config->current_mode->keycode_bindings, + code_modifiers, input_inhibited); + for (size_t i=0;istate_keysyms_translated, + config->current_mode->keysym_bindings, + translated_modifiers, input_inhibited); + if (translated_binding && !binding) { + binding = translated_binding; + } else if (binding && translated_binding && binding != translated_binding) { + wlr_log(L_DEBUG, "encountered duplicate bindings %d and %d", + binding->order, translated_binding->order); + } } - const xkb_keysym_t *raw_keysyms; - size_t raw_keysyms_len = - keyboard_keysyms_raw(keyboard, keycode, &raw_keysyms, &keyboard->modifiers_raw); - pressed_keysyms_update(keyboard->pressed_keysyms_raw, raw_keysyms, - raw_keysyms_len, event->state); - if (!handled && event->state == WLR_KEY_PRESSED) { - handled = keyboard_execute_bindsym(keyboard, - keyboard->pressed_keysyms_raw, keyboard->modifiers_raw, - event->state, input_inhibited); + for (size_t i=0;istate_keysyms_raw, + config->current_mode->keysym_bindings, + raw_modifiers, input_inhibited); + if (raw_binding && !binding) { + binding = raw_binding; + } else if (binding && raw_binding && binding != raw_binding) { + wlr_log(L_DEBUG, "encountered duplicate bindings %d and %d", + binding->order, raw_binding->order); + } + } + + bool handled = false; + + // Execute the identified binding if need be. + if (keyboard->held_binding && binding != keyboard->held_binding && + event->state == WLR_KEY_RELEASED) { + keyboard_execute_command(keyboard, keyboard->held_binding); + handled = true; + } + if (binding != keyboard->held_binding) { + keyboard->held_binding = NULL; + } + if (binding && event->state == WLR_KEY_PRESSED) { + if (binding->release) { + keyboard->held_binding = binding; + } else { + keyboard_execute_command(keyboard, binding); + handled = true; + } } // Compositor bindings if (!handled && event->state == WLR_KEY_PRESSED) { - handled = - keyboard_execute_compositor_binding(keyboard, - keyboard->pressed_keysyms_translated, - keyboard->modifiers_translated, + handled = keyboard_execute_compositor_binding( + keyboard, translated_keysyms, translated_modifiers, translated_keysyms_len); } if (!handled && event->state == WLR_KEY_PRESSED) { - handled = - keyboard_execute_compositor_binding(keyboard, - keyboard->pressed_keysyms_raw, keyboard->modifiers_raw, + handled = keyboard_execute_compositor_binding( + keyboard, raw_keysyms, raw_modifiers, raw_keysyms_len); } @@ -429,6 +322,10 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, wl_list_init(&keyboard->keyboard_key.link); wl_list_init(&keyboard->keyboard_modifiers.link); + keyboard->state_keycodes.last_key_index = -1; + keyboard->state_keysyms_raw.last_key_index = -1; + keyboard->state_keysyms_translated.last_key_index = -1; + return keyboard; } From 705230a26c09c578960b90d1c226680aad0b359d Mon Sep 17 00:00:00 2001 From: frsfnrrg Date: Thu, 31 May 2018 19:54:31 -0400 Subject: [PATCH 04/11] Fix binding bindcode flag initialization --- sway/commands/bind.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/commands/bind.c b/sway/commands/bind.c index f963001bb..139361ddf 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -86,7 +86,7 @@ static struct cmd_results * cmd_bindsym_or_bindcode(int argc, char **argv, bool binding->modifiers = 0; binding->release = false; binding->locked = false; - binding->bindcode = false; + binding->bindcode = bindcode; // Handle --release and --locked while (argc > 0) { From f2fe93d1718d1eacccb27fffa58d4cfecd20edfc Mon Sep 17 00:00:00 2001 From: frsfnrrg Date: Thu, 31 May 2018 20:23:19 -0400 Subject: [PATCH 05/11] Remove almost-always redundant loop in key handling --- sway/input/keyboard.c | 48 ++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index e59d9c03b..c0f637f03 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -229,33 +229,29 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { } // identify which binding should be executed. - struct sway_binding *binding = - check_shortcut_model(&keyboard->state_keycodes, - config->current_mode->keycode_bindings, - code_modifiers, input_inhibited); - for (size_t i=0;istate_keysyms_translated, - config->current_mode->keysym_bindings, - translated_modifiers, input_inhibited); - if (translated_binding && !binding) { - binding = translated_binding; - } else if (binding && translated_binding && binding != translated_binding) { - wlr_log(L_DEBUG, "encountered duplicate bindings %d and %d", - binding->order, translated_binding->order); - } + struct sway_binding *binding = check_shortcut_model( + &keyboard->state_keycodes, + config->current_mode->keycode_bindings, + code_modifiers, input_inhibited); + struct sway_binding *translated_binding = check_shortcut_model( + &keyboard->state_keysyms_translated, + config->current_mode->keysym_bindings, + translated_modifiers, input_inhibited); + if (translated_binding && !binding) { + binding = translated_binding; + } else if (binding && translated_binding && binding != translated_binding) { + wlr_log(L_DEBUG, "encountered duplicate bindings %d and %d", + binding->order, translated_binding->order); } - for (size_t i=0;istate_keysyms_raw, - config->current_mode->keysym_bindings, - raw_modifiers, input_inhibited); - if (raw_binding && !binding) { - binding = raw_binding; - } else if (binding && raw_binding && binding != raw_binding) { - wlr_log(L_DEBUG, "encountered duplicate bindings %d and %d", - binding->order, raw_binding->order); - } + struct sway_binding *raw_binding = check_shortcut_model( + &keyboard->state_keysyms_raw, + config->current_mode->keysym_bindings, + raw_modifiers, input_inhibited); + if (raw_binding && !binding) { + binding = raw_binding; + } else if (binding && raw_binding && binding != raw_binding) { + wlr_log(L_DEBUG, "encountered duplicate bindings %d and %d", + binding->order, raw_binding->order); } bool handled = false; From eb00edc6e278a3c5d603a24ef8ef73c3c5888f12 Mon Sep 17 00:00:00 2001 From: frsfnrrg Date: Fri, 1 Jun 2018 06:50:53 -0400 Subject: [PATCH 06/11] Style fixes for bind.c --- sway/commands/bind.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 139361ddf..d0e3e22f3 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -69,8 +69,9 @@ bool binding_key_compare(struct sway_binding *binding_a, return true; } -static struct cmd_results * cmd_bindsym_or_bindcode(int argc, char **argv, bool bindcode) { - const char* bindtype = bindcode ? "bindcode" : "bindsym"; +static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, + bool bindcode) { + const char *bindtype = bindcode ? "bindcode" : "bindsym"; struct cmd_results *error = NULL; if ((error = checkarg(argc, bindtype, EXPECTED_MORE_THAN, 1))) { @@ -149,7 +150,7 @@ static struct cmd_results * cmd_bindsym_or_bindcode(int argc, char **argv, bool return ret; } } - uint32_t *key = calloc(1, sizeof(xkb_keysym_t)); + uint32_t *key = calloc(1, sizeof(uint32_t)); if (!key) { free_sway_binding(binding); free_flat_list(split); @@ -158,9 +159,9 @@ static struct cmd_results * cmd_bindsym_or_bindcode(int argc, char **argv, bool } if (bindcode) { - *key = (uint32_t) keycode; + *key = (uint32_t)keycode; } else { - *key = (uint32_t) keysym; + *key = (uint32_t)keysym; } list_add(binding->keys, key); From c53e4e6cdec4a1873d8a0d47fc90e95d2b949d04 Mon Sep 17 00:00:00 2001 From: frsfnrrg Date: Fri, 1 Jun 2018 06:51:07 -0400 Subject: [PATCH 07/11] Style fixed for keyboard.c --- sway/input/keyboard.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index c0f637f03..272630465 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -12,10 +12,9 @@ /** * Update the shortcut model state in response to new input */ -static void update_shortcut_model(struct sway_shortcut_state* state, - struct wlr_event_keyboard_key * event, - uint32_t new_key, - bool last_key_was_a_modifier) { +static void update_shortcut_model(struct sway_shortcut_state *state, + struct wlr_event_keyboard_key *event, uint32_t new_key, + bool last_key_was_a_modifier) { if (event->state == WLR_KEY_PRESSED) { if (last_key_was_a_modifier && state->last_key_index >= 0) { // Last pressed key before this one was a modifier. We nullify @@ -26,7 +25,7 @@ static void update_shortcut_model(struct sway_shortcut_state* state, } // Add current key to set; there may be duplicates - for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYS_CAP; i++) { + for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYS_CAP; ++i) { if (!state->pressed_keys[i]) { state->pressed_keys[i] = new_key; state->pressed_keycodes[i] = event->keycode; @@ -35,7 +34,7 @@ static void update_shortcut_model(struct sway_shortcut_state* state, } } } else { - for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYS_CAP; i++) { + for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYS_CAP; ++i) { // The same keycode may match multiple keysyms. if (state->pressed_keycodes[i] == event->keycode) { state->pressed_keys[i] = 0; @@ -50,11 +49,11 @@ static void update_shortcut_model(struct sway_shortcut_state* state, * Returns a binding which matches the shortcut model state (ignoring the * `release` flag). */ -static struct sway_binding* check_shortcut_model( - struct sway_shortcut_state* state, list_t* bindings, +static struct sway_binding *check_shortcut_model( + struct sway_shortcut_state *state, list_t *bindings, uint32_t modifiers, bool locked) { int npressed_keys = 0; - for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYS_CAP; i++) { + for (size_t i = 0; i < SWAY_KEYBOARD_PRESSED_KEYS_CAP; ++i) { if (state->pressed_keys[i]) { ++npressed_keys; } @@ -70,10 +69,10 @@ static struct sway_binding* check_shortcut_model( bool match = true; for (int j = 0; j < binding->keys->length; ++j) { - uint32_t key = *(uint32_t*)binding->keys->items[j]; + uint32_t key = *(uint32_t *)binding->keys->items[j]; bool key_found = false; - for (int k = 0; k < SWAY_KEYBOARD_PRESSED_KEYS_CAP; k++) { + for (int k = 0; k < SWAY_KEYBOARD_PRESSED_KEYS_CAP; ++k) { if (state->pressed_keys[k] == key) { key_found = true; break; @@ -216,13 +215,13 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { // Update shortcut models update_shortcut_model(&keyboard->state_keycodes, event, - (uint32_t)keycode, last_key_was_a_modifier); - for (size_t i=0;istate_keysyms_translated, event, (uint32_t)translated_keysyms[i], last_key_was_a_modifier); } - for (size_t i=0;istate_keysyms_raw, event, (uint32_t)raw_keysyms[i], last_key_was_a_modifier); From 83d559dd03e7e16062881932898b5bdaa5a5f689 Mon Sep 17 00:00:00 2001 From: frsfnrrg Date: Fri, 1 Jun 2018 17:04:04 -0400 Subject: [PATCH 08/11] Rename check_shortcut_model to get_active_binding --- sway/input/keyboard.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index 272630465..b7c622aac 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -49,7 +49,7 @@ static void update_shortcut_model(struct sway_shortcut_state *state, * Returns a binding which matches the shortcut model state (ignoring the * `release` flag). */ -static struct sway_binding *check_shortcut_model( +static struct sway_binding *get_active_binding( struct sway_shortcut_state *state, list_t *bindings, uint32_t modifiers, bool locked) { int npressed_keys = 0; @@ -228,11 +228,11 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { } // identify which binding should be executed. - struct sway_binding *binding = check_shortcut_model( + struct sway_binding *binding = get_active_binding( &keyboard->state_keycodes, config->current_mode->keycode_bindings, code_modifiers, input_inhibited); - struct sway_binding *translated_binding = check_shortcut_model( + struct sway_binding *translated_binding = get_active_binding( &keyboard->state_keysyms_translated, config->current_mode->keysym_bindings, translated_modifiers, input_inhibited); @@ -242,7 +242,7 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { wlr_log(L_DEBUG, "encountered duplicate bindings %d and %d", binding->order, translated_binding->order); } - struct sway_binding *raw_binding = check_shortcut_model( + struct sway_binding *raw_binding = get_active_binding( &keyboard->state_keysyms_raw, config->current_mode->keysym_bindings, raw_modifiers, input_inhibited); From c664d780cc9e7e8234cdaface63f8ff93e7ebfb2 Mon Sep 17 00:00:00 2001 From: frsfnrrg Date: Fri, 1 Jun 2018 17:04:58 -0400 Subject: [PATCH 09/11] Comment to explain sway_shortcut_state lists --- include/sway/input/keyboard.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/include/sway/input/keyboard.h b/include/sway/input/keyboard.h index c9a8d514b..e99a54b17 100644 --- a/include/sway/input/keyboard.h +++ b/include/sway/input/keyboard.h @@ -6,7 +6,20 @@ #define SWAY_KEYBOARD_PRESSED_KEYS_CAP 32 struct sway_shortcut_state { + /** + * A list of pressed key ids (either keysyms or keycodes), + * including duplicates when different keycodes produce the same key id. + * + * Each key id is associated with the keycode (in `pressed_keycodes`) + * whose press generated it, so that the key id can be removed on + * keycode release without recalculating the transient link between + * keycode and key id at the time of the key press. + */ uint32_t pressed_keys[SWAY_KEYBOARD_PRESSED_KEYS_CAP]; + /** + * The list of keycodes associated to currently pressed key ids, + * including duplicates when a keycode generates multiple key ids. + */ uint32_t pressed_keycodes[SWAY_KEYBOARD_PRESSED_KEYS_CAP]; int last_key_index; }; From 480f0c593cc99a1a77b8b26027f2c9e065fb790c Mon Sep 17 00:00:00 2001 From: frsfnrrg Date: Fri, 1 Jun 2018 17:56:13 -0400 Subject: [PATCH 10/11] Rename update_shortcut_model to update_shortcut_state --- sway/input/keyboard.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index b7c622aac..71737cc40 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -12,7 +12,7 @@ /** * Update the shortcut model state in response to new input */ -static void update_shortcut_model(struct sway_shortcut_state *state, +static void update_shortcut_state(struct sway_shortcut_state *state, struct wlr_event_keyboard_key *event, uint32_t new_key, bool last_key_was_a_modifier) { if (event->state == WLR_KEY_PRESSED) { @@ -213,16 +213,16 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { bool last_key_was_a_modifier = code_modifiers != keyboard->last_modifiers; keyboard->last_modifiers = code_modifiers; - // Update shortcut models - update_shortcut_model(&keyboard->state_keycodes, event, + // Update shortcut model state + update_shortcut_state(&keyboard->state_keycodes, event, (uint32_t)keycode, last_key_was_a_modifier); for (size_t i = 0; i < translated_keysyms_len; ++i) { - update_shortcut_model(&keyboard->state_keysyms_translated, + update_shortcut_state(&keyboard->state_keysyms_translated, event, (uint32_t)translated_keysyms[i], last_key_was_a_modifier); } for (size_t i = 0; i < raw_keysyms_len; ++i) { - update_shortcut_model(&keyboard->state_keysyms_raw, + update_shortcut_state(&keyboard->state_keysyms_raw, event, (uint32_t)raw_keysyms[i], last_key_was_a_modifier); } From 78b7b468954c561bb9f800aef54afacbbe6d3d40 Mon Sep 17 00:00:00 2001 From: frsfnrrg Date: Fri, 1 Jun 2018 18:40:51 -0400 Subject: [PATCH 11/11] Style fix, redundant entry removal, fix extra keysym delete --- sway/input/keyboard.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index 71737cc40..420cefa64 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -17,9 +17,8 @@ static void update_shortcut_state(struct sway_shortcut_state *state, bool last_key_was_a_modifier) { if (event->state == WLR_KEY_PRESSED) { if (last_key_was_a_modifier && state->last_key_index >= 0) { - // Last pressed key before this one was a modifier. We nullify - // the key id but not the keycode (as that is used for erasure - // on release) + // Last pressed key before this one was a modifier + state->pressed_keycodes[state->last_key_index] = 0; state->pressed_keys[state->last_key_index] = 0; state->last_key_index = -1; } @@ -62,8 +61,8 @@ static struct sway_binding *get_active_binding( struct sway_binding *binding = bindings->items[i]; if (modifiers ^ binding->modifiers || - npressed_keys != binding->keys->length || - locked > binding->locked) { + npressed_keys != binding->keys->length || + locked > binding->locked) { continue; } @@ -219,12 +218,12 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { for (size_t i = 0; i < translated_keysyms_len; ++i) { update_shortcut_state(&keyboard->state_keysyms_translated, event, (uint32_t)translated_keysyms[i], - last_key_was_a_modifier); + last_key_was_a_modifier && i == 0); } for (size_t i = 0; i < raw_keysyms_len; ++i) { update_shortcut_state(&keyboard->state_keysyms_raw, event, (uint32_t)raw_keysyms[i], - last_key_was_a_modifier); + last_key_was_a_modifier && i == 0); } // identify which binding should be executed.