summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLM-LCL <hello@exaltedelite.club>2024-09-17 20:57:46 +0200
committerLM-LCL <hello@exaltedelite.club>2024-09-17 20:57:46 +0200
commit2bede55d24ab1d36b7a92a3b50fb1618774dfd36 (patch)
treea5302246e9e1cd659907cca60b940080a4a4b89e
parentd49baa8ba39e2f33874f66b5041e775a2d909715 (diff)
changed to classic alttab patches
-rw-r--r--config.def.h13
-rw-r--r--config.h29
-rw-r--r--dwm.c329
-rw-r--r--patches/dwm-alttab-6.4.diff322
-rw-r--r--patches/dwm-alttabclass-6.4.diff304
5 files changed, 986 insertions, 11 deletions
diff --git a/config.def.h b/config.def.h
index ec547a7..bb24efa 100644
--- a/config.def.h
+++ b/config.def.h
@@ -1,5 +1,14 @@
/* See LICENSE file for copyright and license details. */
+/* 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 tabCycleKey2 = 0x31; /* grave key */
+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 = 600; /* tab menu width */
+static const unsigned int maxHTab = 200; /* tab menu height */
+
/* appearance */
static const unsigned int borderpx = 1; /* border pixel of windows */
static const unsigned int snap = 32; /* snap pixel */
@@ -82,7 +91,7 @@ static const Key keys[] = {
{ MODKEY|ShiftMask, XK_j, movestack, {.i = +1 } },
{ MODKEY|ShiftMask, XK_k, movestack, {.i = -1 } },
{ MODKEY, XK_Return, zoom, {0} },
- { MODKEY, XK_Tab, view, {0} },
+ { MODKEY, XK_q, view, {0} },
{ MODKEY|ShiftMask, XK_c, killclient, {0} },
{ MODKEY, XK_t, setlayout, {.v = &layouts[0]} },
{ MODKEY, XK_f, setlayout, {.v = &layouts[1]} },
@@ -95,6 +104,8 @@ static const Key keys[] = {
{ MODKEY, XK_period, focusmon, {.i = +1 } },
{ MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } },
{ MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } },
+ { Mod1Mask, XK_Tab, altTabStart, {.i = 1} },
+ { Mod1Mask, XK_grave, altTabStart, {.i = 0} },
TAGKEYS( XK_1, 0)
TAGKEYS( XK_2, 1)
TAGKEYS( XK_3, 2)
diff --git a/config.h b/config.h
index 4f22ccb..ea73e68 100644
--- a/config.h
+++ b/config.h
@@ -1,15 +1,23 @@
/* See LICENSE file for copyright and license details. */
+/* 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 tabCycleKey2 = 0x31; /* grave key */
+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 = 600; /* tab menu width */
+static const unsigned int maxHTab = 200; /* tab menu height */
/* appearance */
-static const unsigned int borderpx = 1; /* border pixel of windows */
-static const unsigned int snap = 32; /* snap pixel */
-static const unsigned int systraypinning = 0; /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */
-static const unsigned int systrayonleft = 1; /* 0: systray in the right corner, >0: systray on left of status text */
-static const unsigned int systrayspacing = 2; /* systray spacing */
-static const int systraypinningfailfirst = 1; /* 1: if pinning fails, display systray on the first monitor, False: display systray on the last monitor*/
-static const int showsystray = 1; /* 0 means no systray */
-static const int showbar = 1; /* 0 means no bar */
-static const int topbar = 1; /* 0 means bottom bar */
+static const unsigned int borderpx = 1; /* border pixel of windows */
+static const unsigned int snap = 32; /* snap pixel */
+static const unsigned int systraypinning = 0; /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */
+static const unsigned int systrayonleft = 1; /* 0: systray in the right corner, >0: systray on left of status text */
+static const unsigned int systrayspacing = 2; /* systray spacing */
+static const int systraypinningfailfirst = 1; /* 1: if pinning fails, display systray on the first monitor, False: display systray on the last monitor*/
+static const int showsystray = 1; /* 0 means no systray */
+static const int showbar = 1; /* 0 means no bar */
+static const int topbar = 1; /* 0 means bottom bar */
static const int focusonwheel = 0;
static const char *fonts[] = { "monospace:size=12" };
static const char dmenufont[] = "monospace:size=12";
@@ -82,7 +90,6 @@ static const Key keys[] = {
{ MODKEY|ShiftMask, XK_j, movestack, {.i = +1} },
{ MODKEY|ShiftMask, XK_k, movestack, {.i = -1} },
{ MODKEY, XK_Return, zoom, {0} },
- { MODKEY, XK_Tab, view, {0} },
{ MODKEY|ShiftMask, XK_c, killclient, {0} },
{ MODKEY, XK_n, setlayout, {.v = &layouts[0]} },
{ MODKEY, XK_f, setlayout, {.v = &layouts[1]} },
@@ -95,6 +102,8 @@ static const Key keys[] = {
{ MODKEY, XK_period, focusmon, {.i = +1} },
{ MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1} },
{ MODKEY|ShiftMask, XK_period, tagmon, {.i = +1} },
+ { MODKEY, XK_Tab, altTabStart, {.i = 1} },
+ { MODKEY, XK_grave, altTabStart, {.i = 0} },
TAGKEYS( XK_1, 0 )
TAGKEYS( XK_2, 1 )
TAGKEYS( XK_3, 2 )
diff --git a/dwm.c b/dwm.c
index 0994f9f..9629322 100644
--- a/dwm.c
+++ b/dwm.c
@@ -40,6 +40,7 @@
#include <X11/extensions/Xinerama.h>
#endif /* XINERAMA */
#include <X11/Xft/Xft.h>
+#include <time.h>
#include "drw.h"
#include "util.h"
@@ -101,6 +102,7 @@ typedef struct Monitor Monitor;
typedef struct Client Client;
struct Client {
char name[256];
+ char class[256];
float mina, maxa;
int x, y, w, h;
int oldx, oldy, oldw, oldh;
@@ -135,6 +137,13 @@ struct Monitor {
int by; /* bar geometry */
int mx, my, mw, mh; /* screen size */
int wx, wy, ww, wh; /* window area */
+ int altTabN; /* move that many clients forward */
+ int altTabNc; /* move that many clients forward when using tab for same class */
+ int nTabs; /* number of active clients in tag */
+ int ncTabs; /* number of active clients under same class in tag */
+ int isAlt; /* 1,0 */
+ int maxWTab;
+ int maxHTab;
unsigned int seltags;
unsigned int sellt;
unsigned int tagset[2];
@@ -143,8 +152,11 @@ struct Monitor {
Client *clients;
Client *sel;
Client *stack;
+ Client ** altsnext; /* array of all clients in the tag */
+ Client ** altsnextclass; /* array of all clients under same class in the tag */
Monitor *next;
Window barwin;
+ Window tabwin;
const Layout *lt[2];
Pertag *pertag;
};
@@ -264,6 +276,10 @@ static int xerror(Display *dpy, XErrorEvent *ee);
static int xerrordummy(Display *dpy, XErrorEvent *ee);
static int xerrorstart(Display *dpy, XErrorEvent *ee);
static void zoom(const Arg *arg);
+void drawTab(int nwins, int first, Monitor *m);
+void altTabStart(const Arg *arg);
+static void altTabEnd();
+static void getclassname(Client *c);
/* variables */
static Systray *systray = NULL;
@@ -532,6 +548,7 @@ cleanup(void)
Monitor *m;
size_t i;
+ altTabEnd();
view(&a);
selmon->lt[selmon->sellt] = &foo;
for (m = mons; m; m = m->next)
@@ -756,6 +773,8 @@ createmon(void)
m->topbar = topbar;
m->lt[0] = &layouts[0];
m->lt[1] = &layouts[1 % LENGTH(layouts)];
+ m->nTabs = 0;
+ m->ncTabs = 0;
strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
m->pertag = ecalloc(1, sizeof(Pertag));
m->pertag->curtag = m->pertag->prevtag = 1;
@@ -1184,6 +1203,7 @@ manage(Window w, XWindowAttributes *wa)
c->oldbw = wa->border_width;
updatetitle(c);
+ getclassname(c);
if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) {
c->mon = t->mon;
c->tags = t->tags;
@@ -1869,6 +1889,306 @@ spawn(const Arg *arg)
}
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
+altTabClass()
+{
+ /* move to next window */
+ if (selmon->sel != NULL) {
+ selmon->altTabNc++;
+ if (selmon->altTabNc >= selmon->ncTabs)
+ selmon->altTabNc = 0; /* reset altTabNc */
+
+ focus(selmon->altsnextclass[selmon->altTabNc]);
+ }
+
+ /* redraw tab */
+ XRaiseWindow(dpy, selmon->tabwin);
+ drawTab(selmon->ncTabs, 0, selmon);
+}
+
+void
+altTabShift()
+{
+ /* move to prev window */
+ if (selmon->sel != NULL) {
+ selmon->altTabN--;
+ if (selmon->altTabN < 0)
+ selmon->altTabN = selmon->nTabs - 1; /* reset altTabN */
+
+ if (selmon->altsnext[selmon->altTabN]) {
+ focus(selmon->altsnext[selmon->altTabN]);
+ }
+ }
+
+ /* redraw tab */
+ XRaiseWindow(dpy, selmon->tabwin);
+ drawTab(selmon->nTabs, 0, selmon);
+}
+
+void
+altTabShiftClass()
+{
+ /* move to prev window */
+ if (selmon->sel != NULL) {
+ selmon->altTabNc--;
+ if (selmon->altTabNc < 0)
+ selmon->altTabNc = selmon->ncTabs - 1; /* reset altTabNc */
+
+ if (selmon->altsnextclass[selmon->altTabNc]) {
+ focus(selmon->altsnextclass[selmon->altTabNc]);
+ }
+ }
+
+ /* redraw tab */
+ XRaiseWindow(dpy, selmon->tabwin);
+ drawTab(selmon->ncTabs, 0, selmon);
+}
+
+void
+altTabEnd()
+{
+ Client *buff = NULL;
+ 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)
+ buff = selmon->altsnext[selmon->altTabN];
+ else if (selmon->altTabNc != 0) {
+ buff = selmon->altsnextclass[selmon->altTabNc];
+ for (; selmon->altTabN < selmon->nTabs; selmon->altTabN++)
+ if (selmon->altsnext[selmon->altTabN] == selmon->altsnextclass[selmon->altTabNc])
+ break;
+ }
+ if (buff) { /* if user picked original client do nothing */
+ 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 */
+ free(selmon->altsnextclass); /* 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 / nwins;
+
+ int y = 0;
+ for (int i = 0; i < nwins; i++) { /* draw all clients into tabwin */
+ if (nwins == m->nTabs)
+ c = m->altsnext[i];
+ else
+ c = m->altsnextclass[i];
+ if(!ISVISIBLE(c)) continue;
+ /* if (HIDDEN(c)) continue; uncomment if you're using awesomebar patch */
+
+ drw_setscheme(drw, scheme[(c == m->sel) ? SchemeSel : SchemeNorm]);
+ drw_text(drw, 0, y, selmon->maxWTab, h, 0, 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)
+{
+ if (selmon->tabwin)
+ altTabEnd();
+
+ if (selmon->isAlt == 1) {
+ altTabEnd();
+ } else {
+ selmon->isAlt = 1;
+ selmon->altTabN = 0;
+ selmon->altTabNc = 0;
+
+ Client *c;
+ Monitor *m = selmon;
+
+ char tempclass[256] = {'\0'};
+ if (selmon->sel)
+ strncpy(tempclass, selmon->sel->class, 256);
+
+ m->nTabs = 0;
+ m->ncTabs = 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 (!strcmp(c->class, tempclass))
+ ++m->ncTabs;
+ }
+
+ if (m->nTabs > 0) {
+ m->altsnext = (Client **) malloc(m->nTabs * sizeof(Client *));
+ m->altsnextclass = (Client **) malloc(m->ncTabs * sizeof(Client *));
+
+ int listIndex = 0;
+ int listIndexc = 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;
+
+ if (!strcmp(c->class, tempclass))
+ m->altsnextclass[listIndexc++] = c;
+ }
+
+ if (arg->i)
+ drawTab(m->nTabs, 1, m);
+ else
+ drawTab(m->ncTabs, 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;
+ if (arg->i)
+ altTab();
+ else
+ altTabClass();
+ 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 || event.xkey.keycode == tabCycleKey2 ) { /* if XK_s is pressed move to the next window */
+ if (arg->i) {
+ if (CLEANMASK((Mod1Mask|ShiftMask)) == CLEANMASK(event.xkey.state))
+ altTabShift();
+ else
+ altTab();
+ } else {
+ if (CLEANMASK((Mod1Mask|ShiftMask)) == CLEANMASK(event.xkey.state))
+ altTabShiftClass();
+ else
+ altTabClass();
+ }
+
+ }
+ }
+ }
+ }
+
+ 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)
{
if (selmon->sel && arg->ui & TAGMASK) {
@@ -2391,6 +2711,15 @@ updatetitle(Client *c)
}
void
+getclassname(Client *c)
+{
+ gettextprop(c->win, XA_WM_CLASS, c->class, sizeof c->class);
+
+ if (c->class[0] == '\0') /* hack to mark broken clients */
+ strcpy(c->name, broken);
+}
+
+void
updatewindowtype(Client *c)
{
Atom state = getatomprop(c, netatom[NetWMState]);
diff --git a/patches/dwm-alttab-6.4.diff b/patches/dwm-alttab-6.4.diff
new file mode 100644
index 0000000..9bb834e
--- /dev/null
+++ b/patches/dwm-alttab-6.4.diff
@@ -0,0 +1,322 @@
+From 8b1d33db9cbc03bf12df398bb14e62389efc70a3 Mon Sep 17 00:00:00 2001
+From: ViliamKovac1223 <viliamkovac1223@gmail.com>
+Date: Sun, 9 Oct 2022 16:29:28 -0400
+Subject: [PATCH] add alt-tab functionality
+
+Co-authored-by: Daniel Gerblick <daniel.gerblick@gmail.com>
+---
+ config.def.h | 11 ++-
+ dwm.c | 218 +++++++++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 228 insertions(+), 1 deletion(-)
+
+diff --git a/config.def.h b/config.def.h
+index 061ad66..7a9e3b1 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -1,5 +1,13 @@
+ /* See LICENSE file for copyright and license details. */
+
++/* 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 = 600; /* tab menu width */
++static const unsigned int maxHTab = 200; /* tab menu height */
++
+ /* appearance */
+ static const unsigned int borderpx = 1; /* border pixel of windows */
+ static const unsigned int snap = 32; /* snap pixel */
+@@ -71,7 +79,7 @@ static const Key keys[] = {
+ { MODKEY, XK_h, setmfact, {.f = -0.05} },
+ { MODKEY, XK_l, setmfact, {.f = +0.05} },
+ { MODKEY, XK_Return, zoom, {0} },
+- { MODKEY, XK_Tab, view, {0} },
++ { MODKEY, XK_q, view, {0} },
+ { MODKEY|ShiftMask, XK_c, killclient, {0} },
+ { MODKEY, XK_t, setlayout, {.v = &layouts[0]} },
+ { MODKEY, XK_f, setlayout, {.v = &layouts[1]} },
+@@ -84,6 +92,7 @@ static const Key keys[] = {
+ { MODKEY, XK_period, focusmon, {.i = +1 } },
+ { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } },
+ { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } },
++ { Mod1Mask, XK_Tab, altTabStart, {0} },
+ TAGKEYS( XK_1, 0)
+ TAGKEYS( XK_2, 1)
+ TAGKEYS( XK_3, 2)
+diff --git a/dwm.c b/dwm.c
+index e5efb6a..f606311 100644
+--- a/dwm.c
++++ b/dwm.c
+@@ -40,6 +40,7 @@
+ #include <X11/extensions/Xinerama.h>
+ #endif /* XINERAMA */
+ #include <X11/Xft/Xft.h>
++#include <time.h>
+
+ #include "drw.h"
+ #include "util.h"
+@@ -119,6 +120,11 @@ struct Monitor {
+ int by; /* bar geometry */
+ int mx, my, mw, mh; /* screen size */
+ int wx, wy, ww, wh; /* window area */
++ 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];
+@@ -127,8 +133,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];
+ };
+
+@@ -234,6 +242,9 @@ static int xerror(Display *dpy, XErrorEvent *ee);
+ static int xerrordummy(Display *dpy, XErrorEvent *ee);
+ static int xerrorstart(Display *dpy, XErrorEvent *ee);
+ static void zoom(const Arg *arg);
++void drawTab(int nwins, int first, Monitor *m);
++void altTabStart(const Arg *arg);
++static void altTabEnd();
+
+ /* variables */
+ static const char broken[] = "broken";
+@@ -477,6 +488,7 @@ cleanup(void)
+ Monitor *m;
+ size_t i;
+
++ altTabEnd();
+ view(&a);
+ selmon->lt[selmon->sellt] = &foo;
+ for (m = mons; m; m = m->next)
+@@ -644,6 +656,7 @@ createmon(void)
+ m->topbar = topbar;
+ m->lt[0] = &layouts[0];
+ m->lt[1] = &layouts[1 % LENGTH(layouts)];
++ m->nTabs = 0;
+ strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
+ return m;
+ }
+@@ -1648,6 +1661,211 @@ spawn(const Arg *arg)
+ }
+ }
+
++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, 0, 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)
+ {
+--
+2.37.3
+
diff --git a/patches/dwm-alttabclass-6.4.diff b/patches/dwm-alttabclass-6.4.diff
new file mode 100644
index 0000000..0236de4
--- /dev/null
+++ b/patches/dwm-alttabclass-6.4.diff
@@ -0,0 +1,304 @@
+diff -up a/config.def.h b/config.def.h
+--- a/config.def.h 2023-08-17 17:35:28.333393605 +0400
++++ b/config.def.h 2023-08-17 17:24:46.724435876 +0400
+@@ -3,6 +3,7 @@
+ /* 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 tabCycleKey2 = 0x31; /* grave key */
+ 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 = 600; /* tab menu width */
+@@ -93,7 +94,8 @@ static const Key keys[] = {
+ { MODKEY, XK_period, focusmon, {.i = +1 } },
+ { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } },
+ { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } },
+- { Mod1Mask, XK_Tab, altTabStart, {0} },
++ { Mod1Mask, XK_Tab, altTabStart, {.i = 1} },
++ { Mod1Mask, XK_grave, altTabStart, {.i = 0} },
+ TAGKEYS( XK_1, 0)
+ TAGKEYS( XK_2, 1)
+ TAGKEYS( XK_3, 2)
+diff -up a/dwm.c b/dwm.c
+--- a/dwm.c 2023-08-17 17:24:19.753640383 +0400
++++ b/dwm.c 2023-08-18 09:41:00.834187121 +0400
+@@ -87,6 +87,7 @@ typedef struct Monitor Monitor;
+ typedef struct Client Client;
+ struct Client {
+ char name[256];
++ char class[256];
+ float mina, maxa;
+ int x, y, w, h;
+ int oldx, oldy, oldw, oldh;
+@@ -121,7 +122,9 @@ struct Monitor {
+ int mx, my, mw, mh; /* screen size */
+ int wx, wy, ww, wh; /* window area */
+ int altTabN; /* move that many clients forward */
++ int altTabNc; /* move that many clients forward when using tab for same class */
+ int nTabs; /* number of active clients in tag */
++ int ncTabs; /* number of active clients under same class in tag */
+ int isAlt; /* 1,0 */
+ int maxWTab;
+ int maxHTab;
+@@ -134,6 +137,7 @@ struct Monitor {
+ Client *sel;
+ Client *stack;
+ Client ** altsnext; /* array of all clients in the tag */
++ Client ** altsnextclass; /* array of all clients under same class in the tag */
+ Monitor *next;
+ Window barwin;
+ Window tabwin;
+@@ -245,6 +249,7 @@ static void zoom(const Arg *arg);
+ void drawTab(int nwins, int first, Monitor *m);
+ void altTabStart(const Arg *arg);
+ static void altTabEnd();
++static void getclassname(Client *c);
+
+ /* variables */
+ static const char broken[] = "broken";
+@@ -657,6 +662,7 @@ createmon(void)
+ m->lt[0] = &layouts[0];
+ m->lt[1] = &layouts[1 % LENGTH(layouts)];
+ m->nTabs = 0;
++ m->ncTabs = 0;
+ strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
+ return m;
+ }
+@@ -1059,6 +1065,7 @@ manage(Window w, XWindowAttributes *wa)
+ c->oldbw = wa->border_width;
+
+ updatetitle(c);
++ getclassname(c);
+ if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) {
+ c->mon = t->mon;
+ c->tags = t->tags;
+@@ -1683,7 +1690,7 @@ altTab()
+ selmon->altTabN = 0; /* reset altTabN */
+
+ focus(selmon->altsnext[selmon->altTabN]);
+- restack(selmon);
++ /* restack(selmon); */
+ }
+
+ /* redraw tab */
+@@ -1692,8 +1699,64 @@ altTab()
+ }
+
+ void
++altTabClass()
++{
++ /* move to next window */
++ if (selmon->sel != NULL) {
++ selmon->altTabNc++;
++ if (selmon->altTabNc >= selmon->ncTabs)
++ selmon->altTabNc = 0; /* reset altTabNc */
++
++ focus(selmon->altsnextclass[selmon->altTabNc]);
++ }
++
++ /* redraw tab */
++ XRaiseWindow(dpy, selmon->tabwin);
++ drawTab(selmon->ncTabs, 0, selmon);
++}
++
++void
++altTabShift()
++{
++ /* move to prev window */
++ if (selmon->sel != NULL) {
++ selmon->altTabN--;
++ if (selmon->altTabN < 0)
++ selmon->altTabN = selmon->nTabs - 1; /* reset altTabN */
++
++ if (selmon->altsnext[selmon->altTabN]) {
++ focus(selmon->altsnext[selmon->altTabN]);
++ }
++ }
++
++ /* redraw tab */
++ XRaiseWindow(dpy, selmon->tabwin);
++ drawTab(selmon->nTabs, 0, selmon);
++}
++
++void
++altTabShiftClass()
++{
++ /* move to prev window */
++ if (selmon->sel != NULL) {
++ selmon->altTabNc--;
++ if (selmon->altTabNc < 0)
++ selmon->altTabNc = selmon->ncTabs - 1; /* reset altTabNc */
++
++ if (selmon->altsnextclass[selmon->altTabNc]) {
++ focus(selmon->altsnextclass[selmon->altTabNc]);
++ }
++ }
++
++ /* redraw tab */
++ XRaiseWindow(dpy, selmon->tabwin);
++ drawTab(selmon->ncTabs, 0, selmon);
++}
++
++void
+ altTabEnd()
+ {
++ Client *buff = NULL;
+ if (selmon->isAlt == 0)
+ return;
+
+@@ -1703,8 +1766,15 @@ altTabEnd()
+ * 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 != 0)
++ buff = selmon->altsnext[selmon->altTabN];
++ else if (selmon->altTabNc != 0) {
++ buff = selmon->altsnextclass[selmon->altTabNc];
++ for (; selmon->altTabN < selmon->nTabs; selmon->altTabN++)
++ if (selmon->altsnext[selmon->altTabN] == selmon->altsnextclass[selmon->altTabNc])
++ break;
++ }
++ if (buff) { /* if user picked original client do nothing */
+ if (selmon->altTabN > 1)
+ for (int i = selmon->altTabN;i > 0;i--)
+ selmon->altsnext[i] = selmon->altsnext[i - 1];
+@@ -1720,6 +1790,7 @@ altTabEnd()
+ }
+
+ free(selmon->altsnext); /* free list of clients */
++ free(selmon->altsnextclass); /* free list of clients */
+ }
+
+ /* turn off/destroy the window */
+@@ -1779,16 +1850,17 @@ drawTab(int nwins, int first, Monitor *m
+
+ }
+
+- h = selmon->maxHTab / m->nTabs;
++ h = selmon->maxHTab / nwins;
+
+ int y = 0;
+- int n = 0;
+- for (int i = 0;i < m->nTabs;i++) { /* draw all clients into tabwin */
+- c = m->altsnext[i];
++ for (int i = 0; i < nwins; i++) { /* draw all clients into tabwin */
++ if (nwins == m->nTabs)
++ c = m->altsnext[i];
++ else
++ c = m->altsnextclass[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, 0, c->name, 0);
+ y += h;
+@@ -1801,7 +1873,6 @@ drawTab(int nwins, int first, Monitor *m
+ void
+ altTabStart(const Arg *arg)
+ {
+- selmon->altsnext = NULL;
+ if (selmon->tabwin)
+ altTabEnd();
+
+@@ -1810,30 +1881,47 @@ altTabStart(const Arg *arg)
+ } else {
+ selmon->isAlt = 1;
+ selmon->altTabN = 0;
++ selmon->altTabNc = 0;
+
+ Client *c;
+ Monitor *m = selmon;
+
++ char tempclass[256] = {'\0'};
++ if (selmon->sel)
++ strncpy(tempclass, selmon->sel->class, 256);
++
+ m->nTabs = 0;
++ m->ncTabs = 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 (!strcmp(c->class, tempclass))
++ ++m->ncTabs;
+ }
+
+ if (m->nTabs > 0) {
+ m->altsnext = (Client **) malloc(m->nTabs * sizeof(Client *));
++ m->altsnextclass = (Client **) malloc(m->ncTabs * sizeof(Client *));
+
+ int listIndex = 0;
++ int listIndexc = 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;
++
++ if (!strcmp(c->class, tempclass))
++ m->altsnextclass[listIndexc++] = c;
+ }
+
+- drawTab(m->nTabs, 1, m);
++ if (arg->i)
++ drawTab(m->nTabs, 1, m);
++ else
++ drawTab(m->ncTabs, 1, m);
+
+ struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 };
+
+@@ -1848,7 +1936,10 @@ altTabStart(const Arg *arg)
+ }
+
+ XEvent event;
+- altTab();
++ if (arg->i)
++ altTab();
++ else
++ altTabClass();
+ if (grabbed == 0) {
+ altTabEnd();
+ } else {
+@@ -1858,8 +1949,19 @@ altTabStart(const Arg *arg)
+ 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();
++ if (event.xkey.keycode == tabCycleKey || event.xkey.keycode == tabCycleKey2 ) { /* if XK_s is pressed move to the next window */
++ if (arg->i) {
++ if (CLEANMASK((Mod1Mask|ShiftMask)) == CLEANMASK(event.xkey.state))
++ altTabShift();
++ else
++ altTab();
++ } else {
++ if (CLEANMASK((Mod1Mask|ShiftMask)) == CLEANMASK(event.xkey.state))
++ altTabShiftClass();
++ else
++ altTabClass();
++ }
++
+ }
+ }
+ }
+@@ -2231,6 +2333,15 @@ updatetitle(Client *c)
+ strcpy(c->name, broken);
+ }
+
++void
++getclassname(Client *c)
++{
++ gettextprop(c->win, XA_WM_CLASS, c->class, sizeof c->class);
++
++ if (c->class[0] == '\0') /* hack to mark broken clients */
++ strcpy(c->name, broken);
++}
++
+ void
+ updatewindowtype(Client *c)
+ {