summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Makefile1
-rw-r--r--config.def.h48
-rw-r--r--config.h198
-rw-r--r--config.mk6
-rw-r--r--downdate.diff67
-rw-r--r--downdate2.diff78
-rw-r--r--dwl.c277
-rw-r--r--patches/dwl-alwayscenter.diff23
-rw-r--r--patches/dwl-autostart.diff132
-rw-r--r--patches/dwl-cursorwarp.diff145
-rw-r--r--patches/dwl-namedscratchpads.diff176
-rw-r--r--patches/dwl-push.diff129
-rw-r--r--patches/dwl-smartborders.diff164
-rw-r--r--patches/dwl-swallow.diff194
-rw-r--r--push.c63
-rw-r--r--push.h4
-rw-r--r--update.diff147
18 files changed, 1814 insertions, 39 deletions
diff --git a/.gitignore b/.gitignore
index 0dde90e..5c28541 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,3 @@ dwl
*-protocol.c
*-protocol.h
.ccls-cache
-config.h
diff --git a/Makefile b/Makefile
index 1bdc7d6..8fa5630 100644
--- a/Makefile
+++ b/Makefile
@@ -24,6 +24,7 @@ all: dwl
dwl: dwl.o util.o
$(CC) dwl.o util.o $(LDLIBS) $(LDFLAGS) $(DWLCFLAGS) -o $@
dwl.o: dwl.c config.mk config.h client.h xdg-shell-protocol.h wlr-layer-shell-unstable-v1-protocol.h idle-protocol.h
+dwl.o: push.c
util.o: util.c util.h
# wayland scanner rules to generate .h / .c files
diff --git a/config.def.h b/config.def.h
index 29c6dbf..639f92c 100644
--- a/config.def.h
+++ b/config.def.h
@@ -2,21 +2,35 @@
static const int sloppyfocus = 1; /* focus follows mouse */
static const unsigned int borderpx = 1; /* border pixel of windows */
static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */
+static const int smartborders = 1;
static const float rootcolor[] = {0.3, 0.3, 0.3, 1.0};
static const float bordercolor[] = {0.5, 0.5, 0.5, 1.0};
static const float focuscolor[] = {1.0, 0.0, 0.0, 1.0};
/* 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};
+/* cursor warping */
+static const bool cursor_warp = true;
+
/* tagging */
static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
static const Rule rules[] = {
- /* app_id title tags mask isfloating monitor */
+ /* app_id title tags mask isfloating isterm noswallow monitor scratchkey */
/* examples:
- { "Gimp", NULL, 0, 1, -1 },
++ { "Gimp", NULL, 0, 1, 0, 1, -1, 0 },
+ { "firefox", NULL, 1 << 8, 0, 0, 1, -1, 0 },
*/
- { "firefox", NULL, 1 << 8, 0, -1 },
+ { NULL, "sphtop", 0, 1, 0, 1, -1, 'z' },
+ { NULL, "spterm", 0, 1, 0, 1, -1, 'x' },
+ { NULL, "sppmxr", 0, 1, 0, 1, -1, 'c' },
+ { NULL, "spblue", 0, 1, 0, 1, -1, 'v' },
+ { NULL, "spncmp", 0, 1, 0, 1, -1, 'b' },
+ { NULL, "spmutt", 0, 1, 0, 1, -1, 'a' },
+ { NULL, "spprof", 0, 1, 0, 1, -1, 's' },
+ { NULL, "spircc", 0, 1, 0, 1, -1, 'd' },
+ { NULL, "sptodo", 0, 1, 0, 1, -1, 'f' },
+ { NULL, "sptrem", 0, 1, 0, 1, -1, 'g' },
};
/* layout(s) */
@@ -86,6 +100,12 @@ LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE
static const enum libinput_config_accel_profile accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
static const double accel_speed = 0.0;
+/* Autostart */
+static const char *const autostart[] = {
+ "sh", "-c", "swaybg --image /xap/local/background", NULL,
+ NULL /* terminate */
+};
+
/* If you want to use the windows key change this to WLR_MODIFIER_LOGO */
#define MODKEY WLR_MODIFIER_ALT
#define TAGKEYS(KEY,SKEY,TAG) \
@@ -101,11 +121,33 @@ static const double accel_speed = 0.0;
static const char *termcmd[] = { "alacritty", NULL };
static const char *menucmd[] = { "bemenu-run", NULL };
+/* named scratchpads - First arg only serves to match against key in rules*/
+static const char *scratchpadcmd0[] = { "z", "alacritty", "-t", "sphtop", "htop", NULL };
+static const char *scratchpadcmd1[] = { "x", "alacritty", "-t", "spterm", "zsh", NULL };
+static const char *scratchpadcmd2[] = { "c", "alacritty", "-t", "sppmxr", "pulsemixer", NULL };
+static const char *scratchpadcmd3[] = { "v", "alacritty", "-t", "spblue", "bluetoothctl", NULL };
+static const char *scratchpadcmd4[] = { "b", "alacritty", "-t", "spncmp", "ncmpcpp", NULL };
+static const char *scratchpadcmd5[] = { "a", "alacritty", "-t", "spmutt", "neomutt", NULL };
+static const char *scratchpadcmd6[] = { "s", "alacritty", "-t", "spprof", "profanity", NULL };
+static const char *scratchpadcmd7[] = { "d", "alacritty", "-t", "spircc", "irssi", NULL };
+static const char *scratchpadcmd8[] = { "f", "alacritty", "-t", "sptodo", "todo", NULL };
+static const char *scratchpadcmd9[] = { "g", "alacritty", "-t", "sptrem", "tremc", NULL };
+
static const Key keys[] = {
/* Note that Shift changes certain key codes: c -> C, 2 -> at, etc. */
/* modifier key function argument */
{ MODKEY, XKB_KEY_p, spawn, {.v = menucmd} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, spawn, {.v = termcmd} },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_z, togglescratch, {.v = scratchpadcmd0 } },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_x, togglescratch, {.v = scratchpadcmd1 } },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_c, togglescratch, {.v = scratchpadcmd2 } },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_v, togglescratch, {.v = scratchpadcmd3 } },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_b, togglescratch, {.v = scratchpadcmd4 } },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_a, togglescratch, {.v = scratchpadcmd5 } },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_s, togglescratch, {.v = scratchpadcmd6 } },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_d, togglescratch, {.v = scratchpadcmd7 } },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_f, togglescratch, {.v = scratchpadcmd8 } },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_g, togglescratch, {.v = scratchpadcmd9 } },
{ MODKEY, XKB_KEY_j, focusstack, {.i = +1} },
{ MODKEY, XKB_KEY_k, focusstack, {.i = -1} },
{ MODKEY, XKB_KEY_i, incnmaster, {.i = +1} },
diff --git a/config.h b/config.h
new file mode 100644
index 0000000..7745e7d
--- /dev/null
+++ b/config.h
@@ -0,0 +1,198 @@
+/* appearance */
+static const int sloppyfocus = 1; /* focus follows mouse */
+static const unsigned int borderpx = 1; /* border pixel of windows */
+static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */
+static const int smartborders = 1;
+static const float rootcolor[] = {0.3, 0.3, 0.3, 1.0};
+static const float bordercolor[] = {0.5, 0.5, 0.5, 1.0};
+static const float focuscolor[] = {1.0, 0.0, 0.0, 1.0};
+/* 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};
+
+/* cursor warping */
+static const bool cursor_warp = false;
+
+/* tagging */
+static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
+
+static const Rule rules[] = {
+ /* app_id title tags mask isfloating isterm noswallow monitor scratchkey */
+ /* examples:
+ { "Gimp", NULL, 0, 1, 0, 1, -1, 0 },
+ { "firefox", NULL, 1 << 8, 0, 0, 1, -1, 0 },
+ */
+ { NULL, "sphtop", 0, 1, 0, 1, -1, 'z' },
+ { NULL, "spterm", 0, 1, 0, 1, -1, 'x' },
+ { NULL, "sppmxr", 0, 1, 0, 1, -1, 'c' },
+ { NULL, "spblue", 0, 1, 0, 1, -1, 'v' },
+ { NULL, "spncmp", 0, 1, 0, 1, -1, 'b' },
+ { NULL, "spmutt", 0, 1, 0, 1, -1, 'a' },
+ { NULL, "spprof", 0, 1, 0, 1, -1, 's' },
+ { NULL, "spircc", 0, 1, 0, 1, -1, 'd' },
+ { NULL, "sptodo", 0, 1, 0, 1, -1, 'f' },
+ { NULL, "sptrem", 0, 1, 0, 1, -1, 'g' },
+ { "alacritty", NULL, 0, 0, 1, 1, -1, 0 },
+};
+
+/* layout(s) */
+static const Layout layouts[] = {
+ /* symbol arrange function */
+ { "[]=", tile },
+ { "><>", NULL }, /* no layout function means floating behavior */
+ { "[M]", monocle },
+};
+
+/* monitors */
+static const MonitorRule monrules[] = {
+ /* name mfact nmaster scale layout rotate/reflect */
+ /* example of a HiDPI laptop monitor:
+ { "eDP-1", 0.5, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL },
+ */
+ /* defaults */
+ { NULL, 0.55, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL },
+};
+
+/* keyboard */
+static const struct xkb_rule_names xkb_rules = {
+ /* can specify fields: rules, model, layout, variant, options */
+ /* example:
+ .options = "ctrl:nocaps",
+ */
+ .options = NULL,
+};
+
+static const int repeat_rate = 25;
+static const int repeat_delay = 600;
+
+/* Trackpad */
+static const int tap_to_click = 1;
+static const int tap_and_drag = 1;
+static const int drag_lock = 1;
+static const int natural_scrolling = 0;
+static const int disable_while_typing = 1;
+static const int left_handed = 0;
+static const int middle_button_emulation = 0;
+/* You can choose between:
+LIBINPUT_CONFIG_SCROLL_NO_SCROLL
+LIBINPUT_CONFIG_SCROLL_2FG
+LIBINPUT_CONFIG_SCROLL_EDGE
+LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN
+*/
+static const enum libinput_config_scroll_method scroll_method = LIBINPUT_CONFIG_SCROLL_2FG;
+
+/* You can choose between:
+LIBINPUT_CONFIG_CLICK_METHOD_NONE
+LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS
+LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER
+*/
+static const enum libinput_config_click_method click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
+
+/* You can choose between:
+LIBINPUT_CONFIG_SEND_EVENTS_ENABLED
+LIBINPUT_CONFIG_SEND_EVENTS_DISABLED
+LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE
+*/
+static const uint32_t send_events_mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
+
+/* You can choose between:
+LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT
+LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE
+*/
+static const enum libinput_config_accel_profile accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
+static const double accel_speed = 0.0;
+
+/* Autostart */
+static const char *const autostart[] = {
+ "sh", "-c", "~/.config/autostart.sh", NULL,
+ "pkexec", "swhkd", NULL,
+ NULL /* terminate */
+};
+
+/* If you want to use the windows key change this to WLR_MODIFIER_LOGO */
+#define MODKEY WLR_MODIFIER_ALT
+#define TAGKEYS(KEY,SKEY,TAG) \
+ { MODKEY, KEY, view, {.ui = 1 << TAG} }, \
+ { MODKEY|WLR_MODIFIER_CTRL, KEY, toggleview, {.ui = 1 << TAG} }, \
+ { MODKEY|WLR_MODIFIER_SHIFT, SKEY, tag, {.ui = 1 << TAG} }, \
+ { MODKEY|WLR_MODIFIER_CTRL|WLR_MODIFIER_SHIFT,SKEY,toggletag, {.ui = 1 << TAG} }
+
+/* helper for spawning shell commands in the pre dwm-5.0 fashion */
+#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
+
+/* commands */
+static const char *termcmd[] = { "alacritty", NULL };
+static const char *menucmd[] = { "barmenu_run", NULL };
+
+/* named scratchpads - First arg only serves to match against key in rules*/
+static const char *scratchpadcmd0[] = { "z", "alacritty", "-t", "sphtop", "-e", "htop", NULL };
+static const char *scratchpadcmd1[] = { "x", "alacritty", "-t", "spterm", "-e", "zsh", NULL };
+static const char *scratchpadcmd2[] = { "c", "alacritty", "-t", "sppmxr", "-e", "pulsemixer", NULL };
+static const char *scratchpadcmd3[] = { "v", "alacritty", "-t", "spblue", "-e", "bluetoothctl", NULL };
+static const char *scratchpadcmd4[] = { "b", "alacritty", "-t", "spncmp", "-e", "ncmpcpp", NULL };
+static const char *scratchpadcmd5[] = { "a", "alacritty", "-t", "spmutt", "-e", "neomutt", NULL };
+static const char *scratchpadcmd6[] = { "s", "alacritty", "-t", "spprof", "-e", "profanity", NULL };
+static const char *scratchpadcmd7[] = { "d", "alacritty", "-t", "spircc", "-e", "irssi", NULL };
+static const char *scratchpadcmd8[] = { "f", "alacritty", "-t", "sptodo", "-e", "todo", NULL };
+static const char *scratchpadcmd9[] = { "g", "alacritty", "-t", "sptrem", "-e", "tremc", NULL };
+
+static const Key keys[] = {
+ /* Note that Shift changes certain key codes: c -> C, 2 -> at, etc. */
+ /* modifier key function argument */
+ //{ MODKEY, XKB_KEY_p, spawn, {.v = menucmd} },
+ { MODKEY, XKB_KEY_Return, spawn, {.v = termcmd} },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_z, togglescratch, {.v = scratchpadcmd0 } },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_x, togglescratch, {.v = scratchpadcmd1 } },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_c, togglescratch, {.v = scratchpadcmd2 } },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_v, togglescratch, {.v = scratchpadcmd3 } },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_b, togglescratch, {.v = scratchpadcmd4 } },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_a, togglescratch, {.v = scratchpadcmd5 } },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_s, togglescratch, {.v = scratchpadcmd6 } },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_d, togglescratch, {.v = scratchpadcmd7 } },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_f, togglescratch, {.v = scratchpadcmd8 } },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_g, togglescratch, {.v = scratchpadcmd9 } },
+ { MODKEY, XKB_KEY_j, focusstack, {.i = +1} },
+ { MODKEY, XKB_KEY_k, focusstack, {.i = -1} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_J, pushdown, {0} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_K, pushup, {0} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_H, incnmaster, {.i = +1} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_L, incnmaster, {.i = -1} },
+ { MODKEY, XKB_KEY_h, setmfact, {.f = -0.05} },
+ { MODKEY, XKB_KEY_l, setmfact, {.f = +0.05} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, zoom, {0} },
+ { MODKEY, XKB_KEY_Tab, view, {0} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Q, killclient, {0} },
+ { MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[0]} },
+ { MODKEY, XKB_KEY_s, setlayout, {.v = &layouts[1]} },
+ { MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} },
+ { MODKEY, XKB_KEY_space, setlayout, {0} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} },
+ { MODKEY, XKB_KEY_f, 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} },
+ { MODKEY, XKB_KEY_period, focusmon, {.i = WLR_DIRECTION_RIGHT} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_less, tagmon, {.i = WLR_DIRECTION_LEFT} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_greater, tagmon, {.i = WLR_DIRECTION_RIGHT} },
+ TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0),
+ TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1),
+ TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2),
+ TAGKEYS( XKB_KEY_4, XKB_KEY_dollar, 3),
+ TAGKEYS( XKB_KEY_5, XKB_KEY_percent, 4),
+ TAGKEYS( XKB_KEY_6, XKB_KEY_asciicircum, 5),
+ TAGKEYS( XKB_KEY_7, XKB_KEY_ampersand, 6),
+ TAGKEYS( XKB_KEY_8, XKB_KEY_asterisk, 7),
+ TAGKEYS( XKB_KEY_9, XKB_KEY_parenleft, 8),
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_E, quit, {0} },
+
+ /* 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} },
+#define CHVT(n) { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_XF86Switch_VT_##n, chvt, {.ui = (n)} }
+ CHVT(1), CHVT(2), CHVT(3), CHVT(4), CHVT(5), CHVT(6),
+ CHVT(7), CHVT(8), CHVT(9), CHVT(10), CHVT(11), CHVT(12),
+};
+
+static const Button buttons[] = {
+ { MODKEY, BTN_LEFT, moveresize, {.ui = CurMove} },
+ { MODKEY, BTN_MIDDLE, togglefloating, {0} },
+ { MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} },
+};
diff --git a/config.mk b/config.mk
index 4638d5f..b6a5015 100644
--- a/config.mk
+++ b/config.mk
@@ -6,10 +6,10 @@ PREFIX = /usr/local
MANDIR = $(PREFIX)/share/man
# Compile flags that can be used
-#CFLAGS = -pedantic -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare -Wno-unused-function -Wno-unused-variable -Wno-unused-result -Wdeclaration-after-statement
+CFLAGS = -pedantic -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare -Wno-unused-function -Wno-unused-variable -Wno-unused-result -Wdeclaration-after-statement
XWAYLAND =
XLIBS =
# Uncomment to build XWayland support
-#XWAYLAND = -DXWAYLAND
-#XLIBS = xcb
+XWAYLAND = -DXWAYLAND
+XLIBS = xcb
diff --git a/downdate.diff b/downdate.diff
new file mode 100644
index 0000000..7c7644e
--- /dev/null
+++ b/downdate.diff
@@ -0,0 +1,67 @@
+diff --git a/dwl.c b/dwl.c
+index 2533ef7..227e1c4 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -574,7 +574,7 @@ arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, int
+ const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
+ | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
+
+- if (wlr_layer_surface->mapped && exclusive != (state->exclusive_zone > 0))
++ if (exclusive != (state->exclusive_zone > 0))
+ continue;
+
+ bounds = state->exclusive_zone == -1 ? full_area : *usable_area;
+@@ -643,8 +643,6 @@ arrangelayers(Monitor *m)
+ ZWLR_LAYER_SHELL_V1_LAYER_TOP,
+ };
+ LayerSurface *layersurface;
+- if (!m || !m->wlr_output->enabled)
+- return;
+
+ /* Arrange exclusive surfaces from top->bottom */
+ for (i = 3; i >= 0; i--)
+@@ -803,7 +801,6 @@ cleanupmon(struct wl_listener *listener, void *data)
+ wl_list_remove(&m->destroy.link);
+ wl_list_remove(&m->frame.link);
+ wl_list_remove(&m->link);
+- wlr_output->data = NULL;
+ wlr_output_layout_remove(output_layout, m->wlr_output);
+ wlr_scene_output_destroy(m->scene_output);
+
+@@ -1131,6 +1127,8 @@ destroylayersurfacenotify(struct wl_listener *listener, void *data)
+ wl_list_remove(&layersurface->unmap.link);
+ wl_list_remove(&layersurface->surface_commit.link);
+ wlr_scene_node_destroy(layersurface->scene);
++ if (layersurface->mon)
++ arrangelayers(layersurface->mon);
+ free(layersurface);
+ }
+
+@@ -1553,6 +1551,9 @@ mapnotify(struct wl_listener *listener, void *data)
+ }
+ printstatus();
+
++ if (c->isfullscreen)
++ setfullscreen(c, 1);
++
+ c->mon->un_map = 1;
+ if (!c->noswallow) {
+ Client *p = termforwin(c);
+@@ -2028,8 +2029,6 @@ void
+ setfullscreen(Client *c, int fullscreen)
+ {
+ c->isfullscreen = fullscreen;
+- if (!c->mon)
+- return;
+ c->bw = fullscreen ? 0 : borderpx;
+ client_set_fullscreen(c, fullscreen);
+
+@@ -2115,7 +2114,7 @@ setmon(Client *c, Monitor *m, unsigned int newtags)
+ resize(c, c->geom, 0, 1);
+ wlr_surface_send_enter(client_surface(c), m->wlr_output);
+ c->tags = newtags ? newtags : m->tagset[m->seltags]; /* assign tags of target monitor */
+- setfullscreen(c, c->isfullscreen); /* This will call arrange(c->mon) */
++ arrange(m);
+ }
+ focusclient(focustop(selmon), 1);
+ }
diff --git a/downdate2.diff b/downdate2.diff
new file mode 100644
index 0000000..2dec598
--- /dev/null
+++ b/downdate2.diff
@@ -0,0 +1,78 @@
+diff --git a/dwl.c b/dwl.c
+index 2533ef7..227e1c4 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -574,7 +574,7 @@ arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, int
+ const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
+ | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
+
+- if (wlr_layer_surface->mapped && exclusive != (state->exclusive_zone > 0))
++ if (exclusive != (state->exclusive_zone > 0))
+ continue;
+
+ bounds = state->exclusive_zone == -1 ? full_area : *usable_area;
+@@ -643,8 +643,6 @@ arrangelayers(Monitor *m)
+ ZWLR_LAYER_SHELL_V1_LAYER_TOP,
+ };
+ LayerSurface *layersurface;
+- if (!m || !m->wlr_output->enabled)
+- return;
+
+ /* Arrange exclusive surfaces from top->bottom */
+ for (i = 3; i >= 0; i--)
+@@ -803,7 +801,6 @@ cleanupmon(struct wl_listener *listener, void *data)
+ wl_list_remove(&m->destroy.link);
+ wl_list_remove(&m->frame.link);
+ wl_list_remove(&m->link);
+- wlr_output->data = NULL;
+ wlr_output_layout_remove(output_layout, m->wlr_output);
+ wlr_scene_output_destroy(m->scene_output);
+
+@@ -838,9 +835,8 @@ commitlayersurfacenotify(struct wl_listener *listener, void *data)
+ {
+ LayerSurface *layersurface = wl_container_of(listener, layersurface, surface_commit);
+ struct wlr_layer_surface_v1 *wlr_layer_surface = layersurface->layer_surface;
+- struct wlr_output *wlr_output = wlr_layer_surface->output;
+
+- if (!wlr_output || !(layersurface->mon = wlr_output->data))
++ if (!layersurface->mon)
+ return;
+
+ if (layers[wlr_layer_surface->current.layer] != layersurface->scene) {
+@@ -1131,6 +1127,8 @@ destroylayersurfacenotify(struct wl_listener *listener, void *data)
+ wl_list_remove(&layersurface->unmap.link);
+ wl_list_remove(&layersurface->surface_commit.link);
+ wlr_scene_node_destroy(layersurface->scene);
++ if (layersurface->mon)
++ arrangelayers(layersurface->mon);
+ free(layersurface);
+ }
+
+@@ -1553,6 +1551,9 @@ mapnotify(struct wl_listener *listener, void *data)
+ }
+ printstatus();
+
++ if (c->isfullscreen)
++ setfullscreen(c, 1);
++
+ c->mon->un_map = 1;
+ if (!c->noswallow) {
+ Client *p = termforwin(c);
+@@ -2028,8 +2029,6 @@ void
+ setfullscreen(Client *c, int fullscreen)
+ {
+ c->isfullscreen = fullscreen;
+- if (!c->mon)
+- return;
+ c->bw = fullscreen ? 0 : borderpx;
+ client_set_fullscreen(c, fullscreen);
+
+@@ -2115,7 +2114,7 @@ setmon(Client *c, Monitor *m, unsigned int newtags)
+ resize(c, c->geom, 0, 1);
+ wlr_surface_send_enter(client_surface(c), m->wlr_output);
+ c->tags = newtags ? newtags : m->tagset[m->seltags]; /* assign tags of target monitor */
+- setfullscreen(c, c->isfullscreen); /* This will call arrange(c->mon) */
++ arrange(m);
+ }
+ focusclient(focustop(selmon), 1);
+ }
diff --git a/dwl.c b/dwl.c
index 9a7b42e..aba280f 100644
--- a/dwl.c
+++ b/dwl.c
@@ -90,7 +90,8 @@ typedef struct {
} Button;
typedef struct Monitor Monitor;
-typedef struct {
+typedef struct Client Client;
+struct Client{
/* Must keep these three elements in this order */
unsigned int type; /* XDGShell or X11* */
struct wlr_box geom; /* layout-relative, includes border */
@@ -119,9 +120,12 @@ typedef struct {
#endif
unsigned int bw;
unsigned int tags;
- int isfloating, isurgent, isfullscreen;
+ int isfloating, isurgent, isfullscreen, isterm, noswallow;
uint32_t resize; /* configure serial of a pending resize */
-} Client;
+ pid_t pid;
+ Client *swallowing, *swallowedby;
+ char scratchkey;
+};
typedef struct {
uint32_t singular_anchor;
@@ -200,7 +204,10 @@ typedef struct {
const char *title;
unsigned int tags;
int isfloating;
+ int isterm;
+ int noswallow;
int monitor;
+ const char scratchkey;
} Rule;
/* function declarations */
@@ -213,6 +220,7 @@ static void arrange(Monitor *m);
static void arrangelayer(Monitor *m, struct wl_list *list,
struct wlr_box *usable_area, int exclusive);
static void arrangelayers(Monitor *m);
+static void autostartexec(void);
static void axisnotify(struct wl_listener *listener, void *data);
static void buttonpress(struct wl_listener *listener, void *data);
static void chvt(const Arg *arg);
@@ -263,7 +271,7 @@ static void quit(const Arg *arg);
static void quitsignal(int signo);
static void rendermon(struct wl_listener *listener, void *data);
static void requeststartdrag(struct wl_listener *listener, void *data);
-static void resize(Client *c, struct wlr_box geo, int interact);
+static void resize(Client *c, struct wlr_box geo, int interact, int draw_borders);
static void run(char *startup_cmd);
static Client *selclient(void);
static void setcursor(struct wl_listener *listener, void *data);
@@ -277,12 +285,14 @@ static void setsel(struct wl_listener *listener, void *data);
static void setup(void);
static void sigchld(int unused);
static void spawn(const Arg *arg);
+static void spawnscratch(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 tile(Monitor *m);
static void togglefloating(const Arg *arg);
static void togglefullscreen(const Arg *arg);
+static void togglescratch(const Arg *arg);
static void toggletag(const Arg *arg);
static void toggleview(const Arg *arg);
static void unmaplayersurfacenotify(struct wl_listener *listener, void *data);
@@ -292,15 +302,20 @@ static void updatetitle(struct wl_listener *listener, void *data);
static void urgent(struct wl_listener *listener, void *data);
static void view(const Arg *arg);
static void virtualkeyboard(struct wl_listener *listener, void *data);
+static void warpcursor(const Client *c);
static Monitor *xytomon(double x, double y);
static struct wlr_scene_node *xytonode(double x, double y, struct wlr_surface **psurface,
Client **pc, LayerSurface **pl, double *nx, double *ny);
static void zoom(const Arg *arg);
+static pid_t getparentprocess(pid_t p);
+static int isdescprocess(pid_t p, pid_t c);
+static Client *termforwin(Client *w);
+static void swallow(Client *c, Client *w);
/* variables */
static const char broken[] = "broken";
static pid_t child_pid = -1;
-static void *exclusive_focus;
+static struct wlr_surface *exclusive_focus;
static struct wl_display *dpy;
static struct wlr_backend *backend;
static struct wlr_scene *scene;
@@ -372,7 +387,9 @@ static Atom netatom[NetLast];
#endif
/* configuration, allows nested code to access above variables */
+#include "push.h"
#include "config.h"
+#include "push.c"
/* attempt to encapsulate suck into one file */
#include "client.h"
@@ -380,6 +397,9 @@ static Atom netatom[NetLast];
/* compile-time check if all tags fit into an unsigned int bit array. */
struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
+static pid_t *autostart_pids;
+static size_t autostart_len;
+
/* function implementations */
void
applybounds(Client *c, struct wlr_box *bbox)
@@ -407,6 +427,29 @@ applybounds(Client *c, struct wlr_box *bbox)
}
void
+autostartexec(void) {
+ const char *const *p;
+ size_t i = 0;
+
+ /* count entries */
+ for (p = autostart; *p; autostart_len++, p++)
+ while (*++p);
+
+ autostart_pids = calloc(autostart_len, sizeof(pid_t));
+ for (p = autostart; *p; i++, p++) {
+ if ((autostart_pids[i] = fork()) == 0) {
+ setsid();
+ execvp(*p, (char *const *)p);
+ fprintf(stderr, "dwl: execvp %s\n", *p);
+ perror(" failed");
+ _exit(EXIT_FAILURE);
+ }
+ /* skip arguments */
+ while (*++p);
+ }
+}
+
+void
applyexclusive(struct wlr_box *usable_area,
uint32_t anchor, int32_t exclusive,
int32_t margin_top, int32_t margin_right,
@@ -471,6 +514,7 @@ applyrules(Client *c)
Monitor *mon = selmon, *m;
c->isfloating = client_is_float_type(c);
+ c->scratchkey = 0;
if (!(appid = client_get_appid(c)))
appid = broken;
if (!(title = client_get_title(c)))
@@ -480,6 +524,9 @@ applyrules(Client *c)
if ((!r->title || strstr(title, r->title))
&& (!r->id || strstr(appid, r->id))) {
c->isfloating = r->isfloating;
+ c->scratchkey = r->scratchkey;
+ c->isterm = r->isterm;
+ c->noswallow = r->noswallow;
newtags |= r->tags;
i = 0;
wl_list_for_each(m, &mons, link)
@@ -487,6 +534,8 @@ applyrules(Client *c)
mon = m;
}
}
+ c->geom.x = (mon->w.width - c->geom.width) / 2 + mon->m.x;
+ c->geom.y = (mon->w.height - c->geom.height) / 2 + mon->m.y;
wlr_scene_node_reparent(c->scene, layers[c->isfloating ? LyrFloat : LyrTile]);
setmon(c, mon, newtags);
}
@@ -501,6 +550,9 @@ arrange(Monitor *m)
if (m->lt[m->sellt]->arrange)
m->lt[m->sellt]->arrange(m);
motionnotify(0);
+ c = selclient();
+ if (cursor_warp && c)
+ warpcursor(c);
}
void
@@ -522,7 +574,7 @@ arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, int
const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
| ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
- if (wlr_layer_surface->mapped && exclusive != (state->exclusive_zone > 0))
+ if (exclusive != (state->exclusive_zone > 0))
continue;
bounds = state->exclusive_zone == -1 ? full_area : *usable_area;
@@ -591,8 +643,6 @@ arrangelayers(Monitor *m)
ZWLR_LAYER_SHELL_V1_LAYER_TOP,
};
LayerSurface *layersurface;
- if (!m || !m->wlr_output->enabled)
- return;
/* Arrange exclusive surfaces from top->bottom */
for (i = 3; i >= 0; i--)
@@ -615,8 +665,8 @@ arrangelayers(Monitor *m)
layersurface->layer_surface->mapped) {
/* Deactivate the focused client. */
focusclient(NULL, 0);
- exclusive_focus = layersurface;
- client_notify_enter(layersurface->layer_surface->surface, wlr_seat_get_keyboard(seat));
+ exclusive_focus = layersurface->layer_surface->surface;
+ client_notify_enter(exclusive_focus, wlr_seat_get_keyboard(seat));
return;
}
}
@@ -751,7 +801,6 @@ cleanupmon(struct wl_listener *listener, void *data)
wl_list_remove(&m->destroy.link);
wl_list_remove(&m->frame.link);
wl_list_remove(&m->link);
- wlr_output->data = NULL;
wlr_output_layout_remove(output_layout, m->wlr_output);
wlr_scene_output_destroy(m->scene_output);
@@ -774,7 +823,7 @@ closemon(Monitor *m)
wl_list_for_each(c, &clients, link) {
if (c->isfloating && c->geom.x > m->m.width)
resize(c, (struct wlr_box){.x = c->geom.x - m->w.width, .y = c->geom.y,
- .width = c->geom.width, .height = c->geom.height}, 0);
+ .width = c->geom.width, .height = c->geom.height}, 0, 1);
if (c->mon == m)
setmon(c, selmon, c->tags);
}
@@ -786,9 +835,8 @@ commitlayersurfacenotify(struct wl_listener *listener, void *data)
{
LayerSurface *layersurface = wl_container_of(listener, layersurface, surface_commit);
struct wlr_layer_surface_v1 *wlr_layer_surface = layersurface->layer_surface;
- struct wlr_output *wlr_output = wlr_layer_surface->output;
- if (!wlr_output || !(layersurface->mon = wlr_output->data))
+ if (!layersurface->mon)
return;
if (layers[wlr_layer_surface->current.layer] != layersurface->scene) {
@@ -996,6 +1044,8 @@ createnotify(struct wl_listener *listener, void *data)
c->surface.xdg = xdg_surface;
c->bw = borderpx;
+ wl_client_get_credentials(c->surface.xdg->client->client, &c->pid, NULL, NULL);
+
LISTEN(&xdg_surface->events.map, &c->map, mapnotify);
LISTEN(&xdg_surface->events.unmap, &c->unmap, unmapnotify);
LISTEN(&xdg_surface->events.destroy, &c->destroy, destroynotify);
@@ -1077,6 +1127,8 @@ destroylayersurfacenotify(struct wl_listener *listener, void *data)
wl_list_remove(&layersurface->unmap.link);
wl_list_remove(&layersurface->surface_commit.link);
wlr_scene_node_destroy(layersurface->scene);
+ if (layersurface->mon)
+ arrangelayers(layersurface->mon);
free(layersurface);
}
@@ -1133,6 +1185,10 @@ focusclient(Client *c, int lift)
if (exclusive_focus)
return;
+ /* Warp cursor to center of client if it is outside */
+ if (cursor_warp && c)
+ warpcursor(c);
+
/* Raise client in stacking order if requested */
if (c && lift)
wlr_scene_node_raise_to_top(c->scene);
@@ -1255,6 +1311,61 @@ fullscreennotify(struct wl_listener *listener, void *data)
setfullscreen(c, fullscreen);
}
+pid_t
+getparentprocess(pid_t p)
+{
+ unsigned int v = 0;
+
+ FILE *f;
+ char buf[256];
+ snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p);
+
+ if (!(f = fopen(buf, "r")))
+ return 0;
+
+ fscanf(f, "%*u %*s %*c %u", &v);
+ fclose(f);
+
+ return (pid_t)v;
+}
+
+int
+isdescprocess(pid_t p, pid_t c)
+{
+ while (p != c && c != 0)
+ c = getparentprocess(c);
+
+ return (int)c;
+}
+
+Client *
+termforwin(Client *w)
+{
+ Client *c;
+
+ if (!w->pid || w->isterm || w->noswallow)
+ return NULL;
+
+ wl_list_for_each(c, &clients, link)
+ if (c->isterm && !c->swallowing && c->pid && isdescprocess(c->pid, w->pid))
+ return c;
+
+ return NULL;
+}
+
+void
+swallow(Client *c, Client *w) {
+ c->bw = w->bw;
+ c->isfloating = w->isfloating;
+ c->isurgent = w->isurgent;
+ c->isfullscreen = w->isfullscreen;
+ resize(c, w->geom, 0, 0);
+ wl_list_insert(&w->link, &c->link);
+ wl_list_insert(&w->flink, &c->flink);
+ wlr_scene_node_set_enabled(w->scene, 0);
+ wlr_scene_node_set_enabled(c->scene, 1);
+}
+
void
incnmaster(const Arg *arg)
{
@@ -1441,7 +1552,23 @@ mapnotify(struct wl_listener *listener, void *data)
}
printstatus();
+ if (c->isfullscreen)
+ setfullscreen(c, 1);
+
c->mon->un_map = 1;
+ if (!c->noswallow) {
+ Client *p = termforwin(c);
+ if (p) {
+ c->swallowedby = p;
+ p->swallowing = c;
+ wl_list_remove(&c->link);
+ wl_list_remove(&c->flink);
+ swallow(c,p);
+ wl_list_remove(&p->link);
+ wl_list_remove(&p->flink);
+ }
+ arrange(c->mon);
+ }
}
void
@@ -1452,7 +1579,7 @@ monocle(Monitor *m)
wl_list_for_each(c, &clients, link) {
if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
continue;
- resize(c, m->w, 0);
+ resize(c, m->w, 0, !smartborders);
}
focusclient(focustop(m), 1);
}
@@ -1495,11 +1622,11 @@ motionnotify(uint32_t time)
if (cursor_mode == CurMove) {
/* Move the grabbed client to the new position. */
resize(grabc, (struct wlr_box){.x = cursor->x - grabcx, .y = cursor->y - grabcy,
- .width = grabc->geom.width, .height = grabc->geom.height}, 1);
+ .width = grabc->geom.width, .height = grabc->geom.height}, 1, 1);
return;
} else if (cursor_mode == CurResize) {
resize(grabc, (struct wlr_box){.x = grabc->geom.x, .y = grabc->geom.y,
- .width = cursor->x - grabc->geom.x, .height = cursor->y - grabc->geom.y}, 1);
+ .width = cursor->x - grabc->geom.x, .height = cursor->y - grabc->geom.y}, 1, 1);
return;
}
@@ -1717,6 +1844,16 @@ printstatus(void)
void
quit(const Arg *arg)
{
+ size_t i;
+
+ /* kill child processes */
+ for (i = 0; i < autostart_len; i++) {
+ if (0 < autostart_pids[i]) {
+ kill(autostart_pids[i], SIGTERM);
+ waitpid(autostart_pids[i], NULL, 0);
+ }
+ }
+
wl_display_terminate(dpy);
}
@@ -1772,10 +1909,11 @@ requeststartdrag(struct wl_listener *listener, void *data)
}
void
-resize(Client *c, struct wlr_box geo, int interact)
+resize(Client *c, struct wlr_box geo, int interact, int draw_borders)
{
struct wlr_box *bbox = interact ? &sgeom : &c->mon->w;
c->geom = geo;
+ c->bw = draw_borders ? borderpx : 0;
applybounds(c, bbox);
/* Update scene-graph, including borders */
@@ -1804,6 +1942,7 @@ run(char *startup_cmd)
setenv("WAYLAND_DISPLAY", socket, 1);
/* Now that the socket exists, run the startup command */
+ autostartexec();
if (startup_cmd) {
int piperw[2];
if (pipe(piperw) < 0)
@@ -1881,6 +2020,8 @@ setfloating(Client *c, int floating)
{
c->isfloating = floating;
wlr_scene_node_reparent(c->scene, layers[c->isfloating ? LyrFloat : LyrTile]);
+ if (c->isfloating && !c->bw)
+ resize(c, c->mon->m, 0, 1);
arrange(c->mon);
printstatus();
}
@@ -1889,14 +2030,12 @@ void
setfullscreen(Client *c, int fullscreen)
{
c->isfullscreen = fullscreen;
- if (!c->mon)
- return;
c->bw = fullscreen ? 0 : borderpx;
client_set_fullscreen(c, fullscreen);
if (fullscreen) {
c->prev = c->geom;
- resize(c, c->mon->m, 0);
+ resize(c, c->mon->m, 0, 0);
/* The xdg-protocol specifies:
*
* If the fullscreened surface is not opaque, the compositor must make
@@ -1914,7 +2053,7 @@ setfullscreen(Client *c, int fullscreen)
} else {
/* restore previous size instead of arrange for floating windows since
* client positions are set by the user and cannot be recalculated */
- resize(c, c->prev, 0);
+ resize(c, c->prev, 0, 1);
if (c->fullscreen_bg) {
wlr_scene_node_destroy(&c->fullscreen_bg->node);
c->fullscreen_bg = NULL;
@@ -1931,6 +2070,12 @@ setlayout(const Arg *arg)
selmon->sellt ^= 1;
if (arg && arg->v)
selmon->lt[selmon->sellt] = (Layout *)arg->v;
+ if (!selmon->lt[selmon->sellt]->arrange) {
+ /* floating layout, draw borders around all clients */
+ Client *c;
+ wl_list_for_each(c, &clients, link)
+ resize(c, c->mon->m, 0, 1);
+ }
/* TODO change layout symbol? */
arrange(selmon);
printstatus();
@@ -1967,10 +2112,10 @@ setmon(Client *c, Monitor *m, unsigned int newtags)
}
if (m) {
/* Make sure window actually overlaps with the monitor */
- resize(c, c->geom, 0);
+ resize(c, c->geom, 0, 1);
wlr_surface_send_enter(client_surface(c), m->wlr_output);
c->tags = newtags ? newtags : m->tagset[m->seltags]; /* assign tags of target monitor */
- setfullscreen(c, c->isfullscreen); /* This will call arrange(c->mon) */
+ arrange(m);
}
focusclient(focustop(selmon), 1);
}
@@ -2181,11 +2326,24 @@ sigchld(int unused)
* setting our own disposition for SIGCHLD.
*/
pid_t pid;
+
if (signal(SIGCHLD, sigchld) == SIG_ERR)
die("can't install SIGCHLD handler:");
- while (0 < (pid = waitpid(-1, NULL, WNOHANG)))
+ while (0 < (pid = waitpid(-1, NULL, WNOHANG))) {
+ pid_t *p, *lim;
if (pid == child_pid)
child_pid = -1;
+ if (!(p = autostart_pids))
+ continue;
+ lim = &p[autostart_len];
+
+ for (; p < lim; p++) {
+ if (*p == pid) {
+ *p = -1;
+ break;
+ }
+ }
+ }
}
void
@@ -2199,6 +2357,16 @@ spawn(const Arg *arg)
}
}
+void spawnscratch(const Arg *arg)
+{
+ if (fork() == 0) {
+ dup2(STDERR_FILENO, STDOUT_FILENO);
+ setsid();
+ execvp(((char **)arg->v)[1], ((char **)arg->v)+1);
+ die("dwl: execvp %s failed:", ((char **)arg->v)[1]);
+ }
+}
+
void
startdrag(struct wl_listener *listener, void *data)
{
@@ -2236,7 +2404,7 @@ tagmon(const Arg *arg)
void
tile(Monitor *m)
{
- unsigned int i, n = 0, mw, my, ty;
+ unsigned int i, n = 0, mw, my, ty, draw_borders = 1;
Client *c;
wl_list_for_each(c, &clients, link)
@@ -2245,6 +2413,9 @@ tile(Monitor *m)
if (n == 0)
return;
+ if (n == smartborders)
+ draw_borders = 0;
+
if (n > m->nmaster)
mw = m->nmaster ? m->w.width * m->mfact : 0;
else
@@ -2255,11 +2426,11 @@ tile(Monitor *m)
continue;
if (i < m->nmaster) {
resize(c, (struct wlr_box){.x = m->w.x, .y = m->w.y + my, .width = mw,
- .height = (m->w.height - my) / (MIN(n, m->nmaster) - i)}, 0);
+ .height = (m->w.height - my) / (MIN(n, m->nmaster) - i)}, 0, draw_borders);
my += c->geom.height;
} else {
resize(c, (struct wlr_box){.x = m->w.x + mw, .y = m->w.y + ty,
- .width = m->w.width - mw, .height = (m->w.height - ty) / (n - i)}, 0);
+ .width = m->w.width - mw, .height = (m->w.height - ty) / (n - i)}, 0, draw_borders);
ty += c->geom.height;
}
i++;
@@ -2284,6 +2455,29 @@ togglefullscreen(const Arg *arg)
}
void
+togglescratch(const Arg *arg)
+{
+ Client *c;
+ unsigned int found = 0;
+
+ /* search for first window that matches the scratchkey */
+ wl_list_for_each(c, &clients, link)
+ if (c->scratchkey == ((char**)arg->v)[0][0]) {
+ found = 1;
+ break;
+ }
+
+ if (found) {
+ c->tags = VISIBLEON(c, selmon) ? 0 : selmon->tagset[selmon->seltags];
+
+ focusclient(c->tags == 0 ? focustop(selmon) : c, 1);
+ arrange(selmon);
+ } else{
+ spawnscratch(arg);
+ }
+}
+
+void
toggletag(const Arg *arg)
{
unsigned int newtags;
@@ -2319,10 +2513,7 @@ unmaplayersurfacenotify(struct wl_listener *listener, void *data)
layersurface->layer_surface->mapped = (layersurface->mapped = 0);
wlr_scene_node_set_enabled(layersurface->scene, 0);
- if (layersurface->layer_surface->output
- && (layersurface->mon = layersurface->layer_surface->output->data))
- arrangelayers(layersurface->mon);
- if (layersurface == exclusive_focus)
+ if (layersurface->layer_surface->surface == exclusive_focus)
exclusive_focus = NULL;
if (layersurface->layer_surface->surface ==
seat->keyboard_state.focused_surface)
@@ -2339,6 +2530,16 @@ unmapnotify(struct wl_listener *listener, void *data)
cursor_mode = CurNormal;
grabc = NULL;
}
+ if (c->swallowing) {
+ c->swallowing->swallowedby = NULL;
+ c->swallowing = NULL;
+ }
+
+ if (c->swallowedby) {
+ swallow(c->swallowedby, c);
+ c->swallowedby->swallowing = NULL;
+ c->swallowedby = NULL;
+ }
if (c->mon)
c->mon->un_map = 1;
@@ -2440,6 +2641,18 @@ virtualkeyboard(struct wl_listener *listener, void *data)
createkeyboard(device);
}
+void
+warpcursor(const Client *c) {
+ if (cursor->x < c->geom.x ||
+ cursor->x > c->geom.x + c->geom.width ||
+ cursor->y < c->geom.y ||
+ cursor->y > c->geom.y + c->geom.height)
+ wlr_cursor_warp_closest(cursor,
+ NULL,
+ c->geom.x + c->geom.width / 2.0,
+ c->geom.y + c->geom.height / 2.0);
+}
+
Monitor *
xytomon(double x, double y)
{
diff --git a/patches/dwl-alwayscenter.diff b/patches/dwl-alwayscenter.diff
new file mode 100644
index 0000000..24e52ba
--- /dev/null
+++ b/patches/dwl-alwayscenter.diff
@@ -0,0 +1,23 @@
+From 9173fdaf0d1eb853ff194ea11767ac15a0d13560 Mon Sep 17 00:00:00 2001
+From: Guido Cella <guido@guidocella.xyz>
+Date: Sat, 17 Apr 2021 08:59:55 +0200
+Subject: [PATCH] Center floating windows
+
+Credits to Benjamin Chausse for fixing this with multiple monitors.
+---
+ dwl.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/dwl.c b/dwl.c
+index 22287fb3..80c16459 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -467,6 +467,8 @@ applyrules(Client *c)
+ mon = m;
+ }
+ }
++ c->geom.x = (mon->w.width - c->geom.width) / 2 + mon->m.x;
++ c->geom.y = (mon->w.height - c->geom.height) / 2 + mon->m.y;
+ wlr_scene_node_reparent(c->scene, layers[c->isfloating ? LyrFloat : LyrTile]);
+ setmon(c, mon, newtags);
+ }
diff --git a/patches/dwl-autostart.diff b/patches/dwl-autostart.diff
new file mode 100644
index 0000000..1bccc40
--- /dev/null
+++ b/patches/dwl-autostart.diff
@@ -0,0 +1,132 @@
+From 744a6ce137f30b4b4ab9271bfb5ea5b1ccf4423c Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?=
+ <leohdz172@protonmail.com>
+Date: Wed, 9 Feb 2022 07:02:47 -0600
+Subject: [PATCH] apply autostart patch from dwm
+
+https://dwm.suckless.org/patches/cool_autostart/
+---
+ config.def.h | 6 ++++++
+ dwl.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++-
+ 2 files changed, 58 insertions(+), 1 deletion(-)
+
+diff --git a/config.def.h b/config.def.h
+index 29c6dbf8..35372a68 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -86,6 +86,12 @@ LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE
+ static const enum libinput_config_accel_profile accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
+ static const double accel_speed = 0.0;
+
++/* Autostart */
++static const char *const autostart[] = {
++ "sh", "-c", "swaybg --image /xap/local/background", NULL,
++ NULL /* terminate */
++};
++
+ /* If you want to use the windows key change this to WLR_MODIFIER_LOGO */
+ #define MODKEY WLR_MODIFIER_ALT
+ #define TAGKEYS(KEY,SKEY,TAG) \
+diff --git a/dwl.c b/dwl.c
+index 40ea05a6..514ec4b0 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -213,6 +213,7 @@ static void arrange(Monitor *m);
+ static void arrangelayer(Monitor *m, struct wl_list *list,
+ struct wlr_box *usable_area, int exclusive);
+ static void arrangelayers(Monitor *m);
++static void autostartexec(void);
+ static void axisnotify(struct wl_listener *listener, void *data);
+ static void buttonpress(struct wl_listener *listener, void *data);
+ static void chvt(const Arg *arg);
+@@ -379,6 +380,9 @@ static Atom netatom[NetLast];
+ /* compile-time check if all tags fit into an unsigned int bit array. */
+ struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
+
++static pid_t *autostart_pids;
++static size_t autostart_len;
++
+ /* function implementations */
+ void
+ applybounds(Client *c, struct wlr_box *bbox)
+@@ -403,6 +407,29 @@ applybounds(Client *c, struct wlr_box *bbox)
+ c->geom.y = bbox->y;
+ }
+
++void
++autostartexec(void) {
++ const char *const *p;
++ size_t i = 0;
++
++ /* count entries */
++ for (p = autostart; *p; autostart_len++, p++)
++ while (*++p);
++
++ autostart_pids = calloc(autostart_len, sizeof(pid_t));
++ for (p = autostart; *p; i++, p++) {
++ if ((autostart_pids[i] = fork()) == 0) {
++ setsid();
++ execvp(*p, (char *const *)p);
++ fprintf(stderr, "dwl: execvp %s\n", *p);
++ perror(" failed");
++ _exit(EXIT_FAILURE);
++ }
++ /* skip arguments */
++ while (*++p);
++ }
++}
++
+ void
+ applyexclusive(struct wlr_box *usable_area,
+ uint32_t anchor, int32_t exclusive,
+@@ -1721,6 +1748,16 @@ printstatus(void)
+ void
+ quit(const Arg *arg)
+ {
++ size_t i;
++
++ /* kill child processes */
++ for (i = 0; i < autostart_len; i++) {
++ if (0 < autostart_pids[i]) {
++ kill(autostart_pids[i], SIGTERM);
++ waitpid(autostart_pids[i], NULL, 0);
++ }
++ }
++
+ wl_display_terminate(dpy);
+ }
+
+@@ -1808,6 +1845,7 @@ run(char *startup_cmd)
+ setenv("WAYLAND_DISPLAY", socket, 1);
+
+ /* Now that the socket exists, run the startup command */
++ autostartexec();
+ if (startup_cmd) {
+ int piperw[2];
+ if (pipe(piperw) < 0)
+@@ -2183,11 +2221,24 @@ sigchld(int unused)
+ * setting our own disposition for SIGCHLD.
+ */
+ pid_t pid;
++
+ if (signal(SIGCHLD, sigchld) == SIG_ERR)
+ die("can't install SIGCHLD handler:");
+- while (0 < (pid = waitpid(-1, NULL, WNOHANG)))
++ while (0 < (pid = waitpid(-1, NULL, WNOHANG))) {
++ pid_t *p, *lim;
+ if (pid == child_pid)
+ child_pid = -1;
++ if (!(p = autostart_pids))
++ continue;
++ lim = &p[autostart_len];
++
++ for (; p < lim; p++) {
++ if (*p == pid) {
++ *p = -1;
++ break;
++ }
++ }
++ }
+ }
+
+ void
diff --git a/patches/dwl-cursorwarp.diff b/patches/dwl-cursorwarp.diff
new file mode 100644
index 0000000..0c4b320
--- /dev/null
+++ b/patches/dwl-cursorwarp.diff
@@ -0,0 +1,145 @@
+From 8703fcdeac789425fbea768c3efa30884518e72e Mon Sep 17 00:00:00 2001
+From: Faerryn <alexandre.liao@gmail.com>
+Date: Thu, 26 May 2022 23:18:59 -0400
+Subject: [PATCH 1/3] cursor wrap
+
+---
+ dwl.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/dwl.c b/dwl.c
+index d0f5afc8..7427fa06 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -1107,6 +1107,16 @@ focusclient(Client *c, int lift)
+ struct wlr_keyboard *kb;
+ int i;
+
++ /* Warp cursor to center of client if it is outside */
++ if (c && (cursor->x < c->geom.x ||
++ cursor->x > c->geom.x + c->geom.width ||
++ cursor->y < c->geom.y ||
++ cursor->y > c->geom.y + c->geom.height))
++ wlr_cursor_warp_closest( cursor,
++ NULL,
++ c->geom.x + c->geom.width / 2.0,
++ c->geom.y + c->geom.height / 2.0);
++
+ /* Raise client in stacking order if requested */
+ if (c && lift)
+ wlr_scene_node_raise_to_top(c->scene);
+
+From 9cd5b806a96823fc10784a39c86d57156e749d11 Mon Sep 17 00:00:00 2001
+From: Faerryn <alexandre.liao@gmail.com>
+Date: Fri, 27 May 2022 14:51:09 -0400
+Subject: [PATCH 2/3] both focusclient and arrange will warp cursor
+
+---
+ dwl.c | 26 ++++++++++++++++++--------
+ 1 file changed, 18 insertions(+), 8 deletions(-)
+
+diff --git a/dwl.c b/dwl.c
+index 7427fa06..2bdcaa56 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -290,6 +290,7 @@ static void updatetitle(struct wl_listener *listener, void *data);
+ static void urgent(struct wl_listener *listener, void *data);
+ static void view(const Arg *arg);
+ static void virtualkeyboard(struct wl_listener *listener, void *data);
++static void warpcursor(const Client *c);
+ static Monitor *xytomon(double x, double y);
+ static struct wlr_scene_node *xytonode(double x, double y, struct wlr_surface **psurface,
+ Client **pc, LayerSurface **pl, double *nx, double *ny);
+@@ -489,6 +490,9 @@ arrange(Monitor *m)
+ if (m->lt[m->sellt]->arrange)
+ m->lt[m->sellt]->arrange(m);
+ /* TODO recheck pointer focus here... or in resize()? */
++ c = selclient();
++ if (c)
++ warpcursor(c);
+ }
+
+ void
+@@ -1108,14 +1112,8 @@ focusclient(Client *c, int lift)
+ int i;
+
+ /* Warp cursor to center of client if it is outside */
+- if (c && (cursor->x < c->geom.x ||
+- cursor->x > c->geom.x + c->geom.width ||
+- cursor->y < c->geom.y ||
+- cursor->y > c->geom.y + c->geom.height))
+- wlr_cursor_warp_closest( cursor,
+- NULL,
+- c->geom.x + c->geom.width / 2.0,
+- c->geom.y + c->geom.height / 2.0);
++ if (c)
++ warpcursor(c);
+
+ /* Raise client in stacking order if requested */
+ if (c && lift)
+@@ -2375,6 +2373,18 @@ virtualkeyboard(struct wl_listener *listener, void *data)
+ createkeyboard(device);
+ }
+
++void
++warpcursor(const Client *c) {
++ if (cursor->x < c->geom.x ||
++ cursor->x > c->geom.x + c->geom.width ||
++ cursor->y < c->geom.y ||
++ cursor->y > c->geom.y + c->geom.height)
++ wlr_cursor_warp_closest(cursor,
++ NULL,
++ c->geom.x + c->geom.width / 2.0,
++ c->geom.y + c->geom.height / 2.0);
++}
++
+ Monitor *
+ xytomon(double x, double y)
+ {
+
+From 95a8e2c3372b2dc245533001164b1e427751d394 Mon Sep 17 00:00:00 2001
+From: Faerryn <alexandre.liao@gmail.com>
+Date: Sat, 28 May 2022 12:35:32 -0400
+Subject: [PATCH 3/3] add an option to config.h for cursor_warp
+
+---
+ config.def.h | 3 +++
+ dwl.c | 4 ++--
+ 2 files changed, 5 insertions(+), 2 deletions(-)
+
+diff --git a/config.def.h b/config.def.h
+index 4f131dda..f82dd582 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -6,6 +6,9 @@ static const float rootcolor[] = {0.3, 0.3, 0.3, 1.0};
+ static const float bordercolor[] = {0.5, 0.5, 0.5, 1.0};
+ static const float focuscolor[] = {1.0, 0.0, 0.0, 1.0};
+
++/* cursor warping */
++static const bool cursor_warp = true;
++
+ /* tagging */
+ static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
+
+diff --git a/dwl.c b/dwl.c
+index 2bdcaa56..e593b152 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -491,7 +491,7 @@ arrange(Monitor *m)
+ m->lt[m->sellt]->arrange(m);
+ /* TODO recheck pointer focus here... or in resize()? */
+ c = selclient();
+- if (c)
++ if (cursor_warp && c)
+ warpcursor(c);
+ }
+
+@@ -1112,7 +1112,7 @@ focusclient(Client *c, int lift)
+ int i;
+
+ /* Warp cursor to center of client if it is outside */
+- if (c)
++ if (cursor_warp && c)
+ warpcursor(c);
+
+ /* Raise client in stacking order if requested */
diff --git a/patches/dwl-namedscratchpads.diff b/patches/dwl-namedscratchpads.diff
new file mode 100644
index 0000000..a83d322
--- /dev/null
+++ b/patches/dwl-namedscratchpads.diff
@@ -0,0 +1,176 @@
+From 13402c1ccead7a6775520bd1ceef31611583c965 Mon Sep 17 00:00:00 2001
+From: Louis-Michel Raynauld <louismichel@pweb.ca>
+Date: Fri, 29 Jul 2022 23:09:10 -0700
+Subject: [PATCH 1/2] port dwm "namedscratchpads" patch to dwl
+
+---
+ config.def.h | 9 +++++++--
+ dwl.c | 42 ++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 49 insertions(+), 2 deletions(-)
+
+diff --git a/config.def.h b/config.def.h
+index 190b0da4..b5f87336 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -10,11 +10,12 @@ static const float focuscolor[] = {1.0, 0.0, 0.0, 1.0};
+ static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
+
+ static const Rule rules[] = {
+- /* app_id title tags mask isfloating monitor */
++ /* app_id title tags mask isfloating monitor scratchkey */
+ /* examples:
+ { "Gimp", NULL, 0, 1, -1 },
+ */
+- { "firefox", NULL, 1 << 8, 0, -1 },
++ { "firefox", NULL, 1 << 8, 0, -1, 0 },
++ { NULL, "scratchpad", 0, 1, -1, 's' },
+ };
+
+ /* layout(s) */
+@@ -66,11 +67,15 @@ static const int natural_scrolling = 0;
+ static const char *termcmd[] = { "alacritty", NULL };
+ static const char *menucmd[] = { "bemenu-run", NULL };
+
++/* named scratchpads - First arg only serves to match against key in rules*/
++static const char *scratchpadcmd[] = { "s", "alacritty", "-t", "scratchpad", NULL };
++
+ static const Key keys[] = {
+ /* Note that Shift changes certain key codes: c -> C, 2 -> at, etc. */
+ /* modifier key function argument */
+ { MODKEY, XKB_KEY_p, spawn, {.v = menucmd} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, spawn, {.v = termcmd} },
++ { MODKEY, XKB_KEY_grave, togglescratch, {.v = scratchpadcmd } },
+ { MODKEY, XKB_KEY_j, focusstack, {.i = +1} },
+ { MODKEY, XKB_KEY_k, focusstack, {.i = -1} },
+ { MODKEY, XKB_KEY_i, incnmaster, {.i = +1} },
+diff --git a/dwl.c b/dwl.c
+index b09fc6f9..f21488c0 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -118,6 +118,7 @@ typedef struct {
+ int isfloating, isurgent;
+ uint32_t resize; /* configure serial of a pending resize */
+ int isfullscreen;
++ char scratchkey;
+ } Client;
+
+ typedef struct {
+@@ -198,6 +199,7 @@ typedef struct {
+ unsigned int tags;
+ int isfloating;
+ int monitor;
++ const char scratchkey;
+ } Rule;
+
+ /* function declarations */
+@@ -273,12 +275,14 @@ static void setsel(struct wl_listener *listener, void *data);
+ static void setup(void);
+ static void sigchld(int unused);
+ static void spawn(const Arg *arg);
++static void spawnscratch(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 tile(Monitor *m);
+ static void togglefloating(const Arg *arg);
+ static void togglefullscreen(const Arg *arg);
++static void togglescratch(const Arg *arg);
+ static void toggletag(const Arg *arg);
+ static void toggleview(const Arg *arg);
+ static void unmaplayersurface(LayerSurface *layersurface);
+@@ -457,6 +461,7 @@ applyrules(Client *c)
+ Monitor *mon = selmon, *m;
+
+ c->isfloating = client_is_float_type(c);
++ c->scratchkey = 0;
+ if (!(appid = client_get_appid(c)))
+ appid = broken;
+ if (!(title = client_get_title(c)))
+@@ -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->scratchkey = r->scratchkey;
+ newtags |= r->tags;
+ i = 0;
+ wl_list_for_each(m, &mons, link)
+@@ -2092,6 +2098,16 @@ spawn(const Arg *arg)
+ }
+ }
+
++void spawnscratch(const Arg *arg)
++{
++ if (fork() == 0) {
++ dup2(STDERR_FILENO, STDOUT_FILENO);
++ setsid();
++ execvp(((char **)arg->v)[1], ((char **)arg->v)+1);
++ die("dwl: execvp %s failed:", ((char **)arg->v)[1]);
++ }
++}
++
+ void
+ startdrag(struct wl_listener *listener, void *data)
+ {
+@@ -2176,6 +2192,32 @@ togglefullscreen(const Arg *arg)
+ setfullscreen(sel, !sel->isfullscreen);
+ }
+
++void
++togglescratch(const Arg *arg)
++{
++ Client *c;
++ unsigned int found = 0;
++
++ /* search for first window that matches the scratchkey */
++ wl_list_for_each(c, &clients, link)
++ if (c->scratchkey == ((char**)arg->v)[0][0]) {
++ found = 1;
++ break;
++ }
++
++ if (found) {
++ c->tags = VISIBLEON(c, selmon) ? 0 : selmon->tagset[selmon->seltags];
++ focusclient(NULL, 0);
++ if (VISIBLEON(c, selmon)) {
++ focusclient(c, 0);
++ }
++ arrange(selmon);
++
++ } else{
++ spawnscratch(arg);
++ }
++}
++
+ void
+ toggletag(const Arg *arg)
+ {
+
+From be5e75b1bad480a7eedd81ef68cc331687fbe7cd Mon Sep 17 00:00:00 2001
+From: Louis-Michel Raynauld <louismichel@pweb.ca>
+Date: Fri, 5 Aug 2022 22:20:59 -0700
+Subject: [PATCH 2/2] Fix focus after hiding and make sure toggle scratchpad
+ appears on top of others scratchpads if any.
+
+---
+ dwl.c | 7 ++-----
+ 1 file changed, 2 insertions(+), 5 deletions(-)
+
+diff --git a/dwl.c b/dwl.c
+index f21488c0..e0ecdb8e 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -2207,12 +2207,9 @@ togglescratch(const Arg *arg)
+
+ if (found) {
+ c->tags = VISIBLEON(c, selmon) ? 0 : selmon->tagset[selmon->seltags];
+- focusclient(NULL, 0);
+- if (VISIBLEON(c, selmon)) {
+- focusclient(c, 0);
+- }
+- arrange(selmon);
+
++ focusclient(c->tags == 0 ? focustop(selmon) : c, 1);
++ arrange(selmon);
+ } else{
+ spawnscratch(arg);
+ }
diff --git a/patches/dwl-push.diff b/patches/dwl-push.diff
new file mode 100644
index 0000000..06a1532
--- /dev/null
+++ b/patches/dwl-push.diff
@@ -0,0 +1,129 @@
+From dcb7a4d91075a9d918d082eb948c61d26eb0bc02 Mon Sep 17 00:00:00 2001
+From: "Devin J. Pohly" <djpohly@gmail.com>
+Date: Thu, 4 Mar 2021 00:45:50 -0600
+Subject: [PATCH 1/2] port dwm "push" patch to dwl
+
+---
+ Makefile | 2 ++
+ dwl.c | 2 ++
+ push.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 67 insertions(+)
+ create mode 100644 push.c
+
+diff --git a/Makefile b/Makefile
+index a0d1cc37..1701b63e 100644
+--- a/Makefile
++++ b/Makefile
+@@ -49,6 +49,8 @@ config.h: | config.def.h
+
+ dwl.o: config.h client.h xdg-shell-protocol.h wlr-layer-shell-unstable-v1-protocol.h idle-protocol.h
+
++dwl.o: push.c
++
+ dwl: xdg-shell-protocol.o wlr-layer-shell-unstable-v1-protocol.o idle-protocol.o
+
+ clean:
+diff --git a/dwl.c b/dwl.c
+index 72f1046b..4cfd7d07 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -369,7 +369,9 @@ static Atom netatom[NetLast];
+ #endif
+
+ /* configuration, allows nested code to access above variables */
++#include "push.h"
+ #include "config.h"
++#include "push.c"
+
+ /* attempt to encapsulate suck into one file */
+ #include "client.h"
+diff --git a/push.c b/push.c
+new file mode 100644
+index 00000000..26ed462f
+--- /dev/null
++++ b/push.c
+@@ -0,0 +1,63 @@
++static Client *
++nexttiled(Client *sel) {
++ Client *c;
++ wl_list_for_each(c, &sel->link, link) {
++ if (&c->link == &clients)
++ break; /* don't wrap */
++ if (!c->isfloating && VISIBLEON(c, selmon))
++ return c;
++ }
++ return NULL;
++}
++
++static Client *
++prevtiled(Client *sel) {
++ Client *c;
++ wl_list_for_each_reverse(c, &sel->link, link) {
++ if (&c->link == &clients)
++ break; /* don't wrap */
++ if (!c->isfloating && VISIBLEON(c, selmon))
++ return c;
++ }
++ return NULL;
++}
++
++static void
++pushup(const Arg *arg) {
++ Client *sel = selclient();
++ Client *c;
++
++ if(!sel || sel->isfloating)
++ return;
++ if((c = prevtiled(sel))) {
++ /* attach before c */
++ wl_list_remove(&sel->link);
++ wl_list_insert(c->link.prev, &sel->link);
++ } else {
++ /* move to the end */
++ wl_list_remove(&sel->link);
++ wl_list_insert(clients.prev, &sel->link);
++ }
++ focusclient(sel, 1);
++ arrange(selmon);
++}
++
++static void
++pushdown(const Arg *arg) {
++ Client *sel = selclient();
++ Client *c;
++
++ if(!sel || sel->isfloating)
++ return;
++ if((c = nexttiled(sel))) {
++ /* attach after c */
++ wl_list_remove(&sel->link);
++ wl_list_insert(&c->link, &sel->link);
++ } else {
++ /* move to the front */
++ wl_list_remove(&sel->link);
++ wl_list_insert(&clients, &sel->link);
++ }
++ focusclient(sel, 1);
++ arrange(selmon);
++}
+
+From 156a13d9f9f9d8df16b184f5ec0c84e6e5b94df6 Mon Sep 17 00:00:00 2001
+From: "Devin J. Pohly" <djpohly@gmail.com>
+Date: Thu, 4 Mar 2021 13:52:58 -0600
+Subject: [PATCH 2/2] add missing header
+
+---
+ push.h | 4 ++++
+ 1 file changed, 4 insertions(+)
+ create mode 100644 push.h
+
+diff --git a/push.h b/push.h
+new file mode 100644
+index 00000000..59c0f80e
+--- /dev/null
++++ b/push.h
+@@ -0,0 +1,4 @@
++static Client *nexttiled(Client *sel);
++static Client *prevtiled(Client *sel);
++static void pushdown(const Arg *arg);
++static void pushup(const Arg *arg);
diff --git a/patches/dwl-smartborders.diff b/patches/dwl-smartborders.diff
new file mode 100644
index 0000000..9f21db9
--- /dev/null
+++ b/patches/dwl-smartborders.diff
@@ -0,0 +1,164 @@
+From 24d23869dc7c27feddf33753946aac02197fdb67 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?=
+ <leohdz172@protonmail.com>
+Date: Tue, 16 Aug 2022 15:28:00 -0500
+Subject: [PATCH] don't draw borders if there is only one window
+
+Co-authored-by: Andrey Proskurin <andreyproskurin@protonmail.com>
+---
+ config.def.h | 1 +
+ dwl.c | 36 ++++++++++++++++++++++++------------
+ 2 files changed, 25 insertions(+), 12 deletions(-)
+
+diff --git a/config.def.h b/config.def.h
+index 29c6dbf8..19632a50 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -2,6 +2,7 @@
+ static const int sloppyfocus = 1; /* focus follows mouse */
+ static const unsigned int borderpx = 1; /* border pixel of windows */
+ static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */
++static const int smartborders = 1;
+ static const float rootcolor[] = {0.3, 0.3, 0.3, 1.0};
+ static const float bordercolor[] = {0.5, 0.5, 0.5, 1.0};
+ static const float focuscolor[] = {1.0, 0.0, 0.0, 1.0};
+diff --git a/dwl.c b/dwl.c
+index 40ea05a6..8f654f69 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -262,7 +262,7 @@ static void quit(const Arg *arg);
+ static void quitsignal(int signo);
+ static void rendermon(struct wl_listener *listener, void *data);
+ static void requeststartdrag(struct wl_listener *listener, void *data);
+-static void resize(Client *c, struct wlr_box geo, int interact);
++static void resize(Client *c, struct wlr_box geo, int interact, int draw_borders);
+ static void run(char *startup_cmd);
+ static Client *selclient(void);
+ static void setcursor(struct wl_listener *listener, void *data);
+@@ -754,7 +754,7 @@ closemon(Monitor *m)
+ wl_list_for_each(c, &clients, link) {
+ if (c->isfloating && c->geom.x > m->m.width)
+ resize(c, (struct wlr_box){.x = c->geom.x - m->w.width, .y = c->geom.y,
+- .width = c->geom.width, .height = c->geom.height}, 0);
++ .width = c->geom.width, .height = c->geom.height}, 0, 1);
+ if (c->mon == m)
+ setmon(c, selmon, c->tags);
+ }
+@@ -1456,7 +1456,7 @@ monocle(Monitor *m)
+ wl_list_for_each(c, &clients, link) {
+ if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
+ continue;
+- resize(c, m->w, 0);
++ resize(c, m->w, 0, !smartborders);
+ }
+ focusclient(focustop(m), 1);
+ }
+@@ -1499,11 +1499,11 @@ motionnotify(uint32_t time)
+ if (cursor_mode == CurMove) {
+ /* Move the grabbed client to the new position. */
+ resize(grabc, (struct wlr_box){.x = cursor->x - grabcx, .y = cursor->y - grabcy,
+- .width = grabc->geom.width, .height = grabc->geom.height}, 1);
++ .width = grabc->geom.width, .height = grabc->geom.height}, 1, 1);
+ return;
+ } else if (cursor_mode == CurResize) {
+ resize(grabc, (struct wlr_box){.x = grabc->geom.x, .y = grabc->geom.y,
+- .width = cursor->x - grabc->geom.x, .height = cursor->y - grabc->geom.y}, 1);
++ .width = cursor->x - grabc->geom.x, .height = cursor->y - grabc->geom.y}, 1, 1);
+ return;
+ }
+
+@@ -1776,10 +1776,11 @@ requeststartdrag(struct wl_listener *listener, void *data)
+ }
+
+ void
+-resize(Client *c, struct wlr_box geo, int interact)
++resize(Client *c, struct wlr_box geo, int interact, int draw_borders)
+ {
+ struct wlr_box *bbox = interact ? &sgeom : &c->mon->w;
+ c->geom = geo;
++ c->bw = draw_borders ? borderpx : 0;
+ applybounds(c, bbox);
+
+ /* Update scene-graph, including borders */
+@@ -1885,6 +1886,8 @@ setfloating(Client *c, int floating)
+ {
+ c->isfloating = floating;
+ wlr_scene_node_reparent(c->scene, layers[c->isfloating ? LyrFloat : LyrTile]);
++ if (c->isfloating && !c->bw)
++ resize(c, c->mon->m, 0, 1);
+ arrange(c->mon);
+ printstatus();
+ }
+@@ -1898,7 +1901,7 @@ setfullscreen(Client *c, int fullscreen)
+
+ if (fullscreen) {
+ c->prev = c->geom;
+- resize(c, c->mon->m, 0);
++ resize(c, c->mon->m, 0, 0);
+ /* The xdg-protocol specifies:
+ *
+ * If the fullscreened surface is not opaque, the compositor must make
+@@ -1916,7 +1919,7 @@ setfullscreen(Client *c, int fullscreen)
+ } else {
+ /* restore previous size instead of arrange for floating windows since
+ * client positions are set by the user and cannot be recalculated */
+- resize(c, c->prev, 0);
++ resize(c, c->prev, 0, 1);
+ if (c->fullscreen_bg) {
+ wlr_scene_node_destroy(&c->fullscreen_bg->node);
+ c->fullscreen_bg = NULL;
+@@ -1933,6 +1936,12 @@ setlayout(const Arg *arg)
+ selmon->sellt ^= 1;
+ if (arg && arg->v)
+ selmon->lt[selmon->sellt] = (Layout *)arg->v;
++ if (!selmon->lt[selmon->sellt]->arrange) {
++ /* floating layout, draw borders around all clients */
++ Client *c;
++ wl_list_for_each(c, &clients, link)
++ resize(c, c->mon->m, 0, 1);
++ }
+ /* TODO change layout symbol? */
+ arrange(selmon);
+ printstatus();
+@@ -1969,7 +1978,7 @@ setmon(Client *c, Monitor *m, unsigned int newtags)
+ }
+ if (m) {
+ /* Make sure window actually overlaps with the monitor */
+- resize(c, c->geom, 0);
++ resize(c, c->geom, 0, 1);
+ wlr_surface_send_enter(client_surface(c), m->wlr_output);
+ c->tags = newtags ? newtags : m->tagset[m->seltags]; /* assign tags of target monitor */
+ arrange(m);
+@@ -2238,7 +2247,7 @@ tagmon(const Arg *arg)
+ void
+ tile(Monitor *m)
+ {
+- unsigned int i, n = 0, mw, my, ty;
++ unsigned int i, n = 0, mw, my, ty, draw_borders = 1;
+ Client *c;
+
+ wl_list_for_each(c, &clients, link)
+@@ -2247,6 +2256,9 @@ tile(Monitor *m)
+ if (n == 0)
+ return;
+
++ if (n == smartborders)
++ draw_borders = 0;
++
+ if (n > m->nmaster)
+ mw = m->nmaster ? m->w.width * m->mfact : 0;
+ else
+@@ -2257,11 +2269,11 @@ tile(Monitor *m)
+ continue;
+ if (i < m->nmaster) {
+ resize(c, (struct wlr_box){.x = m->w.x, .y = m->w.y + my, .width = mw,
+- .height = (m->w.height - my) / (MIN(n, m->nmaster) - i)}, 0);
++ .height = (m->w.height - my) / (MIN(n, m->nmaster) - i)}, 0, draw_borders);
+ my += c->geom.height;
+ } else {
+ resize(c, (struct wlr_box){.x = m->w.x + mw, .y = m->w.y + ty,
+- .width = m->w.width - mw, .height = (m->w.height - ty) / (n - i)}, 0);
++ .width = m->w.width - mw, .height = (m->w.height - ty) / (n - i)}, 0, draw_borders);
+ ty += c->geom.height;
+ }
+ i++;
diff --git a/patches/dwl-swallow.diff b/patches/dwl-swallow.diff
new file mode 100644
index 0000000..8240686
--- /dev/null
+++ b/patches/dwl-swallow.diff
@@ -0,0 +1,194 @@
+From 17585c99f57856d39fd0d4c489d8bafd103d53f4 Mon Sep 17 00:00:00 2001
+From: Dmitry Zakharchenko <dmitz@disroot.org>
+Date: Sun, 28 Aug 2022 11:38:08 +0300
+Subject: [PATCH] update swallow patch
+
+---
+ config.def.h | 6 ++--
+ dwl.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++--
+ 2 files changed, 97 insertions(+), 6 deletions(-)
+
+diff --git a/config.def.h b/config.def.h
+index 29c6dbf8..4aa6903b 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -12,11 +12,11 @@ static const float fullscreen_bg[] = {0.1, 0.1, 0.1, 1.0};
+ static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
+
+ static const Rule rules[] = {
+- /* app_id title tags mask isfloating monitor */
++ /* app_id title tags mask isfloating isterm noswallow monitor */
+ /* examples:
+- { "Gimp", NULL, 0, 1, -1 },
+++ { "Gimp", NULL, 0, 1, 0, 1, -1 },
+ */
+- { "firefox", NULL, 1 << 8, 0, -1 },
++ { "firefox", NULL, 1 << 8, 0, 0, 1, -1 },
+ };
+
+ /* layout(s) */
+diff --git a/dwl.c b/dwl.c
+index 66aea65c..e897a9ff 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -90,7 +90,8 @@ typedef struct {
+ } Button;
+
+ typedef struct Monitor Monitor;
+-typedef struct {
++typedef struct Client Client;
++struct Client{
+ /* Must keep these three elements in this order */
+ unsigned int type; /* XDGShell or X11* */
+ struct wlr_box geom; /* layout-relative, includes border */
+@@ -119,9 +120,11 @@ typedef struct {
+ #endif
+ unsigned int bw;
+ unsigned int tags;
+- int isfloating, isurgent, isfullscreen;
++ int isfloating, isurgent, isfullscreen, isterm, noswallow;
+ uint32_t resize; /* configure serial of a pending resize */
+-} Client;
++ pid_t pid;
++ Client *swallowing, *swallowedby;
++};
+
+ typedef struct {
+ uint32_t singular_anchor;
+@@ -200,6 +203,8 @@ typedef struct {
+ const char *title;
+ unsigned int tags;
+ int isfloating;
++ int isterm;
++ int noswallow;
+ int monitor;
+ } Rule;
+
+@@ -295,6 +300,10 @@ static Monitor *xytomon(double x, double y);
+ static struct wlr_scene_node *xytonode(double x, double y, struct wlr_surface **psurface,
+ Client **pc, LayerSurface **pl, double *nx, double *ny);
+ static void zoom(const Arg *arg);
++static pid_t getparentprocess(pid_t p);
++static int isdescprocess(pid_t p, pid_t c);
++static Client *termforwin(Client *w);
++static void swallow(Client *c, Client *w);
+
+ /* variables */
+ static const char broken[] = "broken";
+@@ -479,6 +488,8 @@ applyrules(Client *c)
+ if ((!r->title || strstr(title, r->title))
+ && (!r->id || strstr(appid, r->id))) {
+ c->isfloating = r->isfloating;
++ c->isterm = r->isterm;
++ c->noswallow = r->noswallow;
+ newtags |= r->tags;
+ i = 0;
+ wl_list_for_each(m, &mons, link)
+@@ -975,6 +986,8 @@ createnotify(struct wl_listener *listener, void *data)
+ c->surface.xdg = xdg_surface;
+ c->bw = borderpx;
+
++ wl_client_get_credentials(c->surface.xdg->client->client, &c->pid, NULL, NULL);
++
+ LISTEN(&xdg_surface->events.map, &c->map, mapnotify);
+ LISTEN(&xdg_surface->events.unmap, &c->unmap, unmapnotify);
+ LISTEN(&xdg_surface->events.destroy, &c->destroy, destroynotify);
+@@ -1239,6 +1252,61 @@ fullscreennotify(struct wl_listener *listener, void *data)
+ setfullscreen(c, fullscreen);
+ }
+
++pid_t
++getparentprocess(pid_t p)
++{
++ unsigned int v = 0;
++
++ FILE *f;
++ char buf[256];
++ snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p);
++
++ if (!(f = fopen(buf, "r")))
++ return 0;
++
++ fscanf(f, "%*u %*s %*c %u", &v);
++ fclose(f);
++
++ return (pid_t)v;
++}
++
++int
++isdescprocess(pid_t p, pid_t c)
++{
++ while (p != c && c != 0)
++ c = getparentprocess(c);
++
++ return (int)c;
++}
++
++Client *
++termforwin(Client *w)
++{
++ Client *c;
++
++ if (!w->pid || w->isterm || w->noswallow)
++ return NULL;
++
++ wl_list_for_each(c, &clients, link)
++ if (c->isterm && !c->swallowing && c->pid && isdescprocess(c->pid, w->pid))
++ return c;
++
++ return NULL;
++}
++
++void
++swallow(Client *c, Client *w) {
++ c->bw = w->bw;
++ c->isfloating = w->isfloating;
++ c->isurgent = w->isurgent;
++ c->isfullscreen = w->isfullscreen;
++ resize(c, w->geom, 0);
++ wl_list_insert(&w->link, &c->link);
++ wl_list_insert(&w->flink, &c->flink);
++ wlr_scene_node_set_enabled(w->scene, 0);
++ wlr_scene_node_set_enabled(c->scene, 1);
++}
++
+ void
+ incnmaster(const Arg *arg)
+ {
+@@ -1430,6 +1498,19 @@ mapnotify(struct wl_listener *listener, void *data)
+ setfullscreen(c, 1);
+
+ c->mon->un_map = 1;
++ if (!c->noswallow) {
++ Client *p = termforwin(c);
++ if (p) {
++ c->swallowedby = p;
++ p->swallowing = c;
++ wl_list_remove(&c->link);
++ wl_list_remove(&c->flink);
++ swallow(c,p);
++ wl_list_remove(&p->link);
++ wl_list_remove(&p->flink);
++ }
++ arrange(c->mon);
++ }
+ }
+
+ void
+@@ -2322,6 +2403,16 @@ unmapnotify(struct wl_listener *listener, void *data)
+ cursor_mode = CurNormal;
+ grabc = NULL;
+ }
++ if (c->swallowing) {
++ c->swallowing->swallowedby = NULL;
++ c->swallowing = NULL;
++ }
++
++ if (c->swallowedby) {
++ swallow(c->swallowedby, c);
++ c->swallowedby->swallowing = NULL;
++ c->swallowedby = NULL;
++ }
+
+ if (c->mon)
+ c->mon->un_map = 1;
diff --git a/push.c b/push.c
new file mode 100644
index 0000000..26ed462
--- /dev/null
+++ b/push.c
@@ -0,0 +1,63 @@
+static Client *
+nexttiled(Client *sel) {
+ Client *c;
+ wl_list_for_each(c, &sel->link, link) {
+ if (&c->link == &clients)
+ break; /* don't wrap */
+ if (!c->isfloating && VISIBLEON(c, selmon))
+ return c;
+ }
+ return NULL;
+}
+
+static Client *
+prevtiled(Client *sel) {
+ Client *c;
+ wl_list_for_each_reverse(c, &sel->link, link) {
+ if (&c->link == &clients)
+ break; /* don't wrap */
+ if (!c->isfloating && VISIBLEON(c, selmon))
+ return c;
+ }
+ return NULL;
+}
+
+static void
+pushup(const Arg *arg) {
+ Client *sel = selclient();
+ Client *c;
+
+ if(!sel || sel->isfloating)
+ return;
+ if((c = prevtiled(sel))) {
+ /* attach before c */
+ wl_list_remove(&sel->link);
+ wl_list_insert(c->link.prev, &sel->link);
+ } else {
+ /* move to the end */
+ wl_list_remove(&sel->link);
+ wl_list_insert(clients.prev, &sel->link);
+ }
+ focusclient(sel, 1);
+ arrange(selmon);
+}
+
+static void
+pushdown(const Arg *arg) {
+ Client *sel = selclient();
+ Client *c;
+
+ if(!sel || sel->isfloating)
+ return;
+ if((c = nexttiled(sel))) {
+ /* attach after c */
+ wl_list_remove(&sel->link);
+ wl_list_insert(&c->link, &sel->link);
+ } else {
+ /* move to the front */
+ wl_list_remove(&sel->link);
+ wl_list_insert(&clients, &sel->link);
+ }
+ focusclient(sel, 1);
+ arrange(selmon);
+}
diff --git a/push.h b/push.h
new file mode 100644
index 0000000..59c0f80
--- /dev/null
+++ b/push.h
@@ -0,0 +1,4 @@
+static Client *nexttiled(Client *sel);
+static Client *prevtiled(Client *sel);
+static void pushdown(const Arg *arg);
+static void pushup(const Arg *arg);
diff --git a/update.diff b/update.diff
new file mode 100644
index 0000000..d38a529
--- /dev/null
+++ b/update.diff
@@ -0,0 +1,147 @@
+diff --git a/README.md b/README.md
+index 887bf29..6b12ab0 100644
+--- a/README.md
++++ b/README.md
+@@ -1,6 +1,6 @@
+ # dwl - dwm for Wayland
+
+-Join us on our [Discord server](https://discord.gg/jJxZnrGPWN)!
++Join us on our [Discord server](https://discord.gg/jJxZnrGPWN) and at [#dwl](https://web.libera.chat/?channels=#dwl) on irc.libera.chat.
+
+ dwl is a compact, hackable compositor for Wayland based on [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots/). It is intended to fill the same space in the Wayland world that dwm does in X11, primarily in terms of philosophy, and secondarily in terms of functionality. Like dwm, dwl is:
+
+@@ -85,10 +85,6 @@ Existing dwl-specific status bars and dwl-specific scripts for other status bars
+
+ You can find a [list of Wayland applications on the sway wiki](https://github.com/swaywm/sway/wiki/i3-Migration-Guide).
+
+-## IRC channel
+-
+-dwl's IRC channel is #dwl on irc.libera.chat.
+-
+ ## Acknowledgements
+
+ dwl began by extending the TinyWL example provided (CC0) by the sway/wlroots developers. This was made possible in many cases by looking at how sway accomplished something, then trying to do the same in as suckless a way as possible.
+diff --git a/config.h b/config.h
+index bc08e2e..7745e7d 100644
+--- a/config.h
++++ b/config.h
+@@ -49,8 +49,7 @@ static const MonitorRule monrules[] = {
+ { "eDP-1", 0.5, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL },
+ */
+ /* defaults */
+- { "HDMI-A-1", 0.55, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL },
+- { "HDMI-A-2", 0.55, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL },
++ { NULL, 0.55, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL },
+ };
+
+ /* keyboard */
+diff --git a/dwl.c b/dwl.c
+index 227e1c4..2533ef7 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -315,7 +315,7 @@ static void swallow(Client *c, Client *w);
+ /* variables */
+ static const char broken[] = "broken";
+ static pid_t child_pid = -1;
+-static struct wlr_surface *exclusive_focus;
++static void *exclusive_focus;
+ static struct wl_display *dpy;
+ static struct wlr_backend *backend;
+ static struct wlr_scene *scene;
+@@ -574,7 +574,7 @@ arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, int
+ const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
+ | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
+
+- if (exclusive != (state->exclusive_zone > 0))
++ if (wlr_layer_surface->mapped && exclusive != (state->exclusive_zone > 0))
+ continue;
+
+ bounds = state->exclusive_zone == -1 ? full_area : *usable_area;
+@@ -643,6 +643,8 @@ arrangelayers(Monitor *m)
+ ZWLR_LAYER_SHELL_V1_LAYER_TOP,
+ };
+ LayerSurface *layersurface;
++ if (!m || !m->wlr_output->enabled)
++ return;
+
+ /* Arrange exclusive surfaces from top->bottom */
+ for (i = 3; i >= 0; i--)
+@@ -665,8 +667,8 @@ arrangelayers(Monitor *m)
+ layersurface->layer_surface->mapped) {
+ /* Deactivate the focused client. */
+ focusclient(NULL, 0);
+- exclusive_focus = layersurface->layer_surface->surface;
+- client_notify_enter(exclusive_focus, wlr_seat_get_keyboard(seat));
++ exclusive_focus = layersurface;
++ client_notify_enter(layersurface->layer_surface->surface, wlr_seat_get_keyboard(seat));
+ return;
+ }
+ }
+@@ -801,6 +803,7 @@ cleanupmon(struct wl_listener *listener, void *data)
+ wl_list_remove(&m->destroy.link);
+ wl_list_remove(&m->frame.link);
+ wl_list_remove(&m->link);
++ wlr_output->data = NULL;
+ wlr_output_layout_remove(output_layout, m->wlr_output);
+ wlr_scene_output_destroy(m->scene_output);
+
+@@ -835,8 +838,9 @@ commitlayersurfacenotify(struct wl_listener *listener, void *data)
+ {
+ LayerSurface *layersurface = wl_container_of(listener, layersurface, surface_commit);
+ struct wlr_layer_surface_v1 *wlr_layer_surface = layersurface->layer_surface;
++ struct wlr_output *wlr_output = wlr_layer_surface->output;
+
+- if (!layersurface->mon)
++ if (!wlr_output || !(layersurface->mon = wlr_output->data))
+ return;
+
+ if (layers[wlr_layer_surface->current.layer] != layersurface->scene) {
+@@ -1127,8 +1131,6 @@ destroylayersurfacenotify(struct wl_listener *listener, void *data)
+ wl_list_remove(&layersurface->unmap.link);
+ wl_list_remove(&layersurface->surface_commit.link);
+ wlr_scene_node_destroy(layersurface->scene);
+- if (layersurface->mon)
+- arrangelayers(layersurface->mon);
+ free(layersurface);
+ }
+
+@@ -1551,9 +1553,6 @@ mapnotify(struct wl_listener *listener, void *data)
+ }
+ printstatus();
+
+- if (c->isfullscreen)
+- setfullscreen(c, 1);
+-
+ c->mon->un_map = 1;
+ if (!c->noswallow) {
+ Client *p = termforwin(c);
+@@ -2029,6 +2028,8 @@ void
+ setfullscreen(Client *c, int fullscreen)
+ {
+ c->isfullscreen = fullscreen;
++ if (!c->mon)
++ return;
+ c->bw = fullscreen ? 0 : borderpx;
+ client_set_fullscreen(c, fullscreen);
+
+@@ -2114,7 +2115,7 @@ setmon(Client *c, Monitor *m, unsigned int newtags)
+ resize(c, c->geom, 0, 1);
+ wlr_surface_send_enter(client_surface(c), m->wlr_output);
+ c->tags = newtags ? newtags : m->tagset[m->seltags]; /* assign tags of target monitor */
+- arrange(m);
++ setfullscreen(c, c->isfullscreen); /* This will call arrange(c->mon) */
+ }
+ focusclient(focustop(selmon), 1);
+ }
+@@ -2512,7 +2513,10 @@ unmaplayersurfacenotify(struct wl_listener *listener, void *data)
+
+ layersurface->layer_surface->mapped = (layersurface->mapped = 0);
+ wlr_scene_node_set_enabled(layersurface->scene, 0);
+- if (layersurface->layer_surface->surface == exclusive_focus)
++ if (layersurface->layer_surface->output
++ && (layersurface->mon = layersurface->layer_surface->output->data))
++ arrangelayers(layersurface->mon);
++ if (layersurface == exclusive_focus)
+ exclusive_focus = NULL;
+ if (layersurface->layer_surface->surface ==
+ seat->keyboard_state.focused_surface)