From 537ad7e3fdc23602254ba00ba869eab891be7966 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sat, 12 Sep 2020 00:13:05 +0200 Subject: [PATCH 01/78] Restore floating win position after mon add Compensate the coordinate changes when adding a new monitor. Every test so far confirms that monitors are always added to the left, on top of the list, so every floating window's x coordinate has to be incremented by the width of the new monitor. --- dwl.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/dwl.c b/dwl.c index 414aa59..23a7211 100644 --- a/dwl.c +++ b/dwl.c @@ -195,6 +195,7 @@ static void applyexclusive(struct wlr_box *usable_area, uint32_t anchor, int32_t margin_bottom, int32_t margin_left); static void applyrules(Client *c); static void arrange(Monitor *m); +static void arrangefloat(Monitor *m, int sign); static void arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, bool exclusive); static void arrangelayers(Monitor *m); @@ -469,6 +470,17 @@ arrange(Monitor *m) /* XXX recheck pointer focus here... or in resize()? */ } +void +arrangefloat(Monitor *m, int sign) +{ + Client *c; + wl_list_for_each(c, &clients, link) { + if (c->isfloating) + resize(c, c->geom.x + m->w.width * sign, c->geom.y, + c->geom.width, c->geom.height, 0); + } +} + void arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, bool exclusive) { @@ -693,6 +705,7 @@ cleanupmon(struct wl_listener *listener, void *data) free(m); updatemons(); + arrangefloat(m, -1); } void @@ -821,6 +834,11 @@ createmon(struct wl_listener *listener, void *data) /* When adding monitors, the geometries of all monitors must be updated */ updatemons(); + wl_list_for_each(m, &mons, link) { + /* the first monitor in the list is the most recently added */ + arrangefloat(m, 1); + return; + } } void From bece225934216f1d047ebecc5b574ed1b15886f2 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Mon, 14 Sep 2020 17:40:14 +0200 Subject: [PATCH 02/78] Handle monitor unplug Floating widndows with "x < removed monitor's width" aren't resized (they used to disappear in negative coordinates). Actually delete monitors when they are unplugged, recalculate sgeom and give a new monitor to clients that were on the removed one with setmon() arrangefloat() funcion has been exploded to save iterations in cleanupmon(). Also if a monitor that supports auto suspension is turned off, dwl will count it as unplugged (it will become unreachable and all clients will be moved to the leftmost monitor). However, if at least one monitor isn't plugged in, dwl will still crash the same as before. Unlike sway, when the output configuration is changed and restored, (unplug + plug the same monitor for example) previous application positions aren't kept. This is due to the fact that on sway every workspace is unique among all monitors. --- dwl.c | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/dwl.c b/dwl.c index 23a7211..6f2e800 100644 --- a/dwl.c +++ b/dwl.c @@ -195,7 +195,6 @@ static void applyexclusive(struct wlr_box *usable_area, uint32_t anchor, int32_t margin_bottom, int32_t margin_left); static void applyrules(Client *c); static void arrange(Monitor *m); -static void arrangefloat(Monitor *m, int sign); static void arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, bool exclusive); static void arrangelayers(Monitor *m); @@ -470,17 +469,6 @@ arrange(Monitor *m) /* XXX recheck pointer focus here... or in resize()? */ } -void -arrangefloat(Monitor *m, int sign) -{ - Client *c; - wl_list_for_each(c, &clients, link) { - if (c->isfloating) - resize(c, c->geom.x + m->w.width * sign, c->geom.y, - c->geom.width, c->geom.height, 0); - } -} - void arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, bool exclusive) { @@ -699,13 +687,29 @@ void cleanupmon(struct wl_listener *listener, void *data) { struct wlr_output *wlr_output = data; - Monitor *m = wlr_output->data; + Monitor *m = wlr_output->data, *newmon; + Client *c; wl_list_remove(&m->destroy.link); - free(m); + wl_list_remove(&m->frame.link); + wl_list_remove(&m->link); + wlr_output_layout_remove(output_layout, m->wlr_output); + sgeom = *wlr_output_layout_get_box(output_layout, NULL); updatemons(); - arrangefloat(m, -1); + + wl_list_for_each(newmon, &mons, link) { + wl_list_for_each(c, &clients, link) { + if (c->isfloating && c->geom.x > m->m.width) { + resize(c, c->geom.x - m->w.width, c->geom.y, + c->geom.width, c->geom.height, 0); + } + if (c->mon == m) + setmon(c, newmon, 0); + } + break; + } + free(m); } void @@ -836,7 +840,12 @@ createmon(struct wl_listener *listener, void *data) updatemons(); wl_list_for_each(m, &mons, link) { /* the first monitor in the list is the most recently added */ - arrangefloat(m, 1); + Client *c; + wl_list_for_each(c, &clients, link) { + if (c->isfloating) + resize(c, c->geom.x + m->w.width, c->geom.y, + c->geom.width, c->geom.height, 0); + } return; } } From d8f752c9b46f8ef2286cddd45628b6db576b8ddf Mon Sep 17 00:00:00 2001 From: Stivvo Date: Tue, 15 Sep 2020 12:11:40 +0200 Subject: [PATCH 03/78] Keep client tags on unplug When unplugging a monitor, each client is moved to the same tag number as before on the new monitor --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 6f2e800..bdd89fc 100644 --- a/dwl.c +++ b/dwl.c @@ -705,7 +705,7 @@ cleanupmon(struct wl_listener *listener, void *data) c->geom.width, c->geom.height, 0); } if (c->mon == m) - setmon(c, newmon, 0); + setmon(c, newmon, c->tags); } break; } From b30e18fa204bab2a993ce4f0250728f01accc04f Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 17 Oct 2020 20:11:31 +0200 Subject: [PATCH 04/78] Implement the output management protocol It allows clients such as wlr-randr to configure the display. --- dwl.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/dwl.c b/dwl.c index 414aa59..23b627d 100644 --- a/dwl.c +++ b/dwl.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -235,6 +236,9 @@ static void motionabsolute(struct wl_listener *listener, void *data); static void motionnotify(uint32_t time); static void motionrelative(struct wl_listener *listener, void *data); static void moveresize(const Arg *arg); +static void outputmgrapply(struct wl_listener *listener, void *data); +static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test); +static void outputmgrtest(struct wl_listener *listener, void *data); static void pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, uint32_t time); static void quit(const Arg *arg); @@ -288,6 +292,7 @@ static struct wl_list stack; /* stacking z-order */ static struct wl_list independents; static struct wlr_layer_shell_v1 *layer_shell; static struct wlr_xdg_decoration_manager_v1 *xdeco_mgr; +static struct wlr_output_manager_v1 *output_mgr; static struct wlr_cursor *cursor; static struct wlr_xcursor_manager *cursor_mgr; @@ -314,6 +319,8 @@ static struct wl_listener new_output = {.notify = createmon}; static struct wl_listener new_xdeco = {.notify = createxdeco}; static struct wl_listener new_xdg_surface = {.notify = createnotify}; static struct wl_listener new_layer_shell_surface = {.notify = createlayersurface}; +static struct wl_listener output_mgr_apply = {.notify = outputmgrapply}; +static struct wl_listener output_mgr_test = {.notify = outputmgrtest}; static struct wl_listener request_cursor = {.notify = setcursor}; static struct wl_listener request_set_psel = {.notify = setpsel}; static struct wl_listener request_set_sel = {.notify = setsel}; @@ -1406,6 +1413,63 @@ moveresize(const Arg *arg) } } +void +outputmgrapply(struct wl_listener *listener, void *data) +{ + struct wlr_output_configuration_v1 *config = data; + outputmgrapplyortest(config, false); +} + +// apply_output_config +void +outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test) +{ + struct wlr_output_configuration_head_v1 *config_head; + bool ok = true; + + wl_list_for_each(config_head, &config->heads, link) { + struct wlr_output *wlr_output = config_head->state.output; + + wlr_output_enable(wlr_output, config_head->state.enabled); + + if (config_head->state.enabled) { + if (config_head->state.mode) + wlr_output_set_mode(wlr_output, config_head->state.mode); + else + wlr_output_set_custom_mode(wlr_output, + config_head->state.custom_mode.width, + config_head->state.custom_mode.height, + config_head->state.custom_mode.refresh); + + wlr_output_layout_move(output_layout, wlr_output, + config_head->state.x, config_head->state.y); + wlr_output_set_transform(wlr_output, config_head->state.transform); + wlr_output_set_scale(wlr_output, config_head->state.scale); + } + + if (test) { + ok &= wlr_output_test(wlr_output); + wlr_output_rollback(wlr_output); + } else + ok &= wlr_output_commit(wlr_output); + } + if (ok) { + wlr_output_configuration_v1_send_succeeded(config); + if (!test) + updatemons(); + } + else + wlr_output_configuration_v1_send_failed(config); + wlr_output_configuration_v1_destroy(config); +} + +void +outputmgrtest(struct wl_listener *listener, void *data) +{ + struct wlr_output_configuration_v1 *config = data; + outputmgrapplyortest(config, true); +} + void pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, uint32_t time) @@ -1946,6 +2010,10 @@ setup(void) wl_signal_add(&seat->events.request_set_primary_selection, &request_set_psel); + output_mgr = wlr_output_manager_v1_create(dpy); + wl_signal_add(&output_mgr->events.apply, &output_mgr_apply); + wl_signal_add(&output_mgr->events.test, &output_mgr_test); + #ifdef XWAYLAND /* * Initialise the XWayland X server. @@ -2124,15 +2192,28 @@ unmapnotify(struct wl_listener *listener, void *data) void updatemons() { + struct wlr_output_configuration_v1 *config = + wlr_output_configuration_v1_create(); Monitor *m; + wl_list_for_each(m, &mons, link) { + struct wlr_output_configuration_head_v1 *config_head = + wlr_output_configuration_head_v1_create(config, m->wlr_output); + /* Get the effective monitor geometry to use for surfaces */ m->m = m->w = *wlr_output_layout_get_box(output_layout, m->wlr_output); /* Calculate the effective monitor geometry to use for clients */ arrangelayers(m); /* Don't move clients to the left output when plugging monitors */ arrange(m); + + config_head->state.enabled = m->wlr_output->enabled; + config_head->state.mode = m->wlr_output->current_mode; + config_head->state.x = m->m.x; + config_head->state.y = m->m.y; } + + wlr_output_manager_v1_set_configuration(output_mgr, config); } void From f21d3796b842ea944fb05049cd0d6fa2614dce64 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 17 Oct 2020 20:15:59 +0200 Subject: [PATCH 05/78] Move sgeom assignment There is no need to repeat this. This needs to be reculalculated in my output-management implementation too, and since I'm already calling updatemons, this patch avoids having to repeat the assignment again. --- dwl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index bdd89fc..5d8a75c 100644 --- a/dwl.c +++ b/dwl.c @@ -695,7 +695,6 @@ cleanupmon(struct wl_listener *listener, void *data) wl_list_remove(&m->link); wlr_output_layout_remove(output_layout, m->wlr_output); - sgeom = *wlr_output_layout_get_box(output_layout, NULL); updatemons(); wl_list_for_each(newmon, &mons, link) { @@ -831,7 +830,6 @@ createmon(struct wl_listener *listener, void *data) * output (such as DPI, scale factor, manufacturer, etc). */ wlr_output_layout_add_auto(output_layout, wlr_output); - sgeom = *wlr_output_layout_get_box(output_layout, NULL); for (size_t i = 0; i < nlayers; ++i) wl_list_init(&m->layers[i]); @@ -2151,6 +2149,7 @@ unmapnotify(struct wl_listener *listener, void *data) void updatemons() { + sgeom = *wlr_output_layout_get_box(output_layout, NULL); Monitor *m; wl_list_for_each(m, &mons, link) { /* Get the effective monitor geometry to use for surfaces */ From 7017a0c64d3a58635cb5e088fab890c4efa02737 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sun, 18 Oct 2020 18:37:55 +0200 Subject: [PATCH 06/78] fix compile error mixed declaration --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 5d8a75c..48a4fbe 100644 --- a/dwl.c +++ b/dwl.c @@ -2149,8 +2149,8 @@ unmapnotify(struct wl_listener *listener, void *data) void updatemons() { - sgeom = *wlr_output_layout_get_box(output_layout, NULL); Monitor *m; + sgeom = *wlr_output_layout_get_box(output_layout, NULL); wl_list_for_each(m, &mons, link) { /* Get the effective monitor geometry to use for surfaces */ m->m = m->w = *wlr_output_layout_get_box(output_layout, m->wlr_output); From ef7043e4d1445142ab18bf7de886f1963fbade8b Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sat, 24 Oct 2020 23:40:19 +0200 Subject: [PATCH 07/78] closemon() Separate oputput movement from cleanupmon --- dwl.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/dwl.c b/dwl.c index 48a4fbe..9e8c527 100644 --- a/dwl.c +++ b/dwl.c @@ -204,6 +204,7 @@ static void chvt(const Arg *arg); static void cleanup(void); static void cleanupkeyboard(struct wl_listener *listener, void *data); static void cleanupmon(struct wl_listener *listener, void *data); +static void closemon(Monitor *m); static void commitlayersurfacenotify(struct wl_listener *listener, void *data); static void commitnotify(struct wl_listener *listener, void *data); static void createkeyboard(struct wlr_input_device *device); @@ -687,8 +688,7 @@ void cleanupmon(struct wl_listener *listener, void *data) { struct wlr_output *wlr_output = data; - Monitor *m = wlr_output->data, *newmon; - Client *c; + Monitor *m = wlr_output->data; wl_list_remove(&m->destroy.link); wl_list_remove(&m->frame.link); @@ -696,6 +696,16 @@ cleanupmon(struct wl_listener *listener, void *data) wlr_output_layout_remove(output_layout, m->wlr_output); updatemons(); + closemon(m); + free(m); +} + +void +closemon(Monitor *m) +{ + // move all the clients on a closed monitor to another one + Monitor *newmon; + Client *c; wl_list_for_each(newmon, &mons, link) { wl_list_for_each(c, &clients, link) { @@ -708,7 +718,6 @@ cleanupmon(struct wl_listener *listener, void *data) } break; } - free(m); } void From 4c0d59c1a724420bfd6d602c65d7ac6ab05e6eaf Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sun, 25 Oct 2020 11:16:37 +0100 Subject: [PATCH 08/78] Move clients away from a disabled monitor When a monitor is disabled with wlr_randr, all clients on that monitor aren't lost but they are moved to the leftmost monitor with the same method that handles monitor hot unplug --- dwl.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index dda407a..e168f58 100644 --- a/dwl.c +++ b/dwl.c @@ -1465,6 +1465,15 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test) struct wlr_output *wlr_output = config_head->state.output; wlr_output_enable(wlr_output, config_head->state.enabled); + if (!config_head->state.enabled) { + Monitor *m; + wl_list_for_each(m, &mons, link) { + if (m->wlr_output == wlr_output) { + closemon(m); + break; + } + } + } if (config_head->state.enabled) { if (config_head->state.mode) @@ -1491,8 +1500,7 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test) wlr_output_configuration_v1_send_succeeded(config); if (!test) updatemons(); - } - else + } else wlr_output_configuration_v1_send_failed(config); wlr_output_configuration_v1_destroy(config); } From 3c3714aac05643ce59692f2c2442db3def993b4d Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sun, 25 Oct 2020 11:56:21 +0100 Subject: [PATCH 09/78] Handle monitor enable --- dwl.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index e168f58..56801a0 100644 --- a/dwl.c +++ b/dwl.c @@ -1463,10 +1463,17 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test) wl_list_for_each(config_head, &config->heads, link) { struct wlr_output *wlr_output = config_head->state.output; + Monitor *m; wlr_output_enable(wlr_output, config_head->state.enabled); - if (!config_head->state.enabled) { - Monitor *m; + if (config_head->state.enabled) { + wl_list_for_each(m, &mons, link) { + if (m->wlr_output == wlr_output) { + wlr_output_set_mode(m->wlr_output, wlr_output_preferred_mode(m->wlr_output)); + break; + } + } + } else { wl_list_for_each(m, &mons, link) { if (m->wlr_output == wlr_output) { closemon(m); From 42aea0b17d6e221c8932b3c3ce63329c4b6e560d Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sun, 25 Oct 2020 11:56:21 +0100 Subject: [PATCH 10/78] Handle monitor enable --- dwl.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index e168f58..9fcd507 100644 --- a/dwl.c +++ b/dwl.c @@ -1463,10 +1463,17 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test) wl_list_for_each(config_head, &config->heads, link) { struct wlr_output *wlr_output = config_head->state.output; + Monitor *m; wlr_output_enable(wlr_output, config_head->state.enabled); - if (!config_head->state.enabled) { - Monitor *m; + if (!wlr_output->enabled) { + wl_list_for_each(m, &mons, link) { + if (m->wlr_output == wlr_output) { + wlr_output_set_mode(m->wlr_output, wlr_output_preferred_mode(m->wlr_output)); + break; + } + } + } else { wl_list_for_each(m, &mons, link) { if (m->wlr_output == wlr_output) { closemon(m); From 7d67b77a96260aa07d47a6d7ef414be1daa414b4 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Fri, 30 Oct 2020 16:21:00 +0100 Subject: [PATCH 11/78] Cleaner if statement --- dwl.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dwl.c b/dwl.c index 9e8c527..8035cc0 100644 --- a/dwl.c +++ b/dwl.c @@ -709,10 +709,9 @@ closemon(Monitor *m) wl_list_for_each(newmon, &mons, link) { wl_list_for_each(c, &clients, link) { - if (c->isfloating && c->geom.x > m->m.width) { + if (c->isfloating && c->geom.x > m->m.width) resize(c, c->geom.x - m->w.width, c->geom.y, - c->geom.width, c->geom.height, 0); - } + c->geom.width, c->geom.height, 0); if (c->mon == m) setmon(c, newmon, c->tags); } From 618972696de9499fa38b613a47799cf46b2b0135 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Fri, 30 Oct 2020 19:52:20 +0100 Subject: [PATCH 12/78] Fix crash when unplugging a focused monitor Just focus a "safe" monitor before trying to to anything risky --- dwl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dwl.c b/dwl.c index 8035cc0..461dd7b 100644 --- a/dwl.c +++ b/dwl.c @@ -707,6 +707,7 @@ closemon(Monitor *m) Monitor *newmon; Client *c; + focusclient(selclient(), focustop(dirtomon(-1)), 1); wl_list_for_each(newmon, &mons, link) { wl_list_for_each(c, &clients, link) { if (c->isfloating && c->geom.x > m->m.width) From 4deeddceffde88bd117411fb856e86a7d92e456a Mon Sep 17 00:00:00 2001 From: Stivvo Date: Fri, 30 Oct 2020 22:32:00 +0100 Subject: [PATCH 13/78] Actually move clients away from a disabled mon When using wlr-randr every monitor's configuration is reevaluated, so it must check which monitors are actually being disabled. The only way to correctly do that is to compare the names. --- dwl.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/dwl.c b/dwl.c index b70a217..715cb5e 100644 --- a/dwl.c +++ b/dwl.c @@ -1466,21 +1466,10 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test) Monitor *m; wlr_output_enable(wlr_output, config_head->state.enabled); - if (!wlr_output->enabled) { - wl_list_for_each(m, &mons, link) { - if (m->wlr_output == wlr_output) { - wlr_output_set_mode(m->wlr_output, wlr_output_preferred_mode(m->wlr_output)); - break; - } - } - } else { - wl_list_for_each(m, &mons, link) { - if (m->wlr_output == wlr_output) { + if (!config_head->state.enabled) + wl_list_for_each(m, &mons, link) + if (m->wlr_output->name == wlr_output->name) closemon(m); - break; - } - } - } if (config_head->state.enabled) { if (config_head->state.mode) From d4178b9d2a89c24b99e2b26aec8ac15ab5dcdd84 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Fri, 30 Oct 2020 23:22:55 +0100 Subject: [PATCH 14/78] Closemon(), newmon as parameter This allows to fix output-management: move clients to the monitor on the left of the disabled one, instead of the leftmost (which might happen to be the disabled one) Also using wl_list_foreach() and then brake after the first iteration is ugly and inefficient --- dwl.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/dwl.c b/dwl.c index 9e8c527..f688bf6 100644 --- a/dwl.c +++ b/dwl.c @@ -204,7 +204,7 @@ static void chvt(const Arg *arg); static void cleanup(void); static void cleanupkeyboard(struct wl_listener *listener, void *data); static void cleanupmon(struct wl_listener *listener, void *data); -static void closemon(Monitor *m); +static void closemon(Monitor *m, Monitor *newmon); static void commitlayersurfacenotify(struct wl_listener *listener, void *data); static void commitnotify(struct wl_listener *listener, void *data); static void createkeyboard(struct wlr_input_device *device); @@ -688,7 +688,7 @@ void cleanupmon(struct wl_listener *listener, void *data) { struct wlr_output *wlr_output = data; - Monitor *m = wlr_output->data; + Monitor *m = wlr_output->data, *newmon; wl_list_remove(&m->destroy.link); wl_list_remove(&m->frame.link); @@ -696,27 +696,22 @@ cleanupmon(struct wl_listener *listener, void *data) wlr_output_layout_remove(output_layout, m->wlr_output); updatemons(); - closemon(m); + closemon(m, wl_container_of(mons.next, newmon, link)); free(m); } void -closemon(Monitor *m) +closemon(Monitor *m, Monitor *newmon) { // move all the clients on a closed monitor to another one - Monitor *newmon; Client *c; - wl_list_for_each(newmon, &mons, link) { - wl_list_for_each(c, &clients, link) { - if (c->isfloating && c->geom.x > m->m.width) { - resize(c, c->geom.x - m->w.width, c->geom.y, - c->geom.width, c->geom.height, 0); - } - if (c->mon == m) - setmon(c, newmon, c->tags); - } - break; + wl_list_for_each(c, &clients, link) { + if (c->isfloating && c->geom.x > m->m.width) + resize(c, c->geom.x - m->w.width, c->geom.y, + c->geom.width, c->geom.height, 0); + if (c->mon == m) + setmon(c, newmon, c->tags); } } From 388ab9df2f655e7bd228b98ccf48ab612c0ffe4e Mon Sep 17 00:00:00 2001 From: Stivvo Date: Fri, 30 Oct 2020 23:33:10 +0100 Subject: [PATCH 15/78] Move disabled clients to the left To the nearest monitor to the left of the disabled one --- dwl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index 182c097..0432b42 100644 --- a/dwl.c +++ b/dwl.c @@ -1459,13 +1459,13 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test) wl_list_for_each(config_head, &config->heads, link) { struct wlr_output *wlr_output = config_head->state.output; - Monitor *m; + Monitor *m, *newmon; wlr_output_enable(wlr_output, config_head->state.enabled); if (!config_head->state.enabled) wl_list_for_each(m, &mons, link) if (m->wlr_output->name == wlr_output->name) - closemon(m); + closemon(m, wl_container_of(m->link.next, newmon, link)); if (config_head->state.enabled) { if (config_head->state.mode) From 62fb4c086ea5080977edc4ec017390aa924607ed Mon Sep 17 00:00:00 2001 From: Stivvo Date: Fri, 30 Oct 2020 23:49:01 +0100 Subject: [PATCH 16/78] Block access to disabled monitor Before this, pressing mod+comma or mod+period (focusmon function) moved the focus to disabed monitors. Now, all disabled monitors are skipped --- dwl.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/dwl.c b/dwl.c index 0432b42..6bbf8b9 100644 --- a/dwl.c +++ b/dwl.c @@ -1087,10 +1087,12 @@ focusclient(Client *old, Client *c, int lift) void focusmon(const Arg *arg) { - Client *sel = selclient(); - - selmon = dirtomon(arg->i); - focusclient(sel, focustop(selmon), 1); + Client *sel; + do { + sel = selclient(); + selmon = dirtomon(arg->i); + focusclient(sel, focustop(selmon), 1); + } while (!selmon->wlr_output->enabled); } void From fab42e7c41e8379168c49a7f427cdd3ffddd6e34 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sat, 31 Oct 2020 10:39:53 +0100 Subject: [PATCH 17/78] Fix crash unplugging a focused mon Focus newmon, which we know for sure that it isn't be unplugged or disabled --- dwl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 8852f8f..69deeb5 100644 --- a/dwl.c +++ b/dwl.c @@ -706,7 +706,8 @@ closemon(Monitor *m, Monitor *newmon) // move all the clients on a closed monitor to another one Client *c; - focusclient(selclient(), focustop(dirtomon(-1)), 1); + selmon = newmon; + focusclient(selclient(), focustop(newmon), 1); wl_list_for_each(c, &clients, link) { if (c->isfloating && c->geom.x > m->m.width) resize(c, c->geom.x - m->w.width, c->geom.y, From 5622dbdaf3eaa62183d81e9b545a92abcc1a704d Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sat, 31 Oct 2020 10:39:53 +0100 Subject: [PATCH 18/78] Fix crash unplugging focused mon 2 Focus the top client on newmon, which we know for sure that it isn't going to be unplugged or disabled and actually set that as the focused monitor to move the focus. This is necessary to prevent crash when disabling monitors with the output-management patch. --- dwl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 8852f8f..69deeb5 100644 --- a/dwl.c +++ b/dwl.c @@ -706,7 +706,8 @@ closemon(Monitor *m, Monitor *newmon) // move all the clients on a closed monitor to another one Client *c; - focusclient(selclient(), focustop(dirtomon(-1)), 1); + selmon = newmon; + focusclient(selclient(), focustop(newmon), 1); wl_list_for_each(c, &clients, link) { if (c->isfloating && c->geom.x > m->m.width) resize(c, c->geom.x - m->w.width, c->geom.y, From 25671d79051e9054f64de88e77e089a2daf7008f Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sat, 31 Oct 2020 12:43:07 +0100 Subject: [PATCH 19/78] Fix crash when disabling focused mon m->link.next leads to errors if the monitor to disable doesn't have a "next" (right) monitor and is currently focused. dirtmon() does more checks. In some previous commits m->link.next is told to be left monitor, which is wrong Also focusclient() explicitly checks for disabled monitors (this fixes in case of more than one disabled monitor) --- dwl.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/dwl.c b/dwl.c index a2d5092..ab17546 100644 --- a/dwl.c +++ b/dwl.c @@ -1459,16 +1459,26 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test) { struct wlr_output_configuration_head_v1 *config_head; bool ok = true; + Arg ar = {.i = -1}; wl_list_for_each(config_head, &config->heads, link) { struct wlr_output *wlr_output = config_head->state.output; Monitor *m, *newmon; + if (!config_head->state.enabled) { + wl_list_for_each(m, &mons, link) { + if (m->wlr_output->name == wlr_output->name) { + // make sure that the monitor to clean is focused + selmon = m; + focusclient(selclient(), focustop(selmon), 1); + + // focus the left monitor (relative to the current focus) + focusmon(&ar); + closemon(m, wl_container_of(&selmon->link, newmon, link)); + } + } + } wlr_output_enable(wlr_output, config_head->state.enabled); - if (!config_head->state.enabled) - wl_list_for_each(m, &mons, link) - if (m->wlr_output->name == wlr_output->name) - closemon(m, wl_container_of(m->link.next, newmon, link)); if (config_head->state.enabled) { if (config_head->state.mode) From 9f3f15b467a1ea723cd09d9943a27cfb50aa0af8 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sat, 31 Oct 2020 13:00:54 +0100 Subject: [PATCH 20/78] Disable mon faster Since focusmon() now never focuses disabled monitors, there's no need to focus the disabled monitor first --- dwl.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/dwl.c b/dwl.c index ab17546..9b8dda2 100644 --- a/dwl.c +++ b/dwl.c @@ -1463,22 +1463,18 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test) wl_list_for_each(config_head, &config->heads, link) { struct wlr_output *wlr_output = config_head->state.output; - Monitor *m, *newmon; + Monitor *m; + wlr_output_enable(wlr_output, config_head->state.enabled); if (!config_head->state.enabled) { wl_list_for_each(m, &mons, link) { if (m->wlr_output->name == wlr_output->name) { - // make sure that the monitor to clean is focused - selmon = m; - focusclient(selclient(), focustop(selmon), 1); - // focus the left monitor (relative to the current focus) focusmon(&ar); - closemon(m, wl_container_of(&selmon->link, newmon, link)); + closemon(m, selmon); } } } - wlr_output_enable(wlr_output, config_head->state.enabled); if (config_head->state.enabled) { if (config_head->state.mode) From 9f0b16868a082175508c0c747054f324595d5b67 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sat, 31 Oct 2020 13:19:31 +0100 Subject: [PATCH 21/78] Back to closemon() with one parameter With the recent changes in output-management, the extra argument in closemon() would be needed only when unplugging the monitor, so it isn't worth it anymore. Also now is more efficient. --- dwl.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/dwl.c b/dwl.c index 69deeb5..ad481fa 100644 --- a/dwl.c +++ b/dwl.c @@ -204,7 +204,7 @@ static void chvt(const Arg *arg); static void cleanup(void); static void cleanupkeyboard(struct wl_listener *listener, void *data); static void cleanupmon(struct wl_listener *listener, void *data); -static void closemon(Monitor *m, Monitor *newmon); +static void closemon(Monitor *m); static void commitlayersurfacenotify(struct wl_listener *listener, void *data); static void commitnotify(struct wl_listener *listener, void *data); static void createkeyboard(struct wlr_input_device *device); @@ -688,7 +688,7 @@ void cleanupmon(struct wl_listener *listener, void *data) { struct wlr_output *wlr_output = data; - Monitor *m = wlr_output->data, *newmon; + Monitor *m = wlr_output->data; wl_list_remove(&m->destroy.link); wl_list_remove(&m->frame.link); @@ -696,24 +696,26 @@ cleanupmon(struct wl_listener *listener, void *data) wlr_output_layout_remove(output_layout, m->wlr_output); updatemons(); - closemon(m, wl_container_of(mons.next, newmon, link)); + + selmon = wl_container_of(mons.next, selmon, link); + focusclient(selclient(), focustop(selmon), 1); + closemon(m); + free(m); } void -closemon(Monitor *m, Monitor *newmon) +closemon(Monitor *m) { - // move all the clients on a closed monitor to another one + // move closed monitor's clients to the focused one Client *c; - selmon = newmon; - focusclient(selclient(), focustop(newmon), 1); wl_list_for_each(c, &clients, link) { if (c->isfloating && c->geom.x > m->m.width) resize(c, c->geom.x - m->w.width, c->geom.y, c->geom.width, c->geom.height, 0); if (c->mon == m) - setmon(c, newmon, c->tags); + setmon(c, selmon, c->tags); } } From 934ce085b6f4784517d4111ec5c127553a19337d Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sat, 31 Oct 2020 16:18:23 +0100 Subject: [PATCH 22/78] Don't switch to disabled mons after unplug --- dwl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index eeecb75..78fac21 100644 --- a/dwl.c +++ b/dwl.c @@ -704,7 +704,9 @@ cleanupmon(struct wl_listener *listener, void *data) updatemons(); - selmon = wl_container_of(mons.next, selmon, link); + do // don't switch to disabled mons + selmon = wl_container_of(mons.next, selmon, link); + while (!selmon->wlr_output->enabled); focusclient(selclient(), focustop(selmon), 1); closemon(m); From 5221a329e254627c186ba8e4d68f9595d4e3158a Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sat, 31 Oct 2020 16:25:46 +0100 Subject: [PATCH 23/78] closemon() has now only 1 parameter --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 78fac21..ca352c4 100644 --- a/dwl.c +++ b/dwl.c @@ -1475,7 +1475,7 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test) if (m->wlr_output->name == wlr_output->name) { // focus the left monitor (relative to the current focus) focusmon(&ar); - closemon(m, selmon); + closemon(m); } } } From 80a685ee51da69ae0a7bae11620c5f33c3cd2de2 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sat, 31 Oct 2020 17:10:06 +0100 Subject: [PATCH 24/78] Fix crash with no monitors left When there's no monitors left, prevent the while in cleanupmon() to become an infinite loop Also switch to the left monitors instead of the right --- dwl.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dwl.c b/dwl.c index ca352c4..d875eaa 100644 --- a/dwl.c +++ b/dwl.c @@ -696,20 +696,19 @@ cleanupmon(struct wl_listener *listener, void *data) { struct wlr_output *wlr_output = data; Monitor *m = wlr_output->data; + int nmons = wl_list_length(&mons), i = 0; wl_list_remove(&m->destroy.link); wl_list_remove(&m->frame.link); wl_list_remove(&m->link); wlr_output_layout_remove(output_layout, m->wlr_output); - updatemons(); do // don't switch to disabled mons - selmon = wl_container_of(mons.next, selmon, link); - while (!selmon->wlr_output->enabled); + selmon = wl_container_of(mons.prev, selmon, link); + while (!selmon->wlr_output->enabled && i++ < nmons); focusclient(selclient(), focustop(selmon), 1); closemon(m); - free(m); } From d9ab75721af57ad6b19cb167d7e762b5496a275b Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sat, 31 Oct 2020 18:29:33 +0100 Subject: [PATCH 25/78] Don't switch to another disabled monitors Since wlr_output_enable doesn't have any effect before finishing all the procedure, a little hack allows to make use of focusmon(), which must know the latest in about which output is currently disabled Also improve performance in focusmon() and cleaner code in outputmgrapplyortest() --- dwl.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/dwl.c b/dwl.c index d875eaa..11e6ad1 100644 --- a/dwl.c +++ b/dwl.c @@ -1092,11 +1092,11 @@ void focusmon(const Arg *arg) { Client *sel; - do { - sel = selclient(); + do selmon = dirtomon(arg->i); - focusclient(sel, focustop(selmon), 1); - } while (!selmon->wlr_output->enabled); + while (!selmon->wlr_output->enabled); + sel = selclient(); + focusclient(sel, focustop(selmon), 1); } void @@ -1466,19 +1466,8 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test) wl_list_for_each(config_head, &config->heads, link) { struct wlr_output *wlr_output = config_head->state.output; - Monitor *m; wlr_output_enable(wlr_output, config_head->state.enabled); - if (!config_head->state.enabled) { - wl_list_for_each(m, &mons, link) { - if (m->wlr_output->name == wlr_output->name) { - // focus the left monitor (relative to the current focus) - focusmon(&ar); - closemon(m); - } - } - } - if (config_head->state.enabled) { if (config_head->state.mode) wlr_output_set_mode(wlr_output, config_head->state.mode); @@ -1492,6 +1481,17 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test) config_head->state.x, config_head->state.y); wlr_output_set_transform(wlr_output, config_head->state.transform); wlr_output_set_scale(wlr_output, config_head->state.scale); + } else { + Monitor *m; + wl_list_for_each(m, &mons, link) { + if (m->wlr_output->name == wlr_output->name) { + // focus the left monitor (relative to the current focus) + m->wlr_output->enabled = !m->wlr_output->enabled; + focusmon(&ar); + closemon(m); + m->wlr_output->enabled = !m->wlr_output->enabled; + } + } } if (test) { From ee5bd9a643cff9cceb8a34084da8f0657dc54359 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 19 Dec 2020 12:18:00 +0100 Subject: [PATCH 26/78] fix temporarily disabling a single monitor The code in this else completely freezes my system when I run the swayidle command to replicate xset dpms force off. No idea if it works on multiple monitors, but for now avoid running when there's 1 monitor. Also remove the comment with the function name in sway. --- dwl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index 11e6ad1..679a30b 100644 --- a/dwl.c +++ b/dwl.c @@ -1456,7 +1456,6 @@ outputmgrapply(struct wl_listener *listener, void *data) outputmgrapplyortest(config, false); } -// apply_output_config void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test) { @@ -1481,7 +1480,7 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test) config_head->state.x, config_head->state.y); wlr_output_set_transform(wlr_output, config_head->state.transform); wlr_output_set_scale(wlr_output, config_head->state.scale); - } else { + } else if (wl_list_length(&mons) > 1) { Monitor *m; wl_list_for_each(m, &mons, link) { if (m->wlr_output->name == wlr_output->name) { From 64faad7cb6dfc59832ef3249b606df89c23327f8 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Thu, 10 Dec 2020 12:56:35 +0100 Subject: [PATCH 27/78] implement the wlr-data-transfer protocol It makes wl-clipboard work properly in neovim, without having to create a transparent surface that steals focus and causes flickering. It's also required for clipman. --- dwl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dwl.c b/dwl.c index 679a30b..1b92a3e 100644 --- a/dwl.c +++ b/dwl.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -1971,6 +1972,7 @@ setup(void) compositor = wlr_compositor_create(dpy, drw); wlr_export_dmabuf_manager_v1_create(dpy); wlr_screencopy_manager_v1_create(dpy); + wlr_data_control_manager_v1_create(dpy); wlr_data_device_manager_create(dpy); wlr_gamma_control_manager_v1_create(dpy); wlr_primary_selection_v1_device_manager_create(dpy); From 1024928c15cebbeb1c652a6ba4a243f1c330bac8 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 19 Dec 2020 06:57:14 +0100 Subject: [PATCH 28/78] deactivate focused client when spawning a new one Because maprequest immediately calls wl_list_insert(&fstack, &c->flink), in the following call to setmon(), the selclient() which is passed to focusclient() as the old client is actually the newly mapped client, and the real old one is never deactivated. You can see this by, for example, opening Chromium's devtools, then spawning a terminal. The background of the focused line in the devtools doesn't change from light blue to grey. We can't just remove wl_list_insert(&fstack, &c->flink) from maprequest, because calling wl_list_remove in focusclient() with a client that has not been added to the list causes a segmentation fault. Therefore we fix the focusclient call by not passing it the old client every time, but instead using the wlroots function that gets the focused surface and deactivate that, like in TinyWL. This also avoids getting the selected client and passing it to focusclient() on every call unnecessarily, and will allow removing shouldfocusclients in a future commit by checking if old is a layer surface instead. --- dwl.c | 50 ++++++++++++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/dwl.c b/dwl.c index 1b92a3e..cb5341a 100644 --- a/dwl.c +++ b/dwl.c @@ -220,7 +220,7 @@ static void destroylayersurfacenotify(struct wl_listener *listener, void *data); static void destroynotify(struct wl_listener *listener, void *data); static void destroyxdeco(struct wl_listener *listener, void *data); static Monitor *dirtomon(int dir); -static void focusclient(Client *old, Client *c, int lift); +static void focusclient(Client *c, int lift); static void focusmon(const Arg *arg); static void focusstack(const Arg *arg); static Client *focustop(Monitor *m); @@ -630,7 +630,7 @@ buttonpress(struct wl_listener *listener, void *data) case WLR_BUTTON_PRESSED:; /* Change focus if the button was _pressed_ over a client */ if ((c = xytoclient(cursor->x, cursor->y))) - focusclient(selclient(), c, 1); + focusclient(c, 1); keyboard = wlr_seat_get_keyboard(seat); mods = wlr_keyboard_get_modifiers(keyboard); @@ -708,7 +708,7 @@ cleanupmon(struct wl_listener *listener, void *data) do // don't switch to disabled mons selmon = wl_container_of(mons.prev, selmon, link); while (!selmon->wlr_output->enabled && i++ < nmons); - focusclient(selclient(), focustop(selmon), 1); + focusclient(focustop(selmon), 1); closemon(m); free(m); } @@ -1039,9 +1039,10 @@ dirtomon(int dir) } void -focusclient(Client *old, Client *c, int lift) +focusclient(Client *c, int lift) { struct wlr_keyboard *kb = wlr_seat_get_keyboard(seat); + struct wlr_surface *old = seat->keyboard_state.focused_surface; /* Raise client in stacking order if requested */ if (c && lift) { @@ -1050,17 +1051,19 @@ focusclient(Client *old, Client *c, int lift) } /* Nothing else to do? */ - if (c == old) + if (c && WLR_SURFACE(c) == old) return; /* Deactivate old client if focus is changing */ - if (c != old && old) { + if (old && (!c || WLR_SURFACE(c) != old)) { + if (wlr_surface_is_xdg_surface(old)) + wlr_xdg_toplevel_set_activated( + wlr_xdg_surface_from_wlr_surface(old), false); #ifdef XWAYLAND - if (old->type != XDGShell) - wlr_xwayland_surface_activate(old->surface.xwayland, 0); - else + else if (wlr_surface_is_xwayland_surface(old)) + wlr_xwayland_surface_activate( + wlr_xwayland_surface_from_wlr_surface(old), false); #endif - wlr_xdg_toplevel_set_activated(old->surface.xdg, 0); } /* Update wlroots' keyboard focus */ @@ -1092,12 +1095,10 @@ focusclient(Client *old, Client *c, int lift) void focusmon(const Arg *arg) { - Client *sel; do selmon = dirtomon(arg->i); while (!selmon->wlr_output->enabled); - sel = selclient(); - focusclient(sel, focustop(selmon), 1); + focusclient(focustop(selmon), 1); } void @@ -1123,7 +1124,7 @@ focusstack(const Arg *arg) } } /* If only one client is visible on selmon, then c == sel */ - focusclient(sel, c, 1); + focusclient(c, 1); } Client * @@ -1549,7 +1550,7 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, #endif if (sloppyfocus) - focusclient(selclient(), c, 0); + focusclient(c, 0); } void @@ -1893,7 +1894,6 @@ void setmon(Client *c, Monitor *m, unsigned int newtags) { Monitor *oldmon = c->mon; - Client *oldsel = selclient(); if (oldmon == m) return; @@ -1911,7 +1911,7 @@ setmon(Client *c, Monitor *m, unsigned int newtags) c->tags = newtags ? newtags : m->tagset[m->seltags]; /* assign tags of target monitor */ arrange(m); } - focusclient(oldsel, focustop(selmon), 1); + focusclient(focustop(selmon), 1); } void @@ -2119,7 +2119,7 @@ tag(const Arg *arg) Client *sel = selclient(); if (sel && arg->ui & TAGMASK) { sel->tags = arg->ui & TAGMASK; - focusclient(sel, focustop(selmon), 1); + focusclient(focustop(selmon), 1); arrange(selmon); } } @@ -2186,7 +2186,7 @@ toggletag(const Arg *arg) newtags = sel->tags ^ (arg->ui & TAGMASK); if (newtags) { sel->tags = newtags; - focusclient(sel, focustop(selmon), 1); + focusclient(focustop(selmon), 1); arrange(selmon); } } @@ -2194,12 +2194,11 @@ toggletag(const Arg *arg) void toggleview(const Arg *arg) { - Client *sel = selclient(); unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); if (newtagset) { selmon->tagset[selmon->seltags] = newtagset; - focusclient(sel, focustop(selmon), 1); + focusclient(focustop(selmon), 1); arrange(selmon); } } @@ -2210,7 +2209,7 @@ unmaplayersurface(LayerSurface *layersurface) layersurface->layer_surface->mapped = false; if (layersurface->layer_surface->surface == seat->keyboard_state.focused_surface) - focusclient(NULL, selclient(), 1); + focusclient(selclient(), 1); motionnotify(0); } @@ -2266,13 +2265,12 @@ updatemons() void view(const Arg *arg) { - Client *sel = selclient(); if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) return; selmon->seltags ^= 1; /* toggle sel tagset */ if (arg->ui & TAGMASK) selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; - focusclient(sel, focustop(selmon), 1); + focusclient(focustop(selmon), 1); arrange(selmon); } @@ -2319,7 +2317,7 @@ xytomon(double x, double y) void zoom(const Arg *arg) { - Client *c, *sel = selclient(), *oldsel = sel; + Client *c, *sel = selclient(); if (!sel || !selmon->lt[selmon->sellt]->arrange || sel->isfloating) return; @@ -2344,7 +2342,7 @@ zoom(const Arg *arg) wl_list_remove(&sel->link); wl_list_insert(&clients, &sel->link); - focusclient(oldsel, sel, 1); + focusclient(sel, 1); arrange(selmon); } From 41cc23e1cfc248eb10878fd172ca969ed27a5f44 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Fri, 28 Aug 2020 20:18:42 +0200 Subject: [PATCH 29/78] Implement the idle protocol It allows clients such as swayidle and chat applications to monitor user idle time. --- Makefile | 14 +++++++++++-- dwl.c | 12 ++++++++++++ protocols/idle.xml | 49 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 protocols/idle.xml diff --git a/Makefile b/Makefile index 3dc2336..abbbe39 100644 --- a/Makefile +++ b/Makefile @@ -33,12 +33,22 @@ wlr-layer-shell-unstable-v1-protocol.c: wlr-layer-shell-unstable-v1-protocol.o: wlr-layer-shell-unstable-v1-protocol.h +idle-protocol.h: + $(WAYLAND_SCANNER) server-header \ + protocols/idle.xml $@ + +idle-protocol.c: + $(WAYLAND_SCANNER) private-code \ + protocols/idle.xml $@ + +idle-protocol.o: idle-protocol.h + config.h: | config.def.h cp config.def.h $@ -dwl.o: config.h xdg-shell-protocol.h wlr-layer-shell-unstable-v1-protocol.h +dwl.o: config.h xdg-shell-protocol.h wlr-layer-shell-unstable-v1-protocol.h idle-protocol.h -dwl: xdg-shell-protocol.o wlr-layer-shell-unstable-v1-protocol.o +dwl: xdg-shell-protocol.o wlr-layer-shell-unstable-v1-protocol.o idle-protocol.o clean: rm -f dwl *.o *-protocol.h *-protocol.c diff --git a/dwl.c b/dwl.c index cb5341a..1df7288 100644 --- a/dwl.c +++ b/dwl.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -292,6 +293,7 @@ static struct wl_list clients; /* tiling order */ static struct wl_list fstack; /* focus order */ static struct wl_list stack; /* stacking z-order */ static struct wl_list independents; +static struct wlr_idle *idle; static struct wlr_layer_shell_v1 *layer_shell; static struct wlr_xdg_decoration_manager_v1 *xdeco_mgr; static struct wlr_output_manager_v1 *output_mgr; @@ -611,6 +613,7 @@ axisnotify(struct wl_listener *listener, void *data) /* This event is forwarded by the cursor when a pointer emits an axis event, * for example when you move the scroll wheel. */ struct wlr_event_pointer_axis *event = data; + wlr_idle_notify_activity(idle, seat); /* Notify the client with pointer focus of the axis event. */ wlr_seat_pointer_notify_axis(seat, event->time_msec, event->orientation, event->delta, @@ -626,6 +629,8 @@ buttonpress(struct wl_listener *listener, void *data) Client *c; const Button *b; + wlr_idle_notify_activity(idle, seat); + switch (event->state) { case WLR_BUTTON_PRESSED:; /* Change focus if the button was _pressed_ over a client */ @@ -1217,6 +1222,9 @@ keypress(struct wl_listener *listener, void *data) int handled = 0; uint32_t mods = wlr_keyboard_get_modifiers(kb->device->keyboard); + + wlr_idle_notify_activity(idle, seat); + /* On _press_, attempt to process a compositor keybinding. */ if (event->state == WLR_KEY_PRESSED) for (i = 0; i < nsyms; i++) @@ -1346,6 +1354,8 @@ motionnotify(uint32_t time) time = now.tv_sec * 1000 + now.tv_nsec / 1000000; } + wlr_idle_notify_activity(idle, seat); + /* Update selmon (even while dragging a window) */ if (sloppyfocus) selmon = xytomon(cursor->x, cursor->y); @@ -1999,6 +2009,8 @@ setup(void) wl_list_init(&stack); wl_list_init(&independents); + idle = wlr_idle_create(dpy); + layer_shell = wlr_layer_shell_v1_create(dpy); wl_signal_add(&layer_shell->events.new_surface, &new_layer_shell_surface); diff --git a/protocols/idle.xml b/protocols/idle.xml new file mode 100644 index 0000000..92d9989 --- /dev/null +++ b/protocols/idle.xml @@ -0,0 +1,49 @@ + + + . + ]]> + + + This interface allows to monitor user idle time on a given seat. The interface + allows to register timers which trigger after no user activity was registered + on the seat for a given interval. It notifies when user activity resumes. + + This is useful for applications wanting to perform actions when the user is not + interacting with the system, e.g. chat applications setting the user as away, power + management features to dim screen, etc.. + + + + + + + + + + + + + + + + + + + + + + From 90cc3b1e2c824db74e932dbb9733d398619a037c Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Tue, 28 Jul 2020 09:54:08 +0200 Subject: [PATCH 30/78] Allow toggling the layout before selecting a different one --- dwl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 1df7288..497cfe5 100644 --- a/dwl.c +++ b/dwl.c @@ -825,7 +825,8 @@ createmon(struct wl_listener *listener, void *data) m->nmaster = r->nmaster; wlr_output_set_scale(wlr_output, r->scale); wlr_xcursor_manager_load(cursor_mgr, r->scale); - m->lt[0] = m->lt[1] = r->lt; + m->lt[0] = r->lt; + m->lt[1] = &layouts[LENGTH(layouts) > 1 && r->lt != &layouts[1]]; wlr_output_set_transform(wlr_output, r->rr); break; } From 0016a209e4ecb23159a887d6baf26063287a40a6 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Tue, 8 Dec 2020 12:17:14 +0100 Subject: [PATCH 31/78] implement the virtual keyboard protocol This is used by wtype. Also properly cleanup keyboards. Without wl_list_remove(&kb->link) dwl crashed after using wtype 2-3 times. --- dwl.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/dwl.c b/dwl.c index 497cfe5..d883d82 100644 --- a/dwl.c +++ b/dwl.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -275,6 +276,7 @@ static void unmaplayersurfacenotify(struct wl_listener *listener, void *data); static void unmapnotify(struct wl_listener *listener, void *data); static void updatemons(); static void view(const Arg *arg); +static void virtualkeyboard(struct wl_listener *listener, void *data); static Client *xytoclient(double x, double y); static struct wlr_surface *xytolayersurface(struct wl_list *layer_surfaces, double x, double y, double *sx, double *sy); @@ -297,6 +299,7 @@ static struct wlr_idle *idle; static struct wlr_layer_shell_v1 *layer_shell; static struct wlr_xdg_decoration_manager_v1 *xdeco_mgr; static struct wlr_output_manager_v1 *output_mgr; +static struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard_mgr; static struct wlr_cursor *cursor; static struct wlr_xcursor_manager *cursor_mgr; @@ -319,6 +322,7 @@ static struct wl_listener cursor_frame = {.notify = cursorframe}; static struct wl_listener cursor_motion = {.notify = motionrelative}; static struct wl_listener cursor_motion_absolute = {.notify = motionabsolute}; static struct wl_listener new_input = {.notify = inputdevice}; +static struct wl_listener new_virtual_keyboard = {.notify = virtualkeyboard}; static struct wl_listener new_output = {.notify = createmon}; static struct wl_listener new_xdeco = {.notify = createxdeco}; static struct wl_listener new_xdg_surface = {.notify = createnotify}; @@ -693,6 +697,9 @@ cleanupkeyboard(struct wl_listener *listener, void *data) struct wlr_input_device *device = data; Keyboard *kb = device->data; + wl_list_remove(&kb->link); + wl_list_remove(&kb->modifiers.link); + wl_list_remove(&kb->key.link); wl_list_remove(&kb->destroy.link); free(kb); } @@ -2062,6 +2069,9 @@ setup(void) */ wl_list_init(&keyboards); wl_signal_add(&backend->events.new_input, &new_input); + virtual_keyboard_mgr = wlr_virtual_keyboard_manager_v1_create(dpy); + wl_signal_add(&virtual_keyboard_mgr->events.new_virtual_keyboard, + &new_virtual_keyboard); seat = wlr_seat_create(dpy, "seat0"); wl_signal_add(&seat->events.request_set_cursor, &request_cursor); @@ -2287,6 +2297,14 @@ view(const Arg *arg) arrange(selmon); } +void +virtualkeyboard(struct wl_listener *listener, void *data) +{ + struct wlr_virtual_keyboard_v1 *keyboard = data; + struct wlr_input_device *device = &keyboard->input_device; + createkeyboard(device); +} + Client * xytoclient(double x, double y) { From 0f48c9552eaec29e1aac0165bc0e472b05e17ecd Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Tue, 8 Dec 2020 15:10:41 +0100 Subject: [PATCH 32/78] handle the x11 configure event This fixes the window size of old games in Wine. --- dwl.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/dwl.c b/dwl.c index d883d82..924823b 100644 --- a/dwl.c +++ b/dwl.c @@ -96,6 +96,7 @@ typedef struct { } surface; #ifdef XWAYLAND struct wl_listener activate; + struct wl_listener configure; #endif struct wl_listener commit; struct wl_listener map; @@ -335,6 +336,7 @@ static struct wl_listener request_set_sel = {.notify = setsel}; #ifdef XWAYLAND static void activatex11(struct wl_listener *listener, void *data); +static void configurex11(struct wl_listener *listener, void *data); static void createnotifyx11(struct wl_listener *listener, void *data); static Atom getatom(xcb_connection_t *xc, const char *name); static void renderindependents(struct wlr_output *output, struct timespec *now); @@ -2388,6 +2390,15 @@ activatex11(struct wl_listener *listener, void *data) wlr_xwayland_surface_activate(c->surface.xwayland, 1); } +void +configurex11(struct wl_listener *listener, void *data) +{ + Client *c = wl_container_of(listener, c, configure); + struct wlr_xwayland_surface_configure_event *event = data; + wlr_xwayland_surface_configure(c->surface.xwayland, + event->x, event->y, event->width, event->height); +} + void createnotifyx11(struct wl_listener *listener, void *data) { @@ -2407,6 +2418,8 @@ createnotifyx11(struct wl_listener *listener, void *data) wl_signal_add(&xwayland_surface->events.unmap, &c->unmap); c->activate.notify = activatex11; wl_signal_add(&xwayland_surface->events.request_activate, &c->activate); + c->configure.notify = configurex11; + wl_signal_add(&xwayland_surface->events.request_configure, &c->configure); c->destroy.notify = destroynotify; wl_signal_add(&xwayland_surface->events.destroy, &c->destroy); } From 0f04c76387f74827931db0c3692fbdc2f50fa8d8 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Fri, 4 Sep 2020 12:44:09 +0200 Subject: [PATCH 33/78] Basic fullscreen --- dwl.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/dwl.c b/dwl.c index 924823b..4c97cc4 100644 --- a/dwl.c +++ b/dwl.c @@ -102,6 +102,7 @@ typedef struct { struct wl_listener map; struct wl_listener unmap; struct wl_listener destroy; + struct wl_listener fullscreen; struct wlr_box geom; /* layout-relative, includes border */ Monitor *mon; #ifdef XWAYLAND @@ -222,6 +223,7 @@ static void cursorframe(struct wl_listener *listener, void *data); static void destroylayersurfacenotify(struct wl_listener *listener, void *data); static void destroynotify(struct wl_listener *listener, void *data); static void destroyxdeco(struct wl_listener *listener, void *data); +static void fullscreenotify(struct wl_listener *listener, void *data); static Monitor *dirtomon(int dir); static void focusclient(Client *c, int lift); static void focusmon(const Arg *arg); @@ -909,6 +911,9 @@ createnotify(struct wl_listener *listener, void *data) wl_signal_add(&xdg_surface->events.unmap, &c->unmap); c->destroy.notify = destroynotify; wl_signal_add(&xdg_surface->events.destroy, &c->destroy); + + wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, &c->fullscreen); + c->fullscreen.notify = fullscreenotify; } void @@ -1037,6 +1042,12 @@ destroyxdeco(struct wl_listener *listener, void *data) free(d); } +void +fullscreenotify(struct wl_listener *listener, void *data) { + Client *c = wl_container_of(listener, c, fullscreen); + wlr_xdg_toplevel_set_fullscreen(c->surface.xdg, true); +} + Monitor * dirtomon(int dir) { From dd3920e75d2e71c569c3781914cd388f69f45bc9 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Fri, 4 Sep 2020 14:04:19 +0200 Subject: [PATCH 34/78] Toggle fullscreen --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 4c97cc4..c650875 100644 --- a/dwl.c +++ b/dwl.c @@ -1045,7 +1045,7 @@ destroyxdeco(struct wl_listener *listener, void *data) void fullscreenotify(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, fullscreen); - wlr_xdg_toplevel_set_fullscreen(c->surface.xdg, true); + wlr_xdg_toplevel_set_fullscreen(c->surface.xdg, !c->surface.xdg->toplevel->current.fullscreen); } Monitor * From de6db9740104843fced2f7594f58948beb021da4 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Fri, 4 Sep 2020 17:09:12 +0200 Subject: [PATCH 35/78] No borders on fullscreen windows Some code has been borrowed from the smartBorders patch --- dwl.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index c650875..26d977d 100644 --- a/dwl.c +++ b/dwl.c @@ -1045,7 +1045,9 @@ destroyxdeco(struct wl_listener *listener, void *data) void fullscreenotify(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, fullscreen); - wlr_xdg_toplevel_set_fullscreen(c->surface.xdg, !c->surface.xdg->toplevel->current.fullscreen); + wlr_xdg_toplevel_set_fullscreen( + c->surface.xdg, !c->surface.xdg->toplevel->current.fullscreen); + c->bw = (int)c->surface.xdg->toplevel->current.fullscreen * borderpx; } Monitor * @@ -1670,6 +1672,10 @@ renderclients(Monitor *m, struct timespec *now) ox = c->geom.x, oy = c->geom.y; wlr_output_layout_output_coords(output_layout, m->wlr_output, &ox, &oy); + + if (c->bw == 0) + goto render; + w = surface->current.width; h = surface->current.height; borders = (struct wlr_box[4]) { @@ -1687,6 +1693,7 @@ renderclients(Monitor *m, struct timespec *now) m->wlr_output->transform_matrix); } + render: /* This calls our render function for each surface among the * xdg_surface's toplevel and popups. */ rdata.output = m->wlr_output; From 60a732ceb8a436ddc511d2be5feb31a6186f3bf6 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Fri, 4 Sep 2020 17:11:26 +0200 Subject: [PATCH 36/78] Restore windows after fullscreen Store position and size of windows before going fullscreen. This is more efficient than arrange() and also works with floating windows All the clients keep their original position because arrange() isn't used after quitting fullscreen --- dwl.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/dwl.c b/dwl.c index 26d977d..554b9a0 100644 --- a/dwl.c +++ b/dwl.c @@ -112,6 +112,10 @@ typedef struct { unsigned int tags; int isfloating; uint32_t resize; /* configure serial of a pending resize */ + int prevx; + int prevy; + int prevwidth; + int prevheight; } Client; typedef struct { @@ -1048,6 +1052,16 @@ fullscreenotify(struct wl_listener *listener, void *data) { wlr_xdg_toplevel_set_fullscreen( c->surface.xdg, !c->surface.xdg->toplevel->current.fullscreen); c->bw = (int)c->surface.xdg->toplevel->current.fullscreen * borderpx; + + if (c->surface.xdg->toplevel->current.fullscreen) { /* fullscreen off */ + resize(c, c->prevx, c->prevy, c->prevwidth, c->prevheight, 0); + } else { /* fullscreen on */ + c->prevx = c->geom.x; + c->prevy = c->geom.y; + c->prevheight = c->geom.height; + c->prevwidth = c->geom.width; + resize(c, c->mon->w.x, c->mon->w.y, c->mon->w.width, c->mon->w.height, 0); + } } Monitor * From 573535c89c5bd470d2e820851a0b5ef54395b4dc Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sat, 5 Sep 2020 10:37:59 +0200 Subject: [PATCH 37/78] Unlink fullscreen --- dwl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dwl.c b/dwl.c index 554b9a0..c5ed398 100644 --- a/dwl.c +++ b/dwl.c @@ -1026,6 +1026,7 @@ destroynotify(struct wl_listener *listener, void *data) wl_list_remove(&c->map.link); wl_list_remove(&c->unmap.link); wl_list_remove(&c->destroy.link); + wl_list_remove(&c->fullscreen.link); #ifdef XWAYLAND if (c->type == X11Managed) wl_list_remove(&c->activate.link); From 64113a682f64065d6cc5df186cbecdc28565152d Mon Sep 17 00:00:00 2001 From: Stivvo Date: Fri, 4 Sep 2020 19:20:07 +0200 Subject: [PATCH 38/78] Fullscreen xwayland --- dwl.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index c5ed398..bafc9b1 100644 --- a/dwl.c +++ b/dwl.c @@ -116,6 +116,7 @@ typedef struct { int prevy; int prevwidth; int prevheight; + bool isfullscreen; } Client; typedef struct { @@ -344,6 +345,7 @@ static struct wl_listener request_set_sel = {.notify = setsel}; static void activatex11(struct wl_listener *listener, void *data); static void configurex11(struct wl_listener *listener, void *data); static void createnotifyx11(struct wl_listener *listener, void *data); +static void fullscreenotifyx11(struct wl_listener *listener, void *data); static Atom getatom(xcb_connection_t *xc, const char *name); static void renderindependents(struct wlr_output *output, struct timespec *now); static void updatewindowtype(Client *c); @@ -916,8 +918,8 @@ createnotify(struct wl_listener *listener, void *data) c->destroy.notify = destroynotify; wl_signal_add(&xdg_surface->events.destroy, &c->destroy); - wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, &c->fullscreen); c->fullscreen.notify = fullscreenotify; + wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, &c->fullscreen); } void @@ -2455,6 +2457,34 @@ createnotifyx11(struct wl_listener *listener, void *data) wl_signal_add(&xwayland_surface->events.request_configure, &c->configure); c->destroy.notify = destroynotify; wl_signal_add(&xwayland_surface->events.destroy, &c->destroy); + + c->fullscreen.notify = fullscreenotifyx11; + wl_signal_add(&xwayland_surface->events.request_fullscreen, &c->fullscreen); + c->isfullscreen = false; +} + +void +fullscreenotifyx11(struct wl_listener *listener, void *data) { + FILE *xway = fopen("/tmp/dwl/xway", "a"); + Client *c; + c = wl_container_of(listener, c, fullscreen); + c->isfullscreen = !c->isfullscreen; + wlr_xwayland_surface_set_fullscreen( + c->surface.xwayland, c->isfullscreen); + c->bw = ((int)(!c->isfullscreen)) * borderpx; + + fprintf(xway, "fullscreen: %d\n", c->surface.xwayland->fullscreen); + fclose(xway); + + if (c->isfullscreen) { /* fullscreen off */ + c->prevx = c->geom.x; + c->prevy = c->geom.y; + c->prevheight = c->geom.height; + c->prevwidth = c->geom.width; + resize(c, c->mon->w.x, c->mon->w.y, c->mon->w.width, c->mon->w.height, 0); + } else { /* fullscreen on */ + resize(c, c->prevx, c->prevy, c->prevwidth, c->prevheight, 1); + } } Atom From af68b7109497853936ffabc3e6926095976b572f Mon Sep 17 00:00:00 2001 From: Stivvo Date: Fri, 4 Sep 2020 19:56:16 +0200 Subject: [PATCH 39/78] Same fscreen func for xdg and xwayland --- dwl.c | 46 ++++++++++++++-------------------------------- 1 file changed, 14 insertions(+), 32 deletions(-) diff --git a/dwl.c b/dwl.c index bafc9b1..44af864 100644 --- a/dwl.c +++ b/dwl.c @@ -345,7 +345,6 @@ static struct wl_listener request_set_sel = {.notify = setsel}; static void activatex11(struct wl_listener *listener, void *data); static void configurex11(struct wl_listener *listener, void *data); static void createnotifyx11(struct wl_listener *listener, void *data); -static void fullscreenotifyx11(struct wl_listener *listener, void *data); static Atom getatom(xcb_connection_t *xc, const char *name); static void renderindependents(struct wlr_output *output, struct timespec *now); static void updatewindowtype(Client *c); @@ -920,6 +919,7 @@ createnotify(struct wl_listener *listener, void *data) c->fullscreen.notify = fullscreenotify; wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, &c->fullscreen); + c->isfullscreen = false; } void @@ -1052,18 +1052,24 @@ destroyxdeco(struct wl_listener *listener, void *data) void fullscreenotify(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, fullscreen); - wlr_xdg_toplevel_set_fullscreen( - c->surface.xdg, !c->surface.xdg->toplevel->current.fullscreen); - c->bw = (int)c->surface.xdg->toplevel->current.fullscreen * borderpx; + c->isfullscreen = !c->isfullscreen; - if (c->surface.xdg->toplevel->current.fullscreen) { /* fullscreen off */ - resize(c, c->prevx, c->prevy, c->prevwidth, c->prevheight, 0); - } else { /* fullscreen on */ +#ifdef XWAYLAND + if (c->type == X11Managed) + wlr_xwayland_surface_set_fullscreen(c->surface.xwayland, c->isfullscreen); + else +#endif + wlr_xdg_toplevel_set_fullscreen(c->surface.xdg, c->isfullscreen); + + c->bw = ((int)(!c->isfullscreen)) * borderpx; + if (c->isfullscreen) { c->prevx = c->geom.x; c->prevy = c->geom.y; c->prevheight = c->geom.height; c->prevwidth = c->geom.width; resize(c, c->mon->w.x, c->mon->w.y, c->mon->w.width, c->mon->w.height, 0); + } else { + resize(c, c->prevx, c->prevy, c->prevwidth, c->prevheight, 0); } } @@ -2458,35 +2464,11 @@ createnotifyx11(struct wl_listener *listener, void *data) c->destroy.notify = destroynotify; wl_signal_add(&xwayland_surface->events.destroy, &c->destroy); - c->fullscreen.notify = fullscreenotifyx11; + c->fullscreen.notify = fullscreenotify; wl_signal_add(&xwayland_surface->events.request_fullscreen, &c->fullscreen); c->isfullscreen = false; } -void -fullscreenotifyx11(struct wl_listener *listener, void *data) { - FILE *xway = fopen("/tmp/dwl/xway", "a"); - Client *c; - c = wl_container_of(listener, c, fullscreen); - c->isfullscreen = !c->isfullscreen; - wlr_xwayland_surface_set_fullscreen( - c->surface.xwayland, c->isfullscreen); - c->bw = ((int)(!c->isfullscreen)) * borderpx; - - fprintf(xway, "fullscreen: %d\n", c->surface.xwayland->fullscreen); - fclose(xway); - - if (c->isfullscreen) { /* fullscreen off */ - c->prevx = c->geom.x; - c->prevy = c->geom.y; - c->prevheight = c->geom.height; - c->prevwidth = c->geom.width; - resize(c, c->mon->w.x, c->mon->w.y, c->mon->w.width, c->mon->w.height, 0); - } else { /* fullscreen on */ - resize(c, c->prevx, c->prevy, c->prevwidth, c->prevheight, 1); - } -} - Atom getatom(xcb_connection_t *xc, const char *name) { From 2abfd475dedf86c84fc66da42c51cff87251659f Mon Sep 17 00:00:00 2001 From: Stivvo Date: Fri, 4 Sep 2020 19:58:00 +0200 Subject: [PATCH 40/78] isfullscreen int --- dwl.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/dwl.c b/dwl.c index 44af864..25206d9 100644 --- a/dwl.c +++ b/dwl.c @@ -116,7 +116,7 @@ typedef struct { int prevy; int prevwidth; int prevheight; - bool isfullscreen; + int isfullscreen; } Client; typedef struct { @@ -919,7 +919,7 @@ createnotify(struct wl_listener *listener, void *data) c->fullscreen.notify = fullscreenotify; wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, &c->fullscreen); - c->isfullscreen = false; + c->isfullscreen = 0; } void @@ -1050,7 +1050,8 @@ destroyxdeco(struct wl_listener *listener, void *data) } void -fullscreenotify(struct wl_listener *listener, void *data) { +fullscreenotify(struct wl_listener *listener, void *data) +{ Client *c = wl_container_of(listener, c, fullscreen); c->isfullscreen = !c->isfullscreen; @@ -2466,7 +2467,7 @@ createnotifyx11(struct wl_listener *listener, void *data) c->fullscreen.notify = fullscreenotify; wl_signal_add(&xwayland_surface->events.request_fullscreen, &c->fullscreen); - c->isfullscreen = false; + c->isfullscreen = 0; } Atom From d41cc60ec102d06b6c3e41c587fbb05b3a45e05e Mon Sep 17 00:00:00 2001 From: Stivvo Date: Fri, 4 Sep 2020 23:32:29 +0200 Subject: [PATCH 41/78] Handle new windows Windows lose fullscreen state when a new window is created in the same tag --- dwl.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index 25206d9..bd87590 100644 --- a/dwl.c +++ b/dwl.c @@ -228,11 +228,11 @@ static void cursorframe(struct wl_listener *listener, void *data); static void destroylayersurfacenotify(struct wl_listener *listener, void *data); static void destroynotify(struct wl_listener *listener, void *data); static void destroyxdeco(struct wl_listener *listener, void *data); -static void fullscreenotify(struct wl_listener *listener, void *data); static Monitor *dirtomon(int dir); static void focusclient(Client *c, int lift); static void focusmon(const Arg *arg); static void focusstack(const Arg *arg); +static void fullscreenotify(struct wl_listener *listener, void *data); static Client *focustop(Monitor *m); static void getxdecomode(struct wl_listener *listener, void *data); static void incnmaster(const Arg *arg); @@ -251,6 +251,7 @@ static void moveresize(const Arg *arg); static void outputmgrapply(struct wl_listener *listener, void *data); static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test); static void outputmgrtest(struct wl_listener *listener, void *data); +static void quitfullscreen(Client *c); static void pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, uint32_t time); static void quit(const Arg *arg); @@ -887,6 +888,24 @@ createmon(struct wl_listener *listener, void *data) } } +void +quitfullscreen(Client *c) +{ + wl_list_for_each(c, &clients, link) { + if (c->isfullscreen && VISIBLEON(c, c->mon)) { +#ifdef XWAYLAND + if (c->type == X11Managed) + wlr_xwayland_surface_set_fullscreen(c->surface.xwayland, false); + else +#endif + wlr_xdg_toplevel_set_fullscreen(c->surface.xdg, false); + c->bw = borderpx; + resize(c, c->prevx, c->prevy, c->prevwidth, c->prevheight, 0); + c->isfullscreen = 0; + } + } +} + void createnotify(struct wl_listener *listener, void *data) { @@ -902,6 +921,7 @@ createnotify(struct wl_listener *listener, void *data) c = xdg_surface->data = calloc(1, sizeof(*c)); c->surface.xdg = xdg_surface; c->bw = borderpx; + quitfullscreen(c); /* Tell the client not to try anything fancy */ wlr_xdg_toplevel_set_tiled(c->surface.xdg, WLR_EDGE_TOP | @@ -986,7 +1006,6 @@ createxdeco(struct wl_listener *listener, void *data) getxdecomode(&d->request_mode, wlr_deco); } - void cursorframe(struct wl_listener *listener, void *data) { @@ -2452,6 +2471,7 @@ createnotifyx11(struct wl_listener *listener, void *data) c->surface.xwayland = xwayland_surface; c->type = xwayland_surface->override_redirect ? X11Unmanaged : X11Managed; c->bw = borderpx; + quitfullscreen(c); /* Listen to the various events it can emit */ c->map.notify = maprequest; From f125e1b9a4e05a5282765b0b699f8a097ea65783 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sat, 5 Sep 2020 11:22:24 +0200 Subject: [PATCH 42/78] Toggle fullscreen on all clients mod+e allows to toggle fullscreen any client, even those who don't support it themselves --- config.def.h | 1 + dwl.c | 21 ++++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/config.def.h b/config.def.h index 53021cf..d821a96 100644 --- a/config.def.h +++ b/config.def.h @@ -75,6 +75,7 @@ static const Key keys[] = { { 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_e, togglefullscreen, {0} }, { MODKEY, XKB_KEY_0, view, {.ui = ~0} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} }, { MODKEY, XKB_KEY_comma, focusmon, {.i = -1} }, diff --git a/dwl.c b/dwl.c index bd87590..bc2a3e8 100644 --- a/dwl.c +++ b/dwl.c @@ -267,6 +267,7 @@ static void setcursor(struct wl_listener *listener, void *data); static void setpsel(struct wl_listener *listener, void *data); static void setsel(struct wl_listener *listener, void *data); static void setfloating(Client *c, int floating); +static void setfullscreen(Client *c, int fullscreen); static void setlayout(const Arg *arg); static void setmfact(const Arg *arg); static void setmon(Client *c, Monitor *m, unsigned int newtags); @@ -278,6 +279,7 @@ 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 toggletag(const Arg *arg); static void toggleview(const Arg *arg); static void unmaplayersurface(LayerSurface *layersurface); @@ -1069,10 +1071,16 @@ destroyxdeco(struct wl_listener *listener, void *data) } void -fullscreenotify(struct wl_listener *listener, void *data) +togglefullscreen(const Arg *arg) { - Client *c = wl_container_of(listener, c, fullscreen); - c->isfullscreen = !c->isfullscreen; + Client *sel = selclient(); + setfullscreen(sel, !sel->isfullscreen); +} + +void +setfullscreen(Client *c, int fullscreen) +{ + c->isfullscreen = fullscreen; #ifdef XWAYLAND if (c->type == X11Managed) @@ -1093,6 +1101,13 @@ fullscreenotify(struct wl_listener *listener, void *data) } } +void +fullscreenotify(struct wl_listener *listener, void *data) +{ + Client *c = wl_container_of(listener, c, fullscreen); + setfullscreen(c, !c->isfullscreen); +} + Monitor * dirtomon(int dir) { From 5bd9be3a75b215b7f607ae46acf20765237925a8 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sun, 6 Sep 2020 10:27:24 +0200 Subject: [PATCH 43/78] Allow borderpx = 0 --- dwl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index bc2a3e8..85a27f2 100644 --- a/dwl.c +++ b/dwl.c @@ -1731,7 +1731,7 @@ renderclients(Monitor *m, struct timespec *now) wlr_output_layout_output_coords(output_layout, m->wlr_output, &ox, &oy); - if (c->bw == 0) + if (c->isfullscreen || borderpx == 0) goto render; w = surface->current.width; @@ -1751,7 +1751,7 @@ renderclients(Monitor *m, struct timespec *now) m->wlr_output->transform_matrix); } - render: +render: /* This calls our render function for each surface among the * xdg_surface's toplevel and popups. */ rdata.output = m->wlr_output; From 36b9831ffdec9c793a0f8afddbd9e9f9fd7f8865 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Thu, 10 Sep 2020 09:09:46 +0200 Subject: [PATCH 44/78] fix typo --- dwl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dwl.c b/dwl.c index 85a27f2..c051b5f 100644 --- a/dwl.c +++ b/dwl.c @@ -232,7 +232,7 @@ static Monitor *dirtomon(int dir); static void focusclient(Client *c, int lift); static void focusmon(const Arg *arg); static void focusstack(const Arg *arg); -static void fullscreenotify(struct wl_listener *listener, void *data); +static void fullscreennotify(struct wl_listener *listener, void *data); static Client *focustop(Monitor *m); static void getxdecomode(struct wl_listener *listener, void *data); static void incnmaster(const Arg *arg); @@ -939,7 +939,7 @@ createnotify(struct wl_listener *listener, void *data) c->destroy.notify = destroynotify; wl_signal_add(&xdg_surface->events.destroy, &c->destroy); - c->fullscreen.notify = fullscreenotify; + c->fullscreen.notify = fullscreennotify; wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, &c->fullscreen); c->isfullscreen = 0; } @@ -1102,7 +1102,7 @@ setfullscreen(Client *c, int fullscreen) } void -fullscreenotify(struct wl_listener *listener, void *data) +fullscreennotify(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, fullscreen); setfullscreen(c, !c->isfullscreen); @@ -2500,7 +2500,7 @@ createnotifyx11(struct wl_listener *listener, void *data) c->destroy.notify = destroynotify; wl_signal_add(&xwayland_surface->events.destroy, &c->destroy); - c->fullscreen.notify = fullscreenotify; + c->fullscreen.notify = fullscreennotify; wl_signal_add(&xwayland_surface->events.request_fullscreen, &c->fullscreen); c->isfullscreen = 0; } From cb9269df41c66cbd871fc9bc73657e736bee6a0d Mon Sep 17 00:00:00 2001 From: Stivvo Date: Wed, 16 Sep 2020 09:20:07 +0200 Subject: [PATCH 45/78] use m->m (fullscreen on top of layers) --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index c051b5f..ec8c7db 100644 --- a/dwl.c +++ b/dwl.c @@ -1095,7 +1095,7 @@ setfullscreen(Client *c, int fullscreen) c->prevy = c->geom.y; c->prevheight = c->geom.height; c->prevwidth = c->geom.width; - resize(c, c->mon->w.x, c->mon->w.y, c->mon->w.width, c->mon->w.height, 0); + resize(c, c->mon->m.x, c->mon->m.y, c->mon->m.width, c->mon->m.height, 0); } else { resize(c, c->prevx, c->prevy, c->prevwidth, c->prevheight, 0); } From 02a09cb85414df8002c61bd072c5870ab4f6a485 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Thu, 8 Oct 2020 21:04:28 +0200 Subject: [PATCH 46/78] Set fullscreen simpler --- dwl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dwl.c b/dwl.c index ec8c7db..e9daab6 100644 --- a/dwl.c +++ b/dwl.c @@ -1081,16 +1081,16 @@ void setfullscreen(Client *c, int fullscreen) { c->isfullscreen = fullscreen; + c->bw = (1 - fullscreen) * borderpx; #ifdef XWAYLAND if (c->type == X11Managed) - wlr_xwayland_surface_set_fullscreen(c->surface.xwayland, c->isfullscreen); + wlr_xwayland_surface_set_fullscreen(c->surface.xwayland, fullscreen); else #endif - wlr_xdg_toplevel_set_fullscreen(c->surface.xdg, c->isfullscreen); + wlr_xdg_toplevel_set_fullscreen(c->surface.xdg, fullscreen); - c->bw = ((int)(!c->isfullscreen)) * borderpx; - if (c->isfullscreen) { + if (fullscreen) { c->prevx = c->geom.x; c->prevy = c->geom.y; c->prevheight = c->geom.height; From 5a16649e799bab33babb935371253695d9dccc5c Mon Sep 17 00:00:00 2001 From: Stivvo Date: Thu, 8 Oct 2020 21:04:53 +0200 Subject: [PATCH 47/78] Keep windows fullscreen after redraw This fixes the bug that happens when changing workspace (or any time arrange() is called) where there are fullscreen windows, which are still fullscreen but leave the space for layer surfaces like waybar (which should be hidden when going fullscreen) Also as soon one fullscreen window is found hte function returns to improve efficiency --- dwl.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dwl.c b/dwl.c index e9daab6..8bf74b1 100644 --- a/dwl.c +++ b/dwl.c @@ -1405,6 +1405,10 @@ monocle(Monitor *m) wl_list_for_each(c, &clients, link) { if (!VISIBLEON(c, m) || c->isfloating) continue; + if (c->isfullscreen) { + resize(c, c->mon->m.x, c->mon->m.y, c->mon->m.width, c->mon->m.height, 0); + return; + } resize(c, m->w.x, m->w.y, m->w.width, m->w.height, 0); } } @@ -2254,6 +2258,10 @@ tile(Monitor *m) wl_list_for_each(c, &clients, link) { if (!VISIBLEON(c, m) || c->isfloating) continue; + if (c->isfullscreen) { + resize(c, c->mon->m.x, c->mon->m.y, c->mon->m.width, c->mon->m.height, 0); + return; + } if (i < m->nmaster) { h = (m->w.height - my) / (MIN(n, m->nmaster) - i); resize(c, m->w.x, m->w.y + my, mw, h, 0); From 32612c90b6314c99c07c52f9c3f0e4c4cd4f365c Mon Sep 17 00:00:00 2001 From: Stivvo Date: Wed, 14 Oct 2020 15:46:35 +0200 Subject: [PATCH 48/78] Delete quitfullscreen() quitfullscreen() was replicating the functionalities of setfullscreen(c, 0) Reusing setfullscreen() in quitfullscreen() leads to a 3 line function, which is useless since quitfullscreen() is used once anyway --- dwl.c | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/dwl.c b/dwl.c index 8bf74b1..7c5558a 100644 --- a/dwl.c +++ b/dwl.c @@ -251,7 +251,6 @@ static void moveresize(const Arg *arg); static void outputmgrapply(struct wl_listener *listener, void *data); static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test); static void outputmgrtest(struct wl_listener *listener, void *data); -static void quitfullscreen(Client *c); static void pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, uint32_t time); static void quit(const Arg *arg); @@ -890,24 +889,6 @@ createmon(struct wl_listener *listener, void *data) } } -void -quitfullscreen(Client *c) -{ - wl_list_for_each(c, &clients, link) { - if (c->isfullscreen && VISIBLEON(c, c->mon)) { -#ifdef XWAYLAND - if (c->type == X11Managed) - wlr_xwayland_surface_set_fullscreen(c->surface.xwayland, false); - else -#endif - wlr_xdg_toplevel_set_fullscreen(c->surface.xdg, false); - c->bw = borderpx; - resize(c, c->prevx, c->prevy, c->prevwidth, c->prevheight, 0); - c->isfullscreen = 0; - } - } -} - void createnotify(struct wl_listener *listener, void *data) { @@ -919,11 +900,14 @@ createnotify(struct wl_listener *listener, void *data) if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) return; + wl_list_for_each(c, &clients, link) + if (c->isfullscreen && VISIBLEON(c, c->mon)) + setfullscreen(c, 0); + /* Allocate a Client for this surface */ c = xdg_surface->data = calloc(1, sizeof(*c)); c->surface.xdg = xdg_surface; c->bw = borderpx; - quitfullscreen(c); /* Tell the client not to try anything fancy */ wlr_xdg_toplevel_set_tiled(c->surface.xdg, WLR_EDGE_TOP | From 1e134fde972597e2f10c2bdf40d1356dad5b2cb7 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Wed, 14 Oct 2020 17:28:51 +0200 Subject: [PATCH 49/78] Quit fullscreen on new x11 window After the removal of quitfullscreen() dwl wouldn't compile widh xwayland enabled because createnotifyx11 was still using the old function --- dwl.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dwl.c b/dwl.c index 7c5558a..32437b8 100644 --- a/dwl.c +++ b/dwl.c @@ -899,7 +899,6 @@ createnotify(struct wl_listener *listener, void *data) if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) return; - wl_list_for_each(c, &clients, link) if (c->isfullscreen && VISIBLEON(c, c->mon)) setfullscreen(c, 0); @@ -1719,7 +1718,7 @@ renderclients(Monitor *m, struct timespec *now) wlr_output_layout_output_coords(output_layout, m->wlr_output, &ox, &oy); - if (c->isfullscreen || borderpx == 0) + if (c->isfullscreen) goto render; w = surface->current.width; @@ -2472,13 +2471,15 @@ createnotifyx11(struct wl_listener *listener, void *data) { struct wlr_xwayland_surface *xwayland_surface = data; Client *c; + wl_list_for_each(c, &clients, link) + if (c->isfullscreen && VISIBLEON(c, c->mon)) + setfullscreen(c, 0); /* Allocate a Client for this surface */ c = xwayland_surface->data = calloc(1, sizeof(*c)); c->surface.xwayland = xwayland_surface; c->type = xwayland_surface->override_redirect ? X11Unmanaged : X11Managed; c->bw = borderpx; - quitfullscreen(c); /* Listen to the various events it can emit */ c->map.notify = maprequest; From 14ce0162136cd01064ae3bc650d9ffee36291de0 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sat, 24 Oct 2020 16:51:22 +0200 Subject: [PATCH 50/78] Readme: achieve fullscreen + allow borderpx = 0 --- README.md | 1 - dwl.c | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fd83f92..43010dc 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,6 @@ dwl is a work in progress, and it has not yet reached its feature goals in a num - Urgent/attention/focus-request ([not yet supported](https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/9) by xdg-shell protocol) - Statusbar support (built-in or external) - Damage tracking -- Fullscreen/fixed windows (or whatever the Wayland analogues are) ## Acknowledgements diff --git a/dwl.c b/dwl.c index 32437b8..4c03717 100644 --- a/dwl.c +++ b/dwl.c @@ -1073,6 +1073,7 @@ setfullscreen(Client *c, int fullscreen) #endif wlr_xdg_toplevel_set_fullscreen(c->surface.xdg, fullscreen); + // restore previous size instead of arrange to work with floating windows if (fullscreen) { c->prevx = c->geom.x; c->prevy = c->geom.y; @@ -1717,8 +1718,7 @@ renderclients(Monitor *m, struct timespec *now) ox = c->geom.x, oy = c->geom.y; wlr_output_layout_output_coords(output_layout, m->wlr_output, &ox, &oy); - - if (c->isfullscreen) + if (c->bw == 0) goto render; w = surface->current.width; From c89de53de3e0867157730026a333e16430e71e5a Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 19 Dec 2020 18:23:23 +0100 Subject: [PATCH 51/78] remove togglefullscreen keybinding Distribute it as a patch like in dwm since graphical applications usually provide their own keybinding; I guess it's only for terminals. Note that even though these commits don't let you open multiple windows in fullscreen and cycle between them like in dwm, with just fullscreennotify spawning new windows or changing tag would still exit fullscreen automatically, but you would have to toggle fullscreen twice when switching back to the fullscreen window to enter fullscreen again, so this is better since it avoids that. --- config.def.h | 1 - dwl.c | 8 -------- 2 files changed, 9 deletions(-) diff --git a/config.def.h b/config.def.h index d821a96..53021cf 100644 --- a/config.def.h +++ b/config.def.h @@ -75,7 +75,6 @@ static const Key keys[] = { { 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_e, togglefullscreen, {0} }, { MODKEY, XKB_KEY_0, view, {.ui = ~0} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} }, { MODKEY, XKB_KEY_comma, focusmon, {.i = -1} }, diff --git a/dwl.c b/dwl.c index 4c03717..6d51236 100644 --- a/dwl.c +++ b/dwl.c @@ -278,7 +278,6 @@ 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 toggletag(const Arg *arg); static void toggleview(const Arg *arg); static void unmaplayersurface(LayerSurface *layersurface); @@ -1053,13 +1052,6 @@ destroyxdeco(struct wl_listener *listener, void *data) free(d); } -void -togglefullscreen(const Arg *arg) -{ - Client *sel = selclient(); - setfullscreen(sel, !sel->isfullscreen); -} - void setfullscreen(Client *c, int fullscreen) { From 9c2524b06a486cd01bfc06b73d10e2b903e3d82d Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 19 Dec 2020 18:31:26 +0100 Subject: [PATCH 52/78] s/prev/old Be consistent with the rest of the code and dwm --- dwl.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/dwl.c b/dwl.c index 6d51236..02efa43 100644 --- a/dwl.c +++ b/dwl.c @@ -112,10 +112,10 @@ typedef struct { unsigned int tags; int isfloating; uint32_t resize; /* configure serial of a pending resize */ - int prevx; - int prevy; - int prevwidth; - int prevheight; + int oldx; + int oldy; + int oldwidth; + int oldheight; int isfullscreen; } Client; @@ -1067,13 +1067,13 @@ setfullscreen(Client *c, int fullscreen) // restore previous size instead of arrange to work with floating windows if (fullscreen) { - c->prevx = c->geom.x; - c->prevy = c->geom.y; - c->prevheight = c->geom.height; - c->prevwidth = c->geom.width; + c->oldx = c->geom.x; + c->oldy = c->geom.y; + c->oldheight = c->geom.height; + c->oldwidth = c->geom.width; resize(c, c->mon->m.x, c->mon->m.y, c->mon->m.width, c->mon->m.height, 0); } else { - resize(c, c->prevx, c->prevy, c->prevwidth, c->prevheight, 0); + resize(c, c->oldx, c->oldy, c->oldwidth, c->oldheight, 0); } } From 679f6493c97df30cf6604778ac1ce6014dec952b Mon Sep 17 00:00:00 2001 From: Bonicgamer <44382222+Bonicgamer@users.noreply.github.com> Date: Tue, 8 Sep 2020 14:53:34 -0400 Subject: [PATCH 53/78] Made scalebox the way sway does it Scaling a wlr_box without rounding can sometimes make the borders not connected and nice. I noticed this when setting scale in monrules to 1.2 So I've went and copied what Sway did in desktop/output.c but without having a second function and now using a random rounding macro I found on the internet so as to not use round from math.h. --- dwl.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/dwl.c b/dwl.c index 02efa43..2257222 100644 --- a/dwl.c +++ b/dwl.c @@ -55,6 +55,7 @@ #define LENGTH(X) (sizeof X / sizeof X[0]) #define END(A) ((A) + LENGTH(A)) #define TAGMASK ((1 << LENGTH(tags)) - 1) +#define ROUND(X) ((X)>=0?(long)((X)+0.5):(long)((X)-0.5)) #ifdef XWAYLAND #define WLR_SURFACE(C) ((C)->type != XDGShell ? (C)->surface.xwayland->surface : (C)->surface.xdg->surface) #else @@ -1896,10 +1897,10 @@ run(char *startup_cmd) void scalebox(struct wlr_box *box, float scale) { - box->x *= scale; - box->y *= scale; - box->width *= scale; - box->height *= scale; + box->width = ROUND((box->x + box->width) * scale) - ROUND(box->x * scale); + box->height = ROUND((box->y + box->height) * scale) - ROUND(box->y * scale); + box->x = ROUND(box->x * scale); + box->y = ROUND(box->y * scale); } Client * From bfbfe9f2b2f2708c695d9b57590605802ca60f22 Mon Sep 17 00:00:00 2001 From: Oyren Date: Mon, 14 Sep 2020 20:45:39 +0200 Subject: [PATCH 54/78] remove log flags from readme The following commit has removed the logs but they are still in the readme. https://github.com/djpohly/dwl/commit/3b1992ca91b9a468019165c985263f5b1cc78c2c --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index 43010dc..1a8d8ec 100644 --- a/README.md +++ b/README.md @@ -49,12 +49,6 @@ dwl can be run as-is, with no arguments. In an existing Wayland or X11 session, You can also specify a startup program using the `-s` option. The argument to this option will be run at startup as a shell command (using `sh -c`) and can serve a similar function to `.xinitrc`: starting a service manager or other startup applications. Unlike `.xinitrc`, the display server will not shut down when this process terminates. Instead, as dwl is shutting down, it will send this process a SIGTERM and wait for it to terminate (if it hasn't already). This makes it ideal not only for initialization but also for execing into a user-level service manager like s6 or `systemd --user`. -More/less verbose output can be requested with flags as well: - -* `-q`: quiet (log level WLR_SILENT) -* `-v`: verbose (log level WLR_INFO) -* `-d`: debug (log level WLR_DEBUG) - Note: Wayland requires a valid `XDG_RUNTIME_DIR`, which is usually set up by a session manager such as `elogind` or `systemd-logind`. If your system doesn't do this automatically, you will need to configure it prior to launching `dwl`, e.g.: export XDG_RUNTIME_DIR=/tmp/xdg-runtime-$(id -u) From 4f1e557d3d26ee8359750dce8f370d404513a1ca Mon Sep 17 00:00:00 2001 From: will Date: Sat, 17 Oct 2020 13:52:53 +0200 Subject: [PATCH 55/78] Added basic tap-to-click for touchpad users --- Makefile | 2 +- config.def.h | 4 ++++ dwl.c | 6 ++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index abbbe39..bf0b957 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ CFLAGS += -I. -DWLR_USE_UNSTABLE -std=c99 -Werror=declaration-after-statement WAYLAND_PROTOCOLS=$(shell pkg-config --variable=pkgdatadir wayland-protocols) WAYLAND_SCANNER=$(shell pkg-config --variable=wayland_scanner wayland-scanner) -PKGS = wlroots wayland-server xcb xkbcommon +PKGS = wlroots wayland-server xcb xkbcommon libinput CFLAGS += $(foreach p,$(PKGS),$(shell pkg-config --cflags $(p))) LDLIBS += $(foreach p,$(PKGS),$(shell pkg-config --libs $(p))) diff --git a/config.def.h b/config.def.h index 53021cf..c1ad7d6 100644 --- a/config.def.h +++ b/config.def.h @@ -41,6 +41,10 @@ static const struct xkb_rule_names xkb_rules = { .options = "ctrl:nocaps", */ }; + +/* Trackpad */ +int tap_to_click = 1; + static const int repeat_rate = 25; static const int repeat_delay = 600; diff --git a/dwl.c b/dwl.c index 2257222..2907c5f 100644 --- a/dwl.c +++ b/dwl.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +39,7 @@ #include #include #include +#include #include #include #ifdef XWAYLAND @@ -970,6 +972,10 @@ createlayersurface(struct wl_listener *listener, void *data) void createpointer(struct wlr_input_device *device) { + struct libinput_device *libinput_device = (struct libinput_device*) + wlr_libinput_get_device_handle(device); + if (tap_to_click && libinput_device_config_tap_get_finger_count(libinput_device)) + libinput_device_config_tap_set_enabled(libinput_device, LIBINPUT_CONFIG_TAP_ENABLED); /* We don't do anything special with pointers. All of our pointer handling * is proxied through wlr_cursor. On another compositor, you might take this * opportunity to do libinput configuration on the device to set From aa679c4f29fd386e0bb89dd95ec2093f9c998ba8 Mon Sep 17 00:00:00 2001 From: will Date: Sat, 17 Oct 2020 16:18:44 +0200 Subject: [PATCH 56/78] Added support for natural scrolling --- config.def.h | 1 + dwl.c | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/config.def.h b/config.def.h index c1ad7d6..2c11fd3 100644 --- a/config.def.h +++ b/config.def.h @@ -44,6 +44,7 @@ static const struct xkb_rule_names xkb_rules = { /* Trackpad */ int tap_to_click = 1; +int natural_scrolling = 1; static const int repeat_rate = 25; static const int repeat_delay = 600; diff --git a/dwl.c b/dwl.c index 2907c5f..188639e 100644 --- a/dwl.c +++ b/dwl.c @@ -974,8 +974,13 @@ createpointer(struct wlr_input_device *device) { struct libinput_device *libinput_device = (struct libinput_device*) wlr_libinput_get_device_handle(device); + if (tap_to_click && libinput_device_config_tap_get_finger_count(libinput_device)) libinput_device_config_tap_set_enabled(libinput_device, LIBINPUT_CONFIG_TAP_ENABLED); + + if (libinput_device_config_scroll_has_natural_scroll(libinput_device)) + libinput_device_config_scroll_set_natural_scroll_enabled(libinput_device, natural_scrolling); + /* We don't do anything special with pointers. All of our pointer handling * is proxied through wlr_cursor. On another compositor, you might take this * opportunity to do libinput configuration on the device to set From feeacc88c41653937a2698fcb39a455cd41c44d8 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 19 Dec 2020 18:39:30 +0100 Subject: [PATCH 57/78] tweak trackpad variables Add static const and move them below in order to group the keyboard options. --- config.def.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/config.def.h b/config.def.h index 2c11fd3..fc30ba8 100644 --- a/config.def.h +++ b/config.def.h @@ -42,13 +42,13 @@ static const struct xkb_rule_names xkb_rules = { */ }; -/* Trackpad */ -int tap_to_click = 1; -int natural_scrolling = 1; - static const int repeat_rate = 25; static const int repeat_delay = 600; +/* Trackpad */ +static const int tap_to_click = 1; +static const int natural_scrolling = 1; + #define MODKEY WLR_MODIFIER_ALT #define TAGKEYS(KEY,SKEY,TAG) \ { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ From 05883b7b2f823cf292b2f27b8ee2c542e6a8eaa7 Mon Sep 17 00:00:00 2001 From: Keating950 Date: Sun, 22 Nov 2020 12:58:49 -0500 Subject: [PATCH 58/78] add install target to Makefile and corresponding prefix variable to config.mk --- Makefile | 5 +++++ config.mk | 3 +++ 2 files changed, 8 insertions(+) diff --git a/Makefile b/Makefile index bf0b957..3d3028f 100644 --- a/Makefile +++ b/Makefile @@ -53,5 +53,10 @@ dwl: xdg-shell-protocol.o wlr-layer-shell-unstable-v1-protocol.o idle-protocol.o clean: rm -f dwl *.o *-protocol.h *-protocol.c +install: dwl + mkdir -p $(PREFIX)/bin + cp -f dwl $(PREFIX)/bin + chmod 755 $(PREFIX)/bin/dwl + .DEFAULT_GOAL=dwl .PHONY: clean diff --git a/config.mk b/config.mk index a101f23..3958049 100644 --- a/config.mk +++ b/config.mk @@ -1,3 +1,6 @@ +# paths +PREFIX = /usr/local + # Default compile flags (overridable by environment) CFLAGS ?= -g -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare -Wno-error=unused-function From ee7e8688a71c8fc15c4e3f0da882bd24660efbf6 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 19 Dec 2020 18:51:22 +0100 Subject: [PATCH 59/78] s/maprequest/mapnotify This should be consistent with other function names instead of keeping the X name. --- dwl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dwl.c b/dwl.c index 188639e..d19d3ee 100644 --- a/dwl.c +++ b/dwl.c @@ -245,7 +245,7 @@ static void keypress(struct wl_listener *listener, void *data); static void keypressmod(struct wl_listener *listener, void *data); static void killclient(const Arg *arg); static void maplayersurfacenotify(struct wl_listener *listener, void *data); -static void maprequest(struct wl_listener *listener, void *data); +static void mapnotify(struct wl_listener *listener, void *data); static void monocle(Monitor *m); static void motionabsolute(struct wl_listener *listener, void *data); static void motionnotify(uint32_t time); @@ -917,7 +917,7 @@ createnotify(struct wl_listener *listener, void *data) /* Listen to the various events it can emit */ c->commit.notify = commitnotify; wl_signal_add(&xdg_surface->surface->events.commit, &c->commit); - c->map.notify = maprequest; + c->map.notify = mapnotify; wl_signal_add(&xdg_surface->events.map, &c->map); c->unmap.notify = unmapnotify; wl_signal_add(&xdg_surface->events.unmap, &c->unmap); @@ -1349,7 +1349,7 @@ maplayersurfacenotify(struct wl_listener *listener, void *data) } void -maprequest(struct wl_listener *listener, void *data) +mapnotify(struct wl_listener *listener, void *data) { /* Called when the surface is mapped, or ready to display on-screen. */ Client *c = wl_container_of(listener, c, map); @@ -2486,7 +2486,7 @@ createnotifyx11(struct wl_listener *listener, void *data) c->bw = borderpx; /* Listen to the various events it can emit */ - c->map.notify = maprequest; + c->map.notify = mapnotify; wl_signal_add(&xwayland_surface->events.map, &c->map); c->unmap.notify = unmapnotify; wl_signal_add(&xwayland_surface->events.unmap, &c->unmap); From 2eaa8c6de355aeec66d0a81ef8e4937d9aad797a Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 19 Dec 2020 18:56:42 +0100 Subject: [PATCH 60/78] remove useless assignment calloc already initializes ints to 0. --- dwl.c | 1 - 1 file changed, 1 deletion(-) diff --git a/dwl.c b/dwl.c index d19d3ee..689e56a 100644 --- a/dwl.c +++ b/dwl.c @@ -458,7 +458,6 @@ applyrules(Client *c) Monitor *mon = selmon, *m; /* rule matching */ - c->isfloating = 0; #ifdef XWAYLAND if (c->type != XDGShell) { updatewindowtype(c); From ba1540c3d006eb001e45e3b9315a00c9c599d2cc Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 19 Dec 2020 19:32:16 +0100 Subject: [PATCH 61/78] remove goto when the border is 0 Rendering 0-dimension rectangles no longer crashes wlroots. --- dwl.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/dwl.c b/dwl.c index 689e56a..ba0b32a 100644 --- a/dwl.c +++ b/dwl.c @@ -1721,8 +1721,6 @@ renderclients(Monitor *m, struct timespec *now) ox = c->geom.x, oy = c->geom.y; wlr_output_layout_output_coords(output_layout, m->wlr_output, &ox, &oy); - if (c->bw == 0) - goto render; w = surface->current.width; h = surface->current.height; @@ -1741,7 +1739,6 @@ renderclients(Monitor *m, struct timespec *now) m->wlr_output->transform_matrix); } -render: /* This calls our render function for each surface among the * xdg_surface's toplevel and popups. */ rdata.output = m->wlr_output; From 0b2f4f213dc142824aac31e78ee32fb888fbb765 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 19 Dec 2020 19:50:01 +0100 Subject: [PATCH 62/78] remove -Werror=declaration-after-statement wtf is the point of this crap? It makes the code harder to follow, increases the line count and made me fail compilation a million times. We shouldn't blindy follow everything about suckless's style. --- Makefile | 2 +- dwl.c | 312 ++++++++++++++++++++++++++----------------------------- 2 files changed, 151 insertions(+), 163 deletions(-) diff --git a/Makefile b/Makefile index 3d3028f..620d287 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ include config.mk -CFLAGS += -I. -DWLR_USE_UNSTABLE -std=c99 -Werror=declaration-after-statement +CFLAGS += -I. -DWLR_USE_UNSTABLE -std=c99 WAYLAND_PROTOCOLS=$(shell pkg-config --variable=pkgdatadir wayland-protocols) WAYLAND_SCANNER=$(shell pkg-config --variable=wayland_scanner wayland-scanner) diff --git a/dwl.c b/dwl.c index ba0b32a..a4f8b6d 100644 --- a/dwl.c +++ b/dwl.c @@ -389,53 +389,61 @@ applyexclusive(struct wlr_box *usable_area, uint32_t anchor, int32_t exclusive, int32_t margin_top, int32_t margin_right, int32_t margin_bottom, int32_t margin_left) { + if (exclusive <= 0) + return; + struct { uint32_t singular_anchor; uint32_t anchor_triplet; int *positive_axis; int *negative_axis; int margin; - } edges[4]; - - if (exclusive <= 0) - return; - - // Top - edges[0].singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP; - edges[0].anchor_triplet = - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP; - edges[0].positive_axis = &usable_area->y; - edges[0].negative_axis = &usable_area->height; - edges[0].margin = margin_top; - // Bottom - edges[1].singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; - edges[1].anchor_triplet = - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; - edges[1].positive_axis = NULL; - edges[1].negative_axis = &usable_area->height; - edges[1].margin = margin_bottom; - // Left - edges[2].singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT; - edges[2].anchor_triplet = - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; - edges[2].positive_axis = &usable_area->x; - edges[2].negative_axis = &usable_area->width; - edges[2].margin = margin_left; - // Right - edges[3].singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; - edges[3].anchor_triplet = - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; - edges[3].positive_axis = NULL; - edges[3].negative_axis = &usable_area->width; - edges[3].margin = margin_right; + } edges[] = { + // Top + { + .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, + .anchor_triplet = + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, + .positive_axis = &usable_area->y, + .negative_axis = &usable_area->height, + .margin = margin_top, + }, + // Bottom + { + .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, + .anchor_triplet = + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, + .positive_axis = NULL, + .negative_axis = &usable_area->height, + .margin = margin_bottom, + }, + // Left + { + .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT, + .anchor_triplet = + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, + .positive_axis = &usable_area->x, + .negative_axis = &usable_area->width, + .margin = margin_left, + }, + // Right + { + .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT, + .anchor_triplet = + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, + .positive_axis = NULL, + .negative_axis = &usable_area->width, + .margin = margin_right, + }, + }; for (size_t i = 0; i < LENGTH(edges); ++i) { if ((anchor == edges[i].singular_anchor || anchor == edges[i].anchor_triplet) @@ -452,12 +460,8 @@ applyexclusive(struct wlr_box *usable_area, void applyrules(Client *c) { - const char *appid, *title; - unsigned int i, newtags = 0; - const Rule *r; - Monitor *mon = selmon, *m; - /* rule matching */ + const char *appid, *title; #ifdef XWAYLAND if (c->type != XDGShell) { updatewindowtype(c); @@ -474,6 +478,9 @@ applyrules(Client *c) if (!title) title = broken; + unsigned int i, newtags = 0; + const Rule *r; + Monitor *mon = selmon, *m; for (r = rules; r < END(rules); r++) { if ((!r->title || strstr(title, r->title)) && (!r->id || strstr(appid, r->id))) { @@ -505,22 +512,20 @@ arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, bool wl_list_for_each(layersurface, list, link) { struct wlr_layer_surface_v1 *wlr_layer_surface = layersurface->layer_surface; struct wlr_layer_surface_v1_state *state = &wlr_layer_surface->current; - struct wlr_box bounds; struct wlr_box box = { .width = state->desired_width, .height = state->desired_height }; - const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT - | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; - const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP - | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; if (exclusive != (state->exclusive_zone > 0)) continue; - bounds = state->exclusive_zone == -1 ? full_area : *usable_area; + struct wlr_box bounds = state->exclusive_zone == -1 ? + full_area : *usable_area; // Horizontal axis + const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT + | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; if ((state->anchor & both_horiz) && box.width == 0) { box.x = bounds.x; box.width = bounds.width; @@ -532,6 +537,8 @@ arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, bool box.x = bounds.x + ((bounds.width / 2) - (box.width / 2)); } // Vertical axis + const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP + | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; if ((state->anchor & both_vert) && box.height == 0) { box.y = bounds.y; box.height = bounds.height; @@ -576,13 +583,6 @@ void arrangelayers(Monitor *m) { struct wlr_box usable_area = m->m; - uint32_t layers_above_shell[] = { - ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, - ZWLR_LAYER_SHELL_V1_LAYER_TOP, - }; - size_t nlayers = LENGTH(layers_above_shell); - LayerSurface *layersurface; - struct wlr_keyboard *kb = wlr_seat_get_keyboard(seat); // Arrange exclusive surfaces from top->bottom arrangelayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], @@ -610,6 +610,13 @@ arrangelayers(Monitor *m) &usable_area, false); // Find topmost keyboard interactive layer, if such a layer exists + uint32_t layers_above_shell[] = { + ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, + ZWLR_LAYER_SHELL_V1_LAYER_TOP, + }; + size_t nlayers = LENGTH(layers_above_shell); + LayerSurface *layersurface; + struct wlr_keyboard *kb = wlr_seat_get_keyboard(seat); for (size_t i = 0; i < nlayers; ++i) { wl_list_for_each_reverse(layersurface, &m->layers[layers_above_shell[i]], link) { @@ -640,21 +647,19 @@ void buttonpress(struct wl_listener *listener, void *data) { struct wlr_event_pointer_button *event = data; - struct wlr_keyboard *keyboard; - uint32_t mods; - Client *c; - const Button *b; wlr_idle_notify_activity(idle, seat); switch (event->state) { case WLR_BUTTON_PRESSED:; /* Change focus if the button was _pressed_ over a client */ + Client *c; if ((c = xytoclient(cursor->x, cursor->y))) focusclient(c, 1); - keyboard = wlr_seat_get_keyboard(seat); - mods = wlr_keyboard_get_modifiers(keyboard); + struct wlr_keyboard* keyboard = wlr_seat_get_keyboard(seat); + uint32_t mods = wlr_keyboard_get_modifiers(keyboard); + const Button *b; for (b = buttons; b < END(buttons); b++) { if (CLEANMASK(mods) == CLEANMASK(b->mod) && event->button == b->button && b->func) { @@ -721,7 +726,6 @@ cleanupmon(struct wl_listener *listener, void *data) { struct wlr_output *wlr_output = data; Monitor *m = wlr_output->data; - int nmons = wl_list_length(&mons), i = 0; wl_list_remove(&m->destroy.link); wl_list_remove(&m->frame.link); @@ -729,6 +733,7 @@ cleanupmon(struct wl_listener *listener, void *data) wlr_output_layout_remove(output_layout, m->wlr_output); updatemons(); + int nmons = wl_list_length(&mons), i = 0; do // don't switch to disabled mons selmon = wl_container_of(mons.prev, selmon, link); while (!selmon->wlr_output->enabled && i++ < nmons); @@ -758,12 +763,11 @@ 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; - Monitor *m; if (!wlr_output) return; - m = wlr_output->data; + Monitor *m = wlr_output->data; arrangelayers(m); if (layersurface->layer != wlr_layer_surface->current.layer) { @@ -787,16 +791,12 @@ commitnotify(struct wl_listener *listener, void *data) void createkeyboard(struct wlr_input_device *device) { - struct xkb_context *context; - struct xkb_keymap *keymap; - Keyboard *kb; - - kb = device->data = calloc(1, sizeof(*kb)); + Keyboard *kb = device->data = calloc(1, sizeof(*kb)); kb->device = device; /* Prepare an XKB keymap and assign it to the keyboard. */ - context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - keymap = xkb_map_new_from_names(context, &xkb_rules, + struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + struct xkb_keymap *keymap = xkb_map_new_from_names(context, &xkb_rules, XKB_KEYMAP_COMPILE_NO_FLAGS); wlr_keyboard_set_keymap(device->keyboard, keymap); @@ -824,9 +824,6 @@ createmon(struct wl_listener *listener, void *data) /* This event is raised by the backend when a new output (aka a display or * monitor) becomes available. */ struct wlr_output *wlr_output = data; - Monitor *m; - const MonitorRule *r; - size_t nlayers = LENGTH(m->layers); /* The mode is a tuple of (width, height, refresh rate), and each * monitor supports only a specific set of modes. We just pick the @@ -835,9 +832,10 @@ createmon(struct wl_listener *listener, void *data) wlr_output_set_mode(wlr_output, wlr_output_preferred_mode(wlr_output)); /* Allocates and configures monitor state using configured rules */ - m = wlr_output->data = calloc(1, sizeof(*m)); + Monitor *m = wlr_output->data = calloc(1, sizeof(*m)); m->wlr_output = wlr_output; m->tagset[0] = m->tagset[1] = 1; + const MonitorRule *r; for (r = monrules; r < END(monrules); r++) { if (!r->name || strstr(wlr_output->name, r->name)) { m->mfact = r->mfact; @@ -873,6 +871,7 @@ createmon(struct wl_listener *listener, void *data) */ wlr_output_layout_add_auto(output_layout, wlr_output); + size_t nlayers = LENGTH(m->layers); for (size_t i = 0; i < nlayers; ++i) wl_list_init(&m->layers[i]); @@ -932,15 +931,12 @@ void createlayersurface(struct wl_listener *listener, void *data) { struct wlr_layer_surface_v1 *wlr_layer_surface = data; - LayerSurface *layersurface; - Monitor *m; - struct wlr_layer_surface_v1_state old_state; if (!wlr_layer_surface->output) { wlr_layer_surface->output = selmon->wlr_output; } - layersurface = calloc(1, sizeof(LayerSurface)); + LayerSurface *layersurface = calloc(1, sizeof(LayerSurface)); layersurface->surface_commit.notify = commitlayersurfacenotify; wl_signal_add(&wlr_layer_surface->surface->events.commit, @@ -955,14 +951,14 @@ createlayersurface(struct wl_listener *listener, void *data) layersurface->layer_surface = wlr_layer_surface; wlr_layer_surface->data = layersurface; - m = wlr_layer_surface->output->data; + Monitor *m = wlr_layer_surface->output->data; wl_list_insert(&m->layers[wlr_layer_surface->client_pending.layer], &layersurface->link); // Temporarily set the layer's current state to client_pending // so that we can easily arrange it - old_state = wlr_layer_surface->current; + struct wlr_layer_surface_v1_state old_state = wlr_layer_surface->current; wlr_layer_surface->current = wlr_layer_surface->client_pending; arrangelayers(m); wlr_layer_surface->current = old_state; @@ -1016,7 +1012,6 @@ void destroylayersurfacenotify(struct wl_listener *listener, void *data) { LayerSurface *layersurface = wl_container_of(listener, layersurface, destroy); - Monitor *m; if (layersurface->layer_surface->mapped) unmaplayersurface(layersurface); @@ -1026,7 +1021,7 @@ destroylayersurfacenotify(struct wl_listener *listener, void *data) wl_list_remove(&layersurface->unmap.link); wl_list_remove(&layersurface->surface_commit.link); if (layersurface->layer_surface->output) { - m = layersurface->layer_surface->output->data; + Monitor *m = layersurface->layer_surface->output->data; if (m) arrangelayers(m); layersurface->layer_surface->output = NULL; @@ -1114,15 +1109,14 @@ dirtomon(int dir) void focusclient(Client *c, int lift) { - struct wlr_keyboard *kb = wlr_seat_get_keyboard(seat); - struct wlr_surface *old = seat->keyboard_state.focused_surface; - /* Raise client in stacking order if requested */ if (c && lift) { wl_list_remove(&c->slink); wl_list_insert(&stack, &c->slink); } + struct wlr_surface *old = seat->keyboard_state.focused_surface; + /* Nothing else to do? */ if (c && WLR_SURFACE(c) == old) return; @@ -1147,6 +1141,7 @@ focusclient(Client *c, int lift) } /* Have a client, so focus its top-level wlr_surface */ + struct wlr_keyboard *kb = wlr_seat_get_keyboard(seat); if (shouldfocusclients(c->mon)) wlr_seat_keyboard_notify_enter(seat, WLR_SURFACE(c), kb->keycodes, kb->num_keycodes, &kb->modifiers); @@ -1231,7 +1226,6 @@ inputdevice(struct wl_listener *listener, void *data) /* This event is raised by the backend when a new input device becomes * available. */ struct wlr_input_device *device = data; - uint32_t caps; switch (device->type) { case WLR_INPUT_DEVICE_KEYBOARD: createkeyboard(device); @@ -1247,7 +1241,7 @@ inputdevice(struct wl_listener *listener, void *data) * communiciated to the client. In dwl we always have a cursor, even if * there are no pointer devices, so we always include that capability. */ /* XXX do we actually require a cursor? */ - caps = WL_SEAT_CAPABILITY_POINTER; + uint32_t caps = WL_SEAT_CAPABILITY_POINTER; if (!wl_list_empty(&keyboards)) caps |= WL_SEAT_CAPABILITY_KEYBOARD; wlr_seat_set_capabilities(seat, caps); @@ -1279,7 +1273,6 @@ keypress(struct wl_listener *listener, void *data) /* This event is raised when a key is pressed or released. */ Keyboard *kb = wl_container_of(listener, kb, key); struct wlr_event_keyboard_key *event = data; - int i; /* Translate libinput keycode -> xkbcommon */ uint32_t keycode = event->keycode + 8; @@ -1295,7 +1288,7 @@ keypress(struct wl_listener *listener, void *data) /* On _press_, attempt to process a compositor keybinding. */ if (event->state == WLR_KEY_PRESSED) - for (i = 0; i < nsyms; i++) + for (int i = 0; i < nsyms; i++) handled = keybinding(mods, syms[i]) || handled; if (!handled) { @@ -1417,9 +1410,6 @@ motionabsolute(struct wl_listener *listener, void *data) void motionnotify(uint32_t time) { - double sx = 0, sy = 0; - struct wlr_surface *surface = NULL; - Client *c = NULL; struct timespec now; if (!time) { clock_gettime(CLOCK_MONOTONIC, &now); @@ -1445,6 +1435,9 @@ motionnotify(uint32_t time) return; } + struct wlr_surface *surface = NULL; + double sx = 0, sy = 0; + Client *c = NULL; if ((surface = xytolayersurface(&selmon->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], cursor->x, cursor->y, &sx, &sy))) ; @@ -1545,7 +1538,6 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test) { struct wlr_output_configuration_head_v1 *config_head; bool ok = true; - Arg ar = {.i = -1}; wl_list_for_each(config_head, &config->heads, link) { struct wlr_output *wlr_output = config_head->state.output; @@ -1570,6 +1562,7 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test) if (m->wlr_output->name == wlr_output->name) { // focus the left monitor (relative to the current focus) m->wlr_output->enabled = !m->wlr_output->enabled; + Arg ar = {.i = -1}; focusmon(&ar); closemon(m); m->wlr_output->enabled = !m->wlr_output->enabled; @@ -1647,10 +1640,6 @@ render(struct wlr_surface *surface, int sx, int sy, void *data) /* This function is called for every surface that needs to be rendered. */ struct render_data *rdata = data; struct wlr_output *output = rdata->output; - double ox = 0, oy = 0; - struct wlr_box obox; - float matrix[9]; - enum wl_output_transform transform; /* We first obtain a wlr_texture, which is a GPU resource. wlroots * automatically handles negotiating these with the client. The underlying @@ -1665,14 +1654,17 @@ render(struct wlr_surface *surface, int sx, int sy, void *data) * one next to the other, both 1080p, a client on the rightmost display might * have layout coordinates of 2000,100. We need to translate that to * output-local coordinates, or (2000 - 1920). */ + double ox = 0, oy = 0; wlr_output_layout_output_coords(output_layout, output, &ox, &oy); /* We also have to apply the scale factor for HiDPI outputs. This is only * part of the puzzle, dwl does not fully support HiDPI. */ - obox.x = ox + rdata->x + sx; - obox.y = oy + rdata->y + sy; - obox.width = surface->current.width; - obox.height = surface->current.height; + struct wlr_box obox = { + .x = ox + rdata->x + sx, + .y = oy + rdata->y + sy, + .width = surface->current.width, + .height = surface->current.height, + }; scalebox(&obox, output->scale); /* @@ -1686,7 +1678,9 @@ render(struct wlr_surface *surface, int sx, int sy, void *data) * Naturally you can do this any way you like, for example to make a 3D * compositor. */ - transform = wlr_output_transform_invert(surface->current.transform); + float matrix[9]; + enum wl_output_transform transform = wlr_output_transform_invert( + surface->current.transform); wlr_matrix_project_box(matrix, &obox, transform, 0, output->transform_matrix); @@ -1703,12 +1697,6 @@ void renderclients(Monitor *m, struct timespec *now) { Client *c, *sel = selclient(); - const float *color; - double ox, oy; - int i, w, h; - struct render_data rdata; - struct wlr_box *borders; - struct wlr_surface *surface; /* Each subsequent window we render is rendered on top of the last. Because * our stacking list is ordered front-to-back, we iterate over it backwards. */ wl_list_for_each_reverse(c, &stack, slink) { @@ -1717,14 +1705,14 @@ renderclients(Monitor *m, struct timespec *now) output_layout, m->wlr_output, &c->geom)) continue; - surface = WLR_SURFACE(c); - ox = c->geom.x, oy = c->geom.y; + struct wlr_surface *surface = WLR_SURFACE(c); + double ox = c->geom.x, oy = c->geom.y; wlr_output_layout_output_coords(output_layout, m->wlr_output, &ox, &oy); - w = surface->current.width; - h = surface->current.height; - borders = (struct wlr_box[4]) { + int w = surface->current.width; + int h = surface->current.height; + struct wlr_box *borders = (struct wlr_box[4]) { {ox, oy, w + 2 * c->bw, c->bw}, /* top */ {ox, oy + c->bw, c->bw, h}, /* left */ {ox + c->bw + w, oy + c->bw, c->bw, h}, /* right */ @@ -1732,8 +1720,8 @@ renderclients(Monitor *m, struct timespec *now) }; /* Draw window borders */ - color = (c == sel) ? focuscolor : bordercolor; - for (i = 0; i < 4; i++) { + const float *color = (c == sel) ? focuscolor : bordercolor; + for (int i = 0; i < 4; i++) { scalebox(&borders[i], m->wlr_output->scale); wlr_render_rect(drw, &borders[i], color, m->wlr_output->transform_matrix); @@ -1741,10 +1729,12 @@ renderclients(Monitor *m, struct timespec *now) /* This calls our render function for each surface among the * xdg_surface's toplevel and popups. */ - rdata.output = m->wlr_output; - rdata.when = now; - rdata.x = c->geom.x + c->bw; - rdata.y = c->geom.y + c->bw; + struct render_data rdata = { + .output = m->wlr_output, + .when = now, + .x = c->geom.x + c->bw, + .y = c->geom.y + c->bw, + }; #ifdef XWAYLAND if (c->type != XDGShell) wlr_surface_for_each_surface(c->surface.xwayland->surface, render, &rdata); @@ -1757,13 +1747,14 @@ renderclients(Monitor *m, struct timespec *now) void renderlayer(struct wl_list *layer_surfaces, struct timespec *now) { - struct render_data rdata; LayerSurface *layersurface; wl_list_for_each(layersurface, layer_surfaces, link) { - rdata.output = layersurface->layer_surface->output; - rdata.when = now; - rdata.x = layersurface->geo.x; - rdata.y = layersurface->geo.y; + struct render_data rdata = { + .output = layersurface->layer_surface->output, + .when = now, + .x = layersurface->geo.x, + .y = layersurface->geo.y, + }; wlr_surface_for_each_surface(layersurface->layer_surface->surface, render, &rdata); @@ -1773,9 +1764,6 @@ renderlayer(struct wl_list *layer_surfaces, struct timespec *now) void rendermon(struct wl_listener *listener, void *data) { - Client *c; - int render = 1; - /* This function is called every time an output is ready to display a frame, * generally at the output's refresh rate (e.g. 60Hz). */ Monitor *m = wl_container_of(listener, m, frame); @@ -1784,6 +1772,8 @@ rendermon(struct wl_listener *listener, void *data) clock_gettime(CLOCK_MONOTONIC, &now); /* Do not render if any XDG clients have an outstanding resize. */ + Client *c; + int render = 1; wl_list_for_each(c, &stack, slink) { if (c->resize) { wlr_surface_send_frame_done(WLR_SURFACE(c), &now); @@ -1833,11 +1823,11 @@ resize(Client *c, int x, int y, int w, int h, int interact) * compositor, you'd wait for the client to prepare a buffer at * the new size, then commit any movement that was prepared. */ - struct wlr_box *bbox = interact ? &sgeom : &c->mon->w; c->geom.x = x; c->geom.y = y; c->geom.width = w; c->geom.height = h; + struct wlr_box *bbox = interact ? &sgeom : &c->mon->w; applybounds(c, bbox); /* wlroots makes this a no-op if size hasn't changed */ #ifdef XWAYLAND @@ -1854,8 +1844,6 @@ resize(Client *c, int x, int y, int w, int h, int interact) void run(char *startup_cmd) { - pid_t startup_pid = -1; - /* Add a Unix socket to the Wayland display. */ const char *socket = wl_display_add_socket_auto(dpy); if (!socket) @@ -1880,8 +1868,10 @@ run(char *startup_cmd) /* Set the WAYLAND_DISPLAY environment variable to our socket and run the * startup command if requested. */ setenv("WAYLAND_DISPLAY", socket, 1); + + pid_t startup_pid = -1; if (startup_cmd) { - startup_pid = fork(); + pid_t startup_pid = fork(); if (startup_pid < 0) EBARF("startup: fork"); if (startup_pid == 0) { @@ -1889,6 +1879,7 @@ run(char *startup_cmd) EBARF("startup: execl"); } } + /* Run the Wayland event loop. This does not return until you exit the * compositor. Starting the backend rigged up all of the necessary event * loop configuration to listen to libinput events, DRM events, generate @@ -1962,11 +1953,9 @@ setlayout(const Arg *arg) void setmfact(const Arg *arg) { - float f; - if (!arg || !selmon->lt[selmon->sellt]->arrange) return; - f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; + float f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; if (f < 0.1 || f > 0.9) return; selmon->mfact = f; @@ -2271,11 +2260,10 @@ togglefloating(const Arg *arg) void toggletag(const Arg *arg) { - unsigned int newtags; Client *sel = selclient(); if (!sel) return; - newtags = sel->tags ^ (arg->ui & TAGMASK); + unsigned int newtags = sel->tags ^ (arg->ui & TAGMASK); if (newtags) { sel->tags = newtags; focusclient(focustop(selmon), 1); @@ -2469,13 +2457,13 @@ configurex11(struct wl_listener *listener, void *data) void createnotifyx11(struct wl_listener *listener, void *data) { - struct wlr_xwayland_surface *xwayland_surface = data; Client *c; wl_list_for_each(c, &clients, link) if (c->isfullscreen && VISIBLEON(c, c->mon)) setfullscreen(c, 0); /* Allocate a Client for this surface */ + struct wlr_xwayland_surface *xwayland_surface = data; c = xwayland_surface->data = calloc(1, sizeof(*c)); c->surface.xwayland = xwayland_surface; c->type = xwayland_surface->override_redirect ? X11Unmanaged : X11Managed; @@ -2502,10 +2490,8 @@ Atom getatom(xcb_connection_t *xc, const char *name) { Atom atom = 0; - xcb_intern_atom_cookie_t cookie; xcb_intern_atom_reply_t *reply; - - cookie = xcb_intern_atom(xc, 0, strlen(name), name); + xcb_intern_atom_cookie_t cookie = xcb_intern_atom(xc, 0, strlen(name), name); if ((reply = xcb_intern_atom_reply(xc, cookie, NULL))) atom = reply->atom; free(reply); @@ -2517,23 +2503,25 @@ void renderindependents(struct wlr_output *output, struct timespec *now) { Client *c; - struct render_data rdata; - struct wlr_box geom; wl_list_for_each_reverse(c, &independents, link) { - geom.x = c->surface.xwayland->x; - geom.y = c->surface.xwayland->y; - geom.width = c->surface.xwayland->width; - geom.height = c->surface.xwayland->height; + struct wlr_box geom = { + .x = c->surface.xwayland->x, + .y = c->surface.xwayland->y, + .width = c->surface.xwayland->width, + .height = c->surface.xwayland->height, + }; /* Only render visible clients which show on this output */ if (!wlr_output_layout_intersects(output_layout, output, &geom)) continue; - rdata.output = output; - rdata.when = now; - rdata.x = c->surface.xwayland->x; - rdata.y = c->surface.xwayland->y; + struct render_data rdata = { + .output = output, + .when = now, + .x = c->surface.xwayland->x, + .y = c->surface.xwayland->y, + }; wlr_surface_for_each_surface(c->surface.xwayland->surface, render, &rdata); } @@ -2542,8 +2530,7 @@ renderindependents(struct wlr_output *output, struct timespec *now) void updatewindowtype(Client *c) { - size_t i; - for (i = 0; i < c->surface.xwayland->window_type_len; i++) + for (size_t i = 0; i < c->surface.xwayland->window_type_len; i++) if (c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeDialog] || c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeSplash] || c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeToolbar] || @@ -2583,12 +2570,13 @@ xytoindependent(double x, double y) * client loses focus, which ensures that unmanaged are only visible on * the current tag. */ Client *c; - struct wlr_box geom; wl_list_for_each_reverse(c, &independents, link) { - geom.x = c->surface.xwayland->x; - geom.y = c->surface.xwayland->y; - geom.width = c->surface.xwayland->width; - geom.height = c->surface.xwayland->height; + struct wlr_box geom = { + .x = c->surface.xwayland->x, + .y = c->surface.xwayland->y, + .width = c->surface.xwayland->width, + .height = c->surface.xwayland->height, + }; if (wlr_box_contains_point(&geom, x, y)) return c; } From 6b47e2bb621dfba333fb9bd9839b8128c88e58c2 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sun, 20 Dec 2020 13:51:40 +0100 Subject: [PATCH 63/78] use bool Because it's 2020. Passing integers to wlroots variables and functions with bool in their signature is silly. --- config.def.h | 6 +-- dwl.c | 104 ++++++++++++++++++++++++++------------------------- 2 files changed, 56 insertions(+), 54 deletions(-) diff --git a/config.def.h b/config.def.h index fc30ba8..5e33204 100644 --- a/config.def.h +++ b/config.def.h @@ -1,5 +1,5 @@ /* appearance */ -static const int sloppyfocus = 1; /* focus follows mouse */ +static const bool sloppyfocus = true; /* focus follows mouse */ static const unsigned int borderpx = 1; /* border pixel of windows */ static const float rootcolor[] = {0.3, 0.3, 0.3, 1.0}; static const float bordercolor[] = {0.5, 0.5, 0.5, 1.0}; @@ -46,8 +46,8 @@ static const int repeat_rate = 25; static const int repeat_delay = 600; /* Trackpad */ -static const int tap_to_click = 1; -static const int natural_scrolling = 1; +static const bool tap_to_click = true; +static const bool natural_scrolling = true; #define MODKEY WLR_MODIFIER_ALT #define TAGKEYS(KEY,SKEY,TAG) \ diff --git a/dwl.c b/dwl.c index a4f8b6d..39c2bf1 100644 --- a/dwl.c +++ b/dwl.c @@ -113,13 +113,13 @@ typedef struct { #endif int bw; unsigned int tags; - int isfloating; + bool isfloating; uint32_t resize; /* configure serial of a pending resize */ int oldx; int oldy; int oldwidth; int oldheight; - int isfullscreen; + bool isfullscreen; } Client; typedef struct { @@ -190,7 +190,7 @@ typedef struct { const char *id; const char *title; unsigned int tags; - int isfloating; + bool isfloating; int monitor; } Rule; @@ -232,7 +232,7 @@ static void destroylayersurfacenotify(struct wl_listener *listener, void *data); static void destroynotify(struct wl_listener *listener, void *data); static void destroyxdeco(struct wl_listener *listener, void *data); static Monitor *dirtomon(int dir); -static void focusclient(Client *c, int lift); +static void focusclient(Client *c, bool lift); static void focusmon(const Arg *arg); static void focusstack(const Arg *arg); static void fullscreennotify(struct wl_listener *listener, void *data); @@ -240,7 +240,7 @@ static Client *focustop(Monitor *m); static void getxdecomode(struct wl_listener *listener, void *data); static void incnmaster(const Arg *arg); static void inputdevice(struct wl_listener *listener, void *data); -static int keybinding(uint32_t mods, xkb_keysym_t sym); +static bool keybinding(uint32_t mods, xkb_keysym_t sym); static void keypress(struct wl_listener *listener, void *data); static void keypressmod(struct wl_listener *listener, void *data); static void killclient(const Arg *arg); @@ -261,15 +261,15 @@ static void render(struct wlr_surface *surface, int sx, int sy, void *data); static void renderclients(Monitor *m, struct timespec *now); static void renderlayer(struct wl_list *layer_surfaces, struct timespec *now); static void rendermon(struct wl_listener *listener, void *data); -static void resize(Client *c, int x, int y, int w, int h, int interact); +static void resize(Client *c, int x, int y, int w, int h, bool interact); static void run(char *startup_cmd); static void scalebox(struct wlr_box *box, float scale); static Client *selclient(void); static void setcursor(struct wl_listener *listener, void *data); static void setpsel(struct wl_listener *listener, void *data); static void setsel(struct wl_listener *listener, void *data); -static void setfloating(Client *c, int floating); -static void setfullscreen(Client *c, int fullscreen); +static void setfloating(Client *c, bool floating); +static void setfullscreen(Client *c, bool fullscreen); static void setlayout(const Arg *arg); static void setmfact(const Arg *arg); static void setmon(Client *c, Monitor *m, unsigned int newtags); @@ -655,7 +655,7 @@ buttonpress(struct wl_listener *listener, void *data) /* Change focus if the button was _pressed_ over a client */ Client *c; if ((c = xytoclient(cursor->x, cursor->y))) - focusclient(c, 1); + focusclient(c, true); struct wlr_keyboard* keyboard = wlr_seat_get_keyboard(seat); uint32_t mods = wlr_keyboard_get_modifiers(keyboard); @@ -737,7 +737,7 @@ cleanupmon(struct wl_listener *listener, void *data) do // don't switch to disabled mons selmon = wl_container_of(mons.prev, selmon, link); while (!selmon->wlr_output->enabled && i++ < nmons); - focusclient(focustop(selmon), 1); + focusclient(focustop(selmon), true); closemon(m); free(m); } @@ -751,7 +751,7 @@ closemon(Monitor *m) wl_list_for_each(c, &clients, link) { if (c->isfloating && c->geom.x > m->m.width) resize(c, c->geom.x - m->w.width, c->geom.y, - c->geom.width, c->geom.height, 0); + c->geom.width, c->geom.height, false); if (c->mon == m) setmon(c, selmon, c->tags); } @@ -856,7 +856,7 @@ createmon(struct wl_listener *listener, void *data) wl_list_insert(&mons, &m->link); - wlr_output_enable(wlr_output, 1); + wlr_output_enable(wlr_output, true); if (!wlr_output_commit(wlr_output)) return; @@ -883,7 +883,7 @@ createmon(struct wl_listener *listener, void *data) wl_list_for_each(c, &clients, link) { if (c->isfloating) resize(c, c->geom.x + m->w.width, c->geom.y, - c->geom.width, c->geom.height, 0); + c->geom.width, c->geom.height, false); } return; } @@ -901,7 +901,7 @@ createnotify(struct wl_listener *listener, void *data) return; wl_list_for_each(c, &clients, link) if (c->isfullscreen && VISIBLEON(c, c->mon)) - setfullscreen(c, 0); + setfullscreen(c, false); /* Allocate a Client for this surface */ c = xdg_surface->data = calloc(1, sizeof(*c)); @@ -924,7 +924,7 @@ createnotify(struct wl_listener *listener, void *data) c->fullscreen.notify = fullscreennotify; wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, &c->fullscreen); - c->isfullscreen = 0; + c->isfullscreen = false; } void @@ -1059,7 +1059,7 @@ destroyxdeco(struct wl_listener *listener, void *data) } void -setfullscreen(Client *c, int fullscreen) +setfullscreen(Client *c, bool fullscreen) { c->isfullscreen = fullscreen; c->bw = (1 - fullscreen) * borderpx; @@ -1077,9 +1077,10 @@ setfullscreen(Client *c, int fullscreen) c->oldy = c->geom.y; c->oldheight = c->geom.height; c->oldwidth = c->geom.width; - resize(c, c->mon->m.x, c->mon->m.y, c->mon->m.width, c->mon->m.height, 0); + resize(c, c->mon->m.x, c->mon->m.y, + c->mon->m.width, c->mon->m.height, false); } else { - resize(c, c->oldx, c->oldy, c->oldwidth, c->oldheight, 0); + resize(c, c->oldx, c->oldy, c->oldwidth, c->oldheight, false); } } @@ -1107,7 +1108,7 @@ dirtomon(int dir) } void -focusclient(Client *c, int lift) +focusclient(Client *c, bool lift) { /* Raise client in stacking order if requested */ if (c && lift) { @@ -1154,10 +1155,10 @@ focusclient(Client *c, int lift) /* Activate the new client */ #ifdef XWAYLAND if (c->type != XDGShell) - wlr_xwayland_surface_activate(c->surface.xwayland, 1); + wlr_xwayland_surface_activate(c->surface.xwayland, true); else #endif - wlr_xdg_toplevel_set_activated(c->surface.xdg, 1); + wlr_xdg_toplevel_set_activated(c->surface.xdg, true); } void @@ -1166,7 +1167,7 @@ focusmon(const Arg *arg) do selmon = dirtomon(arg->i); while (!selmon->wlr_output->enabled); - focusclient(focustop(selmon), 1); + focusclient(focustop(selmon), true); } void @@ -1192,7 +1193,7 @@ focusstack(const Arg *arg) } } /* If only one client is visible on selmon, then c == sel */ - focusclient(c, 1); + focusclient(c, true); } Client * @@ -1247,7 +1248,7 @@ inputdevice(struct wl_listener *listener, void *data) wlr_seat_set_capabilities(seat, caps); } -int +bool keybinding(uint32_t mods, xkb_keysym_t sym) { /* @@ -1255,13 +1256,13 @@ keybinding(uint32_t mods, xkb_keysym_t sym) * processing keys, rather than passing them on to the client for its own * processing. */ - int handled = 0; + bool handled = false; const Key *k; for (k = keys; k < END(keys); k++) { if (CLEANMASK(mods) == CLEANMASK(k->mod) && sym == k->keysym && k->func) { k->func(&k->arg); - handled = 1; + handled = true; } } return handled; @@ -1281,7 +1282,7 @@ keypress(struct wl_listener *listener, void *data) int nsyms = xkb_state_key_get_syms( kb->device->keyboard->xkb_state, keycode, &syms); - int handled = 0; + bool handled = false; uint32_t mods = wlr_keyboard_get_modifiers(kb->device->keyboard); wlr_idle_notify_activity(idle, seat); @@ -1386,10 +1387,10 @@ monocle(Monitor *m) if (!VISIBLEON(c, m) || c->isfloating) continue; if (c->isfullscreen) { - resize(c, c->mon->m.x, c->mon->m.y, c->mon->m.width, c->mon->m.height, 0); + resize(c, c->mon->m.x, c->mon->m.y, c->mon->m.width, c->mon->m.height, false); return; } - resize(c, m->w.x, m->w.y, m->w.width, m->w.height, 0); + resize(c, m->w.x, m->w.y, m->w.width, m->w.height, false); } } @@ -1426,12 +1427,12 @@ motionnotify(uint32_t time) if (cursor_mode == CurMove) { /* Move the grabbed client to the new position. */ resize(grabc, cursor->x - grabcx, cursor->y - grabcy, - grabc->geom.width, grabc->geom.height, 1); + grabc->geom.width, grabc->geom.height, true); return; } else if (cursor_mode == CurResize) { resize(grabc, grabc->geom.x, grabc->geom.y, cursor->x - grabc->geom.x, - cursor->y - grabc->geom.y, 1); + cursor->y - grabc->geom.y, true); return; } @@ -1507,7 +1508,7 @@ moveresize(const Arg *arg) return; /* Float the window and tell motionnotify to grab it */ - setfloating(grabc, 1); + setfloating(grabc, true); switch (cursor_mode = arg->ui) { case CurMove: grabcx = cursor->x - grabc->geom.x; @@ -1625,7 +1626,7 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, #endif if (sloppyfocus) - focusclient(c, 0); + focusclient(c, false); } void @@ -1773,11 +1774,11 @@ rendermon(struct wl_listener *listener, void *data) /* Do not render if any XDG clients have an outstanding resize. */ Client *c; - int render = 1; + bool render = true; wl_list_for_each(c, &stack, slink) { if (c->resize) { wlr_surface_send_frame_done(WLR_SURFACE(c), &now); - render = 0; + render = false; } } @@ -1816,7 +1817,7 @@ rendermon(struct wl_listener *listener, void *data) } void -resize(Client *c, int x, int y, int w, int h, int interact) +resize(Client *c, int x, int y, int w, int h, bool interact) { /* * Note that I took some shortcuts here. In a more fleshed-out @@ -1930,7 +1931,7 @@ setcursor(struct wl_listener *listener, void *data) } void -setfloating(Client *c, int floating) +setfloating(Client *c, bool floating) { if (c->isfloating == floating) return; @@ -1983,7 +1984,7 @@ setmon(Client *c, Monitor *m, unsigned int newtags) c->tags = newtags ? newtags : m->tagset[m->seltags]; /* assign tags of target monitor */ arrange(m); } - focusclient(focustop(selmon), 1); + focusclient(focustop(selmon), true); } void @@ -2196,7 +2197,7 @@ tag(const Arg *arg) Client *sel = selclient(); if (sel && arg->ui & TAGMASK) { sel->tags = arg->ui & TAGMASK; - focusclient(focustop(selmon), 1); + focusclient(focustop(selmon), true); arrange(selmon); } } @@ -2231,16 +2232,17 @@ tile(Monitor *m) if (!VISIBLEON(c, m) || c->isfloating) continue; if (c->isfullscreen) { - resize(c, c->mon->m.x, c->mon->m.y, c->mon->m.width, c->mon->m.height, 0); + resize(c, c->mon->m.x, c->mon->m.y, + c->mon->m.width, c->mon->m.height, false); return; } if (i < m->nmaster) { h = (m->w.height - my) / (MIN(n, m->nmaster) - i); - resize(c, m->w.x, m->w.y + my, mw, h, 0); + resize(c, m->w.x, m->w.y + my, mw, h, false); my += c->geom.height; } else { h = (m->w.height - ty) / (n - i); - resize(c, m->w.x + mw, m->w.y + ty, m->w.width - mw, h, 0); + resize(c, m->w.x + mw, m->w.y + ty, m->w.width - mw, h, false); ty += c->geom.height; } i++; @@ -2266,7 +2268,7 @@ toggletag(const Arg *arg) unsigned int newtags = sel->tags ^ (arg->ui & TAGMASK); if (newtags) { sel->tags = newtags; - focusclient(focustop(selmon), 1); + focusclient(focustop(selmon), true); arrange(selmon); } } @@ -2278,7 +2280,7 @@ toggleview(const Arg *arg) if (newtagset) { selmon->tagset[selmon->seltags] = newtagset; - focusclient(focustop(selmon), 1); + focusclient(focustop(selmon), true); arrange(selmon); } } @@ -2289,7 +2291,7 @@ unmaplayersurface(LayerSurface *layersurface) layersurface->layer_surface->mapped = false; if (layersurface->layer_surface->surface == seat->keyboard_state.focused_surface) - focusclient(selclient(), 1); + focusclient(selclient(), true); motionnotify(0); } @@ -2350,7 +2352,7 @@ view(const Arg *arg) selmon->seltags ^= 1; /* toggle sel tagset */ if (arg->ui & TAGMASK) selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; - focusclient(focustop(selmon), 1); + focusclient(focustop(selmon), true); arrange(selmon); } @@ -2430,7 +2432,7 @@ zoom(const Arg *arg) wl_list_remove(&sel->link); wl_list_insert(&clients, &sel->link); - focusclient(sel, 1); + focusclient(sel, true); arrange(selmon); } @@ -2442,7 +2444,7 @@ activatex11(struct wl_listener *listener, void *data) /* Only "managed" windows can be activated */ if (c->type == X11Managed) - wlr_xwayland_surface_activate(c->surface.xwayland, 1); + wlr_xwayland_surface_activate(c->surface.xwayland, true); } void @@ -2460,7 +2462,7 @@ createnotifyx11(struct wl_listener *listener, void *data) Client *c; wl_list_for_each(c, &clients, link) if (c->isfullscreen && VISIBLEON(c, c->mon)) - setfullscreen(c, 0); + setfullscreen(c, false); /* Allocate a Client for this surface */ struct wlr_xwayland_surface *xwayland_surface = data; @@ -2483,7 +2485,7 @@ createnotifyx11(struct wl_listener *listener, void *data) c->fullscreen.notify = fullscreennotify; wl_signal_add(&xwayland_surface->events.request_fullscreen, &c->fullscreen); - c->isfullscreen = 0; + c->isfullscreen = false; } Atom @@ -2535,7 +2537,7 @@ updatewindowtype(Client *c) c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeSplash] || c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeToolbar] || c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeUtility]) - c->isfloating = 1; + c->isfloating = true; } void From 444a5f9dec52161f61d8a42644f7b6625d9aabcd Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Fri, 11 Dec 2020 10:55:33 +0100 Subject: [PATCH 64/78] enable adaptive sync The comment in this function's declaration says the backend is free to ignore this setting, so maybe there's no need to make it configurable? --- dwl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dwl.c b/dwl.c index 39c2bf1..773e04a 100644 --- a/dwl.c +++ b/dwl.c @@ -848,6 +848,7 @@ createmon(struct wl_listener *listener, void *data) break; } } + wlr_output_enable_adaptive_sync(wlr_output, true); /* Set up event listeners */ m->frame.notify = rendermon; wl_signal_add(&wlr_output->events.frame, &m->frame); From f80f08848b8b7e08ed2f20c7f0a8675a3b4df394 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sun, 20 Dec 2020 14:29:45 +0100 Subject: [PATCH 65/78] ensure that xwayland cursor defaults to left_ptr Don't show an X cursor when closing an Xwayland window or with certain dropdowns. Based on https://github.com/djpohly/dwl/pull/32 --- dwl.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dwl.c b/dwl.c index 773e04a..7a12817 100644 --- a/dwl.c +++ b/dwl.c @@ -2561,6 +2561,13 @@ xwaylandready(struct wl_listener *listener, void *data) /* assign the one and only seat */ wlr_xwayland_set_seat(xwayland, seat); + /* Set the default XWayland cursor to match the rest of dwl. */ + struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor(cursor_mgr, "left_ptr", 1); + wlr_xwayland_set_cursor(xwayland, + xcursor->images[0]->buffer, xcursor->images[0]->width * 4, + xcursor->images[0]->width, xcursor->images[0]->height, + xcursor->images[0]->hotspot_x, xcursor->images[0]->hotspot_y); + xcb_disconnect(xc); } From 39946e07f2b2ac09cfa622668585f2223fc277d4 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sun, 20 Dec 2020 15:34:36 +0100 Subject: [PATCH 66/78] fix keyboard focus with overlays Don't let internal calls to motionnotify(0) meant to update the pointer focus from maplayersurfacenotify and destroylayersurfacenotify also shift the keyboard focus to the surface under the cursor with sloppyfocus. --- dwl.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/dwl.c b/dwl.c index 7a12817..46ba04a 100644 --- a/dwl.c +++ b/dwl.c @@ -1412,12 +1412,6 @@ motionabsolute(struct wl_listener *listener, void *data) void motionnotify(uint32_t time) { - struct timespec now; - if (!time) { - clock_gettime(CLOCK_MONOTONIC, &now); - time = now.tv_sec * 1000 + now.tv_nsec / 1000000; - } - wlr_idle_notify_activity(idle, seat); /* Update selmon (even while dragging a window) */ @@ -1608,6 +1602,13 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, return; } + bool internal_call = !time; + if (!time) { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + time = now.tv_sec * 1000 + now.tv_nsec / 1000000; + } + /* If surface is already focused, only notify of motion */ if (surface == seat->pointer_state.focused_surface) { wlr_seat_pointer_notify_motion(seat, time, sx, sy); @@ -1626,7 +1627,7 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, return; #endif - if (sloppyfocus) + if (sloppyfocus && !internal_call) focusclient(c, false); } From a571ea465c545f662c4bc9899a31150e045074d0 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sun, 20 Dec 2020 15:19:34 +0100 Subject: [PATCH 67/78] replace shouldfocusclients with checking old And don't activate clients while an overlay is focused. --- dwl.c | 51 ++++++++++++++++++++++++--------------------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/dwl.c b/dwl.c index 46ba04a..888001c 100644 --- a/dwl.c +++ b/dwl.c @@ -275,7 +275,6 @@ static void setmfact(const Arg *arg); static void setmon(Client *c, Monitor *m, unsigned int newtags); static void setup(void); static void sigchld(int unused); -static bool shouldfocusclients(); static void spawn(const Arg *arg); static void tag(const Arg *arg); static void tagmon(const Arg *arg); @@ -1119,10 +1118,16 @@ focusclient(Client *c, bool lift) struct wlr_surface *old = seat->keyboard_state.focused_surface; - /* Nothing else to do? */ if (c && WLR_SURFACE(c) == old) return; + /* Put the new client atop the focus stack and select its monitor */ + if (c) { + wl_list_remove(&c->flink); + wl_list_insert(&fstack, &c->flink); + selmon = c->mon; + } + /* Deactivate old client if focus is changing */ if (old && (!c || WLR_SURFACE(c) != old)) { if (wlr_surface_is_xdg_surface(old)) @@ -1133,9 +1138,23 @@ focusclient(Client *c, bool lift) wlr_xwayland_surface_activate( wlr_xwayland_surface_from_wlr_surface(old), false); #endif + /* If an overlay is focused, don't focus or activate the client, + * but only update its position in fstack to render its border with focuscolor + * and focus it after the overlay is closed. + * It's probably pointless to check if old is a layer surface + * since it can't be anything else at this point. */ + else if (wlr_surface_is_layer_surface(old)) { + struct wlr_layer_surface_v1 *wlr_layer_surface = + wlr_layer_surface_v1_from_wlr_surface(old); + + if (wlr_layer_surface->mapped && ( + wlr_layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP || + wlr_layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY + )) + return; + } } - /* Update wlroots' keyboard focus */ if (!c) { /* With no client, all we have left is to clear focus */ wlr_seat_keyboard_notify_clear_focus(seat); @@ -1144,14 +1163,8 @@ focusclient(Client *c, bool lift) /* Have a client, so focus its top-level wlr_surface */ struct wlr_keyboard *kb = wlr_seat_get_keyboard(seat); - if (shouldfocusclients(c->mon)) - wlr_seat_keyboard_notify_enter(seat, WLR_SURFACE(c), - kb->keycodes, kb->num_keycodes, &kb->modifiers); - - /* Put the new client atop the focus stack and select its monitor */ - wl_list_remove(&c->flink); - wl_list_insert(&fstack, &c->flink); - selmon = c->mon; + wlr_seat_keyboard_notify_enter(seat, WLR_SURFACE(c), + kb->keycodes, kb->num_keycodes, &kb->modifiers); /* Activate the new client */ #ifdef XWAYLAND @@ -2167,22 +2180,6 @@ sigchld(int unused) ; } -bool -shouldfocusclients(Monitor *m) -{ - LayerSurface *layersurface; - uint32_t layers_above_shell[] = { - ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, - ZWLR_LAYER_SHELL_V1_LAYER_TOP, - }; - for (size_t i = 0; i < LENGTH(layers_above_shell); ++i) - wl_list_for_each(layersurface, &m->layers[layers_above_shell[i]], link) - if (layersurface->layer_surface->current.keyboard_interactive && - layersurface->layer_surface->mapped) - return false; - return true; -} - void spawn(const Arg *arg) { From 13c7e039bbd823df6ed8475c1ee1cc90a43d729a Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sun, 20 Dec 2020 15:55:52 +0100 Subject: [PATCH 68/78] deactivate the focused client on overlay focus --- dwl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dwl.c b/dwl.c index 888001c..4fdcc3a 100644 --- a/dwl.c +++ b/dwl.c @@ -621,6 +621,8 @@ arrangelayers(Monitor *m) &m->layers[layers_above_shell[i]], link) { if (layersurface->layer_surface->current.keyboard_interactive && layersurface->layer_surface->mapped) { + // Deactivate the focused client. + focusclient(NULL, false); wlr_seat_keyboard_notify_enter(seat, layersurface->layer_surface->surface, kb->keycodes, kb->num_keycodes, &kb->modifiers); return; From b161b5d8f4f635ad960d629fa2355c71c231e38a Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sun, 20 Dec 2020 16:04:13 +0100 Subject: [PATCH 69/78] don't notify of activity ...or update selmon when we just want to restore pointer focus. --- dwl.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/dwl.c b/dwl.c index 4fdcc3a..cbf0c11 100644 --- a/dwl.c +++ b/dwl.c @@ -1427,11 +1427,14 @@ motionabsolute(struct wl_listener *listener, void *data) void motionnotify(uint32_t time) { - wlr_idle_notify_activity(idle, seat); + // time is 0 in internal calls meant to restore pointer focus. + if (time) { + wlr_idle_notify_activity(idle, seat); - /* Update selmon (even while dragging a window) */ - if (sloppyfocus) - selmon = xytomon(cursor->x, cursor->y); + /* Update selmon (even while dragging a window) */ + if (sloppyfocus) + selmon = xytomon(cursor->x, cursor->y); + } /* If we are currently grabbing the mouse, handle and return */ if (cursor_mode == CurMove) { From 5ed227384b7f5069b2a9d6cb653f08799488c6d8 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sun, 20 Dec 2020 16:06:17 +0100 Subject: [PATCH 70/78] rename drw It's impossible to understand that this stands from drawable if you're not familiar with dwm's code. --- dwl.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/dwl.c b/dwl.c index cbf0c11..a2f3823 100644 --- a/dwl.c +++ b/dwl.c @@ -298,7 +298,7 @@ static void zoom(const Arg *arg); static const char broken[] = "broken"; static struct wl_display *dpy; static struct wlr_backend *backend; -static struct wlr_renderer *drw; +static struct wlr_renderer *renderer; static struct wlr_compositor *compositor; static struct wlr_xdg_shell *xdg_shell; @@ -1707,7 +1707,7 @@ render(struct wlr_surface *surface, int sx, int sy, void *data) /* This takes our matrix, the texture, and an alpha, and performs the actual * rendering on the GPU. */ - wlr_render_texture_with_matrix(drw, texture, matrix, 1); + wlr_render_texture_with_matrix(renderer, texture, matrix, 1); /* This lets the client know that we've displayed that frame and it can * prepare another one now if it likes. */ @@ -1744,7 +1744,7 @@ renderclients(Monitor *m, struct timespec *now) const float *color = (c == sel) ? focuscolor : bordercolor; for (int i = 0; i < 4; i++) { scalebox(&borders[i], m->wlr_output->scale); - wlr_render_rect(drw, &borders[i], color, + wlr_render_rect(renderer, &borders[i], color, m->wlr_output->transform_matrix); } @@ -1808,8 +1808,8 @@ rendermon(struct wl_listener *listener, void *data) if (render) { /* Begin the renderer (calls glViewport and some other GL sanity checks) */ - wlr_renderer_begin(drw, m->wlr_output->width, m->wlr_output->height); - wlr_renderer_clear(drw, rootcolor); + wlr_renderer_begin(renderer, m->wlr_output->width, m->wlr_output->height); + wlr_renderer_clear(renderer, rootcolor); renderlayer(&m->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &now); renderlayer(&m->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], &now); @@ -1830,7 +1830,7 @@ rendermon(struct wl_listener *listener, void *data) /* Conclude rendering and swap the buffers, showing the final frame * on-screen. */ - wlr_renderer_end(drw); + wlr_renderer_end(renderer); } wlr_output_commit(m->wlr_output); @@ -2053,8 +2053,8 @@ setup(void) /* If we don't provide a renderer, autocreate makes a GLES2 renderer for us. * The renderer is responsible for defining the various pixel formats it * supports for shared memory, this configures that for clients. */ - drw = wlr_backend_get_renderer(backend); - wlr_renderer_init_wl_display(drw, dpy); + renderer = wlr_backend_get_renderer(backend); + wlr_renderer_init_wl_display(renderer, dpy); /* This creates some hands-off wlroots interfaces. The compositor is * necessary for clients to allocate surfaces and the data device manager @@ -2062,7 +2062,7 @@ setup(void) * to dig your fingers in and play with their behavior if you want. Note that * the clients cannot set the selection directly without compositor approval, * see the setsel() function. */ - compositor = wlr_compositor_create(dpy, drw); + compositor = wlr_compositor_create(dpy, renderer); wlr_export_dmabuf_manager_v1_create(dpy); wlr_screencopy_manager_v1_create(dpy); wlr_data_control_manager_v1_create(dpy); From 499cb2c2b64fcc118f162d20910ae3a24281e0f0 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sun, 20 Dec 2020 16:07:12 +0100 Subject: [PATCH 71/78] say TODO just wtf is XXX supposed to be? It sounds like a pornographic thing. --- dwl.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dwl.c b/dwl.c index a2f3823..2511dd4 100644 --- a/dwl.c +++ b/dwl.c @@ -499,7 +499,7 @@ arrange(Monitor *m) { if (m->lt[m->sellt]->arrange) m->lt[m->sellt]->arrange(m); - /* XXX recheck pointer focus here... or in resize()? */ + /* TODO recheck pointer focus here... or in resize()? */ } void @@ -671,7 +671,7 @@ buttonpress(struct wl_listener *listener, void *data) break; case WLR_BUTTON_RELEASED: /* If you released any buttons, we exit interactive move/resize mode. */ - /* XXX should reset to the pointer focus's current setcursor */ + /* TODO should reset to the pointer focus's current setcursor */ if (cursor_mode != CurNormal) { wlr_xcursor_manager_set_cursor_image(cursor_mgr, "left_ptr", cursor); @@ -1251,13 +1251,13 @@ inputdevice(struct wl_listener *listener, void *data) createpointer(device); break; default: - /* XXX handle other input device types */ + /* TODO handle other input device types */ break; } /* We need to let the wlr_seat know what our capabilities are, which is * communiciated to the client. In dwl we always have a cursor, even if * there are no pointer devices, so we always include that capability. */ - /* XXX do we actually require a cursor? */ + /* TODO do we actually require a cursor? */ uint32_t caps = WL_SEAT_CAPABILITY_POINTER; if (!wl_list_empty(&keyboards)) caps |= WL_SEAT_CAPABILITY_KEYBOARD; @@ -1879,7 +1879,7 @@ run(char *startup_cmd) * cursor position, and set default cursor image */ selmon = xytomon(cursor->x, cursor->y); - /* XXX hack to get cursor to display in its initial location (100, 100) + /* TODO hack to get cursor to display in its initial location (100, 100) * instead of (0, 0) and then jumping. still may not be fully * initialized, as the image/coordinates are not transformed for the * monitor when displayed here */ @@ -1937,7 +1937,7 @@ setcursor(struct wl_listener *listener, void *data) /* This event is raised by the seat when a client provides a cursor image */ struct wlr_seat_pointer_request_set_cursor_event *event = data; /* If we're "grabbing" the cursor, don't use the client's image */ - /* XXX still need to save the provided surface to restore later */ + /* TODO still need to save the provided surface to restore later */ if (cursor_mode != CurNormal) return; /* This can be sent by any client, so we check to make sure this one is @@ -1966,7 +1966,7 @@ setlayout(const Arg *arg) selmon->sellt ^= 1; if (arg && arg->v) selmon->lt[selmon->sellt] = (Layout *)arg->v; - /* XXX change layout symbol? */ + /* TODO change layout symbol? */ arrange(selmon); } @@ -1992,7 +1992,7 @@ setmon(Client *c, Monitor *m, unsigned int newtags) return; c->mon = m; - /* XXX leave/enter is not optimal but works */ + /* TODO leave/enter is not optimal but works */ if (oldmon) { wlr_surface_send_leave(WLR_SURFACE(c), oldmon->wlr_output); arrange(oldmon); From c9964016b8d4904be483f7c1a2fdd82ea089ef54 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Mon, 21 Dec 2020 03:41:30 +0100 Subject: [PATCH 72/78] update README.md - A maximum SLOC can't be reasonably determined before implementing the missing protocols, so not any time soon - dwl definitely isn't a simple as dwm since it must implement lots of Wayland protocols and not just manage windows. The status bar integration, layer shell popups, damage tracking and IME are gonna require hundreds more lines each. - "Buffering of input when spawning a client so you don't have to wait for the window (use `wl_client_get_credentials` to get the PID) - would this require passing through something like dmenu? Extension protocol?" This sounds exoteric, if anything this should be patch. - Can dwl really be started from within an X session? When I do it from dwm it crashes. - A window's texture is scaled for its "home" monitor only (noticeable when window sits across a monitor boundary) Gonna open a ticket for this rather than keep it in the README. --- README.md | 55 +++++++++++++++++++++++++------------------------------ 1 file changed, 25 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 1a8d8ec..1f757d5 100644 --- a/README.md +++ b/README.md @@ -1,71 +1,66 @@ # dwl - dwm for Wayland -dwl is a compact, hackable compositor for Wayland based on [wlroots](https://github.com/swaywm/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: +dwl is a compact, hackable compositor for Wayland based on [wlroots](https://github.com/swaywm/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: - Easy to understand, hack on, and extend with patches -- One C source file (or a very small number) configurable via `config.h` -- Limited to a maximum number of SLOC (to be determined) +- One C source file configurable via `config.h` - Tied to as few external dependencies as possible +dwl is not meant to provide every feature under the sun. Instead, like dwm, it sticks to features which are necessary, simple, and straightforward to implement given the base on which it is built. Implemented default features are: -dwl is not meant to provide every feature under the sun. Instead, like dwm, it sticks to features which are necessary, simple, and straightforward to implement given the base on which it is built. Since wlroots provides a number of features that are more complicated to accomplish with Xlib and select extensions, dwl can be in some ways more featureful than dwm *while remaining just as simple.* Intended default features are: - -- Any features provided by dwm/Xlib: simple window borders, tags, keybindings, client rules, mouse move/resize (see below for why the built-in status bar is a possible exception) +- Any features provided by dwm/Xlib: simple window borders, tags, keybindings, client rules, mouse move/resize. The built-in status bar is an exception to avoid taking a dependency on FreeType or Pango and increasing the SLOC - Configurable multi-monitor layout support, including position and rotation - Configurable HiDPI/multi-DPI support -- Wayland protocols needed for daily life in the tiling world: at a minimum, xdg-shell and layer-shell (for bars/menus). Protocols trivially provided by wlroots may also be added. +- Various Wayland protocols - XWayland support as provided by wlroots - Zero flickering - Wayland users naturally expect that "every frame is perfect" -- Basic yes/no damage tracking to avoid needless redraws (if it can be done simply and has an impact on power consumption) +Features yet to be implemented are: -Other features under consideration are: - -- Additional Wayland compositor protocols which are trivially provided by wlroots or can be conditionally included via `config.h` settings (e.g. screen capture) -- External bar support instead of a built-in status bar, to avoid taking a dependency on FreeType or Pango -- Buffering of input when spawning a client so you don't have to wait for the window (use `wl_client_get_credentials` to get the PID) - would this require passing through something like dmenu? Extension protocol? -- More in-depth damage region tracking - +- Write a dwl-status protocol that bars can implement to show tags. You can already use Waybar or yambar, but without tag information +- Implement the input-inhibitor protocol to support screen lockers +- Implement the idle-inhibit protocol which the lets application such as mpv disable idle monitoring, and distribute it as a patch +- Layer shell popups (used by Waybar) +- Basic yes/no damage tracking to avoid needless redraws +- More in-depth damage region tracking (this should be worth it according to https://mozillagfx.wordpress.com/2019/10/22/dramatically-reduced-power-usage-in-firefox-70-on-macos-with-core-animation/) +- Implement the text-input and input-method protocols to support IME once ibus implements input-method v2 (see https://github.com/ibus/ibus/pull/2256 and https://github.com/djpohly/dwl/pull/12) +- Implement urgent/attention/focus-request once it's part of the xdg-shell protocol (https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/9) Feature *non-goals* include: - Client-side decoration (any more than is necessary to tell the clients not to) - Client-initiated window management, such as move, resize, and close, which can be done through the compositor - ## Building dwl -dwl has only two dependencies: wlroots (git version currently required) and wayland-protocols. Simply install these and run `make`. +dwl has only two dependencies: wlroots 0.12 and wayland-protocols. Simply install these and run `sudo make install`. +To enable XWayland, you should also install xorg-xwayland and uncomment its flag in `config.mk`. ## Configuration -All configuration is done by editing `config.h` and recompiling, in the same manner as dwm. There is no way to separately restart the window manager in Wayland without restarting the entire display server, so any changes will take effect the next time dwl is executed. - +All configuration is done by editing `config.h` and recompiling, in the same manner as dwm. There is no way to separately restart the window manager in Wayland without restarting the entire display server, so any changes will take effect the next time dwl is executed. ## Running dwl -dwl can be run as-is, with no arguments. In an existing Wayland or X11 session, this will open a window to act as a virtual display. When run from a TTY, the Wayland server will take over the entire virtual terminal. Clients started by dwl will have `WAYLAND_DISPLAY` set in their environment, and other clients can be started from outside the session by setting this variable accordingly. +dwl can be run as-is, with no arguments. In an existing Wayland, this will open a window to act as a virtual display. When run from a TTY, the Wayland server will take over the entire virtual terminal. Clients started by dwl will have `WAYLAND_DISPLAY` set in their environment, and other clients can be started from outside the session by setting this variable accordingly. -You can also specify a startup program using the `-s` option. The argument to this option will be run at startup as a shell command (using `sh -c`) and can serve a similar function to `.xinitrc`: starting a service manager or other startup applications. Unlike `.xinitrc`, the display server will not shut down when this process terminates. Instead, as dwl is shutting down, it will send this process a SIGTERM and wait for it to terminate (if it hasn't already). This makes it ideal not only for initialization but also for execing into a user-level service manager like s6 or `systemd --user`. +You can also specify a startup program using the `-s` option. The argument to this option will be run at startup as a shell command (using `sh -c`) and can serve a similar function to `.xinitrc`: starting a service manager or other startup applications. Unlike `.xinitrc`, the display server will not shut down when this process terminates. Instead, as dwl is shutting down, it will send this process a SIGTERM and wait for it to terminate (if it hasn't already). This makes it ideal not only for initialization but also for execing into a user-level service manager like s6 or `systemd --user`. Note: Wayland requires a valid `XDG_RUNTIME_DIR`, which is usually set up by a session manager such as `elogind` or `systemd-logind`. If your system doesn't do this automatically, you will need to configure it prior to launching `dwl`, e.g.: export XDG_RUNTIME_DIR=/tmp/xdg-runtime-$(id -u) mkdir -p $XDG_RUNTIME_DIR +## Replacements for X applications -## Known limitations and issues +You can find a [list of Wayland applications on the sway wiki](https://github.com/swaywm/sway/wiki/i3-Migration-Guide). -dwl is a work in progress, and it has not yet reached its feature goals in a number of ways: - -- A window's texture is scaled for its "home" monitor only (noticeable when window sits across a monitor boundary) -- XWayland support is new and could use testing -- Urgent/attention/focus-request ([not yet supported](https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/9) by xdg-shell protocol) -- Statusbar support (built-in or external) -- Damage tracking +## IRC channel +dwl's IRC channel is #dwl on irc.freenode.net. ## 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. Speaking of which, many thanks to suckless.org and the dwm developers and community for the inspiration. +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. +Many thanks to suckless.org and the dwm developers and community for the inspiration, and to Devin J. Pohly for creating dwl. From cf7c5eae214609eddbfb98de856d33c26f8964b9 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Mon, 21 Dec 2020 08:05:40 +0100 Subject: [PATCH 73/78] don't reset the cursor image ...in internal calls to restore pointer focus. Necessary for the unclutter patch, and there's no harm in avoiding this call even in mainline; might prevents issues in same edge cases. --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 2511dd4..16f0a88 100644 --- a/dwl.c +++ b/dwl.c @@ -1490,7 +1490,7 @@ motionnotify(uint32_t time) /* If there's no client surface under the cursor, set the cursor image to a * default. This is what makes the cursor image appear when you move it * off of a client or over its border. */ - if (!surface) + if (!surface && time) wlr_xcursor_manager_set_cursor_image(cursor_mgr, "left_ptr", cursor); From 5668c616161d451e6f20be29b31bbf03f0f398a5 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Fri, 18 Sep 2020 21:45:35 +0200 Subject: [PATCH 74/78] Define monitor order with monrules[] The order in which monitors are defined in monrules[] actually matters. Monotors that aren't configured in monrules[], it will always be the leftmost. --- config.def.h | 3 +++ dwl.c | 30 ++++++++++++++++++++++++++---- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/config.def.h b/config.def.h index 5e33204..8d76be4 100644 --- a/config.def.h +++ b/config.def.h @@ -32,6 +32,9 @@ static const MonitorRule monrules[] = { */ /* defaults */ { NULL, 0.55, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL }, + /* with the outputOder patch, the order in which every monitor is defined + * defines its actual position. Non configured monitor, are always added to + * the left */ }; /* keyboard */ diff --git a/dwl.c b/dwl.c index 16f0a88..d54311d 100644 --- a/dwl.c +++ b/dwl.c @@ -175,6 +175,7 @@ struct Monitor { unsigned int tagset[2]; double mfact; int nmaster; + int position; }; typedef struct { @@ -836,8 +837,8 @@ createmon(struct wl_listener *listener, void *data) Monitor *m = wlr_output->data = calloc(1, sizeof(*m)); m->wlr_output = wlr_output; m->tagset[0] = m->tagset[1] = 1; - const MonitorRule *r; - for (r = monrules; r < END(monrules); r++) { + m->position = -1; + for (const MonitorRule *r = monrules; r < END(monrules); r++) { if (!r->name || strstr(wlr_output->name, r->name)) { m->mfact = r->mfact; m->nmaster = r->nmaster; @@ -846,6 +847,7 @@ createmon(struct wl_listener *listener, void *data) m->lt[0] = r->lt; m->lt[1] = &layouts[LENGTH(layouts) > 1 && r->lt != &layouts[1]]; wlr_output_set_transform(wlr_output, r->rr); + m->position = r - monrules; break; } } @@ -856,7 +858,19 @@ createmon(struct wl_listener *listener, void *data) m->destroy.notify = cleanupmon; wl_signal_add(&wlr_output->events.destroy, &m->destroy); - wl_list_insert(&mons, &m->link); + Monitor *moni, *insertmon = NULL; + int x = 0; + wl_list_for_each(moni, &mons, link) + if (m->position > moni->position) + insertmon = moni; + if (insertmon) { + x = insertmon->w.x + insertmon->w.width; + wl_list_insert(&insertmon->link, &m->link); + fprintf(stderr, "%s inserted in pos %d\n", m->wlr_output->name, m->position); + } else { + wl_list_insert(&mons, &m->link); + fprintf(stderr, "%s defaulting\n", m->wlr_output->name); + } wlr_output_enable(wlr_output, true); if (!wlr_output_commit(wlr_output)) @@ -871,7 +885,15 @@ createmon(struct wl_listener *listener, void *data) * display, which Wayland clients can see to find out information about the * output (such as DPI, scale factor, manufacturer, etc). */ - wlr_output_layout_add_auto(output_layout, wlr_output); + wlr_output_layout_add(output_layout, wlr_output, x, 0); + wl_list_for_each_reverse(moni, &mons, link) { + /* all monitors that on the right of the new one must be moved */ + if (moni == m) + break; + wlr_output_layout_move(output_layout, moni->wlr_output, moni->w.x + m->wlr_output->width, 0); + fprintf(stderr, "moved %s to %d", moni->wlr_output->name, moni->w.x + m->wlr_output->width); + } + sgeom = *wlr_output_layout_get_box(output_layout, NULL); size_t nlayers = LENGTH(m->layers); for (size_t i = 0; i < nlayers; ++i) From 33e8a3f1f3382322180c6b80bc48cb2ab4965bcf Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Mon, 21 Dec 2020 11:21:59 +0100 Subject: [PATCH 75/78] update comments and remove debugging printf --- config.def.h | 7 +++---- dwl.c | 12 +++--------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/config.def.h b/config.def.h index 8d76be4..4ab746b 100644 --- a/config.def.h +++ b/config.def.h @@ -24,7 +24,9 @@ static const Layout layouts[] = { { "[M]", monocle }, }; -/* monitors */ +/* monitors + * The order in which monitors are defined determines their position. + * Non-configured monitors are always added to the left. */ static const MonitorRule monrules[] = { /* name mfact nmaster scale layout rotate/reflect */ /* example of a HiDPI laptop monitor: @@ -32,9 +34,6 @@ static const MonitorRule monrules[] = { */ /* defaults */ { NULL, 0.55, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL }, - /* with the outputOder patch, the order in which every monitor is defined - * defines its actual position. Non configured monitor, are always added to - * the left */ }; /* keyboard */ diff --git a/dwl.c b/dwl.c index d54311d..2128751 100644 --- a/dwl.c +++ b/dwl.c @@ -866,20 +866,15 @@ createmon(struct wl_listener *listener, void *data) if (insertmon) { x = insertmon->w.x + insertmon->w.width; wl_list_insert(&insertmon->link, &m->link); - fprintf(stderr, "%s inserted in pos %d\n", m->wlr_output->name, m->position); } else { wl_list_insert(&mons, &m->link); - fprintf(stderr, "%s defaulting\n", m->wlr_output->name); } wlr_output_enable(wlr_output, true); if (!wlr_output_commit(wlr_output)) return; - /* Adds this to the output layout. The add_auto function arranges outputs - * from left-to-right in the order they appear. A more sophisticated - * compositor would let the user configure the arrangement of outputs in the - * layout. + /* Adds this to the output layout in the order it was configured in. * * The output layout utility automatically adds a wl_output global to the * display, which Wayland clients can see to find out information about the @@ -887,11 +882,10 @@ createmon(struct wl_listener *listener, void *data) */ wlr_output_layout_add(output_layout, wlr_output, x, 0); wl_list_for_each_reverse(moni, &mons, link) { - /* all monitors that on the right of the new one must be moved */ + /* All monitors to the right of the new one must be moved */ if (moni == m) break; wlr_output_layout_move(output_layout, moni->wlr_output, moni->w.x + m->wlr_output->width, 0); - fprintf(stderr, "moved %s to %d", moni->wlr_output->name, moni->w.x + m->wlr_output->width); } sgeom = *wlr_output_layout_get_box(output_layout, NULL); @@ -902,7 +896,7 @@ createmon(struct wl_listener *listener, void *data) /* When adding monitors, the geometries of all monitors must be updated */ updatemons(); wl_list_for_each(m, &mons, link) { - /* the first monitor in the list is the most recently added */ + /* The first monitor in the list is the most recently added */ Client *c; wl_list_for_each(c, &clients, link) { if (c->isfloating) From bcf9d8fb9a2f30e5fc0283637ee63350bf78e696 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Mon, 21 Dec 2020 13:06:06 +0100 Subject: [PATCH 76/78] disable natural scrolling by default This inverts the scroll even on regular mice. --- config.def.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.def.h b/config.def.h index 4ab746b..b7e067d 100644 --- a/config.def.h +++ b/config.def.h @@ -49,7 +49,7 @@ static const int repeat_delay = 600; /* Trackpad */ static const bool tap_to_click = true; -static const bool natural_scrolling = true; +static const bool natural_scrolling = false; #define MODKEY WLR_MODIFIER_ALT #define TAGKEYS(KEY,SKEY,TAG) \ From 572ccd92c43b83b677e87c3926d0f5703224c2d8 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Mon, 21 Dec 2020 21:39:26 +0100 Subject: [PATCH 77/78] remove github directory --- .github/ISSUE_TEMPLATE/bug_report.md | 10 ---------- .github/ISSUE_TEMPLATE/enhancement-idea.md | 10 ---------- 2 files changed, 20 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md delete mode 100644 .github/ISSUE_TEMPLATE/enhancement-idea.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 2a18c75..0000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Bug report -about: Something in dwl isn't working correctly -title: '' -labels: 'Type: bug' -assignees: '' - ---- - - diff --git a/.github/ISSUE_TEMPLATE/enhancement-idea.md b/.github/ISSUE_TEMPLATE/enhancement-idea.md deleted file mode 100644 index 92c6c8c..0000000 --- a/.github/ISSUE_TEMPLATE/enhancement-idea.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Enhancement idea -about: Suggest a feature or improvement -title: '' -labels: 'Type: enhancement' -assignees: '' - ---- - - From 3695ac643fb621f9339137afbc274c82ab46d088 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Tue, 22 Dec 2020 09:13:18 +0100 Subject: [PATCH 78/78] spacing --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1f757d5..55eb370 100644 --- a/README.md +++ b/README.md @@ -63,4 +63,5 @@ dwl's IRC channel is #dwl on irc.freenode.net. ## 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. + Many thanks to suckless.org and the dwm developers and community for the inspiration, and to Devin J. Pohly for creating dwl.