[dwm][patch][keysequence] Add new patch - sites - public wiki contents of suckless.org
 (HTM) git clone git://git.suckless.org/sites
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
 (DIR) commit d166954782572f6ca3c36136c31b9e9e6e5efb2e
 (DIR) parent 648d2edf20bb3fb153660f48a27a03adfd121dea
 (HTM) Author: TUVIMEN <hexderm@gmail.com>
       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(-)
       ---
 (DIR) diff --git a/dwm.suckless.org/patches/keysequence/index.md b/dwm.suckless.org/patches/keysequence/index.md
       @@ -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[0]}},
       +        { ShiftMask,        XK_t,        setlayout,            {.v = &layouts[1]}},
       +        { MODKEY,                XK_y,        setlayout,            {.v = &layouts[2]}},
       +        {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 <hexderm@gmail.com>
 (DIR) diff --git a/dwm.suckless.org/patches/keysequence/keysequence-20250606-0d6af14.diff b/dwm.suckless.org/patches/keysequence/keysequence-20250606-0d6af14.diff
       @@ -0,0 +1,158 @@
       +From 0d6af14efa1250631b081ad9d1f3aa0263221fd8 Mon Sep 17 00:00:00 2001
       +From: TUVIMEN <hexderm@gmail.com>
       +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, XineramaScreenInfo *info)
       + }
       + #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 ungrab */
       ++        for (int i = 0; i < 1000; i++) {
       ++                if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync,
       ++                    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
       +