From cc58ad2ef41bf0b9a298366fbdc5afbde31edb17 Mon Sep 17 00:00:00 2001 From: bakkeby Date: Sat, 8 Oct 2022 22:12:03 +0200 Subject: [PATCH] Adding alt-tab patch ref. #303 --- README.md | 5 ++ config.def.h | 14 +++ config.mk | 2 +- dwm.c | 4 + patch/alttab.c | 222 ++++++++++++++++++++++++++++++++++++++++++++++++ patch/alttab.h | 5 ++ patch/include.c | 3 + patch/include.h | 3 + patches.def.h | 5 ++ 9 files changed, 262 insertions(+), 1 deletion(-) create mode 100644 patch/alttab.c create mode 100644 patch/alttab.h diff --git a/README.md b/README.md index 981dd6d..53faf27 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,8 @@ Browsing patches? There is a [map of patches](https://coggle.it/diagram/X9IiSSM6 ### Changelog: +2022-10-08 - Added the alt-tab patch + 2022-08-12 - Added the nametag patch 2022-08-02 - Added the bidi patch @@ -230,6 +232,9 @@ Browsing patches? There is a [map of patches](https://coggle.it/diagram/X9IiSSM6 - [alpha](https://dwm.suckless.org/patches/alpha/) - adds transparency for the status bar + - [alt-tab](https://dwm.suckless.org/patches/alt-tab/) + - adds a window task switcher toggled using alt-tab + - [alternativetags](https://dwm.suckless.org/patches/alternativetags/) - adds alternative tags which can be toggled on the fly for the sole purpose of providing visual aid diff --git a/config.def.h b/config.def.h index 1b06f0d..5d10181 100644 --- a/config.def.h +++ b/config.def.h @@ -122,6 +122,16 @@ static const int ulineall = 0; /* 1 to show underline on all ta #define NAMETAG_COMMAND "dmenu < /dev/null" #endif // NAMETAG_PATCH +#if ALT_TAB_PATCH +/* alt-tab configuration */ +static const unsigned int tabmodkey = 0x40; /* (Alt) when this key is held down the alt-tab functionality stays active. Must be the same modifier as used to run alttabstart */ +static const unsigned int tabcyclekey = 0x17; /* (Tab) when this key is hit the menu moves one position forward in client stack. Must be the same key as used to run alttabstart */ +static const unsigned int tabposy = 1; /* tab position on Y axis, 0 = top, 1 = center, 2 = bottom */ +static const unsigned int tabposx = 1; /* tab position on X axis, 0 = left, 1 = center, 2 = right */ +static const unsigned int maxwtab = 600; /* tab menu width */ +static const unsigned int maxhtab = 200; /* tab menu height */ +#endif // ALT_TAB_PATCH + /* Indicators: see patch/bar_indicators.h for options */ static int tagindicatortype = INDICATOR_TOP_LEFT_SQUARE; static int tiledindicatortype = INDICATOR_NONE; @@ -996,7 +1006,11 @@ static const Key keys[] = { { MODKEY|Mod4Mask, XK_0, togglegaps, {0} }, { MODKEY|Mod4Mask|ShiftMask, XK_0, defaultgaps, {0} }, #endif // VANITYGAPS_PATCH + #if ALT_TAB_PATCH + { Mod1Mask, XK_Tab, alttabstart, {0} }, + #else { MODKEY, XK_Tab, view, {0} }, + #endif // ALT_TAB_PATCH #if SHIFTTAG_PATCH { MODKEY|ShiftMask, XK_Left, shifttag, { .i = -1 } }, // note keybinding conflict with focusadjacenttag tagtoleft { MODKEY|ShiftMask, XK_Right, shifttag, { .i = +1 } }, // note keybinding conflict with focusadjacenttag tagtoright diff --git a/config.mk b/config.mk index b4a6ee5..7db6cba 100644 --- a/config.mk +++ b/config.mk @@ -29,7 +29,7 @@ FREETYPEINC = /usr/include/freetype2 #KVMLIB = -lkvm # Uncomment this for the alpha patch and the winicon patch (BAR_ALPHA_PATCH, BAR_WINICON_PATCH) -#XRENDER = -lXrender +XRENDER = -lXrender # Uncomment this for the mdpcontrol patch / MDPCONTROL_PATCH #MPDCLIENT = -lmpdclient diff --git a/dwm.c b/dwm.c index 6907d94..5652136 100644 --- a/dwm.c +++ b/dwm.c @@ -1245,6 +1245,10 @@ cleanup(void) Layout foo = { "", NULL }; size_t i; + #if ALT_TAB_PATCH + alttabend(); + #endif // ALT_TAB_PATCH + #if SEAMLESS_RESTART_PATCH for (m = mons; m; m = m->next) persistmonitorstate(m); diff --git a/patch/alttab.c b/patch/alttab.c new file mode 100644 index 0000000..861ab2f --- /dev/null +++ b/patch/alttab.c @@ -0,0 +1,222 @@ +int alttabn; /* move that many clients forward */ +int ntabs; /* number of active clients in tag */ +int isalt; +Client **altsnext; /* array of all clients in the tag */ +Window alttabwin; + +void +alttab() +{ + Monitor *m = selmon; + + /* move to next window */ + if (m->sel && m->sel->snext) { + alttabn++; + if (alttabn >= ntabs) + alttabn = 0; /* reset alttabn */ + + focus(altsnext[alttabn]); + restack(m); + } + + /* redraw tab */ + XRaiseWindow(dpy, alttabwin); + drawtab(ntabs, 0, m); +} + +void +alttabend() +{ + Monitor *m = selmon; + Client *buff; + int i; + + if (!isalt) + return; + + /* Move all clients between first and choosen position, + * one down in stack and put choosen client to the first position + * so they remain in right order for the next time that alt-tab is used + */ + if (ntabs > 1) { + if (alttabn != 0) { /* if user picked original client do nothing */ + buff = altsnext[alttabn]; + if (alttabn > 1) + for (i = alttabn; i > 0; i--) + altsnext[i] = altsnext[i - 1]; + else /* swap them if there are just 2 clients */ + altsnext[alttabn] = altsnext[0]; + altsnext[0] = buff; + } + + /* restack clients */ + for (i = ntabs - 1; i >= 0; i--) { + focus(altsnext[i]); + restack(m); + } + + free(altsnext); /* free list of clients */ + } + + /* destroy the window */ + isalt = 0; + ntabs = 0; + XUnmapWindow(dpy, alttabwin); + XDestroyWindow(dpy, alttabwin); +} + +void +drawtab(int nwins, int first, Monitor *m) +{ + Client *c; + int i, h; + int y = 0; + int px = m->mx; + int py = m->my; + + if (first) { + XSetWindowAttributes wa = { + .override_redirect = True, + #if BAR_ALPHA_PATCH + .background_pixel = 0, + .border_pixel = 0, + .colormap = cmap, + #else + .background_pixmap = ParentRelative, + #endif // BAR_ALPHA_PATCH + .event_mask = ButtonPressMask|ExposureMask + }; + + /* decide position of tabwin */ + if (tabposx == 1) + px = m->mx + (m->mw / 2) - (maxwtab / 2); + else if (tabposx == 2) + px = m->mx + m->mw - maxwtab; + + if (tabposy == 1) + py = m->my + (m->mh / 2) - (maxhtab / 2); + else if (tabposy == 2) + py = m->my + m->mh - maxhtab; + + h = maxhtab; + + #if BAR_ALPHA_PATCH + alttabwin = XCreateWindow(dpy, root, px, py, maxwtab, maxhtab, 2, depth, + InputOutput, visual, + CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &wa); + #else + alttabwin = XCreateWindow(dpy, root, px, py, maxwtab, maxhtab, 2, DefaultDepth(dpy, screen), + CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); + #endif // BAR_ALPHA_PATCH + + XDefineCursor(dpy, alttabwin, cursor[CurNormal]->cursor); + XMapRaised(dpy, alttabwin); + } + + h = maxhtab / ntabs; + for (i = 0; i < ntabs; i++) { /* draw all clients into tabwin */ + c = altsnext[i]; + if (!ISVISIBLE(c)) + continue; + if (HIDDEN(c)) + continue; + + drw_setscheme(drw, scheme[c == m->sel ? SchemeSel : SchemeNorm]); + drw_text(drw, 0, y, maxwtab, h, 0, c->name, 0, 0); + y += h; + } + + drw_setscheme(drw, scheme[SchemeNorm]); + drw_map(drw, alttabwin, 0, 0, maxwtab, maxhtab); +} + +void +alttabstart(const Arg *arg) +{ + Client *c; + Monitor *m = selmon; + int grabbed; + int i; + + altsnext = NULL; + if (alttabwin) + alttabend(); + + if (isalt == 1) { + alttabend(); + return; + } + + isalt = 1; + alttabn = 0; + ntabs = 0; + + for (c = m->clients; c; c = c->next) { + if (!ISVISIBLE(c)) + continue; + if (HIDDEN(c)) + continue; + + ++ntabs; + } + + if (!ntabs) { + alttabend(); + return; + } + + altsnext = (Client **) malloc(ntabs * sizeof(Client *)); + + for (i = 0, c = m->stack; c; c = c->snext, i++) { + if (!ISVISIBLE(c)) + continue; + if (HIDDEN(c)) + continue; + + altsnext[i] = c; + } + + drawtab(ntabs, 1, m); + + struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 }; + + /* grab keyboard (take all input from keyboard) */ + grabbed = 1; + for (i = 0; i < 1000; i++) { + if (XGrabKeyboard(dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime) == GrabSuccess) + break; + nanosleep(&ts, NULL); + if (i == 1000 - 1) + grabbed = 0; + } + + XEvent event; + alttab(); + + if (grabbed == 0) { + alttabend(); + return; + } + + while (grabbed) { + XNextEvent(dpy, &event); + if (event.type == KeyPress || event.type == KeyRelease) { + if (event.type == KeyRelease && event.xkey.keycode == tabmodkey) /* if mod key is released break cycle */ + break; + + if (event.type == KeyPress) { + if (event.xkey.keycode == tabcyclekey) { /* if tab is pressed move to the next window */ + alttab(); + } + } + } + } + + c = m->sel; + alttabend(); + + XUngrabKeyboard(dpy, CurrentTime); + focus(c); + restack(m); +} diff --git a/patch/alttab.h b/patch/alttab.h new file mode 100644 index 0000000..2951d04 --- /dev/null +++ b/patch/alttab.h @@ -0,0 +1,5 @@ +#include + +static void drawtab(int nwins, int first, Monitor *m); +static void alttabstart(const Arg *arg); +static void alttabend(); diff --git a/patch/include.c b/patch/include.c index 4263f45..326323c 100644 --- a/patch/include.c +++ b/patch/include.c @@ -98,6 +98,9 @@ #endif /* Other patches */ +#if ALT_TAB_PATCH +#include "alttab.c" +#endif #if ASPECTRESIZE_PATCH #include "aspectresize.c" #endif diff --git a/patch/include.h b/patch/include.h index 65e0eda..9ab7527 100644 --- a/patch/include.h +++ b/patch/include.h @@ -98,6 +98,9 @@ #endif /* Other patches */ +#if ALT_TAB_PATCH +#include "alttab.h" +#endif #if ASPECTRESIZE_PATCH #include "aspectresize.h" #endif diff --git a/patches.def.h b/patches.def.h index deb6e3c..e787ada 100644 --- a/patches.def.h +++ b/patches.def.h @@ -438,6 +438,11 @@ * Other patches */ +/* Adds a window task switcher toggled using alt-tab. + * https://dwm.suckless.org/patches/alt-tab/ + */ +#define ALT_TAB_PATCH 0 + /* All floating windows are centered, like the center patch, but without a rule. * The center patch takes precedence over this patch. * This patch interferes with the center transient windows patches.