store layout per keyboard for every window

Fixes: https://github.com/swaywm/sway/issues/2361

Signed-off-by: Konstantin Kharlamov <Hi-Angel@yandex.ru>
This commit is contained in:
Konstantin Kharlamov 2018-11-04 18:59:57 +03:00
parent 481accca2a
commit 3a5544f56c
9 changed files with 143 additions and 5 deletions

View file

@ -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

View file

@ -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);

View file

@ -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];

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);