Merge branch 'main' into main

This commit is contained in:
metalcranium 2026-03-12 04:04:31 +01:00
commit 447397a44c
46 changed files with 1210 additions and 1556 deletions

View File

@ -47,9 +47,9 @@ If you target the unstable `main` branch, specify that in the `Download` link on
You may choose to include screenshots (hosted in your patch's subdirectory) in your `README.md`. The process is described [here](https://docs.codeberg.org/markdown/using-images/).
8. Use the Codeberg web interface to send a pull request to [dwl-patches] (NOT to [dwl])
9. WHEN YOUR PULL REQUEST IS APPROVED, your Codeberg account will also be granted commit access to [dwl-patches]. Once you have write access, you can make direct modifications/upates to your patches and you are free to create new patches rather than creating pull requests.
9. WHEN YOUR PULL REQUEST IS APPROVED, your Codeberg account will also be granted commit access to [dwl-patches]. Once you have write access, you can make direct modifications/updates to your patches and you are free to create new patches rather than creating pull requests.
Individuals who have made known that they no longer intend to maintain their patches will have commit access to the [dwl-pathces] repository removed.
Individuals who have made known that they no longer intend to maintain their patches will have commit access to the [dwl-patches] repository removed.
A returning user who formerly had commit access is welcome to open an issue on [dwl-patches] requesting commit access be reinstated. When doing so, please link to the original issue opened that granted commit access.

View File

@ -0,0 +1,31 @@
![dwl bar-modes patch example](./example.png)
### Description
Add a mode indicator to bar that tells which mode you are in, just like
river-classic's [dam](https://codeberg.org/sewn/dam) bar.
The string from `modes_labels` defined in `config.h` is used, while normal mode
is ignored.
Another usage is to serve as a hint for each modes keybindings:
```c
enum {
BROWSER,
};
const char *modes_labels[] = {
"[f]irefox [b]rave [c]hromium [q]utebrowser",
};
```
### Dependencies
this patch depends on:
- [bar](https://codeberg.org/dwl/dwl-patches/src/branch/main/patches/bar/) patch
- [modes](https://codeberg.org/dwl/dwl-patches/src/branch/main/patches/modes/) patch
### Download
- [v0.8](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/bar-modes/bar-modes.patch)
### Authors
- [unixchad](https://codeberg.org/unixchad/)

View File

@ -0,0 +1,131 @@
From 4f2c8a99720d90a551bf38f2c8d25ad239346eef Mon Sep 17 00:00:00 2001
From: nate zhou <gnuunixchad@outlook.com>
Date: Mon, 2 Mar 2026 21:54:03 +0800
Subject: [PATCH] Patch: bar-modes for 0.8
Add modes_labels indicator to bar, which behaves like river-classic's
dam bar. This patch has to be applied after the bar and modes patch.
---
dwl.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++----------
1 file changed, 59 insertions(+), 11 deletions(-)
diff --git a/dwl.c b/dwl.c
index ae290ad..df525d3 100644
--- a/dwl.c
+++ b/dwl.c
@@ -1585,19 +1585,26 @@ drawbar(Monitor *m)
uint32_t i, occ = 0, urg = 0;
Client *c;
Buffer *buf;
+ char mode_text[256] = "";
+ int mode_width = 0;
if (!m->scene_buffer->node.enabled)
return;
if (!(buf = bufmon(m)))
return;
- /* draw status first so it can be overdrawn by tags later */
- if (m == selmon) { /* status is only drawn on selected monitor */
- drwl_setscheme(m->drw, colors[SchemeNorm]);
- tw = TEXTW(m, stext) - m->lrpad + 2; /* 2px right padding */
- drwl_text(m->drw, m->b.width - tw, 0, tw, m->b.height, 0, stext, 0);
+ /* get current mode text if in a mode (not normal) */
+ if (active_mode_index >= 0 && active_mode_index < LENGTH(modes_labels) &&
+ modes_labels[active_mode_index]) {
+ strncpy(mode_text, modes_labels[active_mode_index], sizeof(mode_text) - 1);
+ mode_text[sizeof(mode_text) - 1] = '\0';
+ mode_width = TEXTW(m, mode_text);
}
+ /* calculate status text width */
+ drwl_setscheme(m->drw, colors[SchemeNorm]);
+ tw = TEXTW(m, stext) - m->lrpad + 2;
+
wl_list_for_each(c, &clients, link) {
if (c->mon != m)
continue;
@@ -1607,6 +1614,8 @@ drawbar(Monitor *m)
}
x = 0;
c = focustop(m);
+
+ /* draw tags (always shown) */
for (i = 0; i < LENGTH(tags); i++) {
w = TEXTW(m, tags[i]);
drwl_setscheme(m->drw, colors[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]);
@@ -1617,19 +1626,57 @@ drawbar(Monitor *m)
urg & 1 << i);
x += w;
}
+
+ /* draw mode indicator after tags if in a mode */
+ if (mode_text[0]) {
+ drwl_setscheme(m->drw, colors[SchemeSel]);
+ drwl_text(m->drw, x, 0, mode_width, m->b.height, m->lrpad / 2, mode_text, 0);
+ x += mode_width;
+ }
+
+ /* draw layout symbol after mode */
w = TEXTW(m, m->ltsymbol);
drwl_setscheme(m->drw, colors[SchemeNorm]);
- x = drwl_text(m->drw, x, 0, w, m->b.height, m->lrpad / 2, m->ltsymbol, 0);
+ drwl_text(m->drw, x, 0, w, m->b.height, m->lrpad / 2, m->ltsymbol, 0);
+ x += w;
+
+ int remaining = m->b.width - x;
- if ((w = m->b.width - tw - x) > m->b.height) {
- if (c) {
+ if (mode_text[0]) {
+ int title_width = remaining;
+
+ if (title_width > m->b.height && c) {
drwl_setscheme(m->drw, colors[m == selmon ? SchemeSel : SchemeNorm]);
- drwl_text(m->drw, x, 0, w, m->b.height, m->lrpad / 2, client_get_title(c), 0);
+ drwl_text(m->drw, x, 0, title_width, m->b.height, m->lrpad / 2,
+ client_get_title(c), 0);
if (c && c->isfloating)
drwl_rect(m->drw, x + boxs, boxs, boxw, boxw, 0, 0);
- } else {
+ } else if (title_width > 0) {
+ drwl_setscheme(m->drw, colors[SchemeNorm]);
+ drwl_rect(m->drw, x, 0, title_width, m->b.height, 1, 1);
+ }
+ /* no status text when in a mode - completely omitted */
+ } else {
+ /* not in a mode - normal behavior with status */
+ if (remaining >= tw) {
+ drwl_setscheme(m->drw, colors[SchemeNorm]);
+ drwl_text(m->drw, m->b.width - tw, 0, tw, m->b.height, 0, stext, 0);
+
+ int title_space = remaining - tw;
+ if (title_space > m->b.height && c) {
+ drwl_setscheme(m->drw, colors[m == selmon ? SchemeSel : SchemeNorm]);
+ drwl_text(m->drw, x, 0, title_space, m->b.height, m->lrpad / 2,
+ client_get_title(c), 0);
+ if (c && c->isfloating)
+ drwl_rect(m->drw, x + boxs, boxs, boxw, boxw, 0, 0);
+ } else if (title_space > 0) {
+ drwl_setscheme(m->drw, colors[SchemeNorm]);
+ drwl_rect(m->drw, x, 0, title_space, m->b.height, 1, 1);
+ }
+ } else if (remaining > 0) {
drwl_setscheme(m->drw, colors[SchemeNorm]);
- drwl_rect(m->drw, x, 0, w, m->b.height, 1, 1);
+ drwl_text(m->drw, m->b.width - remaining, 0, remaining, m->b.height, 0,
+ stext, 0);
}
}
@@ -3437,6 +3484,7 @@ entermode(const Arg *arg)
{
active_mode_index = arg->i;
printstatus();
+ drawbars();
}
#ifdef XWAYLAND
--
2.53.0

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

@ -301,7 +301,7 @@ index 0000000..125312c
+ pipefd = ecalloc(2, sizeof(int));
+
+ /*
+ * Libdbus forbids calling dbus_connection_dipatch from the
+ * Libdbus forbids calling dbus_connection_dispatch from the
+ * DBusDispatchStatusFunction directly. Notify the event loop of
+ * updates via a self-pipe.
+ */

View File

@ -3,7 +3,7 @@ From: wochap <gean.marroquin@gmail.com>
Date: Tue, 4 Jun 2024 16:02:25 -0500
Subject: [PATCH] implement borders patch
tihs patch adds 2 extra borders relative to the client, they don't
this patch adds 2 extra borders relative to the client, they don't
change the size of the client
---
client.h | 16 +++++++++++++---

View File

@ -15,12 +15,14 @@ bstack (TTT) bstackhoriz (===)
### Download
- [v0.8](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/bottomstack/bottomstack.patch)
- [git branch](https://codeberg.org/wochap/dwl/src/branch/v0.6-b/bottomstack)
- [2024-07-09](https://codeberg.org/dwl/dwl-patches/raw/commit/20de07dc8759200c8a4c9651475acb331d245890/patches/bottomstack/bottomstack.patch)
- [2024-04-11](https://codeberg.org/dwl/dwl-patches/raw/commit/0f4e40fee49d1b8b430778e241b29496ae3b3b70/bottomstack/bottomstack.patch)
- [v0.5](https://codeberg.org/dwl/dwl-patches/raw/commit/5368aa392c7ebf8d7d24c232b80cfae1be457d41/bottomstack/bottomstack.patch)
### Authors
- [unixchad](https://codeberg.org/unixchad)
- [wochap](https://codeberg.org/wochap)
- [DanielMowitz](https://github.com/DanielMowitz)
- [Abanoub8](https://github.com/Abanoub8)

View File

@ -1,7 +1,7 @@
From b352fb08f40b1ee2d8c4748be4922df711e3aaa9 Mon Sep 17 00:00:00 2001
From: wochap <gean.marroquin@gmail.com>
Date: Fri, 5 Jul 2024 10:44:29 -0500
Subject: [PATCH] implement bottomstack
From 86e379b187bcc9f8f5896a87309474fcf3736de9 Mon Sep 17 00:00:00 2001
From: nate zhou <gnuunixchad@outlook.com>
Date: Sat, 28 Feb 2026 21:29:53 +0800
Subject: [PATCH] Patch: bottomstack-0.8.patch
---
config.def.h | 4 +++
@ -9,10 +9,10 @@ Subject: [PATCH] implement bottomstack
2 files changed, 88 insertions(+)
diff --git a/config.def.h b/config.def.h
index 22d2171..5aac3e9 100644
index 8a6eda0..ae87518 100644
--- a/config.def.h
+++ b/config.def.h
@@ -34,6 +34,8 @@ static const Layout layouts[] = {
@@ -33,6 +33,8 @@ static const Layout layouts[] = {
{ "[]=", tile },
{ "><>", NULL }, /* no layout function means floating behavior */
{ "[M]", monocle },
@ -21,7 +21,7 @@ index 22d2171..5aac3e9 100644
};
/* monitors */
@@ -139,6 +141,8 @@ static const Key keys[] = {
@@ -135,6 +137,8 @@ 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]} },
@ -31,10 +31,10 @@ index 22d2171..5aac3e9 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 dc0437e..5648d5f 100644
index 44f3ad9..68c25a5 100644
--- a/dwl.c
+++ b/dwl.c
@@ -57,6 +57,7 @@
@@ -59,6 +59,7 @@
#include <wlr/types/wlr_xdg_decoration_v1.h>
#include <wlr/types/wlr_xdg_output_v1.h>
#include <wlr/types/wlr_xdg_shell.h>
@ -50,8 +50,8 @@ index dc0437e..5648d5f 100644
+static void bstackhoriz(Monitor *m);
/* variables */
static const char broken[] = "broken";
@@ -3160,3 +3163,84 @@ main(int argc, char *argv[])
static pid_t child_pid = -1;
@@ -3213,3 +3216,84 @@ main(int argc, char *argv[])
usage:
die("Usage: %s [-v] [-d] [-s startup command]", argv[0]);
}
@ -137,4 +137,5 @@ index dc0437e..5648d5f 100644
+ }
+}
--
2.45.1
2.53.0

View File

@ -61,7 +61,7 @@ When a new client appears:
btrtile adds couple variables to config.h to fine tune the mouse resizing of tiled clients.
1. **resize_factor**
- A multiplier to transfer pointer movement to client weight ratio. Depends heavily on mouse sensivity.
- A multiplier to transfer pointer movement to client weight ratio. Depends heavily on mouse sensitivity.
Defaults to 0.0002f.
2. **resize_interval_ms**

View File

@ -606,7 +606,7 @@ index 22d2171..92f3ad6 100644
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.1f, 0.1f, 0.1f, 1.0f}; /* You can also use glsl colors */
+static const float resize_factor = 0.0002f; /* Resize multiplier for mouse resizing, depends on mouse sensivity. */
+static const float resize_factor = 0.0002f; /* Resize multiplier for mouse resizing, depends on mouse sensitivity. */
+static const uint32_t resize_interval_ms = 16; /* Resize interval depends on framerate and screen refresh rate. */
+enum Direction { DIR_LEFT, DIR_RIGHT, DIR_UP, DIR_DOWN };

View File

@ -587,7 +587,7 @@ index 22d2171..92f3ad6 100644
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.1f, 0.1f, 0.1f, 1.0f}; /* You can also use glsl colors */
+static const float resize_factor = 0.0002f; /* Resize multiplier for mouse resizing, depends on mouse sensivity. */
+static const float resize_factor = 0.0002f; /* Resize multiplier for mouse resizing, depends on mouse sensitivity. */
+static const uint32_t resize_interval_ms = 16; /* Resize interval depends on framerate and screen refresh rate. */
+enum Direction { DIR_LEFT, DIR_RIGHT, DIR_UP, DIR_DOWN };

View File

@ -1,5 +1,5 @@
### Description
Generate a coredump if dwl exited abnormally (to be more usefull you need to
Generate a coredump if dwl exited abnormally (to be more useful you need to
compile dwl and wlroots with debug symbols)
### Download

View File

@ -2,8 +2,9 @@
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
- [v0.8](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/decklayout/decklayout.patch)
- [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)
- [main 2025-10-08](https://codeberg.org/dwl/dwl-patches/raw/commit/f8d1cfad116c19c01593f7436468ec0cb7a3297b/patches/decklayout/decklayout.patch)
### Authors
- [André Desgualdo Pereira](https://codeberg.org/Kana)

View File

@ -1,7 +1,7 @@
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
From c488515313e20f51ab961691002f9d483682ab16 Mon Sep 17 00:00:00 2001
From: nate zhou <gnuunixchad@outlook.com>
Date: Sat, 28 Feb 2026 21:32:46 +0800
Subject: [PATCH] Patch: decklayout-0.8.patch
---
config.def.h | 2 ++
@ -9,10 +9,10 @@ Subject: [PATCH] decklayout
2 files changed, 63 insertions(+)
diff --git a/config.def.h b/config.def.h
index 95c2afa..cc846eb 100644
index 8a6eda0..3ec9ceb 100644
--- a/config.def.h
+++ b/config.def.h
@@ -34,6 +34,7 @@ static const Layout layouts[] = {
@@ -33,6 +33,7 @@ static const Layout layouts[] = {
{ "[]=", tile },
{ "><>", NULL }, /* no layout function means floating behavior */
{ "[M]", monocle },
@ -20,7 +20,7 @@ index 95c2afa..cc846eb 100644
};
/* monitors */
@@ -139,6 +140,7 @@ static const Key keys[] = {
@@ -135,6 +136,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]} },
@ -29,7 +29,7 @@ index 95c2afa..cc846eb 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 12f441e..227004f 100644
index 44f3ad9..f13f48b 100644
--- a/dwl.c
+++ b/dwl.c
@@ -278,6 +278,7 @@ static void destroylayersurfacenotify(struct wl_listener *listener, void *data);
@ -40,7 +40,7 @@ index 12f441e..227004f 100644
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)
@@ -1838,6 +1839,66 @@ monocle(Monitor *m)
wlr_scene_node_raise_to_top(&c->scene->node);
}
@ -108,5 +108,5 @@ index 12f441e..227004f 100644
motionabsolute(struct wl_listener *listener, void *data)
{
--
2.51.0
2.53.0

View File

@ -8,7 +8,8 @@ There are also two functions that can be bound to a `Key` or `Button`,
2. `toggledimmingclient`: Which toggles dimming for the focused window, as if the client had `neverdim` applied to it. This overwrites an applied `Rule`.
### Download
- [2024-09-18](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/dim-unfocused/dim-unfocused.patch)
- [2026-03-10](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/dim-unfocused/dim-unfocused.patch)
- [2024-09-18](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/dim-unfocused/dim-unfocused-20240918.patch)
- [2024-09-03](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/dim-unfocused/dim-unfocused-20240903.patch)
- [2024-07-14](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/dim-unfocused/dim-unfocused-20240714.patch)
- [2024-05-16](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/dim-unfocused/dim-unfocused-20240516.patch)

View File

@ -0,0 +1,216 @@
diff --git a/client.h b/client.h
index dabea35..3a31c25 100644
--- a/client.h
+++ b/client.h
@@ -319,6 +319,12 @@ client_set_border_color(Client *c, const float color[static 4])
wlr_scene_rect_set_color(c->border[i], color);
}
+static inline void
+client_set_dimmer_state(Client *c, const int dim)
+{
+ wlr_scene_node_set_enabled(&c->dimmer->node, DIMOPT && !c->neverdim && dim);
+}
+
static inline void
client_set_fullscreen(Client *c, int fullscreen)
{
diff --git a/config.def.h b/config.def.h
index 22d2171..4ca21c9 100644
--- a/config.def.h
+++ b/config.def.h
@@ -10,6 +10,7 @@ static const 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 unfocuseddim[] = COLOR(0x00000088);
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.1f, 0.1f, 0.1f, 1.0f}; /* You can also use glsl colors */
@@ -22,10 +23,11 @@ static int log_level = WLR_ERROR;
/* NOTE: ALWAYS keep a rule declared even if you don't use rules (e.g leave at least one example) */
static const Rule rules[] = {
- /* app_id title tags mask isfloating monitor */
- /* examples: */
- { "Gimp_EXAMPLE", NULL, 0, 1, -1 }, /* Start on currently visible tags floating, not tiled */
- { "firefox_EXAMPLE", NULL, 1 << 8, 0, -1 }, /* Start on ONLY tag "9" */
+ /* app_id title tags mask isfloating neverdim monitor */
+ /* examples:
+ { "Gimp_example", NULL, 0, 1, 0, -1 },
+ */
+ { "firefox_example", NULL, 1 << 8, 0, 1, -1 },
};
/* layout(s) */
@@ -140,8 +142,9 @@ static const Key keys[] = {
{ MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} },
{ MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} },
{ MODKEY, XKB_KEY_space, setlayout, {0} },
+ { MODKEY, XKB_KEY_apostrophe, toggledimming, {0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} },
- { MODKEY, XKB_KEY_e, togglefullscreen, {0} },
+ { MODKEY, XKB_KEY_e, togglefullscreen, {0} },
{ MODKEY, XKB_KEY_0, view, {.ui = ~0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} },
{ MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} },
@@ -172,5 +175,6 @@ static const Key keys[] = {
static const Button buttons[] = {
{ MODKEY, BTN_LEFT, moveresize, {.ui = CurMove} },
{ MODKEY, BTN_MIDDLE, togglefloating, {0} },
+ { MODKEY|ShiftMask, BTN_MIDDLE, toggledimmingclient, {0} },
{ MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} },
};
diff --git a/dwl.c b/dwl.c
index dc0c861..dcc3ece 100644
--- a/dwl.c
+++ b/dwl.c
@@ -112,6 +112,7 @@ typedef struct {
Monitor *mon;
struct wlr_scene_tree *scene;
struct wlr_scene_rect *border[4]; /* top, bottom, left, right */
+ struct wlr_scene_rect *dimmer;
struct wlr_scene_tree *scene_surface;
struct wl_list link;
struct wl_list flink;
@@ -141,7 +142,7 @@ typedef struct {
#endif
unsigned int bw;
uint32_t tags;
- int isfloating, isurgent, isfullscreen;
+ int isfloating, isurgent, isfullscreen, neverdim;
uint32_t resize; /* configure serial of a pending resize */
} Client;
@@ -231,6 +232,7 @@ typedef struct {
const char *title;
uint32_t tags;
int isfloating;
+ int neverdim;
int monitor;
} Rule;
@@ -338,6 +340,8 @@ static void startdrag(struct wl_listener *listener, void *data);
static void tag(const Arg *arg);
static void tagmon(const Arg *arg);
static void tile(Monitor *m);
+static void toggledimming(const Arg *arg);
+static void toggledimmingclient(const Arg *arg);
static void togglefloating(const Arg *arg);
static void togglefullscreen(const Arg *arg);
static void toggletag(const Arg *arg);
@@ -410,6 +414,7 @@ static struct wlr_output_layout *output_layout;
static struct wlr_box sgeom;
static struct wl_list mons;
static Monitor *selmon;
+static int DIMOPT = 1;
#ifdef XWAYLAND
static void activatex11(struct wl_listener *listener, void *data);
@@ -466,6 +471,7 @@ applyrules(Client *c)
if ((!r->title || strstr(title, r->title))
&& (!r->id || strstr(appid, r->id))) {
c->isfloating = r->isfloating;
+ c->neverdim = r-> neverdim;
newtags |= r->tags;
i = 0;
wl_list_for_each(m, &mons, link) {
@@ -1365,8 +1371,10 @@ focusclient(Client *c, int lift)
/* Don't change border color if there is an exclusive focus or we are
* handling a drag operation */
- if (!exclusive_focus && !seat->drag)
+ if (!exclusive_focus && !seat->drag) {
client_set_border_color(c, focuscolor);
+ client_set_dimmer_state(c, 0);
+ }
}
/* Deactivate old client if focus is changing */
@@ -1384,7 +1392,7 @@ focusclient(Client *c, int lift)
* and probably other clients */
} else if (old_c && !client_is_unmanaged(old_c) && (!c || !client_wants_focus(c))) {
client_set_border_color(old_c, bordercolor);
-
+ client_set_dimmer_state(old_c, 1);
client_activate_surface(old, 0);
}
}
@@ -1681,8 +1689,7 @@ void
mapnotify(struct wl_listener *listener, void *data)
{
/* Called when the surface is mapped, or ready to display on-screen. */
- Client *p = NULL;
- Client *w, *c = wl_container_of(listener, c, map);
+ Client *p, *w, *d, *c = wl_container_of(listener, c, map);
Monitor *m;
int i;
@@ -1716,6 +1723,10 @@ mapnotify(struct wl_listener *listener, void *data)
c->border[i]->node.data = c;
}
+ c->dimmer = wlr_scene_rect_create(c->scene, 0, 0, unfocuseddim);
+ c->dimmer->node.data = c;
+ client_set_dimmer_state(c, 1);
+
/* 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;
@@ -1734,6 +1745,10 @@ mapnotify(struct wl_listener *listener, void *data)
setmon(c, p->mon, p->tags);
} else {
applyrules(c);
+ d = focustop(selmon);
+ if (d) {
+ client_set_dimmer_state(d, 0);
+ }
}
printstatus();
@@ -2160,7 +2175,7 @@ resize(Client *c, struct wlr_box geo, int interact)
c->geom = geo;
applybounds(c, bbox);
- /* Update scene-graph, including borders */
+ /* Update scene-graph, including borders and dimmer*/
wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y);
wlr_scene_node_set_position(&c->scene_surface->node, c->bw, c->bw);
wlr_scene_rect_set_size(c->border[0], c->geom.width, c->bw);
@@ -2170,6 +2185,8 @@ resize(Client *c, struct wlr_box geo, int interact)
wlr_scene_node_set_position(&c->border[1]->node, 0, c->geom.height - c->bw);
wlr_scene_node_set_position(&c->border[2]->node, 0, c->bw);
wlr_scene_node_set_position(&c->border[3]->node, c->geom.width - c->bw, c->bw);
+ wlr_scene_rect_set_size(c->dimmer, c->geom.width - 2*c->bw, c-> geom.height - 2*c->bw);
+ wlr_scene_node_set_position(&c->dimmer->node, c->bw, c->bw);
/* this is a no-op if size hasn't changed */
c->resize = client_set_size(c, c->geom.width - 2 * c->bw,
@@ -2681,6 +2698,27 @@ tile(Monitor *m)
}
}
+void toggledimming(const Arg *arg)
+{
+ Client *c;
+ DIMOPT ^= 1;
+ wl_list_for_each(c, &clients, link)
+ {
+ client_set_dimmer_state(c, 1);
+ }
+ c = focustop(selmon);
+ if (c)
+ client_set_dimmer_state(c, 0);
+}
+
+void
+toggledimmingclient(const Arg *arg)
+{
+ Client *sel = focustop(selmon);
+ if (sel)
+ sel -> neverdim ^= 1;
+}
+
void
togglefloating(const Arg *arg)
{

View File

@ -1,216 +1 @@
diff --git a/client.h b/client.h
index dabea35..3a31c25 100644
--- a/client.h
+++ b/client.h
@@ -319,6 +319,12 @@ client_set_border_color(Client *c, const float color[static 4])
wlr_scene_rect_set_color(c->border[i], color);
}
+static inline void
+client_set_dimmer_state(Client *c, const int dim)
+{
+ wlr_scene_node_set_enabled(&c->dimmer->node, DIMOPT && !c->neverdim && dim);
+}
+
static inline void
client_set_fullscreen(Client *c, int fullscreen)
{
diff --git a/config.def.h b/config.def.h
index 22d2171..4ca21c9 100644
--- a/config.def.h
+++ b/config.def.h
@@ -10,6 +10,7 @@ static const 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 unfocuseddim[] = COLOR(0x00000088);
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.1f, 0.1f, 0.1f, 1.0f}; /* You can also use glsl colors */
@@ -22,10 +23,11 @@ static int log_level = WLR_ERROR;
/* NOTE: ALWAYS keep a rule declared even if you don't use rules (e.g leave at least one example) */
static const Rule rules[] = {
- /* app_id title tags mask isfloating monitor */
- /* examples: */
- { "Gimp_EXAMPLE", NULL, 0, 1, -1 }, /* Start on currently visible tags floating, not tiled */
- { "firefox_EXAMPLE", NULL, 1 << 8, 0, -1 }, /* Start on ONLY tag "9" */
+ /* app_id title tags mask isfloating neverdim monitor */
+ /* examples:
+ { "Gimp_example", NULL, 0, 1, 0, -1 },
+ */
+ { "firefox_example", NULL, 1 << 8, 0, 1, -1 },
};
/* layout(s) */
@@ -140,8 +142,9 @@ static const Key keys[] = {
{ MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} },
{ MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} },
{ MODKEY, XKB_KEY_space, setlayout, {0} },
+ { MODKEY, XKB_KEY_apostrophe, toggledimming, {0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} },
- { MODKEY, XKB_KEY_e, togglefullscreen, {0} },
+ { MODKEY, XKB_KEY_e, togglefullscreen, {0} },
{ MODKEY, XKB_KEY_0, view, {.ui = ~0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} },
{ MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} },
@@ -172,5 +175,6 @@ static const Key keys[] = {
static const Button buttons[] = {
{ MODKEY, BTN_LEFT, moveresize, {.ui = CurMove} },
{ MODKEY, BTN_MIDDLE, togglefloating, {0} },
+ { MODKEY|ShiftMask, BTN_MIDDLE, toggledimmingclient, {0} },
{ MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} },
};
diff --git a/dwl.c b/dwl.c
index dc0c861..dcc3ece 100644
--- a/dwl.c
+++ b/dwl.c
@@ -112,6 +112,7 @@ typedef struct {
Monitor *mon;
struct wlr_scene_tree *scene;
struct wlr_scene_rect *border[4]; /* top, bottom, left, right */
+ struct wlr_scene_rect *dimmer;
struct wlr_scene_tree *scene_surface;
struct wl_list link;
struct wl_list flink;
@@ -141,7 +142,7 @@ typedef struct {
#endif
unsigned int bw;
uint32_t tags;
- int isfloating, isurgent, isfullscreen;
+ int isfloating, isurgent, isfullscreen, neverdim;
uint32_t resize; /* configure serial of a pending resize */
} Client;
@@ -231,6 +232,7 @@ typedef struct {
const char *title;
uint32_t tags;
int isfloating;
+ int neverdim;
int monitor;
} Rule;
@@ -338,6 +340,8 @@ static void startdrag(struct wl_listener *listener, void *data);
static void tag(const Arg *arg);
static void tagmon(const Arg *arg);
static void tile(Monitor *m);
+static void toggledimming(const Arg *arg);
+static void toggledimmingclient(const Arg *arg);
static void togglefloating(const Arg *arg);
static void togglefullscreen(const Arg *arg);
static void toggletag(const Arg *arg);
@@ -410,6 +414,7 @@ static struct wlr_output_layout *output_layout;
static struct wlr_box sgeom;
static struct wl_list mons;
static Monitor *selmon;
+static int DIMOPT = 1;
#ifdef XWAYLAND
static void activatex11(struct wl_listener *listener, void *data);
@@ -466,6 +471,7 @@ applyrules(Client *c)
if ((!r->title || strstr(title, r->title))
&& (!r->id || strstr(appid, r->id))) {
c->isfloating = r->isfloating;
+ c->neverdim = r-> neverdim;
newtags |= r->tags;
i = 0;
wl_list_for_each(m, &mons, link) {
@@ -1365,8 +1371,10 @@ focusclient(Client *c, int lift)
/* Don't change border color if there is an exclusive focus or we are
* handling a drag operation */
- if (!exclusive_focus && !seat->drag)
+ if (!exclusive_focus && !seat->drag) {
client_set_border_color(c, focuscolor);
+ client_set_dimmer_state(c, 0);
+ }
}
/* Deactivate old client if focus is changing */
@@ -1384,7 +1392,7 @@ focusclient(Client *c, int lift)
* and probably other clients */
} else if (old_c && !client_is_unmanaged(old_c) && (!c || !client_wants_focus(c))) {
client_set_border_color(old_c, bordercolor);
-
+ client_set_dimmer_state(old_c, 1);
client_activate_surface(old, 0);
}
}
@@ -1681,8 +1689,7 @@ void
mapnotify(struct wl_listener *listener, void *data)
{
/* Called when the surface is mapped, or ready to display on-screen. */
- Client *p = NULL;
- Client *w, *c = wl_container_of(listener, c, map);
+ Client *p, *w, *d, *c = wl_container_of(listener, c, map);
Monitor *m;
int i;
@@ -1716,6 +1723,10 @@ mapnotify(struct wl_listener *listener, void *data)
c->border[i]->node.data = c;
}
+ c->dimmer = wlr_scene_rect_create(c->scene, 0, 0, unfocuseddim);
+ c->dimmer->node.data = c;
+ client_set_dimmer_state(c, 1);
+
/* 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;
@@ -1734,6 +1745,10 @@ mapnotify(struct wl_listener *listener, void *data)
setmon(c, p->mon, p->tags);
} else {
applyrules(c);
+ d = focustop(selmon);
+ if (d) {
+ client_set_dimmer_state(d, 0);
+ }
}
printstatus();
@@ -2160,7 +2175,7 @@ resize(Client *c, struct wlr_box geo, int interact)
c->geom = geo;
applybounds(c, bbox);
- /* Update scene-graph, including borders */
+ /* Update scene-graph, including borders and dimmer*/
wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y);
wlr_scene_node_set_position(&c->scene_surface->node, c->bw, c->bw);
wlr_scene_rect_set_size(c->border[0], c->geom.width, c->bw);
@@ -2170,6 +2185,8 @@ resize(Client *c, struct wlr_box geo, int interact)
wlr_scene_node_set_position(&c->border[1]->node, 0, c->geom.height - c->bw);
wlr_scene_node_set_position(&c->border[2]->node, 0, c->bw);
wlr_scene_node_set_position(&c->border[3]->node, c->geom.width - c->bw, c->bw);
+ wlr_scene_rect_set_size(c->dimmer, c->geom.width - 2*c->bw, c-> geom.height - 2*c->bw);
+ wlr_scene_node_set_position(&c->dimmer->node, c->bw, c->bw);
/* this is a no-op if size hasn't changed */
c->resize = client_set_size(c, c->geom.width - 2 * c->bw,
@@ -2681,6 +2698,27 @@ tile(Monitor *m)
}
}
+void toggledimming(const Arg *arg)
+{
+ Client *c;
+ DIMOPT ^= 1;
+ wl_list_for_each(c, &clients, link)
+ {
+ client_set_dimmer_state(c, 1);
+ }
+ c = focustop(selmon);
+ if (c)
+ client_set_dimmer_state(c, 0);
+}
+
+void
+toggledimmingclient(const Arg *arg)
+{
+ Client *sel = focustop(selmon);
+ if (sel)
+ sel -> neverdim ^= 1;
+}
+
void
togglefloating(const Arg *arg)
{
09

View File

@ -0,0 +1,17 @@
### extrabar
Add an extra bar on the other side of the primary bar, shows additional status sections.
_Requires_ `bar` patch to be applied first. Heavily inspired by `dwm`'s extrabar patch.
### Status
The status text on the extra bar is set using the stdin as usual, with the text being split by `;`
For example, if `status1;status2;status3` was set,
then `status1` is set as the status of the primary bar,
`status2` is set as the left status of extrabar,
`status3` is set as the right status of extrabar.
### Author
[dhruva_sambrani](/dhruva_sambrani)

View File

@ -0,0 +1,193 @@
diff --git a/dwl.c b/dwl.c
index 7fe9468..4e3272e 100644
--- a/dwl.c
+++ b/dwl.c
@@ -205,6 +205,7 @@ struct Monitor {
struct wlr_output *wlr_output;
struct wlr_scene_output *scene_output;
struct wlr_scene_buffer *scene_buffer; /* bar buffer */
+ struct wlr_scene_buffer *extra_scene_buffer; /* bar buffer */
struct wlr_scene_rect *fullscreen_bg; /* See createmon() for info */
struct wl_listener frame;
struct wl_listener destroy;
@@ -230,6 +231,7 @@ struct Monitor {
int asleep;
Drwl *drw;
Buffer *pool[2];
+ Buffer *extra_pool[2];
int lrpad;
};
@@ -278,7 +280,7 @@ static void bufdestroy(struct wlr_buffer *buffer);
static bool bufdatabegin(struct wlr_buffer *buffer, uint32_t flags,
void **data, uint32_t *format, size_t *stride);
static void bufdataend(struct wlr_buffer *buffer);
-static Buffer *bufmon(Monitor *m);
+static Buffer *bufmon(Monitor *m, Buffer **pool);
static void bufrelease(struct wl_listener *listener, void *data);
static void buttonpress(struct wl_listener *listener, void *data);
static void chvt(const Arg *arg);
@@ -444,6 +446,8 @@ static struct wl_list mons;
static Monitor *selmon;
static char stext[256];
+static char stext2[256];
+static char stext3[256];
static struct wl_event_source *status_event_source;
static const struct wlr_buffer_impl buffer_impl = {
@@ -627,6 +631,11 @@ arrangelayers(Monitor *m)
usable_area.y += topbar ? m->b.real_height : 0;
}
+ if (m->extra_scene_buffer->node.enabled) {
+ usable_area.height -= m->b.real_height;
+ usable_area.y += topbar ? 0 : m->b.real_height;
+ }
+
/* Arrange exclusive surfaces from top->bottom */
for (i = 3; i >= 0; i--)
arrangelayer(m, &m->layers[i], &usable_area, 1);
@@ -706,23 +715,23 @@ bufdataend(struct wlr_buffer *wlr_buffer)
}
Buffer *
-bufmon(Monitor *m)
+bufmon(Monitor *m, Buffer **pool)
{
size_t i;
Buffer *buf = NULL;
for (i = 0; i < LENGTH(m->pool); i++) {
- if (m->pool[i]) {
- if (m->pool[i]->busy)
+ if (pool[i]) {
+ if (pool[i]->busy)
continue;
- buf = m->pool[i];
+ buf = pool[i];
break;
}
buf = ecalloc(1, sizeof(Buffer) + (m->b.width * 4 * m->b.height));
buf->image = drwl_image_create(NULL, m->b.width, m->b.height, buf->data);
wlr_buffer_init(&buf->base, &buffer_impl, m->b.width, m->b.height);
- m->pool[i] = buf;
+ pool[i] = buf;
break;
}
if (!buf)
@@ -1255,6 +1264,8 @@ createmon(struct wl_listener *listener, void *data)
m->scene_buffer = wlr_scene_buffer_create(layers[LyrBottom], NULL);
m->scene_buffer->point_accepts_input = baracceptsinput;
+ m->extra_scene_buffer = wlr_scene_buffer_create(layers[LyrBottom], NULL);
+ m->extra_scene_buffer->point_accepts_input = baracceptsinput;
updatebar(m);
wl_list_insert(&mons, &m->link);
@@ -1580,7 +1591,7 @@ drawbar(Monitor *m)
if (!m->scene_buffer->node.enabled)
return;
- if (!(buf = bufmon(m)))
+ if (!(buf = bufmon(m, m->pool)))
return;
/* draw status first so it can be overdrawn by tags later */
@@ -1633,13 +1644,52 @@ drawbar(Monitor *m)
wlr_buffer_unlock(&buf->base);
}
+void
+extrabar(Monitor *m)
+{
+ int tw = 0;
+ Buffer *buf = NULL;
+
+ /* Skip if the extra bar scene buffer is not initialized/enabled */
+ if (!m->extra_scene_buffer || !m->extra_scene_buffer->node.enabled)
+ return;
+
+ /* Inline bufmon logic for the extra pool to keep changes contained */
+ if (!(buf = bufmon(m, m->extra_pool)))
+ return;
+
+ buf->busy = true;
+ LISTEN(&buf->base.events.release, &buf->release, bufrelease);
+ wlr_buffer_lock(&buf->base);
+ drwl_setimage(m->drw, buf->image);
+
+ drwl_setscheme(m->drw, colors[SchemeNorm]);
+ drwl_rect(m->drw, 0, 0, m->b.width, m->b.height, 1, 1);
+
+ drwl_setscheme(m->drw, colors[SchemeNorm]);
+ tw = TEXTW(m, stext2) - m->lrpad + 2; /* 2px right padding */
+ drwl_text(m->drw, 2, 0, tw, m->b.height, 0, stext2, 0);
+
+ tw = TEXTW(m, stext3) - m->lrpad + 2; /* 2px right padding */
+ drwl_text(m->drw, m->b.width - tw - 4, 0, tw, m->b.height, 0, stext3, 0);
+
+ wlr_scene_buffer_set_dest_size(m->extra_scene_buffer, m->b.real_width, m->b.real_height);
+ wlr_scene_node_set_position(&m->extra_scene_buffer->node, m->m.x,
+ m->m.y + (topbar ? m->m.height - m->b.real_height : 0));
+
+ wlr_scene_buffer_set_buffer(m->extra_scene_buffer, &buf->base);
+ wlr_buffer_unlock(&buf->base);
+}
+
void
drawbars(void)
{
Monitor *m = NULL;
- wl_list_for_each(m, &mons, link)
+ wl_list_for_each(m, &mons, link) {
drawbar(m);
+ extrabar(m);
+ }
}
void
@@ -2896,7 +2946,7 @@ startdrag(struct wl_listener *listener, void *data)
int
statusin(int fd, unsigned int mask, void *data)
{
- char status[256];
+ char status[3*256];
ssize_t n;
if (mask & WL_EVENT_ERROR)
@@ -2911,7 +2961,18 @@ statusin(int fd, unsigned int mask, void *data)
status[n] = '\0';
status[strcspn(status, "\n")] = '\0';
- strncpy(stext, status, sizeof(stext));
+ char *l1 = strchr(status, ';');
+ if (l1) {
+ *l1 = '\0';
+ char *l2 = strchr(++l1, ';');
+ if (l2) {
+ *l2 = '\0';
+ strncpy(stext3, ++l2, sizeof(stext3));
+ }
+ strncpy(stext2, l1, sizeof(stext2));
+ }
+ strncpy(stext, status, sizeof(stext));
+
drawbars();
return 0;
@@ -3206,6 +3267,12 @@ updatebar(Monitor *m)
m->pool[i] = NULL;
}
+ for (i = 0; i < LENGTH(m->extra_pool); i++)
+ if (m->extra_pool[i]) {
+ wlr_buffer_drop(&m->extra_pool[i]->base);
+ m->extra_pool[i] = NULL;
+ }
+
if (m->b.scale == m->wlr_output->scale && m->drw)
return;

View File

@ -4,7 +4,8 @@ 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
- [2025-10-31](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/gaplessgrid/gaplessgrid.patch)
- [v0.8](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-20251031.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)

View File

@ -0,0 +1,102 @@
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 95c2afa..2054107 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 12f441e..7ab5323 100644
--- a/dwl.c
+++ b/dwl.c
@@ -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);
@@ -1566,6 +1567,56 @@ handlesig(int signo)
quit(NULL);
}
+void
+gaplessgrid(Monitor *m)
+{
+ 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)
{
--
2.51.0

View File

@ -1,7 +1,7 @@
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
From 91d34b1c664fe273a82eb7ff01e92b828feab5a9 Mon Sep 17 00:00:00 2001
From: nate zhou <gnuunixchad@outlook.com>
Date: Sat, 28 Feb 2026 21:42:04 +0800
Subject: [PATCH] Patch: gaplessgrid.patch
---
config.def.h | 2 ++
@ -9,10 +9,10 @@ Subject: [PATCH] take on gaplessgrid and fix minor indentation and typos
2 files changed, 53 insertions(+)
diff --git a/config.def.h b/config.def.h
index 95c2afa..2054107 100644
index 8a6eda0..8cb1782 100644
--- a/config.def.h
+++ b/config.def.h
@@ -34,6 +34,7 @@ static const Layout layouts[] = {
@@ -33,6 +33,7 @@ static const Layout layouts[] = {
{ "[]=", tile },
{ "><>", NULL }, /* no layout function means floating behavior */
{ "[M]", monocle },
@ -20,7 +20,7 @@ index 95c2afa..2054107 100644
};
/* monitors */
@@ -139,6 +140,7 @@ static const Key keys[] = {
@@ -135,6 +136,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]} },
@ -29,7 +29,7 @@ index 95c2afa..2054107 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 12f441e..7ab5323 100644
index 44f3ad9..83c053f 100644
--- a/dwl.c
+++ b/dwl.c
@@ -288,6 +288,7 @@ static void focusstack(const Arg *arg);
@ -98,5 +98,5 @@ index 12f441e..7ab5323 100644
incnmaster(const Arg *arg)
{
--
2.51.0
2.53.0

View File

@ -0,0 +1,15 @@
### Description
Hide the mouse cursor when you start typing, and restore it again when the
mouse cursor moves or a mouse button is pressed, just like
[xbanish](https://github.com/jcs/xbanish).
### credits
Some code is taken from the
[unclutter](https://codeberg.org/dwl/dwl-patches/src/branch/main/patches/unclutter) patch.
### Download
[v0.8](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/hide-cursor-when-typing/hide-cursor-when-typing.patch)
### Authors
- [unixchad](https://codeberg.org/unixchad/)

View File

@ -0,0 +1,170 @@
From c72de058e6635c5bb3d973995ccbdb6e028f354d Mon Sep 17 00:00:00 2001
From: nate zhou <gnuunixchad@outlook.com>
Date: Tue, 3 Mar 2026 16:48:46 +0800
Subject: [PATCH] Patch: hide-cursor-when-typing-0.8.patch
Hide the cursor when start typing, and restore it when start moving
cursor again, just like xbanish.
---
config.def.h | 2 ++
dwl.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++------
2 files changed, 63 insertions(+), 7 deletions(-)
diff --git a/config.def.h b/config.def.h
index 8a6eda0..6205c0a 100644
--- a/config.def.h
+++ b/config.def.h
@@ -102,6 +102,8 @@ LIBINPUT_CONFIG_TAP_MAP_LMR -- 1/2/3 finger tap maps to left/middle/right
*/
static const enum libinput_config_tap_button_map button_map = LIBINPUT_CONFIG_TAP_MAP_LRM;
+static const int hide_cursor_when_typing = 1;
+
/* If you want to use the windows key for MODKEY, use WLR_MODIFIER_LOGO */
#define MODKEY WLR_MODIFIER_ALT
diff --git a/dwl.c b/dwl.c
index 44f3ad9..9a076b6 100644
--- a/dwl.c
+++ b/dwl.c
@@ -289,6 +289,8 @@ 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 handlesig(int signo);
+static void handlecursoractivity(void);
+static int hidecursor(void *data);
static void incnmaster(const Arg *arg);
static void inputdevice(struct wl_listener *listener, void *data);
static int keybinding(uint32_t mods, xkb_keysym_t sym);
@@ -389,6 +391,14 @@ static struct wlr_pointer_constraint_v1 *active_constraint;
static struct wlr_cursor *cursor;
static struct wlr_xcursor_manager *cursor_mgr;
+static struct wl_event_source *hide_source;
+static bool cursor_hidden = false;
+static struct {
+ enum wp_cursor_shape_device_v1_shape shape;
+ struct wlr_surface *surface;
+ int hotspot_x;
+ int hotspot_y;
+} last_cursor;
static struct wlr_scene_rect *root_bg;
static struct wlr_session_lock_manager_v1 *session_lock_mgr;
@@ -610,6 +620,7 @@ axisnotify(struct wl_listener *listener, void *data)
* for example when you move the scroll wheel. */
struct wlr_pointer_axis_event *event = data;
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
+ handlecursoractivity();
/* TODO: allow usage of scroll wheel for mousebindings, it can be implemented
* by checking the event's orientation and the delta of the event */
/* Notify the client with pointer focus of the axis event. */
@@ -628,6 +639,7 @@ buttonpress(struct wl_listener *listener, void *data)
const Button *b;
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
+ handlecursoractivity();
switch (event->state) {
case WL_POINTER_BUTTON_STATE_PRESSED:
@@ -1566,6 +1578,30 @@ handlesig(int signo)
quit(NULL);
}
+void
+handlecursoractivity(void)
+{
+ if (!cursor_hidden)
+ return;
+
+ cursor_hidden = false;
+
+ if (last_cursor.shape)
+ wlr_cursor_set_xcursor(cursor, cursor_mgr,
+ wlr_cursor_shape_v1_name(last_cursor.shape));
+ else
+ wlr_cursor_set_surface(cursor, last_cursor.surface,
+ last_cursor.hotspot_x, last_cursor.hotspot_y);
+}
+
+int
+hidecursor(void *data)
+{
+ wlr_cursor_unset_image(cursor);
+ cursor_hidden = true;
+ return 1;
+}
+
void
incnmaster(const Arg *arg)
{
@@ -1645,6 +1681,11 @@ keypress(struct wl_listener *listener, void *data)
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
+ /* hide cursor when typing starts */
+ if (hide_cursor_when_typing && !cursor_hidden && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
+ hidecursor(NULL);
+ }
+
/* On _press_ if there is no active screen locker,
* attempt to process a compositor keybinding. */
if (!locked && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
@@ -1907,6 +1948,7 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d
wlr_cursor_move(cursor, device, dx, dy);
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
+ handlecursoractivity();
/* Update selmon (even while dragging a window) */
if (sloppyfocus)
@@ -1931,7 +1973,7 @@ motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double d
/* If there's no client surface under the cursor, set the cursor image to a
* default. This is what makes the cursor image appear when you move it
* off of a client or over its border. */
- if (!surface && !seat->drag)
+ if (!surface && !seat->drag && !cursor_hidden)
wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
pointerfocus(c, surface, sx, sy, time);
@@ -2311,9 +2353,16 @@ setcursor(struct wl_listener *listener, void *data)
* use the provided surface as the cursor image. It will set the
* hardware cursor on the output that it's currently on and continue to
* do so as the cursor moves between outputs. */
- if (event->seat_client == seat->pointer_state.focused_client)
- wlr_cursor_set_surface(cursor, event->surface,
- event->hotspot_x, event->hotspot_y);
+ if (event->seat_client == seat->pointer_state.focused_client) {
+ last_cursor.shape = 0;
+ last_cursor.surface = event->surface;
+ last_cursor.hotspot_x = event->hotspot_x;
+ last_cursor.hotspot_y = event->hotspot_y;
+
+ if (!cursor_hidden)
+ wlr_cursor_set_surface(cursor, event->surface,
+ event->hotspot_x, event->hotspot_y);
+ }
}
void
@@ -2325,9 +2374,14 @@ setcursorshape(struct wl_listener *listener, void *data)
/* This can be sent by any client, so we check to make sure this one
* actually has pointer focus first. If so, we can tell the cursor to
* use the provided cursor shape. */
- if (event->seat_client == seat->pointer_state.focused_client)
- wlr_cursor_set_xcursor(cursor, cursor_mgr,
- wlr_cursor_shape_v1_name(event->shape));
+ if (event->seat_client == seat->pointer_state.focused_client) {
+ last_cursor.shape = event->shape;
+ last_cursor.surface = NULL;
+
+ if (!cursor_hidden)
+ wlr_cursor_set_xcursor(cursor, cursor_mgr,
+ wlr_cursor_shape_v1_name(event->shape));
+ }
}
void
--
2.53.0

View File

@ -15,7 +15,7 @@ So in short:
#### Limitations
Reloading the compositor will replace all functionality except for `main`, `setup`, `run` and the reload logic.
Note that you're responsible yourself for reloading ressources like fonts, which may only get acquired once.
Note that you're responsible yourself for reloading resources like fonts, which may only get acquired once.
A lot of components of dwl will also only get run on a trigger (the tiling for example).
So not every change will be immediate.
Furthermore, any patch adding more global state to dwl cannot currently be reloaded properly since
@ -47,13 +47,13 @@ From the cold part there are some newly available macros:
* `CSYM(T, v)` dynamically accesses the value of the symbol `v` of type `T` from the shared object. Use this to query values from config.h for example.
* `LISTEN_GLOBAL(E, L)` is similar to the `LISTEN` macro. `E` is an event and `L` the name of a global
listener. Current implementation is a bit messy and I may fix it if someone bothers me about it.
* `UNLISTEN(L)` takes a listener and unregisteres it. This is important for reloading.
* `UNLISTEN(L)` takes a listener and unregisters it. This is important for reloading.
When adding new code there are some considerations to be made. Since dwl decorates all symbols with `static` by default, we cannot access them as-is.
C's macro system is a bit too powerful though and we use this to our advantage. We will repeatedly define and
undefine a macro called `static` in order to replace the `static` keyword inside some sections.
This allows us to do less refactoring and preserve a lot of the original patch compatability since we're only
strategically adding lines. We're tring to be as minimally invasive as we can.
This allows us to do less refactoring and preserve a lot of the original patch compatibility since we're only
strategically adding lines. We're trying to be as minimally invasive as we can.
As a general guide:
* global state should be global for the cold part and `extern` in the cold part meaning it should be inside a block like this:
```C
@ -87,7 +87,7 @@ Thus, we enclose them the same way we do functions:
#ifdef HOT
... // function definitions here
#endif
* enfore use of the `LISTEN_GLOBAL` and `UNLISTEN` macros (I know this sucks but what can I do, I need to get
* enforce use of the `LISTEN_GLOBAL` and `UNLISTEN` macros (I know this sucks but what can I do, I need to get
access to the callbacks somehow). So you want
* `wl_list_remove(listener.link)` to become `UNLISTEN(listener)` and
* `wl_signal_add(event, global_listener)` to become `LISTEN_GLOBAL(event, global_listener)`.

View File

@ -509,7 +509,7 @@ index def2562..1c9ab67 100644
+ wlr_log_init(CSYM(enum wlr_log_importance, log_level), NULL);
/* The Wayland display is managed by libwayland. It handles accepting
* clients from the Unix socket, manging Wayland globals, and so on. */
* clients from the Unix socket, managing Wayland globals, and so on. */
@@ -2454,7 +2576,7 @@ setup(void)
/* Initialize the scene graph used to lay out windows */

View File

@ -637,7 +637,7 @@ index 4816159..70e99be 100644
+ wlr_log_init(CSYM(enum wlr_log_importance, log_level), NULL);
/* The Wayland display is managed by libwayland. It handles accepting
* clients from the Unix socket, manging Wayland globals, and so on. */
* clients from the Unix socket, managing Wayland globals, and so on. */
@@ -2463,7 +2602,7 @@ setup(void)
/* Initialize the scene graph used to lay out windows */

View File

@ -7,7 +7,7 @@ To enable Xwayland support, you will need to enable it in the wlroots subproject
```sh
meson setup -Dwlroots:xwayland=enabled build
```
It is also reccomended to see the wlroots meson project configuration logs for any
It is also recommended to see the wlroots meson project configuration logs for any
unusual checks, such as requiring `hwdata` for the DRM backend.
### Download

View File

@ -28,8 +28,10 @@ static const Modekey modekeys[] = {
```
### Download
- [v0.8](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/modes/modes.patch)
- [git branch](https://codeberg.org/wochap/dwl/src/branch/v0.5/modes)
- [v0.5](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/modes/modes.patch)
- [v0.5](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/modes/modes-0.5.patch)
### Authors
- [unixchad](https://codeberg.org/unixchad)
- [wochap](https://codeberg.org/wochap)

View File

@ -0,0 +1,165 @@
From a32b85018ff2cea0fc9f9137789860a4aadc3b3a Mon Sep 17 00:00:00 2001
From: wochap <gean.marroquin@gmail.com>
Date: Wed, 6 Mar 2024 07:31:17 -0500
Subject: [PATCH] implement modes
like sway/river modes
---
config.def.h | 20 ++++++++++++++++++++
dwl.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 66 insertions(+)
diff --git a/config.def.h b/config.def.h
index db0babc..1616136 100644
--- a/config.def.h
+++ b/config.def.h
@@ -13,6 +13,13 @@ static const float urgentcolor[] = COLOR(0xff0000ff);
/* To conform the xdg-protocol, set the alpha to zero to restore the old behavior */
static const float fullscreen_bg[] = {0.1, 0.1, 0.1, 1.0}; /* You can also use glsl colors */
+enum {
+ BROWSER,
+};
+const char *modes_labels[] = {
+ "browser",
+};
+
/* tagging - TAGCOUNT must be no greater than 31 */
#define TAGCOUNT (9)
@@ -152,6 +159,8 @@ static const Key keys[] = {
TAGKEYS( XKB_KEY_9, XKB_KEY_parenleft, 8),
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Q, quit, {0} },
+ { MODKEY, XKB_KEY_b, entermode, {.i = BROWSER} },
+
/* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */
{ WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_Terminate_Server, quit, {0} },
/* Ctrl-Alt-Fx is used to switch to another VT, if you don't know what a VT is
@@ -162,6 +171,17 @@ static const Key keys[] = {
CHVT(7), CHVT(8), CHVT(9), CHVT(10), CHVT(11), CHVT(12),
};
+static const Modekey modekeys[] = {
+ /* mode modifier key function argument */
+ { BROWSER, { 0, XKB_KEY_f, spawn, SHCMD("firefox") } },
+ { BROWSER, { 0, XKB_KEY_f, entermode, {.i = NORMAL} } },
+ { BROWSER, { 0, XKB_KEY_b, spawn, SHCMD("brave") } },
+ { BROWSER, { 0, XKB_KEY_b, entermode, {.i = NORMAL} } },
+ { BROWSER, { 0, XKB_KEY_g, spawn, SHCMD("google-chrome-stable") } },
+ { BROWSER, { 0, XKB_KEY_g, entermode, {.i = NORMAL} } },
+ { BROWSER, { 0, XKB_KEY_Escape, entermode, {.i = NORMAL} } },
+};
+
static const Button buttons[] = {
{ MODKEY, BTN_LEFT, moveresize, {.ui = CurMove} },
{ MODKEY, BTN_MIDDLE, togglefloating, {0} },
diff --git a/dwl.c b/dwl.c
index ef27a1d..1ada006 100644
--- a/dwl.c
+++ b/dwl.c
@@ -139,6 +139,11 @@ typedef struct {
const Arg arg;
} Key;
+typedef struct {
+ int mode_index;
+ Key key;
+} Modekey;
+
typedef struct {
struct wl_list link;
struct wlr_keyboard *wlr_keyboard;
@@ -270,6 +275,7 @@ static void handlesig(int signo);
static void incnmaster(const Arg *arg);
static void inputdevice(struct wl_listener *listener, void *data);
static int keybinding(uint32_t mods, xkb_keysym_t sym);
+static int modekeybinding(uint32_t mods, xkb_keysym_t sym);
static void keypress(struct wl_listener *listener, void *data);
static void keypressmod(struct wl_listener *listener, void *data);
static int keyrepeat(void *data);
@@ -327,6 +333,7 @@ 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 entermode(const Arg *arg);
/* variables */
static const char broken[] = "broken";
@@ -377,6 +384,9 @@ static struct wlr_box sgeom;
static struct wl_list mons;
static Monitor *selmon;
+static const int NORMAL = -1;
+static int active_mode_index = NORMAL;
+
#ifdef XWAYLAND
static void activatex11(struct wl_listener *listener, void *data);
static void associatex11(struct wl_listener *listener, void *data);
@@ -1372,6 +1382,11 @@ keybinding(uint32_t mods, xkb_keysym_t sym)
*/
int handled = 0;
const Key *k;
+
+ if (active_mode_index >= 0) {
+ return modekeybinding(mods, sym);
+ }
+
for (k = keys; k < END(keys); k++) {
if (CLEANMASK(mods) == CLEANMASK(k->mod) &&
sym == k->keysym && k->func) {
@@ -1382,6 +1397,29 @@ keybinding(uint32_t mods, xkb_keysym_t sym)
return handled;
}
+int
+modekeybinding(uint32_t mods, xkb_keysym_t sym)
+{
+ int handled = 0;
+ const Modekey *mk;
+ const Key *k;
+
+ for (mk = modekeys; mk < END(modekeys); mk++) {
+ if (active_mode_index != mk->mode_index) {
+ continue;
+ }
+
+ k = &mk->key;
+ if (CLEANMASK(mods) == CLEANMASK(k->mod) &&
+ sym == k->keysym && k->func) {
+ k->func(&k->arg);
+ handled = 1;
+ }
+ }
+
+ return handled;
+}
+
void
keypress(struct wl_listener *listener, void *data)
{
@@ -1851,6 +1889,7 @@ printstatus(void)
printf("%s tags %u %u %u %u\n", m->wlr_output->name, occ, m->tagset[m->seltags],
sel, urg);
printf("%s layout %s\n", m->wlr_output->name, m->ltsymbol);
+ printf("%s mode %s\n", m->wlr_output->name, modes_labels[active_mode_index] ? modes_labels[active_mode_index] : "");
}
fflush(stdout);
}
@@ -2746,6 +2785,13 @@ zoom(const Arg *arg)
arrange(selmon);
}
+void
+entermode(const Arg *arg)
+{
+ active_mode_index = arg->i;
+ printstatus();
+}
+
#ifdef XWAYLAND
void
activatex11(struct wl_listener *listener, void *data)
--
2.42.0

View File

@ -1,21 +1,20 @@
From a32b85018ff2cea0fc9f9137789860a4aadc3b3a Mon Sep 17 00:00:00 2001
From: wochap <gean.marroquin@gmail.com>
Date: Wed, 6 Mar 2024 07:31:17 -0500
Subject: [PATCH] implement modes
From 915115151a4429bab38dd8cff2268f34ee23984f Mon Sep 17 00:00:00 2001
From: nate zhou <gnuunixchad@outlook.com>
Date: Mon, 2 Mar 2026 19:23:59 +0800
Subject: [PATCH] Patch: modes-0.8.patch
like sway/river modes
---
config.def.h | 20 ++++++++++++++++++++
dwl.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 66 insertions(+)
dwl.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 67 insertions(+)
diff --git a/config.def.h b/config.def.h
index db0babc..1616136 100644
index 8a6eda0..bc88ea5 100644
--- a/config.def.h
+++ b/config.def.h
@@ -13,6 +13,13 @@ static const float urgentcolor[] = COLOR(0xff0000ff);
/* To conform the xdg-protocol, set the alpha to zero to restore the old behavior */
static const float fullscreen_bg[] = {0.1, 0.1, 0.1, 1.0}; /* You can also use glsl colors */
@@ -14,6 +14,13 @@ 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 */
+enum {
+ BROWSER,
@ -27,16 +26,16 @@ index db0babc..1616136 100644
/* tagging - TAGCOUNT must be no greater than 31 */
#define TAGCOUNT (9)
@@ -152,6 +159,8 @@ static const Key keys[] = {
@@ -155,6 +162,8 @@ static const Key keys[] = {
TAGKEYS( XKB_KEY_9, XKB_KEY_parenleft, 8),
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Q, quit, {0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_q, quit, {0} },
+ { MODKEY, XKB_KEY_b, entermode, {.i = BROWSER} },
+
/* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */
{ WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_Terminate_Server, quit, {0} },
/* Ctrl-Alt-Fx is used to switch to another VT, if you don't know what a VT is
@@ -162,6 +171,17 @@ static const Key keys[] = {
@@ -165,6 +174,17 @@ static const Key keys[] = {
CHVT(7), CHVT(8), CHVT(9), CHVT(10), CHVT(11), CHVT(12),
};
@ -55,10 +54,10 @@ index db0babc..1616136 100644
{ MODKEY, BTN_LEFT, moveresize, {.ui = CurMove} },
{ MODKEY, BTN_MIDDLE, togglefloating, {0} },
diff --git a/dwl.c b/dwl.c
index ef27a1d..1ada006 100644
index 44f3ad9..9247541 100644
--- a/dwl.c
+++ b/dwl.c
@@ -139,6 +139,11 @@ typedef struct {
@@ -148,6 +148,11 @@ typedef struct {
const Arg arg;
} Key;
@ -68,9 +67,9 @@ index ef27a1d..1ada006 100644
+} Modekey;
+
typedef struct {
struct wl_list link;
struct wlr_keyboard *wlr_keyboard;
@@ -270,6 +275,7 @@ static void handlesig(int signo);
struct wlr_keyboard_group *wlr_group;
@@ -292,6 +297,7 @@ static void handlesig(int signo);
static void incnmaster(const Arg *arg);
static void inputdevice(struct wl_listener *listener, void *data);
static int keybinding(uint32_t mods, xkb_keysym_t sym);
@ -78,27 +77,27 @@ index ef27a1d..1ada006 100644
static void keypress(struct wl_listener *listener, void *data);
static void keypressmod(struct wl_listener *listener, void *data);
static int keyrepeat(void *data);
@@ -327,6 +333,7 @@ static Monitor *xytomon(double x, double y);
@@ -351,6 +357,7 @@ 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 entermode(const Arg *arg);
/* variables */
static const char broken[] = "broken";
@@ -377,6 +384,9 @@ static struct wlr_box sgeom;
static pid_t child_pid = -1;
@@ -406,6 +413,9 @@ static struct wlr_box sgeom;
static struct wl_list mons;
static Monitor *selmon;
+static const int NORMAL = -1;
+static int active_mode_index = NORMAL;
+
#ifdef XWAYLAND
static void activatex11(struct wl_listener *listener, void *data);
static void associatex11(struct wl_listener *listener, void *data);
@@ -1372,6 +1382,11 @@ keybinding(uint32_t mods, xkb_keysym_t sym)
/* global event handlers */
static struct wl_listener cursor_axis = {.notify = axisnotify};
static struct wl_listener cursor_button = {.notify = buttonpress};
@@ -1614,6 +1624,11 @@ keybinding(uint32_t mods, xkb_keysym_t sym)
* processing.
*/
int handled = 0;
const Key *k;
+
+ if (active_mode_index >= 0) {
@ -106,10 +105,10 @@ index ef27a1d..1ada006 100644
+ }
+
for (k = keys; k < END(keys); k++) {
if (CLEANMASK(mods) == CLEANMASK(k->mod) &&
sym == k->keysym && k->func) {
@@ -1382,6 +1397,29 @@ keybinding(uint32_t mods, xkb_keysym_t sym)
return handled;
if (CLEANMASK(mods) == CLEANMASK(k->mod)
&& xkb_keysym_to_lower(sym) == xkb_keysym_to_lower(k->keysym)
@@ -1625,6 +1640,30 @@ keybinding(uint32_t mods, xkb_keysym_t sym)
return 0;
}
+int
@ -134,19 +133,20 @@ index ef27a1d..1ada006 100644
+
+ return handled;
+}
+
+
void
keypress(struct wl_listener *listener, void *data)
{
@@ -1851,6 +1889,7 @@ printstatus(void)
printf("%s tags %u %u %u %u\n", m->wlr_output->name, occ, m->tagset[m->seltags],
sel, urg);
@@ -2119,6 +2158,7 @@ printstatus(void)
printf("%s tags %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32"\n",
m->wlr_output->name, occ, m->tagset[m->seltags], sel, urg);
printf("%s layout %s\n", m->wlr_output->name, m->ltsymbol);
+ printf("%s mode %s\n", m->wlr_output->name, modes_labels[active_mode_index] ? modes_labels[active_mode_index] : "");
}
fflush(stdout);
}
@@ -2746,6 +2785,13 @@ zoom(const Arg *arg)
@@ -3075,6 +3115,13 @@ zoom(const Arg *arg)
arrange(selmon);
}
@ -161,5 +161,5 @@ index ef27a1d..1ada006 100644
void
activatex11(struct wl_listener *listener, void *data)
--
2.42.0
2.53.0

View File

@ -1,7 +1,7 @@
From e42ca1c539437d3098d80983cfe2ad6f938d7a08 Mon Sep 17 00:00:00 2001
From: Eldar Yusupov <eyusupov@gmail.com>
Date: Sun, 17 Mar 2024 19:12:29 +0300
Subject: [PATCH] Restore correct montior for client when it is reattached
Subject: [PATCH] Restore correct monitor for client when it is reattached
---
dwl.c | 24 ++++++++++++++++++++++--

View File

@ -7,7 +7,7 @@ Most of this patch is stored in river-control.h, It contains a list of functions
This patch's main intended use case is to have a startup script that calls dwlctl a bunch to add all the binds/rules you want, without the need of restarting dwl if you make any changes to the list of binds/rules.
This patch also adds keybind modes which allow switching between a diffrent sets of keybinds on the fly.
This patch also adds keybind modes which allow switching between a different sets of keybinds on the fly.
Also you can set a keybind mode as oneshot (meaning as soon as a keybind is activated the mode is switched) by using...
`dwlctl oneshot-mode _layout_you_want_to_make_oneshot_here_ _layout_you_want_to_switch_to_after_keybind_pressed_`
Just make sure to set a mode as oneshot after creating a bind under it otherwise it won't work.

View File

@ -120,7 +120,7 @@ index 95c2afa..ccc3edb 100644
-static const Key keys[] = {
+/* note keys gets cleared with riverctl clear-binds but the keys_always are excluded from being cleared
+ * this is to have a list of fallback keybinds if your riverctl script fails
+ * if you won't like to have keys[] declared commented out the KEYS_USED macro bellow to disable the functionality*/
+ * if you won't like to have keys[] declared commented out the KEYS_USED macro below to disable the functionality*/
+#define KEYS_USED
+static Key keys[] = {
/* Note that Shift changes certain key codes: c -> C, 2 -> at, etc. */
@ -1267,7 +1267,7 @@ index 0000000..599cffe
+ .add_argument = zriver_control_add_argument,
+ .destroy = zriver_control_destroy,
+};
+static void zriver_control_handle_destory(struct wl_resource *resource) {
+static void zriver_control_handle_destroy(struct wl_resource *resource) {
+ struct zriver_arg_list_resource *zriver_arg_list_resource = wl_resource_get_user_data(resource);
+ free(zriver_arg_list_resource);
+ printf("handle destroy\n");
@ -1281,7 +1281,7 @@ index 0000000..599cffe
+
+
+ wl_resource_set_implementation(resource, &zriver_control_interface,
+ zriver_arg_list_resource, zriver_control_handle_destory);
+ zriver_arg_list_resource, zriver_control_handle_destroy);
+}
--
2.51.0

View File

@ -1,6 +1,6 @@
### Description
Makes sticky work as expected with singletagset. The sticky window will
stay on original output until you explicitely put it to a different monitor.
stay on original output until you explicitly put it to a different monitor.
This patch expects both [singletagset](https://codeberg.org/dwl/dwl-patches/src/branch/main/patches/singletagset) and [sticky](https://codeberg.org/dwl/dwl-patches/src/branch/main/patches/sticky) patches to be already in
your tree committed. It applies onto them.

View File

@ -13,7 +13,7 @@ tempdir="/tmp/screenshots"
mkdir -p "$tempdir"
file="$(mktemp -p "$tempdir" "XXXXXX.png")"
# Grab the screenshot! Very conviniently, GEOMETRY format matches the one
# Grab the screenshot! Very conveniently, GEOMETRY format matches the one
# expected by grim
grim -g "$GEOMETRY" "$file" || exit

View File

@ -6,9 +6,11 @@ Swap the focused window with the window (no floating) to the left, right, above,
**NOTE:** this patch uses the same algorithm that River uses to select the window in the given direction.
### Download
- [v0.8](https://codeberg.org/dwl/dwl-patches/raw/commit/a5f66f3d9cdbfc1a3fd7ae9c0288c35c8ec19479/patches/swapandfocusdir/swapandfocusdir.patch)
- [git branch](https://codeberg.org/wochap/dwl/src/branch/v0.5/swapandfocusdir)
- [2024-07-09](https://codeberg.org/dwl/dwl-patches/raw/commit/13d96b51b54500dd24544cf3a73c61b7a1414bc6/patches/swapandfocusdir/swapandfocusdir.patch)
- [v0.5](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/swapandfocusdir/swapandfocusdir.patch)
### Authors
- [unixchad](https://codeberg.org/unixchad)
- [wochap](https://codeberg.org/wochap)

View File

@ -1,7 +1,7 @@
From 285470897406b653e77d732a77356aaf9a70b799 Mon Sep 17 00:00:00 2001
From: wochap <gean.marroquin@gmail.com>
Date: Fri, 5 Jul 2024 12:37:39 -0500
Subject: [PATCH] implement swapandfocusdir
From 6972f383921a892e30ac10b42a3d3ce47b2f93e5 Mon Sep 17 00:00:00 2001
From: nate zhou <gnuunixchad@outlook.com>
Date: Sat, 28 Feb 2026 20:48:27 +0800
Subject: [PATCH] Patch: swapandfocusdir.patch
---
config.def.h | 8 +++
@ -9,10 +9,10 @@ Subject: [PATCH] implement swapandfocusdir
2 files changed, 172 insertions(+)
diff --git a/config.def.h b/config.def.h
index 22d2171..724e15e 100644
index 8a6eda0..6e7fc39 100644
--- a/config.def.h
+++ b/config.def.h
@@ -129,6 +129,14 @@ static const Key keys[] = {
@@ -125,6 +125,14 @@ static const Key keys[] = {
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, spawn, {.v = termcmd} },
{ MODKEY, XKB_KEY_j, focusstack, {.i = +1} },
{ MODKEY, XKB_KEY_k, focusstack, {.i = -1} },
@ -28,10 +28,10 @@ index 22d2171..724e15e 100644
{ MODKEY, XKB_KEY_d, incnmaster, {.i = -1} },
{ MODKEY, XKB_KEY_h, setmfact, {.f = -0.05f} },
diff --git a/dwl.c b/dwl.c
index dc0437e..844c1f5 100644
index 44f3ad9..1893fbd 100644
--- a/dwl.c
+++ b/dwl.c
@@ -241,6 +241,11 @@ typedef struct {
@@ -239,6 +239,11 @@ typedef struct {
struct wl_listener destroy;
} SessionLock;
@ -51,8 +51,8 @@ index dc0437e..844c1f5 100644
+static void swapdir(const Arg *arg);
static Client *focustop(Monitor *m);
static void fullscreennotify(struct wl_listener *listener, void *data);
static void handlesig(int signo);
@@ -1425,6 +1432,163 @@ focusstack(const Arg *arg)
static void gpureset(struct wl_listener *listener, void *data);
@@ -1511,6 +1518,163 @@ focusstack(const Arg *arg)
focusclient(c, 1);
}
@ -213,9 +213,9 @@ index dc0437e..844c1f5 100644
+ arrange(selmon);
+}
+
/* We probably should change the name of this, it sounds like
/* We probably should change the name of this: it sounds like it
* will focus the topmost client of this mon, when actually will
* only return that client */
--
2.45.1
2.53.0

View File

@ -1,13 +0,0 @@
### Description
This combines
[tablet-input](../tablet-input) and
[touch-input](../touch-input)
into a single appliable patch.
The two patches modify similar code areas in `dwl.c` and will conflict if one attempts to direcly apply both.
### Download
- [tablet-and-touch-wlroots-next-f4249db.patch](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/tablet-and-touch/tablet-and-touch-wlroots-next-f4249db.patch)
- [tablet-and-touch-0.8.patch](https://codeberg.org/dwl/dwl-patches/raw/branch/main/patches/tablet-and-touch/tablet-and-touch-0.8.patch)
### Authors
- [fauxmight](https://codeberg.org/fauxmight)

View File

@ -1,584 +0,0 @@
From f0f797f8abebc4624c7e6bdfbb1ec2a3122f3914 Mon Sep 17 00:00:00 2001
From: A Frederick Christensen <dwl@ivories.org>
Date: Thu, 26 Feb 2026 23:20:03 -0600
Subject: [PATCH] Apply tablet-and-touch patch
---
Makefile | 6 +-
config.def.h | 1 +
dwl.c | 398 +++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 404 insertions(+), 1 deletion(-)
diff --git a/Makefile b/Makefile
index 578194f..e0d1835 100644
--- a/Makefile
+++ b/Makefile
@@ -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 \
+ tablet-v2-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 $@
+tablet-v2-protocol.h:
+ $(WAYLAND_SCANNER) server-header \
+ $(WAYLAND_PROTOCOLS)/unstable/tablet/tablet-unstable-v2.xml $@
config.h:
cp config.def.h $@
diff --git a/config.def.h b/config.def.h
index 8a6eda0..1f20dfd 100644
--- a/config.def.h
+++ b/config.def.h
@@ -4,6 +4,7 @@
((hex >> 8) & 0xFF) / 255.0f, \
(hex & 0xFF) / 255.0f }
/* appearance */
+static const int tabletmaptosurface = 0; /* map tablet input to surface(1) or monitor(0) */
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 */
diff --git a/dwl.c b/dwl.c
index 44f3ad9..5ed467d 100644
--- a/dwl.c
+++ b/dwl.c
@@ -51,6 +51,10 @@
#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_tablet_pad.h>
+#include <wlr/types/wlr_tablet_tool.h>
+#include <wlr/types/wlr_tablet_v2.h>
+#include <wlr/types/wlr_touch.h>
#include <wlr/types/wlr_viewporter.h>
#include <wlr/types/wlr_virtual_keyboard_v1.h>
#include <wlr/types/wlr_virtual_pointer_v1.h>
@@ -161,6 +165,12 @@ typedef struct {
struct wl_listener destroy;
} KeyboardGroup;
+typedef struct TouchGroup {
+ struct wl_list link;
+ struct wlr_touch *touch;
+ Monitor *m;
+} TouchGroup;
+
typedef struct {
/* Must keep this field first */
unsigned int type; /* LayerShell */
@@ -268,7 +278,10 @@ static void createnotify(struct wl_listener *listener, void *data);
static void createpointer(struct wlr_pointer *pointer);
static void createpointerconstraint(struct wl_listener *listener, void *data);
static void createpopup(struct wl_listener *listener, void *data);
+static void createtablet(struct wlr_input_device *device);
+static void createtouch(struct wlr_touch *touch);
static void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint);
+static void createtouch(struct wlr_touch *touch);
static void cursorframe(struct wl_listener *listener, void *data);
static void cursorwarptohint(void);
static void destroydecoration(struct wl_listener *listener, void *data);
@@ -281,6 +294,9 @@ 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 destroytablet(struct wl_listener *listener, void *data);
+static void destroytabletsurfacenotify(struct wl_listener *listener, void *data);
+static void destroytablettool(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);
@@ -333,11 +349,20 @@ 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 tablettoolmotion(struct wlr_tablet_v2_tablet_tool *tool, bool change_x, bool change_y, double x, double y, double dx, double dy);
+static void tablettoolproximity(struct wl_listener *listener, void *data);
+static void tablettoolaxis(struct wl_listener *listener, void *data);
+static void tablettoolbutton(struct wl_listener *listener, void *data);
+static void tablettooltip(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 toggletag(const Arg *arg);
static void toggleview(const Arg *arg);
+static void touchdown(struct wl_listener *listener, void *data);
+static void touchup(struct wl_listener *listener, void *data);
+static void touchframe(struct wl_listener *listener, void *data);
+static void touchmotion(struct wl_listener *listener, void *data);
static void unlocksession(struct wl_listener *listener, void *data);
static void unmaplayersurfacenotify(struct wl_listener *listener, void *data);
static void unmapnotify(struct wl_listener *listener, void *data);
@@ -390,6 +415,13 @@ static struct wlr_pointer_constraint_v1 *active_constraint;
static struct wlr_cursor *cursor;
static struct wlr_xcursor_manager *cursor_mgr;
+static struct wlr_tablet_manager_v2 *tablet_mgr;
+static struct wlr_tablet_v2_tablet *tablet = NULL;
+static struct wlr_tablet_v2_tablet_tool *tablet_tool = NULL;
+static struct wlr_tablet_v2_tablet_pad *tablet_pad = NULL;
+static struct wlr_surface *tablet_curr_surface = NULL;
+static struct wl_listener destroy_tablet_surface_listener = {.notify = destroytabletsurfacenotify};
+
static struct wlr_scene_rect *root_bg;
static struct wlr_session_lock_manager_v1 *session_lock_mgr;
static struct wlr_scene_rect *locked_bg;
@@ -405,6 +437,7 @@ static struct wlr_output_layout *output_layout;
static struct wlr_box sgeom;
static struct wl_list mons;
static Monitor *selmon;
+static struct wl_list touches;
/* global event handlers */
static struct wl_listener cursor_axis = {.notify = axisnotify};
@@ -412,6 +445,12 @@ static struct wl_listener cursor_button = {.notify = buttonpress};
static struct wl_listener cursor_frame = {.notify = cursorframe};
static struct wl_listener cursor_motion = {.notify = motionrelative};
static struct wl_listener cursor_motion_absolute = {.notify = motionabsolute};
+static struct wl_listener tablet_device_destroy = {.notify = destroytablet};
+static struct wl_listener tablet_tool_axis = {.notify = tablettoolaxis};
+static struct wl_listener tablet_tool_button = {.notify = tablettoolbutton};
+static struct wl_listener tablet_tool_destroy = {.notify = destroytablettool};
+static struct wl_listener tablet_tool_proximity = {.notify = tablettoolproximity};
+static struct wl_listener tablet_tool_tip = {.notify = tablettooltip};
static struct wl_listener gpu_reset = {.notify = gpureset};
static struct wl_listener layout_change = {.notify = updatemons};
static struct wl_listener new_idle_inhibitor = {.notify = createidleinhibitor};
@@ -434,6 +473,10 @@ static struct wl_listener request_set_sel = {.notify = setsel};
static struct wl_listener request_set_cursor_shape = {.notify = setcursorshape};
static struct wl_listener request_start_drag = {.notify = requeststartdrag};
static struct wl_listener start_drag = {.notify = startdrag};
+static struct wl_listener touch_down = {.notify = touchdown};
+static struct wl_listener touch_frame = {.notify = touchframe};
+static struct wl_listener touch_motion = {.notify = touchmotion};
+static struct wl_listener touch_up = {.notify = touchup};
static struct wl_listener new_session_lock = {.notify = locksession};
#ifdef XWAYLAND
@@ -781,6 +824,10 @@ cleanuplisteners(void)
wl_list_remove(&request_set_cursor_shape.link);
wl_list_remove(&request_start_drag.link);
wl_list_remove(&start_drag.link);
+ wl_list_remove(&touch_down.link);
+ wl_list_remove(&touch_frame.link);
+ wl_list_remove(&touch_motion.link);
+ wl_list_remove(&touch_up.link);
wl_list_remove(&new_session_lock.link);
#ifdef XWAYLAND
wl_list_remove(&new_xwayland_surface.link);
@@ -1199,6 +1246,38 @@ createpopup(struct wl_listener *listener, void *data)
LISTEN_STATIC(&popup->base->surface->events.commit, commitpopup);
}
+void
+createtablet(struct wlr_input_device *device)
+{
+ if (!tablet) {
+ struct libinput_device *device_handle = NULL;
+ if (!wlr_input_device_is_libinput(device) ||
+ !(device_handle = wlr_libinput_get_device_handle(device)))
+ return;
+
+ tablet = wlr_tablet_create(tablet_mgr, seat, device);
+ wl_signal_add(&tablet->wlr_device->events.destroy, &tablet_device_destroy);
+ if (libinput_device_config_send_events_get_modes(device_handle)) {
+ libinput_device_config_send_events_set_mode(device_handle, send_events_mode);
+ wlr_cursor_attach_input_device(cursor, device);
+ }
+ } else if (device == tablet->wlr_device) {
+ wlr_log(WLR_ERROR, "createtablet: duplicate device");
+ } else {
+ wlr_log(WLR_ERROR, "createtablet: already have one tablet");
+ }
+}
+
+void
+createtouch(struct wlr_touch *wlr_touch)
+{
+ TouchGroup *touch = ecalloc(1, sizeof(TouchGroup));
+
+ touch->touch = wlr_touch;
+ wl_list_insert(&touches, &touch->link);
+ wlr_cursor_attach_input_device(cursor, &wlr_touch->base);
+}
+
void
cursorconstrain(struct wlr_pointer_constraint_v1 *constraint)
{
@@ -1383,6 +1462,29 @@ destroykeyboardgroup(struct wl_listener *listener, void *data)
free(group);
}
+void
+destroytablet(struct wl_listener *listener, void *data)
+{
+ wl_list_remove(&tablet_device_destroy.link);
+ wlr_cursor_detach_input_device(cursor, tablet->wlr_device);
+ tablet = NULL;
+}
+
+void
+destroytabletsurfacenotify(struct wl_listener *listener, void *data)
+{
+ if (tablet_curr_surface)
+ wl_list_remove(&destroy_tablet_surface_listener.link);
+ tablet_curr_surface = NULL;
+}
+
+void
+destroytablettool(struct wl_listener *listener, void *data)
+{
+ destroytabletsurfacenotify(NULL, NULL);
+ tablet_tool = NULL;
+}
+
Monitor *
dirtomon(enum wlr_direction dir)
{
@@ -1590,6 +1692,15 @@ inputdevice(struct wl_listener *listener, void *data)
case WLR_INPUT_DEVICE_POINTER:
createpointer(wlr_pointer_from_input_device(device));
break;
+ case WLR_INPUT_DEVICE_TABLET:
+ createtablet(device);
+ break;
+ case WLR_INPUT_DEVICE_TABLET_PAD:
+ tablet_pad = wlr_tablet_pad_create(tablet_mgr, seat, device);
+ break;
+ case WLR_INPUT_DEVICE_TOUCH:
+ createtouch(wlr_touch_from_input_device(device));
+ break;
default:
/* TODO handle other input device types */
break;
@@ -1602,6 +1713,8 @@ inputdevice(struct wl_listener *listener, void *data)
caps = WL_SEAT_CAPABILITY_POINTER;
if (!wl_list_empty(&kb_group->wlr_group->devices))
caps |= WL_SEAT_CAPABILITY_KEYBOARD;
+ if (!wl_list_empty(&touches))
+ caps |= WL_SEAT_CAPABILITY_TOUCH;
wlr_seat_set_capabilities(seat, caps);
}
@@ -2585,6 +2698,8 @@ setup(void)
relative_pointer_mgr = wlr_relative_pointer_manager_v1_create(dpy);
+ tablet_mgr = wlr_tablet_v2_create(dpy);
+
/*
* Creates a cursor, which is a wlroots utility for tracking the cursor
* image shown on screen.
@@ -2614,6 +2729,18 @@ setup(void)
wl_signal_add(&cursor->events.button, &cursor_button);
wl_signal_add(&cursor->events.axis, &cursor_axis);
wl_signal_add(&cursor->events.frame, &cursor_frame);
+ wl_signal_add(&cursor->events.tablet_tool_proximity, &tablet_tool_proximity);
+ wl_signal_add(&cursor->events.tablet_tool_axis, &tablet_tool_axis);
+ wl_signal_add(&cursor->events.tablet_tool_button, &tablet_tool_button);
+ wl_signal_add(&cursor->events.tablet_tool_tip, &tablet_tool_tip);
+
+
+ wl_list_init(&touches);
+
+ wl_signal_add(&cursor->events.touch_down, &touch_down);
+ wl_signal_add(&cursor->events.touch_frame, &touch_frame);
+ wl_signal_add(&cursor->events.touch_motion, &touch_motion);
+ wl_signal_add(&cursor->events.touch_up, &touch_up);
cursor_shape_mgr = wlr_cursor_shape_manager_v1_create(dpy, 1);
wl_signal_add(&cursor_shape_mgr->events.request_set_shape, &request_set_cursor_shape);
@@ -2709,6 +2836,163 @@ tagmon(const Arg *arg)
setmon(sel, dirtomon(arg->i), 0);
}
+void
+tabletapplymap(double x, double y, struct wlr_input_device *dev)
+{
+ Client *p;
+ struct wlr_box geom = {0};
+ if (tabletmaptosurface && tablet_curr_surface) {
+ toplevel_from_wlr_surface(tablet_curr_surface, &p, NULL);
+ if (p) {
+ for (; client_get_parent(p); p = client_get_parent(p));
+ geom.x = p->geom.x + p->bw;
+ geom.y = p->geom.y + p->bw;
+ geom.width = p->geom.width - 2 * p->bw;
+ geom.height = p->geom.height - 2 * p->bw;
+ }
+ }
+ wlr_cursor_map_input_to_region(cursor, dev, &geom);
+ wlr_cursor_map_input_to_output(cursor, dev, selmon->wlr_output);
+}
+
+void
+tablettoolmotion(struct wlr_tablet_v2_tablet_tool *tool, bool change_x, bool change_y,
+ double x, double y, double dx, double dy)
+{
+ struct wlr_surface *surface = NULL;
+ double sx, sy;
+
+ if (!change_x && !change_y)
+ return;
+
+ tabletapplymap(x, y, tablet->wlr_device);
+
+ // TODO: apply constraints
+ switch (tablet_tool->wlr_tool->type) {
+ case WLR_TABLET_TOOL_TYPE_LENS:
+ case WLR_TABLET_TOOL_TYPE_MOUSE:
+ wlr_cursor_move(cursor, tablet->wlr_device, dx, dy);
+ break;
+ default:
+ wlr_cursor_warp_absolute(cursor, tablet->wlr_device, change_x ? x : NAN, change_y ? y : NAN);
+ break;
+ }
+
+ motionnotify(0, NULL, 0, 0, 0, 0);
+
+ xytonode(cursor->x, cursor->y, &surface, NULL, NULL, &sx, &sy);
+ if (surface && !wlr_surface_accepts_tablet_v2(surface, tablet))
+ surface = NULL;
+
+ if (surface != tablet_curr_surface) {
+ if (tablet_curr_surface) {
+ // TODO: wait until all buttons released before leaving
+ if (tablet_tool)
+ wlr_tablet_v2_tablet_tool_notify_proximity_out(tablet_tool);
+ if (tablet_pad)
+ wlr_tablet_v2_tablet_pad_notify_leave(tablet_pad, tablet_curr_surface);
+ wl_list_remove(&destroy_tablet_surface_listener.link);
+ }
+ if (surface) {
+ if (tablet_pad)
+ wlr_tablet_v2_tablet_pad_notify_enter(tablet_pad, tablet, surface);
+ if (tablet_tool)
+ wlr_tablet_v2_tablet_tool_notify_proximity_in(tablet_tool, tablet, surface);
+ wl_signal_add(&surface->events.destroy, &destroy_tablet_surface_listener);
+ }
+ tablet_curr_surface = surface;
+ }
+
+ if (surface)
+ wlr_tablet_v2_tablet_tool_notify_motion(tablet_tool, sx, sy);
+}
+
+void
+tablettoolproximity(struct wl_listener *listener, void *data)
+{
+ struct wlr_tablet_tool_proximity_event *event = data;
+ struct wlr_tablet_tool *tool = event->tool;
+
+ if (!tablet_tool) {
+ tablet_tool = wlr_tablet_tool_create(tablet_mgr, seat, tool);
+ wl_signal_add(&tablet_tool->wlr_tool->events.destroy, &tablet_tool_destroy);
+ wl_signal_add(&tablet_tool->events.set_cursor, &request_cursor);
+ }
+
+ switch (event->state) {
+ case WLR_TABLET_TOOL_PROXIMITY_OUT:
+ wlr_tablet_v2_tablet_tool_notify_proximity_out(tablet_tool);
+ destroytabletsurfacenotify(NULL, NULL);
+ break;
+ case WLR_TABLET_TOOL_PROXIMITY_IN:
+ tablettoolmotion(tablet_tool, true, true, event->x, event->y, 0, 0);
+ break;
+ }
+}
+
+double tilt_x = 0;
+double tilt_y = 0;
+
+void
+tablettoolaxis(struct wl_listener *listener, void *data)
+{
+ struct wlr_tablet_tool_axis_event *event = data;
+
+ tablettoolmotion(tablet_tool,
+ event->updated_axes & WLR_TABLET_TOOL_AXIS_X,
+ event->updated_axes & WLR_TABLET_TOOL_AXIS_Y,
+ event->x, event->y, event->dx, event->dy);
+
+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_PRESSURE)
+ wlr_tablet_v2_tablet_tool_notify_pressure(tablet_tool, event->pressure);
+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_DISTANCE)
+ wlr_tablet_v2_tablet_tool_notify_distance(tablet_tool, event->distance);
+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_X)
+ tilt_x = event->tilt_x;
+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_Y)
+ tilt_y = event->tilt_y;
+ if (event->updated_axes & (WLR_TABLET_TOOL_AXIS_TILT_X | WLR_TABLET_TOOL_AXIS_TILT_Y))
+ wlr_tablet_v2_tablet_tool_notify_tilt(tablet_tool, tilt_x, tilt_y);
+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_ROTATION)
+ wlr_tablet_v2_tablet_tool_notify_rotation(tablet_tool, event->rotation);
+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_SLIDER)
+ wlr_tablet_v2_tablet_tool_notify_slider(tablet_tool, event->slider);
+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_WHEEL)
+ wlr_tablet_v2_tablet_tool_notify_wheel(tablet_tool, event->wheel_delta, 0);
+}
+
+void
+tablettoolbutton(struct wl_listener *listener, void *data)
+{
+ struct wlr_tablet_tool_button_event *event = data;
+ wlr_tablet_v2_tablet_tool_notify_button(tablet_tool, event->button,
+ (enum zwp_tablet_pad_v2_button_state)event->state);
+}
+
+void
+tablettooltip(struct wl_listener *listener, void *data)
+{
+ struct wlr_tablet_tool_tip_event *event = data;
+
+ if (!tablet_curr_surface) {
+ struct wlr_pointer_button_event fakeptrbtnevent = {
+ .button = BTN_LEFT,
+ .state = event->state == WLR_TABLET_TOOL_TIP_UP ?
+ WL_POINTER_BUTTON_STATE_RELEASED : WL_POINTER_BUTTON_STATE_PRESSED,
+ .time_msec = event->time_msec,
+ };
+ buttonpress(NULL, (void *)&fakeptrbtnevent);
+ }
+
+ if (event->state == WLR_TABLET_TOOL_TIP_UP) {
+ wlr_tablet_v2_tablet_tool_notify_up(tablet_tool);
+ return;
+ }
+
+ wlr_tablet_v2_tablet_tool_notify_down(tablet_tool);
+ wlr_tablet_tool_v2_start_implicit_grab(tablet_tool);
+}
+
void
tile(Monitor *m)
{
@@ -2787,6 +3071,120 @@ toggleview(const Arg *arg)
printstatus();
}
+void
+touchdown(struct wl_listener *listener, void *data)
+{
+ struct wlr_touch_down_event *event = data;
+ double lx, ly;
+ double sx, sy;
+ struct wlr_surface *surface;
+ Client *c = NULL;
+ uint32_t serial = 0;
+ Monitor *m;
+
+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
+
+ // Map the input to the appropriate output, to ensure that rotation is
+ // handled.
+ wl_list_for_each(m, &mons, link) {
+ if (m == NULL || m->wlr_output == NULL) {
+ continue;
+ }
+ if (event->touch->output_name != NULL && 0 != strcmp(event->touch->output_name, m->wlr_output->name)) {
+ continue;
+ }
+
+ wlr_cursor_map_input_to_output(cursor, &event->touch->base, m->wlr_output);
+ }
+
+ wlr_cursor_absolute_to_layout_coords(cursor, &event->touch->base, event->x, event->y, &lx, &ly);
+
+ /* Find the client under the pointer and send the event along. */
+ xytonode(lx, ly, &surface, &c, NULL, &sx, &sy);
+ if (sloppyfocus)
+ focusclient(c, 0);
+
+ if (surface != NULL) {
+ serial = wlr_seat_touch_notify_down(seat, surface, event->time_msec, event->touch_id, sx, sy);
+ }
+
+ if (serial && wlr_seat_touch_num_points(seat) == 1) {
+ /* Emulate a mouse click if the touch event wasn't handled */
+ struct wlr_pointer_button_event *button_event = data;
+ struct wlr_pointer_motion_absolute_event *motion_event = data;
+ double dx, dy;
+
+ wlr_cursor_absolute_to_layout_coords(cursor, &motion_event->pointer->base, motion_event->x, motion_event->y, &lx, &ly);
+ wlr_cursor_warp_closest(cursor, &motion_event->pointer->base, lx, ly);
+ dx = lx - cursor->x;
+ dy = ly - cursor->y;
+ motionnotify(motion_event->time_msec, &motion_event->pointer->base, dx, dy, dx, dy);
+
+ button_event->button = BTN_LEFT;
+ button_event->state = WL_POINTER_BUTTON_STATE_PRESSED;
+ buttonpress(listener, button_event);
+ }
+}
+
+void
+touchup(struct wl_listener *listener, void *data)
+{
+ struct wlr_touch_up_event *event = data;
+
+ if (!wlr_seat_touch_get_point(seat, event->touch_id)) {
+ return;
+ }
+
+ if (wlr_seat_touch_num_points(seat) == 1) {
+ struct wlr_pointer_button_event *button_event = data;
+
+ button_event->button = BTN_LEFT;
+ button_event->state = WL_POINTER_BUTTON_STATE_RELEASED;
+ buttonpress(listener, button_event);
+ }
+
+ wlr_seat_touch_notify_up(seat, event->time_msec, event->touch_id);
+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
+}
+
+void
+touchframe(struct wl_listener *listener, void *data)
+{
+ wlr_seat_touch_notify_frame(seat);
+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
+}
+
+void
+touchmotion(struct wl_listener *listener, void *data)
+{
+ struct wlr_touch_motion_event *event = data;
+ double lx, ly;
+ double sx, sy;
+ struct wlr_surface *surface;
+ Client *c = NULL;
+
+ if (!wlr_seat_touch_get_point(seat, event->touch_id)) {
+ return;
+ }
+
+ wlr_cursor_absolute_to_layout_coords(cursor, &event->touch->base, event->x, event->y, &lx, &ly);
+ xytonode(lx, ly, &surface, &c, NULL, &sx, &sy);
+
+ if (c != NULL && surface != NULL) {
+ if (sloppyfocus)
+ focusclient(c, 0);
+ wlr_seat_touch_point_focus(seat, surface, event->time_msec, event->touch_id, sx, sy);
+ } else {
+ if (sloppyfocus)
+ focusclient(NULL, 0);
+ wlr_seat_touch_point_clear_focus(seat, event->time_msec, event->touch_id);
+ }
+ wlr_seat_touch_notify_motion(seat, event->time_msec, event->touch_id, sx, sy);
+
+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
+}
+
+
void
unlocksession(struct wl_listener *listener, void *data)
{
--
2.52.0

View File

@ -1,584 +0,0 @@
From 78eda59b86ca01188fd645fe4062dda75d60acb1 Mon Sep 17 00:00:00 2001
From: A Frederick Christensen <dwl@ivories.org>
Date: Thu, 26 Feb 2026 23:23:49 -0600
Subject: [PATCH] Apply tablet-and-touch patch
---
Makefile | 6 +-
config.def.h | 1 +
dwl.c | 398 +++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 404 insertions(+), 1 deletion(-)
diff --git a/Makefile b/Makefile
index 578194f..e0d1835 100644
--- a/Makefile
+++ b/Makefile
@@ -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 \
+ tablet-v2-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 $@
+tablet-v2-protocol.h:
+ $(WAYLAND_SCANNER) server-header \
+ $(WAYLAND_PROTOCOLS)/unstable/tablet/tablet-unstable-v2.xml $@
config.h:
cp config.def.h $@
diff --git a/config.def.h b/config.def.h
index 8a6eda0..1f20dfd 100644
--- a/config.def.h
+++ b/config.def.h
@@ -4,6 +4,7 @@
((hex >> 8) & 0xFF) / 255.0f, \
(hex & 0xFF) / 255.0f }
/* appearance */
+static const int tabletmaptosurface = 0; /* map tablet input to surface(1) or monitor(0) */
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 */
diff --git a/dwl.c b/dwl.c
index 8a9715d..6dc462a 100644
--- a/dwl.c
+++ b/dwl.c
@@ -52,6 +52,10 @@
#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_tablet_pad.h>
+#include <wlr/types/wlr_tablet_tool.h>
+#include <wlr/types/wlr_tablet_v2.h>
+#include <wlr/types/wlr_touch.h>
#include <wlr/types/wlr_viewporter.h>
#include <wlr/types/wlr_virtual_keyboard_v1.h>
#include <wlr/types/wlr_virtual_pointer_v1.h>
@@ -163,6 +167,12 @@ typedef struct {
struct wl_listener destroy;
} KeyboardGroup;
+typedef struct TouchGroup {
+ struct wl_list link;
+ struct wlr_touch *touch;
+ Monitor *m;
+} TouchGroup;
+
typedef struct {
/* Must keep this field first */
unsigned int type; /* LayerShell */
@@ -270,7 +280,10 @@ static void createnotify(struct wl_listener *listener, void *data);
static void createpointer(struct wlr_pointer *pointer);
static void createpointerconstraint(struct wl_listener *listener, void *data);
static void createpopup(struct wl_listener *listener, void *data);
+static void createtablet(struct wlr_input_device *device);
+static void createtouch(struct wlr_touch *touch);
static void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint);
+static void createtouch(struct wlr_touch *touch);
static void cursorframe(struct wl_listener *listener, void *data);
static void cursorwarptohint(void);
static void destroydecoration(struct wl_listener *listener, void *data);
@@ -283,6 +296,9 @@ 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 destroytablet(struct wl_listener *listener, void *data);
+static void destroytabletsurfacenotify(struct wl_listener *listener, void *data);
+static void destroytablettool(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);
@@ -335,11 +351,20 @@ 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 tablettoolmotion(struct wlr_tablet_v2_tablet_tool *tool, bool change_x, bool change_y, double x, double y, double dx, double dy);
+static void tablettoolproximity(struct wl_listener *listener, void *data);
+static void tablettoolaxis(struct wl_listener *listener, void *data);
+static void tablettoolbutton(struct wl_listener *listener, void *data);
+static void tablettooltip(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 toggletag(const Arg *arg);
static void toggleview(const Arg *arg);
+static void touchdown(struct wl_listener *listener, void *data);
+static void touchup(struct wl_listener *listener, void *data);
+static void touchframe(struct wl_listener *listener, void *data);
+static void touchmotion(struct wl_listener *listener, void *data);
static void unlocksession(struct wl_listener *listener, void *data);
static void unmaplayersurfacenotify(struct wl_listener *listener, void *data);
static void unmapnotify(struct wl_listener *listener, void *data);
@@ -392,6 +417,13 @@ static struct wlr_pointer_constraint_v1 *active_constraint;
static struct wlr_cursor *cursor;
static struct wlr_xcursor_manager *cursor_mgr;
+static struct wlr_tablet_manager_v2 *tablet_mgr;
+static struct wlr_tablet_v2_tablet *tablet = NULL;
+static struct wlr_tablet_v2_tablet_tool *tablet_tool = NULL;
+static struct wlr_tablet_v2_tablet_pad *tablet_pad = NULL;
+static struct wlr_surface *tablet_curr_surface = NULL;
+static struct wl_listener destroy_tablet_surface_listener = {.notify = destroytabletsurfacenotify};
+
static struct wlr_scene_rect *root_bg;
static struct wlr_session_lock_manager_v1 *session_lock_mgr;
static struct wlr_scene_rect *locked_bg;
@@ -407,6 +439,7 @@ static struct wlr_output_layout *output_layout;
static struct wlr_box sgeom;
static struct wl_list mons;
static Monitor *selmon;
+static struct wl_list touches;
/* global event handlers */
static struct wl_listener cursor_axis = {.notify = axisnotify};
@@ -414,6 +447,12 @@ static struct wl_listener cursor_button = {.notify = buttonpress};
static struct wl_listener cursor_frame = {.notify = cursorframe};
static struct wl_listener cursor_motion = {.notify = motionrelative};
static struct wl_listener cursor_motion_absolute = {.notify = motionabsolute};
+static struct wl_listener tablet_device_destroy = {.notify = destroytablet};
+static struct wl_listener tablet_tool_axis = {.notify = tablettoolaxis};
+static struct wl_listener tablet_tool_button = {.notify = tablettoolbutton};
+static struct wl_listener tablet_tool_destroy = {.notify = destroytablettool};
+static struct wl_listener tablet_tool_proximity = {.notify = tablettoolproximity};
+static struct wl_listener tablet_tool_tip = {.notify = tablettooltip};
static struct wl_listener gpu_reset = {.notify = gpureset};
static struct wl_listener layout_change = {.notify = updatemons};
static struct wl_listener new_idle_inhibitor = {.notify = createidleinhibitor};
@@ -436,6 +475,10 @@ static struct wl_listener request_set_sel = {.notify = setsel};
static struct wl_listener request_set_cursor_shape = {.notify = setcursorshape};
static struct wl_listener request_start_drag = {.notify = requeststartdrag};
static struct wl_listener start_drag = {.notify = startdrag};
+static struct wl_listener touch_down = {.notify = touchdown};
+static struct wl_listener touch_frame = {.notify = touchframe};
+static struct wl_listener touch_motion = {.notify = touchmotion};
+static struct wl_listener touch_up = {.notify = touchup};
static struct wl_listener new_session_lock = {.notify = locksession};
#ifdef XWAYLAND
@@ -783,6 +826,10 @@ cleanuplisteners(void)
wl_list_remove(&request_set_cursor_shape.link);
wl_list_remove(&request_start_drag.link);
wl_list_remove(&start_drag.link);
+ wl_list_remove(&touch_down.link);
+ wl_list_remove(&touch_frame.link);
+ wl_list_remove(&touch_motion.link);
+ wl_list_remove(&touch_up.link);
wl_list_remove(&new_session_lock.link);
#ifdef XWAYLAND
wl_list_remove(&new_xwayland_surface.link);
@@ -1201,6 +1248,38 @@ createpopup(struct wl_listener *listener, void *data)
LISTEN_STATIC(&popup->base->surface->events.commit, commitpopup);
}
+void
+createtablet(struct wlr_input_device *device)
+{
+ if (!tablet) {
+ struct libinput_device *device_handle = NULL;
+ if (!wlr_input_device_is_libinput(device) ||
+ !(device_handle = wlr_libinput_get_device_handle(device)))
+ return;
+
+ tablet = wlr_tablet_create(tablet_mgr, seat, device);
+ wl_signal_add(&tablet->wlr_device->events.destroy, &tablet_device_destroy);
+ if (libinput_device_config_send_events_get_modes(device_handle)) {
+ libinput_device_config_send_events_set_mode(device_handle, send_events_mode);
+ wlr_cursor_attach_input_device(cursor, device);
+ }
+ } else if (device == tablet->wlr_device) {
+ wlr_log(WLR_ERROR, "createtablet: duplicate device");
+ } else {
+ wlr_log(WLR_ERROR, "createtablet: already have one tablet");
+ }
+}
+
+void
+createtouch(struct wlr_touch *wlr_touch)
+{
+ TouchGroup *touch = ecalloc(1, sizeof(TouchGroup));
+
+ touch->touch = wlr_touch;
+ wl_list_insert(&touches, &touch->link);
+ wlr_cursor_attach_input_device(cursor, &wlr_touch->base);
+}
+
void
cursorconstrain(struct wlr_pointer_constraint_v1 *constraint)
{
@@ -1385,6 +1464,29 @@ destroykeyboardgroup(struct wl_listener *listener, void *data)
free(group);
}
+void
+destroytablet(struct wl_listener *listener, void *data)
+{
+ wl_list_remove(&tablet_device_destroy.link);
+ wlr_cursor_detach_input_device(cursor, tablet->wlr_device);
+ tablet = NULL;
+}
+
+void
+destroytabletsurfacenotify(struct wl_listener *listener, void *data)
+{
+ if (tablet_curr_surface)
+ wl_list_remove(&destroy_tablet_surface_listener.link);
+ tablet_curr_surface = NULL;
+}
+
+void
+destroytablettool(struct wl_listener *listener, void *data)
+{
+ destroytabletsurfacenotify(NULL, NULL);
+ tablet_tool = NULL;
+}
+
Monitor *
dirtomon(enum wlr_direction dir)
{
@@ -1592,6 +1694,15 @@ inputdevice(struct wl_listener *listener, void *data)
case WLR_INPUT_DEVICE_POINTER:
createpointer(wlr_pointer_from_input_device(device));
break;
+ case WLR_INPUT_DEVICE_TABLET:
+ createtablet(device);
+ break;
+ case WLR_INPUT_DEVICE_TABLET_PAD:
+ tablet_pad = wlr_tablet_pad_create(tablet_mgr, seat, device);
+ break;
+ case WLR_INPUT_DEVICE_TOUCH:
+ createtouch(wlr_touch_from_input_device(device));
+ break;
default:
/* TODO handle other input device types */
break;
@@ -1604,6 +1715,8 @@ inputdevice(struct wl_listener *listener, void *data)
caps = WL_SEAT_CAPABILITY_POINTER;
if (!wl_list_empty(&kb_group->wlr_group->devices))
caps |= WL_SEAT_CAPABILITY_KEYBOARD;
+ if (!wl_list_empty(&touches))
+ caps |= WL_SEAT_CAPABILITY_TOUCH;
wlr_seat_set_capabilities(seat, caps);
}
@@ -2588,6 +2701,8 @@ setup(void)
relative_pointer_mgr = wlr_relative_pointer_manager_v1_create(dpy);
+ tablet_mgr = wlr_tablet_v2_create(dpy);
+
/*
* Creates a cursor, which is a wlroots utility for tracking the cursor
* image shown on screen.
@@ -2617,6 +2732,18 @@ setup(void)
wl_signal_add(&cursor->events.button, &cursor_button);
wl_signal_add(&cursor->events.axis, &cursor_axis);
wl_signal_add(&cursor->events.frame, &cursor_frame);
+ wl_signal_add(&cursor->events.tablet_tool_proximity, &tablet_tool_proximity);
+ wl_signal_add(&cursor->events.tablet_tool_axis, &tablet_tool_axis);
+ wl_signal_add(&cursor->events.tablet_tool_button, &tablet_tool_button);
+ wl_signal_add(&cursor->events.tablet_tool_tip, &tablet_tool_tip);
+
+
+ wl_list_init(&touches);
+
+ wl_signal_add(&cursor->events.touch_down, &touch_down);
+ wl_signal_add(&cursor->events.touch_frame, &touch_frame);
+ wl_signal_add(&cursor->events.touch_motion, &touch_motion);
+ wl_signal_add(&cursor->events.touch_up, &touch_up);
cursor_shape_mgr = wlr_cursor_shape_manager_v1_create(dpy, 1);
wl_signal_add(&cursor_shape_mgr->events.request_set_shape, &request_set_cursor_shape);
@@ -2712,6 +2839,163 @@ tagmon(const Arg *arg)
setmon(sel, dirtomon(arg->i), 0);
}
+void
+tabletapplymap(double x, double y, struct wlr_input_device *dev)
+{
+ Client *p;
+ struct wlr_box geom = {0};
+ if (tabletmaptosurface && tablet_curr_surface) {
+ toplevel_from_wlr_surface(tablet_curr_surface, &p, NULL);
+ if (p) {
+ for (; client_get_parent(p); p = client_get_parent(p));
+ geom.x = p->geom.x + p->bw;
+ geom.y = p->geom.y + p->bw;
+ geom.width = p->geom.width - 2 * p->bw;
+ geom.height = p->geom.height - 2 * p->bw;
+ }
+ }
+ wlr_cursor_map_input_to_region(cursor, dev, &geom);
+ wlr_cursor_map_input_to_output(cursor, dev, selmon->wlr_output);
+}
+
+void
+tablettoolmotion(struct wlr_tablet_v2_tablet_tool *tool, bool change_x, bool change_y,
+ double x, double y, double dx, double dy)
+{
+ struct wlr_surface *surface = NULL;
+ double sx, sy;
+
+ if (!change_x && !change_y)
+ return;
+
+ tabletapplymap(x, y, tablet->wlr_device);
+
+ // TODO: apply constraints
+ switch (tablet_tool->wlr_tool->type) {
+ case WLR_TABLET_TOOL_TYPE_LENS:
+ case WLR_TABLET_TOOL_TYPE_MOUSE:
+ wlr_cursor_move(cursor, tablet->wlr_device, dx, dy);
+ break;
+ default:
+ wlr_cursor_warp_absolute(cursor, tablet->wlr_device, change_x ? x : NAN, change_y ? y : NAN);
+ break;
+ }
+
+ motionnotify(0, NULL, 0, 0, 0, 0);
+
+ xytonode(cursor->x, cursor->y, &surface, NULL, NULL, &sx, &sy);
+ if (surface && !wlr_surface_accepts_tablet_v2(surface, tablet))
+ surface = NULL;
+
+ if (surface != tablet_curr_surface) {
+ if (tablet_curr_surface) {
+ // TODO: wait until all buttons released before leaving
+ if (tablet_tool)
+ wlr_tablet_v2_tablet_tool_notify_proximity_out(tablet_tool);
+ if (tablet_pad)
+ wlr_tablet_v2_tablet_pad_notify_leave(tablet_pad, tablet_curr_surface);
+ wl_list_remove(&destroy_tablet_surface_listener.link);
+ }
+ if (surface) {
+ if (tablet_pad)
+ wlr_tablet_v2_tablet_pad_notify_enter(tablet_pad, tablet, surface);
+ if (tablet_tool)
+ wlr_tablet_v2_tablet_tool_notify_proximity_in(tablet_tool, tablet, surface);
+ wl_signal_add(&surface->events.destroy, &destroy_tablet_surface_listener);
+ }
+ tablet_curr_surface = surface;
+ }
+
+ if (surface)
+ wlr_tablet_v2_tablet_tool_notify_motion(tablet_tool, sx, sy);
+}
+
+void
+tablettoolproximity(struct wl_listener *listener, void *data)
+{
+ struct wlr_tablet_tool_proximity_event *event = data;
+ struct wlr_tablet_tool *tool = event->tool;
+
+ if (!tablet_tool) {
+ tablet_tool = wlr_tablet_tool_create(tablet_mgr, seat, tool);
+ wl_signal_add(&tablet_tool->wlr_tool->events.destroy, &tablet_tool_destroy);
+ wl_signal_add(&tablet_tool->events.set_cursor, &request_cursor);
+ }
+
+ switch (event->state) {
+ case WLR_TABLET_TOOL_PROXIMITY_OUT:
+ wlr_tablet_v2_tablet_tool_notify_proximity_out(tablet_tool);
+ destroytabletsurfacenotify(NULL, NULL);
+ break;
+ case WLR_TABLET_TOOL_PROXIMITY_IN:
+ tablettoolmotion(tablet_tool, true, true, event->x, event->y, 0, 0);
+ break;
+ }
+}
+
+double tilt_x = 0;
+double tilt_y = 0;
+
+void
+tablettoolaxis(struct wl_listener *listener, void *data)
+{
+ struct wlr_tablet_tool_axis_event *event = data;
+
+ tablettoolmotion(tablet_tool,
+ event->updated_axes & WLR_TABLET_TOOL_AXIS_X,
+ event->updated_axes & WLR_TABLET_TOOL_AXIS_Y,
+ event->x, event->y, event->dx, event->dy);
+
+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_PRESSURE)
+ wlr_tablet_v2_tablet_tool_notify_pressure(tablet_tool, event->pressure);
+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_DISTANCE)
+ wlr_tablet_v2_tablet_tool_notify_distance(tablet_tool, event->distance);
+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_X)
+ tilt_x = event->tilt_x;
+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_Y)
+ tilt_y = event->tilt_y;
+ if (event->updated_axes & (WLR_TABLET_TOOL_AXIS_TILT_X | WLR_TABLET_TOOL_AXIS_TILT_Y))
+ wlr_tablet_v2_tablet_tool_notify_tilt(tablet_tool, tilt_x, tilt_y);
+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_ROTATION)
+ wlr_tablet_v2_tablet_tool_notify_rotation(tablet_tool, event->rotation);
+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_SLIDER)
+ wlr_tablet_v2_tablet_tool_notify_slider(tablet_tool, event->slider);
+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_WHEEL)
+ wlr_tablet_v2_tablet_tool_notify_wheel(tablet_tool, event->wheel_delta, 0);
+}
+
+void
+tablettoolbutton(struct wl_listener *listener, void *data)
+{
+ struct wlr_tablet_tool_button_event *event = data;
+ wlr_tablet_v2_tablet_tool_notify_button(tablet_tool, event->button,
+ (enum zwp_tablet_pad_v2_button_state)event->state);
+}
+
+void
+tablettooltip(struct wl_listener *listener, void *data)
+{
+ struct wlr_tablet_tool_tip_event *event = data;
+
+ if (!tablet_curr_surface) {
+ struct wlr_pointer_button_event fakeptrbtnevent = {
+ .button = BTN_LEFT,
+ .state = event->state == WLR_TABLET_TOOL_TIP_UP ?
+ WL_POINTER_BUTTON_STATE_RELEASED : WL_POINTER_BUTTON_STATE_PRESSED,
+ .time_msec = event->time_msec,
+ };
+ buttonpress(NULL, (void *)&fakeptrbtnevent);
+ }
+
+ if (event->state == WLR_TABLET_TOOL_TIP_UP) {
+ wlr_tablet_v2_tablet_tool_notify_up(tablet_tool);
+ return;
+ }
+
+ wlr_tablet_v2_tablet_tool_notify_down(tablet_tool);
+ wlr_tablet_tool_v2_start_implicit_grab(tablet_tool);
+}
+
void
tile(Monitor *m)
{
@@ -2790,6 +3074,120 @@ toggleview(const Arg *arg)
printstatus();
}
+void
+touchdown(struct wl_listener *listener, void *data)
+{
+ struct wlr_touch_down_event *event = data;
+ double lx, ly;
+ double sx, sy;
+ struct wlr_surface *surface;
+ Client *c = NULL;
+ uint32_t serial = 0;
+ Monitor *m;
+
+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
+
+ // Map the input to the appropriate output, to ensure that rotation is
+ // handled.
+ wl_list_for_each(m, &mons, link) {
+ if (m == NULL || m->wlr_output == NULL) {
+ continue;
+ }
+ if (event->touch->output_name != NULL && 0 != strcmp(event->touch->output_name, m->wlr_output->name)) {
+ continue;
+ }
+
+ wlr_cursor_map_input_to_output(cursor, &event->touch->base, m->wlr_output);
+ }
+
+ wlr_cursor_absolute_to_layout_coords(cursor, &event->touch->base, event->x, event->y, &lx, &ly);
+
+ /* Find the client under the pointer and send the event along. */
+ xytonode(lx, ly, &surface, &c, NULL, &sx, &sy);
+ if (sloppyfocus)
+ focusclient(c, 0);
+
+ if (surface != NULL) {
+ serial = wlr_seat_touch_notify_down(seat, surface, event->time_msec, event->touch_id, sx, sy);
+ }
+
+ if (serial && wlr_seat_touch_num_points(seat) == 1) {
+ /* Emulate a mouse click if the touch event wasn't handled */
+ struct wlr_pointer_button_event *button_event = data;
+ struct wlr_pointer_motion_absolute_event *motion_event = data;
+ double dx, dy;
+
+ wlr_cursor_absolute_to_layout_coords(cursor, &motion_event->pointer->base, motion_event->x, motion_event->y, &lx, &ly);
+ wlr_cursor_warp_closest(cursor, &motion_event->pointer->base, lx, ly);
+ dx = lx - cursor->x;
+ dy = ly - cursor->y;
+ motionnotify(motion_event->time_msec, &motion_event->pointer->base, dx, dy, dx, dy);
+
+ button_event->button = BTN_LEFT;
+ button_event->state = WL_POINTER_BUTTON_STATE_PRESSED;
+ buttonpress(listener, button_event);
+ }
+}
+
+void
+touchup(struct wl_listener *listener, void *data)
+{
+ struct wlr_touch_up_event *event = data;
+
+ if (!wlr_seat_touch_get_point(seat, event->touch_id)) {
+ return;
+ }
+
+ if (wlr_seat_touch_num_points(seat) == 1) {
+ struct wlr_pointer_button_event *button_event = data;
+
+ button_event->button = BTN_LEFT;
+ button_event->state = WL_POINTER_BUTTON_STATE_RELEASED;
+ buttonpress(listener, button_event);
+ }
+
+ wlr_seat_touch_notify_up(seat, event->time_msec, event->touch_id);
+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
+}
+
+void
+touchframe(struct wl_listener *listener, void *data)
+{
+ wlr_seat_touch_notify_frame(seat);
+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
+}
+
+void
+touchmotion(struct wl_listener *listener, void *data)
+{
+ struct wlr_touch_motion_event *event = data;
+ double lx, ly;
+ double sx, sy;
+ struct wlr_surface *surface;
+ Client *c = NULL;
+
+ if (!wlr_seat_touch_get_point(seat, event->touch_id)) {
+ return;
+ }
+
+ wlr_cursor_absolute_to_layout_coords(cursor, &event->touch->base, event->x, event->y, &lx, &ly);
+ xytonode(lx, ly, &surface, &c, NULL, &sx, &sy);
+
+ if (c != NULL && surface != NULL) {
+ if (sloppyfocus)
+ focusclient(c, 0);
+ wlr_seat_touch_point_focus(seat, surface, event->time_msec, event->touch_id, sx, sy);
+ } else {
+ if (sloppyfocus)
+ focusclient(NULL, 0);
+ wlr_seat_touch_point_clear_focus(seat, event->time_msec, event->touch_id);
+ }
+ wlr_seat_touch_notify_motion(seat, event->time_msec, event->touch_id, sx, sy);
+
+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
+}
+
+
void
unlocksession(struct wl_listener *listener, void *data)
{
--
2.52.0

View File

@ -60,7 +60,7 @@ index 775dadf..621779e 100644
- /* TODO: allow usage of scroll wheel for mousebindings, it can be implemented
- * by checking the event's orientation and the delta of the event */
+ handlecursoractivity();
+ /* TODO: allow usage of scroll whell for mousebindings, it can be implemented
+ /* TODO: allow usage of scroll wheel for mousebindings, it can be implemented
+ * checking the event's orientation and the delta of the event */
/* Notify the client with pointer focus of the axis event. */
wlr_seat_pointer_notify_axis(seat,

View File

@ -1,7 +1,7 @@
### Description
implement wlr_virtual_pointer_v1 for things like wayvnc server to work
**NOTE:** no longer neccessary if you are using a DWL version after https://codeberg.org/dwl/dwl/commit/ac6074f4fdb8cc263c877f08e16a5805d3bb22d2
**NOTE:** no longer necessary if you are using a DWL version after https://codeberg.org/dwl/dwl/commit/ac6074f4fdb8cc263c877f08e16a5805d3bb22d2
### Download
- [git branch](https://codeberg.org/wochap/dwl/src/v0.5/virtual-pointer)

View File

@ -3,7 +3,7 @@ Implements the function `winview` which switches the visible tags to the tags on
This patch is inspired from <https://dwm.suckless.org/patches/winview/>. Citing the description of the dwm patch:
> Dwm tags are a powerfull feature that allows organizing windows in workspaces. Sometime it can be difficult to remember the tag to activate to unhide a window. With the winview patch the window to unhide can be selected from the all-window view. The user switches to the all-window view (Mod1-0), selects the window (Mod1-j/k or using the mouse) and press Mod1-o. The key Mod1-o switches the view to the selected window tag.
> Dwm tags are a powerful feature that allows organizing windows in workspaces. Sometime it can be difficult to remember the tag to activate to unhide a window. With the winview patch the window to unhide can be selected from the all-window view. The user switches to the all-window view (Mod1-0), selects the window (Mod1-j/k or using the mouse) and press Mod1-o. The key Mod1-o switches the view to the selected window tag.
>
> #### Recommend patches
>

View File

@ -1,5 +1,5 @@
### Description
Implements `swaymsg create_output` command, it allows you to create virtual/headless outputs. But in combination with a VNC server (for example wayvnc), this allows you to essentially have additional monitors, by connecting to the VNC server with an appropiate client (for example on an tablet or laptop).
Implements `swaymsg create_output` command, it allows you to create virtual/headless outputs. But in combination with a VNC server (for example wayvnc), this allows you to essentially have additional monitors, by connecting to the VNC server with an appropriate client (for example on an tablet or laptop).
If you plan to use wayvnc, you'll need [virtual-pointer](https://codeberg.org/dwl/dwl-patches/wiki/virtual-pointer.-) patch as well

View File

@ -417,7 +417,7 @@ index 0000000..0a6e7e5
+I would probably just submit raphi's patchset but I don't think that would be polite.
+-->
+<protocol name="dwl_ipc_unstable_v2">
+ <description summary="inter-proccess-communication about dwl's state">
+ <description summary="inter-process-communication about dwl's state">
+ This protocol allows clients to update and get updates from dwl.
+
+ Warning! The protocol described in this file is experimental and
@ -437,7 +437,7 @@ index 0000000..0a6e7e5
+ This interface is exposed as a global in wl_registry.
+
+ Clients can use this interface to get a dwl_ipc_output.
+ After binding the client will recieve the dwl_ipc_manager.tags and dwl_ipc_manager.layout events.
+ After binding the client will receive the dwl_ipc_manager.tags and dwl_ipc_manager.layout events.
+ The dwl_ipc_manager.tags and dwl_ipc_manager.layout events expose tags and layouts to the client.
+ </description>
+
@ -449,8 +449,8 @@ index 0000000..0a6e7e5
+ </request>
+
+ <request name="get_output">
+ <description summary="get a dwl_ipc_outout for a wl_output">
+ Get a dwl_ipc_outout for the specified wl_output.
+ <description summary="get a dwl_ipc_output for a wl_output">
+ Get a dwl_ipc_output for the specified wl_output.
+ </description>
+ <arg name="id" type="new_id" interface="zdwl_ipc_output_v2"/>
+ <arg name="output" type="object" interface="wl_output"/>
@ -459,7 +459,7 @@ index 0000000..0a6e7e5
+ <event name="tags">
+ <description summary="Announces tag amount">
+ This event is sent after binding.
+ A roundtrip after binding guarantees the client recieved all tags.
+ A roundtrip after binding guarantees the client received all tags.
+ </description>
+ <arg name="amount" type="uint"/>
+ </event>
@ -467,7 +467,7 @@ index 0000000..0a6e7e5
+ <event name="layout">
+ <description summary="Announces a layout">
+ This event is sent after binding.
+ A roundtrip after binding guarantees the client recieved all layouts.
+ A roundtrip after binding guarantees the client received all layouts.
+ </description>
+ <arg name="name" type="string"/>
+ </event>
@ -491,13 +491,13 @@ index 0000000..0a6e7e5
+ </enum>
+
+ <request name="release" type="destructor">
+ <description summary="release dwl_ipc_outout">
+ <description summary="release dwl_ipc_output">
+ Indicates to that the client no longer needs this dwl_ipc_output.
+ </description>
+ </request>
+
+ <event name="toggle_visibility">
+ <description summary="Toggle client visibilty">
+ <description summary="Toggle client visibility">
+ Indicates the client should hide or show themselves.
+ If the client is visible then hide, if hidden then show.
+ </description>
@ -544,7 +544,7 @@ index 0000000..0a6e7e5
+ <event name="layout_symbol" since="1">
+ <description summary="Update the current layout symbol">
+ Indicates the layout has changed. Since layout symbols are dynamic.
+ As opposed to the zdwl_ipc_manager.layout event, this should take precendence when displaying.
+ As opposed to the zdwl_ipc_manager.layout event, this should take precedence when displaying.
+ You can ignore the zdwl_ipc_output.layout event.
+ </description>
+ <arg name="layout" type="string" summary="The new layout"/>
@ -573,7 +573,7 @@ index 0000000..0a6e7e5
+
+ <request name="set_layout">
+ <description summary="Set the layout of this output"/>
+ <arg name="index" type="uint" summary="index of a layout recieved by dwl_ipc_manager.layout"/>
+ <arg name="index" type="uint" summary="index of a layout received by dwl_ipc_manager.layout"/>
+ </request>
+
+ <!-- Version 2 -->