My configuration of sxmo fork of suckless dwm.

git clone git://watertao.xyz/programs/sxmo-dwm.git

commit c475067241bf82746853c687c42fd53f5dda74d7
parent 0cc2a3cccb8eeaed517260618e38059404af1237
Author: Miles Alan <m@milesalan.com>
Date:   Sat,  7 Mar 2020 10:30:15 -0600

Add multikey patch

Diffstat:
MMakefile | 2+-
Mconfig.def.h | 88++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Mdwm.c | 106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
3 files changed, 154 insertions(+), 42 deletions(-)

diff --git a/Makefile b/Makefile @@ -23,7 +23,7 @@ config.h: cp config.def.h $@ dwm: ${OBJ} - ${CC} -o $@ ${OBJ} ${LDFLAGS} + ${CC} -lrt -o $@ ${OBJ} ${LDFLAGS} clean: rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz diff --git a/config.def.h b/config.def.h @@ -34,23 +34,23 @@ static const Rule rules[] = { /* layout(s) */ static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ static const int nmaster = 1; /* number of clients in master area */ -static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ +static const int resizehints = 0; /* 1 means respect size hints in tiled resizals */ static const Layout layouts[] = { /* symbol arrange function */ { "[]=", tile }, /* first entry is default */ - { "><>", NULL }, /* no layout function means floating behavior */ - { "[M]", monocle }, + { "|_|", bstack }, + { "[ ]", monocle }, { NULL, NULL }, }; /* key definitions */ #define MODKEY Mod1Mask #define TAGKEYS(KEY,TAG) \ - { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ - { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ - { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ - { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, + {0, MODKEY, KEY, view, {.ui = 1 << TAG} }, \ + {0, MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \ + {0, MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \ + {0, MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} }, /* helper for spawning shell commands in the pre dwm-5.0 fashion */ #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } @@ -59,44 +59,54 @@ static const Layout layouts[] = { static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */ static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL }; static const char *termcmd[] = { "st", NULL }; +static const char *surfcmd[] = { "surf", NULL }; +#include <X11/XF86keysym.h> static Key keys[] = { - /* modifier key function argument */ - { MODKEY, XK_p, spawn, {.v = dmenucmd } }, - { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, - { MODKEY, XK_b, togglebar, {0} }, - { MODKEY, XK_j, focusstack, {.i = +1 } }, - { MODKEY, XK_k, focusstack, {.i = -1 } }, - { MODKEY, XK_i, incnmaster, {.i = +1 } }, - { MODKEY, XK_d, incnmaster, {.i = -1 } }, - { MODKEY, XK_h, setmfact, {.f = -0.05} }, - { MODKEY, XK_l, setmfact, {.f = +0.05} }, - { MODKEY, XK_Return, zoom, {0} }, - { MODKEY, XK_Tab, view, {0} }, - { MODKEY|ShiftMask, XK_c, killclient, {0} }, - { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, - { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, - { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, - { MODKEY|ControlMask, XK_comma, cyclelayout, {.i = -1 } }, - { MODKEY|ControlMask, XK_period, cyclelayout, {.i = +1 } }, - { MODKEY, XK_space, setlayout, {0} }, - { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, - { MODKEY, XK_0, view, {.ui = ~0 } }, - { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, - { MODKEY, XK_comma, focusmon, {.i = -1 } }, - { MODKEY, XK_period, focusmon, {.i = +1 } }, - { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, - { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, + + {1, 0, XF86XK_AudioRaiseVolume, spawn, SHCMD("dmo_appmenu.sh") }, + {2, 0, XF86XK_AudioRaiseVolume, spawn, SHCMD("dmo_appmenu.sh sys") }, + + {1, 0, XF86XK_AudioLowerVolume, cyclelayout , {.i = +1 } }, + {2, 0, XF86XK_AudioLowerVolume, zoom, {0} }, + {3, 0, XF86XK_AudioLowerVolume, killclient, {0} }, + + {1, 0, XF86XK_PowerOff, spawn, SHCMD("svkbd-layered-toggle") }, + {2, 0, XF86XK_PowerOff, spawn, { .v = termcmd } }, + {3, 0, XF86XK_PowerOff, spawn, { .v = surfcmd } } + + /* + {0, MODKEY, XK_p, spawn, {.v = dmenucmd } }, + {0, MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, + {0, MODKEY, XK_b, togglebar, {0} }, + {0, MODKEY, XK_j, focusstack, {.i = +1 } }, + {0, MODKEY, XK_k, focusstack, {.i = -1 } }, + {0, MODKEY, XK_i, incnmaster, {.i = +1 } }, + {0, MODKEY, XK_d, incnmaster, {.i = -1 } }, + {0, MODKEY, XK_h, setmfact, {.f = -0.05} }, + {0, MODKEY, XK_l, setmfact, {.f = +0.05} }, + {0, MODKEY, XK_Return, zoom, {0} }, + {0, MODKEY, XK_Tab, view, {0} }, + {0, MODKEY|ShiftMask, XK_c, killclient, {0} }, + {0, MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, + {0, MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, + {0, MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, + {0, MODKEY|ControlMask, XK_comma, cyclelayout, {.i = -1 } }, + {0, MODKEY|ControlMask, XK_period, cyclelayout, {.i = +1 } }, + {0, MODKEY, XK_space, setlayout, {0} }, + {0, MODKEY|ShiftMask, XK_space, togglefloating, {0} }, + {0, MODKEY, XK_0, view, {.ui = ~0 } }, + {0, MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, + {0, MODKEY, XK_comma, focusmon, {.i = -1 } }, + {0, MODKEY, XK_period, focusmon, {.i = +1 } }, + {0, MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, + {0, MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, TAGKEYS( XK_1, 0) TAGKEYS( XK_2, 1) TAGKEYS( XK_3, 2) TAGKEYS( XK_4, 3) - TAGKEYS( XK_5, 4) - TAGKEYS( XK_6, 5) - TAGKEYS( XK_7, 6) - TAGKEYS( XK_8, 7) - TAGKEYS( XK_9, 8) - { MODKEY|ShiftMask, XK_q, quit, {0} }, + {0, MODKEY|ShiftMask, XK_q, quit, {0} }, + */ }; /* button definitions */ diff --git a/dwm.c b/dwm.c @@ -20,6 +20,7 @@ * * To understand everything else, start reading main(). */ +#include <time.h> #include <errno.h> #include <locale.h> #include <signal.h> @@ -100,6 +101,7 @@ struct Client { }; typedef struct { + unsigned int npresses; unsigned int mod; KeySym keysym; void (*func)(const Arg *); @@ -177,6 +179,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 keyrelease(XEvent *e); static void killclient(const Arg *arg); static void manage(Window w, XWindowAttributes *wa); static void mappingnotify(XEvent *e); @@ -256,12 +259,14 @@ static void (*handler[LASTEvent]) (XEvent *) = { [Expose] = expose, [FocusIn] = focusin, [KeyPress] = keypress, + [KeyRelease] = keyrelease, [MappingNotify] = mappingnotify, [MapRequest] = maprequest, [MotionNotify] = motionnotify, [PropertyNotify] = propertynotify, [UnmapNotify] = unmapnotify }; +static Atom timeratom; static Atom wmatom[WMLast], netatom[NetLast]; static int running = 1; static Cur *cursor[CurLast]; @@ -271,6 +276,12 @@ static Drw *drw; static Monitor *mons, *selmon; static Window root, wmcheckwin; +#define KEYPRESS_MS_THRESHOLD 200 +#define KEYHOLD_MS_THRESHOLD 700 +static KeySym lastkeysym = NULL; +static unsigned int lastkeypresses = 0; +static unsigned int lastkeyreleased = 0; + /* configuration, allows nested code to access above variables */ #include "config.h" @@ -518,6 +529,7 @@ clientmessage(XEvent *e) XClientMessageEvent *cme = &e->xclient; Client *c = wintoclient(cme->window); + if (cme->message_type == timeratom) { keypresstimerdonesync(cme->data.s[0]); return; } if (!c) return; if (cme->message_type == netatom[NetWMState]) { @@ -1002,6 +1014,71 @@ isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) } #endif /* XINERAMA */ + +void keypresstimerdonesync(int keyindex) { + int i; + Key * maxkeypresskey = NULL; + + if ( + keyindex < 0 && + keys[-1 * keyindex].keysym == lastkeysym && + keys[-1 * keyindex].npresses == lastkeypresses + ) { + // Key hold - find keybinding w/ max npresses and run that fn + for (i = 0; i < LENGTH(keys); i++) { + if ( + keys[-1 * keyindex].keysym == keys[i].keysym && + (maxkeypresskey == NULL || keys[i].npresses > maxkeypresskey->npresses) + ) { + maxkeypresskey = &keys[i]; + } + } + lastkeysym = NULL; + if (maxkeypresskey && maxkeypresskey->func) maxkeypresskey->func(&(maxkeypresskey->arg)); + } else if ( + // Key press + keys[keyindex].keysym == lastkeysym && + keys[keyindex].npresses == lastkeypresses + ) { + if (lastkeyreleased) { + lastkeysym = NULL; + if (keys[keyindex].func) keys[keyindex].func(&(keys[keyindex].arg)); + } else { + keypresstimerdispatch(KEYHOLD_MS_THRESHOLD - KEYPRESS_MS_THRESHOLD, -1 * keyindex); + } + } +} + +void keypresstimerdone(union sigval timer_data) +{ + XEvent ev; + memset(&ev, 0, sizeof ev); + ev.xclient.type = ClientMessage; + ev.xclient.window = root; + ev.xclient.message_type = timeratom; + ev.xclient.format = 16; + ev.xclient.data.s[0] = ((short) timer_data.sival_int); + XSendEvent(dpy, root, False, SubstructureRedirectMask, &ev); + XSync(dpy, False); +} + +void keypresstimerdispatch(int msduration, int data) +{ + struct sigevent timer_signal_event; + timer_t timer; + struct itimerspec timer_period; + timer_signal_event.sigev_notify = SIGEV_THREAD; + timer_signal_event.sigev_notify_function = keypresstimerdone; + timer_signal_event.sigev_value.sival_int = data; + timer_signal_event.sigev_notify_attributes = NULL; + timer_create(CLOCK_MONOTONIC, &timer_signal_event, &timer); + timer_period.it_value.tv_sec = 0; + timer_period.it_value.tv_nsec = msduration * 1000000; + timer_period.it_interval.tv_sec = 0; + timer_period.it_interval.tv_nsec = 0; + timer_settime(timer, 0, &timer_period, NULL); +} + void keypress(XEvent *e) { @@ -1014,8 +1091,31 @@ keypress(XEvent *e) for (i = 0; i < LENGTH(keys); i++) if (keysym == keys[i].keysym && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) - && keys[i].func) - keys[i].func(&(keys[i].arg)); + && keys[i].func) { + // E.g. normal functionality case - npresses 0 + if (keys[i].npresses == 0) { keys[i].func(&(keys[i].arg)); continue; } + + if (lastkeysym != keys[i].keysym) { + lastkeysym = keys[i].keysym; + lastkeypresses = 0; + } + if (lastkeypresses + 1 == keys[i].npresses) { + lastkeypresses++; + lastkeyreleased = 0; + keypresstimerdispatch(KEYPRESS_MS_THRESHOLD, i); + break; + } + } +} + +void +keyrelease(XEvent *e) +{ + KeySym keysym; + XKeyEvent *ev; + ev = &e->xkey; + keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); + if (lastkeysym == keysym) { lastkeyreleased = 1; } } void @@ -1569,6 +1669,7 @@ setup(void) updategeom(); /* init atoms */ utf8string = XInternAtom(dpy, "UTF8_STRING", False); + timeratom = XInternAtom(dpy, "TIMER", False); wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); @@ -2147,6 +2248,7 @@ zoom(const Arg *arg) int main(int argc, char *argv[]) { + XInitThreads(); if (argc == 2 && !strcmp("-v", argv[1])) die("dwm-"VERSION); else if (argc != 1)