From be2bd7231445545f35819cb3d07e338555724e47 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Thu, 29 Oct 2026 17:49:44 +0100 Subject: [PATCH 1/9] remove old comment This was added 4 years in ago in 8cdb997. New users won't understand what "old behavior" refers to, so this line is confusing. And it is self-evident that you should change the alpha value if you want transparency. --- config.def.h | 1 - 1 file changed, 1 deletion(-) diff --git a/config.def.h b/config.def.h index a4019d3..4901e7a 100644 --- a/config.def.h +++ b/config.def.h @@ -12,7 +12,6 @@ static const float rootcolor[] = COLOR(0x222222ff); static const float bordercolor[] = COLOR(0x444444ff); static const float focuscolor[] = COLOR(0x005577ff); static const float urgentcolor[] = COLOR(0xff0000ff); -/* This conforms to the xdg-protocol. Set the alpha to zero to restore the old behavior */ static const float fullscreen_bg[] = {0.0f, 0.0f, 0.0f, 1.0f}; /* You can also use glsl colors */ /* tagging - TAGCOUNT must be no greater than 31 */ From a6e2c155de5daf01f47a726029dc929c4fb8e489 Mon Sep 17 00:00:00 2001 From: guyuming Date: Sun, 1 Nov 2026 06:13:49 +0800 Subject: [PATCH 2/9] fix focustop(): prefer a visible fullscreen client Problem (reproducible on vanilla 0.9, unrelated to any local patch): 1. fullscreen client A on tag 3 (MOD+e); 2. MOD+0 to view all tags; 3. MOD+1 to switch to tag 1, focus lands on client B; 4. MOD+0 again: client A is displayed fullscreen, covering everything, but the focused client is the invisible B; 5. so MOD+e / MOD+q act on B behind A and the user thinks MOD+e does not work on A until A is clicked with the mouse. Root cause: focustop() returns the first VISIBLEON() client on the fstack, but VISIBLEON() only checks whether the client is on a currently visible tag; it does not check whether the client is covered by a fullscreen client. When switching back to tag 0, the last focused client B is first on the fstack, so B gets the focus while A is still displayed fullscreen. Fix: focustop() first looks for a visible fullscreen client on the fstack (at most one fullscreen client per monitor is actually shown, and it always sits on top, covering the whole monitor), and only falls back to the previous "first visible client" if there is none. As a result: - after a tag switch the focus goes back to the fullscreen client that actually occupies the screen, so MOD+e / MOD+q behave as expected; - the fullscreen_bg decision in arrange() is fixed as a side effect (it used to be turned off when the focus was not on the fullscreen client, so a non-opaque fullscreen surface could reveal other clients behind it, which violates the protocol); - focusstack() already returns early when the focused client is fullscreen, so MOD+j/k and the arrow keys no longer move the focus to a covered client either. Verified with headless dwl, two foot clients and keys injected through zwp_virtual_keyboard_v1: before the fix the last step gives focal = winB / fullscreen 0; after the fix focal = winA / fullscreen 1, and MOD+e then restores A as expected. ta: 20261031_022753 --- dwl.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index 89e8caf..7136eb7 100644 --- a/dwl.c +++ b/dwl.c @@ -1700,10 +1700,14 @@ Client * focustop(Monitor *m) { Client *c; - wl_list_for_each(c, &fstack, flink) { + + wl_list_for_each(c, &fstack, flink) + if (VISIBLEON(c, m) && c->isfullscreen) + return c; + + wl_list_for_each(c, &fstack, flink) if (VISIBLEON(c, m)) return c; - } return NULL; } From 4b76704af7e4f2c243fb32014c2e5171d8f3b124 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Thu, 5 Nov 2026 07:37:06 +0100 Subject: [PATCH 3/9] Revert "also make clients tiled on map" This reverts commit d9ddaba762b5c2ae49101b6690c96d3df703bed9. mpv fixed this upstream in https://github.com/mpv-player/mpv/commit/b2c255c13e8e37952dbac6c34da56a690560378b, and mpv 0.42 has been released, so our workaround can be removed. --- dwl.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/dwl.c b/dwl.c index 7136eb7..6ce12a4 100644 --- a/dwl.c +++ b/dwl.c @@ -1980,8 +1980,6 @@ mapnotify(struct wl_listener *listener, void *data) } /* Initialize client geometry with room for border */ - client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | - WLR_EDGE_RIGHT); c->geom.width += 2 * c->bw; c->geom.height += 2 * c->bw; if (client_is_x11(c)) { From 15bff401c4f038d9c5dde5bdcd142f8b3f22a752 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Thu, 5 Nov 2026 20:21:05 +0100 Subject: [PATCH 4/9] refocus after unsetting fullscreen in mapnotify() Since a6e2c15 focustop() prefers fullscreen clients, so the focusclient(focustop(selmon)) in setmon() focuses the fullscreen client before mapnotify() unsets its fullscreen state, and focus never moves to the new client. --- dwl.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 6ce12a4..6f971c6 100644 --- a/dwl.c +++ b/dwl.c @@ -2019,8 +2019,11 @@ mapnotify(struct wl_listener *listener, void *data) unset_fullscreen: m = c->mon ? c->mon : xytomon(c->geom.x, c->geom.y); wl_list_for_each(w, &clients, link) { - if (w != c && w != p && w->isfullscreen && m == w->mon && (w->tags & c->tags)) + if (w != c && w != p && w->isfullscreen && m == w->mon && (w->tags & c->tags)) { setfullscreen(w, 0); + /* setmon() focused w because focustop() prefers fullscreen clients */ + focusclient(focustop(selmon), NULL, 1); + } } } From 0996407e3d736ea76f58e7a368b73964bb1ce1de Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Fri, 6 Nov 2026 08:10:02 +0100 Subject: [PATCH 5/9] fix integer underflow in tile() When clients don't fit, my or ty can exceed m->w.height, and since they were unsigned, m->w.height - my wrapped around to huge heights, hanging dwl. With signed ints the height can become negative instead, which applybounds() raises to the minimum size. Fixes: https://codeberg.org/dwl/dwl/issues/1254 --- dwl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index 6f971c6..acb1b05 100644 --- a/dwl.c +++ b/dwl.c @@ -3054,8 +3054,7 @@ tagmon(const Arg *arg) void tile(Monitor *m) { - unsigned int mw, my, ty; - int i, n = 0; + int i, n = 0, mw, my, ty; Client *c; wl_list_for_each(c, &clients, link) From 8ab0c16f3c6fccc8000e03fd84b048303b421294 Mon Sep 17 00:00:00 2001 From: save196 Date: Sun, 8 Nov 2026 16:18:13 +0200 Subject: [PATCH 6/9] fix the size of fullscreen clients on hidden tags when outputs change Clients moved by closemon() get a stale geometry, and updatemons() only corrected the visible fullscreen client, not those on hidden tags. --- dwl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index acb1b05..e4c1ce2 100644 --- a/dwl.c +++ b/dwl.c @@ -3355,8 +3355,9 @@ updatemons(struct wl_listener *listener, void *data) /* Don't move clients to the left output when plugging monitors */ arrange(m); /* make sure fullscreen clients have the right size */ - if ((c = focustop(m)) && c->isfullscreen) - resize(c, m->m, 0); + wl_list_for_each(c, &clients, link) + if (c->mon == m && c->isfullscreen) + resize(c, m->m, 0); /* Try to re-set the gamma LUT when updating monitors, * it's only really needed when enabling a disabled output, but meh. */ From 86a30c4af22a23112b5458da5347fc0ece0c3e15 Mon Sep 17 00:00:00 2001 From: save196 Date: Mon, 9 Nov 2026 22:25:17 +0200 Subject: [PATCH 7/9] fix out-of-bounds access to consumed[] --- client.h | 2 +- dwl.c | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/client.h b/client.h index ebe665b..bea7a97 100644 --- a/client.h +++ b/client.h @@ -279,7 +279,7 @@ client_notify_enter(struct wlr_surface *s, struct wlr_keyboard *kb) } for (size_t i = 0; i < kb->num_keycodes; i++) { uint32_t key = kb->keycodes[i]; - if (!consumed[key]) + if (key >= LENGTH(consumed) || !consumed[key]) filtered[size++] = key; } wlr_seat_keyboard_notify_enter(seat, s, filtered, size, &kb->modifiers); diff --git a/dwl.c b/dwl.c index e4c1ce2..bb7cf77 100644 --- a/dwl.c +++ b/dwl.c @@ -1835,7 +1835,8 @@ keypress(struct wl_listener *listener, void *data) for (int i = 0; i < 2; i++) { const Key *key = keybinding(group->mods, group->keysyms[i]); if (key) { - consumed[event->keycode] = 1; + if (event->keycode < LENGTH(consumed)) + consumed[event->keycode] = 1; key->func(&key->arg); handled = 1; break; @@ -1852,7 +1853,7 @@ keypress(struct wl_listener *listener, void *data) if (handled || input_method_keyboard_grab_forward_key(group, event)) return; - if (consumed[event->keycode]) { + if (event->keycode < LENGTH(consumed) && consumed[event->keycode]) { if (event->state == WL_KEYBOARD_KEY_STATE_RELEASED) consumed[event->keycode] = 0; return; /* Don't pass to the client the release event of a handled key-press */ From 637842ce4bcd2a5a0561717f498e6f109b60daf3 Mon Sep 17 00:00:00 2001 From: Diego Viola Date: Wed, 11 Nov 2026 04:47:28 -0300 Subject: [PATCH 8/9] fix black screen on QEMU modeset/resize When running dwl inside a QEMU VM and toggling fullscreen in and out, the output can stay as a black screen until a new client (such as foot) forces a redraw. Calling wlr_output_schedule_frame() ensures the frame loop stays active and resolves the issue. --- dwl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dwl.c b/dwl.c index bb7cf77..25796d9 100644 --- a/dwl.c +++ b/dwl.c @@ -2430,6 +2430,7 @@ rendermon(struct wl_listener *listener, void *data) wlr_scene_output_commit(m->scene_output, NULL); clock_gettime(CLOCK_MONOTONIC, &now); + wlr_output_schedule_frame(m->wlr_output); wlr_scene_output_send_frame_done(m->scene_output, &now); } @@ -2459,6 +2460,7 @@ requestmonstate(struct wl_listener *listener, void *data) { struct wlr_output_event_request_state *event = data; wlr_output_commit_state(event->output, event->state); + wlr_output_schedule_frame(event->output); updatemons(NULL, NULL); } From 387bc1b53ffc1b09b61a0abff93c4aba21930b61 Mon Sep 17 00:00:00 2001 From: Diego Viola Date: Wed, 11 Nov 2026 05:30:36 -0300 Subject: [PATCH 9/9] Fix typo --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 25796d9..76bb628 100644 --- a/dwl.c +++ b/dwl.c @@ -523,7 +523,7 @@ applybounds(Client *c, struct wlr_box *bbox) c->geom.width = MAX(min.width + 2 * (int)c->bw, c->geom.width); c->geom.height = MAX(min.height + 2 * (int)c->bw, c->geom.height); /* Some clients set their max size to INT_MAX, which does not violate the - * protocol but it's unnecesary, as they can set their max size to zero. */ + * protocol but it's unnecessary, as they can set their max size to zero. */ if (max.width > 0 && 2 * (int)c->bw <= INT_MAX - max.width) /* Checks for overflow */ c->geom.width = MIN(max.width + 2 * (int)c->bw, c->geom.width); if (max.height > 0 && 2 * (int)c->bw <= INT_MAX - max.height) /* Checks for overflow */