commit dc72dae11b1ae4d76b70881e8c5bd62055234e60 Author: Nikita Ivanov Date: Sun Apr 7 22:03:49 2024 +0200 Add per client keyboard layout and status bar info diff --git a/config.def.h b/config.def.h index 8f498d2..484e522 100644 --- a/config.def.h +++ b/config.def.h @@ -13,6 +13,9 @@ static const float focuscolor[] = COLOR(0x005577ff); static const float urgentcolor[] = COLOR(0xff0000ff); /* This conforms to the xdg-protocol. Set the alpha to zero to restore the old behavior */ static const float fullscreen_bg[] = {0.1f, 0.1f, 0.1f, 1.0f}; /* You can also use glsl colors */ +/* keyboard layout change notification for status bar */ +static const char kblayout_file[] = "/tmp/dwl-kblayout"; +static const char *kblayout_cmd[] = {"pkill", "-RTMIN+1", "someblocks", NULL}; /* tagging - TAGCOUNT must be no greater than 31 */ #define TAGCOUNT (9) diff --git a/dwl.c b/dwl.c index bf763df..c8da00e 100644 --- a/dwl.c +++ b/dwl.c @@ -139,6 +139,7 @@ typedef struct { uint32_t tags; int isfloating, isurgent, isfullscreen; uint32_t resize; /* configure serial of a pending resize */ + unsigned int kblayout_idx; } Client; typedef struct { @@ -286,6 +287,7 @@ static void fullscreennotify(struct wl_listener *listener, void *data); static void handlesig(int signo); static void incnmaster(const Arg *arg); static void inputdevice(struct wl_listener *listener, void *data); +static void kblayout(KeyboardGroup *kb); static int keybinding(uint32_t mods, xkb_keysym_t sym); static void keypress(struct wl_listener *listener, void *data); static void keypressmod(struct wl_listener *listener, void *data); @@ -405,6 +407,8 @@ static struct wlr_box sgeom; static struct wl_list mons; static Monitor *selmon; +static unsigned int kblayout_idx = -1; + #ifdef XWAYLAND static void activatex11(struct wl_listener *listener, void *data); static void associatex11(struct wl_listener *listener, void *data); @@ -795,6 +799,8 @@ createkeyboard(struct wlr_keyboard *keyboard) /* Add the new keyboard to the group */ wlr_keyboard_group_add_keyboard(kb_group.wlr_group, keyboard); + + kblayout(&kb_group); } void @@ -951,6 +957,7 @@ createnotify(struct wl_listener *listener, void *data) struct wlr_xdg_surface *xdg_surface = data; Client *c = NULL; LayerSurface *l = NULL; + struct wlr_keyboard *kb = wlr_seat_get_keyboard(seat); if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { struct wlr_xdg_popup *popup = xdg_surface->popup; @@ -973,6 +980,7 @@ createnotify(struct wl_listener *listener, void *data) c = xdg_surface->data = ecalloc(1, sizeof(*c)); c->surface.xdg = xdg_surface; c->bw = borderpx; + c->kblayout_idx = kb ? kb->modifiers.group : 0; wlr_xdg_toplevel_set_wm_capabilities(xdg_surface->toplevel, WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN); @@ -1236,10 +1244,26 @@ dirtomon(enum wlr_direction dir) void focusclient(Client *c, int lift) { + /* Copied from wlroots/types/wlr_keyboard_group.c */ + struct keyboard_group_device { + struct wlr_keyboard *keyboard; + struct wl_listener key; + struct wl_listener modifiers; + struct wl_listener keymap; + struct wl_listener repeat_info; + struct wl_listener destroy; + struct wl_list link; // wlr_keyboard_group.devices + }; + struct wlr_surface *old = seat->keyboard_state.focused_surface; int unused_lx, unused_ly, old_client_type; Client *old_c = NULL; LayerSurface *old_l = NULL; + xkb_mod_mask_t mdepr, mlatc, mlock; + xkb_layout_index_t ldepr, llatc, llock; + struct keyboard_group_device *device; + struct wlr_keyboard *kb = wlr_seat_get_keyboard(seat); + struct wlr_keyboard_group *group = kb ? wlr_keyboard_group_from_wlr_keyboard(kb) : NULL; if (locked) return; @@ -1292,6 +1316,20 @@ focusclient(Client *c, int lift) } printstatus(); + /* Update keyboard layout */ + if (group) { + // Update the first real device, because kb or group->kb is not a real + // keyboard and its effective layout gets overwritten + device = wl_container_of(group->devices.next, device, link); + mdepr = xkb_state_serialize_mods(device->keyboard->xkb_state, XKB_STATE_MODS_DEPRESSED); + mlatc = xkb_state_serialize_mods(device->keyboard->xkb_state, XKB_STATE_MODS_LATCHED); + mlock = xkb_state_serialize_mods(device->keyboard->xkb_state, XKB_STATE_MODS_LOCKED); + ldepr = xkb_state_serialize_layout(device->keyboard->xkb_state, XKB_STATE_LAYOUT_DEPRESSED); + llatc = xkb_state_serialize_layout(device->keyboard->xkb_state, XKB_STATE_LAYOUT_LATCHED); + llock = c ? c->kblayout_idx : 0; + xkb_state_update_mask(device->keyboard->xkb_state, mdepr, mlatc, mlock, ldepr, llatc, llock); + } + if (!c) { /* With no client, all we have left is to clear focus */ wlr_seat_keyboard_notify_clear_focus(seat); @@ -1302,7 +1340,7 @@ focusclient(Client *c, int lift) motionnotify(0, NULL, 0, 0, 0, 0); /* Have a client, so focus its top-level wlr_surface */ - client_notify_enter(client_surface(c), wlr_seat_get_keyboard(seat)); + client_notify_enter(client_surface(c), kb); /* Activate the new client */ client_activate_surface(client_surface(c), 1); @@ -1427,6 +1465,36 @@ inputdevice(struct wl_listener *listener, void *data) wlr_seat_set_capabilities(seat, caps); } +void +kblayout(KeyboardGroup *kb) +{ + FILE *f; + Client *c; + unsigned int idx = kb->wlr_group->keyboard.modifiers.group; + + // If layout did not change, do nothing + if (kblayout_idx == idx) + return; + kblayout_idx = idx; + + // Update client layout + if ((c = focustop(selmon))) + c->kblayout_idx = kblayout_idx; + + // Save current layout to kblayout_file + if (*kblayout_file && (f = fopen(kblayout_file, "w"))) { + fputs(xkb_keymap_layout_get_name(kb->wlr_group->keyboard.keymap, + idx), f); + fclose(f); + } + + // Run kblayout_cmd + if (kblayout_cmd[0] && fork() == 0) { + execvp(kblayout_cmd[0], (char *const *)kblayout_cmd); + die("dwl: execvp %s failed:", kblayout_cmd[0]); + } +} + int keybinding(uint32_t mods, xkb_keysym_t sym) { @@ -1504,6 +1572,8 @@ keypressmod(struct wl_listener *listener, void *data) /* Send modifiers to the client. */ wlr_seat_keyboard_notify_modifiers(seat, &group->wlr_group->keyboard.modifiers); + + kblayout(group); } int