diff --git a/config.def.h.rej b/config.def.h.rej index dd1a041..36b8c7c 100644 --- a/config.def.h.rej +++ b/config.def.h.rej @@ -1,13 +1,15 @@ --- config.def.h +++ config.def.h -@@ -67,6 +67,10 @@ static const Key keys[] = { - { MODKEY, XK_b, togglebar, {0} }, - { MODKEY, XK_j, focusstack, {.i = +1 } }, - { MODKEY, XK_k, focusstack, {.i = -1 } }, -+ { MODKEY|ControlMask, XK_Left, placedir, {.i = 0 } }, // left -+ { MODKEY|ControlMask, XK_Right, placedir, {.i = 1 } }, // right -+ { MODKEY|ControlMask, XK_Up, placedir, {.i = 2 } }, // up -+ { MODKEY|ControlMask, XK_Down, placedir, {.i = 3 } }, // down - { MODKEY, XK_i, incnmaster, {.i = +1 } }, - { MODKEY, XK_d, incnmaster, {.i = -1 } }, - { MODKEY, XK_h, setmfact, {.f = -0.05} }, +@@ -26,9 +26,11 @@ static const Rule rules[] = { + * WM_CLASS(STRING) = instance, class + * WM_NAME(STRING) = title + */ +- /* class instance title tags mask isfloating monitor */ ++ /* class instance title tags mask isfloating monitor isgame */ + { "Gimp", NULL, NULL, 0, 1, -1 }, + { "Firefox", NULL, NULL, 1 << 8, 0, -1 }, ++ { "Steam", NULL, NULL, 0, 0, -1, 1 }, ++ { "steam_app",NULL, NULL, 0, 0, -1, 1 }, + }; + + /* layout(s) */ diff --git a/config.h b/config.h index 511fb5f..3d86b9d 100644 --- a/config.h +++ b/config.h @@ -52,7 +52,7 @@ static const Rule rules[] = { * WM_CLASS(STRING) = instance, class * WM_NAME(STRING) = title */ - /* class instance title tags mask isfloating isterminal isswallow monitor */ + /* class instance title tags mask isfloating isterminal isswallow monitor isgame */ { "Playground", NULL, NULL, 0, 1, 0, -1 -1 }, { "steam", NULL, NULL, 1 << 1, 0, 0, -1 -1 }, @@ -63,6 +63,9 @@ static const Rule rules[] = { { NULL, "spterm", NULL, SPTAG(0), 1, 0, 0, -1 }, { NULL, "spfm", NULL, SPTAG(1), 1, 0, 0, -1 }, { NULL, "keepassxc", NULL, SPTAG(2), 0, 0, 0, -1 }, + + { "Steam", NULL, NULL, 1 << 4, 0, 0, -1 -1, 0 }, + { "steam_app", NULL, NULL, 1 << 5, 0, 0, -1 -1, 0 }, }; /* layout(s) */ diff --git a/dwm b/dwm index e43878b..7945ac6 100755 Binary files a/dwm and b/dwm differ diff --git a/dwm.c b/dwm.c index 938ba3e..b3d821f 100644 --- a/dwm.c +++ b/dwm.c @@ -57,6 +57,7 @@ * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) #define ISVISIBLEONTAG(C, T) ((C->tags & T)) #define ISVISIBLE(C) ISVISIBLEONTAG(C, C->mon->tagset[C->mon->seltags]) +#define MINIMIZED(C) ((getstate(C->win) == IconicState)) #define MOUSEMASK (BUTTONMASK|PointerMotionMask) #define WIDTH(X) ((X)->w + 2 * (X)->bw) #define HEIGHT(X) ((X)->h + 2 * (X)->bw) @@ -118,6 +119,7 @@ struct Client { int bw, oldbw; unsigned int tags; int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen, isterminal, noswallow; + int isgame; pid_t pid; Client *next; Client *snext; @@ -172,6 +174,7 @@ typedef struct { int isterminal; int noswallow; int monitor; + int isgame; } Rule; typedef struct Systray Systray; @@ -223,6 +226,7 @@ static void killclient(const Arg *arg); static void manage(Window w, XWindowAttributes *wa); static void mappingnotify(XEvent *e); static void maprequest(XEvent *e); +static void minimize(Client *c); static void monocle(Monitor *m); static void motionnotify(XEvent *e); static void movemouse(const Arg *arg); @@ -268,6 +272,7 @@ static void toggleview(const Arg *arg); static void unfocus(Client *c, int setfocus); static void unmanage(Client *c, int destroyed); static void unmapnotify(XEvent *e); +static void unminimize(Client *c); static void updatebarpos(Monitor *m); static void updatebars(void); static void updateclientlist(void); @@ -411,6 +416,7 @@ applyrules(Client *c) c->noswallow = r->noswallow; c->isfloating = r->isfloating; c->tags |= r->tags; + c->isgame = r->isgame; if ((r->tags & SPTAGMASK) && r->isfloating) { c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2); c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2); @@ -1469,6 +1475,29 @@ maprequest(XEvent *e) manage(ev->window, &wa); } +void +minimize(Client *c) +{ + if (!c || MINIMIZED(c)) + return; + + Window w = c->win; + static XWindowAttributes ra, ca; + + // more or less taken directly from blackbox's hide() function + XGrabServer(dpy); + XGetWindowAttributes(dpy, root, &ra); + XGetWindowAttributes(dpy, w, &ca); + // prevent UnmapNotify events + XSelectInput(dpy, root, ra.your_event_mask & ~SubstructureNotifyMask); + XSelectInput(dpy, w, ca.your_event_mask & ~StructureNotifyMask); + XUnmapWindow(dpy, w); + setclientstate(c, IconicState); + XSelectInput(dpy, root, ra.your_event_mask); + XSelectInput(dpy, w, ca.your_event_mask); + XUngrabServer(dpy); +} + void monocle(Monitor *m) { @@ -1997,6 +2026,10 @@ setfocus(Client *c) XA_WINDOW, 32, PropModeReplace, (unsigned char *) &(c->win), 1); } + + if (c->isgame && c->isfullscreen) + unminimize(c); + sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0); } @@ -2359,6 +2392,10 @@ unfocus(Client *c, int setfocus) { if (!c) return; + + if (c->isgame && c->isfullscreen) + minimize(c); + grabbuttons(c, 0); XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); if (setfocus) { @@ -2430,6 +2467,16 @@ unmapnotify(XEvent *e) } } +void +unminimize(Client *c) +{ + if (!c || !MINIMIZED(c)) + return; + + XMapWindow(dpy, c->win); + setclientstate(c, NormalState); +} + void updatebars(void) { diff --git a/dwm.c.orig b/dwm.c.orig index 11f1677..938ba3e 100644 --- a/dwm.c.orig +++ b/dwm.c.orig @@ -228,6 +228,7 @@ static void motionnotify(XEvent *e); static void movemouse(const Arg *arg); static Client *nexttagged(Client *c); static Client *nexttiled(Client *c); +static void placedir(const Arg *arg); static void pop(Client *c); static void propertynotify(XEvent *e); static void quit(const Arg *arg); @@ -1577,6 +1578,103 @@ nexttiled(Client *c) return c; } +void +placedir(const Arg *arg) +{ + Client *s = selmon->sel, *f = NULL, *c, *next, *fprior, *sprior; + + if (!s || s->isfloating) + return; + + unsigned int score = -1; + unsigned int client_score; + int dist; + int dirweight = 20; + + next = s->next; + if (!next) + next = s->mon->clients; + for (c = next; c != s; c = next) { + + next = c->next; + if (!next) + next = s->mon->clients; + + if (!ISVISIBLE(c)) // || HIDDEN(c) + continue; + + switch (arg->i) { + case 0: // left + dist = s->x - c->x - c->w; + client_score = + dirweight * MIN(abs(dist), abs(dist + s->mon->ww)) + + abs(s->y - c->y); + break; + case 1: // right + dist = c->x - s->x - s->w; + client_score = + dirweight * MIN(abs(dist), abs(dist + s->mon->ww)) + + abs(c->y - s->y); + break; + case 2: // up + dist = s->y - c->y - c->h; + client_score = + dirweight * MIN(abs(dist), abs(dist + s->mon->wh)) + + abs(s->x - c->x); + break; + default: + case 3: // down + dist = c->y - s->y - s->h; + client_score = + dirweight * MIN(abs(dist), abs(dist + s->mon->wh)) + + abs(c->x - s->x); + break; + } + + if (((arg->i == 0 || arg->i == 2) && client_score <= score) || client_score < score) { + score = client_score; + f = c; + } + } + + if (f && f != s) { + for (fprior = f->mon->clients; fprior && fprior->next != f; fprior = fprior->next); + for (sprior = s->mon->clients; sprior && sprior->next != s; sprior = sprior->next); + + if (s == fprior) { + next = f->next; + if (sprior) + sprior->next = f; + else + f->mon->clients = f; + f->next = s; + s->next = next; + } else if (f == sprior) { + next = s->next; + if (fprior) + fprior->next = s; + else + s->mon->clients = s; + s->next = f; + f->next = next; + } else { // clients are not adjacent to each other + next = f->next; + f->next = s->next; + s->next = next; + if (fprior) + fprior->next = s; + else + s->mon->clients = s; + if (sprior) + sprior->next = f; + else + f->mon->clients = f; + } + + arrange(f->mon); + } +} + void pop(Client *c) { diff --git a/dwm.c.rej b/dwm.c.rej index 80303f8..aaafc5a 100644 --- a/dwm.c.rej +++ b/dwm.c.rej @@ -1,12 +1,37 @@ --- dwm.c +++ dwm.c -@@ -723,6 +790,9 @@ destroynotify(XEvent *e) - - if ((c = wintoclient(ev->window))) - unmanage(c, 1); +@@ -50,6 +50,7 @@ + #define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ + * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) + #define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) ++#define MINIMIZED(C) ((getstate(C->win) == IconicState)) + #define MOUSEMASK (BUTTONMASK|PointerMotionMask) + #define WIDTH(X) ((X)->w + 2 * (X)->bw) + #define HEIGHT(X) ((X)->h + 2 * (X)->bw) +@@ -92,6 +93,7 @@ struct Client { + int bw, oldbw; + unsigned int tags; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; ++ int isgame; + Client *next; + Client *snext; + Monitor *mon; +@@ -301,6 +306,7 @@ applyrules(Client *c) + { + c->isfloating = r->isfloating; + c->tags |= r->tags; ++ c->isgame = r->isgame; + for (m = mons; m && m->num != r->monitor; m = m->next); + if (m) + c->mon = m; +@@ -1501,6 +1530,10 @@ setfocus(Client *c) + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &(c->win), 1); + } + -+ else if ((c = swallowingclient(ev->window))) -+ unmanage(c->swallowing, 1); ++ if (c->isgame && c->isfullscreen) ++ unminimize(c); ++ + sendevent(c, wmatom[WMTakeFocus]); } - void diff --git a/dwm.o b/dwm.o index 227e964..f828161 100644 Binary files a/dwm.o and b/dwm.o differ diff --git a/patches/dwm-games-6.6.diff b/patches/dwm-games-6.6.diff new file mode 100644 index 0000000..ce4bbdb --- /dev/null +++ b/patches/dwm-games-6.6.diff @@ -0,0 +1,173 @@ +From d06cefd6e1f8b5cd9bdfe694b7dc16da1f00d83e Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Mon, 1 Jul 2024 22:19:15 +0200 +Subject: [PATCH] Game rule patch + +This patch adds a rule identifying clients as a "game" such that +if the client is in fullscreen and it loses focus (e.g. by moving +to another tag) then it will automatically be minimized (set to +IconicState and unmapped). + +When the client receives focus again (e.g. by going back to its +tag) then it will automaticaly be unminimized (set to NormalState +and mapped). This should address many of the black screen or window +is tiny after having moved to another tag and back again. + +This may conflict with the awesomebar patch which skips hidden +(iconic) windows when selecting which client to focus on, i.e. it +would be there but minimized and will not automatically unminimize. + +The function names are minimize and unminimize compared to the same +functions hide and show in the awesomebar patch. The name change is +due to two reasons; 1) because hide and show do something very +different compared to the showhide function and 2) because when +minimizing and unminimizing we do not want to trigger arrange or +a change in focus (which would affect the stacking order and thus +prevent the window receiving focus when we move back to the previous +tag). +--- + config.def.h | 4 +++- + dwm.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 50 insertions(+), 1 deletion(-) + +diff --git a/config.def.h b/config.def.h +index 9efa774..9df3c97 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -26,9 +26,11 @@ static const Rule rules[] = { + * WM_CLASS(STRING) = instance, class + * WM_NAME(STRING) = title + */ +- /* class instance title tags mask isfloating monitor */ ++ /* class instance title tags mask isfloating monitor isgame */ + { "Gimp", NULL, NULL, 0, 1, -1 }, + { "Firefox", NULL, NULL, 1 << 8, 0, -1 }, ++ { "Steam", NULL, NULL, 0, 0, -1, 1 }, ++ { "steam_app",NULL, NULL, 0, 0, -1, 1 }, + }; + + /* layout(s) */ +diff --git a/dwm.c b/dwm.c +index 1443802..29de39a 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -50,6 +50,7 @@ + #define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ + * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) + #define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) ++#define MINIMIZED(C) ((getstate(C->win) == IconicState)) + #define MOUSEMASK (BUTTONMASK|PointerMotionMask) + #define WIDTH(X) ((X)->w + 2 * (X)->bw) + #define HEIGHT(X) ((X)->h + 2 * (X)->bw) +@@ -92,6 +93,7 @@ struct Client { + int bw, oldbw; + unsigned int tags; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; ++ int isgame; + Client *next; + Client *snext; + Monitor *mon; +@@ -138,6 +140,7 @@ typedef struct { + unsigned int tags; + int isfloating; + int monitor; ++ int isgame; + } Rule; + + /* function declarations */ +@@ -180,6 +183,7 @@ static void killclient(const Arg *arg); + static void manage(Window w, XWindowAttributes *wa); + static void mappingnotify(XEvent *e); + static void maprequest(XEvent *e); ++static void minimize(Client *c); + static void monocle(Monitor *m); + static void motionnotify(XEvent *e); + static void movemouse(const Arg *arg); +@@ -215,6 +219,7 @@ static void toggleview(const Arg *arg); + static void unfocus(Client *c, int setfocus); + static void unmanage(Client *c, int destroyed); + static void unmapnotify(XEvent *e); ++static void unminimize(Client *c); + static void updatebarpos(Monitor *m); + static void updatebars(void); + static void updateclientlist(void); +@@ -298,6 +303,7 @@ applyrules(Client *c) + { + c->isfloating = r->isfloating; + c->tags |= r->tags; ++ c->isgame = r->isgame; + for (m = mons; m && m->num != r->monitor; m = m->next); + if (m) + c->mon = m; +@@ -1109,6 +1115,29 @@ maprequest(XEvent *e) + manage(ev->window, &wa); + } + ++void ++minimize(Client *c) ++{ ++ if (!c || MINIMIZED(c)) ++ return; ++ ++ Window w = c->win; ++ static XWindowAttributes ra, ca; ++ ++ // more or less taken directly from blackbox's hide() function ++ XGrabServer(dpy); ++ XGetWindowAttributes(dpy, root, &ra); ++ XGetWindowAttributes(dpy, w, &ca); ++ // prevent UnmapNotify events ++ XSelectInput(dpy, root, ra.your_event_mask & ~SubstructureNotifyMask); ++ XSelectInput(dpy, w, ca.your_event_mask & ~StructureNotifyMask); ++ XUnmapWindow(dpy, w); ++ setclientstate(c, IconicState); ++ XSelectInput(dpy, root, ra.your_event_mask); ++ XSelectInput(dpy, w, ca.your_event_mask); ++ XUngrabServer(dpy); ++} ++ + void + monocle(Monitor *m) + { +@@ -1475,6 +1504,10 @@ setfocus(Client *c) + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &(c->win), 1); + } ++ ++ if (c->isgame && c->isfullscreen) ++ unminimize(c); ++ + sendevent(c, wmatom[WMTakeFocus]); + } + +@@ -1766,6 +1799,10 @@ unfocus(Client *c, int setfocus) + { + if (!c) + return; ++ ++ if (c->isgame && c->isfullscreen) ++ minimize(c); ++ + grabbuttons(c, 0); + XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); + if (setfocus) { +@@ -1814,6 +1851,16 @@ unmapnotify(XEvent *e) + } + } + ++void ++unminimize(Client *c) ++{ ++ if (!c || !MINIMIZED(c)) ++ return; ++ ++ XMapWindow(dpy, c->win); ++ setclientstate(c, NormalState); ++} ++ + void + updatebars(void) + { +-- +2.50.1 +