From a6a07bedeca09d33130253e03cf8d0c188f55776 Mon Sep 17 00:00:00 2001 From: biosfood Date: Thu, 23 Oct 2025 09:28:02 +0200 Subject: [PATCH] individual-keyboard-rules --- patches/individual-keyboard-rules/README.md | 14 + .../individual-keyboard-configurations.patch | 366 ++++++++++++++++++ 2 files changed, 380 insertions(+) create mode 100644 patches/individual-keyboard-rules/README.md create mode 100644 patches/individual-keyboard-rules/individual-keyboard-configurations.patch diff --git a/patches/individual-keyboard-rules/README.md b/patches/individual-keyboard-rules/README.md new file mode 100644 index 0000000..a2a664b --- /dev/null +++ b/patches/individual-keyboard-rules/README.md @@ -0,0 +1,14 @@ +# Individual keyboard configurations + +Apply custom rules for individual keyboards. Under the hood, this is implemented by removing the normal grouping of keyboards. + +The provided example will assign a German input layout to one keyboard and an English one to any other. + +## Download + +- [git branch](https://codeberg.org/biosfood/dwl/src/branch/individual-keyboard-configurations) +- [2025-11-14](./individual-keyboard-configurations.patch) + +## Authors + +- [Lukas Eisenhauer](https://codeberg.org/biosfood) diff --git a/patches/individual-keyboard-rules/individual-keyboard-configurations.patch b/patches/individual-keyboard-rules/individual-keyboard-configurations.patch new file mode 100644 index 0000000..df73347 --- /dev/null +++ b/patches/individual-keyboard-rules/individual-keyboard-configurations.patch @@ -0,0 +1,366 @@ +From 72d166c481d9bec66506c3064efb9ca650c05f87 Mon Sep 17 00:00:00 2001 +From: biosfood +Date: Sun, 12 Oct 2025 20:24:24 +0200 +Subject: [PATCH] individual keyboard rules + +--- + config.def.h | 18 ++++-- + dwl.c | 160 ++++++++++++++++++++++++++------------------------- + 2 files changed, 93 insertions(+), 85 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 95c2afa..fc33b55 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -52,12 +52,18 @@ static const MonitorRule monrules[] = { + }; + + /* keyboard */ +-static const struct xkb_rule_names xkb_rules = { +- /* can specify fields: rules, model, layout, variant, options */ +- /* example: +- .options = "ctrl:nocaps", +- */ +- .options = NULL, ++static const KeyboardRule keyboard_rules[] = { ++ { ++ "AT Translated Set 2 keyboard", ++ { .layout = "de", .options = "caps:super" } ++ }, ++ { ++ "", // fallback ++ { ++ .layout = "en", ++ /* can specify fields: rules, model, layout, variant, options */ ++ } ++ }, + }; + + static const int repeat_rate = 25; +diff --git a/dwl.c b/dwl.c +index 12f441e..1871299 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -149,7 +150,8 @@ typedef struct { + } Key; + + typedef struct { +- struct wlr_keyboard_group *wlr_group; ++ struct wl_list link; ++ struct wlr_keyboard *wlr_keyboard; + + int nsyms; + const xkb_keysym_t *keysyms; /* invalid if nsyms == 0 */ +@@ -159,7 +161,12 @@ typedef struct { + struct wl_listener modifiers; + struct wl_listener key; + struct wl_listener destroy; +-} KeyboardGroup; ++} Keyboard; ++ ++typedef struct { ++ const char *name; ++ struct xkb_rule_names xkb_rules; ++} KeyboardRule; + + typedef struct { + /* Must keep this field first */ +@@ -260,7 +267,6 @@ static void commitpopup(struct wl_listener *listener, void *data); + static void createdecoration(struct wl_listener *listener, void *data); + static void createidleinhibitor(struct wl_listener *listener, void *data); + static void createkeyboard(struct wlr_keyboard *keyboard); +-static KeyboardGroup *createkeyboardgroup(void); + static void createlayersurface(struct wl_listener *listener, void *data); + static void createlocksurface(struct wl_listener *listener, void *data); + static void createmon(struct wl_listener *listener, void *data); +@@ -280,7 +286,7 @@ static void destroylocksurface(struct wl_listener *listener, void *data); + static void destroynotify(struct wl_listener *listener, void *data); + static void destroypointerconstraint(struct wl_listener *listener, void *data); + static void destroysessionlock(struct wl_listener *listener, void *data); +-static void destroykeyboardgroup(struct wl_listener *listener, void *data); ++static void destroykeyboard(struct wl_listener *listener, void *data); + static Monitor *dirtomon(enum wlr_direction dir); + static void focusclient(Client *c, int lift); + static void focusmon(const Arg *arg); +@@ -396,7 +402,7 @@ static struct wlr_scene_rect *locked_bg; + static struct wlr_session_lock_v1 *cur_lock; + + static struct wlr_seat *seat; +-static KeyboardGroup *kb_group; ++static struct wl_list keyboards; + static unsigned int cursor_mode; + static Client *grabc; + static int grabcx, grabcy; /* client-relative */ +@@ -696,6 +702,19 @@ checkidleinhibitor(struct wlr_surface *exclude) + wlr_idle_notifier_v1_set_inhibited(idle_notifier, inhibited); + } + ++void ++cleanupkeyboard(struct wl_listener *listener, void *data) ++{ ++ Keyboard *kb = wl_container_of(listener, kb, destroy); ++ ++ wl_event_source_remove(kb->key_repeat_source); ++ wl_list_remove(&kb->link); ++ wl_list_remove(&kb->modifiers.link); ++ wl_list_remove(&kb->key.link); ++ wl_list_remove(&kb->destroy.link); ++ free(kb); ++} ++ + void + cleanup(void) + { +@@ -711,8 +730,6 @@ cleanup(void) + } + wlr_xcursor_manager_destroy(cursor_mgr); + +- destroykeyboardgroup(&kb_group->destroy, NULL); +- + /* If it's not destroyed manually, it will cause a use-after-free of wlr_seat. + * Destroy it until it's fixed on the wlroots side */ + wlr_backend_destroy(backend); +@@ -940,48 +957,45 @@ createidleinhibitor(struct wl_listener *listener, void *data) + void + createkeyboard(struct wlr_keyboard *keyboard) + { +- /* Set the keymap to match the group keymap */ +- wlr_keyboard_set_keymap(keyboard, kb_group->wlr_group->keyboard.keymap); ++ const struct xkb_rule_names *rules; ++ size_t i; ++ for (i = 0; i < LENGTH(keyboard_rules); i++) { ++ if (strcmp(keyboard->base.name, keyboard_rules[i].name) == 0 || ++ i == LENGTH(keyboard_rules) - 1) { ++ rules = &keyboard_rules[i].xkb_rules; ++ } ++ } + +- /* Add the new keyboard to the group */ +- wlr_keyboard_group_add_keyboard(kb_group->wlr_group, keyboard); +-} + +-KeyboardGroup * +-createkeyboardgroup(void) +-{ +- KeyboardGroup *group = ecalloc(1, sizeof(*group)); + struct xkb_context *context; + struct xkb_keymap *keymap; ++ Keyboard *kb = keyboard->data = ecalloc(1, sizeof(*kb)); ++ kb->wlr_keyboard = keyboard; + +- group->wlr_group = wlr_keyboard_group_create(); +- group->wlr_group->data = group; +- +- /* Prepare an XKB keymap and assign it to the keyboard group. */ ++ /* Prepare an XKB keymap and assign it to the keyboard. */ + context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); +- if (!(keymap = xkb_keymap_new_from_names(context, &xkb_rules, +- XKB_KEYMAP_COMPILE_NO_FLAGS))) +- die("failed to compile keymap"); ++ keymap = xkb_keymap_new_from_names(context, rules, ++ XKB_KEYMAP_COMPILE_NO_FLAGS); ++ if (!keymap) ++ die("createkeyboard: failed to compile keymap"); + +- wlr_keyboard_set_keymap(&group->wlr_group->keyboard, keymap); ++ wlr_keyboard_set_keymap(keyboard, keymap); + xkb_keymap_unref(keymap); + xkb_context_unref(context); ++ wlr_keyboard_set_repeat_info(keyboard, repeat_rate, repeat_delay); + +- wlr_keyboard_set_repeat_info(&group->wlr_group->keyboard, repeat_rate, repeat_delay); ++ /* Here we set up listeners for keyboard events. */ ++ LISTEN(&keyboard->events.modifiers, &kb->modifiers, keypressmod); ++ LISTEN(&keyboard->events.key, &kb->key, keypress); ++ LISTEN(&keyboard->base.events.destroy, &kb->destroy, cleanupkeyboard); + +- /* Set up listeners for keyboard events */ +- LISTEN(&group->wlr_group->keyboard.events.key, &group->key, keypress); +- LISTEN(&group->wlr_group->keyboard.events.modifiers, &group->modifiers, keypressmod); ++ wlr_seat_set_keyboard(seat, keyboard); + +- group->key_repeat_source = wl_event_loop_add_timer(event_loop, keyrepeat, group); ++ kb->key_repeat_source = wl_event_loop_add_timer( ++ wl_display_get_event_loop(dpy), keyrepeat, kb); + +- /* A seat can only have one keyboard, but this is a limitation of the +- * Wayland protocol - not wlroots. We assign all connected keyboards to the +- * same wlr_keyboard_group, which provides a single wlr_keyboard interface for +- * all of them. Set this combined wlr_keyboard as the seat keyboard. +- */ +- wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard); +- return group; ++ /* And add the keyboard to our list of keyboards */ ++ wl_list_insert(&keyboards, &kb->link); + } + + void +@@ -1372,15 +1386,14 @@ destroysessionlock(struct wl_listener *listener, void *data) + } + + void +-destroykeyboardgroup(struct wl_listener *listener, void *data) ++destroykeyboard(struct wl_listener *listener, void *data) + { +- KeyboardGroup *group = wl_container_of(listener, group, destroy); +- wl_event_source_remove(group->key_repeat_source); +- wl_list_remove(&group->key.link); +- wl_list_remove(&group->modifiers.link); +- wl_list_remove(&group->destroy.link); +- wlr_keyboard_group_destroy(group->wlr_group); +- free(group); ++ Keyboard *kb = wl_container_of(listener, kb, destroy); ++ wl_event_source_remove(kb->key_repeat_source); ++ wl_list_remove(&kb->key.link); ++ wl_list_remove(&kb->modifiers.link); ++ wl_list_remove(&kb->destroy.link); ++ free(kb); + } + + Monitor * +@@ -1600,8 +1613,7 @@ inputdevice(struct wl_listener *listener, void *data) + * there are no pointer devices, so we always include that capability. */ + /* TODO do we actually require a cursor? */ + caps = WL_SEAT_CAPABILITY_POINTER; +- if (!wl_list_empty(&kb_group->wlr_group->devices)) +- caps |= WL_SEAT_CAPABILITY_KEYBOARD; ++ caps |= WL_SEAT_CAPABILITY_KEYBOARD; + wlr_seat_set_capabilities(seat, caps); + } + +@@ -1629,7 +1641,7 @@ keypress(struct wl_listener *listener, void *data) + { + int i; + /* This event is raised when a key is pressed or released. */ +- KeyboardGroup *group = wl_container_of(listener, group, key); ++ Keyboard *kb = wl_container_of(listener, kb, key); + struct wlr_keyboard_key_event *event = data; + + /* Translate libinput keycode -> xkbcommon */ +@@ -1637,10 +1649,10 @@ keypress(struct wl_listener *listener, void *data) + /* Get a list of keysyms based on the keymap for this keyboard */ + const xkb_keysym_t *syms; + int nsyms = xkb_state_key_get_syms( +- group->wlr_group->keyboard.xkb_state, keycode, &syms); ++ kb->wlr_keyboard->xkb_state, keycode, &syms); + + int handled = 0; +- uint32_t mods = wlr_keyboard_get_modifiers(&group->wlr_group->keyboard); ++ uint32_t mods = wlr_keyboard_get_modifiers(kb->wlr_keyboard); + + wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); + +@@ -1650,22 +1662,21 @@ keypress(struct wl_listener *listener, void *data) + for (i = 0; i < nsyms; i++) + handled = keybinding(mods, syms[i]) || handled; + } +- +- if (handled && group->wlr_group->keyboard.repeat_info.delay > 0) { +- group->mods = mods; +- group->keysyms = syms; +- group->nsyms = nsyms; +- wl_event_source_timer_update(group->key_repeat_source, +- group->wlr_group->keyboard.repeat_info.delay); ++ if (handled && kb->wlr_keyboard->repeat_info.delay > 0) { ++ kb->mods = mods; ++ kb->keysyms = syms; ++ kb->nsyms = nsyms; ++ wl_event_source_timer_update(kb->key_repeat_source, ++ kb->wlr_keyboard->repeat_info.delay); + } else { +- group->nsyms = 0; +- wl_event_source_timer_update(group->key_repeat_source, 0); ++ kb->nsyms = 0; ++ wl_event_source_timer_update(kb->key_repeat_source, 0); + } + + if (handled) + return; + +- wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard); ++ wlr_seat_set_keyboard(seat, kb->wlr_keyboard); + /* Pass unhandled keycodes along to the client. */ + wlr_seat_keyboard_notify_key(seat, event->time_msec, + event->keycode, event->state); +@@ -1676,27 +1687,27 @@ keypressmod(struct wl_listener *listener, void *data) + { + /* This event is raised when a modifier key, such as shift or alt, is + * pressed. We simply communicate this to the client. */ +- KeyboardGroup *group = wl_container_of(listener, group, modifiers); ++ Keyboard *kb = wl_container_of(listener, kb, modifiers); + +- wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard); ++ wlr_seat_set_keyboard(seat, kb->wlr_keyboard); + /* Send modifiers to the client. */ + wlr_seat_keyboard_notify_modifiers(seat, +- &group->wlr_group->keyboard.modifiers); ++ &kb->wlr_keyboard->modifiers); + } + + int + keyrepeat(void *data) + { +- KeyboardGroup *group = data; ++ Keyboard *kb = data; + int i; +- if (!group->nsyms || group->wlr_group->keyboard.repeat_info.rate <= 0) ++ if (!kb->nsyms || kb->wlr_keyboard->repeat_info.rate <= 0) + return 0; + +- wl_event_source_timer_update(group->key_repeat_source, +- 1000 / group->wlr_group->keyboard.repeat_info.rate); ++ wl_event_source_timer_update(kb->key_repeat_source, ++ 1000 / kb->wlr_keyboard->repeat_info.rate); + +- for (i = 0; i < group->nsyms; i++) +- keybinding(group->mods, group->keysyms[i]); ++ for (i = 0; i < kb->nsyms; i++) ++ keybinding(kb->mods, kb->keysyms[i]); + + return 0; + } +@@ -2448,6 +2459,7 @@ setup(void) + struct sigaction sa = {.sa_flags = SA_RESTART, .sa_handler = handlesig}; + sigemptyset(&sa.sa_mask); + ++ wl_list_init(&keyboards); + for (i = 0; i < (int)LENGTH(sig); i++) + sigaction(sig[i], &sa, NULL); + +@@ -2638,9 +2650,6 @@ setup(void) + wl_signal_add(&seat->events.request_start_drag, &request_start_drag); + wl_signal_add(&seat->events.start_drag, &start_drag); + +- kb_group = createkeyboardgroup(); +- wl_list_init(&kb_group->destroy.link); +- + output_mgr = wlr_output_manager_v1_create(dpy); + wl_signal_add(&output_mgr->events.apply, &output_mgr_apply); + wl_signal_add(&output_mgr->events.test, &output_mgr_test); +@@ -2982,14 +2991,7 @@ void + virtualkeyboard(struct wl_listener *listener, void *data) + { + struct wlr_virtual_keyboard_v1 *kb = data; +- /* virtual keyboards shouldn't share keyboard group */ +- KeyboardGroup *group = createkeyboardgroup(); +- /* Set the keymap to match the group keymap */ +- wlr_keyboard_set_keymap(&kb->keyboard, group->wlr_group->keyboard.keymap); +- LISTEN(&kb->keyboard.base.events.destroy, &group->destroy, destroykeyboardgroup); +- +- /* Add the new keyboard to the group */ +- wlr_keyboard_group_add_keyboard(group->wlr_group, &kb->keyboard); ++ createkeyboard(&kb->keyboard); + } + + void +-- +2.51.2 +