diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | config.def.h | 48 | ||||
-rw-r--r-- | config.h | 198 | ||||
-rw-r--r-- | config.mk | 6 | ||||
-rw-r--r-- | downdate.diff | 67 | ||||
-rw-r--r-- | downdate2.diff | 78 | ||||
-rw-r--r-- | dwl.c | 277 | ||||
-rw-r--r-- | patches/dwl-alwayscenter.diff | 23 | ||||
-rw-r--r-- | patches/dwl-autostart.diff | 132 | ||||
-rw-r--r-- | patches/dwl-cursorwarp.diff | 145 | ||||
-rw-r--r-- | patches/dwl-namedscratchpads.diff | 176 | ||||
-rw-r--r-- | patches/dwl-push.diff | 129 | ||||
-rw-r--r-- | patches/dwl-smartborders.diff | 164 | ||||
-rw-r--r-- | patches/dwl-swallow.diff | 194 | ||||
-rw-r--r-- | push.c | 63 | ||||
-rw-r--r-- | push.h | 4 | ||||
-rw-r--r-- | update.diff | 147 |
18 files changed, 1814 insertions, 39 deletions
@@ -3,4 +3,3 @@ dwl *-protocol.c *-protocol.h .ccls-cache -config.h @@ -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} }, +}; @@ -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); + } @@ -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; @@ -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); +} @@ -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) |