diff options
-rw-r--r-- | include/sway/commands.h | 1 | ||||
-rw-r--r-- | include/sway/config.h | 7 | ||||
-rw-r--r-- | include/sway/input/keyboard.h | 8 | ||||
-rw-r--r-- | include/sway/input/seat.h | 1 | ||||
-rw-r--r-- | sway/commands/seat.c | 1 | ||||
-rw-r--r-- | sway/commands/seat/keyboard_grouping.c | 26 | ||||
-rw-r--r-- | sway/config/seat.c | 5 | ||||
-rw-r--r-- | sway/input/input-manager.c | 2 | ||||
-rw-r--r-- | sway/input/keyboard.c | 272 | ||||
-rw-r--r-- | sway/input/seat.c | 9 | ||||
-rw-r--r-- | sway/meson.build | 1 | ||||
-rw-r--r-- | sway/sway-input.5.scd | 10 |
12 files changed, 305 insertions, 38 deletions
diff --git a/include/sway/commands.h b/include/sway/commands.h index 45b5b0f4..67665d87 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h | |||
@@ -284,6 +284,7 @@ sway_cmd seat_cmd_attach; | |||
284 | sway_cmd seat_cmd_cursor; | 284 | sway_cmd seat_cmd_cursor; |
285 | sway_cmd seat_cmd_fallback; | 285 | sway_cmd seat_cmd_fallback; |
286 | sway_cmd seat_cmd_hide_cursor; | 286 | sway_cmd seat_cmd_hide_cursor; |
287 | sway_cmd seat_cmd_keyboard_grouping; | ||
287 | sway_cmd seat_cmd_pointer_constraint; | 288 | sway_cmd seat_cmd_pointer_constraint; |
288 | sway_cmd seat_cmd_xcursor_theme; | 289 | sway_cmd seat_cmd_xcursor_theme; |
289 | 290 | ||
diff --git a/include/sway/config.h b/include/sway/config.h index 457e0a98..ed542790 100644 --- a/include/sway/config.h +++ b/include/sway/config.h | |||
@@ -176,6 +176,12 @@ enum seat_config_allow_constrain { | |||
176 | CONSTRAIN_DISABLE | 176 | CONSTRAIN_DISABLE |
177 | }; | 177 | }; |
178 | 178 | ||
179 | enum seat_keyboard_grouping { | ||
180 | KEYBOARD_GROUP_DEFAULT, // the default is currently keymap | ||
181 | KEYBOARD_GROUP_NONE, | ||
182 | KEYBOARD_GROUP_KEYMAP | ||
183 | }; | ||
184 | |||
179 | /** | 185 | /** |
180 | * Options for multiseat and other misc device configurations | 186 | * Options for multiseat and other misc device configurations |
181 | */ | 187 | */ |
@@ -185,6 +191,7 @@ struct seat_config { | |||
185 | list_t *attachments; // list of seat_attachment configs | 191 | list_t *attachments; // list of seat_attachment configs |
186 | int hide_cursor_timeout; | 192 | int hide_cursor_timeout; |
187 | enum seat_config_allow_constrain allow_constrain; | 193 | enum seat_config_allow_constrain allow_constrain; |
194 | enum seat_keyboard_grouping keyboard_grouping; | ||
188 | struct { | 195 | struct { |
189 | char *name; | 196 | char *name; |
190 | int size; | 197 | int size; |
diff --git a/include/sway/input/keyboard.h b/include/sway/input/keyboard.h index 4aa0d8f4..72a29ba6 100644 --- a/include/sway/input/keyboard.h +++ b/include/sway/input/keyboard.h | |||
@@ -67,6 +67,14 @@ struct sway_keyboard { | |||
67 | struct sway_binding *repeat_binding; | 67 | struct sway_binding *repeat_binding; |
68 | }; | 68 | }; |
69 | 69 | ||
70 | struct sway_keyboard_group { | ||
71 | struct wlr_keyboard_group *wlr_group; | ||
72 | struct sway_seat_device *seat_device; | ||
73 | struct wl_listener keyboard_key; | ||
74 | struct wl_listener keyboard_modifiers; | ||
75 | struct wl_list link; // sway_seat::keyboard_groups | ||
76 | }; | ||
77 | |||
70 | struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic, | 78 | struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic, |
71 | char **error); | 79 | char **error); |
72 | 80 | ||
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 24a6fed4..32795b03 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h | |||
@@ -90,6 +90,7 @@ struct sway_seat { | |||
90 | struct wl_listener request_set_primary_selection; | 90 | struct wl_listener request_set_primary_selection; |
91 | 91 | ||
92 | struct wl_list devices; // sway_seat_device::link | 92 | struct wl_list devices; // sway_seat_device::link |
93 | struct wl_list keyboard_groups; // sway_keyboard_group::link | ||
93 | 94 | ||
94 | struct wl_list link; // input_manager::seats | 95 | struct wl_list link; // input_manager::seats |
95 | }; | 96 | }; |
diff --git a/sway/commands/seat.c b/sway/commands/seat.c index 197a405e..a2a3fbc4 100644 --- a/sway/commands/seat.c +++ b/sway/commands/seat.c | |||
@@ -18,6 +18,7 @@ static struct cmd_handler seat_handlers[] = { | |||
18 | { "attach", seat_cmd_attach }, | 18 | { "attach", seat_cmd_attach }, |
19 | { "fallback", seat_cmd_fallback }, | 19 | { "fallback", seat_cmd_fallback }, |
20 | { "hide_cursor", seat_cmd_hide_cursor }, | 20 | { "hide_cursor", seat_cmd_hide_cursor }, |
21 | { "keyboard_grouping", seat_cmd_keyboard_grouping }, | ||
21 | { "pointer_constraint", seat_cmd_pointer_constraint }, | 22 | { "pointer_constraint", seat_cmd_pointer_constraint }, |
22 | { "xcursor_theme", seat_cmd_xcursor_theme }, | 23 | { "xcursor_theme", seat_cmd_xcursor_theme }, |
23 | }; | 24 | }; |
diff --git a/sway/commands/seat/keyboard_grouping.c b/sway/commands/seat/keyboard_grouping.c new file mode 100644 index 00000000..959c6f94 --- /dev/null +++ b/sway/commands/seat/keyboard_grouping.c | |||
@@ -0,0 +1,26 @@ | |||
1 | #include <string.h> | ||
2 | #include "sway/commands.h" | ||
3 | #include "sway/config.h" | ||
4 | #include "stringop.h" | ||
5 | |||
6 | struct cmd_results *seat_cmd_keyboard_grouping(int argc, char **argv) { | ||
7 | struct cmd_results *error = NULL; | ||
8 | if ((error = checkarg(argc, "keyboard_grouping", EXPECTED_EQUAL_TO, 1))) { | ||
9 | return error; | ||
10 | } | ||
11 | if (!config->handler_context.seat_config) { | ||
12 | return cmd_results_new(CMD_INVALID, "No seat defined"); | ||
13 | } | ||
14 | |||
15 | struct seat_config *seat_config = config->handler_context.seat_config; | ||
16 | if (strcmp(argv[0], "none") == 0) { | ||
17 | seat_config->keyboard_grouping = KEYBOARD_GROUP_NONE; | ||
18 | } else if (strcmp(argv[0], "keymap") == 0) { | ||
19 | seat_config->keyboard_grouping = KEYBOARD_GROUP_KEYMAP; | ||
20 | } else { | ||
21 | return cmd_results_new(CMD_INVALID, | ||
22 | "Expected syntax `keyboard_grouping none|keymap`"); | ||
23 | } | ||
24 | |||
25 | return cmd_results_new(CMD_SUCCESS, NULL); | ||
26 | } | ||
diff --git a/sway/config/seat.c b/sway/config/seat.c index d4190cec..d2401162 100644 --- a/sway/config/seat.c +++ b/sway/config/seat.c | |||
@@ -27,6 +27,7 @@ struct seat_config *new_seat_config(const char* name) { | |||
27 | } | 27 | } |
28 | seat->hide_cursor_timeout = -1; | 28 | seat->hide_cursor_timeout = -1; |
29 | seat->allow_constrain = CONSTRAIN_DEFAULT; | 29 | seat->allow_constrain = CONSTRAIN_DEFAULT; |
30 | seat->keyboard_grouping = KEYBOARD_GROUP_DEFAULT; | ||
30 | seat->xcursor_theme.name = NULL; | 31 | seat->xcursor_theme.name = NULL; |
31 | seat->xcursor_theme.size = 24; | 32 | seat->xcursor_theme.size = 24; |
32 | 33 | ||
@@ -150,6 +151,10 @@ void merge_seat_config(struct seat_config *dest, struct seat_config *source) { | |||
150 | dest->allow_constrain = source->allow_constrain; | 151 | dest->allow_constrain = source->allow_constrain; |
151 | } | 152 | } |
152 | 153 | ||
154 | if (source->keyboard_grouping != KEYBOARD_GROUP_DEFAULT) { | ||
155 | dest->keyboard_grouping = source->keyboard_grouping; | ||
156 | } | ||
157 | |||
153 | if (source->xcursor_theme.name != NULL) { | 158 | if (source->xcursor_theme.name != NULL) { |
154 | free(dest->xcursor_theme.name); | 159 | free(dest->xcursor_theme.name); |
155 | dest->xcursor_theme.name = strdup(source->xcursor_theme.name); | 160 | dest->xcursor_theme.name = strdup(source->xcursor_theme.name); |
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index 4f9ed891..cfd39bab 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c | |||
@@ -47,7 +47,7 @@ struct sway_seat *input_manager_get_seat(const char *seat_name, bool create) { | |||
47 | char *input_device_get_identifier(struct wlr_input_device *device) { | 47 | char *input_device_get_identifier(struct wlr_input_device *device) { |
48 | int vendor = device->vendor; | 48 | int vendor = device->vendor; |
49 | int product = device->product; | 49 | int product = device->product; |
50 | char *name = strdup(device->name); | 50 | char *name = strdup(device->name ? device->name : ""); |
51 | strip_whitespace(name); | 51 | strip_whitespace(name); |
52 | 52 | ||
53 | char *p = name; | 53 | char *p = name; |
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index cdc4258d..e925c00d 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c | |||
@@ -3,8 +3,9 @@ | |||
3 | #include <strings.h> | 3 | #include <strings.h> |
4 | #include <wlr/backend/multi.h> | 4 | #include <wlr/backend/multi.h> |
5 | #include <wlr/backend/session.h> | 5 | #include <wlr/backend/session.h> |
6 | #include <wlr/types/wlr_idle.h> | ||
7 | #include <wlr/interfaces/wlr_keyboard.h> | 6 | #include <wlr/interfaces/wlr_keyboard.h> |
7 | #include <wlr/types/wlr_idle.h> | ||
8 | #include <wlr/types/wlr_keyboard_group.h> | ||
8 | #include <xkbcommon/xkbcommon-names.h> | 9 | #include <xkbcommon/xkbcommon-names.h> |
9 | #include "sway/commands.h" | 10 | #include "sway/commands.h" |
10 | #include "sway/desktop/transaction.h" | 11 | #include "sway/desktop/transaction.h" |
@@ -150,7 +151,7 @@ static bool update_shortcut_state(struct sway_shortcut_state *state, | |||
150 | static void get_active_binding(const struct sway_shortcut_state *state, | 151 | static void get_active_binding(const struct sway_shortcut_state *state, |
151 | list_t *bindings, struct sway_binding **current_binding, | 152 | list_t *bindings, struct sway_binding **current_binding, |
152 | uint32_t modifiers, bool release, bool locked, const char *input, | 153 | uint32_t modifiers, bool release, bool locked, const char *input, |
153 | xkb_layout_index_t group) { | 154 | bool exact_input, xkb_layout_index_t group) { |
154 | for (int i = 0; i < bindings->length; ++i) { | 155 | for (int i = 0; i < bindings->length; ++i) { |
155 | struct sway_binding *binding = bindings->items[i]; | 156 | struct sway_binding *binding = bindings->items[i]; |
156 | bool binding_locked = (binding->flags & BINDING_LOCKED) != 0; | 157 | bool binding_locked = (binding->flags & BINDING_LOCKED) != 0; |
@@ -162,7 +163,7 @@ static void get_active_binding(const struct sway_shortcut_state *state, | |||
162 | (binding->group != XKB_LAYOUT_INVALID && | 163 | (binding->group != XKB_LAYOUT_INVALID && |
163 | binding->group != group) || | 164 | binding->group != group) || |
164 | (strcmp(binding->input, input) != 0 && | 165 | (strcmp(binding->input, input) != 0 && |
165 | strcmp(binding->input, "*") != 0)) { | 166 | (strcmp(binding->input, "*") != 0 || exact_input))) { |
166 | continue; | 167 | continue; |
167 | } | 168 | } |
168 | 169 | ||
@@ -317,16 +318,15 @@ void sway_keyboard_disarm_key_repeat(struct sway_keyboard *keyboard) { | |||
317 | } | 318 | } |
318 | } | 319 | } |
319 | 320 | ||
320 | static void handle_keyboard_key(struct wl_listener *listener, void *data) { | 321 | static void handle_key_event(struct sway_keyboard *keyboard, |
321 | struct sway_keyboard *keyboard = | 322 | struct wlr_event_keyboard_key *event) { |
322 | wl_container_of(listener, keyboard, keyboard_key); | ||
323 | struct sway_seat* seat = keyboard->seat_device->sway_seat; | 323 | struct sway_seat* seat = keyboard->seat_device->sway_seat; |
324 | struct wlr_seat *wlr_seat = seat->wlr_seat; | 324 | struct wlr_seat *wlr_seat = seat->wlr_seat; |
325 | struct wlr_input_device *wlr_device = | 325 | struct wlr_input_device *wlr_device = |
326 | keyboard->seat_device->input_device->wlr_device; | 326 | keyboard->seat_device->input_device->wlr_device; |
327 | char *device_identifier = input_device_get_identifier(wlr_device); | 327 | char *device_identifier = input_device_get_identifier(wlr_device); |
328 | bool exact_identifier = wlr_device->keyboard->group != NULL; | ||
328 | wlr_idle_notify_activity(server.idle, wlr_seat); | 329 | wlr_idle_notify_activity(server.idle, wlr_seat); |
329 | struct wlr_event_keyboard_key *event = data; | ||
330 | bool input_inhibited = seat->exclusive_client != NULL; | 330 | bool input_inhibited = seat->exclusive_client != NULL; |
331 | 331 | ||
332 | // Identify new keycode, raw keysym(s), and translated keysym(s) | 332 | // Identify new keycode, raw keysym(s), and translated keysym(s) |
@@ -360,21 +360,20 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { | |||
360 | } | 360 | } |
361 | 361 | ||
362 | bool handled = false; | 362 | bool handled = false; |
363 | |||
364 | // Identify active release binding | 363 | // Identify active release binding |
365 | struct sway_binding *binding_released = NULL; | 364 | struct sway_binding *binding_released = NULL; |
366 | get_active_binding(&keyboard->state_keycodes, | 365 | get_active_binding(&keyboard->state_keycodes, |
367 | config->current_mode->keycode_bindings, &binding_released, | 366 | config->current_mode->keycode_bindings, &binding_released, |
368 | code_modifiers, true, input_inhibited, device_identifier, | 367 | code_modifiers, true, input_inhibited, device_identifier, |
369 | keyboard->effective_layout); | 368 | exact_identifier, keyboard->effective_layout); |
370 | get_active_binding(&keyboard->state_keysyms_raw, | 369 | get_active_binding(&keyboard->state_keysyms_raw, |
371 | config->current_mode->keysym_bindings, &binding_released, | 370 | config->current_mode->keysym_bindings, &binding_released, |
372 | raw_modifiers, true, input_inhibited, device_identifier, | 371 | raw_modifiers, true, input_inhibited, device_identifier, |
373 | keyboard->effective_layout); | 372 | exact_identifier, keyboard->effective_layout); |
374 | get_active_binding(&keyboard->state_keysyms_translated, | 373 | get_active_binding(&keyboard->state_keysyms_translated, |
375 | config->current_mode->keysym_bindings, &binding_released, | 374 | config->current_mode->keysym_bindings, &binding_released, |
376 | translated_modifiers, true, input_inhibited, device_identifier, | 375 | translated_modifiers, true, input_inhibited, device_identifier, |
377 | keyboard->effective_layout); | 376 | exact_identifier, keyboard->effective_layout); |
378 | 377 | ||
379 | // Execute stored release binding once no longer active | 378 | // Execute stored release binding once no longer active |
380 | if (keyboard->held_binding && binding_released != keyboard->held_binding && | 379 | if (keyboard->held_binding && binding_released != keyboard->held_binding && |
@@ -395,15 +394,16 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { | |||
395 | get_active_binding(&keyboard->state_keycodes, | 394 | get_active_binding(&keyboard->state_keycodes, |
396 | config->current_mode->keycode_bindings, &binding, | 395 | config->current_mode->keycode_bindings, &binding, |
397 | code_modifiers, false, input_inhibited, device_identifier, | 396 | code_modifiers, false, input_inhibited, device_identifier, |
398 | keyboard->effective_layout); | 397 | exact_identifier, keyboard->effective_layout); |
399 | get_active_binding(&keyboard->state_keysyms_raw, | 398 | get_active_binding(&keyboard->state_keysyms_raw, |
400 | config->current_mode->keysym_bindings, &binding, | 399 | config->current_mode->keysym_bindings, &binding, |
401 | raw_modifiers, false, input_inhibited, device_identifier, | 400 | raw_modifiers, false, input_inhibited, device_identifier, |
402 | keyboard->effective_layout); | 401 | exact_identifier, keyboard->effective_layout); |
403 | get_active_binding(&keyboard->state_keysyms_translated, | 402 | get_active_binding(&keyboard->state_keysyms_translated, |
404 | config->current_mode->keysym_bindings, &binding, | 403 | config->current_mode->keysym_bindings, &binding, |
405 | translated_modifiers, false, input_inhibited, | 404 | translated_modifiers, false, input_inhibited, |
406 | device_identifier, keyboard->effective_layout); | 405 | device_identifier, exact_identifier, |
406 | keyboard->effective_layout); | ||
407 | } | 407 | } |
408 | 408 | ||
409 | // Set up (or clear) keyboard repeat for a pressed binding. Since the | 409 | // Set up (or clear) keyboard repeat for a pressed binding. Since the |
@@ -423,6 +423,12 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { | |||
423 | handled = true; | 423 | handled = true; |
424 | } | 424 | } |
425 | 425 | ||
426 | if (!handled && wlr_device->keyboard->group) { | ||
427 | // Only handle device specific bindings for keyboards in a group | ||
428 | free(device_identifier); | ||
429 | return; | ||
430 | } | ||
431 | |||
426 | // Compositor bindings | 432 | // Compositor bindings |
427 | if (!handled && event->state == WLR_KEY_PRESSED) { | 433 | if (!handled && event->state == WLR_KEY_PRESSED) { |
428 | handled = keyboard_execute_compositor_binding( | 434 | handled = keyboard_execute_compositor_binding( |
@@ -450,6 +456,19 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { | |||
450 | free(device_identifier); | 456 | free(device_identifier); |
451 | } | 457 | } |
452 | 458 | ||
459 | static void handle_keyboard_key(struct wl_listener *listener, void *data) { | ||
460 | struct sway_keyboard *keyboard = | ||
461 | wl_container_of(listener, keyboard, keyboard_key); | ||
462 | handle_key_event(keyboard, data); | ||
463 | } | ||
464 | |||
465 | static void handle_keyboard_group_key(struct wl_listener *listener, | ||
466 | void *data) { | ||
467 | struct sway_keyboard_group *sway_group = | ||
468 | wl_container_of(listener, sway_group, keyboard_key); | ||
469 | handle_key_event(sway_group->seat_device->keyboard, data); | ||
470 | } | ||
471 | |||
453 | static int handle_keyboard_repeat(void *data) { | 472 | static int handle_keyboard_repeat(void *data) { |
454 | struct sway_keyboard *keyboard = (struct sway_keyboard *)data; | 473 | struct sway_keyboard *keyboard = (struct sway_keyboard *)data; |
455 | struct wlr_keyboard *wlr_device = | 474 | struct wlr_keyboard *wlr_device = |
@@ -491,25 +510,40 @@ static void determine_bar_visibility(uint32_t modifiers) { | |||
491 | } | 510 | } |
492 | } | 511 | } |
493 | 512 | ||
494 | static void handle_keyboard_modifiers(struct wl_listener *listener, | 513 | static void handle_modifier_event(struct sway_keyboard *keyboard) { |
495 | void *data) { | ||
496 | struct sway_keyboard *keyboard = | ||
497 | wl_container_of(listener, keyboard, keyboard_modifiers); | ||
498 | struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; | ||
499 | struct wlr_input_device *wlr_device = | 514 | struct wlr_input_device *wlr_device = |
500 | keyboard->seat_device->input_device->wlr_device; | 515 | keyboard->seat_device->input_device->wlr_device; |
501 | wlr_seat_set_keyboard(wlr_seat, wlr_device); | 516 | if (!wlr_device->keyboard->group) { |
502 | wlr_seat_keyboard_notify_modifiers(wlr_seat, &wlr_device->keyboard->modifiers); | 517 | struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; |
518 | wlr_seat_set_keyboard(wlr_seat, wlr_device); | ||
519 | wlr_seat_keyboard_notify_modifiers(wlr_seat, | ||
520 | &wlr_device->keyboard->modifiers); | ||
503 | 521 | ||
504 | uint32_t modifiers = wlr_keyboard_get_modifiers(wlr_device->keyboard); | 522 | uint32_t modifiers = wlr_keyboard_get_modifiers(wlr_device->keyboard); |
505 | determine_bar_visibility(modifiers); | 523 | determine_bar_visibility(modifiers); |
524 | } | ||
506 | 525 | ||
507 | if (wlr_device->keyboard->modifiers.group != keyboard->effective_layout) { | 526 | if (wlr_device->keyboard->modifiers.group != keyboard->effective_layout && |
527 | !wlr_keyboard_group_from_wlr_keyboard(wlr_device->keyboard)) { | ||
508 | keyboard->effective_layout = wlr_device->keyboard->modifiers.group; | 528 | keyboard->effective_layout = wlr_device->keyboard->modifiers.group; |
509 | ipc_event_input("xkb_layout", keyboard->seat_device->input_device); | 529 | ipc_event_input("xkb_layout", keyboard->seat_device->input_device); |
510 | } | 530 | } |
511 | } | 531 | } |
512 | 532 | ||
533 | static void handle_keyboard_modifiers(struct wl_listener *listener, | ||
534 | void *data) { | ||
535 | struct sway_keyboard *keyboard = | ||
536 | wl_container_of(listener, keyboard, keyboard_modifiers); | ||
537 | handle_modifier_event(keyboard); | ||
538 | } | ||
539 | |||
540 | static void handle_keyboard_group_modifiers(struct wl_listener *listener, | ||
541 | void *data) { | ||
542 | struct sway_keyboard_group *group = | ||
543 | wl_container_of(listener, group, keyboard_modifiers); | ||
544 | handle_modifier_event(group->seat_device->keyboard); | ||
545 | } | ||
546 | |||
513 | struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, | 547 | struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, |
514 | struct sway_seat_device *device) { | 548 | struct sway_seat_device *device) { |
515 | struct sway_keyboard *keyboard = | 549 | struct sway_keyboard *keyboard = |
@@ -616,6 +650,163 @@ cleanup: | |||
616 | return keymap; | 650 | return keymap; |
617 | } | 651 | } |
618 | 652 | ||
653 | static bool keymaps_match(struct xkb_keymap *km1, struct xkb_keymap *km2) { | ||
654 | char *km1_str = xkb_keymap_get_as_string(km1, XKB_KEYMAP_FORMAT_TEXT_V1); | ||
655 | char *km2_str = xkb_keymap_get_as_string(km2, XKB_KEYMAP_FORMAT_TEXT_V1); | ||
656 | bool result = strcmp(km1_str, km2_str) == 0; | ||
657 | free(km1_str); | ||
658 | free(km2_str); | ||
659 | return result; | ||
660 | } | ||
661 | |||
662 | static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) { | ||
663 | struct sway_input_device *device = keyboard->seat_device->input_device; | ||
664 | struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard; | ||
665 | struct wlr_keyboard_group *wlr_group = wlr_keyboard->group; | ||
666 | |||
667 | sway_log(SWAY_DEBUG, "Removing keyboard %s from group %p", | ||
668 | device->identifier, wlr_group); | ||
669 | |||
670 | wlr_keyboard_group_remove_keyboard(wlr_keyboard->group, wlr_keyboard); | ||
671 | |||
672 | if (wl_list_empty(&wlr_group->devices)) { | ||
673 | sway_log(SWAY_DEBUG, "Destroying empty keyboard group %p", | ||
674 | wlr_group); | ||
675 | struct sway_keyboard_group *sway_group = wlr_group->data; | ||
676 | wlr_group->data = NULL; | ||
677 | wl_list_remove(&sway_group->link); | ||
678 | wl_list_remove(&sway_group->keyboard_key.link); | ||
679 | wl_list_remove(&sway_group->keyboard_modifiers.link); | ||
680 | free(sway_group->seat_device->keyboard); | ||
681 | free(sway_group->seat_device->input_device); | ||
682 | free(sway_group->seat_device); | ||
683 | free(sway_group); | ||
684 | wlr_keyboard_group_destroy(wlr_group); | ||
685 | } | ||
686 | } | ||
687 | |||
688 | static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) { | ||
689 | struct sway_input_device *device = keyboard->seat_device->input_device; | ||
690 | struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard; | ||
691 | if (!wlr_keyboard->group) { | ||
692 | return; | ||
693 | } | ||
694 | |||
695 | struct sway_seat *seat = keyboard->seat_device->sway_seat; | ||
696 | struct seat_config *sc = seat_get_config(seat); | ||
697 | if (!sc) { | ||
698 | sc = seat_get_config_by_name("*"); | ||
699 | } | ||
700 | |||
701 | switch (sc ? sc->keyboard_grouping : KEYBOARD_GROUP_DEFAULT) { | ||
702 | case KEYBOARD_GROUP_NONE: | ||
703 | sway_keyboard_group_remove(keyboard); | ||
704 | break; | ||
705 | case KEYBOARD_GROUP_DEFAULT: /* fallthrough */ | ||
706 | case KEYBOARD_GROUP_KEYMAP:; | ||
707 | struct wlr_keyboard_group *group = wlr_keyboard->group; | ||
708 | if (!keymaps_match(keyboard->keymap, group->keyboard.keymap)) { | ||
709 | sway_keyboard_group_remove(keyboard); | ||
710 | } | ||
711 | break; | ||
712 | } | ||
713 | } | ||
714 | |||
715 | static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { | ||
716 | struct sway_input_device *device = keyboard->seat_device->input_device; | ||
717 | struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard; | ||
718 | struct sway_seat *seat = keyboard->seat_device->sway_seat; | ||
719 | struct seat_config *sc = seat_get_config(seat); | ||
720 | if (!sc) { | ||
721 | sc = seat_get_config_by_name("*"); | ||
722 | } | ||
723 | |||
724 | if (sc && sc->keyboard_grouping == KEYBOARD_GROUP_NONE) { | ||
725 | // Keyboard grouping is disabled for the seat | ||
726 | return; | ||
727 | } | ||
728 | |||
729 | struct sway_keyboard_group *group; | ||
730 | wl_list_for_each(group, &seat->keyboard_groups, link) { | ||
731 | switch (sc ? sc->keyboard_grouping : KEYBOARD_GROUP_DEFAULT) { | ||
732 | case KEYBOARD_GROUP_NONE: | ||
733 | // Nothing to do. This shouldn't even be reached | ||
734 | return; | ||
735 | case KEYBOARD_GROUP_DEFAULT: /* fallthrough */ | ||
736 | case KEYBOARD_GROUP_KEYMAP:; | ||
737 | struct wlr_keyboard_group *wlr_group = group->wlr_group; | ||
738 | if (keymaps_match(keyboard->keymap, wlr_group->keyboard.keymap)) { | ||
739 | sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", | ||
740 | device->identifier, wlr_group); | ||
741 | wlr_keyboard_group_add_keyboard(wlr_group, wlr_keyboard); | ||
742 | return; | ||
743 | } | ||
744 | break; | ||
745 | } | ||
746 | } | ||
747 | |||
748 | struct sway_keyboard_group *sway_group = | ||
749 | calloc(1, sizeof(struct sway_keyboard_group)); | ||
750 | if (!sway_group) { | ||
751 | sway_log(SWAY_ERROR, "Failed to allocate sway_keyboard_group"); | ||
752 | return; | ||
753 | } | ||
754 | |||
755 | sway_group->wlr_group = wlr_keyboard_group_create(); | ||
756 | if (!sway_group->wlr_group) { | ||
757 | sway_log(SWAY_ERROR, "Failed to create keyboard group"); | ||
758 | goto cleanup; | ||
759 | } | ||
760 | sway_group->wlr_group->data = sway_group; | ||
761 | wlr_keyboard_set_keymap(&sway_group->wlr_group->keyboard, keyboard->keymap); | ||
762 | sway_log(SWAY_DEBUG, "Created keyboard group %p", sway_group->wlr_group); | ||
763 | |||
764 | sway_group->seat_device = calloc(1, sizeof(struct sway_seat_device)); | ||
765 | if (!sway_group->seat_device) { | ||
766 | sway_log(SWAY_ERROR, "Failed to allocate sway_seat_device for group"); | ||
767 | goto cleanup; | ||
768 | } | ||
769 | sway_group->seat_device->sway_seat = seat; | ||
770 | |||
771 | sway_group->seat_device->input_device = | ||
772 | calloc(1, sizeof(struct sway_input_device)); | ||
773 | if (!sway_group->seat_device->input_device) { | ||
774 | sway_log(SWAY_ERROR, "Failed to allocate sway_input_device for group"); | ||
775 | goto cleanup; | ||
776 | } | ||
777 | sway_group->seat_device->input_device->wlr_device = | ||
778 | sway_group->wlr_group->input_device; | ||
779 | |||
780 | if (!sway_keyboard_create(seat, sway_group->seat_device)) { | ||
781 | sway_log(SWAY_ERROR, "Failed to allocate sway_keyboard for group"); | ||
782 | goto cleanup; | ||
783 | } | ||
784 | |||
785 | sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", | ||
786 | device->identifier, sway_group->wlr_group); | ||
787 | wlr_keyboard_group_add_keyboard(sway_group->wlr_group, wlr_keyboard); | ||
788 | |||
789 | wl_list_insert(&seat->keyboard_groups, &sway_group->link); | ||
790 | |||
791 | wl_signal_add(&sway_group->wlr_group->keyboard.events.key, | ||
792 | &sway_group->keyboard_key); | ||
793 | sway_group->keyboard_key.notify = handle_keyboard_group_key; | ||
794 | |||
795 | wl_signal_add(&sway_group->wlr_group->keyboard.events.modifiers, | ||
796 | &sway_group->keyboard_modifiers); | ||
797 | sway_group->keyboard_modifiers.notify = handle_keyboard_group_modifiers; | ||
798 | return; | ||
799 | |||
800 | cleanup: | ||
801 | if (sway_group && sway_group->wlr_group) { | ||
802 | wlr_keyboard_group_destroy(sway_group->wlr_group); | ||
803 | } | ||
804 | free(sway_group->seat_device->keyboard); | ||
805 | free(sway_group->seat_device->input_device); | ||
806 | free(sway_group->seat_device); | ||
807 | free(sway_group); | ||
808 | } | ||
809 | |||
619 | void sway_keyboard_configure(struct sway_keyboard *keyboard) { | 810 | void sway_keyboard_configure(struct sway_keyboard *keyboard) { |
620 | struct input_config *input_config = | 811 | struct input_config *input_config = |
621 | input_device_get_config(keyboard->seat_device->input_device); | 812 | input_device_get_config(keyboard->seat_device->input_device); |
@@ -633,26 +824,23 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { | |||
633 | } | 824 | } |
634 | } | 825 | } |
635 | 826 | ||
636 | bool keymap_changed = false; | 827 | bool keymap_changed = |
828 | keyboard->keymap ? !keymaps_match(keyboard->keymap, keymap) : true; | ||
637 | bool effective_layout_changed = keyboard->effective_layout != 0; | 829 | bool effective_layout_changed = keyboard->effective_layout != 0; |
638 | if (keyboard->keymap) { | ||
639 | char *old_keymap_string = xkb_keymap_get_as_string(keyboard->keymap, | ||
640 | XKB_KEYMAP_FORMAT_TEXT_V1); | ||
641 | char *new_keymap_string = xkb_keymap_get_as_string(keymap, | ||
642 | XKB_KEYMAP_FORMAT_TEXT_V1); | ||
643 | keymap_changed = strcmp(old_keymap_string, new_keymap_string); | ||
644 | free(old_keymap_string); | ||
645 | free(new_keymap_string); | ||
646 | } else { | ||
647 | keymap_changed = true; | ||
648 | } | ||
649 | 830 | ||
650 | if (keymap_changed || config->reloading) { | 831 | if (keymap_changed || config->reloading) { |
651 | xkb_keymap_unref(keyboard->keymap); | 832 | xkb_keymap_unref(keyboard->keymap); |
652 | keyboard->keymap = keymap; | 833 | keyboard->keymap = keymap; |
653 | keyboard->effective_layout = 0; | 834 | keyboard->effective_layout = 0; |
835 | |||
836 | sway_keyboard_group_remove_invalid(keyboard); | ||
837 | |||
654 | wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap); | 838 | wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap); |
655 | 839 | ||
840 | if (!wlr_device->keyboard->group) { | ||
841 | sway_keyboard_group_add(keyboard); | ||
842 | } | ||
843 | |||
656 | xkb_mod_mask_t locked_mods = 0; | 844 | xkb_mod_mask_t locked_mods = 0; |
657 | if (input_config && input_config->xkb_numlock > 0) { | 845 | if (input_config && input_config->xkb_numlock > 0) { |
658 | xkb_mod_index_t mod_index = xkb_map_mod_get_index(keymap, | 846 | xkb_mod_index_t mod_index = xkb_map_mod_get_index(keymap, |
@@ -679,10 +867,19 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { | |||
679 | leds |= (1 << i); | 867 | leds |= (1 << i); |
680 | } | 868 | } |
681 | } | 869 | } |
682 | wlr_keyboard_led_update(wlr_device->keyboard, leds); | 870 | if (wlr_device->keyboard->group) { |
871 | wlr_keyboard_led_update( | ||
872 | &wlr_device->keyboard->group->keyboard, leds); | ||
873 | } else { | ||
874 | wlr_keyboard_led_update(wlr_device->keyboard, leds); | ||
875 | } | ||
683 | } | 876 | } |
684 | } else { | 877 | } else { |
685 | xkb_keymap_unref(keymap); | 878 | xkb_keymap_unref(keymap); |
879 | sway_keyboard_group_remove_invalid(keyboard); | ||
880 | if (!wlr_device->keyboard->group) { | ||
881 | sway_keyboard_group_add(keyboard); | ||
882 | } | ||
686 | } | 883 | } |
687 | 884 | ||
688 | int repeat_rate = 25; | 885 | int repeat_rate = 25; |
@@ -721,6 +918,7 @@ void sway_keyboard_destroy(struct sway_keyboard *keyboard) { | |||
721 | if (!keyboard) { | 918 | if (!keyboard) { |
722 | return; | 919 | return; |
723 | } | 920 | } |
921 | sway_keyboard_group_remove(keyboard); | ||
724 | if (keyboard->keymap) { | 922 | if (keyboard->keymap) { |
725 | xkb_keymap_unref(keyboard->keymap); | 923 | xkb_keymap_unref(keyboard->keymap); |
726 | } | 924 | } |
diff --git a/sway/input/seat.c b/sway/input/seat.c index f486d5e7..fb3e68ee 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c | |||
@@ -102,6 +102,14 @@ static struct sway_keyboard *sway_keyboard_for_wlr_keyboard( | |||
102 | return seat_device->keyboard; | 102 | return seat_device->keyboard; |
103 | } | 103 | } |
104 | } | 104 | } |
105 | struct sway_keyboard_group *group; | ||
106 | wl_list_for_each(group, &seat->keyboard_groups, link) { | ||
107 | struct sway_input_device *input_device = | ||
108 | group->seat_device->input_device; | ||
109 | if (input_device->wlr_device->keyboard == wlr_keyboard) { | ||
110 | return group->seat_device->keyboard; | ||
111 | } | ||
112 | } | ||
105 | return NULL; | 113 | return NULL; |
106 | } | 114 | } |
107 | 115 | ||
@@ -519,6 +527,7 @@ struct sway_seat *seat_create(const char *seat_name) { | |||
519 | handle_request_set_primary_selection; | 527 | handle_request_set_primary_selection; |
520 | 528 | ||
521 | wl_list_init(&seat->devices); | 529 | wl_list_init(&seat->devices); |
530 | wl_list_init(&seat->keyboard_groups); | ||
522 | 531 | ||
523 | wl_list_insert(&server.input->seats, &seat->link); | 532 | wl_list_insert(&server.input->seats, &seat->link); |
524 | 533 | ||
diff --git a/sway/meson.build b/sway/meson.build index e285c09e..5458d3dc 100644 --- a/sway/meson.build +++ b/sway/meson.build | |||
@@ -91,6 +91,7 @@ sway_sources = files( | |||
91 | 'commands/seat/cursor.c', | 91 | 'commands/seat/cursor.c', |
92 | 'commands/seat/fallback.c', | 92 | 'commands/seat/fallback.c', |
93 | 'commands/seat/hide_cursor.c', | 93 | 'commands/seat/hide_cursor.c', |
94 | 'commands/seat/keyboard_grouping.c', | ||
94 | 'commands/seat/pointer_constraint.c', | 95 | 'commands/seat/pointer_constraint.c', |
95 | 'commands/seat/xcursor_theme.c', | 96 | 'commands/seat/xcursor_theme.c', |
96 | 'commands/set.c', | 97 | 'commands/set.c', |
diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index d0bf648b..5631293c 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd | |||
@@ -218,6 +218,16 @@ correct seat. | |||
218 | disables hiding the cursor. The minimal timeout is 100 and any value less | 218 | disables hiding the cursor. The minimal timeout is 100 and any value less |
219 | than that (aside from 0), will be increased to 100. | 219 | than that (aside from 0), will be increased to 100. |
220 | 220 | ||
221 | *seat* <name> keyboard_grouping none|keymap | ||
222 | Set how the keyboards in the seat are grouped together. Currently, there | ||
223 | are two options. _none_ will disable all keyboard grouping. This will make | ||
224 | it so each keyboard device has its own isolated state. _keymap_ will | ||
225 | group the keyboards in the seat by their keymap. This is useful for when | ||
226 | the keyboard appears as multiple separate input devices. In this mode, | ||
227 | the effective layout and repeat info are also synced between the keyboards | ||
228 | in the group. The default is _keymap_. To restore the behavior of older | ||
229 | versions of sway, use _none_. | ||
230 | |||
221 | *seat* <name> pointer_constraint enable|disable|escape | 231 | *seat* <name> pointer_constraint enable|disable|escape |
222 | Enables or disables the ability for clients to capture the cursor (enabled | 232 | Enables or disables the ability for clients to capture the cursor (enabled |
223 | by default) for the seat. This is primarily useful for video games. The | 233 | by default) for the seat. This is primarily useful for video games. The |