From 3a72cd924c45d4bc01f4d8477c14fdf2854f2dd0 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Thu, 8 Apr 2021 07:11:13 -0500 Subject: [PATCH 01/21] fix README for main branch --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 260280a..22dfa07 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Feature *non-goals* include: ## Building dwl -dwl has only two dependencies: wlroots-git and wayland-protocols. Simply install these and run `make`. +dwl has only two dependencies: wlroots 0.13 and wayland-protocols. Simply install these and run `make`. If you wish to build against a Git version of wlroots, check out the [wlroots-next branch](https://github.com/djpohly/dwl/tree/wlroots-next). To enable XWayland, you should also install xorg-xwayland and uncomment its flag in `config.mk`. From 84b26ef1bae83cdf0f305d2c81d345c8ef73d0d0 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Thu, 8 Apr 2021 07:12:50 -0500 Subject: [PATCH 02/21] Revert "fix README for main branch" This reverts commit 3a72cd924c45d4bc01f4d8477c14fdf2854f2dd0. There is probably an easier way to keep this difference, but hey, this works for me. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 22dfa07..260280a 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Feature *non-goals* include: ## Building dwl -dwl has only two dependencies: wlroots 0.13 and wayland-protocols. Simply install these and run `make`. If you wish to build against a Git version of wlroots, check out the [wlroots-next branch](https://github.com/djpohly/dwl/tree/wlroots-next). +dwl has only two dependencies: wlroots-git and wayland-protocols. Simply install these and run `make`. To enable XWayland, you should also install xorg-xwayland and uncomment its flag in `config.mk`. From 9071ce6c848ce214939fb84f85ae77de86de88d7 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Fri, 9 Apr 2021 12:37:49 -0500 Subject: [PATCH 03/21] nuke CSDs, hopefully for good! --- dwl.c | 45 ++++++--------------------------------------- 1 file changed, 6 insertions(+), 39 deletions(-) diff --git a/dwl.c b/dwl.c index 0deae84..d463985 100644 --- a/dwl.c +++ b/dwl.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -228,18 +229,15 @@ static void createmon(struct wl_listener *listener, void *data); static void createnotify(struct wl_listener *listener, void *data); static void createlayersurface(struct wl_listener *listener, void *data); static void createpointer(struct wlr_input_device *device); -static void createxdeco(struct wl_listener *listener, void *data); 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 Monitor *dirtomon(enum wlr_direction dir); static void focusclient(Client *c, int lift); static void focusmon(const Arg *arg); static void focusstack(const Arg *arg); 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); static void inputdevice(struct wl_listener *listener, void *data); static int keybinding(uint32_t mods, xkb_keysym_t sym); @@ -312,7 +310,6 @@ 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; static struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard_mgr; @@ -344,7 +341,6 @@ static struct wl_listener layout_change = {.notify = updatemons}; 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}; static struct wl_listener new_layer_shell_surface = {.notify = createlayersurface}; static struct wl_listener output_mgr_apply = {.notify = outputmgrapply}; @@ -958,18 +954,6 @@ createpointer(struct wlr_input_device *device) wlr_cursor_attach_input_device(cursor, device); } -void -createxdeco(struct wl_listener *listener, void *data) -{ - struct wlr_xdg_toplevel_decoration_v1 *wlr_deco = data; - Decoration *d = wlr_deco->data = calloc(1, sizeof(*d)); - - LISTEN(&wlr_deco->events.request_mode, &d->request_mode, getxdecomode); - LISTEN(&wlr_deco->events.destroy, &d->destroy, destroyxdeco); - - getxdecomode(&d->request_mode, wlr_deco); -} - void cursorframe(struct wl_listener *listener, void *data) { @@ -1020,17 +1004,6 @@ destroynotify(struct wl_listener *listener, void *data) free(c); } -void -destroyxdeco(struct wl_listener *listener, void *data) -{ - struct wlr_xdg_toplevel_decoration_v1 *wlr_deco = data; - Decoration *d = wlr_deco->data; - - wl_list_remove(&d->destroy.link); - wl_list_remove(&d->request_mode.link); - free(d); -} - void togglefullscreen(const Arg *arg) { @@ -1185,14 +1158,6 @@ focustop(Monitor *m) return NULL; } -void -getxdecomode(struct wl_listener *listener, void *data) -{ - struct wlr_xdg_toplevel_decoration_v1 *wlr_deco = data; - wlr_xdg_toplevel_decoration_v1_set_mode(wlr_deco, - WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); -} - void incnmaster(const Arg *arg) { @@ -2051,9 +2016,11 @@ setup(void) xdg_shell = wlr_xdg_shell_create(dpy); wl_signal_add(&xdg_shell->events.new_surface, &new_xdg_surface); - /* Use xdg_decoration protocol to negotiate server-side decorations */ - xdeco_mgr = wlr_xdg_decoration_manager_v1_create(dpy); - wl_signal_add(&xdeco_mgr->events.new_toplevel_decoration, &new_xdeco); + /* Use decoration protocols to negotiate server-side decorations */ + wlr_server_decoration_manager_set_default_mode( + wlr_server_decoration_manager_create(dpy), + WLR_SERVER_DECORATION_MANAGER_MODE_SERVER); + wlr_xdg_decoration_manager_v1_create(dpy); /* * Creates a cursor, which is a wlroots utility for tracking the cursor From b372d4b55e256b96fe926c512499ed90c460d66f Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Wed, 14 Apr 2021 11:15:26 -0500 Subject: [PATCH 04/21] pipe status info into -s command Unlike with X window managers, the display socket in Wayland isn't set up prior to starting the compositor. Because of this, you can't pipe the compositor's output directly into a program which needs access to $WAYLAND_DISPLAY, which is a typical setup for this purpose. Existing scripts have been forced to create a pipe/FIFO or a temporary file as an intermediary. Instead, send the status info directly to stdin of the -s command, which *does* have access to $WAYLAND_DISPLAY. Fixes #103. --- dwl.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index d463985..f4b9b3a 100644 --- a/dwl.c +++ b/dwl.c @@ -1805,15 +1805,22 @@ run(char *startup_cmd) setenv("WAYLAND_DISPLAY", socket, 1); if (startup_cmd) { + int piperw[2]; + pipe(piperw); startup_pid = fork(); if (startup_pid < 0) EBARF("startup: fork"); if (startup_pid == 0) { - dup2(STDERR_FILENO, STDOUT_FILENO); + dup2(piperw[0], STDIN_FILENO); + close(piperw[1]); execl("/bin/sh", "/bin/sh", "-c", startup_cmd, NULL); EBARF("startup: execl"); } + dup2(piperw[1], STDOUT_FILENO); + close(piperw[0]); } + /* If nobody is reading the status output, don't terminate */ + signal(SIGPIPE, SIG_IGN); /* Run the Wayland event loop. This does not return until you exit the * compositor. Starting the backend rigged up all of the necessary event From 6a0dec69ec47ed8143f13016e629e5502d6339a2 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Thu, 15 Apr 2021 13:03:21 -0500 Subject: [PATCH 05/21] re-compile if config.mk changes --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a0d1cc3..1362db8 100644 --- a/Makefile +++ b/Makefile @@ -47,7 +47,7 @@ idle-protocol.o: idle-protocol.h config.h: | config.def.h cp config.def.h $@ -dwl.o: config.h client.h xdg-shell-protocol.h wlr-layer-shell-unstable-v1-protocol.h idle-protocol.h +dwl.o: config.mk config.h client.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 idle-protocol.o From 4170a90fbccb5823f536d7b77c2ba40e5358002b Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Thu, 15 Apr 2021 13:04:31 -0500 Subject: [PATCH 06/21] group phony targets together in Makefile --- Makefile | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 1362db8..fe6ff04 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,14 @@ LDLIBS += $(foreach p,$(PKGS),$(shell pkg-config --libs $(p))) all: dwl +clean: + rm -f dwl *.o *-protocol.h *-protocol.c + +install: dwl + install -D dwl $(PREFIX)/bin/dwl + +.PHONY: all clean install + # wayland-scanner is a tool which generates C headers and rigging for Wayland # protocols, which are specified in XML. wlroots requires you to rig these up # to your build system yourself and provide them in the include path. @@ -50,12 +58,3 @@ config.h: | config.def.h dwl.o: config.mk config.h client.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 idle-protocol.o - -clean: - rm -f dwl *.o *-protocol.h *-protocol.c - -install: dwl - install -D dwl $(PREFIX)/bin/dwl - -.DEFAULT_GOAL=dwl -.PHONY: clean From 3727f4a7b3d230226f0082581444344d563e0f9c Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Thu, 15 Apr 2021 13:05:05 -0500 Subject: [PATCH 07/21] update status info if focused client changes title Fixes #108. --- dwl.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/dwl.c b/dwl.c index d463985..c5308f5 100644 --- a/dwl.c +++ b/dwl.c @@ -96,6 +96,7 @@ typedef struct { struct wl_listener map; struct wl_listener unmap; struct wl_listener destroy; + struct wl_listener set_title; struct wl_listener fullscreen; struct wlr_box geom; /* layout-relative, includes border */ Monitor *mon; @@ -288,6 +289,7 @@ static void unmaplayersurface(LayerSurface *layersurface); static void unmaplayersurfacenotify(struct wl_listener *listener, void *data); static void unmapnotify(struct wl_listener *listener, void *data); static void updatemons(struct wl_listener *listener, void *data); +static void updatetitle(struct wl_listener *listener, void *data); static void view(const Arg *arg); static void virtualkeyboard(struct wl_listener *listener, void *data); static Client *xytoclient(double x, double y); @@ -891,6 +893,7 @@ createnotify(struct wl_listener *listener, void *data) LISTEN(&xdg_surface->events.map, &c->map, mapnotify); LISTEN(&xdg_surface->events.unmap, &c->unmap, unmapnotify); LISTEN(&xdg_surface->events.destroy, &c->destroy, destroynotify); + LISTEN(&xdg_surface->toplevel->events.set_title, &c->set_title, updatetitle); LISTEN(&xdg_surface->toplevel->events.request_fullscreen, &c->fullscreen, fullscreennotify); c->isfullscreen = 0; @@ -994,6 +997,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->set_title.link); wl_list_remove(&c->fullscreen.link); #ifdef XWAYLAND if (c->type == X11Managed) @@ -2291,6 +2295,14 @@ updatemons(struct wl_listener *listener, void *data) wlr_output_manager_v1_set_configuration(output_mgr, config); } +void +updatetitle(struct wl_listener *listener, void *data) +{ + Client *c = wl_container_of(listener, c, set_title); + if (c == focustop(c->mon)) + printstatus(); +} + void view(const Arg *arg) { @@ -2427,6 +2439,7 @@ createnotifyx11(struct wl_listener *listener, void *data) activatex11); LISTEN(&xwayland_surface->events.request_configure, &c->configure, configurex11); + LISTEN(&xwayland_surface->events.set_title, &c->set_title, updatetitle); LISTEN(&xwayland_surface->events.destroy, &c->destroy, destroynotify); LISTEN(&xwayland_surface->events.request_fullscreen, &c->fullscreen, fullscreennotify); From d57db4cac927126d1d006becf5f2ed743ac21474 Mon Sep 17 00:00:00 2001 From: Jason Goulet-Lipman Date: Mon, 19 Apr 2021 09:05:35 -0400 Subject: [PATCH 08/21] added uninstall target --- Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index fe6ff04..5ff69e9 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,10 @@ clean: install: dwl install -D dwl $(PREFIX)/bin/dwl -.PHONY: all clean install +uninstall: + rm -f $(PREFIX)/bin/dwl + +.PHONY: all clean install uninstall # wayland-scanner is a tool which generates C headers and rigging for Wayland # protocols, which are specified in XML. wlroots requires you to rig these up From 1b139a860dacbca8c4b3f8d24930b3f829534206 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Tue, 18 May 2021 11:33:12 -0500 Subject: [PATCH 09/21] update README --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 22dfa07..51aecf8 100644 --- a/README.md +++ b/README.md @@ -14,26 +14,27 @@ dwl is not meant to provide every feature under the sun. Instead, like dwm, it s - Any features provided by dwm/Xlib: simple window borders, tags, keybindings, client rules, mouse move/resize. Providing a built-in status bar is an exception to this goal, to avoid dependencies on font rendering and/or drawing libraries when an external bar could work well. - Configurable multi-monitor layout support, including position and rotation - Configurable HiDPI/multi-DPI support +- Provide information to external status bars via stdout/stdin - Various Wayland protocols -- XWayland support as provided by wlroots +- XWayland support as provided by wlroots (can be enabled in `config.mk`) - Zero flickering - Wayland users naturally expect that "every frame is perfect" Features under consideration (possibly as patches) are: - Protocols made trivial by wlroots -- Provide information to external status bars via stdout or another file descriptor +- Implement urgent/focus-request once the xdg-activation protocol [hits wlroots](https://github.com/swaywm/wlroots/pull/2718) - Implement the input-inhibitor protocol to support screen lockers - Implement the idle-inhibit protocol which lets applications such as mpv disable idle monitoring - Layer shell popups (used by Waybar) - Basic yes/no damage tracking to avoid needless redraws - More in-depth damage region tracking ([which may improve power usage](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: +Feature *non-goals* for the main codebase 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 +- Animations and visual effects ## Building dwl From 93a58abf2955aa01b7c148b85490d443ab017fb7 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sat, 22 May 2021 14:22:37 -0500 Subject: [PATCH 10/21] Wait until map to set window's tiled state Workaround for a bug in Chromium where it fails to attach a buffer to the surface. Fixes #119. --- dwl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dwl.c b/dwl.c index d7e798b..30a64f8 100644 --- a/dwl.c +++ b/dwl.c @@ -885,10 +885,6 @@ createnotify(struct wl_listener *listener, void *data) c->surface.xdg = xdg_surface; c->bw = borderpx; - /* Tell the client not to try anything fancy */ - wlr_xdg_toplevel_set_tiled(c->surface.xdg, WLR_EDGE_TOP | - WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT); - LISTEN(&xdg_surface->surface->events.commit, &c->commit, commitnotify); LISTEN(&xdg_surface->events.map, &c->map, mapnotify); LISTEN(&xdg_surface->events.unmap, &c->unmap, unmapnotify); @@ -1308,6 +1304,10 @@ mapnotify(struct wl_listener *listener, void *data) c->geom.width += 2 * c->bw; c->geom.height += 2 * c->bw; + /* Tell the client not to try anything fancy */ + wlr_xdg_toplevel_set_tiled(c->surface.xdg, WLR_EDGE_TOP | + WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT); + /* Set initial monitor, tags, floating status, and focus */ applyrules(c); } From d8cf65c74f3b7132302027cfbf940de8548d7d17 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sat, 22 May 2021 21:18:48 -0500 Subject: [PATCH 11/21] implement urgency hint --- README.md | 2 +- dwl.c | 50 +++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 51aecf8..dc75cef 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ dwl is not meant to provide every feature under the sun. Instead, like dwm, it s - Configurable multi-monitor layout support, including position and rotation - Configurable HiDPI/multi-DPI support - Provide information to external status bars via stdout/stdin +- Urgency hints via xdg-activate protocol - Various Wayland protocols - XWayland support as provided by wlroots (can be enabled in `config.mk`) - Zero flickering - Wayland users naturally expect that "every frame is perfect" @@ -22,7 +23,6 @@ dwl is not meant to provide every feature under the sun. Instead, like dwm, it s Features under consideration (possibly as patches) are: - Protocols made trivial by wlroots -- Implement urgent/focus-request once the xdg-activation protocol [hits wlroots](https://github.com/swaywm/wlroots/pull/2718) - Implement the input-inhibitor protocol to support screen lockers - Implement the idle-inhibit protocol which lets applications such as mpv disable idle monitoring - Layer shell popups (used by Waybar) diff --git a/dwl.c b/dwl.c index d7e798b..4d0bc84 100644 --- a/dwl.c +++ b/dwl.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -107,7 +108,7 @@ typedef struct { #endif int bw; unsigned int tags; - int isfloating; + int isfloating, isurgent; uint32_t resize; /* configure serial of a pending resize */ int prevx; int prevy; @@ -290,6 +291,7 @@ static void unmaplayersurfacenotify(struct wl_listener *listener, void *data); static void unmapnotify(struct wl_listener *listener, void *data); static void updatemons(struct wl_listener *listener, void *data); static void updatetitle(struct wl_listener *listener, void *data); +static void urgent(struct wl_listener *listener, void *data); static void view(const Arg *arg); static void virtualkeyboard(struct wl_listener *listener, void *data); static Client *xytoclient(double x, double y); @@ -306,6 +308,7 @@ static struct wlr_renderer *drw; static struct wlr_compositor *compositor; static struct wlr_xdg_shell *xdg_shell; +static struct wlr_xdg_activation_v1 *activation; static struct wl_list clients; /* tiling order */ static struct wl_list fstack; /* focus order */ static struct wl_list stack; /* stacking z-order */ @@ -347,6 +350,7 @@ 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_activate = {.notify = urgent}; 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}; @@ -1078,6 +1082,7 @@ focusclient(Client *c, int lift) wl_list_remove(&c->flink); wl_list_insert(&fstack, &c->flink); selmon = c->mon; + c->isurgent = 0; } printstatus(); @@ -1550,22 +1555,29 @@ void printstatus(void) { Monitor *m = NULL; - Client *c = NULL; - unsigned int activetags; + Client *c; + unsigned int occ, urg, sel; wl_list_for_each(m, &mons, link) { - activetags=0; + occ = urg = 0; wl_list_for_each(c, &clients, link) { - if (c->mon == m) - activetags |= c->tags; + if (c->mon != m) + continue; + occ |= c->tags; + if (c->isurgent) + urg |= c->tags; } - if (focustop(m)) + if ((c = focustop(m))) { printf("%s title %s\n", m->wlr_output->name, client_get_title(focustop(m))); - else + sel = c->tags; + } else { printf("%s title \n", m->wlr_output->name); + sel = 0; + } printf("%s selmon %u\n", m->wlr_output->name, m == selmon); - printf("%s tags %u %u\n", m->wlr_output->name, activetags, m->tagset[m->seltags]); + printf("%s tags %u %u %u %u\n", m->wlr_output->name, occ, m->tagset[m->seltags], + sel, urg); printf("%s layout %s\n", m->wlr_output->name, m->lt[m->sellt]->symbol); } fflush(stdout); @@ -1825,6 +1837,7 @@ run(char *startup_cmd) } /* If nobody is reading the status output, don't terminate */ signal(SIGPIPE, SIG_IGN); + printstatus(); /* Run the Wayland event loop. This does not return until you exit the * compositor. Starting the backend rigged up all of the necessary event @@ -1997,6 +2010,10 @@ setup(void) wlr_primary_selection_v1_device_manager_create(dpy); wlr_viewporter_create(dpy); + /* Initializes the interface used to implement urgency hints */ + activation = wlr_xdg_activation_v1_create(dpy); + wl_signal_add(&activation->events.request_activate, &request_activate); + /* Creates an output layout, which a wlroots utility for working with an * arrangement of screens in a physical layout. */ output_layout = wlr_output_layout_create(); @@ -2310,6 +2327,21 @@ updatetitle(struct wl_listener *listener, void *data) printstatus(); } +void +urgent(struct wl_listener *listener, void *data) +{ + struct wlr_xdg_activation_v1_request_activate_event *event = data; + Client *c; + + if (!wlr_surface_is_xdg_surface(event->surface)) + return; + c = wlr_xdg_surface_from_wlr_surface(event->surface)->data; + if (c != selclient()) { + c->isurgent = 1; + printstatus(); + } +} + void view(const Arg *arg) { From 9ab5e01d5b3864f151c222d001a8a2152f29b518 Mon Sep 17 00:00:00 2001 From: Sevz17 Date: Sun, 23 May 2021 11:24:32 -0500 Subject: [PATCH 12/21] before set tiled verify if client is xdg-shell, then set tile --- dwl.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dwl.c b/dwl.c index 30a64f8..3a562b9 100644 --- a/dwl.c +++ b/dwl.c @@ -1304,9 +1304,17 @@ mapnotify(struct wl_listener *listener, void *data) c->geom.width += 2 * c->bw; c->geom.height += 2 * c->bw; +#ifdef XWAYLAND + if (c->type == XDGShell) { + /* Tell the client not to try anything fancy */ + wlr_xdg_toplevel_set_tiled(c->surface.xdg, WLR_EDGE_TOP | + WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT); + } +#else /* Tell the client not to try anything fancy */ wlr_xdg_toplevel_set_tiled(c->surface.xdg, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT); +#endif /* Set initial monitor, tags, floating status, and focus */ applyrules(c); From 06ca86009296c1b8753cba259fd797703a281bbd Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sun, 23 May 2021 18:28:13 -0500 Subject: [PATCH 13/21] factor xwayland hackiness out into client.h --- client.h | 11 +++++++++++ dwl.c | 11 +---------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/client.h b/client.h index f4735c2..56e3089 100644 --- a/client.h +++ b/client.h @@ -141,6 +141,17 @@ client_set_size(Client *c, uint32_t width, uint32_t height) return wlr_xdg_toplevel_set_size(c->surface.xdg, width, height); } +static inline void +client_set_tiled(Client *c, uint32_t edges) +{ +#ifdef XWAYLAND + if (client_is_x11(c)) + return; +#endif + wlr_xdg_toplevel_set_tiled(c->surface.xdg, WLR_EDGE_TOP | + WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT); +} + static inline struct wlr_surface * client_surface(Client *c) { diff --git a/dwl.c b/dwl.c index 3a562b9..9188f06 100644 --- a/dwl.c +++ b/dwl.c @@ -1304,17 +1304,8 @@ mapnotify(struct wl_listener *listener, void *data) c->geom.width += 2 * c->bw; c->geom.height += 2 * c->bw; -#ifdef XWAYLAND - if (c->type == XDGShell) { - /* Tell the client not to try anything fancy */ - wlr_xdg_toplevel_set_tiled(c->surface.xdg, WLR_EDGE_TOP | - WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT); - } -#else /* Tell the client not to try anything fancy */ - wlr_xdg_toplevel_set_tiled(c->surface.xdg, WLR_EDGE_TOP | - WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT); -#endif + client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT); /* Set initial monitor, tags, floating status, and focus */ applyrules(c); From 60c40c0989440fc54aa05b0e27cfbaad8c722fec Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Mon, 24 May 2021 22:03:39 -0500 Subject: [PATCH 14/21] print status on output create Along with starting the -s command earlier, this will allow the initial monitor setup to generate printstatus info. --- dwl.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/dwl.c b/dwl.c index 9188f06..e0b404e 100644 --- a/dwl.c +++ b/dwl.c @@ -842,11 +842,13 @@ createmon(struct wl_listener *listener, void *data) LISTEN(&wlr_output->events.frame, &m->frame, rendermon); LISTEN(&wlr_output->events.destroy, &m->destroy, cleanupmon); - wl_list_insert(&mons, &m->link); wlr_output_enable(wlr_output, 1); if (!wlr_output_commit(wlr_output)) return; + wl_list_insert(&mons, &m->link); + printstatus(); + /* 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 @@ -1786,27 +1788,9 @@ run(char *startup_cmd) const char *socket = wl_display_add_socket_auto(dpy); if (!socket) BARF("startup: display_add_socket_auto"); - - /* Start the backend. This will enumerate outputs and inputs, become the DRM - * master, etc */ - if (!wlr_backend_start(backend)) - BARF("startup: backend_start"); - - /* Now that outputs are initialized, choose initial selmon based on - * cursor position, and set default cursor image */ - selmon = xytomon(cursor->x, cursor->y); - - /* 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 */ - wlr_cursor_warp_closest(cursor, NULL, cursor->x, cursor->y); - wlr_xcursor_manager_set_cursor_image(cursor_mgr, "left_ptr", cursor); - - /* Set the WAYLAND_DISPLAY environment variable to our socket and run the - * startup command if requested. */ setenv("WAYLAND_DISPLAY", socket, 1); + /* Now that the socket exists, run the startup command */ if (startup_cmd) { int piperw[2]; pipe(piperw); @@ -1825,6 +1809,22 @@ run(char *startup_cmd) /* If nobody is reading the status output, don't terminate */ signal(SIGPIPE, SIG_IGN); + /* Start the backend. This will enumerate outputs and inputs, become the DRM + * master, etc */ + if (!wlr_backend_start(backend)) + BARF("startup: backend_start"); + + /* Now that outputs are initialized, choose initial selmon based on + * cursor position, and set default cursor image */ + selmon = xytomon(cursor->x, cursor->y); + + /* 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 */ + wlr_cursor_warp_closest(cursor, NULL, cursor->x, cursor->y); + wlr_xcursor_manager_set_cursor_image(cursor_mgr, "left_ptr", cursor); + /* 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 From 823cefd2920085a0f74899fb679020005a1b6e0b Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Tue, 25 May 2021 02:52:33 -0500 Subject: [PATCH 15/21] handle ephemeral pageflip failures If a transient failure occurs in wlr_output_commit, re-render until it doesn't happen. This could possibly be removed if we decide to implement damage tracking in the future. --- dwl.c | 54 +++++++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/dwl.c b/dwl.c index e0b404e..a38f452 100644 --- a/dwl.c +++ b/dwl.c @@ -1726,38 +1726,42 @@ rendermon(struct wl_listener *listener, void *data) } } - /* wlr_output_attach_render makes the OpenGL context current. */ - if (!wlr_output_attach_render(m->wlr_output, NULL)) - return; + /* HACK: This loop is the simplest way to handle ephemeral pageflip + * failures but probably not the best. Revisit if damage tracking is + * added. */ + do { + /* wlr_output_attach_render makes the OpenGL context current. */ + if (!wlr_output_attach_render(m->wlr_output, NULL)) + return; - 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); + 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); - renderlayer(&m->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &now); - renderlayer(&m->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], &now); - renderclients(m, &now); + renderlayer(&m->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &now); + renderlayer(&m->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], &now); + renderclients(m, &now); #ifdef XWAYLAND - renderindependents(m->wlr_output, &now); + renderindependents(m->wlr_output, &now); #endif - renderlayer(&m->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], &now); - renderlayer(&m->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], &now); + renderlayer(&m->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], &now); + renderlayer(&m->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], &now); - /* Hardware cursors are rendered by the GPU on a separate plane, and can be - * moved around without re-rendering what's beneath them - which is more - * efficient. However, not all hardware supports hardware cursors. For this - * reason, wlroots provides a software fallback, which we ask it to render - * here. wlr_cursor handles configuring hardware vs software cursors for you, - * and this function is a no-op when hardware cursors are in use. */ - wlr_output_render_software_cursors(m->wlr_output, NULL); + /* Hardware cursors are rendered by the GPU on a separate plane, and can be + * moved around without re-rendering what's beneath them - which is more + * efficient. However, not all hardware supports hardware cursors. For this + * reason, wlroots provides a software fallback, which we ask it to render + * here. wlr_cursor handles configuring hardware vs software cursors for you, + * and this function is a no-op when hardware cursors are in use. */ + wlr_output_render_software_cursors(m->wlr_output, NULL); - /* Conclude rendering and swap the buffers, showing the final frame - * on-screen. */ - wlr_renderer_end(drw); - } + /* Conclude rendering and swap the buffers, showing the final frame + * on-screen. */ + wlr_renderer_end(drw); + } - wlr_output_commit(m->wlr_output); + } while (!wlr_output_commit(m->wlr_output)); } void From bd2f7fbb4082d947ec2738cd31d403e9f0c10f50 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Wed, 26 May 2021 23:30:49 -0500 Subject: [PATCH 16/21] exit cleanly on INT/TERM --- dwl.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index a38f452..a8e1dcd 100644 --- a/dwl.c +++ b/dwl.c @@ -259,6 +259,7 @@ static void pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, uint32_t time); static void printstatus(void); static void quit(const Arg *arg); +static void quitsignal(int signo); 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); @@ -1578,6 +1579,12 @@ quit(const Arg *arg) wl_display_terminate(dpy); } +void +quitsignal(int signo) +{ + quit(NULL); +} + void render(struct wlr_surface *surface, int sx, int sy, void *data) { @@ -1965,8 +1972,10 @@ setup(void) * clients from the Unix socket, manging Wayland globals, and so on. */ dpy = wl_display_create(); - /* clean up child processes immediately */ + /* Set up signal handlers */ sigchld(0); + signal(SIGINT, quitsignal); + signal(SIGTERM, quitsignal); /* The backend is a wlroots feature which abstracts the underlying input and * output hardware. The autocreate option will choose the most suitable From c6f96d5391b43268f05787c4171ddc5ed4cbf0b8 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Thu, 3 Jun 2021 01:41:10 -0500 Subject: [PATCH 17/21] mention `-devel` packages It seems like there are people trying dwl who aren't as familiar with how their distros do development, so let's give them a pointer in the right direction. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 51aecf8..5041c1b 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Feature *non-goals* for the main codebase include: ## Building dwl -dwl has only two dependencies: wlroots 0.13 and wayland-protocols. Simply install these and run `make`. If you wish to build against a Git version of wlroots, check out the [wlroots-next branch](https://github.com/djpohly/dwl/tree/wlroots-next). +dwl has only two dependencies: wlroots and wayland-protocols. Simply install these (and their `-devel` versions if your distro has separate development packages) and run `make`. If you wish to build against a Git version of wlroots, check out the [wlroots-next branch](https://github.com/djpohly/dwl/tree/wlroots-next). To enable XWayland, you should also install xorg-xwayland and uncomment its flag in `config.mk`. From 3b05eadeaf5e2de4caf127cfa07642342cccddbc Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Wed, 30 Jun 2021 14:46:20 -0500 Subject: [PATCH 18/21] update notes about starting dwl Includes mention of video/input groups --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fa9d1cc..5b3e4cb 100644 --- a/README.md +++ b/README.md @@ -50,14 +50,19 @@ As in the dwm community, we encourage users to share patches they have created. ## 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 on any of the backends supported by wlroots. This means you can run it as a separate window inside either an X11 or Wayland session, as well as directly from a VT console. Depending on your distro's setup, you may need to add your user to the `video` and `input` groups before you can run dwl on a VT. -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`. +When dwl is run with no arguments, it will launch the server and begin handling any shortcuts configured in `config.h`. There is no status bar or other decoration initially; these are instead clients that can be run within the Wayland session. + +If you would like to run a script or command automatically at startup, you can specify the command using the `-s` option. The argument to this option will be parsed as a shell command (using `sh -c`) and can serve a similar function to `.xinitrc`. 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 for execing into a user service manager like [s6](https://skarnet.org/software/s6/), [anopa](https://jjacky.com/anopa/), [runit](http://smarden.org/runit/faq.html#userservices), or [`systemd --user`](https://wiki.archlinux.org/title/Systemd/User). + +Note: The `-s` command is run as a *child process* of dwl, which means that it does not have the ability to affect the environment of dwl or of any processes that it spawns. If you need to set environment variables that affect the entire dwl session (such as `XDG_RUNTIME_DIR` in the note below), these must be set prior to running dwl. 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 + dwl ## Replacements for X applications From 52e6bf47354b624e220cbff6df33d06ba1c3581e Mon Sep 17 00:00:00 2001 From: David Donahue Date: Thu, 1 Jul 2021 15:20:30 -0500 Subject: [PATCH 19/21] Moved printstatus() call in focusclient() to prevent printstatus being called on every frame when things like dmenu are up --- dwl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index a2a0b69..9c31de2 100644 --- a/dwl.c +++ b/dwl.c @@ -1083,7 +1083,6 @@ focusclient(Client *c, int lift) selmon = c->mon; c->isurgent = 0; } - printstatus(); /* Deactivate old client if focus is changing */ if (old && (!c || client_surface(c) != old)) { @@ -1106,6 +1105,8 @@ focusclient(Client *c, int lift) } } + printstatus(); + if (!c) { /* With no client, all we have left is to clear focus */ wlr_seat_keyboard_notify_clear_focus(seat); From d175a58d733723fdeb307943b034fe7fda6086ed Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Mon, 2 Aug 2021 16:33:38 +0200 Subject: [PATCH 20/21] implement the presentation time protocol This lets applications, such as mpv with --video-sync=display-resample, know accurately when frames are displayed and ensure smooth video playback. --- dwl.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dwl.c b/dwl.c index a2a0b69..20b01b0 100644 --- a/dwl.c +++ b/dwl.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -317,6 +318,7 @@ static struct wl_list independents; static struct wlr_idle *idle; static struct wlr_layer_shell_v1 *layer_shell; static struct wlr_output_manager_v1 *output_mgr; +static struct wlr_presentation *presentation; static struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard_mgr; static struct wlr_cursor *cursor; @@ -1653,6 +1655,8 @@ render(struct wlr_surface *surface, int sx, int sy, void *data) /* This lets the client know that we've displayed that frame and it can * prepare another one now if it likes. */ wlr_surface_send_frame_done(surface, rdata->when); + + wlr_presentation_surface_sampled_on_output(presentation, surface, output); } void @@ -2116,6 +2120,8 @@ setup(void) wl_signal_add(&output_mgr->events.apply, &output_mgr_apply); wl_signal_add(&output_mgr->events.test, &output_mgr_test); + presentation = wlr_presentation_create(dpy, backend); + #ifdef XWAYLAND /* * Initialise the XWayland X server. From be103859f67fefde5878c12f9410b1eb509e2ca3 Mon Sep 17 00:00:00 2001 From: Sevz17 Date: Tue, 17 Aug 2021 21:04:24 -0500 Subject: [PATCH 21/21] fix crash when foot closes --- dwl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index adb5603..875615b 100644 --- a/dwl.c +++ b/dwl.c @@ -808,7 +808,8 @@ commitnotify(struct wl_listener *listener, void *data) c->resize = 0; // Damage the whole screen - wlr_output_damage_add_whole(c->mon->damage); + if (c->mon) + wlr_output_damage_add_whole(c->mon->damage); } void