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.
This commit is contained in:
parent
2f858a1ada
commit
5d882cb5fc
|
@ -284,6 +284,7 @@ sway_cmd seat_cmd_attach;
|
||||||
sway_cmd seat_cmd_cursor;
|
sway_cmd seat_cmd_cursor;
|
||||||
sway_cmd seat_cmd_fallback;
|
sway_cmd seat_cmd_fallback;
|
||||||
sway_cmd seat_cmd_hide_cursor;
|
sway_cmd seat_cmd_hide_cursor;
|
||||||
|
sway_cmd seat_cmd_keyboard_grouping;
|
||||||
sway_cmd seat_cmd_pointer_constraint;
|
sway_cmd seat_cmd_pointer_constraint;
|
||||||
sway_cmd seat_cmd_xcursor_theme;
|
sway_cmd seat_cmd_xcursor_theme;
|
||||||
|
|
||||||
|
|
|
@ -176,6 +176,12 @@ enum seat_config_allow_constrain {
|
||||||
CONSTRAIN_DISABLE
|
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
|
* Options for multiseat and other misc device configurations
|
||||||
*/
|
*/
|
||||||
|
@ -185,6 +191,7 @@ struct seat_config {
|
||||||
list_t *attachments; // list of seat_attachment configs
|
list_t *attachments; // list of seat_attachment configs
|
||||||
int hide_cursor_timeout;
|
int hide_cursor_timeout;
|
||||||
enum seat_config_allow_constrain allow_constrain;
|
enum seat_config_allow_constrain allow_constrain;
|
||||||
|
enum seat_keyboard_grouping keyboard_grouping;
|
||||||
struct {
|
struct {
|
||||||
char *name;
|
char *name;
|
||||||
int size;
|
int size;
|
||||||
|
|
|
@ -67,6 +67,14 @@ struct sway_keyboard {
|
||||||
struct sway_binding *repeat_binding;
|
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,
|
struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic,
|
||||||
char **error);
|
char **error);
|
||||||
|
|
||||||
|
|
|
@ -90,6 +90,7 @@ struct sway_seat {
|
||||||
struct wl_listener request_set_primary_selection;
|
struct wl_listener request_set_primary_selection;
|
||||||
|
|
||||||
struct wl_list devices; // sway_seat_device::link
|
struct wl_list devices; // sway_seat_device::link
|
||||||
|
struct wl_list keyboard_groups; // sway_keyboard_group::link
|
||||||
|
|
||||||
struct wl_list link; // input_manager::seats
|
struct wl_list link; // input_manager::seats
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,6 +18,7 @@ static struct cmd_handler seat_handlers[] = {
|
||||||
{ "attach", seat_cmd_attach },
|
{ "attach", seat_cmd_attach },
|
||||||
{ "fallback", seat_cmd_fallback },
|
{ "fallback", seat_cmd_fallback },
|
||||||
{ "hide_cursor", seat_cmd_hide_cursor },
|
{ "hide_cursor", seat_cmd_hide_cursor },
|
||||||
|
{ "keyboard_grouping", seat_cmd_keyboard_grouping },
|
||||||
{ "pointer_constraint", seat_cmd_pointer_constraint },
|
{ "pointer_constraint", seat_cmd_pointer_constraint },
|
||||||
{ "xcursor_theme", seat_cmd_xcursor_theme },
|
{ "xcursor_theme", seat_cmd_xcursor_theme },
|
||||||
};
|
};
|
||||||
|
|
26
sway/commands/seat/keyboard_grouping.c
Normal file
26
sway/commands/seat/keyboard_grouping.c
Normal file
|
@ -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);
|
||||||
|
}
|
|
@ -27,6 +27,7 @@ struct seat_config *new_seat_config(const char* name) {
|
||||||
}
|
}
|
||||||
seat->hide_cursor_timeout = -1;
|
seat->hide_cursor_timeout = -1;
|
||||||
seat->allow_constrain = CONSTRAIN_DEFAULT;
|
seat->allow_constrain = CONSTRAIN_DEFAULT;
|
||||||
|
seat->keyboard_grouping = KEYBOARD_GROUP_DEFAULT;
|
||||||
seat->xcursor_theme.name = NULL;
|
seat->xcursor_theme.name = NULL;
|
||||||
seat->xcursor_theme.size = 24;
|
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;
|
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) {
|
if (source->xcursor_theme.name != NULL) {
|
||||||
free(dest->xcursor_theme.name);
|
free(dest->xcursor_theme.name);
|
||||||
dest->xcursor_theme.name = strdup(source->xcursor_theme.name);
|
dest->xcursor_theme.name = strdup(source->xcursor_theme.name);
|
||||||
|
|
|
@ -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) {
|
char *input_device_get_identifier(struct wlr_input_device *device) {
|
||||||
int vendor = device->vendor;
|
int vendor = device->vendor;
|
||||||
int product = device->product;
|
int product = device->product;
|
||||||
char *name = strdup(device->name);
|
char *name = strdup(device->name ? device->name : "");
|
||||||
strip_whitespace(name);
|
strip_whitespace(name);
|
||||||
|
|
||||||
char *p = name;
|
char *p = name;
|
||||||
|
|
|
@ -3,8 +3,9 @@
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
#include <wlr/backend/multi.h>
|
#include <wlr/backend/multi.h>
|
||||||
#include <wlr/backend/session.h>
|
#include <wlr/backend/session.h>
|
||||||
#include <wlr/types/wlr_idle.h>
|
|
||||||
#include <wlr/interfaces/wlr_keyboard.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 <xkbcommon/xkbcommon-names.h>
|
||||||
#include "sway/commands.h"
|
#include "sway/commands.h"
|
||||||
#include "sway/desktop/transaction.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,
|
static void get_active_binding(const struct sway_shortcut_state *state,
|
||||||
list_t *bindings, struct sway_binding **current_binding,
|
list_t *bindings, struct sway_binding **current_binding,
|
||||||
uint32_t modifiers, bool release, bool locked, const char *input,
|
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) {
|
for (int i = 0; i < bindings->length; ++i) {
|
||||||
struct sway_binding *binding = bindings->items[i];
|
struct sway_binding *binding = bindings->items[i];
|
||||||
bool binding_locked = (binding->flags & BINDING_LOCKED) != 0;
|
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 != XKB_LAYOUT_INVALID &&
|
||||||
binding->group != group) ||
|
binding->group != group) ||
|
||||||
(strcmp(binding->input, input) != 0 &&
|
(strcmp(binding->input, input) != 0 &&
|
||||||
strcmp(binding->input, "*") != 0)) {
|
(strcmp(binding->input, "*") != 0 || exact_input))) {
|
||||||
continue;
|
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) {
|
static void handle_key_event(struct sway_keyboard *keyboard,
|
||||||
struct sway_keyboard *keyboard =
|
struct wlr_event_keyboard_key *event) {
|
||||||
wl_container_of(listener, keyboard, keyboard_key);
|
|
||||||
struct sway_seat* seat = keyboard->seat_device->sway_seat;
|
struct sway_seat* seat = keyboard->seat_device->sway_seat;
|
||||||
struct wlr_seat *wlr_seat = seat->wlr_seat;
|
struct wlr_seat *wlr_seat = seat->wlr_seat;
|
||||||
struct wlr_input_device *wlr_device =
|
struct wlr_input_device *wlr_device =
|
||||||
keyboard->seat_device->input_device->wlr_device;
|
keyboard->seat_device->input_device->wlr_device;
|
||||||
char *device_identifier = input_device_get_identifier(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);
|
wlr_idle_notify_activity(server.idle, wlr_seat);
|
||||||
struct wlr_event_keyboard_key *event = data;
|
|
||||||
bool input_inhibited = seat->exclusive_client != NULL;
|
bool input_inhibited = seat->exclusive_client != NULL;
|
||||||
|
|
||||||
// Identify new keycode, raw keysym(s), and translated keysym(s)
|
// 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;
|
bool handled = false;
|
||||||
|
|
||||||
// Identify active release binding
|
// Identify active release binding
|
||||||
struct sway_binding *binding_released = NULL;
|
struct sway_binding *binding_released = NULL;
|
||||||
get_active_binding(&keyboard->state_keycodes,
|
get_active_binding(&keyboard->state_keycodes,
|
||||||
config->current_mode->keycode_bindings, &binding_released,
|
config->current_mode->keycode_bindings, &binding_released,
|
||||||
code_modifiers, true, input_inhibited, device_identifier,
|
code_modifiers, true, input_inhibited, device_identifier,
|
||||||
keyboard->effective_layout);
|
exact_identifier, keyboard->effective_layout);
|
||||||
get_active_binding(&keyboard->state_keysyms_raw,
|
get_active_binding(&keyboard->state_keysyms_raw,
|
||||||
config->current_mode->keysym_bindings, &binding_released,
|
config->current_mode->keysym_bindings, &binding_released,
|
||||||
raw_modifiers, true, input_inhibited, device_identifier,
|
raw_modifiers, true, input_inhibited, device_identifier,
|
||||||
keyboard->effective_layout);
|
exact_identifier, keyboard->effective_layout);
|
||||||
get_active_binding(&keyboard->state_keysyms_translated,
|
get_active_binding(&keyboard->state_keysyms_translated,
|
||||||
config->current_mode->keysym_bindings, &binding_released,
|
config->current_mode->keysym_bindings, &binding_released,
|
||||||
translated_modifiers, true, input_inhibited, device_identifier,
|
translated_modifiers, true, input_inhibited, device_identifier,
|
||||||
keyboard->effective_layout);
|
exact_identifier, keyboard->effective_layout);
|
||||||
|
|
||||||
// Execute stored release binding once no longer active
|
// Execute stored release binding once no longer active
|
||||||
if (keyboard->held_binding && binding_released != keyboard->held_binding &&
|
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,
|
get_active_binding(&keyboard->state_keycodes,
|
||||||
config->current_mode->keycode_bindings, &binding,
|
config->current_mode->keycode_bindings, &binding,
|
||||||
code_modifiers, false, input_inhibited, device_identifier,
|
code_modifiers, false, input_inhibited, device_identifier,
|
||||||
keyboard->effective_layout);
|
exact_identifier, keyboard->effective_layout);
|
||||||
get_active_binding(&keyboard->state_keysyms_raw,
|
get_active_binding(&keyboard->state_keysyms_raw,
|
||||||
config->current_mode->keysym_bindings, &binding,
|
config->current_mode->keysym_bindings, &binding,
|
||||||
raw_modifiers, false, input_inhibited, device_identifier,
|
raw_modifiers, false, input_inhibited, device_identifier,
|
||||||
keyboard->effective_layout);
|
exact_identifier, keyboard->effective_layout);
|
||||||
get_active_binding(&keyboard->state_keysyms_translated,
|
get_active_binding(&keyboard->state_keysyms_translated,
|
||||||
config->current_mode->keysym_bindings, &binding,
|
config->current_mode->keysym_bindings, &binding,
|
||||||
translated_modifiers, false, input_inhibited,
|
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
|
// 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;
|
handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!handled && wlr_device->keyboard->group) {
|
||||||
|
// Only handle device specific bindings for keyboards in a group
|
||||||
|
free(device_identifier);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Compositor bindings
|
// Compositor bindings
|
||||||
if (!handled && event->state == WLR_KEY_PRESSED) {
|
if (!handled && event->state == WLR_KEY_PRESSED) {
|
||||||
handled = keyboard_execute_compositor_binding(
|
handled = keyboard_execute_compositor_binding(
|
||||||
|
@ -450,6 +456,19 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) {
|
||||||
free(device_identifier);
|
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) {
|
static int handle_keyboard_repeat(void *data) {
|
||||||
struct sway_keyboard *keyboard = (struct sway_keyboard *)data;
|
struct sway_keyboard *keyboard = (struct sway_keyboard *)data;
|
||||||
struct wlr_keyboard *wlr_device =
|
struct wlr_keyboard *wlr_device =
|
||||||
|
@ -491,23 +510,38 @@ static void determine_bar_visibility(uint32_t modifiers) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void handle_modifier_event(struct sway_keyboard *keyboard) {
|
||||||
|
struct wlr_input_device *wlr_device =
|
||||||
|
keyboard->seat_device->input_device->wlr_device;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
static void handle_keyboard_modifiers(struct wl_listener *listener,
|
||||||
void *data) {
|
void *data) {
|
||||||
struct sway_keyboard *keyboard =
|
struct sway_keyboard *keyboard =
|
||||||
wl_container_of(listener, keyboard, keyboard_modifiers);
|
wl_container_of(listener, keyboard, keyboard_modifiers);
|
||||||
struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;
|
handle_modifier_event(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);
|
|
||||||
|
|
||||||
uint32_t modifiers = wlr_keyboard_get_modifiers(wlr_device->keyboard);
|
static void handle_keyboard_group_modifiers(struct wl_listener *listener,
|
||||||
determine_bar_visibility(modifiers);
|
void *data) {
|
||||||
|
struct sway_keyboard_group *group =
|
||||||
if (wlr_device->keyboard->modifiers.group != keyboard->effective_layout) {
|
wl_container_of(listener, group, keyboard_modifiers);
|
||||||
keyboard->effective_layout = wlr_device->keyboard->modifiers.group;
|
handle_modifier_event(group->seat_device->keyboard);
|
||||||
ipc_event_input("xkb_layout", keyboard->seat_device->input_device);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
|
struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
|
||||||
|
@ -616,6 +650,163 @@ cleanup:
|
||||||
return keymap;
|
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) {
|
void sway_keyboard_configure(struct sway_keyboard *keyboard) {
|
||||||
struct input_config *input_config =
|
struct input_config *input_config =
|
||||||
input_device_get_config(keyboard->seat_device->input_device);
|
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;
|
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) {
|
if (keymap_changed || config->reloading) {
|
||||||
xkb_keymap_unref(keyboard->keymap);
|
xkb_keymap_unref(keyboard->keymap);
|
||||||
keyboard->keymap = keymap;
|
keyboard->keymap = keymap;
|
||||||
keyboard->effective_layout = 0;
|
keyboard->effective_layout = 0;
|
||||||
|
|
||||||
|
sway_keyboard_group_remove_invalid(keyboard);
|
||||||
|
|
||||||
wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap);
|
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;
|
xkb_mod_mask_t locked_mods = 0;
|
||||||
if (input_config && input_config->xkb_numlock > 0) {
|
if (input_config && input_config->xkb_numlock > 0) {
|
||||||
xkb_mod_index_t mod_index = xkb_map_mod_get_index(keymap,
|
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);
|
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 {
|
} else {
|
||||||
xkb_keymap_unref(keymap);
|
xkb_keymap_unref(keymap);
|
||||||
|
sway_keyboard_group_remove_invalid(keyboard);
|
||||||
|
if (!wlr_device->keyboard->group) {
|
||||||
|
sway_keyboard_group_add(keyboard);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int repeat_rate = 25;
|
int repeat_rate = 25;
|
||||||
|
@ -721,6 +918,7 @@ void sway_keyboard_destroy(struct sway_keyboard *keyboard) {
|
||||||
if (!keyboard) {
|
if (!keyboard) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
sway_keyboard_group_remove(keyboard);
|
||||||
if (keyboard->keymap) {
|
if (keyboard->keymap) {
|
||||||
xkb_keymap_unref(keyboard->keymap);
|
xkb_keymap_unref(keyboard->keymap);
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,6 +102,14 @@ static struct sway_keyboard *sway_keyboard_for_wlr_keyboard(
|
||||||
return seat_device->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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -519,6 +527,7 @@ struct sway_seat *seat_create(const char *seat_name) {
|
||||||
handle_request_set_primary_selection;
|
handle_request_set_primary_selection;
|
||||||
|
|
||||||
wl_list_init(&seat->devices);
|
wl_list_init(&seat->devices);
|
||||||
|
wl_list_init(&seat->keyboard_groups);
|
||||||
|
|
||||||
wl_list_insert(&server.input->seats, &seat->link);
|
wl_list_insert(&server.input->seats, &seat->link);
|
||||||
|
|
||||||
|
|
|
@ -91,6 +91,7 @@ sway_sources = files(
|
||||||
'commands/seat/cursor.c',
|
'commands/seat/cursor.c',
|
||||||
'commands/seat/fallback.c',
|
'commands/seat/fallback.c',
|
||||||
'commands/seat/hide_cursor.c',
|
'commands/seat/hide_cursor.c',
|
||||||
|
'commands/seat/keyboard_grouping.c',
|
||||||
'commands/seat/pointer_constraint.c',
|
'commands/seat/pointer_constraint.c',
|
||||||
'commands/seat/xcursor_theme.c',
|
'commands/seat/xcursor_theme.c',
|
||||||
'commands/set.c',
|
'commands/set.c',
|
||||||
|
|
|
@ -218,6 +218,16 @@ correct seat.
|
||||||
disables hiding the cursor. The minimal timeout is 100 and any value less
|
disables hiding the cursor. The minimal timeout is 100 and any value less
|
||||||
than that (aside from 0), will be increased to 100.
|
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
|
*seat* <name> pointer_constraint enable|disable|escape
|
||||||
Enables or disables the ability for clients to capture the cursor (enabled
|
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
|
by default) for the seat. This is primarily useful for video games. The
|
||||||
|
|
Loading…
Reference in a new issue