commit 4519b8b664937938217277f1b25e9d2f935cc8a5
parent f79134fe500f8a1cd192286fb1797e7c495d92bb
Author: lucas-mior <lucas.mior@tutamail.com>
Date: Mon, 6 Nov 2023 13:47:42 -0300
add alttab2 patch for dwm
It adds alt-tab like functionality to dwm, by cycling through
windows/clients in their recently used order. Tag `~0` is used to view
all clients while doing so. One can also click to focus a window.
Diffstat:
3 files changed, 419 insertions(+), 0 deletions(-)
diff --git a/dwm.suckless.org/patches/alttab2/dwm-alttab2+winview-6.4.diff b/dwm.suckless.org/patches/alttab2/dwm-alttab2+winview-6.4.diff
@@ -0,0 +1,217 @@
+diff --git a/config.def.h b/config.def.h
+index 9efa774..95499bd 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -2,6 +2,8 @@
+
+ /* appearance */
+ static const unsigned int borderpx = 1; /* border pixel of windows */
++static const unsigned int tabModKey = 0x40;
++static const unsigned int tabCycleKey = 0x17;
+ static const unsigned int snap = 32; /* snap pixel */
+ static const int showbar = 1; /* 0 means no bar */
+ static const int topbar = 1; /* 0 means bottom bar */
+@@ -95,6 +97,8 @@ static const Key keys[] = {
+ TAGKEYS( XK_8, 7)
+ TAGKEYS( XK_9, 8)
+ { MODKEY|ShiftMask, XK_q, quit, {0} },
++ { MODKEY, XK_o, winview, {0} },
++ { Mod1Mask, XK_Tab, alttab, {0} },
+ };
+
+ /* button definitions */
+diff --git a/dwm.1 b/dwm.1
+index ddc8321..f8a809e 100644
+--- a/dwm.1
++++ b/dwm.1
+@@ -110,6 +110,9 @@ Increase master area size.
+ .B Mod1\-h
+ Decrease master area size.
+ .TP
++.B Mod1\-o
++Select view of the window in focus. The list of tags to be displayed is matched to the window tag list.
++.TP
+ .B Mod1\-Return
+ Zooms/cycles focused window to/from master area (tiled layouts only).
+ .TP
+diff --git a/dwm.c b/dwm.c
+index f1d86b2..71d0ebc 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>
+@@ -142,6 +143,7 @@ typedef struct {
+ } Rule;
+
+ /* function declarations */
++static void alttab(const Arg *arg);
+ static void applyrules(Client *c);
+ static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact);
+ static void arrange(Monitor *m);
+@@ -168,6 +170,7 @@ static void expose(XEvent *e);
+ static void focus(Client *c);
+ static void focusin(XEvent *e);
+ static void focusmon(const Arg *arg);
++static void focusnext(const Arg *arg);
+ static void focusstack(const Arg *arg);
+ static Atom getatomprop(Client *c, Atom prop);
+ static int getrootptr(int *x, int *y);
+@@ -229,6 +232,7 @@ static void updatewmhints(Client *c);
+ static void view(const Arg *arg);
+ static Client *wintoclient(Window w);
+ static Monitor *wintomon(Window w);
++static void winview(const Arg* arg);
+ static int xerror(Display *dpy, XErrorEvent *ee);
+ static int xerrordummy(Display *dpy, XErrorEvent *ee);
+ static int xerrorstart(Display *dpy, XErrorEvent *ee);
+@@ -268,6 +272,8 @@ static Drw *drw;
+ static Monitor *mons, *selmon;
+ static Window root, wmcheckwin;
+
++static int alt_tab_direction = 0;
++
+ /* configuration, allows nested code to access above variables */
+ #include "config.h"
+
+@@ -275,6 +281,79 @@ static Window root, wmcheckwin;
+ struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
+
+ /* function implementations */
++
++static void
++alttab(const Arg *arg) {
++
++ view(&(Arg){ .ui = ~0 });
++ focusnext(&(Arg){ .i = alt_tab_direction });
++
++ int grabbed = 1;
++ int grabbed_keyboard = 1000;
++ for (int i = 0; i < 100; i += 1) {
++ struct timespec ts;
++ ts.tv_sec = 0;
++ ts.tv_nsec = 1000000;
++
++ if (grabbed_keyboard != GrabSuccess) {
++ grabbed_keyboard = XGrabKeyboard(dpy, DefaultRootWindow(dpy), True,
++ GrabModeAsync, GrabModeAsync, CurrentTime);
++ }
++ if (grabbed_keyboard == GrabSuccess) {
++ XGrabButton(dpy, AnyButton, AnyModifier, None, False,
++ BUTTONMASK, GrabModeAsync, GrabModeAsync,
++ None, None);
++ break;
++ }
++ nanosleep(&ts, NULL);
++ if (i == 100 - 1)
++ grabbed = 0;
++ }
++
++ XEvent event;
++ Client *c;
++ Monitor *m;
++ XButtonPressedEvent *ev;
++
++ while (grabbed) {
++ XNextEvent(dpy, &event);
++ switch (event.type) {
++ case KeyPress:
++ if (event.xkey.keycode == tabCycleKey)
++ focusnext(&(Arg){ .i = alt_tab_direction });
++ break;
++ case KeyRelease:
++ if (event.xkey.keycode == tabModKey) {
++ XUngrabKeyboard(dpy, CurrentTime);
++ XUngrabButton(dpy, AnyButton, AnyModifier, None);
++ grabbed = 0;
++ alt_tab_direction = !alt_tab_direction;
++ winview(0);
++ }
++ break;
++ case ButtonPress:
++ ev = &(event.xbutton);
++ if ((m = wintomon(ev->window)) && m != selmon) {
++ unfocus(selmon->sel, 1);
++ selmon = m;
++ focus(NULL);
++ }
++ if ((c = wintoclient(ev->window)))
++ focus(c);
++ XAllowEvents(dpy, AsyncBoth, CurrentTime);
++ break;
++ case ButtonRelease:
++ XUngrabKeyboard(dpy, CurrentTime);
++ XUngrabButton(dpy, AnyButton, AnyModifier, None);
++ grabbed = 0;
++ alt_tab_direction = !alt_tab_direction;
++ winview(0);
++ break;
++ }
++ }
++ return;
++}
++
+ void
+ applyrules(Client *c)
+ {
+@@ -835,6 +914,28 @@ focusmon(const Arg *arg)
+ focus(NULL);
+ }
+
++static void
++focusnext(const Arg *arg) {
++ Monitor *m;
++ Client *c;
++ m = selmon;
++ c = m->sel;
++
++ if (arg->i) {
++ if (c->next)
++ c = c->next;
++ else
++ c = m->clients;
++ } else {
++ Client *last = c;
++ if (last == m->clients)
++ last = NULL;
++ for (c = m->clients; c->next != last; c = c->next);
++ }
++ focus(c);
++ return;
++}
++
+ void
+ focusstack(const Arg *arg)
+ {
+@@ -2092,6 +2193,26 @@ wintomon(Window w)
+ return selmon;
+ }
+
++/* Selects for the view of the focused window. The list of tags */
++/* to be displayed is matched to the focused window tag list. */
++void
++winview(const Arg* arg){
++ Window win, win_r, win_p, *win_c;
++ unsigned nc;
++ int unused;
++ Client* c;
++ Arg a;
++
++ if (!XGetInputFocus(dpy, &win, &unused)) return;
++ while(XQueryTree(dpy, win, &win_r, &win_p, &win_c, &nc)
++ && win_p != win_r) win = win_p;
++
++ if (!(c = wintoclient(win))) return;
++
++ a.ui = c->tags;
++ view(&a);
++}
++
+ /* There's no way to check accesses to destroyed windows, thus those cases are
+ * ignored (especially on UnmapNotify's). Other types of errors call Xlibs
+ * default error handler, which may call exit. */
diff --git a/dwm.suckless.org/patches/alttab2/dwm-alttab2-6.4.diff b/dwm.suckless.org/patches/alttab2/dwm-alttab2-6.4.diff
@@ -0,0 +1,167 @@
+diff --git a/config.def.h b/config.def.h
+index 71ec68b..95499bd 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -2,6 +2,8 @@
+
+ /* appearance */
+ static const unsigned int borderpx = 1; /* border pixel of windows */
++static const unsigned int tabModKey = 0x40;
++static const unsigned int tabCycleKey = 0x17;
+ static const unsigned int snap = 32; /* snap pixel */
+ static const int showbar = 1; /* 0 means no bar */
+ static const int topbar = 1; /* 0 means bottom bar */
+@@ -96,6 +98,7 @@ static const Key keys[] = {
+ TAGKEYS( XK_9, 8)
+ { MODKEY|ShiftMask, XK_q, quit, {0} },
+ { MODKEY, XK_o, winview, {0} },
++ { Mod1Mask, XK_Tab, alttab, {0} },
+ };
+
+ /* button definitions */
+diff --git a/dwm.c b/dwm.c
+index ec076a8..71d0ebc 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>
+@@ -142,6 +143,7 @@ typedef struct {
+ } Rule;
+
+ /* function declarations */
++static void alttab(const Arg *arg);
+ static void applyrules(Client *c);
+ static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact);
+ static void arrange(Monitor *m);
+@@ -168,6 +170,7 @@ static void expose(XEvent *e);
+ static void focus(Client *c);
+ static void focusin(XEvent *e);
+ static void focusmon(const Arg *arg);
++static void focusnext(const Arg *arg);
+ static void focusstack(const Arg *arg);
+ static Atom getatomprop(Client *c, Atom prop);
+ static int getrootptr(int *x, int *y);
+@@ -269,6 +272,8 @@ static Drw *drw;
+ static Monitor *mons, *selmon;
+ static Window root, wmcheckwin;
+
++static int alt_tab_direction = 0;
++
+ /* configuration, allows nested code to access above variables */
+ #include "config.h"
+
+@@ -276,6 +281,79 @@ static Window root, wmcheckwin;
+ struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
+
+ /* function implementations */
++
++static void
++alttab(const Arg *arg) {
++
++ view(&(Arg){ .ui = ~0 });
++ focusnext(&(Arg){ .i = alt_tab_direction });
++
++ int grabbed = 1;
++ int grabbed_keyboard = 1000;
++ for (int i = 0; i < 100; i += 1) {
++ struct timespec ts;
++ ts.tv_sec = 0;
++ ts.tv_nsec = 1000000;
++
++ if (grabbed_keyboard != GrabSuccess) {
++ grabbed_keyboard = XGrabKeyboard(dpy, DefaultRootWindow(dpy), True,
++ GrabModeAsync, GrabModeAsync, CurrentTime);
++ }
++ if (grabbed_keyboard == GrabSuccess) {
++ XGrabButton(dpy, AnyButton, AnyModifier, None, False,
++ BUTTONMASK, GrabModeAsync, GrabModeAsync,
++ None, None);
++ break;
++ }
++ nanosleep(&ts, NULL);
++ if (i == 100 - 1)
++ grabbed = 0;
++ }
++
++ XEvent event;
++ Client *c;
++ Monitor *m;
++ XButtonPressedEvent *ev;
++
++ while (grabbed) {
++ XNextEvent(dpy, &event);
++ switch (event.type) {
++ case KeyPress:
++ if (event.xkey.keycode == tabCycleKey)
++ focusnext(&(Arg){ .i = alt_tab_direction });
++ break;
++ case KeyRelease:
++ if (event.xkey.keycode == tabModKey) {
++ XUngrabKeyboard(dpy, CurrentTime);
++ XUngrabButton(dpy, AnyButton, AnyModifier, None);
++ grabbed = 0;
++ alt_tab_direction = !alt_tab_direction;
++ winview(0);
++ }
++ break;
++ case ButtonPress:
++ ev = &(event.xbutton);
++ if ((m = wintomon(ev->window)) && m != selmon) {
++ unfocus(selmon->sel, 1);
++ selmon = m;
++ focus(NULL);
++ }
++ if ((c = wintoclient(ev->window)))
++ focus(c);
++ XAllowEvents(dpy, AsyncBoth, CurrentTime);
++ break;
++ case ButtonRelease:
++ XUngrabKeyboard(dpy, CurrentTime);
++ XUngrabButton(dpy, AnyButton, AnyModifier, None);
++ grabbed = 0;
++ alt_tab_direction = !alt_tab_direction;
++ winview(0);
++ break;
++ }
++ }
++ return;
++}
++
+ void
+ applyrules(Client *c)
+ {
+@@ -836,6 +914,28 @@ focusmon(const Arg *arg)
+ focus(NULL);
+ }
+
++static void
++focusnext(const Arg *arg) {
++ Monitor *m;
++ Client *c;
++ m = selmon;
++ c = m->sel;
++
++ if (arg->i) {
++ if (c->next)
++ c = c->next;
++ else
++ c = m->clients;
++ } else {
++ Client *last = c;
++ if (last == m->clients)
++ last = NULL;
++ for (c = m->clients; c->next != last; c = c->next);
++ }
++ focus(c);
++ return;
++}
++
+ void
+ focusstack(const Arg *arg)
+ {
diff --git a/dwm.suckless.org/patches/alttab2/index.md b/dwm.suckless.org/patches/alttab2/index.md
@@ -0,0 +1,35 @@
+Alt-tab2
+=======
+
+Description
+-----------
+This patch is based on [alt-tab](../alt-tab/).
+It adds alt-tab like functionality to dwm,
+by cycling through windows/clients in their recently used order.
+Tag `~0` is used to view all clients while doing so.
+One can also click to focus a window.
+In order to use this patch [winview](../winview/) is also necessary.
+
+Rationale
+---------
+For whatever reason, the original alt-tab patch made dwm crash for me.
+Besides, it was a huge patch, this version is simplified to use dwm's
+core features + winview patch instead of drawing an alt tab window.
+
+Configuration
+---------------------
+* `tabModKey` - If this key is hold the alt-tab functionality stays active. This key must be the same as key that is used to active function alttab
+* `tabCycleKey` - If this key is hit the alt-tab program moves one position forward in clients. This key must be the same as key that is used to active function alttab
+
+`tabModkey` and `tabCycleKey` are keycodes values.
+In case you use to other keys, checkout the instructions in
+[alt-tab](../alt-tab/index.md).
+
+Support
+-----
+This patch is kept alive on my [github](https://github.com/lucas-mior/dwm).
+
+Download
+--------
+* [dwm-alttab2-6.4.diff](dwm-alttab2-6.4.diff) (patch on top of winview)
+* [dwm-alttab2+winview-6.4.diff](dwm-alttab2+winview-6.4.diff) (winview + alttab2 as a single patch)