aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Guihot2026-09-16 06:18:28 +1000
committerDaniel Guihot2026-09-16 06:18:49 +1000
commit395aa47c173a9fad7a38ff68ee764d9f93126e11 (patch)
tree8c8965b6593958f933160570da2978829af94bb9
parent2c336f99d14660cb656892875b3431604359ade3 (diff)
downloaddwm-unmapfullscreen-main.tar.gz
dwm-unmapfullscreen-main.zip
Fix fullscreen map and focus transitionsHEADmain
Keep hidden fullscreen clients managed and restore input focus after mapping. Handle hidden clients entering or leaving fullscreen.
-rw-r--r--README.md15
-rw-r--r--dwm-unmapfullscreen-6.8.diff97
2 files changed, 85 insertions, 27 deletions
diff --git a/README.md b/README.md
index c827164..0d60d00 100644
--- a/README.md
+++ b/README.md
@@ -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);