dwm-multikey-6.2.diff - sites - public wiki contents of suckless.org
 (HTM) git clone git://git.suckless.org/sites
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
       dwm-multikey-6.2.diff (13835B)
       ---
            1 From b845451cde90c3b46f7863c27a184555b444e9af Mon Sep 17 00:00:00 2001
            2 From: Miles Alan <m@milesalan.com>
            3 Date: Sat, 18 Apr 2020 19:25:29 -0500
            4 Subject: [PATCH] Multikey: Run different actions for single keybinding based
            5  on # of keypresses
            6 
            7 Changed keypress code to allow keybindings to be selectively dispatched when
            8 tapped a specific # of times as specified by the new npresses field on the
            9 Key struct.
           10 
           11 In the example added to the config.def.h, the tiling layout is set when
           12 Mod+w is tapped once, float layout is set when Mod+w is tapped twice,
           13 and monocole layout is set when Mod+w is tapped three times (or held down).
           14 ---
           15  config.def.h |  84 ++++++++++++++++++++++------------------
           16  config.mk    |   2 +-
           17  dwm.c        | 107 +++++++++++++++++++++++++++++++++++++++++++++++++--
           18  3 files changed, 150 insertions(+), 43 deletions(-)
           19 
           20 diff --git a/config.def.h b/config.def.h
           21 index 1c0b587..dc945b4 100644
           22 --- a/config.def.h
           23 +++ b/config.def.h
           24 @@ -46,10 +46,10 @@ static const Layout layouts[] = {
           25  /* key definitions */
           26  #define MODKEY Mod1Mask
           27  #define TAGKEYS(KEY,TAG) \
           28 -        { MODKEY,                       KEY,      view,           {.ui = 1 << TAG} }, \
           29 -        { MODKEY|ControlMask,           KEY,      toggleview,     {.ui = 1 << TAG} }, \
           30 -        { MODKEY|ShiftMask,             KEY,      tag,            {.ui = 1 << TAG} }, \
           31 -        { MODKEY|ControlMask|ShiftMask, KEY,      toggletag,      {.ui = 1 << TAG} },
           32 +        { 0, MODKEY,                       KEY,      view,           {.ui = 1 << TAG} }, \
           33 +        { 0, MODKEY|ControlMask,           KEY,      toggleview,     {.ui = 1 << TAG} }, \
           34 +        { 0, MODKEY|ShiftMask,             KEY,      tag,            {.ui = 1 << TAG} }, \
           35 +        { 0, MODKEY|ControlMask|ShiftMask, KEY,      toggletag,      {.ui = 1 << TAG} },
           36  
           37  /* helper for spawning shell commands in the pre dwm-5.0 fashion */
           38  #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
           39 @@ -59,41 +59,49 @@ static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn()
           40  static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL };
           41  static const char *termcmd[]  = { "st", NULL };
           42  
           43 +#define MULTIKEY_THRESHOLD_MS_PRESS 200
           44 +#define MULTIKEY_THRESHOLD_MS_HOLD 700
           45 +
           46  static Key keys[] = {
           47 -        /* modifier                     key        function        argument */
           48 -        { MODKEY,                       XK_p,      spawn,          {.v = dmenucmd } },
           49 -        { MODKEY|ShiftMask,             XK_Return, spawn,          {.v = termcmd } },
           50 -        { MODKEY,                       XK_b,      togglebar,      {0} },
           51 -        { MODKEY,                       XK_j,      focusstack,     {.i = +1 } },
           52 -        { MODKEY,                       XK_k,      focusstack,     {.i = -1 } },
           53 -        { MODKEY,                       XK_i,      incnmaster,     {.i = +1 } },
           54 -        { MODKEY,                       XK_d,      incnmaster,     {.i = -1 } },
           55 -        { MODKEY,                       XK_h,      setmfact,       {.f = -0.05} },
           56 -        { MODKEY,                       XK_l,      setmfact,       {.f = +0.05} },
           57 -        { MODKEY,                       XK_Return, zoom,           {0} },
           58 -        { MODKEY,                       XK_Tab,    view,           {0} },
           59 -        { MODKEY|ShiftMask,             XK_c,      killclient,     {0} },
           60 -        { MODKEY,                       XK_t,      setlayout,      {.v = &layouts[0]} },
           61 -        { MODKEY,                       XK_f,      setlayout,      {.v = &layouts[1]} },
           62 -        { MODKEY,                       XK_m,      setlayout,      {.v = &layouts[2]} },
           63 -        { MODKEY,                       XK_space,  setlayout,      {0} },
           64 -        { MODKEY|ShiftMask,             XK_space,  togglefloating, {0} },
           65 -        { MODKEY,                       XK_0,      view,           {.ui = ~0 } },
           66 -        { MODKEY|ShiftMask,             XK_0,      tag,            {.ui = ~0 } },
           67 -        { MODKEY,                       XK_comma,  focusmon,       {.i = -1 } },
           68 -        { MODKEY,                       XK_period, focusmon,       {.i = +1 } },
           69 -        { MODKEY|ShiftMask,             XK_comma,  tagmon,         {.i = -1 } },
           70 -        { MODKEY|ShiftMask,             XK_period, tagmon,         {.i = +1 } },
           71 -        TAGKEYS(                        XK_1,                      0)
           72 -        TAGKEYS(                        XK_2,                      1)
           73 -        TAGKEYS(                        XK_3,                      2)
           74 -        TAGKEYS(                        XK_4,                      3)
           75 -        TAGKEYS(                        XK_5,                      4)
           76 -        TAGKEYS(                        XK_6,                      5)
           77 -        TAGKEYS(                        XK_7,                      6)
           78 -        TAGKEYS(                        XK_8,                      7)
           79 -        TAGKEYS(                        XK_9,                      8)
           80 -        { MODKEY|ShiftMask,             XK_q,      quit,           {0} },
           81 +        /* npresses,   modifier                     key        function        argument */
           82 +        { 0,           MODKEY,                       XK_p,      spawn,          {.v = dmenucmd } },
           83 +        { 0,           MODKEY|ShiftMask,             XK_Return, spawn,          {.v = termcmd } },
           84 +        { 0,           MODKEY,                       XK_b,      togglebar,      {0} },
           85 +        { 0,           MODKEY,                       XK_j,      focusstack,     {.i = +1 } },
           86 +        { 0,           MODKEY,                       XK_k,      focusstack,     {.i = -1 } },
           87 +        { 0,           MODKEY,                       XK_i,      incnmaster,     {.i = +1 } },
           88 +        { 0,           MODKEY,                       XK_d,      incnmaster,     {.i = -1 } },
           89 +        { 0,           MODKEY,                       XK_h,      setmfact,       {.f = -0.05} },
           90 +        { 0,           MODKEY,                       XK_l,      setmfact,       {.f = +0.05} },
           91 +        { 0,           MODKEY,                       XK_Return, zoom,           {0} },
           92 +        { 0,           MODKEY,                       XK_Tab,    view,           {0} },
           93 +        { 0,           MODKEY|ShiftMask,             XK_c,      killclient,     {0} },
           94 +        { 0,           MODKEY,                       XK_t,      setlayout,      {.v = &layouts[0]} },
           95 +        { 0,           MODKEY,                       XK_f,      setlayout,      {.v = &layouts[1]} },
           96 +        { 0,           MODKEY,                       XK_m,      setlayout,      {.v = &layouts[2]} },
           97 +        { 0,           MODKEY,                       XK_space,  setlayout,      {0} },
           98 +        { 0,           MODKEY|ShiftMask,             XK_space,  togglefloating, {0} },
           99 +        { 0,           MODKEY,                       XK_0,      view,           {.ui = ~0 } },
          100 +        { 0,           MODKEY|ShiftMask,             XK_0,      tag,            {.ui = ~0 } },
          101 +        { 0,           MODKEY,                       XK_comma,  focusmon,       {.i = -1 } },
          102 +        { 0,           MODKEY,                       XK_period, focusmon,       {.i = +1 } },
          103 +        { 0,           MODKEY|ShiftMask,             XK_comma,  tagmon,         {.i = -1 } },
          104 +        { 0,           MODKEY|ShiftMask,             XK_period, tagmon,         {.i = +1 } },
          105 +
          106 +        { 1,           MODKEY,                       XK_w,      setlayout,      {.v = &layouts[0]} },
          107 +        { 2,           MODKEY,                       XK_w,      setlayout,      {.v = &layouts[1]} },
          108 +        { 3,           MODKEY,                       XK_w,      setlayout,      {.v = &layouts[2]} },
          109 +
          110 +        TAGKEYS(                                     XK_1,                      0)
          111 +        TAGKEYS(                                     XK_2,                      1)
          112 +        TAGKEYS(                                     XK_3,                      2)
          113 +        TAGKEYS(                                     XK_4,                      3)
          114 +        TAGKEYS(                                     XK_5,                      4)
          115 +        TAGKEYS(                                     XK_6,                      5)
          116 +        TAGKEYS(                                     XK_7,                      6)
          117 +        TAGKEYS(                                     XK_8,                      7)
          118 +        TAGKEYS(                                     XK_9,                      8)
          119 +        { 0,           MODKEY|ShiftMask,             XK_q,      quit,           {0} },
          120  };
          121  
          122  /* button definitions */
          123 diff --git a/config.mk b/config.mk
          124 index 6d36cb7..fe0a2ec 100644
          125 --- a/config.mk
          126 +++ b/config.mk
          127 @@ -22,7 +22,7 @@ FREETYPEINC = /usr/include/freetype2
          128  
          129  # includes and libs
          130  INCS = -I${X11INC} -I${FREETYPEINC}
          131 -LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS}
          132 +LIBS = -L${X11LIB} -lrt -lX11 ${XINERAMALIBS} ${FREETYPELIBS}
          133  
          134  # flags
          135  CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=2 -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
          136 diff --git a/dwm.c b/dwm.c
          137 index 4465af1..975956c 100644
          138 --- a/dwm.c
          139 +++ b/dwm.c
          140 @@ -27,6 +27,7 @@
          141  #include <stdio.h>
          142  #include <stdlib.h>
          143  #include <string.h>
          144 +#include <time.h>
          145  #include <unistd.h>
          146  #include <sys/types.h>
          147  #include <sys/wait.h>
          148 @@ -40,6 +41,7 @@
          149  #include <X11/extensions/Xinerama.h>
          150  #endif /* XINERAMA */
          151  #include <X11/Xft/Xft.h>
          152 +#include <X11/XKBlib.h>
          153  
          154  #include "drw.h"
          155  #include "util.h"
          156 @@ -100,6 +102,7 @@ struct Client {
          157  };
          158  
          159  typedef struct {
          160 +        unsigned int npresses;
          161          unsigned int mod;
          162          KeySym keysym;
          163          void (*func)(const Arg *);
          164 @@ -176,6 +179,10 @@ static void grabbuttons(Client *c, int focused);
          165  static void grabkeys(void);
          166  static void incnmaster(const Arg *arg);
          167  static void keypress(XEvent *e);
          168 +static void keypresstimerdispatch(int msduration, int data);
          169 +static void keypresstimerdone(union sigval timer_data);
          170 +static void keypresstimerdonesync(int idx);
          171 +static void keyrelease(XEvent *e);
          172  static void killclient(const Arg *arg);
          173  static void manage(Window w, XWindowAttributes *wa);
          174  static void mappingnotify(XEvent *e);
          175 @@ -253,13 +260,14 @@ static void (*handler[LASTEvent]) (XEvent *) = {
          176          [Expose] = expose,
          177          [FocusIn] = focusin,
          178          [KeyPress] = keypress,
          179 +        [KeyRelease] = keyrelease,
          180          [MappingNotify] = mappingnotify,
          181          [MapRequest] = maprequest,
          182          [MotionNotify] = motionnotify,
          183          [PropertyNotify] = propertynotify,
          184          [UnmapNotify] = unmapnotify
          185  };
          186 -static Atom wmatom[WMLast], netatom[NetLast];
          187 +static Atom timeratom, wmatom[WMLast], netatom[NetLast];
          188  static int running = 1;
          189  static Cur *cursor[CurLast];
          190  static Clr **scheme;
          191 @@ -268,6 +276,10 @@ static Drw *drw;
          192  static Monitor *mons, *selmon;
          193  static Window root, wmcheckwin;
          194  
          195 +static int multikeypendingindex = -1;
          196 +static timer_t multikeypendingtimer = NULL;
          197 +static int multikeyup = 1;
          198 +
          199  /* configuration, allows nested code to access above variables */
          200  #include "config.h"
          201  
          202 @@ -515,6 +527,10 @@ clientmessage(XEvent *e)
          203          XClientMessageEvent *cme = &e->xclient;
          204          Client *c = wintoclient(cme->window);
          205  
          206 +        if (cme->message_type == timeratom) {
          207 +                keypresstimerdonesync(cme->data.s[0]);
          208 +                return;
          209 +        }
          210          if (!c)
          211                  return;
          212          if (cme->message_type == netatom[NetWMState]) {
          213 @@ -991,11 +1007,92 @@ keypress(XEvent *e)
          214  
          215          ev = &e->xkey;
          216          keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
          217 -        for (i = 0; i < LENGTH(keys); i++)
          218 +        for (i = 0; i < LENGTH(keys); i++) {
          219                  if (keysym == keys[i].keysym
          220                  && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)
          221 -                && keys[i].func)
          222 -                        keys[i].func(&(keys[i].arg));
          223 +                && keys[i].func) {
          224 +                        // E.g. Normal functionality case - npresses 0 == keydown immediate fn
          225 +                        if (keys[i].npresses == 0) {
          226 +                          keys[i].func(&(keys[i].arg));
          227 +                          break;
          228 +                  }
          229 +
          230 +                        // Multikey functionality - find index of key, set global, & dispatch
          231 +                        if (
          232 +                          (multikeypendingindex == -1 && multikeyup && keys[i].npresses == 1) ||
          233 +                          (multikeypendingindex != -1 && keys[multikeypendingindex].npresses + 1 == keys[i].npresses)
          234 +                        ) {
          235 +                                multikeyup = 0;
          236 +                                multikeypendingindex = i;
          237 +                                keypresstimerdispatch(MULTIKEY_THRESHOLD_MS_PRESS, i);
          238 +                                break;
          239 +                        }
          240 +                }
          241 +        }
          242 +}
          243 +
          244 +void
          245 +keypresstimerdispatch(int msduration, int data)
          246 +{
          247 +        struct sigevent timer_signal_event;
          248 +        struct itimerspec timer_period;
          249 +        static int multikeypendingtimer_created = 0;
          250 +        // Clear out the old timer if any set,and dispatch new timer
          251 +        if (multikeypendingtimer_created) {
          252 +                timer_delete(multikeypendingtimer);
          253 +        }
          254 +        timer_signal_event.sigev_notify = SIGEV_THREAD;
          255 +        timer_signal_event.sigev_notify_function = keypresstimerdone;
          256 +        timer_signal_event.sigev_value.sival_int = data;
          257 +        timer_signal_event.sigev_notify_attributes = NULL;
          258 +        timer_create(CLOCK_MONOTONIC, &timer_signal_event, &multikeypendingtimer);
          259 +        multikeypendingtimer_created = 1;
          260 +        timer_period.it_value.tv_sec = 0;
          261 +        timer_period.it_value.tv_nsec = msduration * 1000000;
          262 +        timer_period.it_interval.tv_sec = 0;
          263 +        timer_period.it_interval.tv_nsec =  0;
          264 +        timer_settime(multikeypendingtimer, 0, &timer_period, NULL);
          265 +}
          266 +
          267 +void
          268 +keypresstimerdone(union sigval timer_data)
          269 +{
          270 +        XEvent ev;
          271 +        memset(&ev, 0, sizeof ev);
          272 +        ev.xclient.type = ClientMessage;
          273 +        ev.xclient.window = root;
          274 +        ev.xclient.message_type = timeratom;
          275 +        ev.xclient.format = 16;
          276 +        ev.xclient.data.s[0] = ((short) timer_data.sival_int);
          277 +        XSendEvent(dpy, root, False, SubstructureRedirectMask, &ev);
          278 +        XSync(dpy, False);
          279 +}
          280 +
          281 +void
          282 +keypresstimerdonesync(int idx)
          283 +{
          284 +        int i, maxidx;
          285 +        if (keys[idx].npresses == 1 && !multikeyup) {
          286 +                // Dispatch hold key
          287 +                maxidx = -1;
          288 +                for (i = 0; i < LENGTH(keys); i++)
          289 +                        if (keys[i].keysym == keys[idx].keysym) maxidx = i;
          290 +                if (maxidx != -1)
          291 +                        keypresstimerdispatch(
          292 +                                MULTIKEY_THRESHOLD_MS_HOLD - MULTIKEY_THRESHOLD_MS_PRESS,
          293 +                                maxidx
          294 +                        );
          295 +        } else if (keys[idx].func) {
          296 +                // Run the actual keys' fn
          297 +                keys[idx].func(&(keys[idx].arg));
          298 +                multikeypendingindex = -1;
          299 +        }
          300 +}
          301 +
          302 +void
          303 +keyrelease(XEvent *e)
          304 +{
          305 +        multikeyup = 1;
          306  }
          307  
          308  void
          309 @@ -2127,6 +2224,7 @@ zoom(const Arg *arg)
          310  int
          311  main(int argc, char *argv[])
          312  {
          313 +        XInitThreads();
          314          if (argc == 2 && !strcmp("-v", argv[1]))
          315                  die("dwm-"VERSION);
          316          else if (argc != 1)
          317 @@ -2135,6 +2233,7 @@ main(int argc, char *argv[])
          318                  fputs("warning: no locale support\n", stderr);
          319          if (!(dpy = XOpenDisplay(NULL)))
          320                  die("dwm: cannot open display");
          321 +        XkbSetDetectableAutoRepeat(dpy, True, NULL);
          322          checkotherwm();
          323          setup();
          324  #ifdef __OpenBSD__
          325 -- 
          326 2.30.1
          327