commit b0a8c246398ecc62b16d22ccb63d84cc61914706
parent c7e1829cd463fa21a41f92bcdb7489644d836475
Author: Niky Kovalski <nikykovalski@gmail.com>
Date: Wed, 23 Sep 2026 00:52:57 -0400
add fullscreenmodes patch for dwm
fullscreenmodes patch adds toggleable fullscreen and fakefullscreen
Diffstat:
2 files changed, 304 insertions(+), 0 deletions(-)
diff --git a/dwm.suckless.org/patches/fullscreenmodes/dwm-fullscreenmodes-20260922-44dbc68.diff b/dwm.suckless.org/patches/fullscreenmodes/dwm-fullscreenmodes-20260922-44dbc68.diff
@@ -0,0 +1,280 @@
+From 19ffcdf58d0055c1d9bdefe72f9a23a10a13161f Mon Sep 17 00:00:00 2001
+From: Niky Kovalski <nikykovalski@gmail.com>
+Date: Tue, 22 Sep 2026 15:17:25 -0400
+Subject: [PATCH] fullscreenmodes
+
+- toggle actual fullscreen or fake fullscreen
+- switching between the two is possible
+- the client never exits fullscreen when switching between
+fullscreen modes
+---
+ config.def.h | 8 +++--
+ dwm.1 | 6 ++++
+ dwm.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++-----
+ 3 files changed, 92 insertions(+), 11 deletions(-)
+
+diff --git a/config.def.h b/config.def.h
+index 81c3fc0..a8ee865 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -26,9 +26,9 @@ static const Rule rules[] = {
+ * WM_CLASS(STRING) = instance, class
+ * WM_NAME(STRING) = title
+ */
+- /* class instance title tags mask isfloating monitor */
+- { "Gimp", NULL, NULL, 0, 1, -1 },
+- { "Firefox", NULL, NULL, 1 << 8, 0, -1 },
++ /* class instance title tags mask isfloating isfullscreen isfakefullscreen monitor */
++ { "Gimp", NULL, NULL, 0, 1, 0, 0, -1 },
++ { "Firefox", NULL, NULL, 1 << 8, 0, 0, 1, -1 },
+ };
+
+ /* layout(s) */
+@@ -80,6 +80,8 @@ static const Key keys[] = {
+ { MODKEY, XK_m, setlayout, {.v = &layouts[2]} },
+ { MODKEY, XK_space, setlayout, {0} },
+ { MODKEY|ShiftMask, XK_space, togglefloating, {0} },
++ { MODKEY|ShiftMask, XK_f, togglefullscreen, {0} },
++ { MODKEY|ControlMask, XK_f, togglefakefullscreen, {0} },
+ { MODKEY, XK_0, view, {.ui = ~0 } },
+ { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } },
+ { MODKEY, XK_comma, focusmon, {.i = -1 } },
+diff --git a/dwm.1 b/dwm.1
+index ddc8321..9d4436d 100644
+--- a/dwm.1
++++ b/dwm.1
+@@ -116,6 +116,12 @@ Zooms/cycles focused window to/from master area (tiled layouts only).
+ .B Mod1\-Shift\-c
+ Close focused window.
+ .TP
++.B Mod1\-Shift\-f
++Toggle fullscreen for focused window.
++.TP
++.B Mod1\-Control\-f
++Toggle fake fullscreen for focused window.
++.TP
+ .B Mod1\-Shift\-space
+ Toggle focused window between tiled and floating state.
+ .TP
+diff --git a/dwm.c b/dwm.c
+index ab3a84c..859d3d0 100644
+--- a/dwm.c
++++ b/dwm.c
+@@ -91,7 +91,7 @@ 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, isfullscreen, isfakefullscreen;
+ Client *next;
+ Client *snext;
+ Monitor *mon;
+@@ -137,6 +137,8 @@ typedef struct {
+ const char *title;
+ unsigned int tags;
+ int isfloating;
++ int isfullscreen;
++ int isfakefullscreen;
+ int monitor;
+ } Rule;
+
+@@ -199,6 +201,7 @@ static void sendmon(Client *c, Monitor *m);
+ static void setclientstate(Client *c, long state);
+ static void setfocus(Client *c);
+ static void setfullscreen(Client *c, int fullscreen);
++static void setfullscreenmode(Client *c, int fakefullscreen);
+ static void setlayout(const Arg *arg);
+ static void setmfact(const Arg *arg);
+ static void setup(void);
+@@ -210,6 +213,8 @@ static void tagmon(const Arg *arg);
+ static void tile(Monitor *m);
+ static void togglebar(const Arg *arg);
+ static void togglefloating(const Arg *arg);
++static void togglefakefullscreen(const Arg *arg);
++static void togglefullscreen(const Arg *arg);
+ static void toggletag(const Arg *arg);
+ static void toggleview(const Arg *arg);
+ static void unfocus(Client *c, int setfocus);
+@@ -284,6 +289,8 @@ applyrules(Client *c)
+ XClassHint ch = { NULL, NULL };
+
+ /* rule matching */
++ int isfullscreen = 0;
++ c->isfakefullscreen = 0;
+ c->isfloating = 0;
+ c->tags = 0;
+ XGetClassHint(dpy, c->win, &ch);
+@@ -296,6 +303,8 @@ applyrules(Client *c)
+ && (!r->class || strstr(class, r->class))
+ && (!r->instance || strstr(instance, r->instance)))
+ {
++ isfullscreen = r->isfullscreen;
++ c->isfakefullscreen = r->isfakefullscreen;
+ c->isfloating = r->isfloating;
+ c->tags |= r->tags;
+ for (m = mons; m && m->num != r->monitor; m = m->next);
+@@ -307,6 +316,8 @@ applyrules(Client *c)
+ XFree(ch.res_class);
+ if (ch.res_name)
+ XFree(ch.res_name);
++ if (isfullscreen)
++ setfullscreen(c, 1);
+ c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags];
+ }
+
+@@ -523,7 +534,7 @@ clientmessage(XEvent *e)
+ if (cme->data.l[1] == netatom[NetWMFullscreen]
+ || cme->data.l[2] == netatom[NetWMFullscreen])
+ setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */
+- || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen)));
++ || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && (!c->isfullscreen || c->isfakefullscreen))));
+ } else if (cme->message_type == netatom[NetActiveWindow]) {
+ if (c != selmon->sel && !c->isurgent)
+ seturgent(c, 1);
+@@ -567,7 +578,7 @@ configurenotify(XEvent *e)
+ updatebars();
+ for (m = mons; m; m = m->next) {
+ for (c = m->clients; c; c = c->next)
+- if (c->isfullscreen)
++ if (c->isfullscreen && !c->isfakefullscreen)
+ resizeclient(c, m->mx, m->my, m->mw, m->mh);
+ XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
+ }
+@@ -839,7 +850,7 @@ focusstack(const Arg *arg)
+ {
+ Client *c = NULL, *i;
+
+- if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen))
++ if (!selmon->sel || (selmon->sel->isfullscreen && !selmon->sel->isfakefullscreen && lockfullscreen))
+ return;
+ if (arg->i > 0) {
+ for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next);
+@@ -1153,7 +1164,7 @@ movemouse(const Arg *arg)
+
+ if (!(c = selmon->sel))
+ return;
+- if (c->isfullscreen) /* no support moving fullscreen windows by mouse */
++ if (c->isfullscreen && !c->isfakefullscreen) /* no support moving fullscreen windows by mouse */
+ return;
+ restack(selmon);
+ ocx = c->x;
+@@ -1308,7 +1319,7 @@ resizemouse(const Arg *arg)
+
+ if (!(c = selmon->sel))
+ return;
+- if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */
++ if (c->isfullscreen && !c->isfakefullscreen) /* no support resizing fullscreen windows by mouse */
+ return;
+ restack(selmon);
+ ocx = c->x;
+@@ -1486,6 +1497,10 @@ setfullscreen(Client *c, int fullscreen)
+ XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
+ PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1);
+ c->isfullscreen = 1;
++ if (c->isfakefullscreen) {
++ resizeclient(c, c->x, c->y, c->w, c->h);
++ return;
++ }
+ c->oldstate = c->isfloating;
+ c->oldbw = c->bw;
+ c->bw = 0;
+@@ -1496,6 +1511,29 @@ setfullscreen(Client *c, int fullscreen)
+ XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
+ PropModeReplace, (unsigned char*)0, 0);
+ c->isfullscreen = 0;
++ if (c->isfakefullscreen) {
++ resizeclient(c, c->x, c->y, c->w, c->h);
++ return;
++ }
++ c->isfloating = c->oldstate;
++ c->bw = c->oldbw;
++ c->x = c->oldx;
++ c->y = c->oldy;
++ c->w = c->oldw;
++ c->h = c->oldh;
++ resizeclient(c, c->x, c->y, c->w, c->h);
++ arrange(c->mon);
++ }
++}
++
++void
++setfullscreenmode(Client *c, int fakefullscreen) {
++ if (!c->isfullscreen) {
++ c->isfakefullscreen = fakefullscreen;
++ return;
++ }
++ if (fakefullscreen && !c->isfakefullscreen) {
++ c->isfakefullscreen = 1;
+ c->isfloating = c->oldstate;
+ c->bw = c->oldbw;
+ c->x = c->oldx;
+@@ -1505,6 +1543,15 @@ setfullscreen(Client *c, int fullscreen)
+ resizeclient(c, c->x, c->y, c->w, c->h);
+ arrange(c->mon);
+ }
++ else if (!fakefullscreen && c->isfakefullscreen) {
++ c->isfakefullscreen = 0;
++ c->oldstate = c->isfloating;
++ c->oldbw = c->bw;
++ c->bw = 0;
++ c->isfloating = 1;
++ resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh);
++ XRaiseWindow(dpy, c->win);
++ }
+ }
+
+ void
+@@ -1634,7 +1681,7 @@ showhide(Client *c)
+ if (ISVISIBLE(c)) {
+ /* show clients top down */
+ XMoveWindow(dpy, c->win, c->x, c->y);
+- if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen)
++ if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && (!c->isfullscreen || c->isfakefullscreen))
+ resize(c, c->x, c->y, c->w, c->h, 0);
+ showhide(c->snext);
+ } else {
+@@ -1726,7 +1773,7 @@ togglefloating(const Arg *arg)
+ {
+ if (!selmon->sel)
+ return;
+- if (selmon->sel->isfullscreen) /* no support for fullscreen windows */
++ if (selmon->sel->isfullscreen && !selmon->sel->isfakefullscreen) /* no support for fullscreen windows */
+ return;
+ selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed;
+ if (selmon->sel->isfloating)
+@@ -1735,6 +1782,32 @@ togglefloating(const Arg *arg)
+ arrange(selmon);
+ }
+
++void
++togglefakefullscreen(const Arg *arg)
++{
++ if (!selmon->sel)
++ return;
++ if (selmon->sel->isfullscreen && !selmon->sel->isfakefullscreen)
++ setfullscreenmode(selmon->sel, 1);
++ else {
++ selmon->sel->isfakefullscreen = 1;
++ setfullscreen(selmon->sel, !selmon->sel->isfullscreen);
++ }
++}
++
++void
++togglefullscreen(const Arg *arg)
++{
++ if (!selmon->sel)
++ return;
++ if (selmon->sel->isfullscreen && selmon->sel->isfakefullscreen)
++ setfullscreenmode(selmon->sel, 0);
++ else {
++ selmon->sel->isfakefullscreen = 0;
++ setfullscreen(selmon->sel, !selmon->sel->isfullscreen);
++ }
++}
++
+ void
+ toggletag(const Arg *arg)
+ {
+--
+2.55.0
+
diff --git a/dwm.suckless.org/patches/fullscreenmodes/index.d b/dwm.suckless.org/patches/fullscreenmodes/index.d
@@ -0,0 +1,24 @@
+fullscreenmodes
+===============
+
+Description
+-----------
+This patch adds keybindings to toggle fullscreen and fake fullscreen
+on the focused client. Alternating between the two doesn't cause the
+client to leave fullscreen. It also adds fullscreen and fakefullscreen
+rules for starting applications in fullscreen.
+
+Download
+--------
+* [dwm-fullscreenmodes-20260922-44dbc68.diff](dwm-fullscreenmodes-20260922-44dbc68.diff)
+
+Notes
+-----
+Most of the code comes from [actualfullscreen](../actualfullscreen) and [selectivefakefullscreen](../selectivefakefullscreen)
+but these patches are not compatible. However the authors of those
+projects should get credit for the original fullscreen functionality
+that inspired this patch.
+
+Author
+------
+* Niky Kovalski - <nikykovalski@gmail.com>