summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--README.md5
-rw-r--r--config.def.h14
-rw-r--r--config.mk2
-rw-r--r--dwm.c4
-rw-r--r--patch/alttab.c222
-rw-r--r--patch/alttab.h5
-rw-r--r--patch/include.c3
-rw-r--r--patch/include.h3
-rw-r--r--patches.def.h5
9 files changed, 262 insertions, 1 deletions
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 <time.h>
+
+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.