aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--README.md1
-rw-r--r--config.h11
-rw-r--r--dwm.c218
4 files changed, 234 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
index 8b736b2..9369320 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,8 @@ compile_commands.json
# Zig
.cache
+
+# Patches
+*.orig
+*.rej
+*.diff
diff --git a/README.md b/README.md
index 590ba44..0a0f51f 100644
--- a/README.md
+++ b/README.md
@@ -99,3 +99,4 @@ sudo ninja install
- [cursor](https://dwm.suckless.org/patches/cursorwarp/): Warps the mouse cursor
to the center of the target window when changing focus
- [clientindicators](https://dwm.suckless.org/patches/clientindicators/): Dot indicator of open clients in non-vacent tags
+- [alt tab](https://dwm.suckless.org/patches/alt-tab/): <kbd>alt+tab</kbd> to cycle through clients
diff --git a/config.h b/config.h
index 2e6adc0..b6a6fbc 100644
--- a/config.h
+++ b/config.h
@@ -8,6 +8,14 @@
#define BROWSER "vivaldi"
#define WMNAME "sei"
+/* alt-tab configuration */
+static const unsigned int tabModKey = 0x40; /* if this key is hold the alt-tab functionality stays acitve. This key must be the same as key that is used to active functin altTabStart `*/
+static const unsigned int tabCycleKey = 0x17; /* if this key is hit the alt-tab program moves one position forward in clients stack. This key must be the same as key that is used to active functin altTabStart */
+static const unsigned int tabPosY = 1; /* tab position on Y axis, 0 = bottom, 1 = center, 2 = top */
+static const unsigned int tabPosX = 1; /* tab position on X axis, 0 = left, 1 = center, 2 = right */
+static const unsigned int maxWTab = 300; /* tab menu width */
+static const unsigned int maxHTab = 100; /* tab menu height */
+
/* appearance */
/* followclient:
* 1:
@@ -170,6 +178,7 @@ static const Key keys[] = {
/* { MODKEY|ShiftMask, XK_Escape, spawn, SHCMD("") }, */
{ MODKEY, XK_grave, spawn, {.v = (const char*[]){ "rofi", "-modi", "emoji", "-show", "emoji", NULL } } },
/* { MODKEY|ShiftMask, XK_grave, togglescratch, SHCMD("") }, */
+ { Mod1Mask, XK_Tab, altTabStart, {0} },
TAGKEYS( XK_1, 0)
TAGKEYS( XK_2, 1)
TAGKEYS( XK_3, 2)
@@ -188,7 +197,7 @@ static const Key keys[] = {
{ MODKEY, XK_BackSpace, spawn, {.v = (const char*[]){ "sysact", NULL } } },
{ MODKEY|ShiftMask, XK_BackSpace, spawn, {.v = (const char*[]){ "sysact", NULL } } },
- { MODKEY, XK_Tab, view, {0} },
+ /* { MODKEY, XK_Tab, view, {0} }, */
/* { MODKEY|ShiftMask, XK_Tab, spawn, SHCMD("") }, */
{ MODKEY, XK_q, killclient, {0} },
{ MODKEY|ShiftMask, XK_q, spawn, {.v = (const char*[]){ "sysact", NULL } } },
diff --git a/dwm.c b/dwm.c
index a4659aa..fb4b07d 100644
--- a/dwm.c
+++ b/dwm.c
@@ -48,6 +48,7 @@
#include <X11/Xlib-xcb.h>
#include <X11/extensions/shape.h>
#include <xcb/res.h>
+#include <time.h>
#include "drw.h"
#include "util.h"
@@ -181,6 +182,11 @@ struct Monitor {
int gappiv; /* vertical gap between windows */
int gappoh; /* horizontal outer gaps */
int gappov; /* vertical outer gaps */
+ int altTabN; /* move that many clients forward */
+ int nTabs; /* number of active clients in tag */
+ int isAlt; /* 1,0 */
+ int maxWTab;
+ int maxHTab;
unsigned int seltags;
unsigned int sellt;
unsigned int tagset[2];
@@ -189,8 +195,10 @@ struct Monitor {
Client *clients;
Client *sel;
Client *stack;
+ Client ** altsnext; /* array of all clients in the tag */
Monitor *next;
Window barwin;
+ Window tabwin;
const Layout *lt[2];
Pertag *pertag;
};
@@ -351,6 +359,9 @@ static void zoom(const Arg *arg);
static void xrdb(const Arg *arg);
static void load_xresources(void);
static void resource_load(XrmDatabase db, char *name, enum resource_type rtype, void *dst);
+void drawTab(int nwins, int first, Monitor *m);
+void altTabStart(const Arg *arg);
+static void altTabEnd();
static pid_t getparentprocess(pid_t p);
static int isdescprocess(pid_t p, pid_t c);
@@ -797,6 +808,7 @@ cleanup(void)
Monitor *m;
size_t i;
+ altTabEnd();
view(&a);
selmon->lt[selmon->sellt] = &foo;
for (m = mons; m; m = m->next)
@@ -1038,6 +1050,7 @@ createmon(void)
m->gappov = gappov;
m->lt[0] = &layouts[0];
m->lt[1] = &layouts[1 % LENGTH(layouts)];
+ m->nTabs = 0;
strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
m->pertag = ecalloc(1, sizeof(Pertag));
m->pertag->curtag = m->pertag->prevtag = 1;
@@ -2494,6 +2507,211 @@ setclienttagprop(Client *c)
}
void
+altTab()
+{
+ /* move to next window */
+ if (selmon->sel != NULL && selmon->sel->snext != NULL) {
+ selmon->altTabN++;
+ if (selmon->altTabN >= selmon->nTabs)
+ selmon->altTabN = 0; /* reset altTabN */
+
+ focus(selmon->altsnext[selmon->altTabN]);
+ restack(selmon);
+ }
+
+ /* redraw tab */
+ XRaiseWindow(dpy, selmon->tabwin);
+ drawTab(selmon->nTabs, 0, selmon);
+}
+
+void
+altTabEnd()
+{
+ if (selmon->isAlt == 0)
+ return;
+
+ /*
+ * move all clients between 1st 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 (selmon->nTabs > 1) {
+ if (selmon->altTabN != 0) { /* if user picked original client do nothing */
+ Client *buff = selmon->altsnext[selmon->altTabN];
+ if (selmon->altTabN > 1)
+ for (int i = selmon->altTabN;i > 0;i--)
+ selmon->altsnext[i] = selmon->altsnext[i - 1];
+ else /* swap them if there are just 2 clients */
+ selmon->altsnext[selmon->altTabN] = selmon->altsnext[0];
+ selmon->altsnext[0] = buff;
+ }
+
+ /* restack clients */
+ for (int i = selmon->nTabs - 1;i >= 0;i--) {
+ focus(selmon->altsnext[i]);
+ restack(selmon);
+ }
+
+ free(selmon->altsnext); /* free list of clients */
+ }
+
+ /* turn off/destroy the window */
+ selmon->isAlt = 0;
+ selmon->nTabs = 0;
+ XUnmapWindow(dpy, selmon->tabwin);
+ XDestroyWindow(dpy, selmon->tabwin);
+}
+
+void
+drawTab(int nwins, int first, Monitor *m)
+{
+ /* little documentation of functions */
+ /* void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); */
+ /* int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); */
+ /* void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); */
+
+ Client *c;
+ int h;
+
+ if (first) {
+ Monitor *m = selmon;
+ XSetWindowAttributes wa = {
+ .override_redirect = True,
+ .background_pixmap = ParentRelative,
+ .event_mask = ButtonPressMask|ExposureMask
+ };
+
+ selmon->maxWTab = maxWTab;
+ selmon->maxHTab = maxHTab;
+
+ /* decide position of tabwin */
+ int posX = selmon->mx;
+ int posY = selmon->my;
+ if (tabPosX == 0)
+ posX += 0;
+ if (tabPosX == 1)
+ posX += (selmon->mw / 2) - (maxWTab / 2);
+ if (tabPosX == 2)
+ posX += selmon->mw - maxWTab;
+
+ if (tabPosY == 0)
+ posY += selmon->mh - maxHTab;
+ if (tabPosY == 1)
+ posY += (selmon->mh / 2) - (maxHTab / 2);
+ if (tabPosY == 2)
+ posY += 0;
+
+ h = selmon->maxHTab;
+ /* XCreateWindow(display, parent, x, y, width, height, border_width, depth, class, visual, valuemask, attributes); just reference */
+ m->tabwin = XCreateWindow(dpy, root, posX, posY, selmon->maxWTab, selmon->maxHTab, 2, DefaultDepth(dpy, screen),
+ CopyFromParent, DefaultVisual(dpy, screen),
+ CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); /* create tabwin */
+
+ XDefineCursor(dpy, m->tabwin, cursor[CurNormal]->cursor);
+ XMapRaised(dpy, m->tabwin);
+
+ }
+
+ h = selmon->maxHTab / m->nTabs;
+
+ int y = 0;
+ int n = 0;
+ for (int i = 0;i < m->nTabs;i++) { /* draw all clients into tabwin */
+ c = m->altsnext[i];
+ if(!ISVISIBLE(c)) continue;
+ /* if (HIDDEN(c)) continue; uncomment if you're using awesomebar patch */
+
+ n++;
+ drw_setscheme(drw, scheme[(c == m->sel) ? SchemeSel : SchemeNorm]);
+ drw_text(drw, 0, y, selmon->maxWTab, h, (selmon->maxWTab - (TEXTW(c->name) - (TEXTW(" ") / 2))) / 2, c->name, 0);
+ y += h;
+ }
+
+ drw_setscheme(drw, scheme[SchemeNorm]);
+ drw_map(drw, m->tabwin, 0, 0, selmon->maxWTab, selmon->maxHTab);
+}
+
+void
+altTabStart(const Arg *arg)
+{
+ selmon->altsnext = NULL;
+ if (selmon->tabwin)
+ altTabEnd();
+
+ if (selmon->isAlt == 1) {
+ altTabEnd();
+ } else {
+ selmon->isAlt = 1;
+ selmon->altTabN = 0;
+
+ Client *c;
+ Monitor *m = selmon;
+
+ m->nTabs = 0;
+ for(c = m->clients; c; c = c->next) { /* count clients */
+ if(!ISVISIBLE(c)) continue;
+ /* if (HIDDEN(c)) continue; uncomment if you're using awesomebar patch */
+
+ ++m->nTabs;
+ }
+
+ if (m->nTabs > 0) {
+ m->altsnext = (Client **) malloc(m->nTabs * sizeof(Client *));
+
+ int listIndex = 0;
+ for(c = m->stack; c; c = c->snext) { /* add clients to the list */
+ if(!ISVISIBLE(c)) continue;
+ /* if (HIDDEN(c)) continue; uncomment if you're using awesomebar patch */
+
+ m->altsnext[listIndex++] = c;
+ }
+
+ drawTab(m->nTabs, 1, m);
+
+ struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 };
+
+ /* grab keyboard (take all input from keyboard) */
+ int grabbed = 1;
+ for (int i = 0;i < 1000;i++) {
+ if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync, GrabModeAsync, CurrentTime) == GrabSuccess)
+ break;
+ nanosleep(&ts, NULL);
+ if (i == 1000 - 1)
+ grabbed = 0;
+ }
+
+ XEvent event;
+ altTab();
+ if (grabbed == 0) {
+ altTabEnd();
+ } else {
+ while (grabbed) {
+ XNextEvent(dpy, &event);
+ if (event.type == KeyPress || event.type == KeyRelease) {
+ if (event.type == KeyRelease && event.xkey.keycode == tabModKey) { /* if super key is released break cycle */
+ break;
+ } else if (event.type == KeyPress) {
+ if (event.xkey.keycode == tabCycleKey) {/* if XK_s is pressed move to the next window */
+ altTab();
+ }
+ }
+ }
+ }
+
+ c = selmon->sel;
+ altTabEnd(); /* end the alt-tab functionality */
+ /* XUngrabKeyboard(display, time); just a reference */
+ XUngrabKeyboard(dpy, CurrentTime); /* stop taking all input from keyboard */
+ focus(c);
+ restack(selmon);
+ }
+ } else {
+ altTabEnd(); /* end the alt-tab functionality */
+ }
+ }
+}
+
+void
tag(const Arg *arg)
{
Client *c;