diff options
| author | Daniel Guihot | 2026-09-16 06:18:28 +1000 |
|---|---|---|
| committer | Daniel Guihot | 2026-09-16 06:18:49 +1000 |
| commit | 395aa47c173a9fad7a38ff68ee764d9f93126e11 (patch) | |
| tree | 8c8965b6593958f933160570da2978829af94bb9 | |
| parent | 2c336f99d14660cb656892875b3431604359ade3 (diff) | |
| download | dwm-unmapfullscreen-main.tar.gz dwm-unmapfullscreen-main.zip | |
Keep hidden fullscreen clients managed and restore input focus after
mapping. Handle hidden clients entering or leaving fullscreen.
| -rw-r--r-- | README.md | 15 | ||||
| -rw-r--r-- | dwm-unmapfullscreen-6.8.diff | 97 |
2 files changed, 85 insertions, 27 deletions
@@ -14,13 +14,20 @@ dwm selects `StructureNotifyMask` on clients and `SubstructureNotifyMask` on the root, so calling `XUnmapWindow()` directly reaches `unmapnotify()` and unmanages the client. The patch adds a small `unmapclient()` helper that suppresses both masks around the unmap, the same technique the windowmap -patch uses. Hidden fullscreen clients are still parked off-screen so the -initial map from `manage()` does not put them on the wrong tag. +patch uses. + +Hidden fullscreen clients are not moved off-screen. The client tracks whether +the patch unmapped it, which lets `showhide()` map it before restoring input +focus without querying the X server on every arrange. Normal clients keep +dwm's original off-screen hiding. A hidden client that leaves fullscreen is +mapped off-screen again instead of remaining unmapped. ## Testing -Applies and builds against dwm 6.8. Runtime tested under Xvfb: a hidden -fullscreen client reports map state unmapped and stays managed. +Applies and builds against dwm 6.8. Runtime tested under Xvfb. Hidden +fullscreen clients became unmapped, stayed managed, mapped on return, and +regained real X input focus. Tests also covered a new fullscreen client +assigned to a hidden tag and a hidden client leaving fullscreen. ## Download diff --git a/dwm-unmapfullscreen-6.8.diff b/dwm-unmapfullscreen-6.8.diff index 30eb3ae..bccd7c9 100644 --- a/dwm-unmapfullscreen-6.8.diff +++ b/dwm-unmapfullscreen-6.8.diff @@ -1,28 +1,36 @@ -From d175466d1ecff304ff32ae3ba852e1eec29a9561 Mon Sep 17 00:00:00 2001 +From f45beb0d921ecc3f14f9e41ceccc05bf9f274822 Mon Sep 17 00:00:00 2001 From: Daniel Guihot <daniel@guihot.net> Date: Wed, 16 Sep 2026 05:20:00 +1000 Subject: [PATCH] unmapfullscreen: unmap hidden fullscreen clients -showhide() hides windows by moving them off-screen, which some games -and Wine builds treat as a broken or minimized state and do not recover -from when the tag returns. Unmap and map fullscreen clients instead. +showhide() normally hides windows by moving them off-screen. Some +games and Wine builds do not recover from that state when the tag +returns. Unmap fullscreen clients when hidden and map them when shown +instead. -dwm selects StructureNotifyMask on clients and SubstructureNotifyMask on -the root, so the unmap would reach unmapnotify() and unmanage the -client. unmapclient() suppresses both masks around the unmap, the same -approach the windowmap patch uses. - -Hidden fullscreen clients are still parked off-screen so the initial map -from manage() does not put them on the wrong tag. +Suppress StructureNotifyMask and SubstructureNotifyMask while +unmapping so dwm does not mistake its own UnmapNotify for a withdrawn +client. Map before restoring input focus, and preserve normal +off-screen hiding when a hidden client leaves fullscreen. --- - dwm.c | 22 ++++++++++++++++++++++ - 1 file changed, 22 insertions(+) + dwm.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/dwm.c b/dwm.c -index 53b393e..4bd3988 100644 +index 53b393e..f1c2d9f 100644 --- a/dwm.c +++ b/dwm.c -@@ -214,6 +214,7 @@ static void toggletag(const Arg *arg); +@@ -91,7 +91,8 @@ struct Client { + int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid; + int bw, oldbw; + unsigned int tags; +- int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; ++ int isfixed, isfloating, isurgent, neverfocus, oldstate; ++ int isfullscreen, isunmapped; + Client *next; + Client *snext; + Monitor *mon; +@@ -214,6 +215,7 @@ static void toggletag(const Arg *arg); static void toggleview(const Arg *arg); static void unfocus(Client *c, int setfocus); static void unmanage(Client *c, int destroyed); @@ -30,39 +38,82 @@ index 53b393e..4bd3988 100644 static void unmapnotify(XEvent *e); static void updatebarpos(Monitor *m); static void updatebars(void); -@@ -1634,12 +1635,16 @@ showhide(Client *c) +@@ -1084,7 +1086,8 @@ manage(Window w, XWindowAttributes *wa) + unfocus(selmon->sel, 0); + c->mon->sel = c; + arrange(c->mon); +- XMapWindow(dpy, c->win); ++ if (ISVISIBLE(c) || !c->isfullscreen) ++ XMapWindow(dpy, c->win); + focus(NULL); + } + +@@ -1490,8 +1493,11 @@ setfullscreen(Client *c, int fullscreen) + c->oldbw = c->bw; + c->bw = 0; + c->isfloating = 1; ++ if (!ISVISIBLE(c)) ++ unmapclient(c); + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); +- XRaiseWindow(dpy, c->win); ++ if (ISVISIBLE(c)) ++ XRaiseWindow(dpy, c->win); + } else if (!fullscreen && c->isfullscreen){ + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char*)0, 0); +@@ -1634,13 +1640,27 @@ showhide(Client *c) if (ISVISIBLE(c)) { /* show clients top down */ XMoveWindow(dpy, c->win, c->x, c->y); -+ if (c->isfullscreen) ++ if (c->isunmapped) { + XMapWindow(dpy, c->win); ++ c->isunmapped = 0; ++ if (c == selmon->sel) ++ setfocus(c); ++ } if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen) resize(c, c->x, c->y, c->w, c->h, 0); showhide(c->snext); } else { /* hide clients bottom up */ showhide(c->snext); +- XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); + if (c->isfullscreen) + unmapclient(c); - XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); ++ else { ++ XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); ++ if (c->isunmapped) { ++ XMapWindow(dpy, c->win); ++ c->isunmapped = 0; ++ } ++ } } } -@@ -1801,6 +1806,23 @@ unmanage(Client *c, int destroyed) + +@@ -1801,6 +1821,31 @@ unmanage(Client *c, int destroyed) arrange(m); } +void +unmapclient(Client *c) +{ -+ static XWindowAttributes ra, ca; ++ XWindowAttributes ca, ra; + -+ if (!XGetWindowAttributes(dpy, c->win, &ca) || ca.map_state == IsUnmapped) -+ return; + XGrabServer(dpy); -+ XGetWindowAttributes(dpy, root, &ra); ++ if (!XGetWindowAttributes(dpy, root, &ra) ++ || !XGetWindowAttributes(dpy, c->win, &ca)) { ++ XUngrabServer(dpy); ++ return; ++ } ++ if (ca.map_state == IsUnmapped) { ++ c->isunmapped = 1; ++ XUngrabServer(dpy); ++ return; ++ } + XSelectInput(dpy, root, ra.your_event_mask & ~SubstructureNotifyMask); + XSelectInput(dpy, c->win, ca.your_event_mask & ~StructureNotifyMask); + XUnmapWindow(dpy, c->win); ++ c->isunmapped = 1; + XSelectInput(dpy, root, ra.your_event_mask); + XSelectInput(dpy, c->win, ca.your_event_mask); + XUngrabServer(dpy); |
