Compare commits

...

12 Commits

Author SHA1 Message Date
biosfood
a6a07bedec individual-keyboard-rules 2025-11-14 15:18:42 +01:00
André Desgualdo Pereira
973df560ec fix a bug in controlled fullscreen patch in which the window app would spawn ummapped at the floating layout 2025-11-13 09:20:00 -03:00
korei999
e104ef3495 tearing patch for 0.7 2025-11-11 00:21:49 +02:00
Kana
6b9258972f Merge branch 'main' into take-on-gaplessgrid 2025-10-31 20:01:13 +01:00
André Desgualdo Pereira
ca2a322d20 take on gaplessgrid and fix minor indentation and typos 2025-10-31 15:53:40 -03:00
André Desgualdo Pereira
d181bd4f4b take on winview for maintenance 2025-10-31 19:13:23 +01:00
André Desgualdo Pereira
7be608e4ed fix trailing whitespace at gridall patch 2025-10-31 13:06:03 -03:00
André Desgualdo Pereira
10a056e882 add focus on urgent patch 2025-10-31 12:54:21 +01:00
André Desgualdo Pereira
945c1de501 add controlled fullscreen patch 2025-10-31 12:49:38 +01:00
André Desgualdo Pereira
b74ed046e6 add gridall patch 2025-10-29 09:29:20 -03:00
André Desgualdo Pereira
0a6759e881 decklayout fixes 2025-10-29 02:48:07 +01:00
Zuki Air
3f25b6c7d4 riverctl: add support for changing border colors via dwlctl, and fix bug with changing borderpx not applying 2025-10-26 14:24:20 +00:00
19 changed files with 1691 additions and 27 deletions

View File

@ -0,0 +1,10 @@
### Description
This patch allows a window to adjust its layout as if it was fullscreen, but it won't change its size and position, and it will stays under the control of dwl. For example a video on a browser can occupy the whole space reserved to the window, but we can still resize it and move it and see the status bar.
### Download
- [git branch](https://codeberg.org/Kana/dwl/src/branch/controlled_fullscreen)
- [main 2025-11-13](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/controlled_fullscreen/controlled_fullscreen.patch)
- [2025-10-08](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/controlled_fullscreen/controlled_fullscreen_2025_10_08.patch)
### Authors
- [André Desgualdo Pereira](https://codeberg.org/Kana)

View File

@ -0,0 +1,132 @@
From e0cecc228d436425c0d921a1eec5e0370d24613d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Desgualdo=20Pereira?= <desgua@gmail.com>
Date: Thu, 13 Nov 2025 09:09:22 -0300
Subject: [PATCH] controlled fullscreen fix
---
dwl.c | 32 ++++++++++++--------------------
1 file changed, 12 insertions(+), 20 deletions(-)
diff --git a/dwl.c b/dwl.c
index 12f441e..4f124eb 100644
--- a/dwl.c
+++ b/dwl.c
@@ -518,9 +518,6 @@ arrange(Monitor *m)
}
}
- wlr_scene_node_set_enabled(&m->fullscreen_bg->node,
- (c = focustop(m)) && c->isfullscreen);
-
strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol));
/* We move all clients (except fullscreen and unmanaged) to LyrTile while
@@ -878,7 +875,7 @@ commitnotify(struct wl_listener *listener, void *data)
return;
}
- resize(c, c->geom, (c->isfloating && !c->isfullscreen));
+ resize(c, c->geom, (c->isfloating));
/* mark a pending resize as completed */
if (c->resize && c->resize <= c->surface.xdg->current.configure_serial)
@@ -1490,7 +1487,7 @@ focusstack(const Arg *arg)
{
/* Focus the next or previous client (in tiling order) on selmon */
Client *c, *sel = focustop(selmon);
- if (!sel || (sel->isfullscreen && !client_has_children(sel)))
+ if (!sel)
return;
if (arg->i > 0) {
wl_list_for_each(c, &sel->link, link) {
@@ -1826,7 +1823,7 @@ monocle(Monitor *m)
int n = 0;
wl_list_for_each(c, &clients, link) {
- if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
+ if (!VISIBLEON(c, m) || c->isfloating)
continue;
resize(c, m->w, 0);
n++;
@@ -1957,7 +1954,7 @@ moveresize(const Arg *arg)
if (cursor_mode != CurNormal && cursor_mode != CurPressed)
return;
xytonode(cursor->x, cursor->y, NULL, &grabc, NULL, NULL, NULL);
- if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen)
+ if (!grabc || client_is_unmanaged(grabc))
return;
/* Float the window and tell motionnotify to grab it */
@@ -2332,14 +2329,12 @@ setcursorshape(struct wl_listener *listener, void *data)
void
setfloating(Client *c, int floating)
{
- Client *p = client_get_parent(c);
c->isfloating = floating;
/* If in floating layout do not change the client's layer */
if (!c->mon || !client_surface(c)->mapped || !c->mon->lt[c->mon->sellt]->arrange)
return;
- wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen ||
- (p && p->isfullscreen) ? LyrFS
- : c->isfloating ? LyrFloat : LyrTile]);
+ wlr_scene_node_reparent(&c->scene->node,
+ layers[c->isfloating ? LyrFloat : LyrTile]);
arrange(c->mon);
printstatus();
}
@@ -2352,12 +2347,12 @@ setfullscreen(Client *c, int fullscreen)
return;
c->bw = fullscreen ? 0 : borderpx;
client_set_fullscreen(c, fullscreen);
- wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen
- ? LyrFS : c->isfloating ? LyrFloat : LyrTile]);
+ wlr_scene_node_reparent(&c->scene->node,
+ layers[c->isfloating ? LyrFloat : LyrTile]);
if (fullscreen) {
c->prev = c->geom;
- resize(c, c->mon->m, 0);
+ resize(c, c->mon->w, 0);
} else {
/* restore previous size instead of arrange for floating windows since
* client positions are set by the user and cannot be recalculated */
@@ -2716,7 +2711,7 @@ tile(Monitor *m)
Client *c;
wl_list_for_each(c, &clients, link)
- if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen)
+ if (VISIBLEON(c, m) && !c->isfloating)
n++;
if (n == 0)
return;
@@ -2727,7 +2722,7 @@ tile(Monitor *m)
mw = m->w.width;
i = my = ty = 0;
wl_list_for_each(c, &clients, link) {
- if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
+ if (!VISIBLEON(c, m) || c->isfloating)
continue;
if (i < m->nmaster) {
resize(c, (struct wlr_box){.x = m->w.x, .y = m->w.y + my, .width = mw,
@@ -2747,7 +2742,7 @@ togglefloating(const Arg *arg)
{
Client *sel = focustop(selmon);
/* return if fullscreen */
- if (sel && !sel->isfullscreen)
+ if (sel)
setfloating(sel, !sel->isfloating);
}
@@ -2902,9 +2897,6 @@ updatemons(struct wl_listener *listener, void *data)
arrangelayers(m);
/* 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);
/* Try to re-set the gamma LUT when updating monitors,
* it's only really needed when enabling a disabled output, but meh. */
--
2.51.0

View File

@ -0,0 +1,140 @@
From 33e9b8a227b63e344407c1e4d137b574483cbd1e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Desgualdo=20Pereira?= <desgua@gmail.com>
Date: Wed, 8 Oct 2025 17:38:00 -0300
Subject: [PATCH] controlled fullscreen
---
dwl.c | 33 ++++++++++++---------------------
1 file changed, 12 insertions(+), 21 deletions(-)
diff --git a/dwl.c b/dwl.c
index 12f441e..c74380d 100644
--- a/dwl.c
+++ b/dwl.c
@@ -518,9 +518,6 @@ arrange(Monitor *m)
}
}
- wlr_scene_node_set_enabled(&m->fullscreen_bg->node,
- (c = focustop(m)) && c->isfullscreen);
-
strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol));
/* We move all clients (except fullscreen and unmanaged) to LyrTile while
@@ -878,7 +875,7 @@ commitnotify(struct wl_listener *listener, void *data)
return;
}
- resize(c, c->geom, (c->isfloating && !c->isfullscreen));
+ resize(c, c->geom, (c->isfloating));
/* mark a pending resize as completed */
if (c->resize && c->resize <= c->surface.xdg->current.configure_serial)
@@ -1490,7 +1487,7 @@ focusstack(const Arg *arg)
{
/* Focus the next or previous client (in tiling order) on selmon */
Client *c, *sel = focustop(selmon);
- if (!sel || (sel->isfullscreen && !client_has_children(sel)))
+ if (!sel)
return;
if (arg->i > 0) {
wl_list_for_each(c, &sel->link, link) {
@@ -1826,7 +1823,7 @@ monocle(Monitor *m)
int n = 0;
wl_list_for_each(c, &clients, link) {
- if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
+ if (!VISIBLEON(c, m) || c->isfloating)
continue;
resize(c, m->w, 0);
n++;
@@ -1957,7 +1954,7 @@ moveresize(const Arg *arg)
if (cursor_mode != CurNormal && cursor_mode != CurPressed)
return;
xytonode(cursor->x, cursor->y, NULL, &grabc, NULL, NULL, NULL);
- if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen)
+ if (!grabc || client_is_unmanaged(grabc))
return;
/* Float the window and tell motionnotify to grab it */
@@ -2332,14 +2329,12 @@ setcursorshape(struct wl_listener *listener, void *data)
void
setfloating(Client *c, int floating)
{
- Client *p = client_get_parent(c);
c->isfloating = floating;
/* If in floating layout do not change the client's layer */
if (!c->mon || !client_surface(c)->mapped || !c->mon->lt[c->mon->sellt]->arrange)
return;
- wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen ||
- (p && p->isfullscreen) ? LyrFS
- : c->isfloating ? LyrFloat : LyrTile]);
+ wlr_scene_node_reparent(&c->scene->node,
+ layers[c->isfloating ? LyrFloat : LyrTile]);
arrange(c->mon);
printstatus();
}
@@ -2352,12 +2347,12 @@ setfullscreen(Client *c, int fullscreen)
return;
c->bw = fullscreen ? 0 : borderpx;
client_set_fullscreen(c, fullscreen);
- wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen
- ? LyrFS : c->isfloating ? LyrFloat : LyrTile]);
+ wlr_scene_node_reparent(&c->scene->node,
+ layers[c->isfloating ? LyrFloat : LyrTile]);
if (fullscreen) {
c->prev = c->geom;
- resize(c, c->mon->m, 0);
+ resize(c, c->mon->w, 0);
} else {
/* restore previous size instead of arrange for floating windows since
* client positions are set by the user and cannot be recalculated */
@@ -2413,7 +2408,6 @@ setmon(Client *c, Monitor *m, uint32_t newtags)
/* Make sure window actually overlaps with the monitor */
resize(c, c->geom, 0);
c->tags = newtags ? newtags : m->tagset[m->seltags]; /* assign tags of target monitor */
- setfullscreen(c, c->isfullscreen); /* This will call arrange(c->mon) */
setfloating(c, c->isfloating);
}
focusclient(focustop(selmon), 1);
@@ -2716,7 +2710,7 @@ tile(Monitor *m)
Client *c;
wl_list_for_each(c, &clients, link)
- if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen)
+ if (VISIBLEON(c, m) && !c->isfloating)
n++;
if (n == 0)
return;
@@ -2727,7 +2721,7 @@ tile(Monitor *m)
mw = m->w.width;
i = my = ty = 0;
wl_list_for_each(c, &clients, link) {
- if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
+ if (!VISIBLEON(c, m) || c->isfloating)
continue;
if (i < m->nmaster) {
resize(c, (struct wlr_box){.x = m->w.x, .y = m->w.y + my, .width = mw,
@@ -2747,7 +2741,7 @@ togglefloating(const Arg *arg)
{
Client *sel = focustop(selmon);
/* return if fullscreen */
- if (sel && !sel->isfullscreen)
+ if (sel)
setfloating(sel, !sel->isfloating);
}
@@ -2902,9 +2896,6 @@ updatemons(struct wl_listener *listener, void *data)
arrangelayers(m);
/* 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);
/* Try to re-set the gamma LUT when updating monitors,
* it's only really needed when enabling a disabled output, but meh. */
--
2.51.0

View File

@ -0,0 +1,9 @@
### Description
Deck is a dwl-layout which is inspired by the dwm Deck layout (which is inspired by TTWM window manager). It applies the monocle-layout to the clients in the stack. The master-client is still visible. The stacked clients are like a deck of cards, hence the name.
### Download
- [git branch](https://codeberg.org/Kana/dwl/src/branch/decklayout)
- [main 2025-10-08](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/decklayout/decklayout.patch)
### Authors
- [André Desgualdo Pereira](https://codeberg.org/Kana)

View File

@ -0,0 +1,112 @@
From 095439425e64f2567f141d5d941178b148ef0d3a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Desgualdo=20Pereira?= <desgua@gmail.com>
Date: Sun, 12 Oct 2025 11:44:26 -0300
Subject: [PATCH] decklayout
---
config.def.h | 2 ++
dwl.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 63 insertions(+)
diff --git a/config.def.h b/config.def.h
index 95c2afa..cc846eb 100644
--- a/config.def.h
+++ b/config.def.h
@@ -34,6 +34,7 @@ static const Layout layouts[] = {
{ "[]=", tile },
{ "><>", NULL }, /* no layout function means floating behavior */
{ "[M]", monocle },
+ { "[E]", deck },
};
/* monitors */
@@ -139,6 +140,7 @@ static const Key keys[] = {
{ MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[0]} },
{ MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} },
{ MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} },
+ { MODKEY, XKB_KEY_a, setlayout, {.v = &layouts[3]} },
{ MODKEY, XKB_KEY_space, setlayout, {0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} },
{ MODKEY, XKB_KEY_e, togglefullscreen, {0} },
diff --git a/dwl.c b/dwl.c
index 12f441e..227004f 100644
--- a/dwl.c
+++ b/dwl.c
@@ -278,6 +278,7 @@ static void destroylayersurfacenotify(struct wl_listener *listener, void *data);
static void destroylock(SessionLock *lock, int unlocked);
static void destroylocksurface(struct wl_listener *listener, void *data);
static void destroynotify(struct wl_listener *listener, void *data);
+static void deck(Monitor *m);
static void destroypointerconstraint(struct wl_listener *listener, void *data);
static void destroysessionlock(struct wl_listener *listener, void *data);
static void destroykeyboardgroup(struct wl_listener *listener, void *data);
@@ -1837,6 +1838,66 @@ monocle(Monitor *m)
wlr_scene_node_raise_to_top(&c->scene->node);
}
+void
+deck(Monitor *m)
+{
+ unsigned int mw, my;
+ int i, n = 0;
+ Client *c;
+
+ /* count tiled clients */
+ wl_list_for_each(c, &clients, link)
+
+ /* if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen) */
+ if (VISIBLEON(c, m) && !c->isfloating)
+ n++;
+ if (n == 0)
+ return;
+
+ /* set master width */
+ if (n > m->nmaster)
+ mw = m->nmaster ? (int)roundf(m->w.width * m->mfact) : 0;
+ else
+ mw = m->w.width;
+
+ /* update layout symbol with number of stack windows */
+ /* use the following rules to count only the windows on the deck
+ if (n > m->nmaster)
+ snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n - m->nmaster);
+ else
+ snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); */
+
+ /* or this one to count all windows on the tag */
+ snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n);
+
+ i = my = 0;
+ wl_list_for_each(c, &clients, link) {
+ /* if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) */
+ if (!VISIBLEON(c, m) || c->isfloating)
+ continue;
+
+ if (i < m->nmaster) {
+ /* master clients */
+ resize(c, (struct wlr_box){
+ .x = m->w.x,
+ .y = m->w.y + my,
+ .width = mw,
+ .height = (m->w.height - my) / (MIN(n, m->nmaster) - i)
+ }, 0);
+ my += c->geom.height;
+ } else {
+ /* deck clients: overlap in stack area */
+ resize(c, (struct wlr_box){
+ .x = m->w.x + mw,
+ .y = m->w.y,
+ .width = m->w.width - mw,
+ .height = m->w.height
+ }, 0);
+ }
+ i++;
+ }
+}
+
void
motionabsolute(struct wl_listener *listener, void *data)
{
--
2.51.0

View File

@ -0,0 +1,14 @@
### Description
By default, dwl responds to client requests to client messages by setting the urgency bit on the named window.
This patch changes the focus to the window instead.
Both behaviours are legitimate according to the cursed spec.
This is the approximately the equivalent of the focusonactive patch of dwm.
If you want a more controlled behavior, for example setting which clients can focus, check [activation-rule patch](https://codeberg.org/sevz/dwl-patches/src/branch/activation-rules).
### Download
- [git branch](https://codeberg.org/Kana/dwl/src/branch/focusonurgent)
- [main 2025-10-08](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/focusonurgent/focusonurgent.patch)
### Authors
- [André Desgualdo Pereira](https://codeberg.org/Kana)

View File

@ -0,0 +1,29 @@
From 3613d9a6342fc85279a79ba203f25ff39fc0d8e4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Desgualdo=20Pereira?= <desgua@gmail.com>
Date: Wed, 29 Oct 2025 09:06:00 -0300
Subject: [PATCH] add focusonurgent patch
---
dwl.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/dwl.c b/dwl.c
index 12f441e..5620f66 100644
--- a/dwl.c
+++ b/dwl.c
@@ -2097,8 +2097,10 @@ printstatus(void)
if (c->mon != m)
continue;
occ |= c->tags;
- if (c->isurgent)
- urg |= c->tags;
+ if (c->isurgent) {
+ view(&((Arg){ .ui = c->tags }));
+ focusclient(c, 1);
+ }
}
if ((c = focustop(m))) {
printf("%s title %s\n", m->wlr_output->name, client_get_title(c));
--
2.51.0

View File

@ -4,16 +4,18 @@ Arranges windows in a grid. Except it adjusts the number of windows in the first
On widescreens (w > 2*h), it splits to three columns before splitting rows.
### Download
- [2024-09-18](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/gaplessgrid/gaplessgrid.patch)
- [2025-10-31](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/gaplessgrid/gaplessgrid.patch)
- [2024-09-18](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/gaplessgrid/gaplessgrid-20240918.patch)
- [2024-07-14](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/gaplessgrid/gaplessgrid-20240714.patch)
- [2023-08-01](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/gaplessgrid/gaplessgrid-20230801.patch)
- [git branch](https://codeberg.org/dhruva_sambrani/dwl/src/branch/gaplessgrid)
- [git branch](https://codeberg.org/Kana/dwl/src/branch/gaplessgrid)
## Pre-codeberg
- [2023-11-14](https://github.com/djpohly/dwl/compare/main...Sneethe:gaplessgrid.patch)
- [2021-07-27](https://github.com/djpohly/dwl/compare/main...vnepogodin:gaplessgrid.patch)
### Authors
- [André Desgualdo Pereira](https://codeberg.org/Kana)
- [Dhruva Sambrani](https://codeberg.org/dhruva_sambrani/) (Revived to codeberg)
- [Sneethe](https://github.com/Sneethe/)
- [Vladislav Nepogodin](https://github.com/vnepogodin)
- [Dhruva Sambrani](https://codeberg.org/dhruva_sambrani/) (Revived to codeberg)

View File

@ -0,0 +1,89 @@
diff --git a/config.def.h b/config.def.h
index 22d2171..014a909 100644
--- a/config.def.h
+++ b/config.def.h
@@ -34,6 +34,7 @@ static const Layout layouts[] = {
{ "[]=", tile },
{ "><>", NULL }, /* no layout function means floating behavior */
{ "[M]", monocle },
+ { "###", gaplessgrid },
};
/* monitors */
@@ -139,6 +140,7 @@ static const Key keys[] = {
{ MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[0]} },
{ MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} },
{ MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} },
+ { MODKEY, XKB_KEY_g, setlayout, {.v = &layouts[3]} },
{ MODKEY, XKB_KEY_space, setlayout, {0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} },
{ MODKEY, XKB_KEY_e, togglefullscreen, {0} },
diff --git a/dwl.c b/dwl.c
index dc0c861..875d8cd 100644
--- a/dwl.c
+++ b/dwl.c
@@ -292,6 +292,7 @@ static void focusstack(const Arg *arg);
static Client *focustop(Monitor *m);
static void fullscreennotify(struct wl_listener *listener, void *data);
static void gpureset(struct wl_listener *listener, void *data);
+static void gaplessgrid(Monitor *m);
static void handlesig(int signo);
static void incnmaster(const Arg *arg);
static void inputdevice(struct wl_listener *listener, void *data);
@@ -1510,6 +1511,56 @@ handlesig(int signo)
}
}
+void
+gaplessgrid(Monitor *m)
+{
+ unsigned int n = 0, i = 0, ch, cw, cn, rn, rows, cols;
+ Client *c;
+
+ wl_list_for_each(c, &clients, link)
+ if (VISIBLEON(c, m) && !c->isfloating)
+ n++;
+ if (n == 0)
+ return;
+
+ /* grid dimensions */
+ for (cols = 0; cols <= (n / 2); cols++)
+ if ((cols * cols) >= n)
+ break;
+
+ if (n == 5) /* set layout against the general calculation: not 1:2:2, but 2:3 */
+ cols = 2;
+
+ /* widescreen is better if 3 columns */
+ if (n >= 3 && n <= 6 && (m->w.width / m->w.height) > 1)
+ cols = 3;
+
+ rows = n / cols;
+
+ /* window geometries */
+ cw = cols ? m->w.width / cols : m->w.width;
+ cn = 0; /* current column number */
+ rn = 0; /* current row number */
+ wl_list_for_each(c, &clients, link) {
+ unsigned int cx, cy;
+ if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
+ continue;
+
+ if ((i / rows + 1) > (cols - n % cols))
+ rows = n / cols + 1;
+ ch = rows ? m->w.height / rows : m->w.height;
+ cx = m->w.x + cn * cw;
+ cy = m->w.y + rn * ch;
+ resize(c, (struct wlr_box) { cx, cy, cw, ch}, 0);
+ rn++;
+ if (rn >= rows) {
+ rn = 0;
+ cn++;
+ }
+ i++;
+ }
+}
+
void
incnmaster(const Arg *arg)
{

View File

@ -1,5 +1,15 @@
From 38427b81367e8b4d2aad708a1d463bc793aac65e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Desgualdo=20Pereira?= <desgua@gmail.com>
Date: Fri, 31 Oct 2025 15:46:48 -0300
Subject: [PATCH] take on gaplessgrid and fix minor indentation and typos
---
config.def.h | 2 ++
dwl.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 53 insertions(+)
diff --git a/config.def.h b/config.def.h
index 22d2171..014a909 100644
index 95c2afa..2054107 100644
--- a/config.def.h
+++ b/config.def.h
@@ -34,6 +34,7 @@ static const Layout layouts[] = {
@ -19,10 +29,10 @@ index 22d2171..014a909 100644
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} },
{ MODKEY, XKB_KEY_e, togglefullscreen, {0} },
diff --git a/dwl.c b/dwl.c
index dc0c861..875d8cd 100644
index 12f441e..7ab5323 100644
--- a/dwl.c
+++ b/dwl.c
@@ -292,6 +292,7 @@ static void focusstack(const Arg *arg);
@@ -288,6 +288,7 @@ static void focusstack(const Arg *arg);
static Client *focustop(Monitor *m);
static void fullscreennotify(struct wl_listener *listener, void *data);
static void gpureset(struct wl_listener *listener, void *data);
@ -30,14 +40,14 @@ index dc0c861..875d8cd 100644
static void handlesig(int signo);
static void incnmaster(const Arg *arg);
static void inputdevice(struct wl_listener *listener, void *data);
@@ -1510,6 +1511,56 @@ handlesig(int signo)
}
@@ -1566,6 +1567,56 @@ handlesig(int signo)
quit(NULL);
}
+void
+gaplessgrid(Monitor *m)
+{
+ unsigned int n = 0, i = 0, ch, cw, cn, rn, rows, cols;
+ int n = 0, i = 0, ch, cw, cn, rn, rows, cols;
+ Client *c;
+
+ wl_list_for_each(c, &clients, link)
@ -54,12 +64,12 @@ index dc0c861..875d8cd 100644
+ if (n == 5) /* set layout against the general calculation: not 1:2:2, but 2:3 */
+ cols = 2;
+
+ /* widescreen is better if 3 columns */
+ if (n >= 3 && n <= 6 && (m->w.width / m->w.height) > 1)
+ cols = 3;
+ /* widescreen is better if 3 columns */
+ if (n >= 3 && n <= 6 && (m->w.width / m->w.height) > 1)
+ cols = 3;
+
+ rows = n / cols;
+
+
+ /* window geometries */
+ cw = cols ? m->w.width / cols : m->w.width;
+ cn = 0; /* current column number */
@ -87,3 +97,6 @@ index dc0c861..875d8cd 100644
void
incnmaster(const Arg *arg)
{
--
2.51.0

31
patches/gridall/README.md Normal file
View File

@ -0,0 +1,31 @@
### Description
This patch introduces a new way to view and focus on a client:
1. Press the shortcut defined on config.h once to view all windows across all tags using the gaplessgrid layout.
2. Press it again to view the selected window in its original tag using the monocle layout.
The idea is to press a shortcut (e.g., `Mod+g`) to display all windows, then after
selecting one, press it again to jump to that window's tag and focus on it.
This patch depends on two other patches: [gaplessgrid](https://codeberg.org/dwl/dwl-patches/src/branch/main/patches/gaplessgrid)
and [winview](https://codeberg.org/dwl/dwl-patches/src/branch/main/patches/winview).
If you already have the other two patches added to your `dwl` build, you can simply
apply `gridall.diff`. If not, I've also created a combined patch
that includes winview, gaplessgrid, and gridall `winview+gaplessgrid+gridall.diff`.
Additionally, this patch serves as an example of how to combine two functions
under a single shortcut, allowing users to further customize their `dwl` setup.
**Note:** Sometimes a window may get "lost" in one of the tags. This patch helps
you quickly find and focus on it without having to move it or change its tag.
### Download
- [git branch](https://codeberg.org/Kana/dwl/src/branch/gridall)
- [main gridall 2025-10-08](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/gridall/gridall.diff)
- [main winview+gaplessgrid+gridall 2025-10-08](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/gridall/winview+gaplessgrid+gridall.diff)
### Authors
- [André Desgualdo Pereira](https://codeberg.org/Kana)

View File

@ -0,0 +1,67 @@
diff -up ../dwl_winview_gaplessgrid/config.def.h ./config.def.h
--- ../dwl_winview_gaplessgrid/config.def.h 2025-10-09 09:00:33.058449462 -0300
+++ ./config.def.h 2025-10-09 08:52:57.021707301 -0300
@@ -144,6 +144,7 @@ static const Key keys[] = {
{ MODKEY, XKB_KEY_space, setlayout, {0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} },
{ MODKEY, XKB_KEY_e, togglefullscreen, {0} },
+ { MODKEY, XKB_KEY_a, togglegridall, {0} },
{ MODKEY, XKB_KEY_0, view, {.ui = ~0} },
{ MODKEY, XKB_KEY_o, winview, {0}},
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} },
diff -up ../dwl_winview_gaplessgrid/dwl.c ./dwl.c
--- ../dwl_winview_gaplessgrid/dwl.c 2025-10-09 09:00:56.123374462 -0300
+++ ./dwl.c 2025-10-09 08:55:16.022929525 -0300
@@ -353,6 +353,7 @@ static Monitor *xytomon(double x, double
static void xytonode(double x, double y, struct wlr_surface **psurface,
Client **pc, LayerSurface **pl, double *nx, double *ny);
static void zoom(const Arg *arg);
+static void togglegridall(const Arg *arg);
/* variables */
static pid_t child_pid = -1;
@@ -407,6 +408,7 @@ static struct wlr_output_layout *output_
static struct wlr_box sgeom;
static struct wl_list mons;
static Monitor *selmon;
+static int togglegridallstate = 0;
/* global event handlers */
static struct wl_listener cursor_axis = {.notify = axisnotify};
@@ -3066,6 +3068,36 @@ winview(const Arg *a) {
return;
}
+void
+togglegridall(const Arg *arg)
+{
+ if (togglegridallstate == 0) {
+ /* hide the statusbar when activating the gridall
+ const char *dwlb_hide_cmd[] = { "dwlb", "-hide", "eDP-1", NULL };
+ if (fork() == 0) {
+ setsid();
+ execvp(dwlb_hide_cmd[0], (char *const *)dwlb_hide_cmd);
+ perror("execvp");
+ _exit(1);
+ } */
+ setlayout(&(const Arg){.v = &layouts[3]});
+ view(&(Arg){.ui = ~0});
+ } else {
+ /*const char *dwlb_show_cmd[] = { "dwlb", "-show", "eDP-1", NULL };*/
+ winview(&(const Arg){0});
+ setlayout(&(const Arg){.v = &layouts[0]});
+ /* show the statusbar again after choosing the client window to focus
+ if (fork() == 0) {
+ setsid();
+ execvp(dwlb_show_cmd[0], (char *const *)dwlb_show_cmd);
+ perror("execvp");
+ _exit(1);
+ } */
+ }
+ togglegridallstate = !togglegridallstate;
+}
+
+
Monitor *
xytomon(double x, double y)
{

View File

@ -0,0 +1,162 @@
diff -up ../../dwl_my_contribution/dwl/config.def.h ./config.def.h
--- ../../dwl_my_contribution/dwl/config.def.h 2025-10-09 08:47:04.900053193 -0300
+++ ./config.def.h 2025-10-09 08:52:57.021707301 -0300
@@ -34,6 +34,7 @@ static const Layout layouts[] = {
{ "[]=", tile },
{ "><>", NULL }, /* no layout function means floating behavior */
{ "[M]", monocle },
+ { "###", gaplessgrid },
};
/* monitors */
@@ -139,10 +140,13 @@ static const Key keys[] = {
{ MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[0]} },
{ MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} },
{ MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} },
+ { MODKEY, XKB_KEY_g, setlayout, {.v = &layouts[3]} },
{ MODKEY, XKB_KEY_space, setlayout, {0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} },
{ MODKEY, XKB_KEY_e, togglefullscreen, {0} },
+ { MODKEY, XKB_KEY_a, togglegridall, {0} },
{ MODKEY, XKB_KEY_0, view, {.ui = ~0} },
+ { MODKEY, XKB_KEY_o, winview, {0}},
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} },
{ MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} },
{ MODKEY, XKB_KEY_period, focusmon, {.i = WLR_DIRECTION_RIGHT} },
diff -up ../../dwl_my_contribution/dwl/dwl.c ./dwl.c
--- ../../dwl_my_contribution/dwl/dwl.c 2025-10-09 08:47:04.900053193 -0300
+++ ./dwl.c 2025-10-09 08:55:16.022929525 -0300
@@ -288,6 +288,7 @@ static void focusstack(const Arg *arg);
static Client *focustop(Monitor *m);
static void fullscreennotify(struct wl_listener *listener, void *data);
static void gpureset(struct wl_listener *listener, void *data);
+static void gaplessgrid(Monitor *m);
static void handlesig(int signo);
static void incnmaster(const Arg *arg);
static void inputdevice(struct wl_listener *listener, void *data);
@@ -347,10 +348,12 @@ static void urgent(struct wl_listener *l
static void view(const Arg *arg);
static void virtualkeyboard(struct wl_listener *listener, void *data);
static void virtualpointer(struct wl_listener *listener, void *data);
+static void winview(const Arg *a);
static Monitor *xytomon(double x, double y);
static void xytonode(double x, double y, struct wlr_surface **psurface,
Client **pc, LayerSurface **pl, double *nx, double *ny);
static void zoom(const Arg *arg);
+static void togglegridall(const Arg *arg);
/* variables */
static pid_t child_pid = -1;
@@ -405,6 +408,7 @@ static struct wlr_output_layout *output_
static struct wlr_box sgeom;
static struct wl_list mons;
static Monitor *selmon;
+static int togglegridallstate = 0;
/* global event handlers */
static struct wl_listener cursor_axis = {.notify = axisnotify};
@@ -1567,6 +1571,56 @@ handlesig(int signo)
}
void
+gaplessgrid(Monitor *m)
+{
+ unsigned int n = 0, i = 0, ch, cw, cn, rn, rows, cols;
+ Client *c;
+
+ wl_list_for_each(c, &clients, link)
+ if (VISIBLEON(c, m) && !c->isfloating)
+ n++;
+ if (n == 0)
+ return;
+
+ /* grid dimensions */
+ for (cols = 0; cols <= (n / 2); cols++)
+ if ((cols * cols) >= n)
+ break;
+
+ if (n == 5) /* set layout against the general calculation: not 1:2:2, but 2:3 */
+ cols = 2;
+
+ /* widescreen is better if 3 columns */
+ if (n >= 3 && n <= 6 && (m->w.width / m->w.height) > 1)
+ cols = 3;
+
+ rows = n / cols;
+
+ /* window geometries */
+ cw = cols ? m->w.width / cols : m->w.width;
+ cn = 0; /* current column number */
+ rn = 0; /* current row number */
+ wl_list_for_each(c, &clients, link) {
+ unsigned int cx, cy;
+ if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
+ continue;
+
+ if ((i / rows + 1) > (cols - n % cols))
+ rows = n / cols + 1;
+ ch = rows ? m->w.height / rows : m->w.height;
+ cx = m->w.x + cn * cw;
+ cy = m->w.y + rn * ch;
+ resize(c, (struct wlr_box) { cx, cy, cw, ch}, 0);
+ rn++;
+ if (rn >= rows) {
+ rn = 0;
+ cn++;
+ }
+ i++;
+ }
+}
+
+void
incnmaster(const Arg *arg)
{
if (!arg || !selmon)
@@ -3003,6 +3057,47 @@ virtualpointer(struct wl_listener *liste
wlr_cursor_map_input_to_output(cursor, device, event->suggested_output);
}
+void
+winview(const Arg *a) {
+ Arg b = {0};
+ Client *sel = focustop(selmon);
+ if(!sel)
+ return;
+ b.ui = sel -> tags;
+ view(&b);
+ return;
+}
+
+void
+togglegridall(const Arg *arg)
+{
+ if (togglegridallstate == 0) {
+ /* hide the statusbar when activating the gridall
+ const char *dwlb_hide_cmd[] = { "dwlb", "-hide", "eDP-1", NULL };
+ if (fork() == 0) {
+ setsid();
+ execvp(dwlb_hide_cmd[0], (char *const *)dwlb_hide_cmd);
+ perror("execvp");
+ _exit(1);
+ } */
+ setlayout(&(const Arg){.v = &layouts[3]});
+ view(&(Arg){.ui = ~0});
+ } else {
+ /*const char *dwlb_show_cmd[] = { "dwlb", "-show", "eDP-1", NULL };*/
+ winview(&(const Arg){0});
+ setlayout(&(const Arg){.v = &layouts[0]});
+ /* show the statusbar again after choosing the client window to focus
+ if (fork() == 0) {
+ setsid();
+ execvp(dwlb_show_cmd[0], (char *const *)dwlb_show_cmd);
+ perror("execvp");
+ _exit(1);
+ } */
+ }
+ togglegridallstate = !togglegridallstate;
+}
+
+
Monitor *
xytomon(double x, double y)
{

View File

@ -0,0 +1,14 @@
# Individual keyboard configurations
Apply custom rules for individual keyboards. Under the hood, this is implemented by removing the normal grouping of keyboards.
The provided example will assign a German input layout to one keyboard and an English one to any other.
## Download
- [git branch](https://codeberg.org/biosfood/dwl/src/branch/individual-keyboard-configurations)
- [2025-11-14](./individual-keyboard-configurations.patch)
## Authors
- [Lukas Eisenhauer](https://codeberg.org/biosfood)

View File

@ -0,0 +1,366 @@
From 72d166c481d9bec66506c3064efb9ca650c05f87 Mon Sep 17 00:00:00 2001
From: biosfood <mail@lukas-eisenhauer.de>
Date: Sun, 12 Oct 2025 20:24:24 +0200
Subject: [PATCH] individual keyboard rules
---
config.def.h | 18 ++++--
dwl.c | 160 ++++++++++++++++++++++++++-------------------------
2 files changed, 93 insertions(+), 85 deletions(-)
diff --git a/config.def.h b/config.def.h
index 95c2afa..fc33b55 100644
--- a/config.def.h
+++ b/config.def.h
@@ -52,12 +52,18 @@ static const MonitorRule monrules[] = {
};
/* keyboard */
-static const struct xkb_rule_names xkb_rules = {
- /* can specify fields: rules, model, layout, variant, options */
- /* example:
- .options = "ctrl:nocaps",
- */
- .options = NULL,
+static const KeyboardRule keyboard_rules[] = {
+ {
+ "AT Translated Set 2 keyboard",
+ { .layout = "de", .options = "caps:super" }
+ },
+ {
+ "", // fallback
+ {
+ .layout = "en",
+ /* can specify fields: rules, model, layout, variant, options */
+ }
+ },
};
static const int repeat_rate = 25;
diff --git a/dwl.c b/dwl.c
index 12f441e..1871299 100644
--- a/dwl.c
+++ b/dwl.c
@@ -8,6 +8,7 @@
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
@@ -149,7 +150,8 @@ typedef struct {
} Key;
typedef struct {
- struct wlr_keyboard_group *wlr_group;
+ struct wl_list link;
+ struct wlr_keyboard *wlr_keyboard;
int nsyms;
const xkb_keysym_t *keysyms; /* invalid if nsyms == 0 */
@@ -159,7 +161,12 @@ typedef struct {
struct wl_listener modifiers;
struct wl_listener key;
struct wl_listener destroy;
-} KeyboardGroup;
+} Keyboard;
+
+typedef struct {
+ const char *name;
+ struct xkb_rule_names xkb_rules;
+} KeyboardRule;
typedef struct {
/* Must keep this field first */
@@ -260,7 +267,6 @@ static void commitpopup(struct wl_listener *listener, void *data);
static void createdecoration(struct wl_listener *listener, void *data);
static void createidleinhibitor(struct wl_listener *listener, void *data);
static void createkeyboard(struct wlr_keyboard *keyboard);
-static KeyboardGroup *createkeyboardgroup(void);
static void createlayersurface(struct wl_listener *listener, void *data);
static void createlocksurface(struct wl_listener *listener, void *data);
static void createmon(struct wl_listener *listener, void *data);
@@ -280,7 +286,7 @@ static void destroylocksurface(struct wl_listener *listener, void *data);
static void destroynotify(struct wl_listener *listener, void *data);
static void destroypointerconstraint(struct wl_listener *listener, void *data);
static void destroysessionlock(struct wl_listener *listener, void *data);
-static void destroykeyboardgroup(struct wl_listener *listener, void *data);
+static void destroykeyboard(struct wl_listener *listener, void *data);
static Monitor *dirtomon(enum wlr_direction dir);
static void focusclient(Client *c, int lift);
static void focusmon(const Arg *arg);
@@ -396,7 +402,7 @@ static struct wlr_scene_rect *locked_bg;
static struct wlr_session_lock_v1 *cur_lock;
static struct wlr_seat *seat;
-static KeyboardGroup *kb_group;
+static struct wl_list keyboards;
static unsigned int cursor_mode;
static Client *grabc;
static int grabcx, grabcy; /* client-relative */
@@ -696,6 +702,19 @@ checkidleinhibitor(struct wlr_surface *exclude)
wlr_idle_notifier_v1_set_inhibited(idle_notifier, inhibited);
}
+void
+cleanupkeyboard(struct wl_listener *listener, void *data)
+{
+ Keyboard *kb = wl_container_of(listener, kb, destroy);
+
+ wl_event_source_remove(kb->key_repeat_source);
+ wl_list_remove(&kb->link);
+ wl_list_remove(&kb->modifiers.link);
+ wl_list_remove(&kb->key.link);
+ wl_list_remove(&kb->destroy.link);
+ free(kb);
+}
+
void
cleanup(void)
{
@@ -711,8 +730,6 @@ cleanup(void)
}
wlr_xcursor_manager_destroy(cursor_mgr);
- destroykeyboardgroup(&kb_group->destroy, NULL);
-
/* If it's not destroyed manually, it will cause a use-after-free of wlr_seat.
* Destroy it until it's fixed on the wlroots side */
wlr_backend_destroy(backend);
@@ -940,48 +957,45 @@ createidleinhibitor(struct wl_listener *listener, void *data)
void
createkeyboard(struct wlr_keyboard *keyboard)
{
- /* Set the keymap to match the group keymap */
- wlr_keyboard_set_keymap(keyboard, kb_group->wlr_group->keyboard.keymap);
+ const struct xkb_rule_names *rules;
+ size_t i;
+ for (i = 0; i < LENGTH(keyboard_rules); i++) {
+ if (strcmp(keyboard->base.name, keyboard_rules[i].name) == 0 ||
+ i == LENGTH(keyboard_rules) - 1) {
+ rules = &keyboard_rules[i].xkb_rules;
+ }
+ }
- /* Add the new keyboard to the group */
- wlr_keyboard_group_add_keyboard(kb_group->wlr_group, keyboard);
-}
-KeyboardGroup *
-createkeyboardgroup(void)
-{
- KeyboardGroup *group = ecalloc(1, sizeof(*group));
struct xkb_context *context;
struct xkb_keymap *keymap;
+ Keyboard *kb = keyboard->data = ecalloc(1, sizeof(*kb));
+ kb->wlr_keyboard = keyboard;
- group->wlr_group = wlr_keyboard_group_create();
- group->wlr_group->data = group;
-
- /* Prepare an XKB keymap and assign it to the keyboard group. */
+ /* Prepare an XKB keymap and assign it to the keyboard. */
context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
- if (!(keymap = xkb_keymap_new_from_names(context, &xkb_rules,
- XKB_KEYMAP_COMPILE_NO_FLAGS)))
- die("failed to compile keymap");
+ keymap = xkb_keymap_new_from_names(context, rules,
+ XKB_KEYMAP_COMPILE_NO_FLAGS);
+ if (!keymap)
+ die("createkeyboard: failed to compile keymap");
- wlr_keyboard_set_keymap(&group->wlr_group->keyboard, keymap);
+ wlr_keyboard_set_keymap(keyboard, keymap);
xkb_keymap_unref(keymap);
xkb_context_unref(context);
+ wlr_keyboard_set_repeat_info(keyboard, repeat_rate, repeat_delay);
- wlr_keyboard_set_repeat_info(&group->wlr_group->keyboard, repeat_rate, repeat_delay);
+ /* Here we set up listeners for keyboard events. */
+ LISTEN(&keyboard->events.modifiers, &kb->modifiers, keypressmod);
+ LISTEN(&keyboard->events.key, &kb->key, keypress);
+ LISTEN(&keyboard->base.events.destroy, &kb->destroy, cleanupkeyboard);
- /* Set up listeners for keyboard events */
- LISTEN(&group->wlr_group->keyboard.events.key, &group->key, keypress);
- LISTEN(&group->wlr_group->keyboard.events.modifiers, &group->modifiers, keypressmod);
+ wlr_seat_set_keyboard(seat, keyboard);
- group->key_repeat_source = wl_event_loop_add_timer(event_loop, keyrepeat, group);
+ kb->key_repeat_source = wl_event_loop_add_timer(
+ wl_display_get_event_loop(dpy), keyrepeat, kb);
- /* A seat can only have one keyboard, but this is a limitation of the
- * Wayland protocol - not wlroots. We assign all connected keyboards to the
- * same wlr_keyboard_group, which provides a single wlr_keyboard interface for
- * all of them. Set this combined wlr_keyboard as the seat keyboard.
- */
- wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard);
- return group;
+ /* And add the keyboard to our list of keyboards */
+ wl_list_insert(&keyboards, &kb->link);
}
void
@@ -1372,15 +1386,14 @@ destroysessionlock(struct wl_listener *listener, void *data)
}
void
-destroykeyboardgroup(struct wl_listener *listener, void *data)
+destroykeyboard(struct wl_listener *listener, void *data)
{
- KeyboardGroup *group = wl_container_of(listener, group, destroy);
- wl_event_source_remove(group->key_repeat_source);
- wl_list_remove(&group->key.link);
- wl_list_remove(&group->modifiers.link);
- wl_list_remove(&group->destroy.link);
- wlr_keyboard_group_destroy(group->wlr_group);
- free(group);
+ Keyboard *kb = wl_container_of(listener, kb, destroy);
+ wl_event_source_remove(kb->key_repeat_source);
+ wl_list_remove(&kb->key.link);
+ wl_list_remove(&kb->modifiers.link);
+ wl_list_remove(&kb->destroy.link);
+ free(kb);
}
Monitor *
@@ -1600,8 +1613,7 @@ inputdevice(struct wl_listener *listener, void *data)
* there are no pointer devices, so we always include that capability. */
/* TODO do we actually require a cursor? */
caps = WL_SEAT_CAPABILITY_POINTER;
- if (!wl_list_empty(&kb_group->wlr_group->devices))
- caps |= WL_SEAT_CAPABILITY_KEYBOARD;
+ caps |= WL_SEAT_CAPABILITY_KEYBOARD;
wlr_seat_set_capabilities(seat, caps);
}
@@ -1629,7 +1641,7 @@ keypress(struct wl_listener *listener, void *data)
{
int i;
/* This event is raised when a key is pressed or released. */
- KeyboardGroup *group = wl_container_of(listener, group, key);
+ Keyboard *kb = wl_container_of(listener, kb, key);
struct wlr_keyboard_key_event *event = data;
/* Translate libinput keycode -> xkbcommon */
@@ -1637,10 +1649,10 @@ keypress(struct wl_listener *listener, void *data)
/* Get a list of keysyms based on the keymap for this keyboard */
const xkb_keysym_t *syms;
int nsyms = xkb_state_key_get_syms(
- group->wlr_group->keyboard.xkb_state, keycode, &syms);
+ kb->wlr_keyboard->xkb_state, keycode, &syms);
int handled = 0;
- uint32_t mods = wlr_keyboard_get_modifiers(&group->wlr_group->keyboard);
+ uint32_t mods = wlr_keyboard_get_modifiers(kb->wlr_keyboard);
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
@@ -1650,22 +1662,21 @@ keypress(struct wl_listener *listener, void *data)
for (i = 0; i < nsyms; i++)
handled = keybinding(mods, syms[i]) || handled;
}
-
- if (handled && group->wlr_group->keyboard.repeat_info.delay > 0) {
- group->mods = mods;
- group->keysyms = syms;
- group->nsyms = nsyms;
- wl_event_source_timer_update(group->key_repeat_source,
- group->wlr_group->keyboard.repeat_info.delay);
+ if (handled && kb->wlr_keyboard->repeat_info.delay > 0) {
+ kb->mods = mods;
+ kb->keysyms = syms;
+ kb->nsyms = nsyms;
+ wl_event_source_timer_update(kb->key_repeat_source,
+ kb->wlr_keyboard->repeat_info.delay);
} else {
- group->nsyms = 0;
- wl_event_source_timer_update(group->key_repeat_source, 0);
+ kb->nsyms = 0;
+ wl_event_source_timer_update(kb->key_repeat_source, 0);
}
if (handled)
return;
- wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard);
+ wlr_seat_set_keyboard(seat, kb->wlr_keyboard);
/* Pass unhandled keycodes along to the client. */
wlr_seat_keyboard_notify_key(seat, event->time_msec,
event->keycode, event->state);
@@ -1676,27 +1687,27 @@ keypressmod(struct wl_listener *listener, void *data)
{
/* This event is raised when a modifier key, such as shift or alt, is
* pressed. We simply communicate this to the client. */
- KeyboardGroup *group = wl_container_of(listener, group, modifiers);
+ Keyboard *kb = wl_container_of(listener, kb, modifiers);
- wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard);
+ wlr_seat_set_keyboard(seat, kb->wlr_keyboard);
/* Send modifiers to the client. */
wlr_seat_keyboard_notify_modifiers(seat,
- &group->wlr_group->keyboard.modifiers);
+ &kb->wlr_keyboard->modifiers);
}
int
keyrepeat(void *data)
{
- KeyboardGroup *group = data;
+ Keyboard *kb = data;
int i;
- if (!group->nsyms || group->wlr_group->keyboard.repeat_info.rate <= 0)
+ if (!kb->nsyms || kb->wlr_keyboard->repeat_info.rate <= 0)
return 0;
- wl_event_source_timer_update(group->key_repeat_source,
- 1000 / group->wlr_group->keyboard.repeat_info.rate);
+ wl_event_source_timer_update(kb->key_repeat_source,
+ 1000 / kb->wlr_keyboard->repeat_info.rate);
- for (i = 0; i < group->nsyms; i++)
- keybinding(group->mods, group->keysyms[i]);
+ for (i = 0; i < kb->nsyms; i++)
+ keybinding(kb->mods, kb->keysyms[i]);
return 0;
}
@@ -2448,6 +2459,7 @@ setup(void)
struct sigaction sa = {.sa_flags = SA_RESTART, .sa_handler = handlesig};
sigemptyset(&sa.sa_mask);
+ wl_list_init(&keyboards);
for (i = 0; i < (int)LENGTH(sig); i++)
sigaction(sig[i], &sa, NULL);
@@ -2638,9 +2650,6 @@ setup(void)
wl_signal_add(&seat->events.request_start_drag, &request_start_drag);
wl_signal_add(&seat->events.start_drag, &start_drag);
- kb_group = createkeyboardgroup();
- wl_list_init(&kb_group->destroy.link);
-
output_mgr = wlr_output_manager_v1_create(dpy);
wl_signal_add(&output_mgr->events.apply, &output_mgr_apply);
wl_signal_add(&output_mgr->events.test, &output_mgr_test);
@@ -2982,14 +2991,7 @@ void
virtualkeyboard(struct wl_listener *listener, void *data)
{
struct wlr_virtual_keyboard_v1 *kb = data;
- /* virtual keyboards shouldn't share keyboard group */
- KeyboardGroup *group = createkeyboardgroup();
- /* Set the keymap to match the group keymap */
- wlr_keyboard_set_keymap(&kb->keyboard, group->wlr_group->keyboard.keymap);
- LISTEN(&kb->keyboard.base.events.destroy, &group->destroy, destroykeyboardgroup);
-
- /* Add the new keyboard to the group */
- wlr_keyboard_group_add_keyboard(group->wlr_group, &kb->keyboard);
+ createkeyboard(&kb->keyboard);
}
void
--
2.51.2

View File

@ -1,17 +1,20 @@
From 735440660fd2bccdde982f9c3d758e189ba35e40 Mon Sep 17 00:00:00 2001
From 672df57ab7d5b52f91a712eaf3954d580cdf8ba3 Mon Sep 17 00:00:00 2001
From: Zuki Air <zukirust@gmail.com>
Date: Thu, 7 Aug 2025 13:19:59 +0100
Subject: [PATCH] riverctl patch
make setborderpx apply instantly
add setting border colors via dwlctl
---
.gitignore | 1 +
Makefile | 22 +-
config.def.h | 36 +-
config.def.h | 42 +-
dwl.c | 47 +-
dwlctl.c | 133 +++++
dwlctl.c | 133 ++++
protocols/river-control-unstable-v1.xml | 85 +++
river-control.h | 753 ++++++++++++++++++++++++
7 files changed, 1055 insertions(+), 22 deletions(-)
river-control.h | 789 ++++++++++++++++++++++++
7 files changed, 1094 insertions(+), 25 deletions(-)
create mode 100644 dwlctl.c
create mode 100644 protocols/river-control-unstable-v1.xml
create mode 100644 river-control.h
@ -75,18 +78,25 @@ index 578194f..029dfad 100644
dist: clean
mkdir -p dwl-$(VERSION)
diff --git a/config.def.h b/config.def.h
index 95c2afa..72afbd6 100644
index 95c2afa..ccc3edb 100644
--- a/config.def.h
+++ b/config.def.h
@@ -6,7 +6,7 @@
@@ -6,11 +6,11 @@
/* appearance */
static const int sloppyfocus = 1; /* focus follows mouse */
static const int bypass_surface_visibility = 0; /* 1 means idle inhibitors will disable idle tracking even if it's surface isn't visible */
-static const unsigned int borderpx = 1; /* border pixel of windows */
+static unsigned int borderpx = 1; /* border pixel of windows */
static const float rootcolor[] = COLOR(0x222222ff);
static const float bordercolor[] = COLOR(0x444444ff);
static const float focuscolor[] = COLOR(0x005577ff);
-static const float bordercolor[] = COLOR(0x444444ff);
-static const float focuscolor[] = COLOR(0x005577ff);
-static const float urgentcolor[] = COLOR(0xff0000ff);
+static float bordercolor[] = COLOR(0x444444ff);
+static float focuscolor[] = COLOR(0x005577ff);
+static 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 */
@@ -21,7 +21,14 @@ static const float fullscreen_bg[] = {0.0f, 0.0f, 0.0f, 1.0f}; /* You ca
static int log_level = WLR_ERROR;
@ -480,10 +490,10 @@ index 0000000..aa5fc4d
+</protocol>
diff --git a/river-control.h b/river-control.h
new file mode 100644
index 0000000..59561b6
index 0000000..599cffe
--- /dev/null
+++ b/river-control.h
@@ -0,0 +1,753 @@
@@ -0,0 +1,789 @@
+#include "river-control-unstable-v1-private-protocol.c"
+#include "river-control-unstable-v1-protocol.h"
+#ifdef KEYS_USED
@ -495,6 +505,9 @@ index 0000000..59561b6
+void clear_rules(const Arg*);
+void clear_binds(const Arg*);
+void setborderpx(const Arg*);
+void setfocuscolor(const Arg*);
+void setbordercolor(const Arg*);
+void seturgentcolor(const Arg*);
+struct wl_list arg_str_store;
+struct wl_list rule_str_store;
+struct wl_list rules_list;
@ -534,6 +547,7 @@ index 0000000..59561b6
+ FUNC_STR_ARG_TYPE_INT,
+ FUNC_STR_ARG_TYPE_UINT,
+ FUNC_STR_ARG_TYPE_FLOAT,
+ FUNC_STR_ARG_TYPE_COLOR,
+ FUNC_STR_ARG_TYPE_STRING_ARRAY,
+ FUNC_STR_ARG_TYPE_WLR_DIRECTION,
+ FUNC_STR_ARG_TYPE_LAYOUT,
@ -552,6 +566,9 @@ index 0000000..59561b6
+ { enter_mode, FUNC_STR_ARG_TYPE_STRING_ARRAY, "enter-mode"},
+ { oneshot_mode, FUNC_STR_ARG_TYPE_STRING_ARRAY, "oneshot-mode"},
+ { create_mode_user, FUNC_STR_ARG_TYPE_STRING_ARRAY, "create-mode"},
+ STR(setfocuscolor,FUNC_STR_ARG_TYPE_COLOR),
+ STR(setbordercolor,FUNC_STR_ARG_TYPE_COLOR),
+ STR(seturgentcolor,FUNC_STR_ARG_TYPE_COLOR),
+ STR(setborderpx,FUNC_STR_ARG_TYPE_UINT),
+ STR(setlayout,FUNC_STR_ARG_TYPE_LAYOUT),
+ STR(spawn,FUNC_STR_ARG_TYPE_STRING_ARRAY),
@ -641,10 +658,36 @@ index 0000000..59561b6
+
+void setborderpx(const Arg *arg) {
+ Client *c;
+ Monitor *m;
+ borderpx = arg->ui;
+ wl_list_for_each(c, &clients, link) {
+ c->bw = borderpx;
+ }
+ wl_list_for_each(m, &mons,link) {
+ arrange(m);
+ }
+}
+
+void setfocuscolor(const Arg *arg) {
+ const float color[4] = COLOR(arg->i);
+ int i;
+ for (i = 0; i < 4; i++) {
+ focuscolor[i] = color[i];
+ }
+}
+void setbordercolor(const Arg *arg) {
+ const float color[4] = COLOR(arg->i);
+ int i;
+ for (i = 0; i < 4; i++) {
+ bordercolor[i] = color[i];
+ }
+}
+void seturgentcolor(const Arg *arg) {
+ const float color[4] = COLOR(arg->i);
+ int i;
+ for (i = 0; i < 4; i++) {
+ urgentcolor[i] = color[i];
+ }
+}
+
+void zriver_control_destroy(struct wl_client *client,
@ -1098,7 +1141,10 @@ index 0000000..59561b6
+ args->error = true;
+ args->error_msg = zriver_error_out_of_range;
+ }
+
+ break;
+ case(FUNC_STR_ARG_TYPE_COLOR):
+ arg->i = strtol(argument,NULL,16);
+ break;
+ }
+ }
+ args->argc++;
@ -1238,5 +1284,5 @@ index 0000000..59561b6
+ zriver_arg_list_resource, zriver_control_handle_destory);
+}
--
2.49.1
2.51.0

View File

@ -11,7 +11,8 @@ static const ForceTearingRule force_tearing[] = {
};
```
### Download
- [git branch](https://codeberg.org/korei999/dwl/src/branch/tearing)
- [0.7](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/tearing/tearing-0.7.patch)
- [2025-04-22](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/tearing/tearing.patch)
- [git branch](https://codeberg.org/korei999/dwl/src/branch/tearing)
### Authors
- [korei999](https://codeberg.org/korei999)

View File

@ -0,0 +1,426 @@
From 3c380f4614f7e95134724d846191af5bcf3c4eef Mon Sep 17 00:00:00 2001
From: korei999 <ju7t1xe@gmail.com>
Date: Tue, 11 Nov 2025 00:15:41 +0200
Subject: [PATCH] implement tearing protocol
---
Makefile | 8 +-
config.def.h | 10 +++
dwl.c | 211 +++++++++++++++++++++++++++++++++++++++++++--------
3 files changed, 194 insertions(+), 35 deletions(-)
diff --git a/Makefile b/Makefile
index ff53040..f873069 100644
--- a/Makefile
+++ b/Makefile
@@ -12,7 +12,7 @@ DWLDEVCFLAGS = -g -pedantic -Wall -Wextra -Wdeclaration-after-statement \
-Wfloat-conversion
# CFLAGS / LDFLAGS
-PKGS = wlroots-0.18 wayland-server xkbcommon libinput $(XLIBS)
+PKGS = wlroots-0.18 wayland-server xkbcommon libinput pixman-1 $(XLIBS)
DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(DWLCPPFLAGS) $(DWLDEVCFLAGS) $(CFLAGS)
LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` -lm $(LIBS)
@@ -21,7 +21,8 @@ dwl: dwl.o util.o
$(CC) dwl.o util.o $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@
dwl.o: dwl.c client.h config.h config.mk cursor-shape-v1-protocol.h \
pointer-constraints-unstable-v1-protocol.h wlr-layer-shell-unstable-v1-protocol.h \
- wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h
+ wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h \
+ tearing-control-v1-protocol.h
util.o: util.c util.h
# wayland-scanner is a tool which generates C headers and rigging for Wayland
@@ -45,6 +46,9 @@ wlr-output-power-management-unstable-v1-protocol.h:
xdg-shell-protocol.h:
$(WAYLAND_SCANNER) server-header \
$(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@
+tearing-control-v1-protocol.h:
+ $(WAYLAND_SCANNER) server-header \
+ $(WAYLAND_PROTOCOLS)/staging/tearing-control/tearing-control-v1.xml $@
config.h:
cp config.def.h $@
diff --git a/config.def.h b/config.def.h
index 22d2171..6b27774 100644
--- a/config.def.h
+++ b/config.def.h
@@ -28,6 +28,15 @@ static const Rule rules[] = {
{ "firefox_EXAMPLE", NULL, 1 << 8, 0, -1 }, /* Start on ONLY tag "9" */
};
+/* tearing */
+static int tearing_allowed = 1;
+static int global_tearing = 0;
+static const ForceTearingRule force_tearing_whitelist[] = {
+ {.title = "", .appid = "hl_linux"},
+ {.title = "Warcraft III", .appid = ""},
+ {.title = "", .appid = "gamescope"},
+};
+
/* layout(s) */
static const Layout layouts[] = {
/* symbol arrange function */
@@ -125,6 +134,7 @@ static const char *menucmd[] = { "wmenu-run", NULL };
static const Key keys[] = {
/* Note that Shift changes certain key codes: c -> C, 2 -> at, etc. */
/* modifier key function argument */
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_t, toggleglobaltearing, {0} },
{ MODKEY, XKB_KEY_p, spawn, {.v = menucmd} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, spawn, {.v = termcmd} },
{ MODKEY, XKB_KEY_j, focusstack, {.i = +1} },
diff --git a/dwl.c b/dwl.c
index c717c1d..4c95ede 100644
--- a/dwl.c
+++ b/dwl.c
@@ -50,6 +50,7 @@
#include <wlr/types/wlr_session_lock_v1.h>
#include <wlr/types/wlr_single_pixel_buffer_v1.h>
#include <wlr/types/wlr_subcompositor.h>
+#include <wlr/types/wlr_tearing_control_v1.h>
#include <wlr/types/wlr_viewporter.h>
#include <wlr/types/wlr_virtual_keyboard_v1.h>
#include <wlr/types/wlr_virtual_pointer_v1.h>
@@ -89,6 +90,11 @@ enum { NetWMWindowTypeDialog, NetWMWindowTypeSplash, NetWMWindowTypeToolbar,
NetWMWindowTypeUtility, NetLast }; /* EWMH atoms */
#endif
+typedef struct ForceTearingRule {
+ const char* title;
+ const char* appid;
+} ForceTearingRule;
+
typedef union {
int i;
uint32_t ui;
@@ -141,6 +147,7 @@ typedef struct {
uint32_t tags;
int isfloating, isurgent, isfullscreen;
uint32_t resize; /* configure serial of a pending resize */
+ enum wp_tearing_control_v1_presentation_hint tearing_hint;
} Client;
typedef struct {
@@ -242,6 +249,17 @@ typedef struct {
struct wl_listener destroy;
} SessionLock;
+typedef struct TearingController {
+ struct wlr_tearing_control_v1 *tearing_control;
+ struct wl_listener set_hint;
+ struct wl_listener destroy;
+} TearingController;
+
+typedef struct SendFrameDoneData {
+ struct timespec when;
+ struct Monitor *mon;
+} SendFrameDoneData;
+
/* function declarations */
static void applybounds(Client *c, struct wlr_box *bbox);
static void applyrules(Client *c);
@@ -289,6 +307,7 @@ static void focusclient(Client *c, int lift);
static void focusmon(const Arg *arg);
static void focusstack(const Arg *arg);
static Client *focustop(Monitor *m);
+static void forcetearingrule(Client *c);
static void fullscreennotify(struct wl_listener *listener, void *data);
static void gpureset(struct wl_listener *listener, void *data);
static void handlesig(int signo);
@@ -308,6 +327,7 @@ static void motionnotify(uint32_t time, struct wlr_input_device *device, double
double sy, double sx_unaccel, double sy_unaccel);
static void motionrelative(struct wl_listener *listener, void *data);
static void moveresize(const Arg *arg);
+static int moncantear(Monitor* m);
static void outputmgrapply(struct wl_listener *listener, void *data);
static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test);
static void outputmgrtest(struct wl_listener *listener, void *data);
@@ -322,6 +342,7 @@ static void requeststartdrag(struct wl_listener *listener, void *data);
static void requestmonstate(struct wl_listener *listener, void *data);
static void resize(Client *c, struct wlr_box geo, int interact);
static void run(char *startup_cmd);
+static void sendframedoneiterator(struct wlr_scene_buffer *buffer, int x, int y, void *user_data);
static void setcursor(struct wl_listener *listener, void *data);
static void setcursorshape(struct wl_listener *listener, void *data);
static void setfloating(Client *c, int floating);
@@ -337,9 +358,13 @@ static void spawn(const Arg *arg);
static void startdrag(struct wl_listener *listener, void *data);
static void tag(const Arg *arg);
static void tagmon(const Arg *arg);
+static void tearingcontrollersethint(struct wl_listener *listener, void *data);
+static void tearingcontrollerdestroy(struct wl_listener *listener, void *data);
+static void tearingnewhint(struct wl_listener *listener, void *data);
static void tile(Monitor *m);
static void togglefloating(const Arg *arg);
static void togglefullscreen(const Arg *arg);
+static void toggleglobaltearing(const Arg *arg);
static void toggletag(const Arg *arg);
static void toggleview(const Arg *arg);
static void unlocksession(struct wl_listener *listener, void *data);
@@ -402,6 +427,9 @@ static struct wlr_scene_rect *locked_bg;
static struct wlr_session_lock_v1 *cur_lock;
static struct wl_listener lock_listener = {.notify = locksession};
+static struct wlr_tearing_control_manager_v1 *tearing_control_v1;
+static struct wl_listener tearing_control_new_object = {.notify = tearingnewhint};
+
static struct wlr_seat *seat;
static KeyboardGroup *kb_group;
static unsigned int cursor_mode;
@@ -1466,6 +1494,35 @@ focustop(Monitor *m)
return NULL;
}
+static void
+forcetearingrule(Client *c)
+{
+ int success = 0;
+ const char* appid = client_get_appid(c);
+ const char* title = client_get_title(c);
+
+ for (unsigned i = 0; i < LENGTH(force_tearing_whitelist); ++i) {
+ if (appid) {
+ if (strcmp(force_tearing_whitelist[i].appid, appid) == 0) {
+ success = 1;
+ break;
+ }
+ }
+
+ if (title) {
+ if (strcmp(force_tearing_whitelist[i].title, title) == 0) {
+ success = 1;
+ break;
+ }
+ }
+ }
+
+ if (success) {
+ c->tearing_hint = WP_TEARING_CONTROL_V1_PRESENTATION_HINT_ASYNC;
+ fprintf(stderr, "Forcing tearing for appid: '%s', title: '%s'\n", appid, title);
+ }
+}
+
void
fullscreennotify(struct wl_listener *listener, void *data)
{
@@ -1694,6 +1751,8 @@ mapnotify(struct wl_listener *listener, void *data)
Monitor *m;
int i;
+ forcetearingrule(c);
+
/* Create scene tree for this client and its border */
c->scene = client_surface(c)->data = wlr_scene_tree_create(layers[LyrTile]);
wlr_scene_node_set_enabled(&c->scene->node, c->type != XDGShell);
@@ -1930,6 +1989,13 @@ moveresize(const Arg *arg)
}
}
+int
+moncantear(Monitor* m)
+{
+ Client *c = focustop(m);
+ return (c && c->isfullscreen && c->tearing_hint); /* 1 == ASYNC */
+}
+
void
outputmgrapply(struct wl_listener *listener, void *data)
{
@@ -2102,53 +2168,56 @@ quit(const Arg *arg)
void
rendermon(struct wl_listener *listener, void *data)
{
- /* This function is called every time an output is ready to display a frame,
- * generally at the output's refresh rate (e.g. 60Hz). */
Monitor *m = wl_container_of(listener, m, frame);
- Client *c;
+ struct wlr_scene_output *scene_output = m->scene_output;
struct wlr_output_state pending = {0};
- struct wlr_gamma_control_v1 *gamma_control;
- struct timespec now;
+ struct wlr_gamma_control_v1 *gamma_control = {0};
+ SendFrameDoneData frame_done_data = {0};
- /* Render if no XDG clients have an outstanding resize and are visible on
- * this monitor. */
- wl_list_for_each(c, &clients, link) {
- if (c->resize && !c->isfloating && client_is_rendered_on_mon(c, m) && !client_is_stopped(c))
- goto skip;
+ m->wlr_output->frame_pending = false;
+
+ if (!m->wlr_output->needs_frame && !m->gamma_lut_changed &&
+ !pixman_region32_not_empty(&scene_output->pending_commit_damage)) {
+ goto skip;
}
- /*
- * HACK: The "correct" way to set the gamma is to commit it together with
- * the rest of the state in one go, but to do that we would need to rewrite
- * wlr_scene_output_commit() in order to add the gamma to the pending
- * state before committing, instead try to commit the gamma in one frame,
- * and commit the rest of the state in the next one (or in the same frame if
- * the gamma can not be committed).
- */
+ wlr_output_state_init(&pending);
+ if (!wlr_scene_output_build_state(m->scene_output, &pending, NULL))
+ goto skip;
+
if (m->gamma_lut_changed) {
- gamma_control
- = wlr_gamma_control_manager_v1_get_control(gamma_control_mgr, m->wlr_output);
- m->gamma_lut_changed = 0;
+ m->gamma_lut_changed = false;
+ gamma_control = wlr_gamma_control_manager_v1_get_control(gamma_control_mgr, m->wlr_output);
- if (!wlr_gamma_control_v1_apply(gamma_control, &pending))
- goto commit;
+ if (!wlr_gamma_control_v1_apply(gamma_control, &pending)) {
+ wlr_output_state_finish(&pending);
+ goto skip;
+ }
if (!wlr_output_test_state(m->wlr_output, &pending)) {
wlr_gamma_control_v1_send_failed_and_destroy(gamma_control);
- goto commit;
+ wlr_output_state_set_gamma_lut(&pending, 0, NULL, NULL, NULL);
}
- wlr_output_commit_state(m->wlr_output, &pending);
- wlr_output_schedule_frame(m->wlr_output);
- } else {
-commit:
- wlr_scene_output_commit(m->scene_output, NULL);
}
-skip:
- /* Let clients know a frame has been rendered */
- clock_gettime(CLOCK_MONOTONIC, &now);
- wlr_scene_output_send_frame_done(m->scene_output, &now);
+ if (global_tearing || (tearing_allowed && moncantear(m))) {
+ pending.tearing_page_flip = true;
+
+ if (!wlr_output_test_state(m->wlr_output, &pending)) {
+ fprintf(stderr, "Output test failed on '%s', retrying without tearing page-flip\n", m->wlr_output->name);
+ pending.tearing_page_flip = false;
+ }
+ }
+
+ if (!wlr_output_commit_state(m->wlr_output, &pending))
+ fprintf(stderr, "Page-flip failed on output %s", m->wlr_output->name);
+
wlr_output_state_finish(&pending);
+
+skip:
+ clock_gettime(CLOCK_MONOTONIC, &frame_done_data.when);
+ frame_done_data.mon = m;
+ wlr_scene_output_for_each_buffer(m->scene_output, sendframedoneiterator, &frame_done_data);
}
void
@@ -2272,6 +2341,16 @@ run(char *startup_cmd)
wl_display_run(dpy);
}
+void
+sendframedoneiterator(struct wlr_scene_buffer *buffer, int x, int y, void *user_data)
+{
+ SendFrameDoneData *data = user_data;
+ if (buffer->primary_output != data->mon->scene_output)
+ return;
+
+ wlr_scene_buffer_send_frame_done(buffer, &data->when);
+}
+
void
setcursor(struct wl_listener *listener, void *data)
{
@@ -2628,6 +2707,9 @@ setup(void)
LISTEN_STATIC(&output_mgr->events.apply, outputmgrapply);
LISTEN_STATIC(&output_mgr->events.test, outputmgrtest);
+ tearing_control_v1 = wlr_tearing_control_manager_v1_create(dpy, 1);
+ wl_signal_add(&tearing_control_v1->events.new_object, &tearing_control_new_object);
+
/* Make sure XWayland clients don't connect to the parent X server,
* e.g when running in the x11 backend or the wayland backend and the
* compositor has Xwayland support */
@@ -2691,6 +2773,63 @@ tagmon(const Arg *arg)
setmon(sel, dirtomon(arg->i), 0);
}
+void
+tearingcontrollersethint(struct wl_listener *listener, void *data)
+{
+ Client *c = NULL, *i = NULL;
+ TearingController *controller = wl_container_of(listener, controller, set_hint);
+
+ struct wlr_xdg_surface *surface = wlr_xdg_surface_try_from_wlr_surface(controller->tearing_control->surface);
+#ifdef XWAYLAND
+ struct wlr_xwayland_surface *xsurface = wlr_xwayland_surface_try_from_wlr_surface(controller->tearing_control->surface);
+#endif
+
+ wl_list_for_each(i, &fstack, flink) {
+ if (i->surface.xdg == surface
+#ifdef XWAYLAND
+ || i->surface.xwayland == xsurface
+#endif
+ ) {
+ c = i;
+ break;
+ }
+ }
+
+ if (c) {
+ enum wp_tearing_control_v1_presentation_hint hint = controller->tearing_control->current;
+ fprintf(
+ stderr, "TEARING: found surface: %p(appid: '%s', title: '%s'), hint: %d(%s)\n",
+ (void*)c, client_get_appid(c), client_get_title(c), hint, hint ? "ASYNC" : "VSYNC"
+ );
+ c->tearing_hint = controller->tearing_control->current;
+ }
+}
+
+void
+tearingcontrollerdestroy(struct wl_listener *listener, void *data)
+{
+ TearingController *controller = wl_container_of(listener, controller, destroy);
+
+ wl_list_remove(&controller->set_hint.link);
+ wl_list_remove(&controller->destroy.link);
+ free(controller);
+}
+
+void
+tearingnewhint(struct wl_listener *listener, void *data)
+{
+ struct wlr_tearing_control_v1 *tearing_control = data;
+ TearingController *controller = ecalloc(1, sizeof(*controller));
+
+ controller->tearing_control = tearing_control;
+
+ controller->set_hint.notify = tearingcontrollersethint;
+ wl_signal_add(&tearing_control->events.set_hint, &controller->set_hint);
+
+ controller->destroy.notify = tearingcontrollerdestroy;
+ wl_signal_add(&tearing_control->events.destroy, &controller->destroy);
+}
+
void
tile(Monitor *m)
{
@@ -2742,6 +2881,12 @@ togglefullscreen(const Arg *arg)
setfullscreen(sel, !sel->isfullscreen);
}
+static void
+toggleglobaltearing(const Arg *arg)
+{
+ global_tearing ^= 1;
+}
+
void
toggletag(const Arg *arg)
{
--
2.51.1

View File

@ -31,5 +31,6 @@ This patch is inspired from <https://dwm.suckless.org/patches/winview/>. Citing
- [git branch](https://codeberg.org/dhruva_sambrani/dwl/src/branch/winview)
### Authors
- [André Desgualdo Pereira](https://codeberg.org/Kana)
- [Dhruva Sambrani](https://codeberg.org/dhruva_sambrani)