mirror of
https://codeberg.org/dwl/dwl-patches.git
synced 2025-12-15 09:23:24 +00:00
Compare commits
1 Commits
a6a07bedec
...
5c93b1f831
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c93b1f831 |
14
patches/individual-keyboard-rules/README.md
Normal file
14
patches/individual-keyboard-rules/README.md
Normal file
@ -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)
|
||||||
@ -0,0 +1,366 @@
|
|||||||
|
From 72d166c481d9bec66506c3064efb9ca650c05f87 Mon Sep 17 00:00:00 2001
|
||||||
|
From: biosfood <mail@lukas-eisenhauer.de>
|
||||||
|
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 <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
+#include <string.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
@@ -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
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user