[dwm][patch][keysequence] Add new patch - sites - public wiki contents of suckl… | |
git clone git://git.suckless.org/sites | |
Log | |
Files | |
Refs | |
--- | |
commit d166954782572f6ca3c36136c31b9e9e6e5efb2e | |
parent 648d2edf20bb3fb153660f48a27a03adfd121dea | |
Author: TUVIMEN <[email protected]> | |
Date: Fri, 6 Jun 2025 09:18:30 +0200 | |
[dwm][patch][keysequence] Add new patch | |
Add patch that allows for defining sequential key bindings | |
Diffstat: | |
A dwm.suckless.org/patches/keysequen… | 50 +++++++++++++++++++++++++++… | |
A dwm.suckless.org/patches/keysequen… | 158 +++++++++++++++++++++++++++… | |
2 files changed, 208 insertions(+), 0 deletions(-) | |
--- | |
diff --git a/dwm.suckless.org/patches/keysequence/index.md b/dwm.suckless.org/p… | |
@@ -0,0 +1,50 @@ | |
+keysequence | |
+=========== | |
+ | |
+Description | |
+----------- | |
+ | |
+This patch allows for defining sequential keybindings, for example `MOD+A W`. | |
+This is done not through the `XGrabKey()` which is used for root bindings, | |
+but by the `XGrabKeyboard()` so any key presses that are not defined will | |
+stop the matching without passing them to programs. This behaviour is | |
+less confusing. | |
+ | |
+It defines new bindable function `keypress_other()` that as argument takes | |
+a pointer to array of bindings. | |
+ | |
+ static Key keyseq_a[] = { | |
+ { 0, XK_t, setlayout, {.v = &layouts[… | |
+ { ShiftMask, XK_t, setlayout, {.v = &layouts[… | |
+ { MODKEY, XK_y, setlayout, {.v = &lay… | |
+ {0} | |
+ } | |
+ | |
+ static Key keys[] = { | |
+ { MODKEY, XK_a, keypress_other, {.v = keyseq_a}}, | |
+ {0} | |
+ } | |
+ | |
+This assigns `MOD+a t`, `MOD+a T`, `MOD+a MOD+y` to changing layout, you can | |
+nest bindings endlessly. | |
+ | |
+Notice that now keybinding arrays are ended by `{0}` empty element, | |
+this is necessary because `Arg` structure can take only pointer | |
+sized elements, there's no place to specify size so it has to be | |
+calculated while running. | |
+ | |
+While typing sequence all events are ignored other than key presses | |
+and mouse, moving mouse exits sequence. | |
+ | |
+Save your hands! Don't make long sequences, stick to simple, easy to | |
+access binding and use plain letters, realistically speaking you'll | |
+use it for dmenu scripts, layout bindings, and other rarely used | |
+commands. | |
+ | |
+Download | |
+-------- | |
+* [keysequence-20250606-0d6af14.diff](keysequence-20250606-0d6af14.diff) | |
+ | |
+Author | |
+------ | |
+* TUVIMEN <[email protected]> | |
diff --git a/dwm.suckless.org/patches/keysequence/keysequence-20250606-0d6af14.… | |
@@ -0,0 +1,158 @@ | |
+From 0d6af14efa1250631b081ad9d1f3aa0263221fd8 Mon Sep 17 00:00:00 2001 | |
+From: TUVIMEN <[email protected]> | |
+Date: Fri, 6 Jun 2025 09:08:40 +0200 | |
+Subject: [PATCH] [PATCH] 6.5 keysequence patch | |
+ | |
+--- | |
+ config.def.h | 1 + | |
+ dwm.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++----- | |
+ 2 files changed, 80 insertions(+), 7 deletions(-) | |
+ | |
+diff --git a/config.def.h b/config.def.h | |
+index 9efa774..6104343 100644 | |
+--- a/config.def.h | |
++++ b/config.def.h | |
+@@ -95,6 +95,7 @@ static const Key keys[] = { | |
+ TAGKEYS( XK_8, 7) | |
+ TAGKEYS( XK_9, 8) | |
+ { MODKEY|ShiftMask, XK_q, quit, {0} }, | |
++ {0} | |
+ }; | |
+ | |
+ /* button definitions */ | |
+diff --git a/dwm.c b/dwm.c | |
+index f1d86b2..89a4bc5 100644 | |
+--- a/dwm.c | |
++++ b/dwm.c | |
+@@ -28,6 +28,7 @@ | |
+ #include <stdlib.h> | |
+ #include <string.h> | |
+ #include <unistd.h> | |
++#include <time.h> | |
+ #include <sys/types.h> | |
+ #include <sys/wait.h> | |
+ #include <X11/cursorfont.h> | |
+@@ -177,6 +178,7 @@ static void grabbuttons(Client *c, int focused); | |
+ static void grabkeys(void); | |
+ static void incnmaster(const Arg *arg); | |
+ static void keypress(XEvent *e); | |
++static void keypress_other(const Arg *arg); | |
+ static void killclient(const Arg *arg); | |
+ static void manage(Window w, XWindowAttributes *wa); | |
+ static void mappingnotify(XEvent *e); | |
+@@ -949,6 +951,13 @@ grabbuttons(Client *c, int focused) | |
+ } | |
+ } | |
+ | |
++static char | |
++key_not_empty(const Key *key) | |
++{ | |
++ static const Key empty = {0}; | |
++ return memcmp(key, &empty, sizeof(Key)) != 0; | |
++} | |
++ | |
+ void | |
+ grabkeys(void) | |
+ { | |
+@@ -965,7 +974,7 @@ grabkeys(void) | |
+ if (!syms) | |
+ return; | |
+ for (k = start; k <= end; k++) | |
+- for (i = 0; i < LENGTH(keys); i++) | |
++ for (i = 0; key_not_empty(keys+i); i++) | |
+ /* skip modifier codes, we do that ourselves … | |
+ if (keys[i].keysym == syms[(k - start) * skip… | |
+ for (j = 0; j < LENGTH(modifiers); j+… | |
+@@ -996,8 +1005,8 @@ isuniquegeom(XineramaScreenInfo *unique, size_t n, Xinera… | |
+ } | |
+ #endif /* XINERAMA */ | |
+ | |
+-void | |
+-keypress(XEvent *e) | |
++static void | |
++keypress_r(XEvent *e, const Key *keys) | |
+ { | |
+ unsigned int i; | |
+ KeySym keysym; | |
+@@ -1005,11 +1014,74 @@ keypress(XEvent *e) | |
+ | |
+ ev = &e->xkey; | |
+ keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); | |
+- for (i = 0; i < LENGTH(keys); i++) | |
++ for (i = 0; key_not_empty(keys+i); i++) | |
+ if (keysym == keys[i].keysym | |
+- && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) | |
+- && keys[i].func) | |
+- keys[i].func(&(keys[i].arg)); | |
++ && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) | |
++ && keys[i].func) | |
++ keys[i].func(&(keys[i].arg)); | |
++} | |
++ | |
++static char | |
++grabkeyboard(void) //taken from dmenu | |
++{ | |
++ struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 }; | |
++ | |
++ /* try to grab keyboard, we may have to wait for another process to u… | |
++ for (int i = 0; i < 1000; i++) { | |
++ if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabMode… | |
++ GrabModeAsync, CurrentTime) == GrabSuccess) | |
++ return 0; | |
++ nanosleep(&ts, NULL); | |
++ } | |
++ return 1; //failed | |
++} | |
++ | |
++static char | |
++keysym_is_modifier(KeySym s) | |
++{ | |
++ return (s == XK_Shift_L || | |
++ s == XK_Shift_R || | |
++ s == XK_Control_L || | |
++ s == XK_Control_R || | |
++ s == XK_Caps_Lock || | |
++ s == XK_Shift_Lock || | |
++ s == XK_Meta_L || | |
++ s == XK_Meta_R || | |
++ s == XK_Alt_L || | |
++ s == XK_Alt_R || | |
++ s == XK_Super_L || | |
++ s == XK_Super_R || | |
++ s == XK_Hyper_L || | |
++ s == XK_Hyper_R); | |
++} | |
++ | |
++void | |
++keypress_other(const Arg *arg) | |
++{ | |
++ if (grabkeyboard()) | |
++ return; | |
++ | |
++ XEvent ev; | |
++ while (!XNextEvent(dpy, &ev)) { | |
++ if (ev.type == ButtonPress) | |
++ break; | |
++ if (ev.type == KeyPress) { | |
++ KeySym keysym = XKeycodeToKeysym(dpy, (KeyCode)ev.xkey.keycode, 0… | |
++ if (keysym_is_modifier(keysym)) | |
++ continue; | |
++ keypress_r(&ev, (Key*)arg->v); | |
++ break; | |
++ } | |
++ } | |
++ | |
++ XUngrabKeyboard(dpy, CurrentTime); | |
++ grabkeys(); | |
++} | |
++ | |
++void | |
++keypress(XEvent *e) | |
++{ | |
++ keypress_r(e,keys); | |
+ } | |
+ | |
+ void | |
+-- | |
+2.49.0 | |
+ |