From 3a5544f56c0dc9bfb8c27dd4cafc3a3f9a31937c Mon Sep 17 00:00:00 2001 From: Konstantin Kharlamov Date: Sun, 4 Nov 2018 18:59:57 +0300 Subject: [PATCH] store layout per keyboard for every window Fixes: https://github.com/swaywm/sway/issues/2361 Signed-off-by: Konstantin Kharlamov --- include/sway/input/keyboard.h | 10 +++++++ include/sway/tree/view.h | 4 ++- sway/config/seat.c | 5 ++++ sway/desktop/xdg_shell.c | 6 +++- sway/desktop/xdg_shell_v6.c | 6 +++- sway/desktop/xwayland.c | 6 +++- sway/input/keyboard.c | 55 +++++++++++++++++++++++++++++++++++ sway/input/seat.c | 29 ++++++++++++++++++ sway/tree/view.c | 27 ++++++++++++++++- 9 files changed, 143 insertions(+), 5 deletions(-) diff --git a/include/sway/input/keyboard.h b/include/sway/input/keyboard.h index b86220535..55dac5693 100644 --- a/include/sway/input/keyboard.h +++ b/include/sway/input/keyboard.h @@ -63,10 +63,16 @@ struct sway_keyboard { struct wl_event_source *key_repeat_source; struct sway_binding *repeat_binding; + xkb_layout_index_t default_kbd_layout; }; struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic); +struct sway_layout_per_kbd { + struct wlr_keyboard *kbd; + xkb_layout_index_t layout; +}; + struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, struct sway_seat_device *device); @@ -74,5 +80,9 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard); void sway_keyboard_destroy(struct sway_keyboard *keyboard); +void sway_keyboard_set_layout(struct wlr_keyboard *kbd, xkb_layout_index_t layout); + +xkb_layout_index_t sway_keyboard_get_layout(struct xkb_state *kbd_state); + void sway_keyboard_disarm_key_repeat(struct sway_keyboard *keyboard); #endif diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 4ce487fca..af6d22d63 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -111,6 +111,8 @@ struct sway_view { } events; struct wl_listener surface_new_subsurface; + + list_t *kbd_layouts; // struct sway_layout_per_kbd * }; struct sway_xdg_shell_v6_view { @@ -309,7 +311,7 @@ void view_for_each_popup(struct sway_view *view, // view implementation -void view_init(struct sway_view *view, enum sway_view_type type, +bool view_init(struct sway_view *view, enum sway_view_type type, const struct sway_view_impl *impl); void view_destroy(struct sway_view *view); diff --git a/sway/config/seat.c b/sway/config/seat.c index 04a44e3a8..f7f48775c 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->keep_keyboard_layout = KEYBOARD_LAYOUT_UNDEFINED; return seat; } @@ -116,6 +117,10 @@ void merge_seat_config(struct seat_config *dest, struct seat_config *source) { dest->fallback = source->fallback; } + if (source->keep_keyboard_layout != KEYBOARD_LAYOUT_UNDEFINED) { + dest->keep_keyboard_layout = source->keep_keyboard_layout; + } + for (int i = 0; i < source->attachments->length; ++i) { struct seat_attachment_config *source_attachment = source->attachments->items[i]; diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 9e914f144..85dfbcda8 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -504,7 +504,11 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { return; } - view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl); + if (!view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl)) { + sway_assert(false, "Failed to init view"); + return; + } + xdg_shell_view->view.wlr_xdg_surface = xdg_surface; xdg_shell_view->map.notify = handle_map; diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 5ac589cfd..ac5c6ea9a 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -492,7 +492,11 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { return; } - view_init(&xdg_shell_v6_view->view, SWAY_VIEW_XDG_SHELL_V6, &view_impl); + if (!view_init(&xdg_shell_v6_view->view, SWAY_VIEW_XDG_SHELL_V6, &view_impl)) { + sway_assert(false, "Failed to init view"); + return; + } + xdg_shell_v6_view->view.wlr_xdg_surface_v6 = xdg_surface; xdg_shell_v6_view->map.notify = handle_map; diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index f6ca8f818..3d52980b9 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -595,7 +595,11 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) { return; } - view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl); + if (!view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl)) { + sway_assert(false, "Failed to init view"); + return; + } + xwayland_view->view.wlr_xwayland_surface = xsurface; wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy); diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index dcfaa4fad..546515c76 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -12,6 +12,7 @@ #include "sway/input/keyboard.h" #include "sway/input/seat.h" #include "sway/ipc-server.h" +#include "sway/tree/view.h" #include "log.h" static struct modifier_key { @@ -475,6 +476,20 @@ static void handle_keyboard_modifiers(struct wl_listener *listener, determine_bar_visibility(modifiers); } +static void add_kbd_to_view(struct sway_container *con, void *data) { + if (!con->view) { + return; + } + + struct sway_layout_per_kbd *lpk = calloc(1, sizeof(struct sway_layout_per_kbd)); + if (!sway_assert(lpk, "Failed to allocate sway_layout_per_kbd")) { + return; + } + + *lpk = *(struct sway_layout_per_kbd *)data; + list_add(con->view->kbd_layouts, lpk); +} + struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, struct sway_seat_device *device) { struct sway_keyboard *keyboard = @@ -580,6 +595,32 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { wl_signal_add(&wlr_device->keyboard->events.modifiers, &keyboard->keyboard_modifiers); keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers; + + keyboard->default_kbd_layout = + sway_keyboard_get_layout(wlr_device->keyboard->xkb_state); + struct sway_layout_per_kbd lpk = { + .kbd = wlr_device->keyboard, + .layout = keyboard->default_kbd_layout + }; + root_for_each_container(add_kbd_to_view, &lpk); +} + +static void rm_kbd_from_view(struct sway_container *con, void *data) { + if (!con->view) { + return; + } + + list_t *kbd_layouts = con->view->kbd_layouts; + for (int i = 0; i < kbd_layouts->length; ++i) { + struct sway_layout_per_kbd *lpk = kbd_layouts->items[i]; + if (lpk->kbd == (struct wlr_keyboard *)data) { + free(lpk); + list_del(kbd_layouts, i); + return; + } + } + sway_assert(false, + "sway_layout_per_kbd not found in sway_view for removal"); } void sway_keyboard_destroy(struct sway_keyboard *keyboard) { @@ -593,5 +634,19 @@ void sway_keyboard_destroy(struct sway_keyboard *keyboard) { wl_list_remove(&keyboard->keyboard_modifiers.link); sway_keyboard_disarm_key_repeat(keyboard); wl_event_source_remove(keyboard->key_repeat_source); + root_for_each_container(rm_kbd_from_view, + keyboard->seat_device->input_device->wlr_device->keyboard); free(keyboard); } + +void sway_keyboard_set_layout(struct wlr_keyboard *kbd, xkb_layout_index_t layout) { + wlr_keyboard_notify_modifiers(kbd, + kbd->modifiers.depressed, + kbd->modifiers.latched, + kbd->modifiers.locked, + layout); +} + +xkb_layout_index_t sway_keyboard_get_layout(struct xkb_state *kbd_state) { + return xkb_state_serialize_layout(kbd_state, XKB_STATE_LAYOUT_EFFECTIVE); +} diff --git a/sway/input/seat.c b/sway/input/seat.c index ce009d7e4..ffbcac831 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -79,6 +79,13 @@ static void seat_send_activate(struct sway_node *node, struct sway_seat *seat) { } } +static void set_kbd_layouts(const struct sway_view *view) { + for (int i = 0; i < view->kbd_layouts->length; ++i) { + struct sway_layout_per_kbd *lpk = view->kbd_layouts->items[i]; + sway_keyboard_set_layout(lpk->kbd, lpk->layout); + } +} + /** * If con is a view, set it as active and enable keyboard input. * If con is a container, set all child views as active and don't enable @@ -99,6 +106,12 @@ static void seat_send_focus(struct sway_node *node, struct sway_seat *seat) { #endif struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); if (keyboard) { + const struct seat_config *seat_config = seat_get_config(seat); + if (seat_config) { + if (seat_config->keep_keyboard_layout == KEYBOARD_LAYOUT_PER_WINDOW) { + set_kbd_layouts(view); + } + } wlr_seat_keyboard_notify_enter(seat->wlr_seat, view->surface, keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers); @@ -765,6 +778,13 @@ static void send_unfocus(struct sway_container *con, void *data) { } } +static void save_kbd_layouts(struct sway_view *view) { + for (int i = 0; i < view->kbd_layouts->length; ++i) { + struct sway_layout_per_kbd *lpk = view->kbd_layouts->items[i]; + lpk->layout = sway_keyboard_get_layout(lpk->kbd->xkb_state); + } +} + // Unfocus the container and any children (eg. when leaving `focus parent`) static void seat_send_unfocus(struct sway_node *node, struct sway_seat *seat) { sway_cursor_constrain(seat->cursor, NULL); @@ -772,6 +792,15 @@ static void seat_send_unfocus(struct sway_node *node, struct sway_seat *seat) { if (node->type == N_WORKSPACE) { workspace_for_each_container(node->sway_workspace, send_unfocus, seat); } else { + struct sway_view *view = node->type == N_CONTAINER ? + node->sway_container->view : NULL; + const struct seat_config *seat_config = seat_get_config(seat); + + if (seat_config && view && seat_is_input_allowed(seat, view->surface) && + seat_config->keep_keyboard_layout == KEYBOARD_LAYOUT_PER_WINDOW) { + save_kbd_layouts(view); + } + send_unfocus(node->sway_container, seat); container_for_each_child(node->sway_container, send_unfocus, seat); } diff --git a/sway/tree/view.c b/sway/tree/view.c index 4fd3a65ae..eed75ff2e 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -18,6 +18,7 @@ #include "sway/desktop.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" +#include "sway/input/keyboard.h" #include "sway/ipc-server.h" #include "sway/output.h" #include "sway/input/seat.h" @@ -30,13 +31,36 @@ #include "pango.h" #include "stringop.h" -void view_init(struct sway_view *view, enum sway_view_type type, +static bool view_setup_kbd_layouts(struct sway_view *view) { + struct sway_seat *seat = NULL; + wl_list_for_each(seat, &server.input->seats, link) { + struct sway_seat_device *device = NULL; + wl_list_for_each(device, &seat->devices, link) { + struct wlr_input_device *wlr_input_device = device->input_device->wlr_device; + if (wlr_input_device->type != WLR_INPUT_DEVICE_KEYBOARD) { + continue; + } + struct sway_layout_per_kbd *lpk = calloc(1, sizeof(struct sway_layout_per_kbd)); + if (!sway_assert(lpk, "Failed to allocate sway_layout_per_kbd")) { + return false; + } + lpk->kbd = wlr_input_device->keyboard; + lpk->layout = device->keyboard->default_kbd_layout; + list_add(view->kbd_layouts, lpk); + } + } + return true; +} + +bool view_init(struct sway_view *view, enum sway_view_type type, const struct sway_view_impl *impl) { view->type = type; view->impl = impl; view->executed_criteria = create_list(); + view->kbd_layouts = create_list(); view->allow_request_urgent = true; wl_signal_init(&view->events.unmap); + return view_setup_kbd_layouts(view); } void view_destroy(struct sway_view *view) { @@ -55,6 +79,7 @@ void view_destroy(struct sway_view *view) { list_free(view->executed_criteria); free(view->title_format); + list_free_items_and_destroy(view->kbd_layouts); if (view->impl->destroy) { view->impl->destroy(view);