diff options
author | Brian Ashworth <bosrsf04@gmail.com> | 2019-07-26 12:02:18 -0400 |
---|---|---|
committer | Simon Ser <contact@emersion.fr> | 2019-08-01 18:54:58 +0300 |
commit | 8ee054b1b95b7466c0dd89bfc9026f4083fb0016 (patch) | |
tree | bce2d4bfba93e6889c1aa57085297e4a997af2d8 | |
parent | input/keyboard: don't reset layout for same keymap (diff) | |
download | sway-8ee054b1b95b7466c0dd89bfc9026f4083fb0016.tar.gz sway-8ee054b1b95b7466c0dd89bfc9026f4083fb0016.tar.zst sway-8ee054b1b95b7466c0dd89bfc9026f4083fb0016.zip |
bindsym/code: add group support
This adds support for specifying a binding for a specific group. Any
binding without a group listed will be available in all groups. The
priority for matching bindings is as follows: input device, group, and
locked state.
For full compatibility with i3, this also adds Mode_switch as an alias
for Group2. Since i3 only supports this for backwards compatibility
with older versions of i3, it is implemented here, but not documented.
-rw-r--r-- | include/sway/config.h | 1 | ||||
-rw-r--r-- | sway/commands/bind.c | 33 | ||||
-rw-r--r-- | sway/input/keyboard.c | 42 | ||||
-rw-r--r-- | sway/sway.5.scd | 15 |
4 files changed, 76 insertions, 15 deletions
diff --git a/include/sway/config.h b/include/sway/config.h index f1426453..c65d9353 100644 --- a/include/sway/config.h +++ b/include/sway/config.h | |||
@@ -53,6 +53,7 @@ struct sway_binding { | |||
53 | list_t *keys; // sorted in ascending order | 53 | list_t *keys; // sorted in ascending order |
54 | list_t *syms; // sorted in ascending order; NULL if BINDING_CODE is not set | 54 | list_t *syms; // sorted in ascending order; NULL if BINDING_CODE is not set |
55 | uint32_t modifiers; | 55 | uint32_t modifiers; |
56 | xkb_layout_index_t group; | ||
56 | char *command; | 57 | char *command; |
57 | }; | 58 | }; |
58 | 59 | ||
diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 767c2fee..5ec89982 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c | |||
@@ -78,6 +78,10 @@ static bool binding_key_compare(struct sway_binding *binding_a, | |||
78 | return false; | 78 | return false; |
79 | } | 79 | } |
80 | 80 | ||
81 | if (binding_a->group != binding_b->group) { | ||
82 | return false; | ||
83 | } | ||
84 | |||
81 | if (binding_a->modifiers ^ binding_b->modifiers) { | 85 | if (binding_a->modifiers ^ binding_b->modifiers) { |
82 | return false; | 86 | return false; |
83 | } | 87 | } |
@@ -337,6 +341,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, | |||
337 | } | 341 | } |
338 | binding->input = strdup("*"); | 342 | binding->input = strdup("*"); |
339 | binding->keys = create_list(); | 343 | binding->keys = create_list(); |
344 | binding->group = XKB_LAYOUT_INVALID; | ||
340 | binding->modifiers = 0; | 345 | binding->modifiers = 0; |
341 | binding->flags = 0; | 346 | binding->flags = 0; |
342 | binding->type = bindcode ? BINDING_KEYCODE : BINDING_KEYSYM; | 347 | binding->type = bindcode ? BINDING_KEYCODE : BINDING_KEYSYM; |
@@ -387,6 +392,34 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, | |||
387 | 392 | ||
388 | list_t *split = split_string(argv[0], "+"); | 393 | list_t *split = split_string(argv[0], "+"); |
389 | for (int i = 0; i < split->length; ++i) { | 394 | for (int i = 0; i < split->length; ++i) { |
395 | // Check for group | ||
396 | if (strncmp(split->items[i], "Group", strlen("Group")) == 0) { | ||
397 | if (binding->group != XKB_LAYOUT_INVALID) { | ||
398 | free_sway_binding(binding); | ||
399 | list_free_items_and_destroy(split); | ||
400 | return cmd_results_new(CMD_FAILURE, | ||
401 | "Only one group can be specified"); | ||
402 | } | ||
403 | char *end; | ||
404 | int group = strtol(split->items[i] + strlen("Group"), &end, 10); | ||
405 | if (group < 1 || group > 4 || end[0] != '\0') { | ||
406 | free_sway_binding(binding); | ||
407 | list_free_items_and_destroy(split); | ||
408 | return cmd_results_new(CMD_FAILURE, "Invalid group"); | ||
409 | } | ||
410 | binding->group = group - 1; | ||
411 | continue; | ||
412 | } else if (strcmp(split->items[i], "Mode_switch") == 0) { | ||
413 | // For full i3 compatibility, Mode_switch is an alias for Group2 | ||
414 | if (binding->group != XKB_LAYOUT_INVALID) { | ||
415 | free_sway_binding(binding); | ||
416 | list_free_items_and_destroy(split); | ||
417 | return cmd_results_new(CMD_FAILURE, | ||
418 | "Only one group can be specified"); | ||
419 | } | ||
420 | binding->group = 1; | ||
421 | } | ||
422 | |||
390 | // Check for a modifier key | 423 | // Check for a modifier key |
391 | uint32_t mod; | 424 | uint32_t mod; |
392 | if ((mod = get_modifier_mask_by_name(split->items[i])) > 0) { | 425 | if ((mod = get_modifier_mask_by_name(split->items[i])) > 0) { |
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index aecabbf4..680d1f69 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c | |||
@@ -143,7 +143,8 @@ static void update_shortcut_state(struct sway_shortcut_state *state, | |||
143 | */ | 143 | */ |
144 | static void get_active_binding(const struct sway_shortcut_state *state, | 144 | static void get_active_binding(const struct sway_shortcut_state *state, |
145 | list_t *bindings, struct sway_binding **current_binding, | 145 | list_t *bindings, struct sway_binding **current_binding, |
146 | uint32_t modifiers, bool release, bool locked, const char *input) { | 146 | uint32_t modifiers, bool release, bool locked, const char *input, |
147 | xkb_layout_index_t group) { | ||
147 | for (int i = 0; i < bindings->length; ++i) { | 148 | for (int i = 0; i < bindings->length; ++i) { |
148 | struct sway_binding *binding = bindings->items[i]; | 149 | struct sway_binding *binding = bindings->items[i]; |
149 | bool binding_locked = (binding->flags & BINDING_LOCKED) != 0; | 150 | bool binding_locked = (binding->flags & BINDING_LOCKED) != 0; |
@@ -152,6 +153,8 @@ static void get_active_binding(const struct sway_shortcut_state *state, | |||
152 | if (modifiers ^ binding->modifiers || | 153 | if (modifiers ^ binding->modifiers || |
153 | release != binding_release || | 154 | release != binding_release || |
154 | locked > binding_locked || | 155 | locked > binding_locked || |
156 | (binding->group != XKB_LAYOUT_INVALID && | ||
157 | binding->group != group) || | ||
155 | (strcmp(binding->input, input) != 0 && | 158 | (strcmp(binding->input, input) != 0 && |
156 | strcmp(binding->input, "*") != 0)) { | 159 | strcmp(binding->input, "*") != 0)) { |
157 | continue; | 160 | continue; |
@@ -186,10 +189,14 @@ static void get_active_binding(const struct sway_shortcut_state *state, | |||
186 | bool current_locked = | 189 | bool current_locked = |
187 | ((*current_binding)->flags & BINDING_LOCKED) != 0; | 190 | ((*current_binding)->flags & BINDING_LOCKED) != 0; |
188 | bool current_input = strcmp((*current_binding)->input, input) == 0; | 191 | bool current_input = strcmp((*current_binding)->input, input) == 0; |
192 | bool current_group_set = | ||
193 | (*current_binding)->group != XKB_LAYOUT_INVALID; | ||
189 | bool binding_input = strcmp(binding->input, input) == 0; | 194 | bool binding_input = strcmp(binding->input, input) == 0; |
195 | bool binding_group_set = binding->group != XKB_LAYOUT_INVALID; | ||
190 | 196 | ||
191 | if (current_input == binding_input | 197 | if (current_input == binding_input |
192 | && current_locked == binding_locked) { | 198 | && current_locked == binding_locked |
199 | && current_group_set == binding_group_set) { | ||
193 | sway_log(SWAY_DEBUG, | 200 | sway_log(SWAY_DEBUG, |
194 | "Encountered conflicting bindings %d and %d", | 201 | "Encountered conflicting bindings %d and %d", |
195 | (*current_binding)->order, binding->order); | 202 | (*current_binding)->order, binding->order); |
@@ -200,14 +207,22 @@ static void get_active_binding(const struct sway_shortcut_state *state, | |||
200 | continue; // Prefer the correct input | 207 | continue; // Prefer the correct input |
201 | } | 208 | } |
202 | 209 | ||
203 | if (current_input == binding_input && current_locked == locked) { | 210 | if (current_input == binding_input && |
204 | continue; // Prefer correct lock state for matching inputs | 211 | (*current_binding)->group == group) { |
212 | continue; // Prefer correct group for matching inputs | ||
213 | } | ||
214 | |||
215 | if (current_input == binding_input && | ||
216 | current_group_set == binding_group_set && | ||
217 | current_locked == locked) { | ||
218 | continue; // Prefer correct lock state for matching input+group | ||
205 | } | 219 | } |
206 | } | 220 | } |
207 | 221 | ||
208 | *current_binding = binding; | 222 | *current_binding = binding; |
209 | if (strcmp((*current_binding)->input, input) == 0 && | 223 | if (strcmp((*current_binding)->input, input) == 0 && |
210 | (((*current_binding)->flags & BINDING_LOCKED) == locked)) { | 224 | (((*current_binding)->flags & BINDING_LOCKED) == locked) && |
225 | (*current_binding)->group == group) { | ||
211 | return; // If a perfect match is found, quit searching | 226 | return; // If a perfect match is found, quit searching |
212 | } | 227 | } |
213 | } | 228 | } |
@@ -344,13 +359,16 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { | |||
344 | struct sway_binding *binding_released = NULL; | 359 | struct sway_binding *binding_released = NULL; |
345 | get_active_binding(&keyboard->state_keycodes, | 360 | get_active_binding(&keyboard->state_keycodes, |
346 | config->current_mode->keycode_bindings, &binding_released, | 361 | config->current_mode->keycode_bindings, &binding_released, |
347 | code_modifiers, true, input_inhibited, device_identifier); | 362 | code_modifiers, true, input_inhibited, device_identifier, |
363 | keyboard->effective_layout); | ||
348 | get_active_binding(&keyboard->state_keysyms_raw, | 364 | get_active_binding(&keyboard->state_keysyms_raw, |
349 | config->current_mode->keysym_bindings, &binding_released, | 365 | config->current_mode->keysym_bindings, &binding_released, |
350 | raw_modifiers, true, input_inhibited, device_identifier); | 366 | raw_modifiers, true, input_inhibited, device_identifier, |
367 | keyboard->effective_layout); | ||
351 | get_active_binding(&keyboard->state_keysyms_translated, | 368 | get_active_binding(&keyboard->state_keysyms_translated, |
352 | config->current_mode->keysym_bindings, &binding_released, | 369 | config->current_mode->keysym_bindings, &binding_released, |
353 | translated_modifiers, true, input_inhibited, device_identifier); | 370 | translated_modifiers, true, input_inhibited, device_identifier, |
371 | keyboard->effective_layout); | ||
354 | 372 | ||
355 | // Execute stored release binding once no longer active | 373 | // Execute stored release binding once no longer active |
356 | if (keyboard->held_binding && binding_released != keyboard->held_binding && | 374 | if (keyboard->held_binding && binding_released != keyboard->held_binding && |
@@ -370,14 +388,16 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { | |||
370 | if (event->state == WLR_KEY_PRESSED) { | 388 | if (event->state == WLR_KEY_PRESSED) { |
371 | get_active_binding(&keyboard->state_keycodes, | 389 | get_active_binding(&keyboard->state_keycodes, |
372 | config->current_mode->keycode_bindings, &binding, | 390 | config->current_mode->keycode_bindings, &binding, |
373 | code_modifiers, false, input_inhibited, device_identifier); | 391 | code_modifiers, false, input_inhibited, device_identifier, |
392 | keyboard->effective_layout); | ||
374 | get_active_binding(&keyboard->state_keysyms_raw, | 393 | get_active_binding(&keyboard->state_keysyms_raw, |
375 | config->current_mode->keysym_bindings, &binding, | 394 | config->current_mode->keysym_bindings, &binding, |
376 | raw_modifiers, false, input_inhibited, device_identifier); | 395 | raw_modifiers, false, input_inhibited, device_identifier, |
396 | keyboard->effective_layout); | ||
377 | get_active_binding(&keyboard->state_keysyms_translated, | 397 | get_active_binding(&keyboard->state_keysyms_translated, |
378 | config->current_mode->keysym_bindings, &binding, | 398 | config->current_mode->keysym_bindings, &binding, |
379 | translated_modifiers, false, input_inhibited, | 399 | translated_modifiers, false, input_inhibited, |
380 | device_identifier); | 400 | device_identifier, keyboard->effective_layout); |
381 | } | 401 | } |
382 | 402 | ||
383 | // Set up (or clear) keyboard repeat for a pressed binding. Since the | 403 | // Set up (or clear) keyboard repeat for a pressed binding. Since the |
diff --git a/sway/sway.5.scd b/sway/sway.5.scd index ac80de3c..3e445e0e 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd | |||
@@ -328,14 +328,17 @@ runtime. | |||
328 | 328 | ||
329 | for_window <criteria> move container to output <output> | 329 | for_window <criteria> move container to output <output> |
330 | 330 | ||
331 | *bindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] [--to-code] [--input-device=<device>] [--no-warn] <key combo> <command> | 331 | *bindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \ |
332 | [--to-code] [--input-device=<device>] [--no-warn] [Group<1-4>+]<key combo> \ | ||
333 | <command> | ||
332 | Binds _key combo_ to execute the sway command _command_ when pressed. You | 334 | Binds _key combo_ to execute the sway command _command_ when pressed. You |
333 | may use XKB key names here (*xev*(1) is a good tool for discovering these). | 335 | may use XKB key names here (*xev*(1) is a good tool for discovering these). |
334 | With the flag _--release_, the command is executed when the key combo is | 336 | With the flag _--release_, the command is executed when the key combo is |
335 | released. If _input-device_ is given, the binding will only be executed for | 337 | released. If _input-device_ is given, the binding will only be executed for |
336 | that input device and will be executed instead of any binding that is | 338 | that input device and will be executed instead of any binding that is |
337 | generic to all devices. By default, if you overwrite a binding, swaynag | 339 | generic to all devices. If a group number is given, then the binding will |
338 | will give you a warning. To silence this, use the _--no-warn_ flag. | 340 | only be available for that group. By default, if you overwrite a binding, |
341 | swaynag will give you a warning. To silence this, use the _--no-warn_ flag. | ||
339 | 342 | ||
340 | Unless the flag _--locked_ is set, the command will not be run when a | 343 | Unless the flag _--locked_ is set, the command will not be run when a |
341 | screen locking program is active. If there is a matching binding with | 344 | screen locking program is active. If there is a matching binding with |
@@ -356,6 +359,9 @@ runtime. | |||
356 | 6=scroll left, 7=scroll right, 8=back, 9=forward). For the latter option, | 359 | 6=scroll left, 7=scroll right, 8=back, 9=forward). For the latter option, |
357 | you can find the event names using _libinput debug-events_. | 360 | you can find the event names using _libinput debug-events_. |
358 | 361 | ||
362 | The priority for matching bindings is as follows: input device, group, | ||
363 | and locked state. | ||
364 | |||
359 | _--whole-window_, _--border_, and _--exclude-titlebar_ are mouse-only options | 365 | _--whole-window_, _--border_, and _--exclude-titlebar_ are mouse-only options |
360 | which affect the region in which the mouse bindings can be triggered. By | 366 | which affect the region in which the mouse bindings can be triggered. By |
361 | default, mouse bindings are only triggered when over the title bar. With the | 367 | default, mouse bindings are only triggered when over the title bar. With the |
@@ -375,7 +381,8 @@ runtime. | |||
375 | bindsym Mod1+Shift+f exec firefox | 381 | bindsym Mod1+Shift+f exec firefox |
376 | ``` | 382 | ``` |
377 | 383 | ||
378 | *bindcode* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] [--input-device=<device>] [--no-warn] <code> <command> | 384 | *bindcode* [--whole-window] [--border] [--exclude-titlebar] [--release] \ |
385 | [--locked] [--input-device=<device>] [--no-warn] [Group<1-4>+]<code> <command> | ||
379 | is also available for binding with key/button codes instead of key/button names. | 386 | is also available for binding with key/button codes instead of key/button names. |
380 | 387 | ||
381 | *bindswitch* [--locked] [--no-warn] [--reload] <switch>:<state> <command> | 388 | *bindswitch* [--locked] [--no-warn] [--reload] <switch>:<state> <command> |