diff --git a/include/config.h b/include/config.h index 5e1c39f3a..8220f8046 100644 --- a/include/config.h +++ b/include/config.h @@ -22,6 +22,7 @@ struct sway_variable { */ struct sway_binding { int order; + bool release; list_t *keys; uint32_t modifiers; char *command; diff --git a/include/input_state.h b/include/input_state.h index 79e27d91d..903301fb1 100644 --- a/include/input_state.h +++ b/include/input_state.h @@ -9,6 +9,9 @@ // returns true if key has been pressed, otherwise false bool check_key(uint32_t key_sym, uint32_t key_code); +// returns true if key_sym matches latest released key. +bool check_released_key(uint32_t key_sym); + // sets a key as pressed void press_key(uint32_t key_sym, uint32_t key_code); diff --git a/sway/commands.c b/sway/commands.c index 4e5bc712c..6a3165969 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -163,9 +163,25 @@ static struct cmd_results *cmd_bindsym(int argc, char **argv) { return cmd_results_new(CMD_FAILURE, "bindsym", "Can only be used in config file."); } + struct sway_binding *binding = malloc(sizeof(struct sway_binding)); binding->keys = create_list(); binding->modifiers = 0; + binding->release = false; + + // Handle --release + if (strcmp("--release", argv[0]) == 0) { + if (argc >= 3) { + binding->release = true; + argv++; + argc--; + } else { + return cmd_results_new(CMD_FAILURE, "bindsym", + "Invalid bindsym command" + "(expected more than 2 arguments, got %d)", argc); + } + } + binding->command = join_args(argv + 1, argc - 1); list_t *split = split_string(argv[0], "+"); diff --git a/sway/handlers.c b/sway/handlers.c index 470f3c953..e0acebea4 100644 --- a/sway/handlers.c +++ b/sway/handlers.c @@ -336,6 +336,43 @@ static void handle_view_state_request(wlc_handle view, enum wlc_view_state_bit s return; } +static bool handle_bindsym(struct sway_binding *binding) { + bool match = false; + int i; + for (i = 0; i < binding->keys->length; ++i) { + xkb_keysym_t *key = binding->keys->items[i]; + if ((match = check_key(*key, 0)) == false) { + break; + } + } + + if (match) { + struct cmd_results *res = handle_command(binding->command); + if (res->status != CMD_SUCCESS) { + sway_log(L_ERROR, "Command '%s' failed: %s", res->input, res->error); + } + free_cmd_results(res); + return true; + } + + return false; +} + +static bool handle_bindsym_release(struct sway_binding *binding) { + if (binding->keys->length == 1) { + xkb_keysym_t *key = binding->keys->items[0]; + if (check_released_key(*key)) { + struct cmd_results *res = handle_command(binding->command); + if (res->status != CMD_SUCCESS) { + sway_log(L_ERROR, "Command '%s' failed: %s", res->input, res->error); + } + free_cmd_results(res); + return true; + } + } + + return false; +} static bool handle_key(wlc_handle view, uint32_t time, const struct wlc_modifiers *modifiers, uint32_t key, enum wlc_key_state state) { @@ -366,33 +403,6 @@ static bool handle_key(wlc_handle view, uint32_t time, const struct wlc_modifier release_key(sym, key); } - for (i = 0; i < mode->bindings->length; ++i) { - struct sway_binding *binding = mode->bindings->items[i]; - - if ((modifiers->mods ^ binding->modifiers) == 0) { - bool match = false; - int j; - for (j = 0; j < binding->keys->length; ++j) { - xkb_keysym_t *key = binding->keys->items[j]; - if ((match = check_key(*key, 0)) == false) { - break; - } - } - if (match) { - if (state == WLC_KEY_STATE_PRESSED) { - struct cmd_results *res = handle_command(binding->command); - if (res->status != CMD_SUCCESS) { - sway_log(L_ERROR, "Command '%s' failed: %s", res->input, res->error); - } - free_cmd_results(res); - return EVENT_HANDLED; - } else if (state == WLC_KEY_STATE_RELEASED) { - // TODO: --released - } - } - } - } - // handle bar modifiers pressed/released uint32_t modifier; for (i = 0; i < config->active_bar_modifiers->length; ++i) { @@ -409,6 +419,27 @@ static bool handle_key(wlc_handle view, uint32_t time, const struct wlc_modifier } // update modifiers state modifiers_state_update(modifiers->mods); + + // handle bindings + for (i = 0; i < mode->bindings->length; ++i) { + struct sway_binding *binding = mode->bindings->items[i]; + if ((modifiers->mods ^ binding->modifiers) == 0) { + switch (state) { + case WLC_KEY_STATE_PRESSED: { + if (!binding->release && handle_bindsym(binding)) { + return EVENT_HANDLED; + } + break; + } + case WLC_KEY_STATE_RELEASED: + if (binding->release && handle_bindsym_release(binding)) { + return EVENT_HANDLED; + } + break; + } + } + } + return EVENT_PASSTHROUGH; } diff --git a/sway/input_state.c b/sway/input_state.c index 2f40b6c2f..868680831 100644 --- a/sway/input_state.c +++ b/sway/input_state.c @@ -22,6 +22,8 @@ struct key_state { static struct key_state key_state_array[KEY_STATE_MAX_LENGTH]; +static struct key_state last_released; + static uint32_t modifiers_state; void input_init(void) { @@ -31,6 +33,9 @@ void input_init(void) { key_state_array[i] = none; } + struct key_state none = { 0, 0, 0 }; + last_released = none; + modifiers_state = 0; } @@ -76,6 +81,12 @@ bool check_key(uint32_t key_sym, uint32_t key_code) { return find_key(key_sym, key_code, false) < KEY_STATE_MAX_LENGTH; } +bool check_released_key(uint32_t key_sym) { + return (key_sym != 0 + && (last_released.key_sym == key_sym + || last_released.alt_sym == key_sym)); +} + void press_key(uint32_t key_sym, uint32_t key_code) { if (key_code == 0) { return; @@ -94,6 +105,9 @@ void press_key(uint32_t key_sym, uint32_t key_code) { void release_key(uint32_t key_sym, uint32_t key_code) { uint8_t index = find_key(key_sym, key_code, true); if (index < KEY_STATE_MAX_LENGTH) { + last_released.key_sym = key_state_array[index].key_sym; + last_released.alt_sym = key_state_array[index].alt_sym; + last_released.key_code = key_state_array[index].key_code; struct key_state none = { 0, 0, 0 }; key_state_array[index] = none; }