aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Ashworth <bosrsf04@gmail.com>2019-11-03 14:20:05 -0500
committerDrew DeVault <sir@cmpwn.com>2019-11-21 10:42:10 -0500
commit5d882cb5fc2d9d9fd68439021e48a90aa2e50e79 (patch)
treec519449aebb2fa15406407eee5af3cd4164e1b62
parent2f858a1adaef17241ca6fda973f2b867b25e1971 (diff)
Add support for wlr_keyboard_group
A wlr_keyboard_group allows for multiple keyboard devices to be combined into one logical keyboard. This is useful for keyboards that are split into multiple input devices despite appearing as one physical keyboard in the user's mind. This adds support for wlr_keyboard_groups to sway. There are two keyboard groupings currently supported, which can be set on a per-seat basis. The first keyboard grouping is none, which disables all grouping and provides no functional change. The second is keymap, which groups the keyboard devices in the seat by their keymap. With this grouping, the effective layout and repeat info is also synced across keyboard devices in the seat. Device specific bindings will still be executed as normal, but everything else related to key and modifier events will be handled by the keyboard group's keyboard.
-rw-r--r--include/sway/commands.h1
-rw-r--r--include/sway/config.h7
-rw-r--r--include/sway/input/keyboard.h8
-rw-r--r--include/sway/input/seat.h1
-rw-r--r--sway/commands/seat.c1
-rw-r--r--sway/commands/seat/keyboard_grouping.c26
-rw-r--r--sway/config/seat.c5
-rw-r--r--sway/input/input-manager.c2
-rw-r--r--sway/input/keyboard.c272
-rw-r--r--sway/input/seat.c9
-rw-r--r--sway/meson.build1
-rw-r--r--sway/sway-input.5.scd10
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;
sway_cmd seat_cmd_cursor;
sway_cmd seat_cmd_fallback;
sway_cmd seat_cmd_hide_cursor;
+sway_cmd seat_cmd_keyboard_grouping;
sway_cmd seat_cmd_pointer_constraint;
sway_cmd seat_cmd_xcursor_theme;
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 {
CONSTRAIN_DISABLE
};
+enum seat_keyboard_grouping {
+ KEYBOARD_GROUP_DEFAULT, // the default is currently keymap
+ KEYBOARD_GROUP_NONE,
+ KEYBOARD_GROUP_KEYMAP
+};
+
/**
* Options for multiseat and other misc device configurations
*/
@@ -185,6 +191,7 @@ struct seat_config {
list_t *attachments; // list of seat_attachment configs
int hide_cursor_timeout;
enum seat_config_allow_constrain allow_constrain;
+ enum seat_keyboard_grouping keyboard_grouping;
struct {
char *name;
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 {
struct sway_binding *repeat_binding;
};
+struct sway_keyboard_group {
+ struct wlr_keyboard_group *wlr_group;
+ struct sway_seat_device *seat_device;
+ struct wl_listener keyboard_key;
+ struct wl_listener keyboard_modifiers;
+ struct wl_list link; // sway_seat::keyboard_groups
+};
+
struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic,
char **error);
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 {
struct wl_listener request_set_primary_selection;
struct wl_list devices; // sway_seat_device::link
+ struct wl_list keyboard_groups; // sway_keyboard_group::link
struct wl_list link; // input_manager::seats
};
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[] = {
{ "attach", seat_cmd_attach },
{ "fallback", seat_cmd_fallback },
{ "hide_cursor", seat_cmd_hide_cursor },
+ { "keyboard_grouping", seat_cmd_keyboard_grouping },
{ "pointer_constraint", seat_cmd_pointer_constraint },
{ "xcursor_theme", seat_cmd_xcursor_theme },
};
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 @@
+#include <string.h>
+#include "sway/commands.h"
+#include "sway/config.h"
+#include "stringop.h"
+
+struct cmd_results *seat_cmd_keyboard_grouping(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "keyboard_grouping", EXPECTED_EQUAL_TO, 1))) {
+ return error;
+ }
+ if (!config->handler_context.seat_config) {
+ return cmd_results_new(CMD_INVALID, "No seat defined");
+ }
+
+ struct seat_config *seat_config = config->handler_context.seat_config;
+ if (strcmp(argv[0], "none") == 0) {
+ seat_config->keyboard_grouping = KEYBOARD_GROUP_NONE;
+ } else if (strcmp(argv[0], "keymap") == 0) {
+ seat_config->keyboard_grouping = KEYBOARD_GROUP_KEYMAP;
+ } else {
+ return cmd_results_new(CMD_INVALID,
+ "Expected syntax `keyboard_grouping none|keymap`");
+ }
+
+ return cmd_results_new(CMD_SUCCESS, NULL);
+}
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) {
}
seat->hide_cursor_timeout = -1;
seat->allow_constrain = CONSTRAIN_DEFAULT;
+ seat->keyboard_grouping = KEYBOARD_GROUP_DEFAULT;
seat->xcursor_theme.name = NULL;
seat->xcursor_theme.size = 24;
@@ -150,6 +151,10 @@ void merge_seat_config(struct seat_config *dest, struct seat_config *source) {
dest->allow_constrain = source->allow_constrain;
}
+ if (source->keyboard_grouping != KEYBOARD_GROUP_DEFAULT) {
+ dest->keyboard_grouping = source->keyboard_grouping;
+ }
+
if (source->xcursor_theme.name != NULL) {
free(dest->xcursor_theme.name);
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) {
char *input_device_get_identifier(struct wlr_input_device *device) {
int vendor = device->vendor;
int product = device->product;
- char *name = strdup(device->name);
+ char *name = strdup(device->name ? device->name : "");
strip_whitespace(name);
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 @@
#include <strings.h>
#include <wlr/backend/multi.h>
#include <wlr/backend/session.h>
-#include <wlr/types/wlr_idle.h>
#include <wlr/interfaces/wlr_keyboard.h>
+#include <wlr/types/wlr_idle.h>
+#include <wlr/types/wlr_keyboard_group.h>
#include <xkbcommon/xkbcommon-names.h>
#include "sway/commands.h"
#include "sway/desktop/transaction.h"
@@ -150,7 +151,7 @@ static bool update_shortcut_state(struct sway_shortcut_state *state,
static void get_active_binding(const struct sway_shortcut_state *state,
list_t *bindings, struct sway_binding **current_binding,
uint32_t modifiers, bool release, bool locked, const char *input,
- xkb_layout_index_t group) {
+ bool exact_input, xkb_layout_index_t group) {
for (int i = 0; i < bindings->length; ++i) {
struct sway_binding *binding = bindings->items[i];
bool binding_locked = (binding->flags & BINDING_LOCKED) != 0;
@@ -162,7 +163,7 @@ static void get_active_binding(const struct sway_shortcut_state *state,
(binding->group != XKB_LAYOUT_INVALID &&
binding->group != group) ||
(strcmp(binding->input, input) != 0 &&
- strcmp(binding->input, "*") != 0)) {
+ (strcmp(binding->input, "*") != 0 || exact_input))) {
continue;
}
@@ -317,16 +318,15 @@ void sway_keyboard_disarm_key_repeat(struct sway_keyboard *keyboard) {
}
}
-static void handle_keyboard_key(struct wl_listener *listener, void *data) {
- struct sway_keyboard *keyboard =
- wl_container_of(listener, keyboard, keyboard_key);
+static void handle_key_event(struct sway_keyboard *keyboard,
+ struct wlr_event_keyboard_key *event) {
struct sway_seat* seat = keyboard->seat_device->sway_seat;
struct wlr_seat *wlr_seat = seat->wlr_seat;
struct wlr_input_device *wlr_device =
keyboard->seat_device->input_device->wlr_device;
char *device_identifier = input_device_get_identifier(wlr_device);
+ bool exact_identifier = wlr_device->keyboard->group != NULL;
wlr_idle_notify_activity(server.idle, wlr_seat);
- struct wlr_event_keyboard_key *event = data;
bool input_inhibited = seat->exclusive_client != NULL;
// 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) {
}
bool handled = false;
-
// Identify active release binding
struct sway_binding *binding_released = NULL;
get_active_binding(&keyboard->state_keycodes,
config->current_mode->keycode_bindings, &binding_released,
code_modifiers, true, input_inhibited, device_identifier,
- keyboard->effective_layout);
+ exact_identifier, keyboard->effective_layout);
get_active_binding(&keyboard->state_keysyms_raw,
config->current_mode->keysym_bindings, &binding_released,
raw_modifiers, true, input_inhibited, device_identifier,
- keyboard->effective_layout);
+ exact_identifier, keyboard->effective_layout);
get_active_binding(&keyboard->state_keysyms_translated,
config->current_mode->keysym_bindings, &binding_released,
translated_modifiers, true, input_inhibited, device_identifier,
- keyboard->effective_layout);
+ exact_identifier, keyboard->effective_layout);
// Execute stored release binding once no longer active
if (keyboard->held_binding && binding_released != keyboard->held_binding &&
@@ -395,15 +394,16 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
get_active_binding(&keyboard->state_keycodes,
config->current_mode->keycode_bindings, &binding,
code_modifiers, false, input_inhibited, device_identifier,
- keyboard->effective_layout);
+ exact_identifier, keyboard->effective_layout);
get_active_binding(&keyboard->state_keysyms_raw,
config->current_mode->keysym_bindings, &binding,
raw_modifiers, false, input_inhibited, device_identifier,
- keyboard->effective_layout);
+ exact_identifier, keyboard->effective_layout);
get_active_binding(&keyboard->state_keysyms_translated,
config->current_mode->keysym_bindings, &binding,
translated_modifiers, false, input_inhibited,
- device_identifier, keyboard->effective_layout);
+ device_identifier, exact_identifier,
+ keyboard->effective_layout);
}
// 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) {
handled = true;
}
+ if (!handled && wlr_device->keyboard->group) {
+ // Only handle device specific bindings for keyboards in a group
+ free(device_identifier);
+ return;
+ }
+
// Compositor bindings
if (!handled && event->state == WLR_KEY_PRESSED) {
handled = keyboard_execute_compositor_binding(
@@ -450,6 +456,19 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
free(device_identifier);
}
+static void handle_keyboard_key(struct wl_listener *listener, void *data) {
+ struct sway_keyboard *keyboard =
+ wl_container_of(listener, keyboard, keyboard_key);
+ handle_key_event(keyboard, data);
+}
+
+static void handle_keyboard_group_key(struct wl_listener *listener,
+ void *data) {
+ struct sway_keyboard_group *sway_group =
+ wl_container_of(listener, sway_group, keyboard_key);
+ handle_key_event(sway_group->seat_device->keyboard, data);
+}
+
static int handle_keyboard_repeat(void *data) {
struct sway_keyboard *keyboard = (struct sway_keyboard *)data;
struct wlr_keyboard *wlr_device =
@@ -491,25 +510,40 @@ static void determine_bar_visibility(uint32_t modifiers) {
}
}
-static void handle_keyboard_modifiers(struct wl_listener *listener,
- void *data) {
- struct sway_keyboard *keyboard =
- wl_container_of(listener, keyboard, keyboard_modifiers);
- struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;
+static void handle_modifier_event(struct sway_keyboard *keyboard) {
struct wlr_input_device *wlr_device =
keyboard->seat_device->input_device->wlr_device;
- wlr_seat_set_keyboard(wlr_seat, wlr_device);
- wlr_seat_keyboard_notify_modifiers(wlr_seat, &wlr_device->keyboard->modifiers);
+ if (!wlr_device->keyboard->group) {
+ struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;
+ wlr_seat_set_keyboard(wlr_seat, wlr_device);
+ wlr_seat_keyboard_notify_modifiers(wlr_seat,
+ &wlr_device->keyboard->modifiers);
- uint32_t modifiers = wlr_keyboard_get_modifiers(wlr_device->keyboard);
- determine_bar_visibility(modifiers);
+ uint32_t modifiers = wlr_keyboard_get_modifiers(wlr_device->keyboard);
+ determine_bar_visibility(modifiers);
+ }
- if (wlr_device->keyboard->modifiers.group != keyboard->effective_layout) {
+ if (wlr_device->keyboard->modifiers.group != keyboard->effective_layout &&
+ !wlr_keyboard_group_from_wlr_keyboard(wlr_device->keyboard)) {
keyboard->effective_layout = wlr_device->keyboard->modifiers.group;
ipc_event_input("xkb_layout", keyboard->seat_device->input_device);
}
}
+static void handle_keyboard_modifiers(struct wl_listener *listener,
+ void *data) {
+ struct sway_keyboard *keyboard =
+ wl_container_of(listener, keyboard, keyboard_modifiers);
+ handle_modifier_event(keyboard);
+}
+
+static void handle_keyboard_group_modifiers(struct wl_listener *listener,
+ void *data) {
+ struct sway_keyboard_group *group =
+ wl_container_of(listener, group, keyboard_modifiers);
+ handle_modifier_event(group->seat_device->keyboard);
+}
+
struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
struct sway_seat_device *device) {
struct sway_keyboard *keyboard =
@@ -616,6 +650,163 @@ cleanup:
return keymap;
}
+static bool keymaps_match(struct xkb_keymap *km1, struct xkb_keymap *km2) {
+ char *km1_str = xkb_keymap_get_as_string(km1, XKB_KEYMAP_FORMAT_TEXT_V1);
+ char *km2_str = xkb_keymap_get_as_string(km2, XKB_KEYMAP_FORMAT_TEXT_V1);
+ bool result = strcmp(km1_str, km2_str) == 0;
+ free(km1_str);
+ free(km2_str);
+ return result;
+}
+
+static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) {
+ struct sway_input_device *device = keyboard->seat_device->input_device;
+ struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard;
+ struct wlr_keyboard_group *wlr_group = wlr_keyboard->group;
+
+ sway_log(SWAY_DEBUG, "Removing keyboard %s from group %p",
+ device->identifier, wlr_group);
+
+ wlr_keyboard_group_remove_keyboard(wlr_keyboard->group, wlr_keyboard);
+
+ if (wl_list_empty(&wlr_group->devices)) {
+ sway_log(SWAY_DEBUG, "Destroying empty keyboard group %p",
+ wlr_group);
+ struct sway_keyboard_group *sway_group = wlr_group->data;
+ wlr_group->data = NULL;
+ wl_list_remove(&sway_group->link);
+ wl_list_remove(&sway_group->keyboard_key.link);
+ wl_list_remove(&sway_group->keyboard_modifiers.link);
+ free(sway_group->seat_device->keyboard);
+ free(sway_group->seat_device->input_device);
+ free(sway_group->seat_device);
+ free(sway_group);
+ wlr_keyboard_group_destroy(wlr_group);
+ }
+}
+
+static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) {
+ struct sway_input_device *device = keyboard->seat_device->input_device;
+ struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard;
+ if (!wlr_keyboard->group) {
+ return;
+ }
+
+ struct sway_seat *seat = keyboard->seat_device->sway_seat;
+ struct seat_config *sc = seat_get_config(seat);
+ if (!sc) {
+ sc = seat_get_config_by_name("*");
+ }
+
+ switch (sc ? sc->keyboard_grouping : KEYBOARD_GROUP_DEFAULT) {
+ case KEYBOARD_GROUP_NONE:
+ sway_keyboard_group_remove(keyboard);
+ break;
+ case KEYBOARD_GROUP_DEFAULT: /* fallthrough */
+ case KEYBOARD_GROUP_KEYMAP:;
+ struct wlr_keyboard_group *group = wlr_keyboard->group;
+ if (!keymaps_match(keyboard->keymap, group->keyboard.keymap)) {
+ sway_keyboard_group_remove(keyboard);
+ }
+ break;
+ }
+}
+
+static void sway_keyboard_group_add(struct sway_keyboard *keyboard) {
+ struct sway_input_device *device = keyboard->seat_device->input_device;
+ struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard;
+ struct sway_seat *seat = keyboard->seat_device->sway_seat;
+ struct seat_config *sc = seat_get_config(seat);
+ if (!sc) {
+ sc = seat_get_config_by_name("*");
+ }
+
+ if (sc && sc->keyboard_grouping == KEYBOARD_GROUP_NONE) {
+ // Keyboard grouping is disabled for the seat
+ return;
+ }
+
+ struct sway_keyboard_group *group;
+ wl_list_for_each(group, &seat->keyboard_groups, link) {
+ switch (sc ? sc->keyboard_grouping : KEYBOARD_GROUP_DEFAULT) {
+ case KEYBOARD_GROUP_NONE:
+ // Nothing to do. This shouldn't even be reached
+ return;
+ case KEYBOARD_GROUP_DEFAULT: /* fallthrough */
+ case KEYBOARD_GROUP_KEYMAP:;
+ struct wlr_keyboard_group *wlr_group = group->wlr_group;
+ if (keymaps_match(keyboard->keymap, wlr_group->keyboard.keymap)) {
+ sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p",
+ device->identifier, wlr_group);
+ wlr_keyboard_group_add_keyboard(wlr_group, wlr_keyboard);
+ return;
+ }
+ break;
+ }
+ }
+
+ struct sway_keyboard_group *sway_group =
+ calloc(1, sizeof(struct sway_keyboard_group));
+ if (!sway_group) {
+ sway_log(SWAY_ERROR, "Failed to allocate sway_keyboard_group");
+ return;
+ }
+
+ sway_group->wlr_group = wlr_keyboard_group_create();
+ if (!sway_group->wlr_group) {
+ sway_log(SWAY_ERROR, "Failed to create keyboard group");
+ goto cleanup;
+ }
+ sway_group->wlr_group->data = sway_group;
+ wlr_keyboard_set_keymap(&sway_group->wlr_group->keyboard, keyboard->keymap);
+ sway_log(SWAY_DEBUG, "Created keyboard group %p", sway_group->wlr_group);
+
+ sway_group->seat_device = calloc(1, sizeof(struct sway_seat_device));
+ if (!sway_group->seat_device) {
+ sway_log(SWAY_ERROR, "Failed to allocate sway_seat_device for group");
+ goto cleanup;
+ }
+ sway_group->seat_device->sway_seat = seat;
+
+ sway_group->seat_device->input_device =
+ calloc(1, sizeof(struct sway_input_device));
+ if (!sway_group->seat_device->input_device) {
+ sway_log(SWAY_ERROR, "Failed to allocate sway_input_device for group");
+ goto cleanup;
+ }
+ sway_group->seat_device->input_device->wlr_device =
+ sway_group->wlr_group->input_device;
+
+ if (!sway_keyboard_create(seat, sway_group->seat_device)) {
+ sway_log(SWAY_ERROR, "Failed to allocate sway_keyboard for group");
+ goto cleanup;
+ }
+
+ sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p",
+ device->identifier, sway_group->wlr_group);
+ wlr_keyboard_group_add_keyboard(sway_group->wlr_group, wlr_keyboard);
+
+ wl_list_insert(&seat->keyboard_groups, &sway_group->link);
+
+ wl_signal_add(&sway_group->wlr_group->keyboard.events.key,
+ &sway_group->keyboard_key);
+ sway_group->keyboard_key.notify = handle_keyboard_group_key;
+
+ wl_signal_add(&sway_group->wlr_group->keyboard.events.modifiers,
+ &sway_group->keyboard_modifiers);
+ sway_group->keyboard_modifiers.notify = handle_keyboard_group_modifiers;
+ return;
+
+cleanup:
+ if (sway_group && sway_group->wlr_group) {
+ wlr_keyboard_group_destroy(sway_group->wlr_group);
+ }
+ free(sway_group->seat_device->keyboard);
+ free(sway_group->seat_device->input_device);
+ free(sway_group->seat_device);
+ free(sway_group);
+}
+
void sway_keyboard_configure(struct sway_keyboard *keyboard) {
struct input_config *input_config =
input_device_get_config(keyboard->seat_device->input_device);
@@ -633,26 +824,23 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) {
}
}
- bool keymap_changed = false;
+ bool keymap_changed =
+ keyboard->keymap ? !keymaps_match(keyboard->keymap, keymap) : true;
bool effective_layout_changed = keyboard->effective_layout != 0;
- if (keyboard->keymap) {
- char *old_keymap_string = xkb_keymap_get_as_string(keyboard->keymap,
- XKB_KEYMAP_FORMAT_TEXT_V1);
- char *new_keymap_string = xkb_keymap_get_as_string(keymap,
- XKB_KEYMAP_FORMAT_TEXT_V1);
- keymap_changed = strcmp(old_keymap_string, new_keymap_string);
- free(old_keymap_string);
- free(new_keymap_string);
- } else {
- keymap_changed = true;
- }
if (keymap_changed || config->reloading) {
xkb_keymap_unref(keyboard->keymap);
keyboard->keymap = keymap;
keyboard->effective_layout = 0;
+
+ sway_keyboard_group_remove_invalid(keyboard);
+
wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap);
+ if (!wlr_device->keyboard->group) {
+ sway_keyboard_group_add(keyboard);
+ }
+
xkb_mod_mask_t locked_mods = 0;
if (input_config && input_config->xkb_numlock > 0) {
xkb_mod_index_t mod_index = xkb_map_mod_get_index(keymap,
@@ -679,10 +867,19 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) {
leds |= (1 << i);
}
}
- wlr_keyboard_led_update(wlr_device->keyboard, leds);
+ if (wlr_device->keyboard->group) {
+ wlr_keyboard_led_update(
+ &wlr_device->keyboard->group->keyboard, leds);
+ } else {
+ wlr_keyboard_led_update(wlr_device->keyboard, leds);
+ }
}
} else {
xkb_keymap_unref(keymap);
+ sway_keyboard_group_remove_invalid(keyboard);
+ if (!wlr_device->keyboard->group) {
+ sway_keyboard_group_add(keyboard);
+ }
}
int repeat_rate = 25;
@@ -721,6 +918,7 @@ void sway_keyboard_destroy(struct sway_keyboard *keyboard) {
if (!keyboard) {
return;
}
+ sway_keyboard_group_remove(keyboard);
if (keyboard->keymap) {
xkb_keymap_unref(keyboard->keymap);
}
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(
return seat_device->keyboard;
}
}
+ struct sway_keyboard_group *group;
+ wl_list_for_each(group, &seat->keyboard_groups, link) {
+ struct sway_input_device *input_device =
+ group->seat_device->input_device;
+ if (input_device->wlr_device->keyboard == wlr_keyboard) {
+ return group->seat_device->keyboard;
+ }
+ }
return NULL;
}
@@ -519,6 +527,7 @@ struct sway_seat *seat_create(const char *seat_name) {
handle_request_set_primary_selection;
wl_list_init(&seat->devices);
+ wl_list_init(&seat->keyboard_groups);
wl_list_insert(&server.input->seats, &seat->link);
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(
'commands/seat/cursor.c',
'commands/seat/fallback.c',
'commands/seat/hide_cursor.c',
+ 'commands/seat/keyboard_grouping.c',
'commands/seat/pointer_constraint.c',
'commands/seat/xcursor_theme.c',
'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.
disables hiding the cursor. The minimal timeout is 100 and any value less
than that (aside from 0), will be increased to 100.
+*seat* <name> keyboard_grouping none|keymap
+ Set how the keyboards in the seat are grouped together. Currently, there
+ are two options. _none_ will disable all keyboard grouping. This will make
+ it so each keyboard device has its own isolated state. _keymap_ will
+ group the keyboards in the seat by their keymap. This is useful for when
+ the keyboard appears as multiple separate input devices. In this mode,
+ the effective layout and repeat info are also synced between the keyboards
+ in the group. The default is _keymap_. To restore the behavior of older
+ versions of sway, use _none_.
+
*seat* <name> pointer_constraint enable|disable|escape
Enables or disables the ability for clients to capture the cursor (enabled
by default) for the seat. This is primarily useful for video games. The