This commit is contained in:
2026-07-12 08:16:17 +03:30
commit 72be1234e1
2027 changed files with 221388 additions and 0 deletions
+33
View File
@@ -0,0 +1,33 @@
### Description
This patch adds the ability to pass keys that are specified in the config header globally, similar to Hyprland's approach.
This might deal with Wayland's lack of global shortcuts.
Example:
```
static const PassKeypressRule pass_rules[] = {
ADDPASSRULE("com.obsproject.Studio", XKB_KEY_Home),
ADDPASSRULE("WebCord", XKB_KEY_n),
/* xkb key is case-insensitive */
};
```
This will pass the `Home` key (alongside with mods) to OBS regardless of what client is currently focused, if any.
The string "com.obsproject.Studio" should match the exact appid of the client.
To get the appid use [dwlmsg](https://codeberg.org/notchoc/dwlmsg) or run stock dwl from a terminal then launch the needed application inside, dwl will print all the info to the stdout.
Note that if a popup (like [fuzzel](https://codeberg.org/dnkl/fuzzel)) is focused, no keys will be globally passed.
This is done so these menus don't get closed after hitting some of the global keys.
## Warning
This patch is a stupid hack, it doesn't work all the time.
Examples: obs needs to be clicked on once before applying global hotkeys.
Electron (discord/webcord/chromium) with wayland backend ignores the very first press.
Other programs might not work at all.
### Download
- [git branch](https://codeberg.org/korei999/dwl/src/branch/globalkey)
- [2024-06-08](https://codeberg.org/dwl/dwl-patches/src/branch/main/patches/globalkey/globalkey.patch)
### Authors
- [korei999](https://codeberg.org/korei999)
+131
View File
@@ -0,0 +1,131 @@
From f36e3f134c9f14a9821783d9908471ed0bdca0ed Mon Sep 17 00:00:00 2001
From: korei999 <ju7t1xe@gmail.com>
Date: Fri, 14 Mar 2025 20:05:45 +0200
Subject: [PATCH] implement globalkey patch
---
config.def.h | 8 +++++++
dwl.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 69 insertions(+)
diff --git a/config.def.h b/config.def.h
index 22d2171..25486c8 100644
--- a/config.def.h
+++ b/config.def.h
@@ -122,6 +122,14 @@ static const enum libinput_config_tap_button_map button_map = LIBINPUT_CONFIG_TA
static const char *termcmd[] = { "foot", NULL };
static const char *menucmd[] = { "wmenu-run", NULL };
+#define ADDPASSRULE(S, K) {.appid = S, .len = LENGTH(S), .key = K}
+static const PassKeypressRule pass_rules[] = {
+ ADDPASSRULE("com.obsproject.Studio", XKB_KEY_Home),
+ ADDPASSRULE("com.obsproject.Studio", XKB_KEY_End),
+ ADDPASSRULE("com.obsproject.Studio", XKB_KEY_F12),
+ ADDPASSRULE("WebCord", XKB_KEY_n),
+};
+
static const Key keys[] = {
/* Note that Shift changes certain key codes: c -> C, 2 -> at, etc. */
/* modifier key function argument */
diff --git a/dwl.c b/dwl.c
index 4816159..9ad64dd 100644
--- a/dwl.c
+++ b/dwl.c
@@ -217,6 +217,12 @@ typedef struct {
int x, y;
} MonitorRule;
+typedef struct {
+ const char* appid;
+ size_t len;
+ uint32_t key;
+} PassKeypressRule;
+
typedef struct {
struct wlr_pointer_constraint_v1 *constraint;
struct wl_listener destroy;
@@ -293,6 +299,7 @@ static void incnmaster(const Arg *arg);
static void inputdevice(struct wl_listener *listener, void *data);
static int keybinding(uint32_t mods, xkb_keysym_t sym);
static void keypress(struct wl_listener *listener, void *data);
+static void keypressglobal(struct wlr_surface *last_surface, struct wlr_keyboard *keyboard, struct wlr_keyboard_key_event *event, uint32_t mods, xkb_keysym_t keysym);
static void keypressmod(struct wl_listener *listener, void *data);
static int keyrepeat(void *data);
static void killclient(const Arg *arg);
@@ -1628,6 +1635,12 @@ keypress(struct wl_listener *listener, void *data)
/* This event is raised when a key is pressed or released. */
KeyboardGroup *group = wl_container_of(listener, group, key);
struct wlr_keyboard_key_event *event = data;
+ struct wlr_surface *last_surface = seat->keyboard_state.focused_surface;
+ struct wlr_xdg_surface *xdg_surface = last_surface ? wlr_xdg_surface_try_from_wlr_surface(last_surface) : NULL;
+ int pass = 0;
+#ifdef XWAYLAND
+ struct wlr_xwayland_surface *xsurface = last_surface ? wlr_xwayland_surface_try_from_wlr_surface(last_surface) : NULL;
+#endif
/* Translate libinput keycode -> xkbcommon */
uint32_t keycode = event->keycode + 8;
@@ -1662,12 +1675,60 @@ keypress(struct wl_listener *listener, void *data)
if (handled)
return;
+ /* don't pass when popup is focused
+ * this is better than having popups (like fuzzel or wmenu) closing while typing in a passed keybind */
+ pass = (xdg_surface && xdg_surface->role != WLR_XDG_SURFACE_ROLE_POPUP) || !last_surface
+#ifdef XWAYLAND
+ || xsurface
+#endif
+ ;
+ /* passed keys don't get repeated */
+ if (!locked && pass) {
+ for (i = 0; i < nsyms; ++i)
+ keypressglobal(last_surface, &group->wlr_group->keyboard, event, mods, syms[i]);
+ }
+
wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard);
/* Pass unhandled keycodes along to the client. */
wlr_seat_keyboard_notify_key(seat, event->time_msec,
event->keycode, event->state);
}
+void
+keypressglobal(struct wlr_surface *last_surface, struct wlr_keyboard *keyboard, struct wlr_keyboard_key_event *event, uint32_t mods, xkb_keysym_t keysym)
+{
+ Client *c = NULL, *lastc = focustop(selmon);
+ int reset = false;
+ const char *appid = NULL;
+
+ for (size_t r = 0; r < LENGTH(pass_rules); r++) {
+ uint32_t rcode = xkb_keysym_to_upper(pass_rules[r].key);
+ uint32_t pcode = xkb_keysym_to_upper(keysym);
+
+ /* match key only (case insensitive) ignoring mods */
+ if (rcode == pcode) {
+ wl_list_for_each(c, &clients, link) {
+ if (c && c != lastc) {
+ appid = client_get_appid(c);
+ if (appid && strncmp(appid, pass_rules[r].appid, pass_rules[r].len) == 0) {
+ reset = true;
+
+ client_notify_enter(client_surface(c), keyboard);
+ client_activate_surface(client_surface(c), 1);
+ wlr_seat_keyboard_send_key(seat, event->time_msec, event->keycode, event->state);
+
+ goto done;
+ }
+ }
+ }
+ }
+ }
+
+done:
+ if (reset && last_surface)
+ client_notify_enter(last_surface, keyboard);
+}
+
void
keypressmod(struct wl_listener *listener, void *data)
{
--
2.48.1