From 84b26ef1bae83cdf0f305d2c81d345c8ef73d0d0 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Thu, 8 Apr 2021 07:12:50 -0500 Subject: [PATCH 001/117] 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 22dfa0797..260280a87 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 002/117] nuke CSDs, hopefully for good! --- dwl.c | 45 ++++++--------------------------------------- 1 file changed, 6 insertions(+), 39 deletions(-) diff --git a/dwl.c b/dwl.c index 0deae8424..d463985cf 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 003/117] 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 d463985cf..f4b9b3ac3 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 004/117] re-compile if config.mk changes --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a0d1cc379..1362db8a4 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 005/117] group phony targets together in Makefile --- Makefile | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 1362db8a4..fe6ff04c6 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 006/117] 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 d463985cf..c5308f5e5 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 007/117] added uninstall target --- Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index fe6ff04c6..5ff69e953 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 008/117] update README --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 22dfa0797..51aecf845 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 009/117] 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 d7e798b9f..30a64f803 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 010/117] 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 51aecf845..dc75cef71 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 d7e798b9f..4d0bc84c0 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 011/117] 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 30a64f803..3a562b95a 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 012/117] 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 f4735c2e0..56e30897b 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 3a562b95a..9188f0640 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 013/117] 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 9188f0640..e0b404e29 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 014/117] 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 | 58 +++++++++++++++++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/dwl.c b/dwl.c index e0b404e29..a38f45235 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); - - /* 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); - } + 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); + + /* 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 015/117] exit cleanly on INT/TERM --- dwl.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index a38f45235..a8e1dcd52 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 016/117] 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 51aecf845..5041c1baa 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 017/117] 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 fa9d1cc1a..5b3e4cba6 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 018/117] 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 a2a0b692c..9c31de215 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 019/117] 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 a2a0b692c..20b01b069 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 8aa50dfdf1bd6daefe2a58f507a0d1353e47719f Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Tue, 3 Aug 2021 06:29:26 +0200 Subject: [PATCH 020/117] update IRC channel --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5b3e4cba6..3b3a72f22 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ You can find a [list of Wayland applications on the sway wiki](https://github.co ## IRC channel -dwl's IRC channel is #dwl on irc.freenode.net. +dwl's IRC channel is #dwl on irc.libera.chat. ## Acknowledgements From 3273f749ea86d3e7178955a0771d6783deae20e4 Mon Sep 17 00:00:00 2001 From: Palanix Date: Sat, 21 Aug 2021 01:53:38 +0200 Subject: [PATCH 021/117] wlr_layer_surface_v1_close has been replaced by wlr_layer_surface_v1_destroy --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index a2a0b692c..3b0694852 100644 --- a/dwl.c +++ b/dwl.c @@ -553,7 +553,7 @@ arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, int box.y -= state->margin.bottom; } if (box.width < 0 || box.height < 0) { - wlr_layer_surface_v1_close(wlr_layer_surface); + wlr_layer_surface_v1_destroy(wlr_layer_surface); continue; } layersurface->geo = box; From d4e08c07629b4534e45b292614899a6b9bb876bd Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Mon, 23 Aug 2021 18:59:31 -0500 Subject: [PATCH 022/117] update deprecated xkb function name --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index b89853765..2c1634b78 100644 --- a/dwl.c +++ b/dwl.c @@ -793,7 +793,7 @@ createkeyboard(struct wlr_input_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, + keymap = xkb_keymap_new_from_names(context, &xkb_rules, XKB_KEYMAP_COMPILE_NO_FLAGS); wlr_keyboard_set_keymap(device->keyboard, keymap); From 79dcc0d3271395fe1258d818718209254d846b1b Mon Sep 17 00:00:00 2001 From: Tobias Bengfort Date: Sat, 4 Sep 2021 13:46:58 +0200 Subject: [PATCH 023/117] reset cursor mode when grabc is unmapped --- dwl.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dwl.c b/dwl.c index 2c1634b78..c798685fa 100644 --- a/dwl.c +++ b/dwl.c @@ -2292,6 +2292,10 @@ unmapnotify(struct wl_listener *listener, void *data) { /* Called when the surface is unmapped, and should no longer be shown. */ Client *c = wl_container_of(listener, c, unmap); + if (c == grabc) { + cursor_mode = CurNormal; + grabc = NULL; + } wl_list_remove(&c->link); if (client_is_unmanaged(c)) return; From 2e9c4d8ea90e47667ef4fa417aa3e6f46034699e Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sun, 5 Sep 2021 11:41:23 -0500 Subject: [PATCH 024/117] simplify client_for_each_surface All the XDG surface iterator does is iterate the main wlr_surface, then iterate the popups. If we inline that function, we can merge part of it with the X11 case. --- client.h | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/client.h b/client.h index 56e30897b..4fd186353 100644 --- a/client.h +++ b/client.h @@ -5,7 +5,7 @@ * that they will simply compile out if the chosen #defines leave them unused. */ -/* Leave this function first; it's used in the others */ +/* Leave these functions first; they're used in the others */ static inline int client_is_x11(Client *c) { @@ -16,6 +16,16 @@ client_is_x11(Client *c) #endif } +static inline struct wlr_surface * +client_surface(Client *c) +{ +#ifdef XWAYLAND + if (client_is_x11(c)) + return c->surface.xwayland->surface; +#endif + return c->surface.xdg->surface; +} + /* The others */ static inline void client_activate_surface(struct wlr_surface *s, int activated) @@ -35,14 +45,12 @@ client_activate_surface(struct wlr_surface *s, int activated) static inline void client_for_each_surface(Client *c, wlr_surface_iterator_func_t fn, void *data) { + wlr_surface_for_each_surface(client_surface(c), fn, data); #ifdef XWAYLAND - if (client_is_x11(c)) { - wlr_surface_for_each_surface(c->surface.xwayland->surface, - fn, data); + if (client_is_x11(c)) return; - } #endif - wlr_xdg_surface_for_each_surface(c->surface.xdg, fn, data); + wlr_xdg_surface_for_each_popup_surface(c->surface.xdg, fn, data); } static inline const char * @@ -152,16 +160,6 @@ client_set_tiled(Client *c, uint32_t edges) WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT); } -static inline struct wlr_surface * -client_surface(Client *c) -{ -#ifdef XWAYLAND - if (client_is_x11(c)) - return c->surface.xwayland->surface; -#endif - return c->surface.xdg->surface; -} - static inline struct wlr_surface * client_surface_at(Client *c, double cx, double cy, double *sx, double *sy) { From 0c1e621b82fb55b5994ae5ab9956160a47dfec80 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Wed, 8 Sep 2021 23:16:56 -0500 Subject: [PATCH 025/117] simplify fullscreen expression --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 2c1634b78..1951adb44 100644 --- a/dwl.c +++ b/dwl.c @@ -1025,7 +1025,7 @@ void setfullscreen(Client *c, int fullscreen) { c->isfullscreen = fullscreen; - c->bw = (1 - fullscreen) * borderpx; + c->bw = fullscreen ? 0 : borderpx; client_set_fullscreen(c, fullscreen); if (fullscreen) { From 929d3d9569086963c7597dc1fcd38d59896278bc Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sun, 5 Sep 2021 15:55:36 -0500 Subject: [PATCH 026/117] use type enum to distinguish Client from LayerSurface --- dwl.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index c4ca60fad..08f7e32e8 100644 --- a/dwl.c +++ b/dwl.c @@ -65,10 +65,10 @@ /* enums */ enum { CurNormal, CurMove, CurResize }; /* cursor */ +enum { XDGShell, LayerShell, X11Managed, X11Unmanaged }; /* client types */ #ifdef XWAYLAND enum { NetWMWindowTypeDialog, NetWMWindowTypeSplash, NetWMWindowTypeToolbar, NetWMWindowTypeUtility, NetLast }; /* EWMH atoms */ -enum { XDGShell, X11Managed, X11Unmanaged }; /* client types */ #endif typedef union { @@ -87,6 +87,8 @@ typedef struct { typedef struct Monitor Monitor; typedef struct { + /* Must be first */ + unsigned int type; /* XDGShell or X11* */ struct wl_list link; struct wl_list flink; struct wl_list slink; @@ -103,7 +105,6 @@ typedef struct { struct wlr_box geom; /* layout-relative, includes border */ Monitor *mon; #ifdef XWAYLAND - unsigned int type; struct wl_listener activate; struct wl_listener configure; #endif @@ -140,6 +141,8 @@ typedef struct { } Keyboard; typedef struct { + /* Must be first */ + unsigned int type; /* LayerShell */ struct wlr_layer_surface_v1 *layer_surface; struct wl_list link; @@ -917,6 +920,7 @@ createlayersurface(struct wl_listener *listener, void *data) } layersurface = calloc(1, sizeof(LayerSurface)); + layersurface->type = LayerShell; LISTEN(&wlr_layer_surface->surface->events.commit, &layersurface->surface_commit, commitlayersurfacenotify); LISTEN(&wlr_layer_surface->events.destroy, &layersurface->destroy, From 1b38801eef319a9f8b618bf29564104af6b0a39d Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sun, 5 Sep 2021 16:07:28 -0500 Subject: [PATCH 027/117] use scene-graph API for Client/LayerSurface --- dwl.c | 424 ++++++++++++++++------------------------------------------ 1 file changed, 116 insertions(+), 308 deletions(-) diff --git a/dwl.c b/dwl.c index 08f7e32e8..2f4bb4c96 100644 --- a/dwl.c +++ b/dwl.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -60,12 +61,12 @@ #define LENGTH(X) (sizeof X / sizeof X[0]) #define END(A) ((A) + LENGTH(A)) #define TAGMASK ((1 << LENGTH(tags)) - 1) -#define ROUND(X) ((int)((X)+0.5)) #define LISTEN(E, L, H) wl_signal_add((E), ((L)->notify = (H), (L))) /* enums */ enum { CurNormal, CurMove, CurResize }; /* cursor */ enum { XDGShell, LayerShell, X11Managed, X11Unmanaged }; /* client types */ +enum { LyrBg, LyrBottom, LyrTop, LyrOverlay, LyrTile, LyrFloat, NUM_LAYERS }; /* scene layers */ #ifdef XWAYLAND enum { NetWMWindowTypeDialog, NetWMWindowTypeSplash, NetWMWindowTypeToolbar, NetWMWindowTypeUtility, NetLast }; /* EWMH atoms */ @@ -89,9 +90,11 @@ typedef struct Monitor Monitor; typedef struct { /* Must be first */ unsigned int type; /* XDGShell or X11* */ + struct wlr_scene_node *scene; + struct wlr_scene_rect *border[4]; + struct wlr_scene_node *scene_surface; struct wl_list link; struct wl_list flink; - struct wl_list slink; union { struct wlr_xdg_surface *xdg; struct wlr_xwayland_surface *xwayland; @@ -143,8 +146,9 @@ typedef struct { typedef struct { /* Must be first */ unsigned int type; /* LayerShell */ - struct wlr_layer_surface_v1 *layer_surface; + struct wlr_scene_node *scene; struct wl_list link; + struct wlr_layer_surface_v1 *layer_surface; struct wl_listener destroy; struct wl_listener map; @@ -203,14 +207,6 @@ typedef struct { int monitor; } Rule; -/* Used to move all of the data necessary to render a surface from the top-level - * frame handler to the per-surface render function. */ -struct render_data { - struct wlr_output *output; - struct timespec *when; - int x, y; /* layout-relative */ -}; - /* function declarations */ static void applybounds(Client *c, struct wlr_box *bbox); static void applyexclusive(struct wlr_box *usable_area, uint32_t anchor, @@ -265,13 +261,9 @@ static void pointerfocus(Client *c, struct wlr_surface *surface, 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); 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 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); @@ -299,9 +291,8 @@ 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); -static struct wlr_surface *xytolayersurface(struct wl_list *layer_surfaces, - double x, double y, double *sx, double *sy); +static struct wlr_scene_node *xytonode(double x, double y, struct wlr_surface **psurface, + Client **pc, LayerSurface **pl, double *nx, double *ny); static Monitor *xytomon(double x, double y); static void zoom(const Arg *arg); @@ -309,6 +300,8 @@ static void zoom(const Arg *arg); static const char broken[] = "broken"; static struct wl_display *dpy; static struct wlr_backend *backend; +static struct wlr_scene *scene; +static struct wlr_scene_node *layers[NUM_LAYERS]; static struct wlr_renderer *drw; static struct wlr_compositor *compositor; @@ -316,7 +309,6 @@ 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 */ static struct wl_list independents; static struct wlr_idle *idle; static struct wlr_layer_shell_v1 *layer_shell; @@ -366,9 +358,7 @@ 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); static void xwaylandready(struct wl_listener *listener, void *data); -static Client *xytoindependent(double x, double y); static struct wl_listener new_xwayland_surface = {.notify = createnotifyx11}; static struct wl_listener xwayland_ready = {.notify = xwaylandready}; static struct wlr_xwayland *xwayland; @@ -483,12 +473,17 @@ applyrules(Client *c) mon = m; } } + wlr_scene_node_reparent(c->scene, layers[c->isfloating ? LyrFloat : LyrTile]); setmon(c, mon, newtags); } void arrange(Monitor *m) { + Client *c; + wl_list_for_each(c, &clients, link) + wlr_scene_node_set_enabled(c->scene, VISIBLEON(c, c->mon)); + if (m->lt[m->sellt]->arrange) m->lt[m->sellt]->arrange(m); /* TODO recheck pointer focus here... or in resize()? */ @@ -567,6 +562,7 @@ arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, int applyexclusive(usable_area, state->anchor, state->exclusive_zone, state->margin.top, state->margin.right, state->margin.bottom, state->margin.left); + wlr_scene_node_set_position(layersurface->scene, box.x, box.y); wlr_layer_surface_v1_configure(wlr_layer_surface, box.width, box.height); } } @@ -650,7 +646,8 @@ buttonpress(struct wl_listener *listener, void *data) switch (event->state) { case WLR_BUTTON_PRESSED:; /* Change focus if the button was _pressed_ over a client */ - if ((c = xytoclient(cursor->x, cursor->y))) + xytonode(cursor->x, cursor->y, NULL, &c, NULL, NULL, NULL); + if (c) focusclient(c, 1); keyboard = wlr_seat_get_keyboard(seat); @@ -762,6 +759,9 @@ commitlayersurfacenotify(struct wl_listener *listener, void *data) struct wlr_output *wlr_output = wlr_layer_surface->output; Monitor *m; + wlr_scene_node_reparent(layersurface->scene, + layers[wlr_layer_surface->current.layer]); + if (!wlr_output) return; @@ -932,8 +932,13 @@ createlayersurface(struct wl_listener *listener, void *data) layersurface->layer_surface = wlr_layer_surface; wlr_layer_surface->data = layersurface; - m = wlr_layer_surface->output->data; + + layersurface->scene = wlr_scene_surface_tree_create( + layers[wlr_layer_surface->client_pending.layer], + wlr_layer_surface->surface); + layersurface->scene->data = layersurface; + wl_list_insert(&m->layers[wlr_layer_surface->client_pending.layer], &layersurface->link); @@ -1075,8 +1080,9 @@ focusclient(Client *c, int lift) /* Raise client in stacking order if requested */ if (c && lift) { - wl_list_remove(&c->slink); - wl_list_insert(&stack, &c->slink); + /* This isn't easy to do via the current API */ + wl_list_remove(&c->scene->state.link); + wl_list_insert(c->scene->parent->state.children.prev, &c->scene->state.link); } if (c && client_surface(c) == old) @@ -1303,27 +1309,40 @@ 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); + int i; + + /* Create scene tree for this client and its border */ + c->scene = &wlr_scene_tree_create(layers[LyrTile])->node; + c->scene_surface = wlr_scene_surface_tree_create(c->scene, client_surface(c)); + c->scene_surface->data = c; + for (i = 0; i < 4; i++) { + c->border[i] = wlr_scene_rect_create(c->scene, 0, 0, bordercolor); + c->border[i]->node.data = c; + } if (client_is_unmanaged(c)) { + /* Floating, no border */ + wlr_scene_node_reparent(c->scene, layers[LyrFloat]); + c->bw = 0; + /* Insert this independent into independents lists. */ wl_list_insert(&independents, &c->link); return; } - /* Insert this client into client lists. */ - wl_list_insert(&clients, &c->link); - wl_list_insert(&fstack, &c->flink); - wl_list_insert(&stack, &c->slink); - + /* Initialize client geometry with room for border */ + client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT); client_get_geometry(c, &c->geom); c->geom.width += 2 * c->bw; c->geom.height += 2 * c->bw; - /* Tell the client not to try anything fancy */ - client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT); + /* Insert this client into client lists. */ + wl_list_insert(&clients, &c->link); + wl_list_insert(&fstack, &c->flink); /* Set initial monitor, tags, floating status, and focus */ applyrules(c); + resize(c, c->geom.x, c->geom.y, c->geom.width, c->geom.height, 0); } void @@ -1356,8 +1375,8 @@ void motionnotify(uint32_t time) { double sx = 0, sy = 0; - struct wlr_surface *surface = NULL; Client *c = NULL; + struct wlr_surface *surface = NULL; // time is 0 in internal calls meant to restore pointer focus. if (time) { @@ -1381,32 +1400,8 @@ motionnotify(uint32_t time) return; } - if ((surface = xytolayersurface(&selmon->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], - cursor->x, cursor->y, &sx, &sy))) - ; - else if ((surface = xytolayersurface(&selmon->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], - cursor->x, cursor->y, &sx, &sy))) - ; -#ifdef XWAYLAND - /* Find an independent under the pointer and send the event along. */ - else if ((c = xytoindependent(cursor->x, cursor->y))) { - surface = wlr_surface_surface_at(c->surface.xwayland->surface, - cursor->x - c->surface.xwayland->x - c->bw, - cursor->y - c->surface.xwayland->y - c->bw, &sx, &sy); - - /* Otherwise, find the client under the pointer and send the event along. */ - } -#endif - else if ((c = xytoclient(cursor->x, cursor->y))) { - surface = client_surface_at(c, cursor->x - c->geom.x - c->bw, - cursor->y - c->geom.y - c->bw, &sx, &sy); - } - else if ((surface = xytolayersurface(&selmon->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], - cursor->x, cursor->y, &sx, &sy))) - ; - else - surface = xytolayersurface(&selmon->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], - cursor->x, cursor->y, &sx, &sy); + /* Find the client under the pointer and send the event along. */ + xytonode(cursor->x, cursor->y, &surface, &c, NULL, &sx, &sy); /* 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 @@ -1437,7 +1432,10 @@ motionrelative(struct wl_listener *listener, void *data) void moveresize(const Arg *arg) { - if (cursor_mode != CurNormal || !(grabc = xytoclient(cursor->x, cursor->y))) + if (cursor_mode != CurNormal) + return; + xytonode(cursor->x, cursor->y, NULL, &grabc, NULL, NULL, NULL); + if (!grabc) return; /* Float the window and tell motionnotify to grab it */ @@ -1604,140 +1602,11 @@ quitsignal(int signo) quit(NULL); } -void -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 - * resource could be an opaque handle passed from the client, or the client - * could have sent a pixel buffer which we copied to the GPU, or a few other - * means. You don't have to worry about this, wlroots takes care of it. */ - struct wlr_texture *texture = wlr_surface_get_texture(surface); - if (!texture) - return; - - /* The client has a position in layout coordinates. If you have two displays, - * 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). */ - 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; - scalebox(&obox, output->scale); - - /* - * Those familiar with OpenGL are also familiar with the role of matrices - * in graphics programming. We need to prepare a matrix to render the - * client with. wlr_matrix_project_box is a helper which takes a box with - * a desired x, y coordinates, width and height, and an output geometry, - * then prepares an orthographic projection and multiplies the necessary - * transforms to produce a model-view-projection matrix. - * - * Naturally you can do this any way you like, for example to make a 3D - * compositor. - */ - transform = wlr_output_transform_invert(surface->current.transform); - wlr_matrix_project_box(matrix, &obox, transform, 0, - output->transform_matrix); - - /* 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); - - /* 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 -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) { - /* Only render visible clients which show on this monitor */ - if (!VISIBLEON(c, c->mon) || !wlr_output_layout_intersects( - output_layout, m->wlr_output, &c->geom)) - continue; - - surface = client_surface(c); - ox = c->geom.x, oy = c->geom.y; - wlr_output_layout_output_coords(output_layout, m->wlr_output, - &ox, &oy); - - if (c->bw) { - w = surface->current.width; - h = surface->current.height; - 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 */ - {ox, oy + c->bw + h, w + 2 * c->bw, c->bw}, /* bottom */ - }; - - /* Draw window borders */ - color = (c == sel) ? focuscolor : bordercolor; - for (i = 0; i < 4; i++) { - scalebox(&borders[i], m->wlr_output->scale); - wlr_render_rect(drw, &borders[i], color, - m->wlr_output->transform_matrix); - } - } - - /* 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; - client_for_each_surface(c, render, &rdata); - } -} - -void -renderlayer(struct wl_list *layer_surfaces, struct timespec *now) -{ - LayerSurface *layersurface; - wl_list_for_each(layersurface, layer_surfaces, link) { - 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); - } -} - void rendermon(struct wl_listener *listener, void *data) { Client *c; - int render = 1; + int skip = 0; /* This function is called every time an output is ready to display a frame, * generally at the output's refresh rate (e.g. 60Hz). */ @@ -1746,13 +1615,9 @@ rendermon(struct wl_listener *listener, void *data) struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - /* Do not render if any XDG clients have an outstanding resize. */ - wl_list_for_each(c, &stack, slink) { - if (c->resize) { - wlr_surface_send_frame_done(client_surface(c), &now); - render = 0; - } - } + /* Skip rendering if any XDG clients have an outstanding resize. */ + wl_list_for_each(c, &clients, link) + skip = skip || c->resize; /* HACK: This loop is the simplest way to handle ephemeral pageflip * failures but probably not the best. Revisit if damage tracking is @@ -1762,50 +1627,46 @@ rendermon(struct wl_listener *listener, void *data) if (!wlr_output_attach_render(m->wlr_output, NULL)) return; - if (render) { + if (!skip) { /* 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); -#ifdef XWAYLAND - 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); - - /* 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); + /* Render the scene at (-mx, -my) to get this monitor's view. + * wlroots will not render windows falling outside the box. */ + wlr_scene_render_output(scene, m->wlr_output, -m->m.x, -m->m.y, NULL); /* Conclude rendering and swap the buffers, showing the final frame * on-screen. */ wlr_renderer_end(drw); } - } while (!wlr_output_commit(m->wlr_output)); + + /* Let clients know a frame has been rendered */ + wl_list_for_each(c, &clients, link) + wlr_surface_send_frame_done(client_surface(c), &now); } void resize(Client *c, int x, int y, int w, int h, int interact) { - /* - * Note that I took some shortcuts here. In a more fleshed-out - * 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; applybounds(c, bbox); + + /* Update scene-graph, including borders */ + wlr_scene_node_set_position(c->scene, c->geom.x, c->geom.y); + wlr_scene_node_set_position(c->scene_surface, c->bw, c->bw); + wlr_scene_rect_set_size(c->border[0], c->geom.width, c->bw); + wlr_scene_rect_set_size(c->border[1], c->geom.width, c->bw); + wlr_scene_rect_set_size(c->border[2], c->bw, c->geom.height - 2 * c->bw); + wlr_scene_rect_set_size(c->border[3], c->bw, c->geom.height - 2 * c->bw); + wlr_scene_node_set_position(&c->border[1]->node, 0, c->geom.height - c->bw); + wlr_scene_node_set_position(&c->border[3]->node, c->geom.width - c->bw, c->bw); + /* wlroots makes this a no-op if size hasn't changed */ c->resize = client_set_size(c, c->geom.width - 2 * c->bw, c->geom.height - 2 * c->bw); @@ -1870,15 +1731,6 @@ run(char *startup_cmd) } } -void -scalebox(struct wlr_box *box, float 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 * selclient(void) { @@ -1911,6 +1763,7 @@ void setfloating(Client *c, int floating) { c->isfloating = floating; + wlr_scene_node_reparent(c->scene, layers[c->isfloating ? LyrFloat : LyrTile]); arrange(c->mon); } @@ -1957,7 +1810,7 @@ setmon(Client *c, Monitor *m, unsigned int newtags) } if (m) { /* Make sure window actually overlaps with the monitor */ - applybounds(c, &m->m); + resize(c, c->geom.x, c->geom.y, c->geom.width, c->geom.height, 0); wlr_surface_send_enter(client_surface(c), m->wlr_output); c->tags = newtags ? newtags : m->tagset[m->seltags]; /* assign tags of target monitor */ arrange(m); @@ -2010,6 +1863,15 @@ setup(void) if (!(backend = wlr_backend_autocreate(dpy))) BARF("couldn't create backend"); + /* Initialize the scene graph used to lay out windows */ + scene = wlr_scene_create(); + layers[LyrBg] = &wlr_scene_tree_create(&scene->node)->node; + layers[LyrBottom] = &wlr_scene_tree_create(&scene->node)->node; + layers[LyrTile] = &wlr_scene_tree_create(&scene->node)->node; + layers[LyrFloat] = &wlr_scene_tree_create(&scene->node)->node; + layers[LyrTop] = &wlr_scene_tree_create(&scene->node)->node; + layers[LyrOverlay] = &wlr_scene_tree_create(&scene->node)->node; + /* 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. */ @@ -2054,7 +1916,6 @@ setup(void) */ wl_list_init(&clients); wl_list_init(&fstack); - wl_list_init(&stack); wl_list_init(&independents); idle = wlr_idle_create(dpy); @@ -2302,7 +2163,7 @@ unmapnotify(struct wl_listener *listener, void *data) setmon(c, NULL, 0); wl_list_remove(&c->flink); - wl_list_remove(&c->slink); + wlr_scene_node_destroy(c->scene); } void @@ -2386,37 +2247,31 @@ virtualkeyboard(struct wl_listener *listener, void *data) createkeyboard(device); } -Client * -xytoclient(double x, double y) -{ - /* Find the topmost visible client (if any) at point (x, y), including - * borders. This relies on stack being ordered from top to bottom. */ - Client *c; - wl_list_for_each(c, &stack, slink) - if (VISIBLEON(c, c->mon) && wlr_box_contains_point(&c->geom, x, y)) - return c; - return NULL; -} - -struct wlr_surface * -xytolayersurface(struct wl_list *layer_surfaces, double x, double y, - double *sx, double *sy) +struct wlr_scene_node * +xytonode(double x, double y, struct wlr_surface **psurface, + Client **pc, LayerSurface **pl, double *nx, double *ny) { - LayerSurface *layersurface; - wl_list_for_each_reverse(layersurface, layer_surfaces, link) { - struct wlr_surface *sub; - if (!layersurface->layer_surface->mapped) - continue; - sub = wlr_layer_surface_v1_surface_at( - layersurface->layer_surface, - x - layersurface->geo.x, - y - layersurface->geo.y, - sx, sy); - if (sub) - return sub; - + struct wlr_scene_node *node, *pnode; + struct wlr_surface *surface = NULL; + Client *c = NULL; + LayerSurface *l = NULL; + + if ((node = wlr_scene_node_at(&scene->node, x, y, nx, ny))) { + if (node->type == WLR_SCENE_NODE_SURFACE) + surface = wlr_scene_surface_from_node(node)->surface; + /* Walk the tree to find a node that knows the client */ + for (pnode = node; pnode && !c; pnode = pnode->parent) + c = pnode->data; + if (c && c->type == LayerShell) { + c = NULL; + l = pnode->data; + } } - return NULL; + + if (psurface) *psurface = surface; + if (pc) *pc = c; + if (pl) *pl = l; + return node; } Monitor * @@ -2520,31 +2375,6 @@ getatom(xcb_connection_t *xc, const char *name) return atom; } -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; - - /* 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; - wlr_surface_for_each_surface(c->surface.xwayland->surface, render, &rdata); - } -} - void xwaylandready(struct wl_listener *listener, void *data) { @@ -2575,28 +2405,6 @@ xwaylandready(struct wl_listener *listener, void *data) xcb_disconnect(xc); } - -Client * -xytoindependent(double x, double y) -{ - /* Find the topmost visible independent at point (x, y). - * For independents, the most recently created can be used as the "top". - * We rely on the X11 convention of unmapping unmanaged when the "owning" - * client loses focus, which ensures that unmanaged are only visible on - * the current tag. */ - Client *c; - wl_list_for_each_reverse(c, &independents, link) { - 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; - } - return NULL; -} #endif int From be6f573b4ef723a3985489b0ac0eb035d7c34420 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sun, 5 Sep 2021 16:09:26 -0500 Subject: [PATCH 028/117] use scene to keep track of LayerSurfaces' layers --- dwl.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/dwl.c b/dwl.c index 2f4bb4c96..de4e573a9 100644 --- a/dwl.c +++ b/dwl.c @@ -156,7 +156,6 @@ typedef struct { struct wl_listener surface_commit; struct wlr_box geo; - enum zwlr_layer_shell_v1_layer layer; } LayerSurface; typedef struct { @@ -764,16 +763,14 @@ commitlayersurfacenotify(struct wl_listener *listener, void *data) if (!wlr_output) return; - m = wlr_output->data; - arrangelayers(m); - if (layersurface->layer != wlr_layer_surface->current.layer) { + if (layers[wlr_layer_surface->current.layer] != layersurface->scene) { wl_list_remove(&layersurface->link); wl_list_insert(&m->layers[wlr_layer_surface->current.layer], &layersurface->link); - layersurface->layer = wlr_layer_surface->current.layer; } + arrangelayers(m); } void From 0146a9954b2735a08b326abd69492ac3803ec709 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sun, 5 Sep 2021 22:35:07 -0500 Subject: [PATCH 029/117] use scene_output for damage-tracked rendering --- dwl.c | 53 ++++++++++------------------------------------------- 1 file changed, 10 insertions(+), 43 deletions(-) diff --git a/dwl.c b/dwl.c index de4e573a9..2b37533fd 100644 --- a/dwl.c +++ b/dwl.c @@ -174,6 +174,7 @@ typedef struct { struct Monitor { struct wl_list link; struct wlr_output *wlr_output; + struct wlr_scene_output *scene_output; struct wl_listener frame; struct wl_listener destroy; struct wlr_box m; /* monitor area, layout-relative */ @@ -862,20 +863,8 @@ 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(output_layout, wlr_output, r->x, r->y); - sgeom = *wlr_output_layout_get_box(output_layout, NULL); - - /* When adding monitors, the geometries of all monitors must be updated */ - wl_list_for_each(m, &mons, link) { - /* The first monitor in the list is the most recently added */ - 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; - } + m->scene_output = wlr_scene_output_create(scene, wlr_output); + wlr_output_layout_add_auto(output_layout, wlr_output); } void @@ -1602,44 +1591,21 @@ quitsignal(int signo) void rendermon(struct wl_listener *listener, void *data) { - Client *c; - int skip = 0; - /* 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); - + Client *c; + int skip = 0; struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - /* Skip rendering if any XDG clients have an outstanding resize. */ + /* Render if no XDG clients have an outstanding resize. */ wl_list_for_each(c, &clients, link) skip = skip || c->resize; - - /* 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 (!skip) { - /* 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); - - /* Render the scene at (-mx, -my) to get this monitor's view. - * wlroots will not render windows falling outside the box. */ - wlr_scene_render_output(scene, m->wlr_output, -m->m.x, -m->m.y, NULL); - - /* Conclude rendering and swap the buffers, showing the final frame - * on-screen. */ - wlr_renderer_end(drw); - } - } while (!wlr_output_commit(m->wlr_output)); + if (!skip && !wlr_scene_output_commit(m->scene_output)) + return; /* Let clients know a frame has been rendered */ + clock_gettime(CLOCK_MONOTONIC, &now); wl_list_for_each(c, &clients, link) wlr_surface_send_frame_done(client_surface(c), &now); } @@ -2186,6 +2152,7 @@ updatemons(struct wl_listener *listener, void *data) /* Get the effective monitor geometry to use for surfaces */ m->m = m->w = *wlr_output_layout_get_box(output_layout, m->wlr_output); + wlr_scene_output_set_position(m->scene_output, m->m.x, m->m.y); /* Calculate the effective monitor geometry to use for clients */ arrangelayers(m); /* Don't move clients to the left output when plugging monitors */ From c8bf457c0f12955f89abf88e929a97d96d6f46de Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Tue, 21 Sep 2021 10:42:43 -0500 Subject: [PATCH 030/117] fixup: follow name change on surface_tree_create --- dwl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index 2b37533fd..a15bbd8c1 100644 --- a/dwl.c +++ b/dwl.c @@ -920,7 +920,7 @@ createlayersurface(struct wl_listener *listener, void *data) wlr_layer_surface->data = layersurface; m = wlr_layer_surface->output->data; - layersurface->scene = wlr_scene_surface_tree_create( + layersurface->scene = wlr_scene_subsurface_tree_create( layers[wlr_layer_surface->client_pending.layer], wlr_layer_surface->surface); layersurface->scene->data = layersurface; @@ -1299,7 +1299,7 @@ mapnotify(struct wl_listener *listener, void *data) /* Create scene tree for this client and its border */ c->scene = &wlr_scene_tree_create(layers[LyrTile])->node; - c->scene_surface = wlr_scene_surface_tree_create(c->scene, client_surface(c)); + c->scene_surface = wlr_scene_subsurface_tree_create(c->scene, client_surface(c)); c->scene_surface->data = c; for (i = 0; i < 4; i++) { c->border[i] = wlr_scene_rect_create(c->scene, 0, 0, bordercolor); From 7de6920bd781d77b2d8d5abb847258c6153638c7 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Tue, 21 Sep 2021 14:42:36 -0500 Subject: [PATCH 031/117] send frame_done to all visible surfaces --- dwl.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index a15bbd8c1..9e811ca7b 100644 --- a/dwl.c +++ b/dwl.c @@ -261,6 +261,7 @@ static void pointerfocus(Client *c, struct wlr_surface *surface, static void printstatus(void); static void quit(const Arg *arg); static void quitsignal(int signo); +static void rendered(struct wlr_surface *surface, int sx, int sy, void *data); 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 run(char *startup_cmd); @@ -1588,6 +1589,13 @@ quitsignal(int signo) quit(NULL); } +void +rendered(struct wlr_surface *surface, int sx, int sy, void *data) +{ + struct timespec *now = data; + wlr_surface_send_frame_done(surface, now); +} + void rendermon(struct wl_listener *listener, void *data) { @@ -1607,7 +1615,8 @@ rendermon(struct wl_listener *listener, void *data) /* Let clients know a frame has been rendered */ clock_gettime(CLOCK_MONOTONIC, &now); wl_list_for_each(c, &clients, link) - wlr_surface_send_frame_done(client_surface(c), &now); + if (VISIBLEON(c, c->mon)) + client_for_each_surface(c, rendered, &now); } void From 1e1482adcb3bad768c51b47db9bd86b9e54a9001 Mon Sep 17 00:00:00 2001 From: Leonardo Hernandez Hernandez Date: Fri, 24 Sep 2021 16:12:12 -0500 Subject: [PATCH 032/117] client_pending has been renamed as pending in wlr_layer_surface_v1 as seen in swaywm/wlroots@59fa363 --- dwl.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dwl.c b/dwl.c index a876e8c0a..4a58770e7 100644 --- a/dwl.c +++ b/dwl.c @@ -930,13 +930,13 @@ createlayersurface(struct wl_listener *listener, void *data) wlr_layer_surface->data = layersurface; m = wlr_layer_surface->output->data; - wl_list_insert(&m->layers[wlr_layer_surface->client_pending.layer], + wl_list_insert(&m->layers[wlr_layer_surface->pending.layer], &layersurface->link); - // Temporarily set the layer's current state to client_pending + // Temporarily set the layer's current state to pending // so that we can easily arrange it old_state = wlr_layer_surface->current; - wlr_layer_surface->current = wlr_layer_surface->client_pending; + wlr_layer_surface->current = wlr_layer_surface->pending; arrangelayers(m); wlr_layer_surface->current = old_state; } From 99fbebcae35aae1d9b673bddb6ebd6eb05feeecf Mon Sep 17 00:00:00 2001 From: ARDiDo <90479315+ARDiDo@users.noreply.github.com> Date: Sun, 26 Sep 2021 20:19:36 -0400 Subject: [PATCH 033/117] Remove redundant xcursor manager --- dwl.c | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/dwl.c b/dwl.c index 2c1634b78..6303c25f8 100644 --- a/dwl.c +++ b/dwl.c @@ -323,10 +323,6 @@ static struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard_mgr; static struct wlr_cursor *cursor; static struct wlr_xcursor_manager *cursor_mgr; -#ifdef XWAYLAND -static struct wlr_xcursor *xcursor; -static struct wlr_xcursor_manager *xcursor_mgr; -#endif static struct wlr_seat *seat; static struct wl_list keyboards; @@ -2133,18 +2129,6 @@ setup(void) wl_signal_add(&xwayland->events.ready, &xwayland_ready); wl_signal_add(&xwayland->events.new_surface, &new_xwayland_surface); - /* - * Create the XWayland cursor manager at scale 1, setting its default - * pointer to match the rest of dwl. - */ - xcursor_mgr = wlr_xcursor_manager_create(NULL, 24); - wlr_xcursor_manager_load(xcursor_mgr, 1); - if ((xcursor = wlr_xcursor_manager_get_xcursor(xcursor_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); - setenv("DISPLAY", xwayland->display_name, 1); } else { fprintf(stderr, "failed to setup XWayland X server, continuing without it\n"); From df332de9d22f0680a362cd9408a96df58a1d04bc Mon Sep 17 00:00:00 2001 From: Leonardo Hernandez Hernandez Date: Fri, 24 Sep 2021 16:07:06 -0500 Subject: [PATCH 034/117] send frame_done also to all layer surfaces this fixes an issue when bemenu don't update his surface when typing --- dwl.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 9e811ca7b..50ac1b1b7 100644 --- a/dwl.c +++ b/dwl.c @@ -1603,7 +1603,8 @@ rendermon(struct wl_listener *listener, void *data) * generally at the output's refresh rate (e.g. 60Hz). */ Monitor *m = wl_container_of(listener, m, frame); Client *c; - int skip = 0; + LayerSurface *layer; + int i, skip = 0; struct timespec now; /* Render if no XDG clients have an outstanding resize. */ @@ -1617,6 +1618,11 @@ rendermon(struct wl_listener *listener, void *data) wl_list_for_each(c, &clients, link) if (VISIBLEON(c, c->mon)) client_for_each_surface(c, rendered, &now); + + for (i = 0; i < LENGTH(m->layers); i++) + wl_list_for_each(layer, &m->layers[i], link) + wlr_layer_surface_v1_for_each_surface(layer->layer_surface, rendered, &now); + } void From 2c9423d1b7a387d63b07b96ecbda725f1e16002b Mon Sep 17 00:00:00 2001 From: Leonardo Hernandez Hernandez Date: Sun, 3 Oct 2021 22:08:00 -0500 Subject: [PATCH 035/117] `wlr_xdg_surface.configure_serial` has been moved into `wlr_xdg_surface_state` as seen in swaywm/wlroots@0e34208 --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 4a58770e7..868d57b8c 100644 --- a/dwl.c +++ b/dwl.c @@ -779,7 +779,7 @@ commitnotify(struct wl_listener *listener, void *data) Client *c = wl_container_of(listener, c, commit); /* mark a pending resize as completed */ - if (c->resize && c->resize <= c->surface.xdg->configure_serial) + if (c->resize && c->resize <= c->surface.xdg->current.configure_serial) c->resize = 0; } From 2d9740c2fc481d42eb0deace797553172cc234d9 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Wed, 6 Oct 2021 13:00:05 -0500 Subject: [PATCH 036/117] document status information and <&- in README As mentioned in #158. --- README.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5b3e4cba6..79a7d8f4f 100644 --- a/README.md +++ b/README.md @@ -54,16 +54,24 @@ dwl can be run on any of the backends supported by wlroots. This means you can r 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). +If you would like to run a script or command automatically at startup, you can specify the command using the `-s` option. This command will be executed as a shell command using `/bin/sh -c`. It serves a similar function to `.xinitrc`, but differs in that the display server will not shut down when this process terminates. Instead, dwl will send this process a SIGTERM at shutdown 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.: +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, these must be set prior to running dwl. For example, 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 +### Status information + +Information about selected layouts, current window title, and selected/occupied/urgent tags is written to the stdin of the `-s` command (see the `printstatus()` function for details). This information can be used to populate an external status bar with a script that parses the information. Failing to read this information will cause dwl to block, so if you do want to run a startup command that does not consume the status information, you can close standard input with the `<&-` shell redirection, for example: + + dwl -s 'foot --server <&-' + +If your startup command is a shell script, you can achieve the same inside the script with the line + + exec <&- + ## Replacements for X applications You can find a [list of Wayland applications on the sway wiki](https://github.com/swaywm/sway/wiki/i3-Migration-Guide). From ebfefa84bad5a930df4df85b150c27f1d4fe6de6 Mon Sep 17 00:00:00 2001 From: Humm Date: Wed, 13 Oct 2021 23:11:40 +0200 Subject: [PATCH 037/117] -s: close unused fds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dup2 doesn’t close fds, it only duplicates them. The old ones weren’t closed, causing problems (like dwl blocking due to the child process never reading from the reading end, even if stdin has been closed). --- dwl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dwl.c b/dwl.c index 6303c25f8..f84460f7d 100644 --- a/dwl.c +++ b/dwl.c @@ -1823,11 +1823,13 @@ run(char *startup_cmd) EBARF("startup: fork"); if (startup_pid == 0) { dup2(piperw[0], STDIN_FILENO); + close(piperw[0]); close(piperw[1]); execl("/bin/sh", "/bin/sh", "-c", startup_cmd, NULL); EBARF("startup: execl"); } dup2(piperw[1], STDOUT_FILENO); + close(piperw[1]); close(piperw[0]); } /* If nobody is reading the status output, don't terminate */ From 894f2a3152481f7c53baad14dabc1511b70c7bd7 Mon Sep 17 00:00:00 2001 From: Leonardo Hernandez Hernandez Date: Sun, 31 Oct 2021 15:32:49 -0600 Subject: [PATCH 038/117] change border color according to focus state --- dwl.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/dwl.c b/dwl.c index 58a1bd95e..a2054b5b8 100644 --- a/dwl.c +++ b/dwl.c @@ -1064,6 +1064,8 @@ focusclient(Client *c, int lift) { struct wlr_surface *old = seat->keyboard_state.focused_surface; struct wlr_keyboard *kb; + Client *w; + int i; /* Raise client in stacking order if requested */ if (c && lift) { @@ -1081,6 +1083,9 @@ focusclient(Client *c, int lift) wl_list_insert(&fstack, &c->flink); selmon = c->mon; c->isurgent = 0; + + for (i = 0; i < 4; i++) + wlr_scene_rect_set_color(c->border[i], focuscolor); } /* Deactivate old client if focus is changing */ @@ -1100,6 +1105,16 @@ focusclient(Client *c, int lift) )) return; } else { +#ifdef XWAYLAND + if (wlr_surface_is_xwayland_surface(old)) + w = wlr_xwayland_surface_from_wlr_surface(old)->data; + else +#endif + w = wlr_xdg_surface_from_wlr_surface(old)->data; + + for (i = 0; i < 4; i++) + wlr_scene_rect_set_color(w->border[i], bordercolor); + client_activate_surface(old, 0); } } @@ -1305,6 +1320,7 @@ mapnotify(struct wl_listener *listener, void *data) for (i = 0; i < 4; i++) { c->border[i] = wlr_scene_rect_create(c->scene, 0, 0, bordercolor); c->border[i]->node.data = c; + wlr_scene_rect_set_color(c->border[i], bordercolor); } if (client_is_unmanaged(c)) { From 03e167dbb70fbc967e310f95200bcd63f43cac72 Mon Sep 17 00:00:00 2001 From: Raphael Robatsch Date: Sat, 13 Nov 2021 17:22:52 +0100 Subject: [PATCH 039/117] fullscreennotify: don't crash if called before map SDL2 calls xdg_toplevel.unset_fullscreen() before the surface is mapped. This causes a segfault in dwl because setfullscreen() expects the surface to be mapped already. Therefore, delay the setfullscreen call until the surface is mapped. --- dwl.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 6303c25f8..f99fc9ace 100644 --- a/dwl.c +++ b/dwl.c @@ -1042,7 +1042,13 @@ void fullscreennotify(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, fullscreen); - setfullscreen(c, !c->isfullscreen); + struct wlr_xdg_toplevel_set_fullscreen_event *event = data; + if (!c->mon) { + /* if the client is not mapped yet, let mapnotify() call setfullscreen() */ + c->isfullscreen = event->fullscreen; + return; + } + setfullscreen(c, event->fullscreen); } Monitor * @@ -1316,6 +1322,9 @@ mapnotify(struct wl_listener *listener, void *data) /* Set initial monitor, tags, floating status, and focus */ applyrules(c); + + if (c->isfullscreen) + setfullscreen(c, 1); } void From 52dbc97ed69f368e63fae57b028072f01d07ee7d Mon Sep 17 00:00:00 2001 From: Leonardo Hernandez Hernandez Date: Sun, 3 Oct 2021 22:08:00 -0500 Subject: [PATCH 040/117] `wlr_xdg_surface.configure_serial` has been moved into `wlr_xdg_surface_state` as seen in swaywm/wlroots@0e34208 --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 5a525d552..f563fd382 100644 --- a/dwl.c +++ b/dwl.c @@ -779,7 +779,7 @@ commitnotify(struct wl_listener *listener, void *data) Client *c = wl_container_of(listener, c, commit); /* mark a pending resize as completed */ - if (c->resize && c->resize <= c->surface.xdg->configure_serial) + if (c->resize && c->resize <= c->surface.xdg->current.configure_serial) c->resize = 0; } From 27f66c87152765942324f85be6b83d0028de295e Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Thu, 16 Dec 2021 11:50:11 -0600 Subject: [PATCH 041/117] explicitly create renderer and allocator autocreate was removed --- dwl.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/dwl.c b/dwl.c index f563fd382..4353cfd50 100644 --- a/dwl.c +++ b/dwl.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -307,6 +308,7 @@ static const char broken[] = "broken"; static struct wl_display *dpy; static struct wlr_backend *backend; static struct wlr_renderer *drw; +static struct wlr_allocator *alloc; static struct wlr_compositor *compositor; static struct wlr_xdg_shell *xdg_shell; @@ -822,6 +824,8 @@ createmon(struct wl_listener *listener, void *data) Monitor *m = wlr_output->data = calloc(1, sizeof(*m)); m->wlr_output = wlr_output; + wlr_output_init_render(wlr_output, alloc, drw); + /* Initialize monitor state using configured rules */ for (size_t i = 0; i < LENGTH(m->layers); i++) wl_list_init(&m->layers[i]); @@ -2006,12 +2010,15 @@ setup(void) if (!(backend = wlr_backend_autocreate(dpy))) BARF("couldn't create backend"); - /* 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); + /* Create a renderer with the default implementation */ + if (!(drw = wlr_renderer_autocreate(backend))) + BARF("couldn't create renderer"); wlr_renderer_init_wl_display(drw, dpy); + /* Create a default allocator */ + if (!(alloc = wlr_allocator_autocreate(backend, drw))) + BARF("couldn't create allocator"); + /* This creates some hands-off wlroots interfaces. The compositor is * necessary for clients to allocate surfaces and the data device manager * handles the clipboard. Each of these wlroots interfaces has room for you From 317175da08aedf80f0bfaf71849feea531872a88 Mon Sep 17 00:00:00 2001 From: A Frederick Christensen Date: Fri, 31 Dec 2021 14:51:50 -0600 Subject: [PATCH 042/117] Newly launched or closed clients ALWAYS generate status update Prior to this change, if a client whose tag(s) are not currently selected is launched or killed, no update to status was printed and status bars being fed by printstatus() did not update newly active or newly inactive (but unselected) tags. --- dwl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dwl.c b/dwl.c index 868346271..fcd770017 100644 --- a/dwl.c +++ b/dwl.c @@ -1320,6 +1320,7 @@ mapnotify(struct wl_listener *listener, void *data) /* Set initial monitor, tags, floating status, and focus */ applyrules(c); + printstatus(); } void @@ -2290,6 +2291,7 @@ unmapnotify(struct wl_listener *listener, void *data) setmon(c, NULL, 0); wl_list_remove(&c->flink); wl_list_remove(&c->slink); + printstatus(); } void From f587b2fd2c9f5967b9e8ca99fe70fbc80fb6c220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arma=C3=ABl=20Gu=C3=A9neau?= Date: Sat, 8 Jan 2022 17:41:45 +0100 Subject: [PATCH 043/117] fix client_set_tiled, which was ignoring its "edges" argument --- client.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client.h b/client.h index 4fd186353..a6b37235b 100644 --- a/client.h +++ b/client.h @@ -156,8 +156,7 @@ client_set_tiled(Client *c, uint32_t edges) 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); + wlr_xdg_toplevel_set_tiled(c->surface.xdg, edges); } static inline struct wlr_surface * From ac896a7df4bac35d43579b450f1e4eeabe7a9d44 Mon Sep 17 00:00:00 2001 From: A Frederick Christensen Date: Tue, 1 Feb 2022 18:58:32 -0600 Subject: [PATCH 044/117] Shift+6 generates XKB_KEY_asciicircum --- config.def.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.def.h b/config.def.h index 089aa3795..738a3ca8a 100644 --- a/config.def.h +++ b/config.def.h @@ -96,7 +96,7 @@ static const Key keys[] = { TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2), TAGKEYS( XKB_KEY_4, XKB_KEY_dollar, 3), TAGKEYS( XKB_KEY_5, XKB_KEY_percent, 4), - TAGKEYS( XKB_KEY_6, XKB_KEY_caret, 5), + TAGKEYS( XKB_KEY_6, XKB_KEY_asciicircum, 5), TAGKEYS( XKB_KEY_7, XKB_KEY_ampersand, 6), TAGKEYS( XKB_KEY_8, XKB_KEY_asterisk, 7), TAGKEYS( XKB_KEY_9, XKB_KEY_parenleft, 8), From b0098d9d095509e4999965d261fe4282040a187a Mon Sep 17 00:00:00 2001 From: Nihal Jere Date: Tue, 22 Feb 2022 23:03:26 -0600 Subject: [PATCH 045/117] die on allocation failure --- dwl.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dwl.c b/dwl.c index 36a387177..1b9260b34 100644 --- a/dwl.c +++ b/dwl.c @@ -787,6 +787,8 @@ createkeyboard(struct wlr_input_device *device) struct xkb_context *context; struct xkb_keymap *keymap; Keyboard *kb = device->data = calloc(1, sizeof(*kb)); + if (!kb) + EBARF("createkeyboard: calloc"); kb->device = device; /* Prepare an XKB keymap and assign it to the keyboard. */ @@ -818,6 +820,8 @@ createmon(struct wl_listener *listener, void *data) struct wlr_output *wlr_output = data; const MonitorRule *r; Monitor *m = wlr_output->data = calloc(1, sizeof(*m)); + if (!m) + EBARF("createmon: calloc"); m->wlr_output = wlr_output; wlr_output_init_render(wlr_output, alloc, drw); @@ -891,6 +895,8 @@ createnotify(struct wl_listener *listener, void *data) /* Allocate a Client for this surface */ c = xdg_surface->data = calloc(1, sizeof(*c)); + if (!c) + EBARF("createnotify: calloc"); c->surface.xdg = xdg_surface; c->bw = borderpx; @@ -917,6 +923,8 @@ createlayersurface(struct wl_listener *listener, void *data) } layersurface = calloc(1, sizeof(LayerSurface)); + if (!layersurface) + EBARF("layersurface: calloc"); LISTEN(&wlr_layer_surface->surface->events.commit, &layersurface->surface_commit, commitlayersurfacenotify); LISTEN(&wlr_layer_surface->events.destroy, &layersurface->destroy, @@ -2478,6 +2486,8 @@ createnotifyx11(struct wl_listener *listener, void *data) /* Allocate a Client for this surface */ c = xwayland_surface->data = calloc(1, sizeof(*c)); + if (!c) + EBARF("createnotifyx11: calloc"); c->surface.xwayland = xwayland_surface; c->type = xwayland_surface->override_redirect ? X11Unmanaged : X11Managed; c->bw = borderpx; From d1ff1e6f75d9c53c953957b5c0a64e0bcb40008b Mon Sep 17 00:00:00 2001 From: Leonardo Hernandez Hernandez Date: Tue, 28 Sep 2021 18:08:52 -0500 Subject: [PATCH 046/117] remove typedef `Decoration` --- dwl.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/dwl.c b/dwl.c index 36a387177..9f6a2e7f9 100644 --- a/dwl.c +++ b/dwl.c @@ -119,11 +119,6 @@ typedef struct { int isfullscreen; } Client; -typedef struct { - struct wl_listener request_mode; - struct wl_listener destroy; -} Decoration; - typedef struct { uint32_t mod; xkb_keysym_t keysym; From 3e6d584de107a3d555d652b55bf5227d03f2f957 Mon Sep 17 00:00:00 2001 From: Leonardo Hernandez Hernandez Date: Tue, 2 Nov 2021 17:09:54 -0600 Subject: [PATCH 047/117] update URL to wlroots project (GitHub->GitLab) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 79a7d8f4f..13898037a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Join us on our [Discord server](https://discord.gg/jJxZnrGPWN)! -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://gitlab.freedesktop.org/wlroots/wlroots/). It is intended to fill the same space in the Wayland world that dwm does in X11, primarily in terms of philosophy, and secondarily in terms of functionality. Like dwm, dwl is: - Easy to understand, hack on, and extend with patches - One C source file (or a very small number) configurable via `config.h` From 8cace1921823e250041f7d85d939960f33824451 Mon Sep 17 00:00:00 2001 From: Leonardo Hernandez Hernandez Date: Thu, 4 Nov 2021 08:19:13 -0600 Subject: [PATCH 048/117] fix crash when the last monitor is disconnected --- dwl.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/dwl.c b/dwl.c index 0619ff104..67a3cd565 100644 --- a/dwl.c +++ b/dwl.c @@ -57,7 +57,7 @@ #define MAX(A, B) ((A) > (B) ? (A) : (B)) #define MIN(A, B) ((A) < (B) ? (A) : (B)) #define CLEANMASK(mask) (mask & ~WLR_MODIFIER_CAPS) -#define VISIBLEON(C, M) ((C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags])) +#define VISIBLEON(C, M) ((M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags])) #define LENGTH(X) (sizeof X / sizeof X[0]) #define END(A) ((A) + LENGTH(A)) #define TAGMASK ((1 << LENGTH(tags)) - 1) @@ -720,10 +720,11 @@ cleanupmon(struct wl_listener *listener, void *data) wl_list_remove(&m->link); wlr_output_layout_remove(output_layout, m->wlr_output); - nmons = wl_list_length(&mons); - do // don't switch to disabled mons - selmon = wl_container_of(mons.prev, selmon, link); - while (!selmon->wlr_output->enabled && i++ < nmons); + if ((nmons = wl_list_length(&mons))) + 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); closemon(m); free(m); @@ -860,6 +861,16 @@ createmon(struct wl_listener *listener, void *data) wlr_output_layout_add(output_layout, wlr_output, r->x, r->y); sgeom = *wlr_output_layout_get_box(output_layout, NULL); + /* If length == 1 we need update selmon. + * Maybe it will change in run(). */ + if (wl_list_length(&mons) == 1) { + Client *c; + selmon = m; + /* If there is any client, set c->mon to this monitor */ + wl_list_for_each(c, &clients, link) + setmon(c, m, c->tags); + } + /* When adding monitors, the geometries of all monitors must be updated */ wl_list_for_each(m, &mons, link) { /* The first monitor in the list is the most recently added */ From f673305e86bea5804fa8c68ffa43f120e7d9f9fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Thu, 10 Mar 2022 11:37:11 -0600 Subject: [PATCH 049/117] replace tabs by spaces in alignment --- config.def.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.def.h b/config.def.h index 738a3ca8a..84086594d 100644 --- a/config.def.h +++ b/config.def.h @@ -84,7 +84,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_e, togglefullscreen, {0} }, { MODKEY, XKB_KEY_0, view, {.ui = ~0} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} }, { MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} }, From 5d9c9a9a685bb026b35032ab8cbc0575785a74ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Thu, 10 Mar 2022 14:34:36 -0600 Subject: [PATCH 050/117] don't warn about unused result Closes: #186 --- config.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.mk b/config.mk index cd4e821c1..d1b161e7e 100644 --- a/config.mk +++ b/config.mk @@ -2,7 +2,7 @@ PREFIX = /usr/local # Default compile flags (overridable by environment) -CFLAGS ?= -g -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare -Wno-unused-function -Wno-unused-variable -Wdeclaration-after-statement +CFLAGS ?= -g -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare -Wno-unused-function -Wno-unused-variable -Wno-unused-result -Wdeclaration-after-statement # Uncomment to build XWayland support #CFLAGS += -DXWAYLAND From 05a473335ea4c69569d9ad34298b4f42a555e6e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Thu, 10 Mar 2022 14:48:14 -0600 Subject: [PATCH 051/117] use wlr_box for previous geom --- dwl.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/dwl.c b/dwl.c index 67a3cd565..ba150fad6 100644 --- a/dwl.c +++ b/dwl.c @@ -101,7 +101,7 @@ typedef struct { struct wl_listener destroy; struct wl_listener set_title; struct wl_listener fullscreen; - struct wlr_box geom; /* layout-relative, includes border */ + struct wlr_box geom, prev; /* layout-relative, includes border */ Monitor *mon; #ifdef XWAYLAND unsigned int type; @@ -112,10 +112,6 @@ typedef struct { unsigned int tags; int isfloating, isurgent; uint32_t resize; /* configure serial of a pending resize */ - int prevx; - int prevy; - int prevwidth; - int prevheight; int isfullscreen; } Client; @@ -1035,15 +1031,12 @@ setfullscreen(Client *c, int fullscreen) client_set_fullscreen(c, fullscreen); if (fullscreen) { - c->prevx = c->geom.x; - c->prevy = c->geom.y; - c->prevheight = c->geom.height; - c->prevwidth = c->geom.width; + c->prev = c->geom; resize(c, c->mon->m.x, c->mon->m.y, c->mon->m.width, c->mon->m.height, 0); } else { /* restore previous size instead of arrange for floating windows since * client positions are set by the user and cannot be recalculated */ - resize(c, c->prevx, c->prevy, c->prevwidth, c->prevheight, 0); + resize(c, c->prev.x, c->prev.y, c->prev.width, c->prev.height, 0); arrange(c->mon); } } From 2b2f72d7c287fe3255dfa8f7db13f81c6a534a57 Mon Sep 17 00:00:00 2001 From: Leonardo Hernandez Hernandez Date: Fri, 24 Sep 2021 16:07:06 -0500 Subject: [PATCH 052/117] use wlr_scene_output_send_frame_done() --- dwl.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/dwl.c b/dwl.c index 301256bdb..5dd566aba 100644 --- a/dwl.c +++ b/dwl.c @@ -262,7 +262,6 @@ static void pointerfocus(Client *c, struct wlr_surface *surface, static void printstatus(void); static void quit(const Arg *arg); static void quitsignal(int signo); -static void rendered(struct wlr_surface *surface, int sx, int sy, void *data); 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 run(char *startup_cmd); @@ -1605,13 +1604,6 @@ quitsignal(int signo) quit(NULL); } -void -rendered(struct wlr_surface *surface, int sx, int sy, void *data) -{ - struct timespec *now = data; - wlr_surface_send_frame_done(surface, now); -} - void rendermon(struct wl_listener *listener, void *data) { @@ -1631,14 +1623,7 @@ rendermon(struct wl_listener *listener, void *data) /* Let clients know a frame has been rendered */ clock_gettime(CLOCK_MONOTONIC, &now); - wl_list_for_each(c, &clients, link) - if (VISIBLEON(c, c->mon)) - client_for_each_surface(c, rendered, &now); - - for (i = 0; i < LENGTH(m->layers); i++) - wl_list_for_each(layer, &m->layers[i], link) - wlr_layer_surface_v1_for_each_surface(layer->layer_surface, rendered, &now); - + wlr_scene_output_send_frame_done(m->scene_output, &now); } void From b97d9e1ce102fe0aa2331b480827b523164c7d42 Mon Sep 17 00:00:00 2001 From: Leonardo Hernandez Hernandez Date: Sun, 31 Oct 2021 17:05:40 -0600 Subject: [PATCH 053/117] use wlr_scene_node_raise_to_top() --- dwl.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/dwl.c b/dwl.c index 5dd566aba..4e90d1718 100644 --- a/dwl.c +++ b/dwl.c @@ -1067,11 +1067,8 @@ focusclient(Client *c, int lift) int i; /* Raise client in stacking order if requested */ - if (c && lift) { - /* This isn't easy to do via the current API */ - wl_list_remove(&c->scene->state.link); - wl_list_insert(c->scene->parent->state.children.prev, &c->scene->state.link); - } + if (c && lift) + wlr_scene_node_raise_to_top(c->scene); if (c && client_surface(c) == old) return; From 4465dcb6da1fb91177fc26b4513d14c48eb56d5e Mon Sep 17 00:00:00 2001 From: Leonardo Hernandez Hernandez Date: Tue, 15 Feb 2022 17:38:56 -0600 Subject: [PATCH 054/117] fix left border 'y' position also add comment about border ordering --- dwl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 4e90d1718..d2f910cc5 100644 --- a/dwl.c +++ b/dwl.c @@ -92,7 +92,7 @@ typedef struct { /* Must be first */ unsigned int type; /* XDGShell or X11* */ struct wlr_scene_node *scene; - struct wlr_scene_rect *border[4]; + struct wlr_scene_rect *border[4]; /* top, bottom, left, right */ struct wlr_scene_node *scene_surface; struct wl_list link; struct wl_list flink; @@ -1641,6 +1641,7 @@ resize(Client *c, int x, int y, int w, int h, int interact) wlr_scene_rect_set_size(c->border[2], c->bw, c->geom.height - 2 * c->bw); wlr_scene_rect_set_size(c->border[3], c->bw, c->geom.height - 2 * c->bw); wlr_scene_node_set_position(&c->border[1]->node, 0, c->geom.height - c->bw); + wlr_scene_node_set_position(&c->border[2]->node, 0, c->bw); wlr_scene_node_set_position(&c->border[3]->node, c->geom.width - c->bw, c->bw); /* wlroots makes this a no-op if size hasn't changed */ From 0e5d7124de4e7ba0baa080547fcfee41b1d5174d Mon Sep 17 00:00:00 2001 From: Leonardo Hernandez Hernandez Date: Wed, 26 Jan 2022 00:54:00 -0600 Subject: [PATCH 055/117] use loop to call arrangelayer zwlr_layer_shell_v1_layer are ordered by bottom-most first so we can just use a loop from 3 to 0 --- dwl.c | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/dwl.c b/dwl.c index 9991c952d..cd9e74d90 100644 --- a/dwl.c +++ b/dwl.c @@ -560,6 +560,7 @@ arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, int void arrangelayers(Monitor *m) { + int i; struct wlr_box usable_area = m->m; uint32_t layers_above_shell[] = { ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, @@ -568,30 +569,18 @@ arrangelayers(Monitor *m) 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], - &usable_area, 1); - arrangelayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], - &usable_area, 1); - arrangelayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], - &usable_area, 1); - arrangelayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], - &usable_area, 1); + /* Arrange exclusive surfaces from top->bottom */ + for (i = 3; i >= 0; i--) + arrangelayer(m, &m->layers[i], &usable_area, 1); if (memcmp(&usable_area, &m->w, sizeof(struct wlr_box))) { m->w = usable_area; arrange(m); } - // Arrange non-exlusive surfaces from top->bottom - arrangelayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], - &usable_area, 0); - arrangelayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], - &usable_area, 0); - arrangelayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], - &usable_area, 0); - arrangelayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], - &usable_area, 0); + /* Arrange non-exlusive surfaces from top->bottom */ + for (i = 3; i >= 0; i--) + arrangelayer(m, &m->layers[i], &usable_area, 0); // Find topmost keyboard interactive layer, if such a layer exists for (size_t i = 0; i < LENGTH(layers_above_shell); i++) { From 4d26d302200f20294b97d01e49a9a4a7f0b189d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Fri, 11 Mar 2022 18:52:22 -0600 Subject: [PATCH 056/117] suckless style: don't use '//' for comments --- dwl.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/dwl.c b/dwl.c index cd9e74d90..47b5187e9 100644 --- a/dwl.c +++ b/dwl.c @@ -164,7 +164,7 @@ struct Monitor { struct wl_listener destroy; struct wlr_box m; /* monitor area, layout-relative */ struct wlr_box w; /* window area, layout-relative */ - struct wl_list layers[4]; // LayerSurface::link + struct wl_list layers[4]; /* LayerSurface::link */ const Layout *lt[2]; unsigned int seltags; unsigned int sellt; @@ -394,7 +394,7 @@ applyexclusive(struct wlr_box *usable_area, int32_t margin_top, int32_t margin_right, int32_t margin_bottom, int32_t margin_left) { Edge edges[] = { - { // Top + { /* Top */ .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, .anchor_triplet = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | @@ -403,7 +403,7 @@ applyexclusive(struct wlr_box *usable_area, .negative_axis = &usable_area->height, .margin = margin_top, }, - { // Bottom + { /* Bottom */ .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, .anchor_triplet = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | @@ -412,7 +412,7 @@ applyexclusive(struct wlr_box *usable_area, .negative_axis = &usable_area->height, .margin = margin_bottom, }, - { // Left + { /* Left */ .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT, .anchor_triplet = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | @@ -421,7 +421,7 @@ applyexclusive(struct wlr_box *usable_area, .negative_axis = &usable_area->width, .margin = margin_left, }, - { // Right + { /* Right */ .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT, .anchor_triplet = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | @@ -504,7 +504,7 @@ arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, int bounds = state->exclusive_zone == -1 ? full_area : *usable_area; - // Horizontal axis + /* Horizontal axis */ if ((state->anchor & both_horiz) && box.width == 0) { box.x = bounds.x; box.width = bounds.width; @@ -515,7 +515,7 @@ arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, int } else { box.x = bounds.x + ((bounds.width / 2) - (box.width / 2)); } - // Vertical axis + /* Vertical axis */ if ((state->anchor & both_vert) && box.height == 0) { box.y = bounds.y; box.height = bounds.height; @@ -526,7 +526,7 @@ arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, int } else { box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); } - // Margin + /* Margin */ if ((state->anchor & both_horiz) == both_horiz) { box.x += state->margin.left; box.width -= state->margin.left + state->margin.right; @@ -582,13 +582,13 @@ arrangelayers(Monitor *m) for (i = 3; i >= 0; i--) arrangelayer(m, &m->layers[i], &usable_area, 0); - // Find topmost keyboard interactive layer, if such a layer exists + /* Find topmost keyboard interactive layer, if such a layer exists */ for (size_t i = 0; i < LENGTH(layers_above_shell); i++) { wl_list_for_each_reverse(layersurface, &m->layers[layers_above_shell[i]], link) { if (layersurface->layer_surface->current.keyboard_interactive && layersurface->layer_surface->mapped) { - // Deactivate the focused client. + /* Deactivate the focused client. */ focusclient(NULL, 0); wlr_seat_keyboard_notify_enter(seat, layersurface->layer_surface->surface, kb->keycodes, kb->num_keycodes, &kb->modifiers); @@ -706,7 +706,7 @@ cleanupmon(struct wl_listener *listener, void *data) wlr_output_layout_remove(output_layout, m->wlr_output); if ((nmons = wl_list_length(&mons))) - do // don't switch to disabled mons + do /* don't switch to disabled mons */ selmon = wl_container_of(mons.prev, selmon, link); while (!selmon->wlr_output->enabled && i++ < nmons); @@ -718,7 +718,7 @@ cleanupmon(struct wl_listener *listener, void *data) void closemon(Monitor *m) { - // move closed monitor's clients to the focused one + /* move closed monitor's clients to the focused one */ Client *c; wl_list_for_each(c, &clients, link) { @@ -932,8 +932,9 @@ createlayersurface(struct wl_listener *listener, void *data) wl_list_insert(&m->layers[wlr_layer_surface->pending.layer], &layersurface->link); - // Temporarily set the layer's current state to pending - // so that we can easily arrange it + /* Temporarily set the layer's current state to pending + * so that we can easily arrange it + */ old_state = wlr_layer_surface->current; wlr_layer_surface->current = wlr_layer_surface->pending; arrangelayers(m); @@ -1352,7 +1353,7 @@ motionnotify(uint32_t time) struct wlr_surface *surface = NULL; Client *c = NULL; - // time is 0 in internal calls meant to restore pointer focus. + /* time is 0 in internal calls meant to restore pointer focus. */ if (time) { wlr_idle_notify_activity(idle, seat); @@ -2607,8 +2608,7 @@ main(int argc, char *argv[]) if (optind < argc) goto usage; - // Wayland requires XDG_RUNTIME_DIR for creating its communications - // socket + /* Wayland requires XDG_RUNTIME_DIR for creating its communications socket */ if (!getenv("XDG_RUNTIME_DIR")) BARF("XDG_RUNTIME_DIR must be set"); setup(); From 08020d61b7ed2c13a544c7c3f051754088475a2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Fri, 11 Mar 2022 23:02:37 -0600 Subject: [PATCH 057/117] more style fixes --- dwl.c | 45 +++++++++++++++++---------------------------- 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/dwl.c b/dwl.c index 47b5187e9..65244e76e 100644 --- a/dwl.c +++ b/dwl.c @@ -623,7 +623,7 @@ buttonpress(struct wl_listener *listener, void *data) wlr_idle_notify_activity(idle, seat); switch (event->state) { - case WLR_BUTTON_PRESSED:; + case WLR_BUTTON_PRESSED: /* Change focus if the button was _pressed_ over a client */ if ((c = xytoclient(cursor->x, cursor->y))) focusclient(c, 1); @@ -642,8 +642,7 @@ buttonpress(struct wl_listener *listener, void *data) /* If you released any buttons, we exit interactive move/resize mode. */ /* 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); + wlr_xcursor_manager_set_cursor_image(cursor_mgr, "left_ptr", cursor); cursor_mode = CurNormal; /* Drop the window off on its new monitor */ selmon = xytomon(cursor->x, cursor->y); @@ -1278,16 +1277,16 @@ void killclient(const Arg *arg) { Client *sel = selclient(); - if (!sel) - return; - client_send_close(sel); + if (sel) + client_send_close(sel); } void maplayersurfacenotify(struct wl_listener *listener, void *data) { LayerSurface *layersurface = wl_container_of(listener, layersurface, map); - wlr_surface_send_enter(layersurface->layer_surface->surface, layersurface->layer_surface->output); + wlr_surface_send_enter(layersurface->layer_surface->surface, + layersurface->layer_surface->output); motionnotify(0); } @@ -1406,8 +1405,7 @@ motionnotify(uint32_t time) * default. This is what makes the cursor image appear when you move it * off of a client or over its border. */ if (!surface && time) - wlr_xcursor_manager_set_cursor_image(cursor_mgr, - "left_ptr", cursor); + wlr_xcursor_manager_set_cursor_image(cursor_mgr, "left_ptr", cursor); pointerfocus(c, surface, sx, sy, time); } @@ -1423,8 +1421,7 @@ motionrelative(struct wl_listener *listener, void *data) * special configuration applied for the specific input device which * generated the event. You can pass NULL for the device if you want to move * the cursor around without any input. */ - wlr_cursor_move(cursor, event->device, - event->delta_x, event->delta_y); + wlr_cursor_move(cursor, event->device, event->delta_x, event->delta_y); motionnotify(event->time_msec); } @@ -1644,8 +1641,7 @@ render(struct wlr_surface *surface, int sx, int sy, void *data) * compositor. */ transform = wlr_output_transform_invert(surface->current.transform); - wlr_matrix_project_box(matrix, &obox, transform, 0, - output->transform_matrix); + wlr_matrix_project_box(matrix, &obox, transform, 0, output->transform_matrix); /* This takes our matrix, the texture, and an alpha, and performs the actual * rendering on the GPU. */ @@ -1678,8 +1674,7 @@ renderclients(Monitor *m, struct timespec *now) surface = client_surface(c); ox = c->geom.x, oy = c->geom.y; - wlr_output_layout_output_coords(output_layout, m->wlr_output, - &ox, &oy); + wlr_output_layout_output_coords(output_layout, m->wlr_output, &ox, &oy); if (c->bw) { w = surface->current.width; @@ -1695,8 +1690,7 @@ renderclients(Monitor *m, struct timespec *now) color = (c == sel) ? focuscolor : bordercolor; for (i = 0; i < 4; i++) { scalebox(&borders[i], m->wlr_output->scale); - wlr_render_rect(drw, &borders[i], color, - m->wlr_output->transform_matrix); + wlr_render_rect(drw, &borders[i], color, m->wlr_output->transform_matrix); } } @@ -2113,12 +2107,9 @@ setup(void) 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); - wl_signal_add(&seat->events.request_set_selection, - &request_set_sel); - wl_signal_add(&seat->events.request_set_primary_selection, - &request_set_psel); + wl_signal_add(&seat->events.request_set_cursor, &request_cursor); + wl_signal_add(&seat->events.request_set_selection, &request_set_sel); + 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); @@ -2226,10 +2217,9 @@ void togglefloating(const Arg *arg) { Client *sel = selclient(); - if (!sel) - return; /* return if fullscreen */ - setfloating(sel, !sel->isfloating /* || sel->isfixed */); + if (sel && !sel->isfullscreen) + setfloating(sel, !sel->isfloating); } void @@ -2491,8 +2481,7 @@ createnotifyx11(struct wl_listener *listener, void *data) /* Listen to the various events it can emit */ LISTEN(&xwayland_surface->events.map, &c->map, mapnotify); LISTEN(&xwayland_surface->events.unmap, &c->unmap, unmapnotify); - LISTEN(&xwayland_surface->events.request_activate, &c->activate, - activatex11); + LISTEN(&xwayland_surface->events.request_activate, &c->activate, activatex11); LISTEN(&xwayland_surface->events.request_configure, &c->configure, configurex11); LISTEN(&xwayland_surface->events.set_title, &c->set_title, updatetitle); From 0c4740b27735199f2c194b47c85ab321250631b4 Mon Sep 17 00:00:00 2001 From: Humm Date: Wed, 5 Jan 2022 01:54:02 +0100 Subject: [PATCH 058/117] add dwl(1) Documentation is good. Man pages are documentation. A program without a man page is worthless. --- Makefile | 5 +- config.mk | 1 + dwl.1 | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 dwl.1 diff --git a/Makefile b/Makefile index 5ff69e953..536454f57 100644 --- a/Makefile +++ b/Makefile @@ -15,10 +15,11 @@ clean: rm -f dwl *.o *-protocol.h *-protocol.c install: dwl - install -D dwl $(PREFIX)/bin/dwl + install -Dm755 dwl $(PREFIX)/bin/dwl + install -Dm644 dwl.1 $(MANDIR)/man1/dwl.1 uninstall: - rm -f $(PREFIX)/bin/dwl + rm -f $(PREFIX)/bin/dwl $(MANDIR)/man1/dwl.1 .PHONY: all clean install uninstall diff --git a/config.mk b/config.mk index cd4e821c1..e38a8e5e9 100644 --- a/config.mk +++ b/config.mk @@ -1,5 +1,6 @@ # paths PREFIX = /usr/local +MANDIR = $(PREFIX)/share/man # Default compile flags (overridable by environment) CFLAGS ?= -g -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare -Wno-unused-function -Wno-unused-variable -Wdeclaration-after-statement diff --git a/dwl.1 b/dwl.1 new file mode 100644 index 000000000..eea0f700e --- /dev/null +++ b/dwl.1 @@ -0,0 +1,144 @@ +.Dd January 8, 2021 +.Dt DWL 1 +.Os +.Sh NAME +.Nm dwl +.Nd dwm for Wayland +.Sh SYNOPSIS +.Nm +.Op Fl s Ar command +.Sh DESCRIPTION +.Nm +is a Wayland compositor based on wlroots. +It is intended to fill the same space in the Wayland world that +.Nm dwm +does for X11. +.Pp +When given the +.Fl s +option, +.Nm +starts a shell process running +.Ar command +when starting. +When stopping, it sends +.Dv SIGTERM +to the child process and waits for it to exit. +.Pp +Users are encouraged to customize +.Nm +by editing the sources, in particular +.Pa config.h . +The default key bindings are as follows: +.Bl -tag -width 20n -offset indent -compact +.It Mod-[1-9] +Show only all windows with a tag. +.It Mod-Ctrl-[1-9] +Show all windows with a tag. +.It Mod-Shift-[1-9] +Move window to a single tag. +.It Mod-Ctrl-Shift-[1-9] +Toggle tag for window. +.It Mod-p +Spawn +.Nm bemenu-run . +.It Mod-Shift-Return +Spawn +.Nm alacritty . +.It Mod-[jk] +Move focus down/up the stack. +.It Mod-[id] +Increase/decrease number of windows in master area. +.It Mod-[hl] +Decrease/increase master area. +.It Mod-Return +Move window on top of stack or switch top of stack with second window. +.It Mod-Tab +Show only all windows with previous tag. +.It Mod-Shift-c +Close window. +.It Mod-t +Switch to tabbed layout. +.It Mod-f +Switch to floating layout. +.It Mod-m +Switch to monocle layout. +.It Mod-Space +Switch to previous layout. +.It Mod-Shift-Space +Toggle floating state of window. +.It Mod-e +Toggle fullscreen state of window. +.It Mod-0 +Show all windows. +.It Mod-Shift-0 +Set all tags for window. +.It Mod-, +Move focus to previous monitor. +.It Mod-. +Move focus to next monitor. +.It Mod-Shift-, +Move window to previous monitor. +.It Mod-Shift-. +Move window to next monitor. +.It Mod-Shift-q +Quit +.Nm . +.El +These might differ depending on your keyboard layout. +.Sh ENVIRONMENT +These environment variables are used by +.Nm : +.Bl -tag -width XDG_RUNTIME_DIR +.It Ev XDG_RUNTIME_DIR +A directory where temporary user files, such as the Wayland socket, +are stored. +.It Ev XDG_CONFIG_DIR +A directory containung configuration of various programs and +libraries, including libxkbcommon. +.It Ev DISPLAY , WAYLAND_DISPLAY , WAYLAND_SOCKET +Tell how to connect to an underlying X11 or Wayland server. +.It Ev WLR_* +Various variables specific to wlroots. +.It Ev XKB_* , XLOCALEDIR , XCOMPOSEFILE +Various variables specific to libxkbcommon. +.It Ev XCURSOR_PATH +List of directories to search for XCursor themes in. +.It Ev HOME +A directory where there are always dear files there for you. +Waiting for you to clean them up. +.El +.Pp +These are set by +.Nm : +.Bl -tag -width WAYLAND_DISPLAY +.It Ev WAYLAND_DISPLAY +Tell how to connect to +.Nm . +.It Ev DISPLAY +If using +.Nm Xwayland , +tell how to connect to the +.Nm Xwayland +server. +.El +.Sh EXAMPLES +Start +.Nm +with s6 in the background: +.Dl dwl -s 's6-svscan <&-' +.Sh SEE ALSO +.Xr alacritty 1 , +.Xr bemenu 1 , +.Xr dwm 1 , +.Xr xkeyboard-config 7 +.Sh CAVEATS +The child process's standard input is connected with a pipe to +.Nm . +If the child process neither reads from the pipe nor closes its +standard input, +.Nm +will freeze after a while due to it blocking when writing to the full +pipe buffer. +.Sh BUGS +All of them. From 2cd0b3173d2ba7078347a8172b497d12fa592549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Sun, 13 Mar 2022 15:16:06 -0600 Subject: [PATCH 059/117] print status about floating and fullscreen --- dwl.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 65244e76e..1388acbe9 100644 --- a/dwl.c +++ b/dwl.c @@ -1036,6 +1036,7 @@ setfullscreen(Client *c, int fullscreen) resize(c, c->prev.x, c->prev.y, c->prev.width, c->prev.height, 0); arrange(c->mon); } + printstatus(); } void @@ -1568,10 +1569,14 @@ printstatus(void) urg |= c->tags; } if ((c = focustop(m))) { - printf("%s title %s\n", m->wlr_output->name, client_get_title(focustop(m))); + printf("%s title %s\n", m->wlr_output->name, client_get_title(c)); + printf("%s fullscreen %u\n", m->wlr_output->name, c->isfullscreen); + printf("%s floating %u\n", m->wlr_output->name, c->isfloating); sel = c->tags; } else { printf("%s title \n", m->wlr_output->name); + printf("%s fullscreen \n", m->wlr_output->name); + printf("%s floating \n", m->wlr_output->name); sel = 0; } @@ -1902,6 +1907,7 @@ setfloating(Client *c, int floating) { c->isfloating = floating; arrange(c->mon); + printstatus(); } void From ebff6e38a02086bd6078a444641a83cb226f9995 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Sun, 13 Mar 2022 17:11:52 -0600 Subject: [PATCH 060/117] always call arrange() on setfullscreen() also don't count full screen clients on tile() --- dwl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index 1388acbe9..20e7c5c5f 100644 --- a/dwl.c +++ b/dwl.c @@ -1034,8 +1034,8 @@ setfullscreen(Client *c, int fullscreen) /* restore previous size instead of arrange for floating windows since * client positions are set by the user and cannot be recalculated */ resize(c, c->prev.x, c->prev.y, c->prev.width, c->prev.height, 0); - arrange(c->mon); } + arrange(c->mon); printstatus(); } @@ -2193,7 +2193,7 @@ tile(Monitor *m) Client *c; wl_list_for_each(c, &clients, link) - if (VISIBLEON(c, m) && !c->isfloating) + if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen) n++; if (n == 0) return; From 43228bd493f53f996a645156f0505b63e79a4f72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Sun, 13 Mar 2022 20:54:44 -0600 Subject: [PATCH 061/117] don't use fullscreen event in fullscreennotify() --- client.h | 10 ++++++++++ dwl.c | 7 ++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/client.h b/client.h index 56e30897b..f95568831 100644 --- a/client.h +++ b/client.h @@ -95,6 +95,16 @@ client_is_float_type(Client *c) return 0; } +static inline int +client_wants_fullscreen(Client *c) +{ +#ifdef XWAYLAND + if (client_is_x11(c)) + return c->surface.xwayland->fullscreen; +#endif + return c->surface.xdg->toplevel->requested.fullscreen; +} + static inline int client_is_unmanaged(Client *c) { diff --git a/dwl.c b/dwl.c index f99fc9ace..612f3d7f4 100644 --- a/dwl.c +++ b/dwl.c @@ -1042,13 +1042,14 @@ void fullscreennotify(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, fullscreen); - struct wlr_xdg_toplevel_set_fullscreen_event *event = data; + int fullscreen = client_wants_fullscreen(c); + if (!c->mon) { /* if the client is not mapped yet, let mapnotify() call setfullscreen() */ - c->isfullscreen = event->fullscreen; + c->isfullscreen = fullscreen; return; } - setfullscreen(c, event->fullscreen); + setfullscreen(c, fullscreen); } Monitor * From 1087bc5db98faf12e02e57433ed52cbc9845e98b Mon Sep 17 00:00:00 2001 From: Leonardo Hernandez Hernandez Date: Fri, 29 Oct 2021 18:38:24 -0500 Subject: [PATCH 062/117] use wlr_scene_xdg_surface_create() for xdg_popups --- dwl.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index d2f910cc5..8deee978e 100644 --- a/dwl.c +++ b/dwl.c @@ -875,7 +875,11 @@ createnotify(struct wl_listener *listener, void *data) struct wlr_xdg_surface *xdg_surface = data; Client *c; - if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) + if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { + xdg_surface->surface->data = wlr_scene_xdg_surface_create( + xdg_surface->popup->parent->data, xdg_surface); + return; + } else if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_NONE) return; /* Allocate a Client for this surface */ @@ -1311,7 +1315,8 @@ mapnotify(struct wl_listener *listener, void *data) /* Create scene tree for this client and its border */ c->scene = &wlr_scene_tree_create(layers[LyrTile])->node; - c->scene_surface = wlr_scene_subsurface_tree_create(c->scene, client_surface(c)); + c->scene_surface = client_surface(c)->data = + wlr_scene_subsurface_tree_create(c->scene, client_surface(c)); c->scene_surface->data = c; for (i = 0; i < 4; i++) { c->border[i] = wlr_scene_rect_create(c->scene, 0, 0, bordercolor); From b92c0ff57fbf9cc9ffb2a33e689acc08b8745f1d Mon Sep 17 00:00:00 2001 From: Leonardo Hernandez Hernandez Date: Tue, 1 Feb 2022 01:16:56 -0600 Subject: [PATCH 063/117] add support for layer_shell popups --- dwl.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/dwl.c b/dwl.c index 8deee978e..0d8a1b1b3 100644 --- a/dwl.c +++ b/dwl.c @@ -871,7 +871,10 @@ void createnotify(struct wl_listener *listener, void *data) { /* This event is raised when wlr_xdg_shell receives a new xdg surface from a - * client, either a toplevel (application window) or popup. */ + * client, either a toplevel (application window) or popup, + * or when wlr_layer_shell receives a new popup from a layer. + * If you want to do something tricky with popups you should check if + * its parent is wlr_xdg_shell or wlr_layer_shell */ struct wlr_xdg_surface *xdg_surface = data; Client *c; @@ -924,8 +927,8 @@ createlayersurface(struct wl_listener *listener, void *data) wlr_layer_surface->data = layersurface; m = wlr_layer_surface->output->data; - layersurface->scene = wlr_scene_subsurface_tree_create( - layers[wlr_layer_surface->pending.layer], + layersurface->scene = wlr_layer_surface->surface->data = + wlr_scene_subsurface_tree_create(layers[wlr_layer_surface->pending.layer], wlr_layer_surface->surface); layersurface->scene->data = layersurface; From 863eedd05e8d292ccef2530d9b4b8b5475b8cbef Mon Sep 17 00:00:00 2001 From: Leonardo Hernandez Hernandez Date: Mon, 31 Jan 2022 14:02:59 -0600 Subject: [PATCH 064/117] set correct position for unmanaged clients - don't allow to move/resize with them - don't focus unmanaged clients on buttonpress() --- dwl.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index 0d8a1b1b3..7f8d3f4e7 100644 --- a/dwl.c +++ b/dwl.c @@ -645,7 +645,8 @@ buttonpress(struct wl_listener *listener, void *data) case WLR_BUTTON_PRESSED:; /* Change focus if the button was _pressed_ over a client */ xytonode(cursor->x, cursor->y, NULL, &c, NULL, NULL, NULL); - if (c) + /* Don't focus unmanaged clients */ + if (c && !client_is_unmanaged(c)) focusclient(c, 1); keyboard = wlr_seat_get_keyboard(seat); @@ -1328,9 +1329,12 @@ mapnotify(struct wl_listener *listener, void *data) } if (client_is_unmanaged(c)) { + client_get_geometry(c, &c->geom); /* Floating, no border */ wlr_scene_node_reparent(c->scene, layers[LyrFloat]); c->bw = 0; + wlr_scene_node_set_position(c->scene, c->geom.x + borderpx, + c->geom.y + borderpx); /* Insert this independent into independents lists. */ wl_list_insert(&independents, &c->link); @@ -1442,7 +1446,7 @@ moveresize(const Arg *arg) if (cursor_mode != CurNormal) return; xytonode(cursor->x, cursor->y, NULL, &grabc, NULL, NULL, NULL); - if (!grabc) + if (!grabc || client_is_unmanaged(grabc)) return; /* Float the window and tell motionnotify to grab it */ From 2768af5a9bfd7cb5f874a8d61f4bc9a1188b82fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Wed, 16 Mar 2022 21:42:45 -0600 Subject: [PATCH 065/117] make sure configure and activate listeners are removed from list --- dwl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index 3c57c23c5..09ddc9a91 100644 --- a/dwl.c +++ b/dwl.c @@ -1004,9 +1004,10 @@ destroynotify(struct wl_listener *listener, void *data) wl_list_remove(&c->set_title.link); wl_list_remove(&c->fullscreen.link); #ifdef XWAYLAND - if (c->type == X11Managed) + if (c->type != XDGShell) { + wl_list_remove(&c->configure.link); wl_list_remove(&c->activate.link); - else if (c->type == XDGShell) + } else #endif wl_list_remove(&c->commit.link); free(c); From 294fb324d8f67c33552b15d3f1f79fe524d5f8fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Wed, 16 Mar 2022 23:08:17 -0600 Subject: [PATCH 066/117] constraint popups to its parent client Closes: #146 Closes: #155 --- client.h | 21 +++++++++++++++++++++ dwl.c | 9 ++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/client.h b/client.h index 191dcc523..22454a52a 100644 --- a/client.h +++ b/client.h @@ -179,3 +179,24 @@ client_surface_at(Client *c, double cx, double cy, double *sx, double *sy) #endif return wlr_xdg_surface_surface_at(c->surface.xdg, cx, cy, sx, sy); } + +static inline Client * +client_from_popup(struct wlr_xdg_popup *popup) +{ + struct wlr_xdg_surface *surface = popup->base; + + while (1) { + switch (surface->role) { + case WLR_XDG_SURFACE_ROLE_POPUP: + if (!wlr_surface_is_xdg_surface(surface->popup->parent)) + return NULL; + + surface = wlr_xdg_surface_from_wlr_surface(surface->popup->parent); + break; + case WLR_XDG_SURFACE_ROLE_TOPLEVEL: + return surface->data; + case WLR_XDG_SURFACE_ROLE_NONE: + return NULL; + } + } +} diff --git a/dwl.c b/dwl.c index 09ddc9a91..58f1b36d7 100644 --- a/dwl.c +++ b/dwl.c @@ -880,7 +880,14 @@ createnotify(struct wl_listener *listener, void *data) struct wlr_xdg_surface *xdg_surface = data; Client *c; - if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) + if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { + struct wlr_box box; + if (!(c = client_from_popup(xdg_surface->popup))) + return; + client_get_geometry(c, &box); + wlr_xdg_popup_unconstrain_from_box(xdg_surface->popup, &box); + return; + } else if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_NONE) return; /* Allocate a Client for this surface */ From 1dfd867d9caa61d9f3fabf695a72b2fea35b6193 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Thu, 17 Mar 2022 17:02:03 -0600 Subject: [PATCH 067/117] fix crash of Firefox when opening a popup larger than its size --- dwl.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index 58f1b36d7..8e0a399d7 100644 --- a/dwl.c +++ b/dwl.c @@ -882,9 +882,11 @@ createnotify(struct wl_listener *listener, void *data) if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { struct wlr_box box; - if (!(c = client_from_popup(xdg_surface->popup))) + if (!(c = client_from_popup(xdg_surface->popup)) || !c->mon) return; - client_get_geometry(c, &box); + box = c->mon->m; + box.x -= c->geom.x; + box.y -= c->geom.y; wlr_xdg_popup_unconstrain_from_box(xdg_surface->popup, &box); return; } else if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_NONE) From f1c92b05fb124d6865d4dfb0c121b3dbf7fd5407 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Fri, 18 Mar 2022 00:49:47 -0600 Subject: [PATCH 068/117] get old client by surface's node --- dwl.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/dwl.c b/dwl.c index 347178b1d..4df0e95ea 100644 --- a/dwl.c +++ b/dwl.c @@ -1076,7 +1076,6 @@ focusclient(Client *c, int lift) { struct wlr_surface *old = seat->keyboard_state.focused_surface; struct wlr_keyboard *kb; - Client *w; int i; /* Raise client in stacking order if requested */ @@ -1114,15 +1113,11 @@ focusclient(Client *c, int lift) )) return; } else { -#ifdef XWAYLAND - if (wlr_surface_is_xwayland_surface(old)) - w = wlr_xwayland_surface_from_wlr_surface(old)->data; - else -#endif - w = wlr_xdg_surface_from_wlr_surface(old)->data; - - for (i = 0; i < 4; i++) - wlr_scene_rect_set_color(w->border[i], bordercolor); + Client *w; + struct wlr_scene_node *node = old->data; + if ((w = node->data)) + for (i = 0; i < 4; i++) + wlr_scene_rect_set_color(w->border[i], bordercolor); client_activate_surface(old, 0); } From 1b22ef16161fe832e14aa29678e09eb7df56e395 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Fri, 18 Mar 2022 00:52:21 -0600 Subject: [PATCH 069/117] use xdg_shell helper for xwayland continue using wlr_scene_subsurface_create() --- dwl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index 4df0e95ea..464554cdc 100644 --- a/dwl.c +++ b/dwl.c @@ -1319,8 +1319,9 @@ mapnotify(struct wl_listener *listener, void *data) /* Create scene tree for this client and its border */ c->scene = &wlr_scene_tree_create(layers[LyrTile])->node; - c->scene_surface = client_surface(c)->data = - wlr_scene_subsurface_tree_create(c->scene, client_surface(c)); + c->scene_surface = client_surface(c)->data = c->type == XDGShell + ? wlr_scene_xdg_surface_create(c->scene, c->surface.xdg) + : wlr_scene_subsurface_tree_create(c->scene, client_surface(c)); c->scene_surface->data = c; for (i = 0; i < 4; i++) { c->border[i] = wlr_scene_rect_create(c->scene, 0, 0, bordercolor); From 0815626d4c4db3aa05ed256975476d6a31be2125 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Fri, 18 Mar 2022 00:59:52 -0600 Subject: [PATCH 070/117] pointerfocus: only use provided surface if a client is given focus it --- dwl.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/dwl.c b/dwl.c index 464554cdc..392cec106 100644 --- a/dwl.c +++ b/dwl.c @@ -1540,9 +1540,8 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, struct timespec now; int internal_call = !time; - /* Use top level surface if nothing more specific given */ - if (c && !surface) - surface = client_surface(c); + if (sloppyfocus && !internal_call && c && !client_is_unmanaged(c)) + focusclient(c, 0); /* If surface is NULL, clear pointer focus */ if (!surface) { @@ -1555,21 +1554,12 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, 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); - return; - } - - /* Otherwise, let the client know that the mouse cursor has entered one - * of its surfaces, and make keyboard focus follow if desired. */ + /* Let the client know that the mouse cursor has entered one + * of its surfaces, and make keyboard focus follow if desired. + * wlroots makes this a no-op if surface is already focused */ wlr_seat_pointer_notify_enter(seat, surface, sx, sy); + wlr_seat_pointer_notify_motion(seat, time, sx, sy); - if (!c || client_is_unmanaged(c)) - return; - - if (sloppyfocus && !internal_call) - focusclient(c, 0); } void From 254f799fde51faf71ce3ec5e7af75826f4263d81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Fri, 18 Mar 2022 01:02:50 -0600 Subject: [PATCH 071/117] do not create borders for unmanaged clients --- dwl.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/dwl.c b/dwl.c index 392cec106..8f6e81603 100644 --- a/dwl.c +++ b/dwl.c @@ -1323,17 +1323,11 @@ mapnotify(struct wl_listener *listener, void *data) ? wlr_scene_xdg_surface_create(c->scene, c->surface.xdg) : wlr_scene_subsurface_tree_create(c->scene, client_surface(c)); c->scene_surface->data = c; - for (i = 0; i < 4; i++) { - c->border[i] = wlr_scene_rect_create(c->scene, 0, 0, bordercolor); - c->border[i]->node.data = c; - wlr_scene_rect_set_color(c->border[i], bordercolor); - } if (client_is_unmanaged(c)) { client_get_geometry(c, &c->geom); - /* Floating, no border */ + /* Floating */ wlr_scene_node_reparent(c->scene, layers[LyrFloat]); - c->bw = 0; wlr_scene_node_set_position(c->scene, c->geom.x + borderpx, c->geom.y + borderpx); @@ -1342,6 +1336,13 @@ mapnotify(struct wl_listener *listener, void *data) return; } + for (i = 0; i < 4; i++) { + c->border[i] = wlr_scene_rect_create(c->scene, 0, 0, bordercolor); + c->border[i]->node.data = c; + wlr_scene_rect_set_color(c->border[i], bordercolor); + wlr_scene_node_lower_to_bottom(&c->border[i]->node); + } + /* Initialize client geometry with room for border */ client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT); client_get_geometry(c, &c->geom); From 467123dc99db4a537f1043e081cc4a6db136417b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Fri, 18 Mar 2022 01:03:33 -0600 Subject: [PATCH 072/117] make sure to destroy wlr_scene_node of unmanaged clients --- dwl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 8f6e81603..c63dbe471 100644 --- a/dwl.c +++ b/dwl.c @@ -2139,8 +2139,10 @@ unmapnotify(struct wl_listener *listener, void *data) grabc = NULL; } wl_list_remove(&c->link); - if (client_is_unmanaged(c)) + if (client_is_unmanaged(c)) { + wlr_scene_node_destroy(c->scene); return; + } setmon(c, NULL, 0); wl_list_remove(&c->flink); From 475c13414479d1013c83309fcc4bb8a8707aa721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Fri, 18 Mar 2022 01:25:53 -0600 Subject: [PATCH 073/117] do not allow set client size less than its min size --- client.h | 20 ++++++++++++++++++++ dwl.c | 6 ++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/client.h b/client.h index 191dcc523..c59b7a9f3 100644 --- a/client.h +++ b/client.h @@ -179,3 +179,23 @@ client_surface_at(Client *c, double cx, double cy, double *sx, double *sy) #endif return wlr_xdg_surface_surface_at(c->surface.xdg, cx, cy, sx, sy); } + +static inline void +client_min_size(Client *c, int *width, int *height) +{ + struct wlr_xdg_toplevel *toplevel; + struct wlr_xdg_toplevel_state *state; +#ifdef XWAYLAND + if (client_is_x11(c)) { + struct wlr_xwayland_surface_size_hints *size_hints; + size_hints = c->surface.xwayland->size_hints; + *width = size_hints->min_width; + *height = size_hints->min_height; + return; + } +#endif + toplevel = c->surface.xdg->toplevel; + state = &toplevel->current; + *width = state->min_width; + *height = state->min_height; +} diff --git a/dwl.c b/dwl.c index c63dbe471..d2f0718ee 100644 --- a/dwl.c +++ b/dwl.c @@ -1636,11 +1636,13 @@ rendermon(struct wl_listener *listener, void *data) void resize(Client *c, int x, int y, int w, int h, int interact) { + int min_width = 0, min_height = 0; struct wlr_box *bbox = interact ? &sgeom : &c->mon->w; + client_min_size(c, &min_width, &min_height); c->geom.x = x; c->geom.y = y; - c->geom.width = w; - c->geom.height = h; + c->geom.width = MAX(min_width + 2 * c->bw, w); + c->geom.height = MAX(min_height + 2 * c->bw, h); applybounds(c, bbox); /* Update scene-graph, including borders */ From e4bf83e26d4eccfcab3d54c6ebdbcc53a90346cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Fri, 18 Mar 2022 01:43:30 -0600 Subject: [PATCH 074/117] update README.md --- README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 13898037a..d09459997 100644 --- a/README.md +++ b/README.md @@ -19,15 +19,14 @@ dwl is not meant to provide every feature under the sun. Instead, like dwm, it s - 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" +- Layer shell popups (used by Waybar) +- Damage tracking provided by scenegraph API Features under consideration (possibly as patches) are: - Protocols made trivial by wlroots -- 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 input-inhibitor protocol to support screen lockers (see https://github.com/djpohly/dwl/pull/132) +- Implement the idle-inhibit protocol which lets applications such as mpv disable idle monitoring (see https://github.com/djpohly/dwl/pull/133) - 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) Feature *non-goals* for the main codebase include: From e645ea8301f3d6edf14cfb36c4d663bf721d3200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Fri, 18 Mar 2022 10:40:40 -0600 Subject: [PATCH 075/117] attach presentation to scene --- dwl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dwl.c b/dwl.c index bb3ff9aee..7f4da7865 100644 --- a/dwl.c +++ b/dwl.c @@ -1986,6 +1986,7 @@ setup(void) wl_signal_add(&output_mgr->events.test, &output_mgr_test); presentation = wlr_presentation_create(dpy, backend); + wlr_scene_set_presentation(scene, presentation); #ifdef XWAYLAND /* From 19c14b058c9188b7010ffd62bcb1276417e9ed67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Fri, 18 Mar 2022 11:04:34 -0600 Subject: [PATCH 076/117] remove unneeded variables --- dwl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index 7f4da7865..2d9528677 100644 --- a/dwl.c +++ b/dwl.c @@ -1625,8 +1625,7 @@ rendermon(struct wl_listener *listener, void *data) * generally at the output's refresh rate (e.g. 60Hz). */ Monitor *m = wl_container_of(listener, m, frame); Client *c; - LayerSurface *layer; - int i, skip = 0; + int skip = 0; struct timespec now; /* Render if no XDG clients have an outstanding resize. */ From dd463b25c7de4ea802038997a93ea749297b8c2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Sun, 20 Mar 2022 12:32:44 -0600 Subject: [PATCH 077/117] remove independents list --- dwl.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/dwl.c b/dwl.c index 2d9528677..01f51ad96 100644 --- a/dwl.c +++ b/dwl.c @@ -302,7 +302,6 @@ 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 independents; static struct wlr_idle *idle; static struct wlr_layer_shell_v1 *layer_shell; static struct wlr_output_manager_v1 *output_mgr; @@ -1337,9 +1336,6 @@ mapnotify(struct wl_listener *listener, void *data) wlr_scene_node_reparent(c->scene, layers[LyrFloat]); wlr_scene_node_set_position(c->scene, c->geom.x + borderpx, c->geom.y + borderpx); - - /* Insert this independent into independents lists. */ - wl_list_insert(&independents, &c->link); return; } @@ -1917,7 +1913,6 @@ setup(void) */ wl_list_init(&clients); wl_list_init(&fstack); - wl_list_init(&independents); idle = wlr_idle_create(dpy); @@ -2147,12 +2142,13 @@ unmapnotify(struct wl_listener *listener, void *data) cursor_mode = CurNormal; grabc = NULL; } - wl_list_remove(&c->link); + if (client_is_unmanaged(c)) { wlr_scene_node_destroy(c->scene); return; } + wl_list_remove(&c->link); setmon(c, NULL, 0); wl_list_remove(&c->flink); wlr_scene_node_destroy(c->scene); From 0dea553428c8c534ea8bbb914b1ec775cf36e4d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Sun, 20 Mar 2022 19:09:28 -0600 Subject: [PATCH 078/117] destroy scene_output in cleanupmon() --- dwl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dwl.c b/dwl.c index 01f51ad96..74f854967 100644 --- a/dwl.c +++ b/dwl.c @@ -703,6 +703,7 @@ cleanupmon(struct wl_listener *listener, void *data) wl_list_remove(&m->frame.link); wl_list_remove(&m->link); wlr_output_layout_remove(output_layout, m->wlr_output); + wlr_scene_output_destroy(m->scene_output); if ((nmons = wl_list_length(&mons))) do /* don't switch to disabled mons */ From c50f187c1f784c010b1261848fade72b9401b28f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Mon, 21 Mar 2022 13:52:33 -0600 Subject: [PATCH 079/117] improve floating detection mostly copied from sway --- client.h | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/client.h b/client.h index a5fc0d20c..41c9b9aba 100644 --- a/client.h +++ b/client.h @@ -91,16 +91,37 @@ client_get_title(Client *c) static inline int client_is_float_type(Client *c) { + struct wlr_xdg_toplevel *toplevel; + struct wlr_xdg_toplevel_state state; + #ifdef XWAYLAND - if (client_is_x11(c)) - 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] || - c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeUtility]) + if (client_is_x11(c)) { + struct wlr_xwayland_surface *surface = c->surface.xwayland; + struct wlr_xwayland_surface_size_hints *size_hints; + if (surface->modal) + return 1; + + for (size_t i = 0; i < surface->window_type_len; i++) + if (surface->window_type[i] == netatom[NetWMWindowTypeDialog] || + surface->window_type[i] == netatom[NetWMWindowTypeSplash] || + surface->window_type[i] == netatom[NetWMWindowTypeToolbar] || + surface->window_type[i] == netatom[NetWMWindowTypeUtility]) return 1; + + size_hints = surface->size_hints; + if (size_hints && size_hints->min_width > 0 && size_hints->min_height > 0 + && (size_hints->max_width == size_hints->min_width || + size_hints->max_height == size_hints->min_height)) + return 1; + } #endif - return 0; + + toplevel = c->surface.xdg->toplevel; + state = toplevel->current; + return (state.min_width != 0 && state.min_height != 0 + && (state.min_width == state.max_width + || state.min_height == state.max_height)) + || toplevel->parent; } static inline int From 2bc01debdcf2c6cf13583c6a67f8a6a824446408 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Mon, 21 Mar 2022 13:28:44 -0600 Subject: [PATCH 080/117] remove a useless resize in mapnotify() applyrules() calls setmon() which calls resize() --- dwl.c | 1 - 1 file changed, 1 deletion(-) diff --git a/dwl.c b/dwl.c index 74f854967..0b82e4232 100644 --- a/dwl.c +++ b/dwl.c @@ -1359,7 +1359,6 @@ mapnotify(struct wl_listener *listener, void *data) /* Set initial monitor, tags, floating status, and focus */ applyrules(c); - resize(c, c->geom.x, c->geom.y, c->geom.width, c->geom.height, 0); printstatus(); if (c->isfullscreen) From ee1a72211d066de3766980d0452945df0a50334c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Mon, 21 Mar 2022 21:17:58 -0600 Subject: [PATCH 081/117] only skip frames if there are visible clients that have a resize --- dwl.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index 0b82e4232..cd9e8d573 100644 --- a/dwl.c +++ b/dwl.c @@ -1624,9 +1624,11 @@ rendermon(struct wl_listener *listener, void *data) int skip = 0; struct timespec now; - /* Render if no XDG clients have an outstanding resize. */ + /* Render if no XDG clients have an outstanding resize and are visible on + * this monitor. + */ wl_list_for_each(c, &clients, link) - skip = skip || c->resize; + skip = skip || (c->resize && VISIBLEON(c, m)); if (!skip && !wlr_scene_output_commit(m->scene_output)) return; From 86fe15f76c752cb848da580af8f7d897f932cae6 Mon Sep 17 00:00:00 2001 From: Sevz Date: Mon, 21 Mar 2022 22:34:30 -0600 Subject: [PATCH 082/117] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 9b9eef46e..64e205409 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -7,4 +7,16 @@ assignees: '' --- +## Info +dwl's commit: +wlroots version: +## Description + From 330792b1fc37db86743d7c186577776e05295e00 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Mon, 21 Mar 2022 23:03:52 +0100 Subject: [PATCH 083/117] implement drag and drop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For brevity, only a single drag icon at a time is supported. Co-authored-by: Leonardo Hernández Hernández --- dwl.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/dwl.c b/dwl.c index cd9e8d573..22287fb30 100644 --- a/dwl.c +++ b/dwl.c @@ -227,6 +227,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 Monitor *dirtomon(enum wlr_direction dir); +static void dragicondestroy(struct wl_listener *listener, void *data); static void focusclient(Client *c, int lift); static void focusmon(const Arg *arg); static void focusstack(const Arg *arg); @@ -254,6 +255,7 @@ static void printstatus(void); static void quit(const Arg *arg); static void quitsignal(int signo); static void rendermon(struct wl_listener *listener, void *data); +static void requeststartdrag(struct wl_listener *listener, void *data); static void resize(Client *c, int x, int y, int w, int h, int interact); static void run(char *startup_cmd); static Client *selclient(void); @@ -268,6 +270,7 @@ static void setmon(Client *c, Monitor *m, unsigned int newtags); static void setup(void); static void sigchld(int unused); static void spawn(const Arg *arg); +static void startdrag(struct wl_listener *listener, void *data); static void tag(const Arg *arg); static void tagmon(const Arg *arg); static void tile(Monitor *m); @@ -340,6 +343,9 @@ 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}; +static struct wl_listener request_start_drag = {.notify = requeststartdrag}; +static struct wl_listener start_drag = {.notify = startdrag}; +static struct wl_listener drag_icon_destroy = {.notify = dragicondestroy}; #ifdef XWAYLAND static void activatex11(struct wl_listener *listener, void *data); @@ -1023,6 +1029,16 @@ destroynotify(struct wl_listener *listener, void *data) free(c); } +void +dragicondestroy(struct wl_listener *listener, void *data) +{ + struct wlr_drag_icon *icon = data; + wlr_scene_node_destroy(icon->data); + // Focus enter isn't sent during drag, so refocus the focused node. + focusclient(selclient(), 1); + motionnotify(0); +} + void togglefullscreen(const Arg *arg) { @@ -1400,11 +1416,16 @@ motionnotify(uint32_t time) /* time is 0 in internal calls meant to restore pointer focus. */ if (time) { + struct wlr_drag_icon *icon; wlr_idle_notify_activity(idle, seat); /* Update selmon (even while dragging a window) */ if (sloppyfocus) selmon = xytomon(cursor->x, cursor->y); + + if (seat->drag && (icon = seat->drag->icon)) + wlr_scene_node_set_position(icon->data, cursor->x + icon->surface->sx, + cursor->y + icon->surface->sy); } /* If we are currently grabbing the mouse, handle and return */ @@ -1665,6 +1686,18 @@ resize(Client *c, int x, int y, int w, int h, int interact) c->geom.height - 2 * c->bw); } +void +requeststartdrag(struct wl_listener *listener, void *data) +{ + struct wlr_seat_request_start_drag_event *event = data; + + if (wlr_seat_validate_pointer_grab_serial(seat, event->origin, + event->serial)) + wlr_seat_start_pointer_drag(seat, event->drag, event->serial); + else + wlr_data_source_destroy(event->drag->source); +} + void run(char *startup_cmd) { @@ -1976,6 +2009,8 @@ setup(void) wl_signal_add(&seat->events.request_set_cursor, &request_cursor); wl_signal_add(&seat->events.request_set_selection, &request_set_sel); wl_signal_add(&seat->events.request_set_primary_selection, &request_set_psel); + wl_signal_add(&seat->events.request_start_drag, &request_start_drag); + wl_signal_add(&seat->events.start_drag, &start_drag); output_mgr = wlr_output_manager_v1_create(dpy); wl_signal_add(&output_mgr->events.apply, &output_mgr_apply); @@ -2026,6 +2061,19 @@ spawn(const Arg *arg) } } +void +startdrag(struct wl_listener *listener, void *data) +{ + struct wlr_drag *drag = data; + + if (!drag->icon) + return; + + drag->icon->data = wlr_scene_subsurface_tree_create(layers[LyrTop], drag->icon->surface); + wlr_scene_node_raise_to_top(drag->icon->data); + wl_signal_add(&drag->icon->events.destroy, &drag_icon_destroy); +} + void tag(const Arg *arg) { From 9aec6049ecbefe3618f34002d2239cc9462c07e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Tue, 22 Mar 2022 15:02:02 -0600 Subject: [PATCH 084/117] clients now works as expected in drag motion --- dwl.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/dwl.c b/dwl.c index 22287fb30..f8d01f251 100644 --- a/dwl.c +++ b/dwl.c @@ -1416,16 +1416,11 @@ motionnotify(uint32_t time) /* time is 0 in internal calls meant to restore pointer focus. */ if (time) { - struct wlr_drag_icon *icon; wlr_idle_notify_activity(idle, seat); /* Update selmon (even while dragging a window) */ if (sloppyfocus) selmon = xytomon(cursor->x, cursor->y); - - if (seat->drag && (icon = seat->drag->icon)) - wlr_scene_node_set_position(icon->data, cursor->x + icon->surface->sx, - cursor->y + icon->surface->sy); } /* If we are currently grabbing the mouse, handle and return */ @@ -1564,6 +1559,7 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, { struct timespec now; int internal_call = !time; + struct wlr_drag_icon *icon; if (sloppyfocus && !internal_call && c && !client_is_unmanaged(c)) focusclient(c, 0); @@ -1585,6 +1581,13 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, wlr_seat_pointer_notify_enter(seat, surface, sx, sy); wlr_seat_pointer_notify_motion(seat, time, sx, sy); + /* If there are is a drag icon, update its position */ + /* For anyone who wants to change this function: for some reason + * (maybe a wlroots bug?, or is it intended?) if we change the node position + * before telling the seat for a motion, the clients don't recognize the drag */ + if (seat->drag && (icon = seat->drag->icon)) + wlr_scene_node_set_position(icon->data, cursor->x + icon->surface->sx, + cursor->y + icon->surface->sy); } void From d8f430accfb66c2575de608bb1b4c71815e4379a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Tue, 22 Mar 2022 23:29:01 -0600 Subject: [PATCH 085/117] add sway LICENSE file part of the code in dwl is taken from sway, so credit it. dwm and sway are both licensed under the MIT license --- LICENSE | 2 +- LICENSE.sway | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 LICENSE.sway diff --git a/LICENSE b/LICENSE index e4bb015a2..658085a4a 100644 --- a/LICENSE +++ b/LICENSE @@ -2,7 +2,7 @@ dwl - dwm for Wayland Copyright © 2020 dwl team -See also the files LICENSE.tinywl and LICENSE.dwm. +See also the files LICENSE.tinywl, LICENSE.dwm and LICENSE.sway. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/LICENSE.sway b/LICENSE.sway new file mode 100644 index 000000000..3e0cacc25 --- /dev/null +++ b/LICENSE.sway @@ -0,0 +1,19 @@ +Copyright (c) 2016-2017 Drew DeVault + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 326eee14445f8a2c08e80c30778445630c75d3bb Mon Sep 17 00:00:00 2001 From: Quentin Rameau Date: Mon, 12 Jul 2021 23:44:16 +0200 Subject: [PATCH 086/117] Add a configuration option for fullscreen locking Some people are annoyed to have this new behaviour forced for some application which use fake fullscreen. --- config.def.h | 1 + dwl.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/config.def.h b/config.def.h index 84086594d..539dba6a8 100644 --- a/config.def.h +++ b/config.def.h @@ -1,6 +1,7 @@ /* appearance */ static const int sloppyfocus = 1; /* focus follows mouse */ static const unsigned int borderpx = 1; /* border pixel of windows */ +static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */ static const float rootcolor[] = {0.3, 0.3, 0.3, 1.0}; static const float bordercolor[] = {0.5, 0.5, 0.5, 1.0}; static const float focuscolor[] = {1.0, 0.0, 0.0, 1.0}; diff --git a/dwl.c b/dwl.c index f8d01f251..9ea44e2a8 100644 --- a/dwl.c +++ b/dwl.c @@ -1177,7 +1177,7 @@ focusstack(const Arg *arg) { /* Focus the next or previous client (in tiling order) on selmon */ Client *c, *sel = selclient(); - if (!sel) + if (!sel || (sel->isfullscreen && lockfullscreen)) return; if (arg->i > 0) { wl_list_for_each(c, &sel->link, link) { From 7d724dc7f34561e7a6d4ab97ff24b07805593f63 Mon Sep 17 00:00:00 2001 From: Palanix Date: Mon, 28 Feb 2022 23:46:24 +0100 Subject: [PATCH 087/117] Fix dwl freezing when resizing --- dwl.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/dwl.c b/dwl.c index 9ea44e2a8..3185062a3 100644 --- a/dwl.c +++ b/dwl.c @@ -178,6 +178,7 @@ struct Monitor { unsigned int tagset[2]; double mfact; int nmaster; + int un_map; /* If a map/unmap happened on this monitor, then this should be true */ }; typedef struct { @@ -1379,6 +1380,8 @@ mapnotify(struct wl_listener *listener, void *data) if (c->isfullscreen) setfullscreen(c, 1); + + c->mon->un_map = 1; } void @@ -1648,17 +1651,27 @@ rendermon(struct wl_listener *listener, void *data) int skip = 0; struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + /* Render if no XDG clients have an outstanding resize and are visible on - * this monitor. - */ - wl_list_for_each(c, &clients, link) - skip = skip || (c->resize && VISIBLEON(c, m)); + * this monitor. */ + /* Checking m->un_map for every client is not optimal but works */ + wl_list_for_each(c, &clients, link) { + if ((c->resize && m->un_map) || (c->type == XDGShell + && (c->surface.xdg->pending.geometry.width != + c->surface.xdg->current.geometry.width + || c->surface.xdg->pending.geometry.height != + c->surface.xdg->current.geometry.height))) { + /* Lie */ + wlr_surface_send_frame_done(client_surface(c), &now); + skip = 1; + } + } if (!skip && !wlr_scene_output_commit(m->scene_output)) return; - /* Let clients know a frame has been rendered */ - clock_gettime(CLOCK_MONOTONIC, &now); wlr_scene_output_send_frame_done(m->scene_output, &now); + m->un_map = 0; } void @@ -2196,6 +2209,9 @@ unmapnotify(struct wl_listener *listener, void *data) grabc = NULL; } + if (c->mon) + c->mon->un_map = 1; + if (client_is_unmanaged(c)) { wlr_scene_node_destroy(c->scene); return; From 4ef89996242c3b31a9957666f9f5b8cf7ef5cce8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Wed, 23 Mar 2022 14:03:07 -0600 Subject: [PATCH 088/117] add note about how to change MODKEY for windows key --- config.def.h | 1 + 1 file changed, 1 insertion(+) diff --git a/config.def.h b/config.def.h index 539dba6a8..9bdf8b564 100644 --- a/config.def.h +++ b/config.def.h @@ -52,6 +52,7 @@ static const int repeat_delay = 600; static const int tap_to_click = 1; static const int natural_scrolling = 0; +/* If you want to use the windows key change this to WLR_MODIFIER_LOGO */ #define MODKEY WLR_MODIFIER_ALT #define TAGKEYS(KEY,SKEY,TAG) \ { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ From 3bace9ce6b9d5fa7c8871a414ed97976e44ecb9c Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Wed, 23 Mar 2022 22:01:04 +0100 Subject: [PATCH 089/117] inline the presentation variable This variable can be removed since with scene-graph wlr_presentation_surface_sampled_on_output no longer needs to be called. --- dwl.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dwl.c b/dwl.c index 3185062a3..65d138109 100644 --- a/dwl.c +++ b/dwl.c @@ -309,7 +309,6 @@ static struct wl_list fstack; /* focus order */ 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; @@ -2032,8 +2031,7 @@ 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); - wlr_scene_set_presentation(scene, presentation); + wlr_scene_set_presentation(scene, wlr_presentation_create(dpy, backend)); #ifdef XWAYLAND /* From 7a2e0eef746950f6ac0863dcbb5c957836a6d8fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Wed, 23 Mar 2022 12:13:49 -0600 Subject: [PATCH 090/117] Revert "clients now works as expected in drag motion" This reverts commit 9aec6049ecbefe3618f34002d2239cc9462c07e9. this problem is caused because xytonode() returns the surface of the drag icon --- dwl.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/dwl.c b/dwl.c index 65d138109..ce53dfa9a 100644 --- a/dwl.c +++ b/dwl.c @@ -1418,11 +1418,16 @@ motionnotify(uint32_t time) /* time is 0 in internal calls meant to restore pointer focus. */ if (time) { + struct wlr_drag_icon *icon; wlr_idle_notify_activity(idle, seat); /* Update selmon (even while dragging a window) */ if (sloppyfocus) selmon = xytomon(cursor->x, cursor->y); + + if (seat->drag && (icon = seat->drag->icon)) + wlr_scene_node_set_position(icon->data, cursor->x + icon->surface->sx, + cursor->y + icon->surface->sy); } /* If we are currently grabbing the mouse, handle and return */ @@ -1561,7 +1566,6 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, { struct timespec now; int internal_call = !time; - struct wlr_drag_icon *icon; if (sloppyfocus && !internal_call && c && !client_is_unmanaged(c)) focusclient(c, 0); @@ -1583,13 +1587,6 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, wlr_seat_pointer_notify_enter(seat, surface, sx, sy); wlr_seat_pointer_notify_motion(seat, time, sx, sy); - /* If there are is a drag icon, update its position */ - /* For anyone who wants to change this function: for some reason - * (maybe a wlroots bug?, or is it intended?) if we change the node position - * before telling the seat for a motion, the clients don't recognize the drag */ - if (seat->drag && (icon = seat->drag->icon)) - wlr_scene_node_set_position(icon->data, cursor->x + icon->surface->sx, - cursor->y + icon->surface->sy); } void From bf8cc526deab39e269da219667ae24b6f8adecc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Wed, 23 Mar 2022 14:18:38 -0600 Subject: [PATCH 091/117] set position of the drag icon in startdrag() --- dwl.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/dwl.c b/dwl.c index ce53dfa9a..536b4d633 100644 --- a/dwl.c +++ b/dwl.c @@ -1415,21 +1415,20 @@ motionnotify(uint32_t time) double sx = 0, sy = 0; Client *c = NULL; struct wlr_surface *surface = NULL; + struct wlr_drag_icon *icon; /* time is 0 in internal calls meant to restore pointer focus. */ if (time) { - struct wlr_drag_icon *icon; wlr_idle_notify_activity(idle, seat); /* Update selmon (even while dragging a window) */ if (sloppyfocus) selmon = xytomon(cursor->x, cursor->y); - - if (seat->drag && (icon = seat->drag->icon)) - wlr_scene_node_set_position(icon->data, cursor->x + icon->surface->sx, - cursor->y + icon->surface->sy); } + if (seat->drag && (icon = seat->drag->icon)) + wlr_scene_node_set_position(icon->data, cursor->x + icon->surface->sx, + cursor->y + icon->surface->sy); /* If we are currently grabbing the mouse, handle and return */ if (cursor_mode == CurMove) { /* Move the grabbed client to the new position. */ @@ -2081,7 +2080,7 @@ startdrag(struct wl_listener *listener, void *data) return; drag->icon->data = wlr_scene_subsurface_tree_create(layers[LyrTop], drag->icon->surface); - wlr_scene_node_raise_to_top(drag->icon->data); + motionnotify(0); wl_signal_add(&drag->icon->events.destroy, &drag_icon_destroy); } From feb972acd0ea130bc3af0f0f7072a70fa3c786bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Wed, 23 Mar 2022 13:50:08 -0600 Subject: [PATCH 092/117] fix drag icon's surface returned by xytonode --- dwl.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/dwl.c b/dwl.c index 536b4d633..f8c226428 100644 --- a/dwl.c +++ b/dwl.c @@ -67,7 +67,7 @@ /* enums */ enum { CurNormal, CurMove, CurResize }; /* cursor */ enum { XDGShell, LayerShell, X11Managed, X11Unmanaged }; /* client types */ -enum { LyrBg, LyrBottom, LyrTop, LyrOverlay, LyrTile, LyrFloat, NUM_LAYERS }; /* scene layers */ +enum { LyrBg, LyrBottom, LyrTop, LyrOverlay, LyrTile, LyrFloat, LyrNoFocus, NUM_LAYERS }; /* scene layers */ #ifdef XWAYLAND enum { NetWMWindowTypeDialog, NetWMWindowTypeSplash, NetWMWindowTypeToolbar, NetWMWindowTypeUtility, NetLast }; /* EWMH atoms */ @@ -1911,6 +1911,7 @@ setup(void) layers[LyrFloat] = &wlr_scene_tree_create(&scene->node)->node; layers[LyrTop] = &wlr_scene_tree_create(&scene->node)->node; layers[LyrOverlay] = &wlr_scene_tree_create(&scene->node)->node; + layers[LyrNoFocus] = &wlr_scene_tree_create(&scene->node)->node; /* Create a renderer with the default implementation */ if (!(drw = wlr_renderer_autocreate(backend))) @@ -2079,7 +2080,7 @@ startdrag(struct wl_listener *listener, void *data) if (!drag->icon) return; - drag->icon->data = wlr_scene_subsurface_tree_create(layers[LyrTop], drag->icon->surface); + drag->icon->data = wlr_scene_subsurface_tree_create(layers[LyrNoFocus], drag->icon->surface); motionnotify(0); wl_signal_add(&drag->icon->events.destroy, &drag_icon_destroy); } @@ -2308,17 +2309,23 @@ xytonode(double x, double y, struct wlr_surface **psurface, struct wlr_surface *surface = NULL; Client *c = NULL; LayerSurface *l = NULL; - - if ((node = wlr_scene_node_at(&scene->node, x, y, nx, ny))) { - if (node->type == WLR_SCENE_NODE_SURFACE) - surface = wlr_scene_surface_from_node(node)->surface; - /* Walk the tree to find a node that knows the client */ - for (pnode = node; pnode && !c; pnode = pnode->parent) - c = pnode->data; - if (c && c->type == LayerShell) { - c = NULL; - l = pnode->data; + int i; + int focus_order[] = { LyrOverlay, LyrTop, LyrFloat, LyrTile, LyrBottom, LyrBg }; + + for (i = 0; i < LENGTH(focus_order); i++) { + if ((node = wlr_scene_node_at(layers[focus_order[i]], x, y, nx, ny))) { + if (node->type == WLR_SCENE_NODE_SURFACE) + surface = wlr_scene_surface_from_node(node)->surface; + /* Walk the tree to find a node that knows the client */ + for (pnode = node; pnode && !c; pnode = pnode->parent) + c = pnode->data; + if (c && c->type == LayerShell) { + c = NULL; + l = pnode->data; + } } + if (surface) + break; } if (psurface) *psurface = surface; From ae614ee5125891a4d75fac97c754207f97834a7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Mon, 12 Jul 2021 19:58:55 -0500 Subject: [PATCH 093/117] implement idle-inhibitor protocol This allows clients to disable idle monitoring --- README.md | 2 +- dwl.c | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 13a00001f..22172d543 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ 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 +- Idle-inhibit protocol which lets applications such as mpv disable idle monitoring - Provide information to external status bars via stdout/stdin - Urgency hints via xdg-activate protocol - Various Wayland protocols @@ -26,7 +27,6 @@ Features under consideration (possibly as patches) are: - Protocols made trivial by wlroots - Implement the input-inhibitor protocol to support screen lockers (see https://github.com/djpohly/dwl/pull/132) -- Implement the idle-inhibit protocol which lets applications such as mpv disable idle monitoring (see https://github.com/djpohly/dwl/pull/133) - 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) Feature *non-goals* for the main codebase include: diff --git a/dwl.c b/dwl.c index f8c226428..01d165b79 100644 --- a/dwl.c +++ b/dwl.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -219,12 +220,14 @@ 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 createidleinhibitor(struct wl_listener *listener, void *data); static void createkeyboard(struct wlr_input_device *device); 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 cursorframe(struct wl_listener *listener, void *data); +static void destroyidleinhibitor(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 Monitor *dirtomon(enum wlr_direction dir); @@ -307,6 +310,7 @@ static struct wlr_xdg_activation_v1 *activation; static struct wl_list clients; /* tiling order */ static struct wl_list fstack; /* focus order */ static struct wlr_idle *idle; +static struct wlr_idle_inhibit_manager_v1 *idle_inhibit_mgr; static struct wlr_layer_shell_v1 *layer_shell; static struct wlr_output_manager_v1 *output_mgr; static struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard_mgr; @@ -331,6 +335,8 @@ static struct wl_listener cursor_button = {.notify = buttonpress}; static struct wl_listener cursor_frame = {.notify = cursorframe}; static struct wl_listener cursor_motion = {.notify = motionrelative}; static struct wl_listener cursor_motion_absolute = {.notify = motionabsolute}; +static struct wl_listener idle_inhibitor_create = {.notify = createidleinhibitor}; +static struct wl_listener idle_inhibitor_destroy = {.notify = destroyidleinhibitor}; 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}; @@ -769,6 +775,15 @@ commitnotify(struct wl_listener *listener, void *data) c->resize = 0; } +void +createidleinhibitor(struct wl_listener *listener, void *data) +{ + struct wlr_idle_inhibitor_v1 *idle_inhibitor = data; + wl_signal_add(&idle_inhibitor->events.destroy, &idle_inhibitor_destroy); + + wlr_idle_set_enabled(idle, seat, 0); +} + void createkeyboard(struct wlr_input_device *device) { @@ -988,6 +1003,14 @@ cursorframe(struct wl_listener *listener, void *data) wlr_seat_pointer_notify_frame(seat); } +void +destroyidleinhibitor(struct wl_listener *listener, void *data) +{ + /* I've been testing and at this point the inhibitor has not yet been + * removed from the list, checking if it has at least one item. */ + wlr_idle_set_enabled(idle, seat, wl_list_length(&idle_inhibit_mgr->inhibitors) <= 1); +} + void destroylayersurfacenotify(struct wl_listener *listener, void *data) { @@ -1147,6 +1170,7 @@ focusclient(Client *c, int lift) } printstatus(); + wlr_idle_set_enabled(idle, seat, wl_list_empty(&idle_inhibit_mgr->inhibitors)); if (!c) { /* With no client, all we have left is to clear focus */ @@ -1963,6 +1987,9 @@ setup(void) idle = wlr_idle_create(dpy); + idle_inhibit_mgr = wlr_idle_inhibit_v1_create(dpy); + wl_signal_add(&idle_inhibit_mgr->events.new_inhibitor, &idle_inhibitor_create); + layer_shell = wlr_layer_shell_v1_create(dpy); wl_signal_add(&layer_shell->events.new_surface, &new_layer_shell_surface); From a95338ca43c7b979ef81fb9d6b0c561beda93be2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Sat, 3 Jul 2021 15:48:40 -0500 Subject: [PATCH 094/117] implement input-inhibitor protocol --- README.md | 2 +- dwl.c | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 22172d543..72488e34e 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ dwl is not meant to provide every feature under the sun. Instead, like dwm, it s - Idle-inhibit protocol which lets applications such as mpv disable idle monitoring - Provide information to external status bars via stdout/stdin - Urgency hints via xdg-activate protocol +- Support screen lockers via input-inhibitor 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" @@ -26,7 +27,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 the input-inhibitor protocol to support screen lockers (see https://github.com/djpohly/dwl/pull/132) - 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) Feature *non-goals* for the main codebase include: diff --git a/dwl.c b/dwl.c index 01d165b79..1bee09c10 100644 --- a/dwl.c +++ b/dwl.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -311,6 +312,7 @@ static struct wl_list clients; /* tiling order */ static struct wl_list fstack; /* focus order */ static struct wlr_idle *idle; static struct wlr_idle_inhibit_manager_v1 *idle_inhibit_mgr; +static struct wlr_input_inhibit_manager *input_inhibit_mgr; static struct wlr_layer_shell_v1 *layer_shell; static struct wlr_output_manager_v1 *output_mgr; static struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard_mgr; @@ -1309,8 +1311,10 @@ keypress(struct wl_listener *listener, void *data) wlr_idle_notify_activity(idle, seat); - /* On _press_, attempt to process a compositor keybinding. */ - if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) + /* On _press_ if there is no active screen locker, + * attempt to process a compositor keybinding. */ + if (!input_inhibit_mgr->active_inhibitor + && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) for (i = 0; i < nsyms; i++) handled = keybinding(mods, syms[i]) || handled; @@ -1996,6 +2000,8 @@ setup(void) xdg_shell = wlr_xdg_shell_create(dpy); wl_signal_add(&xdg_shell->events.new_surface, &new_xdg_surface); + input_inhibit_mgr = wlr_input_inhibit_manager_create(dpy); + /* Use decoration protocols to negotiate server-side decorations */ wlr_server_decoration_manager_set_default_mode( wlr_server_decoration_manager_create(dpy), From cb4265ac8c48faeac385b038b59ac53a736d142d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Thu, 24 Mar 2022 14:19:08 -0600 Subject: [PATCH 095/117] check `m` in commitlayersurfacenotify() --- dwl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index 1bee09c10..529dbf887 100644 --- a/dwl.c +++ b/dwl.c @@ -755,9 +755,8 @@ commitlayersurfacenotify(struct wl_listener *listener, void *data) wlr_scene_node_reparent(layersurface->scene, layers[wlr_layer_surface->current.layer]); - if (!wlr_output) + if (!wlr_output || !(m = wlr_output->data)) return; - m = wlr_output->data; if (layers[wlr_layer_surface->current.layer] != layersurface->scene) { wl_list_remove(&layersurface->link); From aab397c30b9b9e4223a4f408431eb3aa5521cb92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Tue, 15 Mar 2022 15:52:26 -0600 Subject: [PATCH 096/117] new functions ecalloc() and die() die() replaces EBARF and BARF and allow us to add `-pedantic` to CFLAGS --- Makefile | 4 ++-- config.mk | 2 +- dwl.c | 46 ++++++++++++++++++---------------------------- util.c | 35 +++++++++++++++++++++++++++++++++++ util.h | 4 ++++ 5 files changed, 60 insertions(+), 31 deletions(-) create mode 100644 util.c create mode 100644 util.h diff --git a/Makefile b/Makefile index 536454f57..bba8c9fd1 100644 --- a/Makefile +++ b/Makefile @@ -59,6 +59,6 @@ idle-protocol.o: idle-protocol.h config.h: | config.def.h cp 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.o: config.mk config.h client.h xdg-shell-protocol.h wlr-layer-shell-unstable-v1-protocol.h idle-protocol.h util.h -dwl: xdg-shell-protocol.o wlr-layer-shell-unstable-v1-protocol.o idle-protocol.o +dwl: xdg-shell-protocol.o wlr-layer-shell-unstable-v1-protocol.o idle-protocol.o util.o diff --git a/config.mk b/config.mk index 960fc8a11..ab6118533 100644 --- a/config.mk +++ b/config.mk @@ -3,7 +3,7 @@ PREFIX = /usr/local MANDIR = $(PREFIX)/share/man # Default compile flags (overridable by environment) -CFLAGS ?= -g -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare -Wno-unused-function -Wno-unused-variable -Wno-unused-result -Wdeclaration-after-statement +CFLAGS ?= -g -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare -Wno-unused-function -Wno-unused-variable -Wno-unused-result -Wdeclaration-after-statement -pedantic # Uncomment to build XWayland support #CFLAGS += -DXWAYLAND diff --git a/dwl.c b/dwl.c index 529dbf887..a3b33aa1b 100644 --- a/dwl.c +++ b/dwl.c @@ -54,9 +54,9 @@ #include #endif +#include "util.h" + /* macros */ -#define BARF(fmt, ...) do { fprintf(stderr, fmt "\n", ##__VA_ARGS__); exit(EXIT_FAILURE); } while (0) -#define EBARF(fmt, ...) BARF(fmt ": %s", ##__VA_ARGS__, strerror(errno)) #define MAX(A, B) ((A) > (B) ? (A) : (B)) #define MIN(A, B) ((A) < (B) ? (A) : (B)) #define CLEANMASK(mask) (mask & ~WLR_MODIFIER_CAPS) @@ -790,9 +790,7 @@ createkeyboard(struct wlr_input_device *device) { struct xkb_context *context; struct xkb_keymap *keymap; - Keyboard *kb = device->data = calloc(1, sizeof(*kb)); - if (!kb) - EBARF("createkeyboard: calloc"); + Keyboard *kb = device->data = ecalloc(1, sizeof(*kb)); kb->device = device; /* Prepare an XKB keymap and assign it to the keyboard. */ @@ -823,9 +821,7 @@ createmon(struct wl_listener *listener, void *data) * monitor) becomes available. */ struct wlr_output *wlr_output = data; const MonitorRule *r; - Monitor *m = wlr_output->data = calloc(1, sizeof(*m)); - if (!m) - EBARF("createmon: calloc"); + Monitor *m = wlr_output->data = ecalloc(1, sizeof(*m)); m->wlr_output = wlr_output; wlr_output_init_render(wlr_output, alloc, drw); @@ -910,9 +906,7 @@ createnotify(struct wl_listener *listener, void *data) return; /* Allocate a Client for this surface */ - c = xdg_surface->data = calloc(1, sizeof(*c)); - if (!c) - EBARF("createnotify: calloc"); + c = xdg_surface->data = ecalloc(1, sizeof(*c)); c->surface.xdg = xdg_surface; c->bw = borderpx; @@ -938,9 +932,7 @@ createlayersurface(struct wl_listener *listener, void *data) wlr_layer_surface->output = selmon->wlr_output; } - layersurface = calloc(1, sizeof(LayerSurface)); - if (!layersurface) - EBARF("layersurface: calloc"); + layersurface = ecalloc(1, sizeof(LayerSurface)); layersurface->type = LayerShell; LISTEN(&wlr_layer_surface->surface->events.commit, &layersurface->surface_commit, commitlayersurfacenotify); @@ -1744,7 +1736,7 @@ run(char *startup_cmd) /* Add a Unix socket to the Wayland display. */ const char *socket = wl_display_add_socket_auto(dpy); if (!socket) - BARF("startup: display_add_socket_auto"); + die("startup: display_add_socket_auto"); setenv("WAYLAND_DISPLAY", socket, 1); /* Now that the socket exists, run the startup command */ @@ -1753,13 +1745,13 @@ run(char *startup_cmd) pipe(piperw); startup_pid = fork(); if (startup_pid < 0) - EBARF("startup: fork"); + die("startup: fork:"); if (startup_pid == 0) { dup2(piperw[0], STDIN_FILENO); close(piperw[0]); close(piperw[1]); execl("/bin/sh", "/bin/sh", "-c", startup_cmd, NULL); - EBARF("startup: execl"); + die("startup: execl:"); } dup2(piperw[1], STDOUT_FILENO); close(piperw[1]); @@ -1772,7 +1764,7 @@ run(char *startup_cmd) /* Start the backend. This will enumerate outputs and inputs, become the DRM * master, etc */ if (!wlr_backend_start(backend)) - BARF("startup: backend_start"); + die("startup: backend_start"); /* Now that outputs are initialized, choose initial selmon based on * cursor position, and set default cursor image */ @@ -1928,7 +1920,7 @@ setup(void) * if the backend does not support hardware cursors (some older GPUs * don't). */ if (!(backend = wlr_backend_autocreate(dpy))) - BARF("couldn't create backend"); + die("couldn't create backend"); /* Initialize the scene graph used to lay out windows */ scene = wlr_scene_create(); @@ -1942,12 +1934,12 @@ setup(void) /* Create a renderer with the default implementation */ if (!(drw = wlr_renderer_autocreate(backend))) - BARF("couldn't create renderer"); + die("couldn't create renderer"); wlr_renderer_init_wl_display(drw, dpy); /* Create a default allocator */ if (!(alloc = wlr_allocator_autocreate(backend, drw))) - BARF("couldn't create allocator"); + die("couldn't create allocator"); /* This creates some hands-off wlroots interfaces. The compositor is * necessary for clients to allocate surfaces and the data device manager @@ -2088,7 +2080,7 @@ sigchld(int unused) * setting our own disposition for SIGCHLD. */ if (signal(SIGCHLD, sigchld) == SIG_ERR) - EBARF("can't install SIGCHLD handler"); + die("can't install SIGCHLD handler:"); while (0 < waitpid(-1, NULL, WNOHANG)) ; } @@ -2100,7 +2092,7 @@ spawn(const Arg *arg) dup2(STDERR_FILENO, STDOUT_FILENO); setsid(); execvp(((char **)arg->v)[0], (char **)arg->v); - EBARF("dwl: execvp %s failed", ((char **)arg->v)[0]); + die("dwl: execvp %s failed:", ((char **)arg->v)[0]); } } @@ -2435,9 +2427,7 @@ createnotifyx11(struct wl_listener *listener, void *data) setfullscreen(c, 0); /* Allocate a Client for this surface */ - c = xwayland_surface->data = calloc(1, sizeof(*c)); - if (!c) - EBARF("createnotifyx11: calloc"); + c = xwayland_surface->data = ecalloc(1, sizeof(*c)); c->surface.xwayland = xwayland_surface; c->type = xwayland_surface->override_redirect ? X11Unmanaged : X11Managed; c->bw = borderpx; @@ -2517,12 +2507,12 @@ main(int argc, char *argv[]) /* Wayland requires XDG_RUNTIME_DIR for creating its communications socket */ if (!getenv("XDG_RUNTIME_DIR")) - BARF("XDG_RUNTIME_DIR must be set"); + die("XDG_RUNTIME_DIR must be set"); setup(); run(startup_cmd); cleanup(); return EXIT_SUCCESS; usage: - BARF("Usage: %s [-s startup command]", argv[0]); + die("Usage: %s [-s startup command]", argv[0]); } diff --git a/util.c b/util.c new file mode 100644 index 000000000..932f89b92 --- /dev/null +++ b/util.c @@ -0,0 +1,35 @@ +/* See LICENSE.dwm file for copyright and license details. */ +#include +#include +#include +#include + +#include "util.h" + +void * +ecalloc(size_t nmemb, size_t size) +{ + void *p; + + if (!(p = calloc(nmemb, size))) + die("calloc:"); + return p; +} + +void +die(const char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + if (fmt[0] && fmt[strlen(fmt)-1] == ':') { + fputc(' ', stderr); + perror(NULL); + } else { + fputc('\n', stderr); + } + + exit(1); +} diff --git a/util.h b/util.h new file mode 100644 index 000000000..4c9411756 --- /dev/null +++ b/util.h @@ -0,0 +1,4 @@ +/* See LICENSE.dwm file for copyright and license details. */ + +void die(const char *fmt, ...); +void *ecalloc(size_t nmemb, size_t size); From 2d6f932ecfc9580721cfa6a2f8dfa22d2af540c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Thu, 31 Mar 2022 09:21:27 -0600 Subject: [PATCH 097/117] don't let -pedantic be overridable by environment --- Makefile | 2 +- config.mk | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index bba8c9fd1..70c31d37d 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ include config.mk -CFLAGS += -I. -DWLR_USE_UNSTABLE -std=c99 +CFLAGS += -I. -DWLR_USE_UNSTABLE -std=c99 -pedantic WAYLAND_PROTOCOLS=$(shell pkg-config --variable=pkgdatadir wayland-protocols) WAYLAND_SCANNER=$(shell pkg-config --variable=wayland_scanner wayland-scanner) diff --git a/config.mk b/config.mk index ab6118533..960fc8a11 100644 --- a/config.mk +++ b/config.mk @@ -3,7 +3,7 @@ PREFIX = /usr/local MANDIR = $(PREFIX)/share/man # Default compile flags (overridable by environment) -CFLAGS ?= -g -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare -Wno-unused-function -Wno-unused-variable -Wno-unused-result -Wdeclaration-after-statement -pedantic +CFLAGS ?= -g -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare -Wno-unused-function -Wno-unused-variable -Wno-unused-result -Wdeclaration-after-statement # Uncomment to build XWayland support #CFLAGS += -DXWAYLAND From ae313911153b9c9ca8bef8ded97b95c0c2f49624 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Thu, 31 Mar 2022 09:31:01 -0600 Subject: [PATCH 098/117] initialize rules and xkb_rules to fix compile errors with `-pedantic` --- config.def.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config.def.h b/config.def.h index 9bdf8b564..00f6a85d1 100644 --- a/config.def.h +++ b/config.def.h @@ -13,8 +13,8 @@ static const Rule rules[] = { /* app_id title tags mask isfloating monitor */ /* examples: { "Gimp", NULL, 0, 1, -1 }, - { "firefox", NULL, 1 << 8, 0, -1 }, */ + { "firefox", NULL, 1 << 8, 0, -1 }, }; /* layout(s) */ @@ -43,6 +43,7 @@ static const struct xkb_rule_names xkb_rules = { /* example: .options = "ctrl:nocaps", */ + .options = "", }; static const int repeat_rate = 25; From b424602ebc2dc26a3c974ee2a87ab745821491b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Thu, 31 Mar 2022 09:39:34 -0600 Subject: [PATCH 099/117] add DESTDIR --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 70c31d37d..48a0aa76f 100644 --- a/Makefile +++ b/Makefile @@ -15,11 +15,11 @@ clean: rm -f dwl *.o *-protocol.h *-protocol.c install: dwl - install -Dm755 dwl $(PREFIX)/bin/dwl - install -Dm644 dwl.1 $(MANDIR)/man1/dwl.1 + install -Dm755 dwl $(DESTDIR)$(PREFIX)/bin/dwl + install -Dm644 dwl.1 $(DESTDIR)$(MANDIR)/man1/dwl.1 uninstall: - rm -f $(PREFIX)/bin/dwl $(MANDIR)/man1/dwl.1 + rm -f $(DESTDIR)$(PREFIX)/bin/dwl $(DESTDIR)$(MANDIR)/man1/dwl.1 .PHONY: all clean install uninstall From 79b7e755b010fc1486a89a46251a404cb97a341a Mon Sep 17 00:00:00 2001 From: Raphael Robatsch Date: Sat, 6 Nov 2021 19:51:17 +0100 Subject: [PATCH 100/117] Layer shell: Prevent infinte configure/commit loop Check the wlr_layer_surface_v1_state.committed bitmask to see if we need to rearrange. This is also what sway does. Without this check, every commit request (even if only the attached buffer changed) will lead to another configure event, which will lead to another commit, etc. This loop results in swaybg consuming 100% CPU. Co-authored-by: Owen Rafferty --- dwl.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dwl.c b/dwl.c index a3b33aa1b..bc74c96c2 100644 --- a/dwl.c +++ b/dwl.c @@ -140,6 +140,7 @@ typedef struct { typedef struct { /* Must be first */ unsigned int type; /* LayerShell */ + int mapped; struct wlr_scene_node *scene; struct wl_list link; struct wlr_layer_surface_v1 *layer_surface; @@ -758,6 +759,12 @@ commitlayersurfacenotify(struct wl_listener *listener, void *data) if (!wlr_output || !(m = wlr_output->data)) return; + if (wlr_layer_surface->current.committed == 0 + && layersurface->mapped == wlr_layer_surface->mapped) + return; + + layersurface->mapped = wlr_layer_surface->mapped; + if (layers[wlr_layer_surface->current.layer] != layersurface->scene) { wl_list_remove(&layersurface->link); wl_list_insert(&m->layers[wlr_layer_surface->current.layer], From 4d3adea68325b70eafbd56a31023f96b92532b8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Thu, 31 Mar 2022 12:24:09 -0600 Subject: [PATCH 101/117] die on pipe failure --- dwl.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dwl.c b/dwl.c index bc74c96c2..ac6212032 100644 --- a/dwl.c +++ b/dwl.c @@ -1749,9 +1749,9 @@ run(char *startup_cmd) /* Now that the socket exists, run the startup command */ if (startup_cmd) { int piperw[2]; - pipe(piperw); - startup_pid = fork(); - if (startup_pid < 0) + if (pipe(piperw) < 0) + die("startup: pipe:"); + if ((startup_pid = fork()) < 0) die("startup: fork:"); if (startup_pid == 0) { dup2(piperw[0], STDIN_FILENO); From 720f56161e5401afc1526d1ebfb977b191ca10f5 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sun, 3 Apr 2022 00:01:52 -0500 Subject: [PATCH 102/117] Remove vestigial monitor configuration info The x/y fields in monitor rules are unused and were meant to be deleted. Also removes the outdated comment in config.h. --- config.def.h | 4 +--- dwl.c | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/config.def.h b/config.def.h index 00f6a85d1..7e616950b 100644 --- a/config.def.h +++ b/config.def.h @@ -25,9 +25,7 @@ static const Layout layouts[] = { { "[M]", monocle }, }; -/* monitors - * The order in which monitors are defined determines their position. - * Non-configured monitors are always added to the left. */ +/* monitors */ static const MonitorRule monrules[] = { /* name mfact nmaster scale layout rotate/reflect x y */ /* example of a HiDPI laptop monitor: diff --git a/dwl.c b/dwl.c index ac6212032..24d015f76 100644 --- a/dwl.c +++ b/dwl.c @@ -191,8 +191,6 @@ typedef struct { float scale; const Layout *lt; enum wl_output_transform rr; - int x; - int y; } MonitorRule; typedef struct { From 6901743b0c334079ddc185af4f6844c336631d84 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sun, 3 Apr 2022 00:13:11 -0500 Subject: [PATCH 103/117] Remove removed fields from struct instances --- config.def.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.def.h b/config.def.h index 7e616950b..7eb80b512 100644 --- a/config.def.h +++ b/config.def.h @@ -29,10 +29,10 @@ static const Layout layouts[] = { static const MonitorRule monrules[] = { /* name mfact nmaster scale layout rotate/reflect x y */ /* example of a HiDPI laptop monitor: - { "eDP-1", 0.5, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, 0, 0 }, + { "eDP-1", 0.5, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL }, */ /* defaults */ - { NULL, 0.55, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, 0, 0 }, + { NULL, 0.55, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL }, }; /* keyboard */ From 437aea866268779e9599c584d5c5ab25ae682840 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sun, 3 Apr 2022 00:14:08 -0500 Subject: [PATCH 104/117] It's past my bedtime. --- config.def.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.def.h b/config.def.h index 7eb80b512..190b0da41 100644 --- a/config.def.h +++ b/config.def.h @@ -27,7 +27,7 @@ static const Layout layouts[] = { /* monitors */ static const MonitorRule monrules[] = { - /* name mfact nmaster scale layout rotate/reflect x y */ + /* name mfact nmaster scale layout rotate/reflect */ /* example of a HiDPI laptop monitor: { "eDP-1", 0.5, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL }, */ From 02ac9378c4fb40969db6e897605016bbaf422961 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Tue, 5 Apr 2022 22:07:20 -0500 Subject: [PATCH 105/117] includes: abc --- dwl.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/dwl.c b/dwl.c index 24d015f76..816eaa55d 100644 --- a/dwl.c +++ b/dwl.c @@ -3,6 +3,7 @@ */ #define _POSIX_C_SOURCE 200809L #include +#include #include #include #include @@ -10,9 +11,9 @@ #include #include #include -#include #include #include +#include #include #include #include @@ -21,13 +22,12 @@ #include #include #include -#include -#include #include #include -#include +#include +#include #include -#include +#include #include #include #include @@ -35,10 +35,10 @@ #include #include #include -#include #include -#include +#include #include +#include #include #include #include @@ -46,7 +46,6 @@ #include #include #include -#include #include #include #ifdef XWAYLAND From af741e586b80440b59b61e8fdc22c69fb7e8a457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Tue, 5 Apr 2022 22:15:46 -0500 Subject: [PATCH 106/117] typedefs: abc --- dwl.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dwl.c b/dwl.c index 816eaa55d..424485f42 100644 --- a/dwl.c +++ b/dwl.c @@ -120,6 +120,14 @@ typedef struct { int isfullscreen; } Client; +typedef struct { + uint32_t singular_anchor; + uint32_t anchor_triplet; + int *positive_axis; + int *negative_axis; + int margin; +} Edge; + typedef struct { uint32_t mod; xkb_keysym_t keysym; @@ -152,14 +160,6 @@ typedef struct { struct wlr_box geo; } LayerSurface; -typedef struct { - uint32_t singular_anchor; - uint32_t anchor_triplet; - int *positive_axis; - int *negative_axis; - int margin; -} Edge; - typedef struct { const char *symbol; void (*arrange)(Monitor *); From c00697e6438164e08baacc309b3dcdb727053aeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Tue, 5 Apr 2022 23:04:04 -0500 Subject: [PATCH 107/117] abc --- dwl.c | 238 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 119 insertions(+), 119 deletions(-) diff --git a/dwl.c b/dwl.c index 424485f42..b09fc6f92 100644 --- a/dwl.c +++ b/dwl.c @@ -221,9 +221,9 @@ static void commitlayersurfacenotify(struct wl_listener *listener, void *data); static void commitnotify(struct wl_listener *listener, void *data); static void createidleinhibitor(struct wl_listener *listener, void *data); static void createkeyboard(struct wlr_input_device *device); +static void createlayersurface(struct wl_listener *listener, void *data); 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 cursorframe(struct wl_listener *listener, void *data); static void destroyidleinhibitor(struct wl_listener *listener, void *data); @@ -234,8 +234,8 @@ static void dragicondestroy(struct wl_listener *listener, void *data); 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 fullscreennotify(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); @@ -263,13 +263,13 @@ static void resize(Client *c, int x, int y, int w, int h, int interact); static void run(char *startup_cmd); 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 setlayout(const Arg *arg); static void setmfact(const Arg *arg); static void setmon(Client *c, Monitor *m, unsigned int newtags); +static void setpsel(struct wl_listener *listener, void *data); +static void setsel(struct wl_listener *listener, void *data); static void setup(void); static void sigchld(int unused); static void spawn(const Arg *arg); @@ -289,9 +289,9 @@ 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 Monitor *xytomon(double x, double y); static struct wlr_scene_node *xytonode(double x, double y, struct wlr_surface **psurface, Client **pc, LayerSurface **pl, double *nx, double *ny); -static Monitor *xytomon(double x, double y); static void zoom(const Arg *arg); /* variables */ @@ -818,6 +818,50 @@ createkeyboard(struct wlr_input_device *device) wl_list_insert(&keyboards, &kb->link); } +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 = ecalloc(1, sizeof(LayerSurface)); + layersurface->type = LayerShell; + LISTEN(&wlr_layer_surface->surface->events.commit, + &layersurface->surface_commit, commitlayersurfacenotify); + LISTEN(&wlr_layer_surface->events.destroy, &layersurface->destroy, + destroylayersurfacenotify); + LISTEN(&wlr_layer_surface->events.map, &layersurface->map, + maplayersurfacenotify); + LISTEN(&wlr_layer_surface->events.unmap, &layersurface->unmap, + unmaplayersurfacenotify); + + layersurface->layer_surface = wlr_layer_surface; + wlr_layer_surface->data = layersurface; + m = wlr_layer_surface->output->data; + + layersurface->scene = wlr_layer_surface->surface->data = + wlr_scene_subsurface_tree_create(layers[wlr_layer_surface->pending.layer], + wlr_layer_surface->surface); + layersurface->scene->data = layersurface; + + wl_list_insert(&m->layers[wlr_layer_surface->pending.layer], + &layersurface->link); + + /* Temporarily set the layer's current state to pending + * so that we can easily arrange it + */ + old_state = wlr_layer_surface->current; + wlr_layer_surface->current = wlr_layer_surface->pending; + arrangelayers(m); + wlr_layer_surface->current = old_state; +} + void createmon(struct wl_listener *listener, void *data) { @@ -924,50 +968,6 @@ createnotify(struct wl_listener *listener, void *data) c->isfullscreen = 0; } -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 = ecalloc(1, sizeof(LayerSurface)); - layersurface->type = LayerShell; - LISTEN(&wlr_layer_surface->surface->events.commit, - &layersurface->surface_commit, commitlayersurfacenotify); - LISTEN(&wlr_layer_surface->events.destroy, &layersurface->destroy, - destroylayersurfacenotify); - LISTEN(&wlr_layer_surface->events.map, &layersurface->map, - maplayersurfacenotify); - LISTEN(&wlr_layer_surface->events.unmap, &layersurface->unmap, - unmaplayersurfacenotify); - - layersurface->layer_surface = wlr_layer_surface; - wlr_layer_surface->data = layersurface; - m = wlr_layer_surface->output->data; - - layersurface->scene = wlr_layer_surface->surface->data = - wlr_scene_subsurface_tree_create(layers[wlr_layer_surface->pending.layer], - wlr_layer_surface->surface); - layersurface->scene->data = layersurface; - - wl_list_insert(&m->layers[wlr_layer_surface->pending.layer], - &layersurface->link); - - /* Temporarily set the layer's current state to pending - * so that we can easily arrange it - */ - old_state = wlr_layer_surface->current; - wlr_layer_surface->current = wlr_layer_surface->pending; - arrangelayers(m); - wlr_layer_surface->current = old_state; -} - void createpointer(struct wlr_input_device *device) { @@ -1049,57 +1049,6 @@ destroynotify(struct wl_listener *listener, void *data) free(c); } -void -dragicondestroy(struct wl_listener *listener, void *data) -{ - struct wlr_drag_icon *icon = data; - wlr_scene_node_destroy(icon->data); - // Focus enter isn't sent during drag, so refocus the focused node. - focusclient(selclient(), 1); - motionnotify(0); -} - -void -togglefullscreen(const Arg *arg) -{ - Client *sel = selclient(); - if (sel) - setfullscreen(sel, !sel->isfullscreen); -} - -void -setfullscreen(Client *c, int fullscreen) -{ - c->isfullscreen = fullscreen; - c->bw = fullscreen ? 0 : borderpx; - client_set_fullscreen(c, fullscreen); - - if (fullscreen) { - c->prev = c->geom; - resize(c, c->mon->m.x, c->mon->m.y, c->mon->m.width, c->mon->m.height, 0); - } else { - /* restore previous size instead of arrange for floating windows since - * client positions are set by the user and cannot be recalculated */ - resize(c, c->prev.x, c->prev.y, c->prev.width, c->prev.height, 0); - } - arrange(c->mon); - printstatus(); -} - -void -fullscreennotify(struct wl_listener *listener, void *data) -{ - Client *c = wl_container_of(listener, c, fullscreen); - int fullscreen = client_wants_fullscreen(c); - - if (!c->mon) { - /* if the client is not mapped yet, let mapnotify() call setfullscreen() */ - c->isfullscreen = fullscreen; - return; - } - setfullscreen(c, fullscreen); -} - Monitor * dirtomon(enum wlr_direction dir) { @@ -1114,6 +1063,16 @@ dirtomon(enum wlr_direction dir) return selmon; } +void +dragicondestroy(struct wl_listener *listener, void *data) +{ + struct wlr_drag_icon *icon = data; + wlr_scene_node_destroy(icon->data); + // Focus enter isn't sent during drag, so refocus the focused node. + focusclient(selclient(), 1); + motionnotify(0); +} + void focusclient(Client *c, int lift) { @@ -1229,6 +1188,20 @@ focustop(Monitor *m) return NULL; } +void +fullscreennotify(struct wl_listener *listener, void *data) +{ + Client *c = wl_container_of(listener, c, fullscreen); + int fullscreen = client_wants_fullscreen(c); + + if (!c->mon) { + /* if the client is not mapped yet, let mapnotify() call setfullscreen() */ + c->isfullscreen = fullscreen; + return; + } + setfullscreen(c, fullscreen); +} + void incnmaster(const Arg *arg) { @@ -1692,6 +1665,18 @@ rendermon(struct wl_listener *listener, void *data) m->un_map = 0; } +void +requeststartdrag(struct wl_listener *listener, void *data) +{ + struct wlr_seat_request_start_drag_event *event = data; + + if (wlr_seat_validate_pointer_grab_serial(seat, event->origin, + event->serial)) + wlr_seat_start_pointer_drag(seat, event->drag, event->serial); + else + wlr_data_source_destroy(event->drag->source); +} + void resize(Client *c, int x, int y, int w, int h, int interact) { @@ -1720,18 +1705,6 @@ resize(Client *c, int x, int y, int w, int h, int interact) c->geom.height - 2 * c->bw); } -void -requeststartdrag(struct wl_listener *listener, void *data) -{ - struct wlr_seat_request_start_drag_event *event = data; - - if (wlr_seat_validate_pointer_grab_serial(seat, event->origin, - event->serial)) - wlr_seat_start_pointer_drag(seat, event->drag, event->serial); - else - wlr_data_source_destroy(event->drag->source); -} - void run(char *startup_cmd) { @@ -1830,6 +1803,25 @@ setfloating(Client *c, int floating) printstatus(); } +void +setfullscreen(Client *c, int fullscreen) +{ + c->isfullscreen = fullscreen; + c->bw = fullscreen ? 0 : borderpx; + client_set_fullscreen(c, fullscreen); + + if (fullscreen) { + c->prev = c->geom; + resize(c, c->mon->m.x, c->mon->m.y, c->mon->m.width, c->mon->m.height, 0); + } else { + /* restore previous size instead of arrange for floating windows since + * client positions are set by the user and cannot be recalculated */ + resize(c, c->prev.x, c->prev.y, c->prev.width, c->prev.height, 0); + } + arrange(c->mon); + printstatus(); +} + void setlayout(const Arg *arg) { @@ -2176,6 +2168,14 @@ togglefloating(const Arg *arg) setfloating(sel, !sel->isfloating); } +void +togglefullscreen(const Arg *arg) +{ + Client *sel = selclient(); + if (sel) + setfullscreen(sel, !sel->isfullscreen); +} + void toggletag(const Arg *arg) { @@ -2329,6 +2329,13 @@ virtualkeyboard(struct wl_listener *listener, void *data) createkeyboard(device); } +Monitor * +xytomon(double x, double y) +{ + struct wlr_output *o = wlr_output_layout_output_at(output_layout, x, y); + return o ? o->data : NULL; +} + struct wlr_scene_node * xytonode(double x, double y, struct wlr_surface **psurface, Client **pc, LayerSurface **pl, double *nx, double *ny) @@ -2362,13 +2369,6 @@ xytonode(double x, double y, struct wlr_surface **psurface, return node; } -Monitor * -xytomon(double x, double y) -{ - struct wlr_output *o = wlr_output_layout_output_at(output_layout, x, y); - return o ? o->data : NULL; -} - void zoom(const Arg *arg) { From b86fcf6504e7b6aafcace2e099d2de822c25fbf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Sun, 10 Apr 2022 21:38:48 -0500 Subject: [PATCH 108/117] add missing return in client_is_floating_type() This causes all Xwayland clients to be treated as floating --- client.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client.h b/client.h index 41c9b9aba..2ea0d22ad 100644 --- a/client.h +++ b/client.h @@ -113,6 +113,8 @@ client_is_float_type(Client *c) && (size_hints->max_width == size_hints->min_width || size_hints->max_height == size_hints->min_height)) return 1; + + return 0; } #endif From 426730a20dc938d66e424e6a5fd00ad852e116dd Mon Sep 17 00:00:00 2001 From: Fredrik Engstrand Date: Sat, 16 Apr 2022 12:22:47 +0200 Subject: [PATCH 109/117] fix: compile correctly on guix --- Makefile | 2 +- dscm-config.h | 6 ++++- dscm-utils.h | 2 +- dwl.c | 38 +++++++++++---------------- guix.scm | 73 +++++++++++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 93 insertions(+), 28 deletions(-) diff --git a/Makefile b/Makefile index b89a6511d..c60e36472 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ include config.mk -CFLAGS += -I. -DWLR_USE_UNSTABLE -std=c99 -Wno-declaration-after-statement -pedantic +CFLAGS += -I. -DWLR_USE_UNSTABLE -std=c99 -Wno-declaration-after-statement WAYLAND_PROTOCOLS=$(shell pkg-config --variable=pkgdatadir wayland-protocols) WAYLAND_SCANNER=$(shell pkg-config --variable=wayland_scanner wayland-scanner) diff --git a/dscm-config.h b/dscm-config.h index e71ea556b..c7b08c007 100644 --- a/dscm-config.h +++ b/dscm-config.h @@ -9,6 +9,7 @@ static int repeat_delay = 600; static int sloppyfocus = 1; static int tap_to_click = 1; static int natural_scrolling = 1; +static int lockfullscreen = 1; static unsigned int borderpx = 1; static double default_alpha = 1.0; static unsigned int gappih = 0; @@ -98,7 +99,7 @@ dscm_parse_key(unsigned int index, SCM key, void *data) /* Should we use `xkb_keycode_is_legal_x11`? */ if (!xkb_keycode_is_legal_x11(keycode) || !xkb_keycode_is_legal_ext(keycode)) - BARF("dscm: keycode '%d' is not a legal keycode\n", keycode); + die("dscm: keycode '%d' is not a legal keycode\n", keycode); ((Key*)data)[index] = (Key){ .mod = dscm_alist_get_modifiers(key, "modifiers"), .keycode = keycode, @@ -139,6 +140,9 @@ dscm_config_parse(char *config_file) scm_c_primitive_load(config_file); config = dscm_get_variable("config"); + /* TODO: Implement this */ + /* lockfullscreen = dscm_alist_get_int(config, "lock-fullscreen"); */ + lockfullscreen = 1; sloppyfocus = dscm_alist_get_int(config, "sloppy-focus"); tap_to_click = dscm_alist_get_int(config, "tap-to-click"); natural_scrolling = dscm_alist_get_int(config, "natural-scrolling"); diff --git a/dscm-utils.h b/dscm-utils.h index 6c246aee9..4af65664d 100644 --- a/dscm-utils.h +++ b/dscm-utils.h @@ -61,7 +61,7 @@ dscm_alist_get_proc_pointer(SCM alist, const char *key) SCM eval = scm_primitive_eval(value); /* SCM_UNPACK_POINTER is only allowed on expressions where SCM_IMP is 0 */ if (SCM_IMP(eval) == 1) - BARF("dscm: invalid callback procedure. SCM_IMP(proc) = 1"); + die("dscm: invalid callback procedure. SCM_IMP(proc) = 1"); if (scm_procedure_p(eval) == SCM_BOOL_T) { proc = SCM_UNPACK_POINTER(eval); scm_gc_protect_object(eval); diff --git a/dwl.c b/dwl.c index 9a7eadd9d..746afff68 100644 --- a/dwl.c +++ b/dwl.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -66,7 +65,6 @@ #define VISIBLEON(C, M) ((M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags])) #define LENGTH(X) (sizeof X / sizeof X[0]) #define ROUND(X) ((int)((X)+0.5)) -#define TAGMASK ((1 << LENGTH(tags)) - 1) #define LISTEN(E, L, H) wl_signal_add((E), ((L)->notify = (H), (L))) /* constants */ @@ -168,10 +166,9 @@ typedef struct { } LayerSurface; typedef struct { - const char *symbol; + char *symbol; scm_t_bits *arrange; char *id; - void (*arrange)(Monitor *); } Layout; typedef struct { @@ -210,15 +207,14 @@ struct Monitor { }; typedef struct { - const char *name; + char *name; float mfact; int nmaster; float scale; const Layout *lt; enum wl_output_transform rr; - /* TODO: Include tehse? */ - /* int x; */ - /* int y; */ + int x; + int y; } MonitorRule; typedef struct { @@ -578,7 +574,6 @@ arrange(Monitor *m) wlr_scene_node_set_enabled(c->scene, VISIBLEON(c, c->mon)); if (m->lt[m->sellt]->arrange) - m->lt[m->sellt]->arrange(m); dscm_safe_call(DSCM_CALL_ARRANGE, m->lt[m->sellt]->arrange, m); /* TODO recheck pointer focus here... or in resize()? */ } @@ -998,7 +993,7 @@ createmon(struct wl_listener *listener, void *data) m->tagset[0] = m->tagset[1] = 1; for (int i = 0; i < nummonrules; i++) { r = monrules[i]; - if (!r->name || strstr(wlr_output->name, r.name)) { + if (!r.name || strstr(wlr_output->name, r.name)) { m->mfact = r.mfact; m->nmaster = r.nmaster; wlr_output_set_scale(wlr_output, r.scale); @@ -1150,14 +1145,6 @@ destroyidleinhibitor(struct wl_listener *listener, void *data) wlr_idle_set_enabled(idle, seat, wl_list_length(&idle_inhibit_mgr->inhibitors) <= 1); } -void -destroyidleinhibitor(struct wl_listener *listener, void *data) -{ - /* I've been testing and at this point the inhibitor has not yet been - * removed from the list, checking if it has at least one item. */ - wlr_idle_set_enabled(idle, seat, wl_list_length(&idle_inhibit_mgr->inhibitors) <= 1); -} - void destroylayersurfacenotify(struct wl_listener *listener, void *data) { @@ -1174,7 +1161,6 @@ destroylayersurfacenotify(struct wl_listener *listener, void *data) Monitor *m = layersurface->layer_surface->output->data; if (m) { arrangelayers(m); - wlr_output_damage_add_whole(m->damage); } layersurface->layer_surface->output = NULL; } @@ -1258,7 +1244,7 @@ focusclient(Client *c, int lift) * It's probably pointless to check if old is a layer surface * since it can't be anything else at this point. */ if (wlr_surface_is_layer_surface(old)) { - struct wlr_layer_surface_v1 *oldsurface = + struct wlr_layer_surface_v1 *wlr_layer_surface = wlr_layer_surface_v1_from_wlr_surface(old); if (wlr_layer_surface->mapped && ( @@ -1416,12 +1402,18 @@ keybinding(uint32_t mods, xkb_keycode_t keycode) void keypress(struct wl_listener *listener, void *data) { + int i; /* 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; /* Translate libinput keycode -> xkbcommon */ uint32_t keycode = event->keycode + 8; + /* Get a list of keysyms based on the keymap for this keyboard */ + const xkb_keysym_t *syms; + int nsyms = xkb_state_key_get_syms( + kb->device->keyboard->xkb_state, keycode, &syms); + int handled = 0; uint32_t mods = wlr_keyboard_get_modifiers(kb->device->keyboard); @@ -1581,12 +1573,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, 1, 0); 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, 1, 0); return; } @@ -2137,7 +2129,7 @@ setmon(Client *c, Monitor *m, unsigned int newtags) } if (m) { /* Make sure window actually overlaps with the monitor */ - resize(c, c->geom.x, c->geom.y, c->geom.width, c->geom.height, 0); + resize(c, c->geom.x, c->geom.y, c->geom.width, c->geom.height, 0, 1); wlr_surface_send_enter(client_surface(c), m->wlr_output); c->tags = newtags ? newtags : m->tagset[m->seltags]; /* assign tags of target monitor */ arrange(m); diff --git a/guix.scm b/guix.scm index b66dcd344..ccd4399a3 100644 --- a/guix.scm +++ b/guix.scm @@ -2,9 +2,13 @@ (guix gexp) (guix utils) (guix packages) + (guix download) (guix git-download) (gnu packages wm) - (gnu packages guile)) + (gnu packages guile) + (gnu packages xdisorg) + (gnu packages libffi) + (gnu packages freedesktop)) (define this-directory (dirname (current-filename))) @@ -14,13 +18,78 @@ #:recursive? #t #:select? (git-predicate this-directory))) +(define libdrm-2.4.109 + (package + (inherit libdrm) + (name "libdrm") + (version "2.4.109") + (source (origin + (method url-fetch) + (uri (string-append + "https://dri.freedesktop.org/libdrm/libdrm-" + version ".tar.xz")) + (sha256 + (base32 + "09kzrdsd14zr0i3izvi5mck4vqccl3c9hr84r9i4is0zikh554v2")))))) + +(define wayland-1.20.0 + (package + (inherit wayland) + (name "wayland") + (version "1.20.0") + (source (origin + (method url-fetch) + (uri (string-append "https://wayland.freedesktop.org/releases/" + name "-" version ".tar.xz")) + (sha256 + (base32 + "09c7rpbwavjg4y16mrfa57gk5ix6rnzpvlnv1wp7fnbh9hak985q")))) + (propagated-inputs + (list libffi)))) + +(define wayland-protocols-1.24 + (package + (inherit wayland-protocols) + (name "wayland-protocols") + (version "1.24") + (source (origin + (method url-fetch) + (uri (string-append + "https://wayland.freedesktop.org/releases/" + "wayland-protocols-" version ".tar.xz")) + (sha256 + (base32 + "1hlb6gvyqlmsdkv5179ccj07p04cn6xacjkgklakbszczv7xiw5z")))) + (inputs + (list wayland-1.20.0)))) + +(define wlroots-0.15.0 + (package + (inherit wlroots) + (name "wlroots") + (version "0.15.0") + (source + (origin + (method git-fetch) + (uri (git-reference + (url "https://gitlab.freedesktop.org/wlroots/wlroots") + (commit version))) + (file-name (git-file-name name version)) + (sha256 + (base32 "0wdzs0wpv61pxgy3mx3xjsndyfmbj30v47d3w9ymmnd4r479n41n")))) + (propagated-inputs + (modify-inputs (package-propagated-inputs wlroots) + (prepend libdrm-2.4.109) + (replace "wayland-protocols" wayland-protocols-1.24) + (replace "wayland" wayland-1.20.0))))) + (package (inherit dwl) (source source) (name "dwl-guile-devel") (inputs `(("guile-3.0" ,guile-3.0) - ("wlroots" ,wlroots))) + ("wlroots-0.15.0" ,wlroots-0.15.0))) (arguments (substitute-keyword-arguments (package-arguments dwl) From 655b807101e3df6dfb56f27416e0dd2633d7e9e9 Mon Sep 17 00:00:00 2001 From: Fredrik Engstrand Date: Sat, 16 Apr 2022 12:29:21 +0200 Subject: [PATCH 110/117] fix: remove duplicate includes --- dwl.c | 1 - 1 file changed, 1 deletion(-) diff --git a/dwl.c b/dwl.c index 746afff68..5f558d2ee 100644 --- a/dwl.c +++ b/dwl.c @@ -37,7 +37,6 @@ #include #include #include -#include #include #include #include From ad9a6aceda0f9b1b45f960b226b2f190a67e5611 Mon Sep 17 00:00:00 2001 From: Fredrik Engstrand Date: Sat, 16 Apr 2022 13:28:04 +0200 Subject: [PATCH 111/117] fix: use tabs for indentation and remove duplicate code --- dwl.c | 4145 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 2055 insertions(+), 2090 deletions(-) diff --git a/dwl.c b/dwl.c index 5f558d2ee..fac7f7377 100644 --- a/dwl.c +++ b/dwl.c @@ -75,154 +75,154 @@ enum { XDGShell, LayerShell, X11Managed, X11Unmanaged }; /* client types */ enum { LyrBg, LyrBottom, LyrTop, LyrOverlay, LyrTile, LyrFloat, LyrNoFocus, NUM_LAYERS }; /* scene layers */ #ifdef XWAYLAND enum { NetWMWindowTypeDialog, NetWMWindowTypeSplash, NetWMWindowTypeToolbar, - NetWMWindowTypeUtility, NetLast }; /* EWMH atoms */ + NetWMWindowTypeUtility, NetLast }; /* EWMH atoms */ #endif typedef union { - int i; - unsigned int ui; - float f; - void *v; + int i; + unsigned int ui; + float f; + void *v; } Arg; typedef struct { - unsigned int mod; - unsigned int button; - scm_t_bits *func; + unsigned int mod; + unsigned int button; + scm_t_bits *func; } Button; typedef struct Monitor Monitor; typedef struct { - /* Must be first */ - unsigned int type; /* XDGShell or X11* */ - struct wlr_scene_node *scene; - struct wlr_scene_rect *border[4]; /* top, bottom, left, right */ - struct wlr_scene_node *scene_surface; - struct wl_list link; - struct wl_list flink; - union { - struct wlr_xdg_surface *xdg; - struct wlr_xwayland_surface *xwayland; - } surface; - struct wl_listener commit; - 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, prev; /* layout-relative, includes border */ - Monitor *mon; + /* Must be first */ + unsigned int type; /* XDGShell or X11* */ + struct wlr_scene_node *scene; + struct wlr_scene_rect *border[4]; /* top, bottom, left, right */ + struct wlr_scene_node *scene_surface; + struct wl_list link; + struct wl_list flink; + union { + struct wlr_xdg_surface *xdg; + struct wlr_xwayland_surface *xwayland; + } surface; + struct wl_listener commit; + 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, prev; /* layout-relative, includes border */ + Monitor *mon; #ifdef XWAYLAND - struct wl_listener activate; - struct wl_listener configure; + struct wl_listener activate; + struct wl_listener configure; #endif - int bw; - unsigned int tags; - int isfloating, isurgent; - uint32_t resize; /* configure serial of a pending resize */ - int isfullscreen; - double alpha; - double prevalpha; + int bw; + unsigned int tags; + int isfloating, isurgent; + uint32_t resize; /* configure serial of a pending resize */ + int isfullscreen; + double alpha; + double prevalpha; } Client; typedef struct { - uint32_t singular_anchor; - uint32_t anchor_triplet; - int *positive_axis; - int *negative_axis; - int margin; + uint32_t singular_anchor; + uint32_t anchor_triplet; + int *positive_axis; + int *negative_axis; + int margin; } Edge; typedef struct { - uint32_t mod; - xkb_keycode_t keycode; - scm_t_bits *func; + uint32_t mod; + xkb_keycode_t keycode; + scm_t_bits *func; } Key; typedef struct { - struct wl_list link; - struct wlr_input_device *device; + struct wl_list link; + struct wlr_input_device *device; - struct wl_listener modifiers; - struct wl_listener key; - struct wl_listener destroy; + struct wl_listener modifiers; + struct wl_listener key; + struct wl_listener destroy; } Keyboard; typedef struct { - /* Must be first */ - unsigned int type; /* LayerShell */ - int mapped; - struct wlr_scene_node *scene; - struct wl_list link; - struct wlr_layer_surface_v1 *layer_surface; - - struct wl_listener destroy; - struct wl_listener map; - struct wl_listener unmap; - struct wl_listener surface_commit; - - struct wlr_box geo; + /* Must be first */ + unsigned int type; /* LayerShell */ + int mapped; + struct wlr_scene_node *scene; + struct wl_list link; + struct wlr_layer_surface_v1 *layer_surface; + + struct wl_listener destroy; + struct wl_listener map; + struct wl_listener unmap; + struct wl_listener surface_commit; + + struct wlr_box geo; } LayerSurface; typedef struct { - char *symbol; - scm_t_bits *arrange; - char *id; + char *symbol; + scm_t_bits *arrange; + char *id; } Layout; typedef struct { - struct wl_list link; - struct wl_resource *resource; + struct wl_list link; + struct wl_resource *resource; } DscmClient; typedef struct { - struct wl_list link; - struct wl_resource *resource; - struct Monitor *monitor; + struct wl_list link; + struct wl_resource *resource; + struct Monitor *monitor; } DscmMonitor; struct Monitor { - struct wl_list link; - struct wlr_output *wlr_output; - struct wlr_scene_output *scene_output; - struct wl_listener frame; - struct wl_listener destroy; - struct wlr_box m; /* monitor area, layout-relative */ - struct wlr_box w; /* window area, layout-relative */ - struct wl_list layers[4]; /* LayerSurface::link */ - struct wl_list dscm; - const Layout *lt[2]; - unsigned int seltags; - unsigned int sellt; - unsigned int tagset[2]; - unsigned int prevtagset; - double mfact; - int gappih; /* horizontal gap between windows */ - int gappiv; /* vertical gap between windows */ - int gappoh; /* horizontal outer gaps */ - int gappov; /* vertical outer gaps */ - int nmaster; - int un_map; /* If a map/unmap happened on this monitor, then this should be true */ + struct wl_list link; + struct wlr_output *wlr_output; + struct wlr_scene_output *scene_output; + struct wl_listener frame; + struct wl_listener destroy; + struct wlr_box m; /* monitor area, layout-relative */ + struct wlr_box w; /* window area, layout-relative */ + struct wl_list layers[4]; /* LayerSurface::link */ + struct wl_list dscm; + const Layout *lt[2]; + unsigned int seltags; + unsigned int sellt; + unsigned int tagset[2]; + unsigned int prevtagset; + double mfact; + int gappih; /* horizontal gap between windows */ + int gappiv; /* vertical gap between windows */ + int gappoh; /* horizontal outer gaps */ + int gappov; /* vertical outer gaps */ + int nmaster; + int un_map; /* If a map/unmap happened on this monitor, then this should be true */ }; typedef struct { - char *name; - float mfact; - int nmaster; - float scale; - const Layout *lt; - enum wl_output_transform rr; - int x; - int y; + char *name; + float mfact; + int nmaster; + float scale; + const Layout *lt; + enum wl_output_transform rr; + int x; + int y; } MonitorRule; typedef struct { - char *id; - char *title; - unsigned int tags; - int isfloating; - double alpha; - int monitor; + char *id; + char *title; + unsigned int tags; + int isfloating; + double alpha; + int monitor; } Rule; /* dscm protocol */ @@ -234,28 +234,28 @@ static void dscm_destroymon(struct wl_resource *resource); static void dscm_printstatusmon(Monitor *m, const DscmMonitor *mon); static void dscm_printstatus(Monitor *m); static void dscm_settags(struct wl_client *client, struct wl_resource *resource, - uint32_t t, uint32_t toggle_tagset); + uint32_t t, uint32_t toggle_tagset); static void dscm_setlayout(struct wl_client *client, struct wl_resource *resource, - uint32_t layout); + uint32_t layout); static void dscm_setclient(struct wl_client *client, struct wl_resource *resource, - uint32_t and, uint32_t xor); + uint32_t and, uint32_t xor); static void dscm_release(struct wl_client *client, struct wl_resource *resource); static void dscm_getmon(struct wl_client *client, struct wl_resource *resource, - uint32_t id, struct wl_resource *output); + uint32_t id, struct wl_resource *output); static void dscm_destroy(struct wl_resource *resource); static void dscm_bind(struct wl_client *client, void *data, uint32_t version, - uint32_t id); + uint32_t id); /* function declarations */ static void applybounds(Client *c, struct wlr_box *bbox); static void 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); + int32_t exclusive, int32_t margin_top, + int32_t margin_right, int32_t margin_bottom, + int32_t margin_left); static void applyrules(Client *c); static void arrange(Monitor *m); static void arrangelayer(Monitor *m, struct wl_list *list, - struct wlr_box *usable_area, int exclusive); + struct wlr_box *usable_area, int exclusive); static void arrangelayers(Monitor *m); static void axisnotify(struct wl_listener *listener, void *data); static void buttonpress(struct wl_listener *listener, void *data); @@ -298,19 +298,18 @@ 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 new_subnotify(struct wl_listener *listener, void *data); static void outputmgrapply(struct wl_listener *listener, void *data); static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, - int test); + int 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); + double sx, double sy, uint32_t time); static void printstatus(void); static void quit(const Arg *arg); static void quitsignal(int signo); static void rendermon(struct wl_listener *listener, void *data); static void resize(Client *c, int x, int y, int w, int h, int interact, - int draw_borders); + int draw_borders); static void requeststartdrag(struct wl_listener *listener, void *data); static void run(char *startup_cmd); static Client *selclient(void); @@ -345,7 +344,7 @@ static void virtualkeyboard(struct wl_listener *listener, void *data); static void writepid(const char *runtimedir); static Monitor *xytomon(double x, double y); static struct wlr_scene_node *xytonode(double x, double y, struct wlr_surface **psurface, - Client **pc, LayerSurface **pl, double *nx, double *ny); + Client **pc, LayerSurface **pl, double *nx, double *ny); static void zoom(const Arg *arg); static void setgaps(int oh, int ov, int ih, int iv); static void incrgaps(const Arg *arg); @@ -392,7 +391,6 @@ static struct wl_list keyboards; static unsigned int cursor_mode; static Client *grabc; static int grabcx, grabcy; /* client-relative */ -static struct wlr_drag_icon* drag_icon; static struct wlr_output_layout *output_layout; static struct wlr_box sgeom; @@ -410,7 +408,6 @@ static struct wl_listener cursor_motion_absolute = {.notify = motionabsolute}; static struct wl_listener idle_inhibitor_create = {.notify = createidleinhibitor}; static struct wl_listener idle_inhibitor_destroy = {.notify = destroyidleinhibitor}; static struct wl_listener layout_change = {.notify = updatemons}; -static struct wl_listener new_idle_inhibitor = {.notify = createidleinhibitor}; 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}; @@ -428,21 +425,20 @@ static struct wl_listener drag_icon_destroy = {.notify = dragicondestroy}; /* dscm event handlers */ static struct dscm_monitor_v1_interface dscm_monitor_implementation = { - .release = dscm_closemon, - .set_tags = dscm_settags, - .set_layout = dscm_setlayout, - .set_client_tags = dscm_setclient, + .release = dscm_closemon, + .set_tags = dscm_settags, + .set_layout = dscm_setlayout, + .set_client_tags = dscm_setclient, }; static struct dscm_v1_interface dscm_implementation = { - .release = dscm_release, - .get_monitor = dscm_getmon, + .release = dscm_release, + .get_monitor = dscm_getmon, }; #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); -void commitnotifyx11(struct wl_listener *listener, void *data); static Atom getatom(xcb_connection_t *xc, const char *name); static void xwaylandready(struct wl_listener *listener, void *data); static struct wl_listener new_xwayland_surface = {.notify = createnotifyx11}; @@ -463,2662 +459,2631 @@ static Atom netatom[NetLast]; void applybounds(Client *c, struct wlr_box *bbox) { - /* set minimum possible */ - c->geom.width = MAX(1, c->geom.width); - c->geom.height = MAX(1, c->geom.height); + /* set minimum possible */ + c->geom.width = MAX(1, c->geom.width); + c->geom.height = MAX(1, c->geom.height); - if (c->geom.x >= bbox->x + bbox->width) - c->geom.x = bbox->x + bbox->width - c->geom.width; - if (c->geom.y >= bbox->y + bbox->height) - c->geom.y = bbox->y + bbox->height - c->geom.height; - if (c->geom.x + c->geom.width + 2 * c->bw <= bbox->x) - c->geom.x = bbox->x; - if (c->geom.y + c->geom.height + 2 * c->bw <= bbox->y) - c->geom.y = bbox->y; + if (c->geom.x >= bbox->x + bbox->width) + c->geom.x = bbox->x + bbox->width - c->geom.width; + if (c->geom.y >= bbox->y + bbox->height) + c->geom.y = bbox->y + bbox->height - c->geom.height; + if (c->geom.x + c->geom.width + 2 * c->bw <= bbox->x) + c->geom.x = bbox->x; + if (c->geom.y + c->geom.height + 2 * c->bw <= bbox->y) + c->geom.y = bbox->y; } void 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) { - Edge 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) - && exclusive + edges[i].margin > 0) { - if (edges[i].positive_axis) - *edges[i].positive_axis += exclusive + edges[i].margin; - if (edges[i].negative_axis) - *edges[i].negative_axis -= exclusive + edges[i].margin; - break; - } - } + uint32_t anchor, int32_t exclusive, + int32_t margin_top, int32_t margin_right, + int32_t margin_bottom, int32_t margin_left) { + Edge 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) + && exclusive + edges[i].margin > 0) { + if (edges[i].positive_axis) + *edges[i].positive_axis += exclusive + edges[i].margin; + if (edges[i].negative_axis) + *edges[i].negative_axis -= exclusive + edges[i].margin; + break; + } + } } void applyrules(Client *c) { - /* rule matching */ - const char *appid, *title; - unsigned int j, newtags = 0; - Rule r; - Monitor *mon = selmon, *m; - - c->isfloating = client_is_float_type(c); - if (!(appid = client_get_appid(c))) - appid = broken; - if (!(title = client_get_title(c))) - title = broken; - - - for (int i = 0; i < numrules; i++) { - r = rules[i]; - if ((!r.title || strstr(title, r.title)) - && (!r.id || strstr(appid, r.id))) { - c->isfloating = r.isfloating; - c->alpha = r.alpha; - newtags |= r.tags; - i = 0; - wl_list_for_each(m, &mons, link) - if (r.monitor == i++) - mon = m; - } - } - wlr_scene_node_reparent(c->scene, layers[c->isfloating ? LyrFloat : LyrTile]); - setmon(c, mon, newtags); + /* rule matching */ + const char *appid, *title; + unsigned int j, newtags = 0; + Rule r; + Monitor *mon = selmon, *m; + + c->isfloating = client_is_float_type(c); + if (!(appid = client_get_appid(c))) + appid = broken; + if (!(title = client_get_title(c))) + title = broken; + + + for (int i = 0; i < numrules; i++) { + r = rules[i]; + if ((!r.title || strstr(title, r.title)) + && (!r.id || strstr(appid, r.id))) { + c->isfloating = r.isfloating; + c->alpha = r.alpha; + newtags |= r.tags; + j = 0; + wl_list_for_each(m, &mons, link) + if (r.monitor == j++) + mon = m; + } + } + wlr_scene_node_reparent(c->scene, layers[c->isfloating ? LyrFloat : LyrTile]); + setmon(c, mon, newtags); } void arrange(Monitor *m) { - Client *c; - wl_list_for_each(c, &clients, link) - wlr_scene_node_set_enabled(c->scene, VISIBLEON(c, c->mon)); + Client *c; + wl_list_for_each(c, &clients, link) + wlr_scene_node_set_enabled(c->scene, VISIBLEON(c, c->mon)); - if (m->lt[m->sellt]->arrange) - dscm_safe_call(DSCM_CALL_ARRANGE, m->lt[m->sellt]->arrange, m); - /* TODO recheck pointer focus here... or in resize()? */ + if (m->lt[m->sellt]->arrange) + dscm_safe_call(DSCM_CALL_ARRANGE, m->lt[m->sellt]->arrange, m); + /* TODO recheck pointer focus here... or in resize()? */ } void arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, - int exclusive) -{ - LayerSurface *layersurface; - struct wlr_box full_area = m->m; - - 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; - - /* Horizontal axis */ - if ((state->anchor & both_horiz) && box.width == 0) { - box.x = bounds.x; - box.width = bounds.width; - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { - box.x = bounds.x; - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { - box.x = bounds.x + (bounds.width - box.width); - } else { - box.x = bounds.x + ((bounds.width / 2) - (box.width / 2)); - } - /* Vertical axis */ - if ((state->anchor & both_vert) && box.height == 0) { - box.y = bounds.y; - box.height = bounds.height; - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { - box.y = bounds.y; - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { - box.y = bounds.y + (bounds.height - box.height); - } else { - box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); - } - /* Margin */ - if ((state->anchor & both_horiz) == both_horiz) { - box.x += state->margin.left; - box.width -= state->margin.left + state->margin.right; - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { - box.x += state->margin.left; - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { - box.x -= state->margin.right; - } - if ((state->anchor & both_vert) == both_vert) { - box.y += state->margin.top; - box.height -= state->margin.top + state->margin.bottom; - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { - box.y += state->margin.top; - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { - box.y -= state->margin.bottom; - } - if (box.width < 0 || box.height < 0) { - wlr_layer_surface_v1_destroy(wlr_layer_surface); - continue; - } - layersurface->geo = box; - - if (state->exclusive_zone > 0) - applyexclusive(usable_area, state->anchor, state->exclusive_zone, - state->margin.top, state->margin.right, - state->margin.bottom, state->margin.left); - wlr_scene_node_set_position(layersurface->scene, box.x, box.y); - wlr_layer_surface_v1_configure(wlr_layer_surface, box.width, box.height); - } + int exclusive) +{ + LayerSurface *layersurface; + struct wlr_box full_area = m->m; + + 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; + + /* Horizontal axis */ + if ((state->anchor & both_horiz) && box.width == 0) { + box.x = bounds.x; + box.width = bounds.width; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { + box.x = bounds.x; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { + box.x = bounds.x + (bounds.width - box.width); + } else { + box.x = bounds.x + ((bounds.width / 2) - (box.width / 2)); + } + /* Vertical axis */ + if ((state->anchor & both_vert) && box.height == 0) { + box.y = bounds.y; + box.height = bounds.height; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { + box.y = bounds.y; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { + box.y = bounds.y + (bounds.height - box.height); + } else { + box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); + } + /* Margin */ + if ((state->anchor & both_horiz) == both_horiz) { + box.x += state->margin.left; + box.width -= state->margin.left + state->margin.right; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { + box.x += state->margin.left; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { + box.x -= state->margin.right; + } + if ((state->anchor & both_vert) == both_vert) { + box.y += state->margin.top; + box.height -= state->margin.top + state->margin.bottom; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { + box.y += state->margin.top; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { + box.y -= state->margin.bottom; + } + if (box.width < 0 || box.height < 0) { + wlr_layer_surface_v1_destroy(wlr_layer_surface); + continue; + } + layersurface->geo = box; + + if (state->exclusive_zone > 0) + applyexclusive(usable_area, state->anchor, state->exclusive_zone, + state->margin.top, state->margin.right, + state->margin.bottom, state->margin.left); + wlr_scene_node_set_position(layersurface->scene, box.x, box.y); + wlr_layer_surface_v1_configure(wlr_layer_surface, box.width, box.height); + } } void arrangelayers(Monitor *m) { - int i; - struct wlr_box usable_area = m->m; - uint32_t layers_above_shell[] = { - ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, - ZWLR_LAYER_SHELL_V1_LAYER_TOP, - }; - LayerSurface *layersurface; - struct wlr_keyboard *kb = wlr_seat_get_keyboard(seat); - - /* Arrange exclusive surfaces from top->bottom */ - for (i = 3; i >= 0; i--) - arrangelayer(m, &m->layers[i], &usable_area, 1); - - if (memcmp(&usable_area, &m->w, sizeof(struct wlr_box))) { - m->w = usable_area; - arrange(m); - } - - /* Arrange non-exlusive surfaces from top->bottom */ - for (i = 3; i >= 0; i--) - arrangelayer(m, &m->layers[i], &usable_area, 0); - - /* Find topmost keyboard interactive layer, if such a layer exists */ - for (size_t i = 0; i < LENGTH(layers_above_shell); i++) { - wl_list_for_each_reverse(layersurface, - &m->layers[layers_above_shell[i]], link) { - if (layersurface->layer_surface->current.keyboard_interactive && - layersurface->layer_surface->mapped) { - /* Deactivate the focused client. */ - focusclient(NULL, 0); - wlr_seat_keyboard_notify_enter(seat, layersurface->layer_surface->surface, - kb->keycodes, kb->num_keycodes, &kb->modifiers); - return; - } - } - } + int i; + struct wlr_box usable_area = m->m; + uint32_t layers_above_shell[] = { + ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, + ZWLR_LAYER_SHELL_V1_LAYER_TOP, + }; + LayerSurface *layersurface; + struct wlr_keyboard *kb = wlr_seat_get_keyboard(seat); + + /* Arrange exclusive surfaces from top->bottom */ + for (i = 3; i >= 0; i--) + arrangelayer(m, &m->layers[i], &usable_area, 1); + + if (memcmp(&usable_area, &m->w, sizeof(struct wlr_box))) { + m->w = usable_area; + arrange(m); + } + + /* Arrange non-exlusive surfaces from top->bottom */ + for (i = 3; i >= 0; i--) + arrangelayer(m, &m->layers[i], &usable_area, 0); + + /* Find topmost keyboard interactive layer, if such a layer exists */ + for (size_t i = 0; i < LENGTH(layers_above_shell); i++) { + wl_list_for_each_reverse(layersurface, + &m->layers[layers_above_shell[i]], link) { + if (layersurface->layer_surface->current.keyboard_interactive && + layersurface->layer_surface->mapped) { + /* Deactivate the focused client. */ + focusclient(NULL, 0); + wlr_seat_keyboard_notify_enter(seat, layersurface->layer_surface->surface, + kb->keycodes, kb->num_keycodes, &kb->modifiers); + return; + } + } + } } void 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, - event->delta_discrete, event->source); + /* 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, + event->delta_discrete, event->source); } void buttonpress(struct wl_listener *listener, void *data) { - struct wlr_event_pointer_button *event = data; - struct wlr_keyboard *keyboard; - uint32_t mods; - Client *c; - 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 */ - xytonode(cursor->x, cursor->y, NULL, &c, NULL, NULL, NULL); - /* Don't focus unmanaged clients */ - if (c && !client_is_unmanaged(c)) - focusclient(c, 1); - - keyboard = wlr_seat_get_keyboard(seat); - mods = wlr_keyboard_get_modifiers(keyboard); - for (int i = 0; i < numbuttons; i++) { - b = buttons[i]; - if (CLEANMASK(mods) == CLEANMASK(b.mod) && - event->button == b.button && b.func) { - dscm_safe_call(DSCM_CALL_ACTION, b.func, NULL); - return; - } - } - break; - case WLR_BUTTON_RELEASED: - /* If you released any buttons, we exit interactive move/resize mode. */ - /* 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); - cursor_mode = CurNormal; - /* Drop the window off on its new monitor */ - selmon = xytomon(cursor->x, cursor->y); - setmon(grabc, selmon, 0); - return; - } - break; - } - /* If the event wasn't handled by the compositor, notify the client with - * pointer focus that a button press has occurred */ - wlr_seat_pointer_notify_button(seat, - event->time_msec, event->button, event->state); + struct wlr_event_pointer_button *event = data; + struct wlr_keyboard *keyboard; + uint32_t mods; + Client *c; + 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 */ + xytonode(cursor->x, cursor->y, NULL, &c, NULL, NULL, NULL); + /* Don't focus unmanaged clients */ + if (c && !client_is_unmanaged(c)) + focusclient(c, 1); + + keyboard = wlr_seat_get_keyboard(seat); + mods = wlr_keyboard_get_modifiers(keyboard); + for (int i = 0; i < numbuttons; i++) { + b = buttons[i]; + if (CLEANMASK(mods) == CLEANMASK(b.mod) && + event->button == b.button && b.func) { + dscm_safe_call(DSCM_CALL_ACTION, b.func, NULL); + return; + } + } + break; + case WLR_BUTTON_RELEASED: + /* If you released any buttons, we exit interactive move/resize mode. */ + /* 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); + cursor_mode = CurNormal; + /* Drop the window off on its new monitor */ + selmon = xytomon(cursor->x, cursor->y); + setmon(grabc, selmon, 0); + return; + } + break; + } + /* If the event wasn't handled by the compositor, notify the client with + * pointer focus that a button press has occurred */ + wlr_seat_pointer_notify_button(seat, + event->time_msec, event->button, event->state); } void changealpha(const Arg *arg) { - Client *sel = selclient(); + Client *sel = selclient(); - if (sel) { - sel->alpha += arg->f; - if (sel->alpha > 1.0) - sel->alpha = 1.0; + if (sel) { + sel->alpha += arg->f; + if (sel->alpha > 1.0) + sel->alpha = 1.0; - if (sel->alpha < 0.1) - sel->alpha = 0.1; - } + if (sel->alpha < 0.1) + sel->alpha = 0.1; + } } void chvt(const Arg *arg) { - wlr_session_change_vt(wlr_backend_get_session(backend), arg->ui); + wlr_session_change_vt(wlr_backend_get_session(backend), arg->ui); } void cleanup(void) { #ifdef XWAYLAND - wlr_xwayland_destroy(xwayland); + wlr_xwayland_destroy(xwayland); #endif - wl_display_destroy_clients(dpy); + wl_display_destroy_clients(dpy); - wlr_backend_destroy(backend); - wlr_xcursor_manager_destroy(cursor_mgr); - wlr_cursor_destroy(cursor); - wlr_output_layout_destroy(output_layout); - wlr_seat_destroy(seat); - wl_display_destroy(dpy); + wlr_backend_destroy(backend); + wlr_xcursor_manager_destroy(cursor_mgr); + wlr_cursor_destroy(cursor); + wlr_output_layout_destroy(output_layout); + wlr_seat_destroy(seat); + wl_display_destroy(dpy); } void cleanupkeyboard(struct wl_listener *listener, void *data) { - struct wlr_input_device *device = data; - Keyboard *kb = device->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); + 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); } void cleanupmon(struct wl_listener *listener, void *data) { - struct wlr_output *wlr_output = data; - DscmMonitor *mon, *montmp; - Monitor *m = wlr_output->data; - int nmons, i = 0; + struct wlr_output *wlr_output = data; + DscmMonitor *mon, *montmp; + Monitor *m = wlr_output->data; + int nmons, 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); - wlr_scene_output_destroy(m->scene_output); + 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); + wlr_scene_output_destroy(m->scene_output); - if ((nmons = wl_list_length(&mons))) - do /* don't switch to disabled mons */ - selmon = wl_container_of(mons.prev, selmon, link); - while (!selmon->wlr_output->enabled && i++ < nmons); + if ((nmons = wl_list_length(&mons))) + 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); - closemon(m); - free(m); + focusclient(focustop(selmon), 1); + closemon(m); + free(m); } void closemon(Monitor *m) { - /* move closed monitor's clients to the focused one */ - Client *c; + /* move closed monitor's clients to the focused one */ + Client *c; - 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, 1); - if (c->mon == m) - setmon(c, selmon, c->tags); - } + 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, 1); + if (c->mon == m) + setmon(c, selmon, c->tags); + } } void 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; + 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; - wlr_scene_node_reparent(layersurface->scene, - layers[wlr_layer_surface->current.layer]); + wlr_scene_node_reparent(layersurface->scene, + layers[wlr_layer_surface->current.layer]); - if (!wlr_output || !(m = wlr_output->data)) - return; + if (!wlr_output || !(m = wlr_output->data)) + return; - if (wlr_layer_surface->current.committed == 0 - && layersurface->mapped == wlr_layer_surface->mapped) - return; + if (wlr_layer_surface->current.committed == 0 + && layersurface->mapped == wlr_layer_surface->mapped) + return; - layersurface->mapped = wlr_layer_surface->mapped; + layersurface->mapped = wlr_layer_surface->mapped; - if (layers[wlr_layer_surface->current.layer] != layersurface->scene) { - wl_list_remove(&layersurface->link); - wl_list_insert(&m->layers[wlr_layer_surface->current.layer], - &layersurface->link); - } - arrangelayers(m); + if (layers[wlr_layer_surface->current.layer] != layersurface->scene) { + wl_list_remove(&layersurface->link); + wl_list_insert(&m->layers[wlr_layer_surface->current.layer], + &layersurface->link); + } + arrangelayers(m); } void commitnotify(struct wl_listener *listener, void *data) { - Client *c = wl_container_of(listener, c, commit); + Client *c = wl_container_of(listener, c, commit); - /* mark a pending resize as completed */ - if (c->resize && c->resize <= c->surface.xdg->current.configure_serial) - c->resize = 0; + /* mark a pending resize as completed */ + if (c->resize && c->resize <= c->surface.xdg->current.configure_serial) + c->resize = 0; } void createidleinhibitor(struct wl_listener *listener, void *data) { - struct wlr_idle_inhibitor_v1 *idle_inhibitor = data; - wl_signal_add(&idle_inhibitor->events.destroy, &idle_inhibitor_destroy); + struct wlr_idle_inhibitor_v1 *idle_inhibitor = data; + wl_signal_add(&idle_inhibitor->events.destroy, &idle_inhibitor_destroy); - wlr_idle_set_enabled(idle, seat, 0); + wlr_idle_set_enabled(idle, seat, 0); } void createkeyboard(struct wlr_input_device *device) { - struct xkb_context *context; - struct xkb_keymap *keymap; - Keyboard *kb = device->data = ecalloc(1, sizeof(*kb)); - kb->device = device; + struct xkb_context *context; + struct xkb_keymap *keymap; + Keyboard *kb = device->data = ecalloc(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_keymap_new_from_names(context, xkb_rules, - XKB_KEYMAP_COMPILE_NO_FLAGS); + /* Prepare an XKB keymap and assign it to the keyboard. */ + context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + keymap = xkb_keymap_new_from_names(context, xkb_rules, + XKB_KEYMAP_COMPILE_NO_FLAGS); - wlr_keyboard_set_keymap(device->keyboard, keymap); - xkb_keymap_unref(keymap); - xkb_context_unref(context); - wlr_keyboard_set_repeat_info(device->keyboard, repeat_rate, repeat_delay); + wlr_keyboard_set_keymap(device->keyboard, keymap); + xkb_keymap_unref(keymap); + xkb_context_unref(context); + wlr_keyboard_set_repeat_info(device->keyboard, repeat_rate, repeat_delay); - /* Here we set up listeners for keyboard events. */ - LISTEN(&device->keyboard->events.modifiers, &kb->modifiers, keypressmod); - LISTEN(&device->keyboard->events.key, &kb->key, keypress); - LISTEN(&device->events.destroy, &kb->destroy, cleanupkeyboard); + /* Here we set up listeners for keyboard events. */ + LISTEN(&device->keyboard->events.modifiers, &kb->modifiers, keypressmod); + LISTEN(&device->keyboard->events.key, &kb->key, keypress); + LISTEN(&device->events.destroy, &kb->destroy, cleanupkeyboard); - wlr_seat_set_keyboard(seat, device); + wlr_seat_set_keyboard(seat, device); - /* And add the keyboard to our list of keyboards */ - wl_list_insert(&keyboards, &kb->link); + /* And add the keyboard to our list of keyboards */ + wl_list_insert(&keyboards, &kb->link); } 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; + 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; - } + if (!wlr_layer_surface->output) { + wlr_layer_surface->output = selmon->wlr_output; + } - layersurface = ecalloc(1, sizeof(LayerSurface)); - layersurface->type = LayerShell; - LISTEN(&wlr_layer_surface->surface->events.commit, - &layersurface->surface_commit, commitlayersurfacenotify); - LISTEN(&wlr_layer_surface->events.destroy, &layersurface->destroy, - destroylayersurfacenotify); - LISTEN(&wlr_layer_surface->events.map, &layersurface->map, - maplayersurfacenotify); - LISTEN(&wlr_layer_surface->events.unmap, &layersurface->unmap, - unmaplayersurfacenotify); + layersurface = ecalloc(1, sizeof(LayerSurface)); + layersurface->type = LayerShell; + LISTEN(&wlr_layer_surface->surface->events.commit, + &layersurface->surface_commit, commitlayersurfacenotify); + LISTEN(&wlr_layer_surface->events.destroy, &layersurface->destroy, + destroylayersurfacenotify); + LISTEN(&wlr_layer_surface->events.map, &layersurface->map, + maplayersurfacenotify); + LISTEN(&wlr_layer_surface->events.unmap, &layersurface->unmap, + unmaplayersurfacenotify); - layersurface->layer_surface = wlr_layer_surface; - wlr_layer_surface->data = layersurface; - m = wlr_layer_surface->output->data; + layersurface->layer_surface = wlr_layer_surface; + wlr_layer_surface->data = layersurface; + m = wlr_layer_surface->output->data; - layersurface->scene = wlr_layer_surface->surface->data = - wlr_scene_subsurface_tree_create(layers[wlr_layer_surface->pending.layer], - wlr_layer_surface->surface); - layersurface->scene->data = layersurface; + layersurface->scene = wlr_layer_surface->surface->data = + wlr_scene_subsurface_tree_create(layers[wlr_layer_surface->pending.layer], + wlr_layer_surface->surface); + layersurface->scene->data = layersurface; - wl_list_insert(&m->layers[wlr_layer_surface->pending.layer], - &layersurface->link); + wl_list_insert(&m->layers[wlr_layer_surface->pending.layer], + &layersurface->link); - /* Temporarily set the layer's current state to pending - * so that we can easily arrange it - */ - old_state = wlr_layer_surface->current; - wlr_layer_surface->current = wlr_layer_surface->pending; - arrangelayers(m); - wlr_layer_surface->current = old_state; + /* Temporarily set the layer's current state to pending + * so that we can easily arrange it + */ + old_state = wlr_layer_surface->current; + wlr_layer_surface->current = wlr_layer_surface->pending; + arrangelayers(m); + wlr_layer_surface->current = old_state; } void 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; - MonitorRule r; - Monitor *m = wlr_output->data = ecalloc(1, sizeof(*m)); - wl_list_init(&m->dscm); - m->wlr_output = wlr_output; - m->gappih = gappih; - m->gappiv = gappiv; - m->gappoh = gappoh; - m->gappov = gappov; - - wlr_output_init_render(wlr_output, alloc, drw); - - /* Initialize monitor state using configured rules */ - for (size_t i = 0; i < LENGTH(m->layers); i++) - wl_list_init(&m->layers[i]); - m->tagset[0] = m->tagset[1] = 1; - for (int i = 0; i < nummonrules; i++) { - r = monrules[i]; - if (!r.name || strstr(wlr_output->name, r.name)) { - m->mfact = r.mfact; - 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; - wlr_output_set_transform(wlr_output, r.rr); - break; - } - } - - /* The mode is a tuple of (width, height, refresh rate), and each - * monitor supports only a specific set of modes. We just pick the - * monitor's preferred mode; a more sophisticated compositor would let - * the user configure it. */ - wlr_output_set_mode(wlr_output, wlr_output_preferred_mode(wlr_output)); - wlr_output_enable_adaptive_sync(wlr_output, 1); - - /* Set up event listeners */ - LISTEN(&wlr_output->events.frame, &m->frame, rendermon); - LISTEN(&wlr_output->events.destroy, &m->destroy, cleanupmon); - - 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 - * display, which Wayland clients can see to find out information about the - * output (such as DPI, scale factor, manufacturer, etc). - */ - m->scene_output = wlr_scene_output_create(scene, wlr_output); - wlr_output_layout_add_auto(output_layout, wlr_output); - - /* If length == 1 we need update selmon. - * Maybe it will change in run(). */ - if (wl_list_length(&mons) == 1) { - Client *c; - selmon = m; - /* If there is any client, set c->mon to this monitor */ - wl_list_for_each(c, &clients, link) - setmon(c, m, c->tags); - } + /* This event is raised by the backend when a new output (aka a display or + * monitor) becomes available. */ + struct wlr_output *wlr_output = data; + MonitorRule r; + Monitor *m = wlr_output->data = ecalloc(1, sizeof(*m)); + wl_list_init(&m->dscm); + m->wlr_output = wlr_output; + m->gappih = gappih; + m->gappiv = gappiv; + m->gappoh = gappoh; + m->gappov = gappov; + + wlr_output_init_render(wlr_output, alloc, drw); + + /* Initialize monitor state using configured rules */ + for (size_t i = 0; i < LENGTH(m->layers); i++) + wl_list_init(&m->layers[i]); + m->tagset[0] = m->tagset[1] = 1; + for (int i = 0; i < nummonrules; i++) { + r = monrules[i]; + if (!r.name || strstr(wlr_output->name, r.name)) { + m->mfact = r.mfact; + 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; + wlr_output_set_transform(wlr_output, r.rr); + break; + } + } + + /* The mode is a tuple of (width, height, refresh rate), and each + * monitor supports only a specific set of modes. We just pick the + * monitor's preferred mode; a more sophisticated compositor would let + * the user configure it. */ + wlr_output_set_mode(wlr_output, wlr_output_preferred_mode(wlr_output)); + wlr_output_enable_adaptive_sync(wlr_output, 1); + + /* Set up event listeners */ + LISTEN(&wlr_output->events.frame, &m->frame, rendermon); + LISTEN(&wlr_output->events.destroy, &m->destroy, cleanupmon); + + 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 + * display, which Wayland clients can see to find out information about the + * output (such as DPI, scale factor, manufacturer, etc). + */ + m->scene_output = wlr_scene_output_create(scene, wlr_output); + wlr_output_layout_add_auto(output_layout, wlr_output); + + /* If length == 1 we need update selmon. + * Maybe it will change in run(). */ + if (wl_list_length(&mons) == 1) { + Client *c; + selmon = m; + /* If there is any client, set c->mon to this monitor */ + wl_list_for_each(c, &clients, link) + setmon(c, m, c->tags); + } } void createnotify(struct wl_listener *listener, void *data) { - /* This event is raised when wlr_xdg_shell receives a new xdg surface from a - * client, either a toplevel (application window) or popup, - * or when wlr_layer_shell receives a new popup from a layer. - * If you want to do something tricky with popups you should check if - * its parent is wlr_xdg_shell or wlr_layer_shell */ - struct wlr_xdg_surface *xdg_surface = data; - Client *c; - - if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { - struct wlr_box box; - xdg_surface->surface->data = wlr_scene_xdg_surface_create( - xdg_surface->popup->parent->data, xdg_surface); - if (!(c = client_from_popup(xdg_surface->popup)) || !c->mon) - return; - box = c->mon->m; - box.x -= c->geom.x; - box.y -= c->geom.y; - wlr_xdg_popup_unconstrain_from_box(xdg_surface->popup, &box); - return; - } else if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_NONE) - return; - - /* Allocate a Client for this surface */ - c = xdg_surface->data = ecalloc(1, sizeof(*c)); - c->surface.xdg = xdg_surface; - c->bw = borderpx; - c->alpha = default_alpha; - - 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); - 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; + /* This event is raised when wlr_xdg_shell receives a new xdg surface from a + * client, either a toplevel (application window) or popup, + * or when wlr_layer_shell receives a new popup from a layer. + * If you want to do something tricky with popups you should check if + * its parent is wlr_xdg_shell or wlr_layer_shell */ + struct wlr_xdg_surface *xdg_surface = data; + Client *c; + + if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { + struct wlr_box box; + xdg_surface->surface->data = wlr_scene_xdg_surface_create( + xdg_surface->popup->parent->data, xdg_surface); + if (!(c = client_from_popup(xdg_surface->popup)) || !c->mon) + return; + box = c->mon->m; + box.x -= c->geom.x; + box.y -= c->geom.y; + wlr_xdg_popup_unconstrain_from_box(xdg_surface->popup, &box); + return; + } else if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_NONE) + return; + + /* Allocate a Client for this surface */ + c = xdg_surface->data = ecalloc(1, sizeof(*c)); + c->surface.xdg = xdg_surface; + c->bw = borderpx; + c->alpha = default_alpha; + + 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); + 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; } void createpointer(struct wlr_input_device *device) { - if (wlr_input_device_is_libinput(device)) { - struct libinput_device *libinput_device = (struct libinput_device*) - wlr_libinput_get_device_handle(device); + if (wlr_input_device_is_libinput(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 (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); - } + 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 - * acceleration, etc. */ - wlr_cursor_attach_input_device(cursor, device); + /* 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 + * acceleration, etc. */ + wlr_cursor_attach_input_device(cursor, device); } void cursorframe(struct wl_listener *listener, void *data) { - /* This event is forwarded by the cursor when a pointer emits an frame - * event. Frame events are sent after regular pointer events to group - * multiple events together. For instance, two axis events may happen at the - * same time, in which case a frame event won't be sent in between. */ - /* Notify the client with pointer focus of the frame event. */ - wlr_seat_pointer_notify_frame(seat); + /* This event is forwarded by the cursor when a pointer emits an frame + * event. Frame events are sent after regular pointer events to group + * multiple events together. For instance, two axis events may happen at the + * same time, in which case a frame event won't be sent in between. */ + /* Notify the client with pointer focus of the frame event. */ + wlr_seat_pointer_notify_frame(seat); } void cyclelayout(const Arg *arg) { - Layout *l; - unsigned int index = 0; - for (l = (Layout *)layouts; l != selmon->lt[selmon->sellt]; l++, index++); - if (arg->i > 0) { - if (index < (numlayouts - 1)) - setlayout(&((Arg) {.v = (l + 1)})); - else - setlayout(&((Arg) {.v = layouts})); - } else { - if (index > 0) - setlayout(&((Arg) {.v = (l - 1)})); - else - setlayout(&((Arg) {.v = &layouts[numlayouts - 1]})); - } + Layout *l; + unsigned int index = 0; + for (l = (Layout *)layouts; l != selmon->lt[selmon->sellt]; l++, index++); + if (arg->i > 0) { + if (index < (numlayouts - 1)) + setlayout(&((Arg) {.v = (l + 1)})); + else + setlayout(&((Arg) {.v = layouts})); + } else { + if (index > 0) + setlayout(&((Arg) {.v = (l - 1)})); + else + setlayout(&((Arg) {.v = &layouts[numlayouts - 1]})); + } } void destroyidleinhibitor(struct wl_listener *listener, void *data) { - /* I've been testing and at this point the inhibitor has not yet been - * removed from the list, checking if it has at least one item. */ - wlr_idle_set_enabled(idle, seat, wl_list_length(&idle_inhibit_mgr->inhibitors) <= 1); + /* I've been testing and at this point the inhibitor has not yet been + * removed from the list, checking if it has at least one item. */ + wlr_idle_set_enabled(idle, seat, wl_list_length(&idle_inhibit_mgr->inhibitors) <= 1); } void destroylayersurfacenotify(struct wl_listener *listener, void *data) { - LayerSurface *layersurface = wl_container_of(listener, layersurface, destroy); + LayerSurface *layersurface = wl_container_of(listener, layersurface, destroy); - if (layersurface->layer_surface->mapped) - unmaplayersurface(layersurface); - wl_list_remove(&layersurface->link); - wl_list_remove(&layersurface->destroy.link); - wl_list_remove(&layersurface->map.link); - wl_list_remove(&layersurface->unmap.link); - wl_list_remove(&layersurface->surface_commit.link); - if (layersurface->layer_surface->output) { - Monitor *m = layersurface->layer_surface->output->data; - if (m) { - arrangelayers(m); - } - layersurface->layer_surface->output = NULL; - } - free(layersurface); + if (layersurface->layer_surface->mapped) + unmaplayersurface(layersurface); + wl_list_remove(&layersurface->link); + wl_list_remove(&layersurface->destroy.link); + wl_list_remove(&layersurface->map.link); + wl_list_remove(&layersurface->unmap.link); + wl_list_remove(&layersurface->surface_commit.link); + if (layersurface->layer_surface->output) { + Monitor *m = layersurface->layer_surface->output->data; + if (m) + arrangelayers(m); + layersurface->layer_surface->output = NULL; + } + free(layersurface); } void destroynotify(struct wl_listener *listener, void *data) { - /* Called when the surface is destroyed and should never be shown again. */ - Client *c = wl_container_of(listener, c, destroy); - 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); + /* Called when the surface is destroyed and should never be shown again. */ + Client *c = wl_container_of(listener, c, destroy); + 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 != XDGShell) { - wl_list_remove(&c->configure.link); - wl_list_remove(&c->activate.link); - } else + if (c->type != XDGShell) { + wl_list_remove(&c->configure.link); + wl_list_remove(&c->activate.link); + } else #endif - wl_list_remove(&c->commit.link); - free(c); + wl_list_remove(&c->commit.link); + free(c); } Monitor * dirtomon(enum wlr_direction dir) { - struct wlr_output *next; - if ((next = wlr_output_layout_adjacent_output( - output_layout, dir, selmon->wlr_output, selmon->m.x, selmon->m.y))) - return next->data; - if ((next = wlr_output_layout_farthest_output( - output_layout, dir ^ (WLR_DIRECTION_LEFT|WLR_DIRECTION_RIGHT), - selmon->wlr_output, selmon->m.x, selmon->m.y))) - return next->data; - return selmon; + struct wlr_output *next; + if ((next = wlr_output_layout_adjacent_output( + output_layout, dir, selmon->wlr_output, selmon->m.x, selmon->m.y))) + return next->data; + if ((next = wlr_output_layout_farthest_output( + output_layout, dir ^ (WLR_DIRECTION_LEFT|WLR_DIRECTION_RIGHT), + selmon->wlr_output, selmon->m.x, selmon->m.y))) + return next->data; + return selmon; } void dragicondestroy(struct wl_listener *listener, void *data) { - struct wlr_drag_icon *icon = data; - wlr_scene_node_destroy(icon->data); - // Focus enter isn't sent during drag, so refocus the focused node. - focusclient(selclient(), 1); - motionnotify(0); + struct wlr_drag_icon *icon = data; + wlr_scene_node_destroy(icon->data); + // Focus enter isn't sent during drag, so refocus the focused node. + focusclient(selclient(), 1); + motionnotify(0); } void focusclient(Client *c, int lift) { - struct wlr_surface *old = seat->keyboard_state.focused_surface; - struct wlr_keyboard *kb; - int i; - - /* Raise client in stacking order if requested */ - if (c && lift) - wlr_scene_node_raise_to_top(c->scene); - - if (c && client_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; - c->isurgent = 0; - - for (i = 0; i < 4; i++) - wlr_scene_rect_set_color(c->border[i], focuscolor); - } - - /* Deactivate old client if focus is changing */ - if (old && (!c || client_surface(c) != old)) { - /* 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. */ - 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; - } else { - Client *w; - struct wlr_scene_node *node = old->data; - if ((w = node->data)) - for (i = 0; i < 4; i++) - wlr_scene_rect_set_color(w->border[i], bordercolor); - - client_activate_surface(old, 0); - } - } - - printstatus(); - wlr_idle_set_enabled(idle, seat, wl_list_empty(&idle_inhibit_mgr->inhibitors)); - - if (!c) { - /* With no client, all we have left is to clear focus */ - wlr_seat_keyboard_notify_clear_focus(seat); - return; - } - - /* TODO: Remove this? */ - /* Have a client, so focus its top-level wlr_surface */ - kb = wlr_seat_get_keyboard(seat); - wlr_seat_keyboard_notify_enter(seat, client_surface(c), - kb->keycodes, kb->num_keycodes, &kb->modifiers); - - /* Activate the new client */ - client_activate_surface(client_surface(c), 1); + struct wlr_surface *old = seat->keyboard_state.focused_surface; + struct wlr_keyboard *kb; + int i; + + /* Raise client in stacking order if requested */ + if (c && lift) + wlr_scene_node_raise_to_top(c->scene); + + if (c && client_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; + c->isurgent = 0; + + for (i = 0; i < 4; i++) + wlr_scene_rect_set_color(c->border[i], focuscolor); + } + + /* Deactivate old client if focus is changing */ + if (old && (!c || client_surface(c) != old)) { + /* 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. */ + 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; + } else { + Client *w; + struct wlr_scene_node *node = old->data; + if ((w = node->data)) + for (i = 0; i < 4; i++) + wlr_scene_rect_set_color(w->border[i], bordercolor); + + client_activate_surface(old, 0); + } + } + + printstatus(); + wlr_idle_set_enabled(idle, seat, wl_list_empty(&idle_inhibit_mgr->inhibitors)); + + if (!c) { + /* With no client, all we have left is to clear focus */ + wlr_seat_keyboard_notify_clear_focus(seat); + return; + } + + /* Have a client, so focus its top-level wlr_surface */ + kb = wlr_seat_get_keyboard(seat); + wlr_seat_keyboard_notify_enter(seat, client_surface(c), + kb->keycodes, kb->num_keycodes, &kb->modifiers); + + /* Activate the new client */ + client_activate_surface(client_surface(c), 1); } void focusmon(const Arg *arg) { - do - selmon = dirtomon(arg->i); - while (!selmon->wlr_output->enabled); - focusclient(focustop(selmon), 1); + do + selmon = dirtomon(arg->i); + while (!selmon->wlr_output->enabled); + focusclient(focustop(selmon), 1); } void focusstack(const Arg *arg) { - /* Focus the next or previous client (in tiling order) on selmon */ - Client *c, *sel = selclient(); - if (!sel || (sel->isfullscreen && lockfullscreen)) - return; - if (arg->i > 0) { - wl_list_for_each(c, &sel->link, link) { - if (&c->link == &clients) - continue; /* wrap past the sentinel node */ - if (VISIBLEON(c, selmon)) - break; /* found it */ - } - } else { - wl_list_for_each_reverse(c, &sel->link, link) { - if (&c->link == &clients) - continue; /* wrap past the sentinel node */ - if (VISIBLEON(c, selmon)) - break; /* found it */ - } - } - /* If only one client is visible on selmon, then c == sel */ - focusclient(c, 1); + /* Focus the next or previous client (in tiling order) on selmon */ + Client *c, *sel = selclient(); + if (!sel || (sel->isfullscreen && lockfullscreen)) + return; + if (arg->i > 0) { + wl_list_for_each(c, &sel->link, link) { + if (&c->link == &clients) + continue; /* wrap past the sentinel node */ + if (VISIBLEON(c, selmon)) + break; /* found it */ + } + } else { + wl_list_for_each_reverse(c, &sel->link, link) { + if (&c->link == &clients) + continue; /* wrap past the sentinel node */ + if (VISIBLEON(c, selmon)) + break; /* found it */ + } + } + /* If only one client is visible on selmon, then c == sel */ + focusclient(c, 1); } Client * focustop(Monitor *m) { - Client *c; - wl_list_for_each(c, &fstack, flink) - if (VISIBLEON(c, m)) - return c; - return NULL; + Client *c; + wl_list_for_each(c, &fstack, flink) + if (VISIBLEON(c, m)) + return c; + return NULL; } void fullscreennotify(struct wl_listener *listener, void *data) { - Client *c = wl_container_of(listener, c, fullscreen); - int fullscreen = client_wants_fullscreen(c); + Client *c = wl_container_of(listener, c, fullscreen); + int fullscreen = client_wants_fullscreen(c); - if (!c->mon) { - /* if the client is not mapped yet, let mapnotify() call setfullscreen() */ - c->isfullscreen = fullscreen; - return; - } - setfullscreen(c, fullscreen); + if (!c->mon) { + /* if the client is not mapped yet, let mapnotify() call setfullscreen() */ + c->isfullscreen = fullscreen; + return; + } + setfullscreen(c, fullscreen); } void incnmaster(const Arg *arg) { - selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); - arrange(selmon); + selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); + arrange(selmon); } void 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); - break; - case WLR_INPUT_DEVICE_POINTER: - createpointer(device); - break; - default: - /* 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. */ - /* TODO do we actually require a cursor? */ - caps = WL_SEAT_CAPABILITY_POINTER; - if (!wl_list_empty(&keyboards)) - caps |= WL_SEAT_CAPABILITY_KEYBOARD; - wlr_seat_set_capabilities(seat, caps); + /* 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); + break; + case WLR_INPUT_DEVICE_POINTER: + createpointer(device); + break; + default: + /* 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. */ + /* TODO do we actually require a cursor? */ + caps = WL_SEAT_CAPABILITY_POINTER; + if (!wl_list_empty(&keyboards)) + caps |= WL_SEAT_CAPABILITY_KEYBOARD; + wlr_seat_set_capabilities(seat, caps); } int keybinding(uint32_t mods, xkb_keycode_t keycode) { - /* - * Here we handle compositor keybindings. This is when the compositor is - * processing keys, rather than passing them on to the client for its own - * processing. - */ - int handled = 0; - Key k; - for (int i = 0; i < numkeys; i++) { - k = keys[i]; - if (CLEANMASK(mods) == CLEANMASK(k.mod) && - keycode == k.keycode && k.func) { - dscm_safe_call(DSCM_CALL_ACTION, k.func, NULL); - handled = 1; - } - } - return handled; + /* + * Here we handle compositor keybindings. This is when the compositor is + * processing keys, rather than passing them on to the client for its own + * processing. + */ + int handled = 0; + Key k; + for (int i = 0; i < numkeys; i++) { + k = keys[i]; + if (CLEANMASK(mods) == CLEANMASK(k.mod) && + keycode == k.keycode && k.func) { + dscm_safe_call(DSCM_CALL_ACTION, k.func, NULL); + handled = 1; + } + } + return handled; } void keypress(struct wl_listener *listener, void *data) { - int i; - /* 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; + /* 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; - /* Translate libinput keycode -> xkbcommon */ - uint32_t keycode = event->keycode + 8; - /* Get a list of keysyms based on the keymap for this keyboard */ - const xkb_keysym_t *syms; - int nsyms = xkb_state_key_get_syms( - kb->device->keyboard->xkb_state, keycode, &syms); + /* Translate libinput keycode -> xkbcommon */ + uint32_t keycode = event->keycode + 8; + /* Get a list of keysyms based on the keymap for this keyboard */ + const xkb_keysym_t *syms; + int nsyms = xkb_state_key_get_syms( + kb->device->keyboard->xkb_state, keycode, &syms); - int handled = 0; - uint32_t mods = wlr_keyboard_get_modifiers(kb->device->keyboard); + int handled = 0; + uint32_t mods = wlr_keyboard_get_modifiers(kb->device->keyboard); - wlr_idle_notify_activity(idle, seat); + wlr_idle_notify_activity(idle, seat); - if (!input_inhibit_mgr->active_inhibitor - && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) - handled = keybinding(mods, keycode) || handled; + if (!input_inhibit_mgr->active_inhibitor + && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) + handled = keybinding(mods, keycode) || handled; - /* On _press_ if there is no active screen locker, - * attempt to process a compositor keybinding. */ - if (!input_inhibit_mgr->active_inhibitor - && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) - for (i = 0; i < nsyms; i++) - handled = keybinding(mods, syms[i]) || handled; + /* On _press_ if there is no active screen locker, + * attempt to process a compositor keybinding. */ + if (!input_inhibit_mgr->active_inhibitor + && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) + for (i = 0; i < nsyms; i++) + handled = keybinding(mods, syms[i]) || handled; - if (!handled) { - /* Pass unhandled keycodes along to the client. */ - wlr_seat_set_keyboard(seat, kb->device); - wlr_seat_keyboard_notify_key(seat, event->time_msec, - event->keycode, event->state); - } + if (!handled) { + /* Pass unhandled keycodes along to the client. */ + wlr_seat_set_keyboard(seat, kb->device); + wlr_seat_keyboard_notify_key(seat, event->time_msec, + event->keycode, event->state); + } } void keypressmod(struct wl_listener *listener, void *data) { - /* This event is raised when a modifier key, such as shift or alt, is - * pressed. We simply communicate this to the client. */ - Keyboard *kb = wl_container_of(listener, kb, modifiers); - /* - * A seat can only have one keyboard, but this is a limitation of the - * Wayland protocol - not wlroots. We assign all connected keyboards to the - * same seat. You can swap out the underlying wlr_keyboard like this and - * wlr_seat handles this transparently. - */ - wlr_seat_set_keyboard(seat, kb->device); - /* Send modifiers to the client. */ - wlr_seat_keyboard_notify_modifiers(seat, - &kb->device->keyboard->modifiers); + /* This event is raised when a modifier key, such as shift or alt, is + * pressed. We simply communicate this to the client. */ + Keyboard *kb = wl_container_of(listener, kb, modifiers); + /* + * A seat can only have one keyboard, but this is a limitation of the + * Wayland protocol - not wlroots. We assign all connected keyboards to the + * same seat. You can swap out the underlying wlr_keyboard like this and + * wlr_seat handles this transparently. + */ + wlr_seat_set_keyboard(seat, kb->device); + /* Send modifiers to the client. */ + wlr_seat_keyboard_notify_modifiers(seat, + &kb->device->keyboard->modifiers); } void killclient(const Arg *arg) { - Client *sel = selclient(); - if (sel) - client_send_close(sel); + Client *sel = selclient(); + if (sel) + client_send_close(sel); } void maplayersurfacenotify(struct wl_listener *listener, void *data) { - LayerSurface *layersurface = wl_container_of(listener, layersurface, map); - wlr_surface_send_enter(layersurface->layer_surface->surface, - layersurface->layer_surface->output); - motionnotify(0); + LayerSurface *layersurface = wl_container_of(listener, layersurface, map); + wlr_surface_send_enter(layersurface->layer_surface->surface, + layersurface->layer_surface->output); + motionnotify(0); } void 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); - int i; + /* Called when the surface is mapped, or ready to display on-screen. */ + Client *c = wl_container_of(listener, c, map); + int i; - /* Create scene tree for this client and its border */ - c->scene = &wlr_scene_tree_create(layers[LyrTile])->node; - c->scene_surface = client_surface(c)->data = c->type == XDGShell - ? wlr_scene_xdg_surface_create(c->scene, c->surface.xdg) - : wlr_scene_subsurface_tree_create(c->scene, client_surface(c)); - c->scene_surface->data = c; + /* Create scene tree for this client and its border */ + c->scene = &wlr_scene_tree_create(layers[LyrTile])->node; + c->scene_surface = client_surface(c)->data = c->type == XDGShell + ? wlr_scene_xdg_surface_create(c->scene, c->surface.xdg) + : wlr_scene_subsurface_tree_create(c->scene, client_surface(c)); + c->scene_surface->data = c; - if (client_is_unmanaged(c)) { - client_get_geometry(c, &c->geom); - /* Floating */ - wlr_scene_node_reparent(c->scene, layers[LyrFloat]); - wlr_scene_node_set_position(c->scene, c->geom.x + borderpx, - c->geom.y + borderpx); - return; - } + if (client_is_unmanaged(c)) { + client_get_geometry(c, &c->geom); + /* Floating */ + wlr_scene_node_reparent(c->scene, layers[LyrFloat]); + wlr_scene_node_set_position(c->scene, c->geom.x + borderpx, + c->geom.y + borderpx); + return; + } - for (i = 0; i < 4; i++) { - c->border[i] = wlr_scene_rect_create(c->scene, 0, 0, bordercolor); - c->border[i]->node.data = c; - wlr_scene_rect_set_color(c->border[i], bordercolor); - wlr_scene_node_lower_to_bottom(&c->border[i]->node); - } + for (i = 0; i < 4; i++) { + c->border[i] = wlr_scene_rect_create(c->scene, 0, 0, bordercolor); + c->border[i]->node.data = c; + wlr_scene_rect_set_color(c->border[i], bordercolor); + wlr_scene_node_lower_to_bottom(&c->border[i]->node); + } - /* Initialize client geometry with room for border */ - client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT); - client_get_geometry(c, &c->geom); - c->geom.width += 2 * c->bw; - c->geom.height += 2 * c->bw; + /* Initialize client geometry with room for border */ + client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT); + client_get_geometry(c, &c->geom); + c->geom.width += 2 * c->bw; + c->geom.height += 2 * c->bw; - /* Insert this client into client lists. */ - wl_list_insert(&clients, &c->link); - wl_list_insert(&fstack, &c->flink); + /* Insert this client into client lists. */ + wl_list_insert(&clients, &c->link); + wl_list_insert(&fstack, &c->flink); - /* Set initial monitor, tags, floating status, and focus */ - applyrules(c); - printstatus(); + /* Set initial monitor, tags, floating status, and focus */ + applyrules(c); + printstatus(); - if (c->isfullscreen) - setfullscreen(c, 1); + if (c->isfullscreen) + setfullscreen(c, 1); - c->mon->un_map = 1; + c->mon->un_map = 1; } void monocle(Monitor *m) { - Client *c; + Client *c; - wl_list_for_each(c, &clients, link) { - if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) - continue; - resize(c, m->w.x, m->w.y, m->w.width, m->w.height, 0, !smartborders); - } + wl_list_for_each(c, &clients, link) { + if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) + continue; + resize(c, m->w.x, m->w.y, m->w.width, m->w.height, 0, !smartborders); + } } void motionabsolute(struct wl_listener *listener, void *data) { - /* This event is forwarded by the cursor when a pointer emits an _absolute_ - * motion event, from 0..1 on each axis. This happens, for example, when - * wlroots is running under a Wayland window rather than KMS+DRM, and you - * move the mouse over the window. You could enter the window from any edge, - * so we have to warp the mouse there. There is also some hardware which - * emits these events. */ - struct wlr_event_pointer_motion_absolute *event = data; - wlr_cursor_warp_absolute(cursor, event->device, event->x, event->y); - motionnotify(event->time_msec); + /* This event is forwarded by the cursor when a pointer emits an _absolute_ + * motion event, from 0..1 on each axis. This happens, for example, when + * wlroots is running under a Wayland window rather than KMS+DRM, and you + * move the mouse over the window. You could enter the window from any edge, + * so we have to warp the mouse there. There is also some hardware which + * emits these events. */ + struct wlr_event_pointer_motion_absolute *event = data; + wlr_cursor_warp_absolute(cursor, event->device, event->x, event->y); + motionnotify(event->time_msec); } void motionnotify(uint32_t time) { - double sx = 0, sy = 0; - Client *c = NULL; - struct wlr_surface *surface = NULL; - struct wlr_drag_icon *icon; - - /* 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); - } - - if (seat->drag && (icon = seat->drag->icon)) - wlr_scene_node_set_position(icon->data, cursor->x + icon->surface->sx, - cursor->y + icon->surface->sy); - /* If we are currently grabbing the mouse, handle and return */ - 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, 0); - 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, 0); - return; - } - - /* Find the client under the pointer and send the event along. */ - xytonode(cursor->x, cursor->y, &surface, &c, NULL, &sx, &sy); - - /* 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 && time) - wlr_xcursor_manager_set_cursor_image(cursor_mgr, "left_ptr", cursor); - - pointerfocus(c, surface, sx, sy, time); + double sx = 0, sy = 0; + Client *c = NULL; + struct wlr_surface *surface = NULL; + struct wlr_drag_icon *icon; + + /* 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); + } + + if (seat->drag && (icon = seat->drag->icon)) + wlr_scene_node_set_position(icon->data, cursor->x + icon->surface->sx, + cursor->y + icon->surface->sy); + /* If we are currently grabbing the mouse, handle and return */ + 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, 1); + 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, 1); + return; + } + + /* Find the client under the pointer and send the event along. */ + xytonode(cursor->x, cursor->y, &surface, &c, NULL, &sx, &sy); + + /* 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 && time) + wlr_xcursor_manager_set_cursor_image(cursor_mgr, "left_ptr", cursor); + + pointerfocus(c, surface, sx, sy, time); } void motionrelative(struct wl_listener *listener, void *data) { - /* This event is forwarded by the cursor when a pointer emits a _relative_ - * pointer motion event (i.e. a delta) */ - struct wlr_event_pointer_motion *event = data; - /* The cursor doesn't move unless we tell it to. The cursor automatically - * handles constraining the motion to the output layout, as well as any - * special configuration applied for the specific input device which - * generated the event. You can pass NULL for the device if you want to move - * the cursor around without any input. */ - wlr_cursor_move(cursor, event->device, event->delta_x, event->delta_y); - motionnotify(event->time_msec); + /* This event is forwarded by the cursor when a pointer emits a _relative_ + * pointer motion event (i.e. a delta) */ + struct wlr_event_pointer_motion *event = data; + /* The cursor doesn't move unless we tell it to. The cursor automatically + * handles constraining the motion to the output layout, as well as any + * special configuration applied for the specific input device which + * generated the event. You can pass NULL for the device if you want to move + * the cursor around without any input. */ + wlr_cursor_move(cursor, event->device, event->delta_x, event->delta_y); + motionnotify(event->time_msec); } void moveresize(const Arg *arg) { - if (cursor_mode != CurNormal) - return; - xytonode(cursor->x, cursor->y, NULL, &grabc, NULL, NULL, NULL); - if (!grabc || client_is_unmanaged(grabc)) - return; - - /* Float the window and tell motionnotify to grab it */ - setfloating(grabc, 1); - switch (cursor_mode = arg->ui) { - case CurMove: - grabcx = cursor->x - grabc->geom.x; - grabcy = cursor->y - grabc->geom.y; - wlr_xcursor_manager_set_cursor_image(cursor_mgr, "fleur", cursor); - break; - case CurResize: - /* Doesn't work for X11 output - the next absolute motion event - * returns the cursor to where it started */ - wlr_cursor_warp_closest(cursor, NULL, - grabc->geom.x + grabc->geom.width, - grabc->geom.y + grabc->geom.height); - wlr_xcursor_manager_set_cursor_image(cursor_mgr, - "bottom_right_corner", cursor); - break; - } + if (cursor_mode != CurNormal) + return; + xytonode(cursor->x, cursor->y, NULL, &grabc, NULL, NULL, NULL); + if (!grabc || client_is_unmanaged(grabc)) + return; + + /* Float the window and tell motionnotify to grab it */ + setfloating(grabc, 1); + switch (cursor_mode = arg->ui) { + case CurMove: + grabcx = cursor->x - grabc->geom.x; + grabcy = cursor->y - grabc->geom.y; + wlr_xcursor_manager_set_cursor_image(cursor_mgr, "fleur", cursor); + break; + case CurResize: + /* Doesn't work for X11 output - the next absolute motion event + * returns the cursor to where it started */ + wlr_cursor_warp_closest(cursor, NULL, + grabc->geom.x + grabc->geom.width, + grabc->geom.y + grabc->geom.height); + wlr_xcursor_manager_set_cursor_image(cursor_mgr, + "bottom_right_corner", cursor); + break; + } } void outputmgrapply(struct wl_listener *listener, void *data) { - struct wlr_output_configuration_v1 *config = data; - outputmgrapplyortest(config, 0); + struct wlr_output_configuration_v1 *config = data; + outputmgrapplyortest(config, 0); } void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test) { - /* - * Called when a client such as wlr-randr requests a change in output - * configuration. This is only one way that the layout can be changed, - * so any Monitor information should be updated by updatemons() after an - * output_layout.change event, not here. - */ - struct wlr_output_configuration_head_v1 *config_head; - int ok = 1; - - 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 (!(ok = wlr_output_test(wlr_output))) - break; - } - wl_list_for_each(config_head, &config->heads, link) { - if (ok && !test) - wlr_output_commit(config_head->state.output); - else - wlr_output_rollback(config_head->state.output); - } - if (ok) - wlr_output_configuration_v1_send_succeeded(config); - else - wlr_output_configuration_v1_send_failed(config); - wlr_output_configuration_v1_destroy(config); + /* + * Called when a client such as wlr-randr requests a change in output + * configuration. This is only one way that the layout can be changed, + * so any Monitor information should be updated by updatemons() after an + * output_layout.change event, not here. + */ + struct wlr_output_configuration_head_v1 *config_head; + int ok = 1; + + 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 (!(ok = wlr_output_test(wlr_output))) + break; + } + wl_list_for_each(config_head, &config->heads, link) { + if (ok && !test) + wlr_output_commit(config_head->state.output); + else + wlr_output_rollback(config_head->state.output); + } + if (ok) + wlr_output_configuration_v1_send_succeeded(config); + 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, 1); + struct wlr_output_configuration_v1 *config = data; + outputmgrapplyortest(config, 1); } void pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, - uint32_t time) + uint32_t time) { - struct timespec now; - int internal_call = !time; + struct timespec now; + int internal_call = !time; - if (sloppyfocus && !internal_call && c && !client_is_unmanaged(c)) - focusclient(c, 0); + if (sloppyfocus && !internal_call && c && !client_is_unmanaged(c)) + focusclient(c, 0); - /* If surface is NULL, clear pointer focus */ - if (!surface) { - wlr_seat_pointer_notify_clear_focus(seat); - return; - } + /* If surface is NULL, clear pointer focus */ + if (!surface) { + wlr_seat_pointer_notify_clear_focus(seat); + return; + } - if (internal_call) { - clock_gettime(CLOCK_MONOTONIC, &now); - time = now.tv_sec * 1000 + now.tv_nsec / 1000000; - } + if (internal_call) { + clock_gettime(CLOCK_MONOTONIC, &now); + time = now.tv_sec * 1000 + now.tv_nsec / 1000000; + } - /* Let the client know that the mouse cursor has entered one - * of its surfaces, and make keyboard focus follow if desired. - * wlroots makes this a no-op if surface is already focused */ - wlr_seat_pointer_notify_enter(seat, surface, sx, sy); - wlr_seat_pointer_notify_motion(seat, time, sx, sy); + /* Let the client know that the mouse cursor has entered one + * of its surfaces, and make keyboard focus follow if desired. + * wlroots makes this a no-op if surface is already focused */ + wlr_seat_pointer_notify_enter(seat, surface, sx, sy); + wlr_seat_pointer_notify_motion(seat, time, sx, sy); } void printstatus(void) { - Monitor *m = NULL; - Client *c; - unsigned int occ, urg, sel; - - wl_list_for_each(m, &mons, link) { - occ = urg = 0; - wl_list_for_each(c, &clients, link) { - if (c->mon != m) - continue; - occ |= c->tags; - if (c->isurgent) - urg |= c->tags; - } - if ((c = focustop(m))) { - printf("%s title %s\n", m->wlr_output->name, client_get_title(c)); - printf("%s fullscreen %u\n", m->wlr_output->name, c->isfullscreen); - printf("%s floating %u\n", m->wlr_output->name, c->isfloating); - sel = c->tags; - } else { - printf("%s title \n", m->wlr_output->name); - printf("%s fullscreen \n", m->wlr_output->name); - printf("%s floating \n", m->wlr_output->name); - sel = 0; - } - - printf("%s selmon %u\n", m->wlr_output->name, m == selmon); - 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); - dscm_printstatus(m); - } - fflush(stdout); + Monitor *m = NULL; + Client *c; + unsigned int occ, urg, sel; + + wl_list_for_each(m, &mons, link) { + occ = urg = 0; + wl_list_for_each(c, &clients, link) { + if (c->mon != m) + continue; + occ |= c->tags; + if (c->isurgent) + urg |= c->tags; + } + if ((c = focustop(m))) { + printf("%s title %s\n", m->wlr_output->name, client_get_title(c)); + printf("%s fullscreen %u\n", m->wlr_output->name, c->isfullscreen); + printf("%s floating %u\n", m->wlr_output->name, c->isfloating); + sel = c->tags; + } else { + printf("%s title \n", m->wlr_output->name); + printf("%s fullscreen \n", m->wlr_output->name); + printf("%s floating \n", m->wlr_output->name); + sel = 0; + } + + printf("%s selmon %u\n", m->wlr_output->name, m == selmon); + 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); + dscm_printstatus(m); + } + fflush(stdout); } void quit(const Arg *arg) { - wl_display_terminate(dpy); + wl_display_terminate(dpy); } void quitsignal(int signo) { - quit(NULL); + quit(NULL); } void rendermon(struct wl_listener *listener, void *data) { - /* 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); - Client *c; - int skip = 0; - struct timespec now; - - clock_gettime(CLOCK_MONOTONIC, &now); - - /* Render if no XDG clients have an outstanding resize and are visible on - * this monitor. */ - /* Checking m->un_map for every client is not optimal but works */ - wl_list_for_each(c, &clients, link) { - if ((c->resize && m->un_map) || (c->type == XDGShell - && (c->surface.xdg->pending.geometry.width != - c->surface.xdg->current.geometry.width - || c->surface.xdg->pending.geometry.height != - c->surface.xdg->current.geometry.height))) { - /* Lie */ - wlr_surface_send_frame_done(client_surface(c), &now); - skip = 1; - } - } - if (!skip && !wlr_scene_output_commit(m->scene_output)) - return; - /* Let clients know a frame has been rendered */ - wlr_scene_output_send_frame_done(m->scene_output, &now); - m->un_map = 0; + /* 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); + Client *c; + int skip = 0; + struct timespec now; + + clock_gettime(CLOCK_MONOTONIC, &now); + + /* Render if no XDG clients have an outstanding resize and are visible on + * this monitor. */ + /* Checking m->un_map for every client is not optimal but works */ + wl_list_for_each(c, &clients, link) { + if ((c->resize && m->un_map) || (c->type == XDGShell + && (c->surface.xdg->pending.geometry.width != + c->surface.xdg->current.geometry.width + || c->surface.xdg->pending.geometry.height != + c->surface.xdg->current.geometry.height))) { + /* Lie */ + wlr_surface_send_frame_done(client_surface(c), &now); + skip = 1; + } + } + if (!skip && !wlr_scene_output_commit(m->scene_output)) + return; + /* Let clients know a frame has been rendered */ + wlr_scene_output_send_frame_done(m->scene_output, &now); + m->un_map = 0; } void requeststartdrag(struct wl_listener *listener, void *data) { - struct wlr_seat_request_start_drag_event *event = data; + struct wlr_seat_request_start_drag_event *event = data; - if (wlr_seat_validate_pointer_grab_serial(seat, event->origin, - event->serial)) - wlr_seat_start_pointer_drag(seat, event->drag, event->serial); - else - wlr_data_source_destroy(event->drag->source); + if (wlr_seat_validate_pointer_grab_serial(seat, event->origin, + event->serial)) + wlr_seat_start_pointer_drag(seat, event->drag, event->serial); + else + wlr_data_source_destroy(event->drag->source); } void setupsignals() { - /* Block real-time signals so that they can be - * used as custom user signals. */ - struct sigaction sa; - sa.sa_handler = SIG_IGN; - for (int i = SIGRTMIN; i <= SIGRTMAX; i++) - sigaction(i, &sa, NULL); + /* Block real-time signals so that they can be + * used as custom user signals. */ + struct sigaction sa; + sa.sa_handler = SIG_IGN; + for (int i = SIGRTMIN; i <= SIGRTMAX; i++) + sigaction(i, &sa, NULL); } void resize(Client *c, int x, int y, int w, int h, int interact, int draw_borders) { - int min_width = 0, min_height = 0; - struct wlr_box *bbox = interact ? &sgeom : &c->mon->w; - client_min_size(c, &min_width, &min_height); - c->geom.x = x; - c->geom.y = y; - c->geom.width = MAX(min_width + 2 * c->bw, w); - c->geom.height = MAX(min_height + 2 * c->bw, h); - c->bw = (1 - !draw_borders) * borderpx; - applybounds(c, bbox); - - /* Update scene-graph, including borders */ - wlr_scene_node_set_position(c->scene, c->geom.x, c->geom.y); - wlr_scene_node_set_position(c->scene_surface, c->bw, c->bw); - wlr_scene_rect_set_size(c->border[0], c->geom.width, c->bw); - wlr_scene_rect_set_size(c->border[1], c->geom.width, c->bw); - wlr_scene_rect_set_size(c->border[2], c->bw, c->geom.height - 2 * c->bw); - wlr_scene_rect_set_size(c->border[3], c->bw, c->geom.height - 2 * c->bw); - wlr_scene_node_set_position(&c->border[1]->node, 0, c->geom.height - c->bw); - wlr_scene_node_set_position(&c->border[2]->node, 0, c->bw); - wlr_scene_node_set_position(&c->border[3]->node, c->geom.width - c->bw, c->bw); - - /* wlroots makes this a no-op if size hasn't changed */ - c->resize = client_set_size(c, c->geom.width - 2 * c->bw, - c->geom.height - 2 * c->bw); + int min_width = 0, min_height = 0; + struct wlr_box *bbox = interact ? &sgeom : &c->mon->w; + client_min_size(c, &min_width, &min_height); + c->geom.x = x; + c->geom.y = y; + c->geom.width = MAX(min_width + 2 * c->bw, w); + c->geom.height = MAX(min_height + 2 * c->bw, h); + c->bw = (1 - !draw_borders) * borderpx; + applybounds(c, bbox); + + /* Update scene-graph, including borders */ + wlr_scene_node_set_position(c->scene, c->geom.x, c->geom.y); + wlr_scene_node_set_position(c->scene_surface, c->bw, c->bw); + wlr_scene_rect_set_size(c->border[0], c->geom.width, c->bw); + wlr_scene_rect_set_size(c->border[1], c->geom.width, c->bw); + wlr_scene_rect_set_size(c->border[2], c->bw, c->geom.height - 2 * c->bw); + wlr_scene_rect_set_size(c->border[3], c->bw, c->geom.height - 2 * c->bw); + wlr_scene_node_set_position(&c->border[1]->node, 0, c->geom.height - c->bw); + wlr_scene_node_set_position(&c->border[2]->node, 0, c->bw); + wlr_scene_node_set_position(&c->border[3]->node, c->geom.width - c->bw, c->bw); + + /* wlroots makes this a no-op if size hasn't changed */ + c->resize = client_set_size(c, c->geom.width - 2 * c->bw, + c->geom.height - 2 * c->bw); } 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) - die("startup: display_add_socket_auto"); - setenv("WAYLAND_DISPLAY", socket, 1); - - /* Now that the socket exists, run the startup command */ - if (startup_cmd) { - int piperw[2]; - if (pipe(piperw) < 0) - die("startup: pipe:"); - if ((startup_pid = fork()) < 0) - die("startup: fork:"); - if (startup_pid == 0) { - dup2(piperw[0], STDIN_FILENO); - close(piperw[0]); - close(piperw[1]); - execl("/bin/sh", "/bin/sh", "-c", startup_cmd, NULL); - die("startup: execl:"); - } - dup2(piperw[1], STDOUT_FILENO); - close(piperw[1]); - close(piperw[0]); - } - /* If nobody is reading the status output, don't terminate */ - signal(SIGPIPE, SIG_IGN); - printstatus(); - - /* Start the backend. This will enumerate outputs and inputs, become the DRM - * master, etc */ - if (!wlr_backend_start(backend)) - die("startup: backend_start"); - - /* 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 - * frame events at the refresh rate, and so on. */ - wl_display_run(dpy); - - if (startup_cmd) { - kill(startup_pid, SIGTERM); - waitpid(startup_pid, NULL, 0); - } + pid_t startup_pid = -1; + + /* Add a Unix socket to the Wayland display. */ + const char *socket = wl_display_add_socket_auto(dpy); + if (!socket) + die("startup: display_add_socket_auto"); + setenv("WAYLAND_DISPLAY", socket, 1); + + /* Now that the socket exists, run the startup command */ + if (startup_cmd) { + int piperw[2]; + if (pipe(piperw) < 0) + die("startup: pipe:"); + if ((startup_pid = fork()) < 0) + die("startup: fork:"); + if (startup_pid == 0) { + dup2(piperw[0], STDIN_FILENO); + close(piperw[0]); + close(piperw[1]); + execl("/bin/sh", "/bin/sh", "-c", startup_cmd, NULL); + die("startup: execl:"); + } + dup2(piperw[1], STDOUT_FILENO); + close(piperw[1]); + close(piperw[0]); + } + /* If nobody is reading the status output, don't terminate */ + signal(SIGPIPE, SIG_IGN); + printstatus(); + + /* Start the backend. This will enumerate outputs and inputs, become the DRM + * master, etc */ + if (!wlr_backend_start(backend)) + die("startup: backend_start"); + + /* 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 + * frame events at the refresh rate, and so on. */ + wl_display_run(dpy); + + if (startup_cmd) { + kill(startup_pid, SIGTERM); + waitpid(startup_pid, NULL, 0); + } } Client * selclient(void) { - Client *c = wl_container_of(fstack.next, c, flink); - if (wl_list_empty(&fstack) || !VISIBLEON(c, selmon)) - return NULL; - return c; + Client *c = wl_container_of(fstack.next, c, flink); + if (wl_list_empty(&fstack) || !VISIBLEON(c, selmon)) + return NULL; + return c; } void 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 */ - /* 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 - * actually has pointer focus first. If so, we can tell the cursor to - * use the provided surface as the cursor image. It will set the - * hardware cursor on the output that it's currently on and continue to - * do so as the cursor moves between outputs. */ - if (event->seat_client == seat->pointer_state.focused_client) - wlr_cursor_set_surface(cursor, event->surface, - event->hotspot_x, event->hotspot_y); + /* 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 */ + /* 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 + * actually has pointer focus first. If so, we can tell the cursor to + * use the provided surface as the cursor image. It will set the + * hardware cursor on the output that it's currently on and continue to + * do so as the cursor moves between outputs. */ + if (event->seat_client == seat->pointer_state.focused_client) + wlr_cursor_set_surface(cursor, event->surface, + event->hotspot_x, event->hotspot_y); } void setfloating(Client *c, int floating) { - c->isfloating = floating; - wlr_scene_node_reparent(c->scene, layers[c->isfloating ? LyrFloat : LyrTile]); - arrange(c->mon); - printstatus(); + c->isfloating = floating; + wlr_scene_node_reparent(c->scene, layers[c->isfloating ? LyrFloat : LyrTile]); + arrange(c->mon); + printstatus(); } void setgaps(int oh, int ov, int ih, int iv) { - if (oh < 0) oh = 0; - if (ov < 0) ov = 0; - if (ih < 0) ih = 0; - if (iv < 0) iv = 0; + if (oh < 0) oh = 0; + if (ov < 0) ov = 0; + if (ih < 0) ih = 0; + if (iv < 0) iv = 0; - selmon->gappoh = oh; - selmon->gappov = ov; - selmon->gappih = ih; - selmon->gappiv = iv; - arrange(selmon); + selmon->gappoh = oh; + selmon->gappov = ov; + selmon->gappih = ih; + selmon->gappiv = iv; + arrange(selmon); } void togglegaps(const Arg *arg) { - enablegaps = !enablegaps; - arrange(selmon); + enablegaps = !enablegaps; + arrange(selmon); } void defaultgaps(const Arg *arg) { - setgaps(gappoh, gappov, gappih, gappiv); + setgaps(gappoh, gappov, gappih, gappiv); } void incrgaps(const Arg *arg) { - setgaps( - selmon->gappoh + arg->i, - selmon->gappov + arg->i, - selmon->gappih + arg->i, - selmon->gappiv + arg->i - ); + setgaps( + selmon->gappoh + arg->i, + selmon->gappov + arg->i, + selmon->gappih + arg->i, + selmon->gappiv + arg->i + ); } void incrigaps(const Arg *arg) { - setgaps( - selmon->gappoh, - selmon->gappov, - selmon->gappih + arg->i, - selmon->gappiv + arg->i - ); + setgaps( + selmon->gappoh, + selmon->gappov, + selmon->gappih + arg->i, + selmon->gappiv + arg->i + ); } void incrogaps(const Arg *arg) { - setgaps( - selmon->gappoh + arg->i, - selmon->gappov + arg->i, - selmon->gappih, - selmon->gappiv - ); + setgaps( + selmon->gappoh + arg->i, + selmon->gappov + arg->i, + selmon->gappih, + selmon->gappiv + ); } void incrohgaps(const Arg *arg) { - setgaps( - selmon->gappoh + arg->i, - selmon->gappov, - selmon->gappih, - selmon->gappiv - ); + setgaps( + selmon->gappoh + arg->i, + selmon->gappov, + selmon->gappih, + selmon->gappiv + ); } void incrovgaps(const Arg *arg) { - setgaps( - selmon->gappoh, - selmon->gappov + arg->i, - selmon->gappih, - selmon->gappiv - ); + setgaps( + selmon->gappoh, + selmon->gappov + arg->i, + selmon->gappih, + selmon->gappiv + ); } void incrihgaps(const Arg *arg) { - setgaps( - selmon->gappoh, - selmon->gappov, - selmon->gappih + arg->i, - selmon->gappiv - ); + setgaps( + selmon->gappoh, + selmon->gappov, + selmon->gappih + arg->i, + selmon->gappiv + ); } void incrivgaps(const Arg *arg) { - setgaps( - selmon->gappoh, - selmon->gappov, - selmon->gappih, - selmon->gappiv + arg->i - ); + setgaps( + selmon->gappoh, + selmon->gappov, + selmon->gappih, + selmon->gappiv + arg->i + ); } void setfullscreen(Client *c, int fullscreen) { - c->isfullscreen = fullscreen; - c->bw = fullscreen ? 0 : borderpx; - client_set_fullscreen(c, fullscreen); + c->isfullscreen = fullscreen; + c->bw = fullscreen ? 0 : borderpx; + client_set_fullscreen(c, fullscreen); - if (fullscreen) { - c->prev = c->geom; - c->prevalpha = c->alpha; - c->alpha = 1; - resize(c, c->mon->m.x, c->mon->m.y, c->mon->m.width, c->mon->m.height, 0, !smartborders); - } else { - c->alpha = c->prevalpha; - /* restore previous size instead of arrange for floating windows since - * client positions are set by the user and cannot be recalculated */ - resize(c, c->prev.x, c->prev.y, c->prev.width, c->prev.height, 0, 1); - } - arrange(c->mon); - printstatus(); + if (fullscreen) { + c->prev = c->geom; + c->prevalpha = c->alpha; + c->alpha = 1; + resize(c, c->mon->m.x, c->mon->m.y, c->mon->m.width, c->mon->m.height, 0, !smartborders); + } else { + c->alpha = c->prevalpha; + /* restore previous size instead of arrange for floating windows since + * client positions are set by the user and cannot be recalculated */ + resize(c, c->prev.x, c->prev.y, c->prev.width, c->prev.height, 0, 1); + } + arrange(c->mon); + printstatus(); } void setlayout(const Arg *arg) { - if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) - selmon->sellt ^= 1; - if (arg && arg->v) - selmon->lt[selmon->sellt] = (Layout *)arg->v; - if (!selmon->lt[selmon->sellt]->arrange) { - /* floating layout, draw borders around all clients */ - Client *c; - wl_list_for_each(c, &clients, link) { - if (c->bw) - continue; - resize(c, c->geom.x, c->geom.y, c->geom.width, c->geom.height, 0, 1); - } - } - /* TODO change layout symbol? */ - arrange(selmon); - printstatus(); + if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) + selmon->sellt ^= 1; + if (arg && arg->v) + selmon->lt[selmon->sellt] = (Layout *)arg->v; + if (!selmon->lt[selmon->sellt]->arrange) { + /* floating layout, draw borders around all clients */ + Client *c; + wl_list_for_each(c, &clients, link) { + if (c->bw) + continue; + resize(c, c->geom.x, c->geom.y, c->geom.width, c->geom.height, 0, 1); + } + } + /* TODO change layout symbol? */ + arrange(selmon); + printstatus(); } /* arg > 1.0 will set mfact absolutely */ void setmfact(const Arg *arg) { - float f; + float f; - if (!arg || !selmon->lt[selmon->sellt]->arrange) - return; - f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; - if (f < 0.1 || f > 0.9) - return; - selmon->mfact = f; - arrange(selmon); + if (!arg || !selmon->lt[selmon->sellt]->arrange) + return; + f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; + if (f < 0.1 || f > 0.9) + return; + selmon->mfact = f; + arrange(selmon); } void setmon(Client *c, Monitor *m, unsigned int newtags) { - Monitor *oldmon = c->mon; + Monitor *oldmon = c->mon; - if (oldmon == m) - return; - c->mon = m; + if (oldmon == m) + return; + c->mon = m; - /* TODO leave/enter is not optimal but works */ - if (oldmon) { - wlr_surface_send_leave(client_surface(c), oldmon->wlr_output); - arrange(oldmon); - } - if (m) { - /* Make sure window actually overlaps with the monitor */ - resize(c, c->geom.x, c->geom.y, c->geom.width, c->geom.height, 0, 1); - wlr_surface_send_enter(client_surface(c), m->wlr_output); - c->tags = newtags ? newtags : m->tagset[m->seltags]; /* assign tags of target monitor */ - arrange(m); - } - focusclient(focustop(selmon), 1); + /* TODO leave/enter is not optimal but works */ + if (oldmon) { + wlr_surface_send_leave(client_surface(c), oldmon->wlr_output); + arrange(oldmon); + } + if (m) { + /* Make sure window actually overlaps with the monitor */ + resize(c, c->geom.x, c->geom.y, c->geom.width, c->geom.height, 0, 1); + wlr_surface_send_enter(client_surface(c), m->wlr_output); + c->tags = newtags ? newtags : m->tagset[m->seltags]; /* assign tags of target monitor */ + arrange(m); + } + focusclient(focustop(selmon), 1); } void setpsel(struct wl_listener *listener, void *data) { - /* This event is raised by the seat when a client wants to set the selection, - * usually when the user copies something. wlroots allows compositors to - * ignore such requests if they so choose, but in dwl we always honor - */ - struct wlr_seat_request_set_primary_selection_event *event = data; - wlr_seat_set_primary_selection(seat, event->source, event->serial); + /* This event is raised by the seat when a client wants to set the selection, + * usually when the user copies something. wlroots allows compositors to + * ignore such requests if they so choose, but in dwl we always honor + */ + struct wlr_seat_request_set_primary_selection_event *event = data; + wlr_seat_set_primary_selection(seat, event->source, event->serial); } void setsel(struct wl_listener *listener, void *data) { - /* This event is raised by the seat when a client wants to set the selection, - * usually when the user copies something. wlroots allows compositors to - * ignore such requests if they so choose, but in dwl we always honor - */ - struct wlr_seat_request_set_selection_event *event = data; - wlr_seat_set_selection(seat, event->source, event->serial); + /* This event is raised by the seat when a client wants to set the selection, + * usually when the user copies something. wlroots allows compositors to + * ignore such requests if they so choose, but in dwl we always honor + */ + struct wlr_seat_request_set_selection_event *event = data; + wlr_seat_set_selection(seat, event->source, event->serial); } int reloadconfig(int signal, void *data) { - Client *c; - Monitor *m; - char *config_file = (char*)data; + Client *c; + Monitor *m; + char *config_file = (char*)data; - dscm_config_parse(config_file); + dscm_config_parse(config_file); - /* Redraw clients */ - wl_list_for_each(c, &clients, link) { - if (c->bw > 0) - c->bw = borderpx; - resize(c, c->geom.x, c->geom.y, c->geom.width, c->geom.height, 0, c->bw); - } + /* Redraw clients */ + wl_list_for_each(c, &clients, link) { + if (c->bw > 0) + c->bw = borderpx; + resize(c, c->geom.x, c->geom.y, c->geom.width, c->geom.height, 0, c->bw); + } - /* Rearrange clients on all monitors */ - wl_list_for_each(m, &mons, link) - arrange(m); + /* Rearrange clients on all monitors */ + wl_list_for_each(m, &mons, link) + arrange(m); - /* Send events to observing clients, notifying of possible changes */ - dscm_sendevents(); + /* Send events to observing clients, notifying of possible changes */ + dscm_sendevents(); - return 0; + return 0; } void setup(char *config_file) { - /* The Wayland display is managed by libwayland. It handles accepting - * clients from the Unix socket, manging Wayland globals, and so on. */ - dpy = wl_display_create(); - - /* Set up signal handlers */ - sigchld(0); - signal(SIGINT, quitsignal); - signal(SIGTERM, quitsignal); - - /* Block user signals so that they can be handled */ - setupsignals(); - - /* The backend is a wlroots feature which abstracts the underlying input and - * output hardware. The autocreate option will choose the most suitable - * backend based on the current environment, such as opening an X11 window - * if an X11 server is running. The NULL argument here optionally allows you - * to pass in a custom renderer if wlr_renderer doesn't meet your needs. The - * backend uses the renderer, for example, to fall back to software cursors - * if the backend does not support hardware cursors (some older GPUs - * don't). */ - if (!(backend = wlr_backend_autocreate(dpy))) - die("couldn't create backend"); - - /* Initialize the scene graph used to lay out windows */ - scene = wlr_scene_create(); - layers[LyrBg] = &wlr_scene_tree_create(&scene->node)->node; - layers[LyrBottom] = &wlr_scene_tree_create(&scene->node)->node; - layers[LyrTile] = &wlr_scene_tree_create(&scene->node)->node; - layers[LyrFloat] = &wlr_scene_tree_create(&scene->node)->node; - layers[LyrTop] = &wlr_scene_tree_create(&scene->node)->node; - layers[LyrOverlay] = &wlr_scene_tree_create(&scene->node)->node; - layers[LyrNoFocus] = &wlr_scene_tree_create(&scene->node)->node; - - /* Create a renderer with the default implementation */ - if (!(drw = wlr_renderer_autocreate(backend))) - die("couldn't create renderer"); - wlr_renderer_init_wl_display(drw, dpy); - - /* Create a default allocator */ - if (!(alloc = wlr_allocator_autocreate(backend, drw))) - die("couldn't create allocator"); - - /* This creates some hands-off wlroots interfaces. The compositor is - * necessary for clients to allocate surfaces and the data device manager - * handles the clipboard. Each of these wlroots interfaces has room for you - * 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); - 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); - 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(); - wl_signal_add(&output_layout->events.change, &layout_change); - wlr_xdg_output_manager_v1_create(dpy, output_layout); - - /* Initializes the interface used to implement urgency hints */ - activation = wlr_xdg_activation_v1_create(dpy); - wl_signal_add(&activation->events.request_activate, &request_activate); - - /* Set up our client lists and the xdg-shell. The xdg-shell is a - * Wayland protocol which is used for application windows. For more - * detail on shells, refer to the article: - * - * https://drewdevault.com/2018/07/29/Wayland-shells.html - */ - wl_list_init(&clients); - wl_list_init(&fstack); - - /* Configure a listener to be notified when new outputs are available on the - * backend. */ - wl_list_init(&mons); - wl_signal_add(&backend->events.new_output, &new_output); - - idle_inhibit_mgr = wlr_idle_inhibit_v1_create(dpy); - wl_signal_add(&idle_inhibit_mgr->events.new_inhibitor, &idle_inhibitor_create); - - layer_shell = wlr_layer_shell_v1_create(dpy); - wl_signal_add(&layer_shell->events.new_surface, &new_layer_shell_surface); - - /* TODO: Remove? */ - idle = wlr_idle_create(dpy); - - input_inhibit_mgr = wlr_input_inhibit_manager_create(dpy); - - /* 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); - - layer_shell = wlr_layer_shell_v1_create(dpy); - wl_signal_add(&layer_shell->events.new_surface, &new_layer_shell_surface); - - xdg_shell = wlr_xdg_shell_create(dpy); - wl_signal_add(&xdg_shell->events.new_surface, &new_xdg_surface); - - input_inhibit_mgr = wlr_input_inhibit_manager_create(dpy); - - /* - * Configures a seat, which is a single "seat" at which a user sits and - * operates the computer. This conceptually includes up to one keyboard, - * pointer, touch, and drawing tablet device. We also rig up a listener to - * let us know when new input devices are available on the backend. - */ - 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); - wl_signal_add(&seat->events.request_set_selection, &request_set_sel); - wl_signal_add(&seat->events.request_set_primary_selection, &request_set_psel); - wl_signal_add(&seat->events.request_start_drag, &request_start_drag); - wl_signal_add(&seat->events.start_drag, &start_drag); - - /* - * Creates a cursor, which is a wlroots utility for tracking the cursor - * image shown on screen. - */ - cursor = wlr_cursor_create(); - wlr_cursor_attach_output_layout(cursor, output_layout); - - /* Creates an xcursor manager, another wlroots utility which loads up - * Xcursor themes to source cursor images from and makes sure that cursor - * images are available at all scale factors on the screen (necessary for - * HiDPI support). Scaled cursors will be loaded with each output. */ - cursor_mgr = wlr_xcursor_manager_create(NULL, 24); - - /* - * wlr_cursor *only* displays an image on screen. It does not move around - * when the pointer moves. However, we can attach input devices to it, and - * it will generate aggregate events for all of them. In these events, we - * can choose how we want to process them, forwarding them to clients and - * moving the cursor around. More detail on this process is described in my - * input handling blog post: - * - * https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html - * - * And more comments are sprinkled throughout the notify functions above. - */ - wl_signal_add(&cursor->events.motion, &cursor_motion); - wl_signal_add(&cursor->events.motion_absolute, &cursor_motion_absolute); - wl_signal_add(&cursor->events.button, &cursor_button); - wl_signal_add(&cursor->events.axis, &cursor_axis); - wl_signal_add(&cursor->events.frame, &cursor_frame); - - /* - * Configures a seat, which is a single "seat" at which a user sits and - * operates the computer. This conceptually includes up to one keyboard, - * pointer, touch, and drawing tablet device. We also rig up a listener to - * let us know when new input devices are available on the backend. - */ - 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); - wl_signal_add(&seat->events.request_set_selection, - &request_set_sel); - wl_signal_add(&seat->events.request_set_primary_selection, - &request_set_psel); - wl_signal_add(&seat->events.request_start_drag, &request_start_drag); - wl_signal_add(&seat->events.start_drag, &start_drag); - - 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); - - struct wl_event_loop *loop = wl_display_get_event_loop(dpy); - - /* Add handlers for user signals */ - /* TODO: Add config option for adding custom handlers to signal. */ - wl_event_loop_add_signal(loop, SIGRTMIN, &reloadconfig, config_file); - - presentation = wlr_presentation_create(dpy, backend); - wl_global_create(dpy, &dscm_v1_interface, 1, NULL, dscm_bind); - - wlr_scene_set_presentation(scene, wlr_presentation_create(dpy, backend)); + /* The Wayland display is managed by libwayland. It handles accepting + * clients from the Unix socket, manging Wayland globals, and so on. */ + dpy = wl_display_create(); + + /* Set up signal handlers */ + sigchld(0); + signal(SIGINT, quitsignal); + signal(SIGTERM, quitsignal); + + /* Block user signals so that they can be handled */ + setupsignals(); + + /* The backend is a wlroots feature which abstracts the underlying input and + * output hardware. The autocreate option will choose the most suitable + * backend based on the current environment, such as opening an X11 window + * if an X11 server is running. The NULL argument here optionally allows you + * to pass in a custom renderer if wlr_renderer doesn't meet your needs. The + * backend uses the renderer, for example, to fall back to software cursors + * if the backend does not support hardware cursors (some older GPUs + * don't). */ + if (!(backend = wlr_backend_autocreate(dpy))) + die("couldn't create backend"); + + /* Initialize the scene graph used to lay out windows */ + scene = wlr_scene_create(); + layers[LyrBg] = &wlr_scene_tree_create(&scene->node)->node; + layers[LyrBottom] = &wlr_scene_tree_create(&scene->node)->node; + layers[LyrTile] = &wlr_scene_tree_create(&scene->node)->node; + layers[LyrFloat] = &wlr_scene_tree_create(&scene->node)->node; + layers[LyrTop] = &wlr_scene_tree_create(&scene->node)->node; + layers[LyrOverlay] = &wlr_scene_tree_create(&scene->node)->node; + layers[LyrNoFocus] = &wlr_scene_tree_create(&scene->node)->node; + + /* Create a renderer with the default implementation */ + if (!(drw = wlr_renderer_autocreate(backend))) + die("couldn't create renderer"); + wlr_renderer_init_wl_display(drw, dpy); + + /* Create a default allocator */ + if (!(alloc = wlr_allocator_autocreate(backend, drw))) + die("couldn't create allocator"); + + /* This creates some hands-off wlroots interfaces. The compositor is + * necessary for clients to allocate surfaces and the data device manager + * handles the clipboard. Each of these wlroots interfaces has room for you + * 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); + 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); + 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(); + wl_signal_add(&output_layout->events.change, &layout_change); + wlr_xdg_output_manager_v1_create(dpy, output_layout); + + /* Configure a listener to be notified when new outputs are available on the + * backend. */ + wl_list_init(&mons); + wl_signal_add(&backend->events.new_output, &new_output); + + /* Initializes the interface used to implement urgency hints */ + activation = wlr_xdg_activation_v1_create(dpy); + wl_signal_add(&activation->events.request_activate, &request_activate); + + /* Set up our client lists and the xdg-shell. The xdg-shell is a + * Wayland protocol which is used for application windows. For more + * detail on shells, refer to the article: + * + * https://drewdevault.com/2018/07/29/Wayland-shells.html + */ + wl_list_init(&clients); + wl_list_init(&fstack); + + idle = wlr_idle_create(dpy); + + idle_inhibit_mgr = wlr_idle_inhibit_v1_create(dpy); + wl_signal_add(&idle_inhibit_mgr->events.new_inhibitor, &idle_inhibitor_create); + + layer_shell = wlr_layer_shell_v1_create(dpy); + wl_signal_add(&layer_shell->events.new_surface, &new_layer_shell_surface); + + xdg_shell = wlr_xdg_shell_create(dpy); + wl_signal_add(&xdg_shell->events.new_surface, &new_xdg_surface); + + input_inhibit_mgr = wlr_input_inhibit_manager_create(dpy); + + /* 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 + * image shown on screen. + */ + cursor = wlr_cursor_create(); + wlr_cursor_attach_output_layout(cursor, output_layout); + + /* Creates an xcursor manager, another wlroots utility which loads up + * Xcursor themes to source cursor images from and makes sure that cursor + * images are available at all scale factors on the screen (necessary for + * HiDPI support). Scaled cursors will be loaded with each output. */ + cursor_mgr = wlr_xcursor_manager_create(NULL, 24); + + /* + * wlr_cursor *only* displays an image on screen. It does not move around + * when the pointer moves. However, we can attach input devices to it, and + * it will generate aggregate events for all of them. In these events, we + * can choose how we want to process them, forwarding them to clients and + * moving the cursor around. More detail on this process is described in my + * input handling blog post: + * + * https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html + * + * And more comments are sprinkled throughout the notify functions above. + */ + wl_signal_add(&cursor->events.motion, &cursor_motion); + wl_signal_add(&cursor->events.motion_absolute, &cursor_motion_absolute); + wl_signal_add(&cursor->events.button, &cursor_button); + wl_signal_add(&cursor->events.axis, &cursor_axis); + wl_signal_add(&cursor->events.frame, &cursor_frame); + + /* + * Configures a seat, which is a single "seat" at which a user sits and + * operates the computer. This conceptually includes up to one keyboard, + * pointer, touch, and drawing tablet device. We also rig up a listener to + * let us know when new input devices are available on the backend. + */ + 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); + wl_signal_add(&seat->events.request_set_selection, &request_set_sel); + wl_signal_add(&seat->events.request_set_primary_selection, &request_set_psel); + wl_signal_add(&seat->events.request_start_drag, &request_start_drag); + wl_signal_add(&seat->events.start_drag, &start_drag); + + 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); + + wlr_scene_set_presentation(scene, wlr_presentation_create(dpy, backend)); + + struct wl_event_loop *loop = wl_display_get_event_loop(dpy); + + /* Add handlers for user signals */ + /* TODO: Add config option for adding custom handlers to signal. */ + wl_event_loop_add_signal(loop, SIGRTMIN, &reloadconfig, config_file); + wl_global_create(dpy, &dscm_v1_interface, 1, NULL, dscm_bind); #ifdef XWAYLAND - /* - * Initialise the XWayland X server. - * It will be started when the first X client is started. - */ - xwayland = wlr_xwayland_create(dpy, compositor, 1); - if (xwayland) { - wl_signal_add(&xwayland->events.ready, &xwayland_ready); - wl_signal_add(&xwayland->events.new_surface, &new_xwayland_surface); - - setenv("DISPLAY", xwayland->display_name, 1); - } else { - fprintf(stderr, "failed to setup XWayland X server, continuing without it\n"); - } + /* + * Initialise the XWayland X server. + * It will be started when the first X client is started. + */ + xwayland = wlr_xwayland_create(dpy, compositor, 1); + if (xwayland) { + wl_signal_add(&xwayland->events.ready, &xwayland_ready); + wl_signal_add(&xwayland->events.new_surface, &new_xwayland_surface); + + setenv("DISPLAY", xwayland->display_name, 1); + } else { + fprintf(stderr, "failed to setup XWayland X server, continuing without it\n"); + } #endif } void sigchld(int unused) { - /* We should be able to remove this function in favor of a simple - * signal(SIGCHLD, SIG_IGN); - * but the Xwayland implementation in wlroots currently prevents us from - * setting our own disposition for SIGCHLD. - */ - if (signal(SIGCHLD, sigchld) == SIG_ERR) - die("can't install SIGCHLD handler:"); - while (0 < waitpid(-1, NULL, WNOHANG)) - ; + /* We should be able to remove this function in favor of a simple + * signal(SIGCHLD, SIG_IGN); + * but the Xwayland implementation in wlroots currently prevents us from + * setting our own disposition for SIGCHLD. + */ + if (signal(SIGCHLD, sigchld) == SIG_ERR) + die("can't install SIGCHLD handler:"); + while (0 < waitpid(-1, NULL, WNOHANG)) + ; } void spawn(const Arg *arg) { - if (fork() == 0) { - dup2(STDERR_FILENO, STDOUT_FILENO); - setsid(); - execvp(((char **)arg->v)[0], (char **)arg->v); - die("dwl: execvp %s failed:", ((char **)arg->v)[0]); - } + if (fork() == 0) { + dup2(STDERR_FILENO, STDOUT_FILENO); + setsid(); + execvp(((char **)arg->v)[0], (char **)arg->v); + die("dwl: execvp %s failed:", ((char **)arg->v)[0]); + } } void startdrag(struct wl_listener *listener, void *data) { - struct wlr_drag *drag = data; + struct wlr_drag *drag = data; - if (!drag->icon) - return; + if (!drag->icon) + return; - drag->icon->data = wlr_scene_subsurface_tree_create(layers[LyrNoFocus], drag->icon->surface); - motionnotify(0); - wl_signal_add(&drag->icon->events.destroy, &drag_icon_destroy); + drag->icon->data = wlr_scene_subsurface_tree_create(layers[LyrNoFocus], drag->icon->surface); + motionnotify(0); + wl_signal_add(&drag->icon->events.destroy, &drag_icon_destroy); } void tag(const Arg *arg) { - Client *sel = selclient(); - if (sel && arg->ui & TAGMASK) { - sel->tags = arg->ui & TAGMASK; - focusclient(focustop(selmon), 1); - arrange(selmon); - } - printstatus(); + Client *sel = selclient(); + if (sel && arg->ui & TAGMASK) { + sel->tags = arg->ui & TAGMASK; + focusclient(focustop(selmon), 1); + arrange(selmon); + } + printstatus(); } void tagmon(const Arg *arg) { - Client *sel = selclient(); - if (!sel) - return; - setmon(sel, dirtomon(arg->i), 0); + Client *sel = selclient(); + if (!sel) + return; + setmon(sel, dirtomon(arg->i), 0); } void tile(Monitor *m) { - unsigned int i, n = 0, h, r, oe = enablegaps, ie = enablegaps, mw, my, ty, - enableborders = 1; - Client *c; - - wl_list_for_each(c, &clients, link) - if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen) - n++; - if (n == 0) - return; - - if (smartgaps == n) - oe = 0; // outer gaps disabled - if (smartborders == n) - enableborders = 0; - - if (n > m->nmaster) - mw = m->nmaster ? (m->w.width + m->gappiv*ie) * m->mfact : 0; - else - mw = m->w.width - 2*m->gappov*oe + m->gappiv*ie; - i = 0; - my = ty = m->gappoh*oe; - wl_list_for_each(c, &clients, link) { - if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) - continue; - if (i < m->nmaster) { - r = MIN(n, m->nmaster) - i; - h = (m->w.height - my - m->gappoh*oe - m->gappih*ie * (r - 1)) / r; - resize(c, m->w.x + m->gappov*oe, m->w.y + my, - mw - m->gappiv*ie, h, 0, enableborders); - my += c->geom.height + m->gappih*ie; - } else { - r = n - i; - h = (m->w.height - ty - m->gappoh*oe - m->gappih*ie * (r - 1)) / r; - resize(c, m->w.x + mw + m->gappov*oe, m->w.y + ty, - m->w.width - mw - 2*m->gappov*oe, h, 0, enableborders); - ty += c->geom.height + m->gappih*ie; - } - i++; - } + unsigned int i, n = 0, h, r, oe = enablegaps, ie = enablegaps, mw, my, ty, + enableborders = 1; + Client *c; + + wl_list_for_each(c, &clients, link) + if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen) + n++; + if (n == 0) + return; + + if (smartgaps == n) + oe = 0; // outer gaps disabled + if (smartborders == n) + enableborders = 0; + + if (n > m->nmaster) + mw = m->nmaster ? (m->w.width + m->gappiv*ie) * m->mfact : 0; + else + mw = m->w.width - 2*m->gappov*oe + m->gappiv*ie; + i = 0; + my = ty = m->gappoh*oe; + wl_list_for_each(c, &clients, link) { + if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) + continue; + if (i < m->nmaster) { + r = MIN(n, m->nmaster) - i; + h = (m->w.height - my - m->gappoh*oe - m->gappih*ie * (r - 1)) / r; + resize(c, m->w.x + m->gappov*oe, m->w.y + my, + mw - m->gappiv*ie, h, 0, enableborders); + my += c->geom.height + m->gappih*ie; + } else { + r = n - i; + h = (m->w.height - ty - m->gappoh*oe - m->gappih*ie * (r - 1)) / r; + resize(c, m->w.x + mw + m->gappov*oe, m->w.y + ty, + m->w.width - mw - 2*m->gappov*oe, h, 0, enableborders); + ty += c->geom.height + m->gappih*ie; + } + i++; + } } void togglefloating(const Arg *arg) { - Client *sel = selclient(); - /* return if fullscreen */ - if (sel && !sel->isfullscreen) - setfloating(sel, !sel->isfloating); + Client *sel = selclient(); + /* return if fullscreen */ + if (sel && !sel->isfullscreen) + setfloating(sel, !sel->isfloating); } void togglefullscreen(const Arg *arg) { - Client *sel = selclient(); - if (sel) - setfullscreen(sel, !sel->isfullscreen); + Client *sel = selclient(); + if (sel) + setfullscreen(sel, !sel->isfullscreen); } void toggletag(const Arg *arg) { - unsigned int newtags; - Client *sel = selclient(); - if (!sel) - return; - newtags = sel->tags ^ (arg->ui & TAGMASK); - if (newtags) { - sel->tags = newtags; - focusclient(focustop(selmon), 1); - arrange(selmon); - } - printstatus(); + unsigned int newtags; + Client *sel = selclient(); + if (!sel) + return; + newtags = sel->tags ^ (arg->ui & TAGMASK); + if (newtags) { + sel->tags = newtags; + focusclient(focustop(selmon), 1); + arrange(selmon); + } + printstatus(); } void toggleview(const Arg *arg) { - unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); + unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); - if (newtagset) { - selmon->tagset[selmon->seltags] = newtagset; - focusclient(focustop(selmon), 1); - arrange(selmon); - } - printstatus(); + if (newtagset) { + selmon->tagset[selmon->seltags] = newtagset; + focusclient(focustop(selmon), 1); + arrange(selmon); + } + printstatus(); } void unmaplayersurface(LayerSurface *layersurface) { - layersurface->layer_surface->mapped = 0; - if (layersurface->layer_surface->surface == - seat->keyboard_state.focused_surface) - focusclient(selclient(), 1); - motionnotify(0); + layersurface->layer_surface->mapped = 0; + if (layersurface->layer_surface->surface == + seat->keyboard_state.focused_surface) + focusclient(selclient(), 1); + motionnotify(0); } void unmaplayersurfacenotify(struct wl_listener *listener, void *data) { - LayerSurface *layersurface = wl_container_of(listener, layersurface, unmap); - unmaplayersurface(layersurface); + LayerSurface *layersurface = wl_container_of(listener, layersurface, unmap); + unmaplayersurface(layersurface); } void unmapnotify(struct wl_listener *listener, void *data) { - /* Called when the surface is unmapped, and should no longer be shown. */ - Client *c = wl_container_of(listener, c, unmap); - if (c == grabc) { - cursor_mode = CurNormal; - grabc = NULL; - } + /* Called when the surface is unmapped, and should no longer be shown. */ + Client *c = wl_container_of(listener, c, unmap); + if (c == grabc) { + cursor_mode = CurNormal; + grabc = NULL; + } - if (c->mon) - c->mon->un_map = 1; + if (c->mon) + c->mon->un_map = 1; - if (client_is_unmanaged(c)) { - wlr_scene_node_destroy(c->scene); - return; - } + if (client_is_unmanaged(c)) { + wlr_scene_node_destroy(c->scene); + return; + } - wl_list_remove(&c->link); - setmon(c, NULL, 0); - wl_list_remove(&c->flink); - wlr_scene_node_destroy(c->scene); - printstatus(); + wl_list_remove(&c->link); + setmon(c, NULL, 0); + wl_list_remove(&c->flink); + wlr_scene_node_destroy(c->scene); + printstatus(); } void updatemons(struct wl_listener *listener, void *data) { - /* - * Called whenever the output layout changes: adding or removing a - * monitor, changing an output's mode or position, etc. This is where - * the change officially happens and we update geometry, window - * positions, focus, and the stored configuration in wlroots' - * output-manager implementation. - */ - struct wlr_output_configuration_v1 *config = - wlr_output_configuration_v1_create(); - Monitor *m; - sgeom = *wlr_output_layout_get_box(output_layout, NULL); - 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); - - /* TODO: move clients off disabled monitors */ - /* TODO: move focus if selmon is disabled */ - - /* Get the effective monitor geometry to use for surfaces */ - m->m = m->w = *wlr_output_layout_get_box(output_layout, m->wlr_output); - wlr_scene_output_set_position(m->scene_output, m->m.x, m->m.y); - /* 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); + /* + * Called whenever the output layout changes: adding or removing a + * monitor, changing an output's mode or position, etc. This is where + * the change officially happens and we update geometry, window + * positions, focus, and the stored configuration in wlroots' + * output-manager implementation. + */ + struct wlr_output_configuration_v1 *config = + wlr_output_configuration_v1_create(); + Monitor *m; + sgeom = *wlr_output_layout_get_box(output_layout, NULL); + 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); + + /* TODO: move clients off disabled monitors */ + /* TODO: move focus if selmon is disabled */ + + /* Get the effective monitor geometry to use for surfaces */ + m->m = m->w = *wlr_output_layout_get_box(output_layout, m->wlr_output); + wlr_scene_output_set_position(m->scene_output, m->m.x, m->m.y); + /* 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 updatetitle(struct wl_listener *listener, void *data) { - Client *c = wl_container_of(listener, c, set_title); - if (c == focustop(c->mon)) - printstatus(); + Client *c = wl_container_of(listener, c, set_title); + if (c == focustop(c->mon)) + printstatus(); } void urgent(struct wl_listener *listener, void *data) { - struct wlr_xdg_activation_v1_request_activate_event *event = data; - Client *c; + 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(); - } + 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) { - unsigned int currtagset = selmon->tagset[selmon->seltags]; - if ((arg->ui & TAGMASK) == currtagset) - return; - selmon->prevtagset = currtagset; - selmon->seltags ^= 1; /* toggle sel tagset */ - if (arg->ui & TAGMASK) - selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; - focusclient(focustop(selmon), 1); - arrange(selmon); - printstatus(); + unsigned int currtagset = selmon->tagset[selmon->seltags]; + if ((arg->ui & TAGMASK) == currtagset) + return; + selmon->prevtagset = currtagset; + selmon->seltags ^= 1; /* toggle sel tagset */ + if (arg->ui & TAGMASK) + selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; + focusclient(focustop(selmon), 1); + arrange(selmon); + printstatus(); } void viewprev(const Arg *arg) { - unsigned int tagset = selmon->tagset[selmon->seltags]; - selmon->tagset[selmon->seltags] = selmon->prevtagset; - selmon->prevtagset = tagset; - focusclient(focustop(selmon), 1); - arrange(selmon); - printstatus(); + unsigned int tagset = selmon->tagset[selmon->seltags]; + selmon->tagset[selmon->seltags] = selmon->prevtagset; + selmon->prevtagset = tagset; + focusclient(focustop(selmon), 1); + arrange(selmon); + printstatus(); } 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); + struct wlr_virtual_keyboard_v1 *keyboard = data; + struct wlr_input_device *device = &keyboard->input_device; + createkeyboard(device); } void writepid(const char *runtimedir) { - /* Writes a pid file so that services managers like Shepherd - * can provision dwl-guile correcly to other services that - * depend on an existing wayland display. */ - char buf[128]; - FILE *pidfile; - snprintf(buf, 128, "%s/dwl-guile.pid", runtimedir); - pidfile = fopen(buf, "w"); - fprintf(pidfile, "%d", getpid()); - fclose(pidfile); + /* Writes a pid file so that services managers like Shepherd + * can provision dwl-guile correcly to other services that + * depend on an existing wayland display. */ + char buf[128]; + FILE *pidfile; + snprintf(buf, 128, "%s/dwl-guile.pid", runtimedir); + pidfile = fopen(buf, "w"); + fprintf(pidfile, "%d", getpid()); + fclose(pidfile); } Monitor * xytomon(double x, double y) { - struct wlr_output *o = wlr_output_layout_output_at(output_layout, x, y); - return o ? o->data : NULL; + struct wlr_output *o = wlr_output_layout_output_at(output_layout, x, y); + return o ? o->data : NULL; } struct wlr_scene_node * xytonode(double x, double y, struct wlr_surface **psurface, - Client **pc, LayerSurface **pl, double *nx, double *ny) -{ - struct wlr_scene_node *node, *pnode; - struct wlr_surface *surface = NULL; - Client *c = NULL; - LayerSurface *l = NULL; - int i; - int focus_order[] = { LyrOverlay, LyrTop, LyrFloat, LyrTile, LyrBottom, LyrBg }; - - for (i = 0; i < LENGTH(focus_order); i++) { - if ((node = wlr_scene_node_at(layers[focus_order[i]], x, y, nx, ny))) { - if (node->type == WLR_SCENE_NODE_SURFACE) - surface = wlr_scene_surface_from_node(node)->surface; - /* Walk the tree to find a node that knows the client */ - for (pnode = node; pnode && !c; pnode = pnode->parent) - c = pnode->data; - if (c && c->type == LayerShell) { - c = NULL; - l = pnode->data; - } - } - if (surface) - break; - } - - if (psurface) *psurface = surface; - if (pc) *pc = c; - if (pl) *pl = l; - return node; + Client **pc, LayerSurface **pl, double *nx, double *ny) +{ + struct wlr_scene_node *node, *pnode; + struct wlr_surface *surface = NULL; + Client *c = NULL; + LayerSurface *l = NULL; + int i; + int focus_order[] = { LyrOverlay, LyrTop, LyrFloat, LyrTile, LyrBottom, LyrBg }; + + for (i = 0; i < LENGTH(focus_order); i++) { + if ((node = wlr_scene_node_at(layers[focus_order[i]], x, y, nx, ny))) { + if (node->type == WLR_SCENE_NODE_SURFACE) + surface = wlr_scene_surface_from_node(node)->surface; + /* Walk the tree to find a node that knows the client */ + for (pnode = node; pnode && !c; pnode = pnode->parent) + c = pnode->data; + if (c && c->type == LayerShell) { + c = NULL; + l = pnode->data; + } + } + if (surface) + break; + } + + if (psurface) *psurface = surface; + if (pc) *pc = c; + if (pl) *pl = l; + return node; } void zoom(const Arg *arg) { - Client *c, *sel = selclient(); + Client *c, *sel = selclient(); - if (!sel || !selmon->lt[selmon->sellt]->arrange || sel->isfloating) - return; + if (!sel || !selmon->lt[selmon->sellt]->arrange || sel->isfloating) + return; - /* Search for the first tiled window that is not sel, marking sel as - * NULL if we pass it along the way */ - wl_list_for_each(c, &clients, link) - if (VISIBLEON(c, selmon) && !c->isfloating) { - if (c != sel) - break; - sel = NULL; - } + /* Search for the first tiled window that is not sel, marking sel as + * NULL if we pass it along the way */ + wl_list_for_each(c, &clients, link) + if (VISIBLEON(c, selmon) && !c->isfloating) { + if (c != sel) + break; + sel = NULL; + } - /* Return if no other tiled window was found */ - if (&c->link == &clients) - return; + /* Return if no other tiled window was found */ + if (&c->link == &clients) + return; - /* If we passed sel, move c to the front; otherwise, move sel to the - * front */ - if (!sel) - sel = c; - wl_list_remove(&sel->link); - wl_list_insert(&clients, &sel->link); + /* If we passed sel, move c to the front; otherwise, move sel to the + * front */ + if (!sel) + sel = c; + wl_list_remove(&sel->link); + wl_list_insert(&clients, &sel->link); - focusclient(sel, 1); - arrange(selmon); + focusclient(sel, 1); + arrange(selmon); } #ifdef XWAYLAND void activatex11(struct wl_listener *listener, void *data) { - Client *c = wl_container_of(listener, c, activate); + Client *c = wl_container_of(listener, c, activate); - /* Only "managed" windows can be activated */ - if (c->type == X11Managed) - wlr_xwayland_surface_activate(c->surface.xwayland, 1); + /* Only "managed" windows can be activated */ + if (c->type == X11Managed) + 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); + 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) { - 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 = ecalloc(1, sizeof(*c)); - c->surface.xwayland = xwayland_surface; - c->type = xwayland_surface->override_redirect ? X11Unmanaged : X11Managed; - c->bw = borderpx; - c->isfullscreen = 0; - c->alpha = default_alpha; - - /* Listen to the various events it can emit */ - LISTEN(&xwayland_surface->events.map, &c->map, mapnotify); - LISTEN(&xwayland_surface->events.unmap, &c->unmap, unmapnotify); - LISTEN(&xwayland_surface->events.request_activate, &c->activate, 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); + 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 = ecalloc(1, sizeof(*c)); + c->surface.xwayland = xwayland_surface; + c->type = xwayland_surface->override_redirect ? X11Unmanaged : X11Managed; + c->bw = borderpx; + c->isfullscreen = 0; + c->alpha = default_alpha; + + /* Listen to the various events it can emit */ + LISTEN(&xwayland_surface->events.map, &c->map, mapnotify); + LISTEN(&xwayland_surface->events.unmap, &c->unmap, unmapnotify); + LISTEN(&xwayland_surface->events.request_activate, &c->activate, 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); } Atom getatom(xcb_connection_t *xc, const char *name) { - Atom atom = 0; - xcb_intern_atom_reply_t *reply; - 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); + Atom atom = 0; + xcb_intern_atom_reply_t *reply; + 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); - return atom; + return atom; } void xwaylandready(struct wl_listener *listener, void *data) { - struct wlr_xcursor *xcursor; - xcb_connection_t *xc = xcb_connect(xwayland->display_name, NULL); - int err = xcb_connection_has_error(xc); - if (err) { - fprintf(stderr, "xcb_connect to X server failed with code %d\n. \ - Continuing with degraded functionality.\n", err); - return; - } - - /* Collect atoms we are interested in. If getatom returns 0, we will - * not detect that window type. */ - netatom[NetWMWindowTypeDialog] = getatom(xc, "_NET_WM_WINDOW_TYPE_DIALOG"); - netatom[NetWMWindowTypeSplash] = getatom(xc, "_NET_WM_WINDOW_TYPE_SPLASH"); - netatom[NetWMWindowTypeToolbar] = getatom(xc, "_NET_WM_WINDOW_TYPE_TOOLBAR"); - netatom[NetWMWindowTypeUtility] = getatom(xc, "_NET_WM_WINDOW_TYPE_UTILITY"); - - /* assign the one and only seat */ - wlr_xwayland_set_seat(xwayland, seat); - - /* Set the default XWayland cursor to match the rest of dwl. */ - if ((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); + struct wlr_xcursor *xcursor; + xcb_connection_t *xc = xcb_connect(xwayland->display_name, NULL); + int err = xcb_connection_has_error(xc); + if (err) { + fprintf(stderr, "xcb_connect to X server failed with code %d\n. \ + Continuing with degraded functionality.\n", err); + return; + } + + /* Collect atoms we are interested in. If getatom returns 0, we will + * not detect that window type. */ + netatom[NetWMWindowTypeDialog] = getatom(xc, "_NET_WM_WINDOW_TYPE_DIALOG"); + netatom[NetWMWindowTypeSplash] = getatom(xc, "_NET_WM_WINDOW_TYPE_SPLASH"); + netatom[NetWMWindowTypeToolbar] = getatom(xc, "_NET_WM_WINDOW_TYPE_TOOLBAR"); + netatom[NetWMWindowTypeUtility] = getatom(xc, "_NET_WM_WINDOW_TYPE_UTILITY"); + + /* assign the one and only seat */ + wlr_xwayland_set_seat(xwayland, seat); + + /* Set the default XWayland cursor to match the rest of dwl. */ + if ((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); } #endif void dscm_sendeventsclient(DscmClient *c) { - char root[HEXLENGTH], border[HEXLENGTH], - focus[HEXLENGTH], text[HEXLENGTH]; + char root[HEXLENGTH], border[HEXLENGTH], + focus[HEXLENGTH], text[HEXLENGTH]; - for (int i = 0; i < numtags; i++) - dscm_v1_send_tag(c->resource, tags[i]); - for (int i = 0; i < numlayouts; i++) - dscm_v1_send_layout(c->resource, layouts[i].symbol); + for (int i = 0; i < numtags; i++) + dscm_v1_send_tag(c->resource, tags[i]); + for (int i = 0; i < numlayouts; i++) + dscm_v1_send_layout(c->resource, layouts[i].symbol); - dscm_rgbatostr(root, rootcolor); - dscm_rgbatostr(border, bordercolor); - dscm_rgbatostr(focus, focuscolor); - dscm_rgbatostr(text, textcolor); - dscm_v1_send_colorscheme(c->resource, root, border, focus, text); + dscm_rgbatostr(root, rootcolor); + dscm_rgbatostr(border, bordercolor); + dscm_rgbatostr(focus, focuscolor); + dscm_rgbatostr(text, textcolor); + dscm_v1_send_colorscheme(c->resource, root, border, focus, text); } void dscm_sendevents(void) { - DscmClient *c; - wl_list_for_each(c, &dscm_clients, link) - dscm_sendeventsclient(c); + DscmClient *c; + wl_list_for_each(c, &dscm_clients, link) + dscm_sendeventsclient(c); } void dscm_rgbatostr(char *buf, float *color) { - if (!color) - return; + if (!color) + return; - unsigned int r, g, b, a; - r = MAX(0, MIN(255, (int)ROUND(color[0] * 256.0))); - g = MAX(0, MIN(255, (int)ROUND(color[1] * 256.0))); - b = MAX(0, MIN(255, (int)ROUND(color[2] * 256.0))); - a = MAX(0, MIN(255, (int)ROUND(color[3] * 256.0))); - snprintf(buf, HEXLENGTH, "%02X%02X%02X%02X", r, g, b, a); + unsigned int r, g, b, a; + r = MAX(0, MIN(255, (int)ROUND(color[0] * 256.0))); + g = MAX(0, MIN(255, (int)ROUND(color[1] * 256.0))); + b = MAX(0, MIN(255, (int)ROUND(color[2] * 256.0))); + a = MAX(0, MIN(255, (int)ROUND(color[3] * 256.0))); + snprintf(buf, HEXLENGTH, "%02X%02X%02X%02X", r, g, b, a); } void dscm_closemon(struct wl_client *client, struct wl_resource *resource) { - wl_resource_destroy(resource); + wl_resource_destroy(resource); } void dscm_destroymon(struct wl_resource *resource) { - DscmMonitor *mon = wl_resource_get_user_data(resource); - if (mon) { - wl_list_remove(&mon->link); - free(mon); - } + DscmMonitor *mon = wl_resource_get_user_data(resource); + if (mon) { + wl_list_remove(&mon->link); + free(mon); + } } void dscm_printstatusmon(Monitor *m, const DscmMonitor *mon) { - Client *c, *focused; - int tagmask, state, numclients, focusedclient; - focused = focustop(m); - dscm_monitor_v1_send_selected(mon->resource, m == selmon); - - for (int tag = 0; tag < numtags; tag++) { - numclients = state = 0; - focusedclient = -1; - tagmask = 1 << tag; - if ((tagmask & m->tagset[m->seltags]) != 0) - state = state | DSCM_MONITOR_V1_TAG_STATE_ACTIVE; - wl_list_for_each(c, &clients, link) { - if (c->mon != m) - continue; - if (!(c->tags & tagmask)) - continue; - if (c == focused) - focusedclient = numclients; - numclients++; - if (c->isurgent) - state = state | DSCM_MONITOR_V1_TAG_STATE_URGENT; - } - dscm_monitor_v1_send_tag(mon->resource, tag, state, - numclients, focusedclient); - } - dscm_monitor_v1_send_layout(mon->resource, m->lt[m->sellt] - layouts); - dscm_monitor_v1_send_title(mon->resource, focused ? - client_get_title(focused) : ""); - dscm_monitor_v1_send_frame(mon->resource); + Client *c, *focused; + int tagmask, state, numclients, focusedclient; + focused = focustop(m); + dscm_monitor_v1_send_selected(mon->resource, m == selmon); + + for (int tag = 0; tag < numtags; tag++) { + numclients = state = 0; + focusedclient = -1; + tagmask = 1 << tag; + if ((tagmask & m->tagset[m->seltags]) != 0) + state = state | DSCM_MONITOR_V1_TAG_STATE_ACTIVE; + wl_list_for_each(c, &clients, link) { + if (c->mon != m) + continue; + if (!(c->tags & tagmask)) + continue; + if (c == focused) + focusedclient = numclients; + numclients++; + if (c->isurgent) + state = state | DSCM_MONITOR_V1_TAG_STATE_URGENT; + } + dscm_monitor_v1_send_tag(mon->resource, tag, state, + numclients, focusedclient); + } + dscm_monitor_v1_send_layout(mon->resource, m->lt[m->sellt] - layouts); + dscm_monitor_v1_send_title(mon->resource, focused ? + client_get_title(focused) : ""); + dscm_monitor_v1_send_frame(mon->resource); } void dscm_printstatus(Monitor *m) { - DscmMonitor *mon; - wl_list_for_each(mon, &m->dscm, link) - dscm_printstatusmon(m, mon); + DscmMonitor *mon; + wl_list_for_each(mon, &m->dscm, link) + dscm_printstatusmon(m, mon); } void dscm_settags(struct wl_client *client, struct wl_resource *resource, - uint32_t t, uint32_t toggletagset) + uint32_t t, uint32_t toggletagset) { - DscmMonitor *mon; - Monitor *m; - mon = wl_resource_get_user_data(resource); - if (!mon) - return; - m = mon->monitor; - if ((t & TAGMASK) == m->tagset[m->seltags]) - return; - if (toggletagset) - m->seltags ^= 1; - if (t & TAGMASK) - m->tagset[m->seltags] = t & TAGMASK; + DscmMonitor *mon; + Monitor *m; + mon = wl_resource_get_user_data(resource); + if (!mon) + return; + m = mon->monitor; + if ((t & TAGMASK) == m->tagset[m->seltags]) + return; + if (toggletagset) + m->seltags ^= 1; + if (t & TAGMASK) + m->tagset[m->seltags] = t & TAGMASK; - focusclient(focustop(m), 1); - arrange(m); - printstatus(); + focusclient(focustop(m), 1); + arrange(m); + printstatus(); } void dscm_setlayout(struct wl_client *client, struct wl_resource *resource, - uint32_t layout) + uint32_t layout) { - DscmMonitor *mon; - Monitor *m; - mon = wl_resource_get_user_data(resource); - if (!mon) - return; - m = mon->monitor; - if (layout >= numlayouts) - return; - if (layout != m->lt[m->sellt] - layouts) - m->sellt ^= 1; + DscmMonitor *mon; + Monitor *m; + mon = wl_resource_get_user_data(resource); + if (!mon) + return; + m = mon->monitor; + if (layout >= numlayouts) + return; + if (layout != m->lt[m->sellt] - layouts) + m->sellt ^= 1; - m->lt[m->sellt] = &layouts[layout]; - arrange(m); - printstatus(); + m->lt[m->sellt] = &layouts[layout]; + arrange(m); + printstatus(); } void dscm_setclient(struct wl_client *client, struct wl_resource *resource, - uint32_t and, uint32_t xor) -{ - DscmMonitor *mon; - Client *sel; - unsigned int newtags; - mon = wl_resource_get_user_data(resource); - if (!mon) - return; - sel = focustop(mon->monitor); - if (!sel) - return; - newtags = (sel->tags & and) ^ xor; - if (newtags) { - sel->tags = newtags; - focusclient(focustop(selmon), 1); - arrange(selmon); - printstatus(); - } + uint32_t and, uint32_t xor) +{ + DscmMonitor *mon; + Client *sel; + unsigned int newtags; + mon = wl_resource_get_user_data(resource); + if (!mon) + return; + sel = focustop(mon->monitor); + if (!sel) + return; + newtags = (sel->tags & and) ^ xor; + if (newtags) { + sel->tags = newtags; + focusclient(focustop(selmon), 1); + arrange(selmon); + printstatus(); + } } void dscm_release(struct wl_client *client, struct wl_resource *resource) { - wl_resource_destroy(resource); + wl_resource_destroy(resource); } void dscm_getmon(struct wl_client *client, struct wl_resource *resource, - uint32_t id, struct wl_resource *output) -{ - DscmMonitor *mon; - struct wlr_output *wlr_output = wlr_output_from_resource(output); - struct Monitor *m = wlr_output->data; - struct wl_resource *dscm_monitor_resource = wl_resource_create( - client, - &dscm_monitor_v1_interface, wl_resource_get_version(resource), id); - if (!resource) { - wl_client_post_no_memory(client); - return; - } - mon = calloc(1, sizeof(DscmMonitor)); - mon->resource = dscm_monitor_resource; - mon->monitor = m; - wl_resource_set_implementation(dscm_monitor_resource, &dscm_monitor_implementation, - mon, dscm_destroymon); - wl_list_insert(&m->dscm, &mon->link); - dscm_printstatusmon(m, mon); + uint32_t id, struct wl_resource *output) +{ + DscmMonitor *mon; + struct wlr_output *wlr_output = wlr_output_from_resource(output); + struct Monitor *m = wlr_output->data; + struct wl_resource *dscm_monitor_resource = wl_resource_create( + client, + &dscm_monitor_v1_interface, wl_resource_get_version(resource), id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + mon = calloc(1, sizeof(DscmMonitor)); + mon->resource = dscm_monitor_resource; + mon->monitor = m; + wl_resource_set_implementation(dscm_monitor_resource, &dscm_monitor_implementation, + mon, dscm_destroymon); + wl_list_insert(&m->dscm, &mon->link); + dscm_printstatusmon(m, mon); } void dscm_destroy(struct wl_resource *resource) { - DscmClient *c = wl_resource_get_user_data(resource); - if (c) { - wl_list_remove(&c->link); - free(c); - } + DscmClient *c = wl_resource_get_user_data(resource); + if (c) { + wl_list_remove(&c->link); + free(c); + } } void dscm_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { - DscmClient *c; - struct wl_resource *resource = wl_resource_create( - client, - &dscm_v1_interface, version, id); - if (!resource) { - wl_client_post_no_memory(client); - return; - } + DscmClient *c; + struct wl_resource *resource = wl_resource_create( + client, + &dscm_v1_interface, version, id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } - c = calloc(1, sizeof(DscmClient)); - c->resource = resource; - wl_resource_set_implementation(resource, &dscm_implementation, c, dscm_destroy); - wl_list_insert(&dscm_clients, &c->link); - dscm_sendevents(); + c = calloc(1, sizeof(DscmClient)); + c->resource = resource; + wl_resource_set_implementation(resource, &dscm_implementation, c, dscm_destroy); + wl_list_insert(&dscm_clients, &c->link); + dscm_sendevents(); } int main(int argc, char *argv[]) { - int c; - char *startup_cmd = NULL, *config_file = NULL, *runtimedir = NULL; - - while ((c = getopt(argc, argv, "s:c:h")) != -1) { - if (c == 's') - startup_cmd = optarg; - else if (c == 'c') - config_file = optarg; - else - goto usage; - } - if (optind < argc) - goto usage; - - // Wayland requires XDG_RUNTIME_DIR for creating its communications - // socket - if (!(runtimedir = getenv("XDG_RUNTIME_DIR"))) - die("XDG_RUNTIME_DIR must be set"); - if (!config_file) - die("error: config path must be set using '-c'"); - scm_init_guile(); - dscm_register(); - dscm_config_parse(config_file); - setup(config_file); - writepid(runtimedir); - run(startup_cmd); - dscm_config_cleanup(); - cleanup(); - return EXIT_SUCCESS; + int c; + char *startup_cmd = NULL, *config_file = NULL, *runtimedir = NULL; + + while ((c = getopt(argc, argv, "s:c:h")) != -1) { + if (c == 's') + startup_cmd = optarg; + else if (c == 'c') + config_file = optarg; + else + goto usage; + } + if (optind < argc) + goto usage; + + // Wayland requires XDG_RUNTIME_DIR for creating its communications + // socket + if (!(runtimedir = getenv("XDG_RUNTIME_DIR"))) + die("XDG_RUNTIME_DIR must be set"); + if (!config_file) + die("error: config path must be set using '-c'"); + scm_init_guile(); + dscm_register(); + dscm_config_parse(config_file); + setup(config_file); + writepid(runtimedir); + run(startup_cmd); + dscm_config_cleanup(); + cleanup(); + return EXIT_SUCCESS; usage: - die("Usage: %s [-c path to config.scm] [-s startup command]", argv[0]); + die("Usage: %s [-c path to config.scm] [-s startup command]", argv[0]); } From 5b354202c125f119b7f84c6b169b76def2ec74a7 Mon Sep 17 00:00:00 2001 From: Fredrik Engstrand Date: Sat, 16 Apr 2022 13:51:37 +0200 Subject: [PATCH 112/117] fix: use wlroots 0.15.1 --- guix.scm | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/guix.scm b/guix.scm index ccd4399a3..5e9e25a10 100644 --- a/guix.scm +++ b/guix.scm @@ -63,11 +63,11 @@ (inputs (list wayland-1.20.0)))) -(define wlroots-0.15.0 +(define wlroots-0.15.1 (package (inherit wlroots) (name "wlroots") - (version "0.15.0") + (version "0.15.1") (source (origin (method git-fetch) @@ -76,7 +76,7 @@ (commit version))) (file-name (git-file-name name version)) (sha256 - (base32 "0wdzs0wpv61pxgy3mx3xjsndyfmbj30v47d3w9ymmnd4r479n41n")))) + (base32 "00s73nhi3sc48l426jdlqwpclg41kx1hv0yk4yxhbzw19gqpfm1h")))) (propagated-inputs (modify-inputs (package-propagated-inputs wlroots) (prepend libdrm-2.4.109) @@ -88,8 +88,7 @@ (source source) (name "dwl-guile-devel") (inputs - `(("guile-3.0" ,guile-3.0) - ("wlroots-0.15.0" ,wlroots-0.15.0))) + (list guile-3.0 wlroots-0.15.1)) (arguments (substitute-keyword-arguments (package-arguments dwl) From 6d6bcea2bea8860a0335394b0c58460ee09feffb Mon Sep 17 00:00:00 2001 From: Fredrik Engstrand Date: Sat, 16 Apr 2022 13:55:38 +0200 Subject: [PATCH 113/117] chore: create diffs based on dwl v0.3.1 --- scripts/create-patches.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/create-patches.sh b/scripts/create-patches.sh index 41cd35917..d5b973257 100755 --- a/scripts/create-patches.sh +++ b/scripts/create-patches.sh @@ -16,10 +16,11 @@ do git fetch origin patch/$patch git diff $DIFF_ORIGIN patch/$patch \ ':(exclude)README.md' \ + ':(exclude)README.org' \ ':(exclude)patches' \ ':(exclude)scripts/create-patches.sh' \ ':(exclude).gitignore' > patches/$patch.patch done -# Create dwl-guile patch based on dwl v0.2.1 -git diff v0.2.1 > patches/dwl-guile.patch +# Create dwl-guile patch based on dwl v0.3.1 +git diff v0.3.1 > patches/dwl-guile.patch From 0be0d3b07142d6d31760e85993cb68553b7f49ab Mon Sep 17 00:00:00 2001 From: Fredrik Engstrand Date: Sat, 16 Apr 2022 13:57:32 +0200 Subject: [PATCH 114/117] fix: load lockfullscreen from config --- dscm-config.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dscm-config.h b/dscm-config.h index c7b08c007..7185a4447 100644 --- a/dscm-config.h +++ b/dscm-config.h @@ -140,9 +140,7 @@ dscm_config_parse(char *config_file) scm_c_primitive_load(config_file); config = dscm_get_variable("config"); - /* TODO: Implement this */ - /* lockfullscreen = dscm_alist_get_int(config, "lock-fullscreen"); */ - lockfullscreen = 1; + lockfullscreen = dscm_alist_get_int(config, "lock-fullscreen"); sloppyfocus = dscm_alist_get_int(config, "sloppy-focus"); tap_to_click = dscm_alist_get_int(config, "tap-to-click"); natural_scrolling = dscm_alist_get_int(config, "natural-scrolling"); From dc4487ad58e2bbb9d74db56785076cff2584f946 Mon Sep 17 00:00:00 2001 From: Fredrik Engstrand Date: Sat, 16 Apr 2022 14:22:59 +0200 Subject: [PATCH 115/117] fix: remove duplicate code --- dwl.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/dwl.c b/dwl.c index fac7f7377..b4cd06125 100644 --- a/dwl.c +++ b/dwl.c @@ -2252,10 +2252,6 @@ setup(char *config_file) wl_list_init(&mons); wl_signal_add(&backend->events.new_output, &new_output); - /* Initializes the interface used to implement urgency hints */ - activation = wlr_xdg_activation_v1_create(dpy); - wl_signal_add(&activation->events.request_activate, &request_activate); - /* Set up our client lists and the xdg-shell. The xdg-shell is a * Wayland protocol which is used for application windows. For more * detail on shells, refer to the article: From 5e7e94b8927bf32497bc014a83da2ac08848ede7 Mon Sep 17 00:00:00 2001 From: Fredrik Engstrand Date: Sat, 16 Apr 2022 14:30:04 +0200 Subject: [PATCH 116/117] fix: correctly use keycodes for keybindings --- dwl.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/dwl.c b/dwl.c index b4cd06125..29d4f3ccf 100644 --- a/dwl.c +++ b/dwl.c @@ -1395,18 +1395,12 @@ keybinding(uint32_t mods, xkb_keycode_t keycode) void keypress(struct wl_listener *listener, void *data) { - int i; /* 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; /* Translate libinput keycode -> xkbcommon */ uint32_t keycode = event->keycode + 8; - /* Get a list of keysyms based on the keymap for this keyboard */ - const xkb_keysym_t *syms; - int nsyms = xkb_state_key_get_syms( - kb->device->keyboard->xkb_state, keycode, &syms); - int handled = 0; uint32_t mods = wlr_keyboard_get_modifiers(kb->device->keyboard); @@ -1416,13 +1410,6 @@ keypress(struct wl_listener *listener, void *data) && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) handled = keybinding(mods, keycode) || handled; - /* On _press_ if there is no active screen locker, - * attempt to process a compositor keybinding. */ - if (!input_inhibit_mgr->active_inhibitor - && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) - for (i = 0; i < nsyms; i++) - handled = keybinding(mods, syms[i]) || handled; - if (!handled) { /* Pass unhandled keycodes along to the client. */ wlr_seat_set_keyboard(seat, kb->device); From 45263508e3a3cbe38ed8072a53d43c763fb3bb64 Mon Sep 17 00:00:00 2001 From: Fredrik Engstrand Date: Sat, 16 Apr 2022 16:35:40 +0200 Subject: [PATCH 117/117] fix: ensure that fullscreened clients are on top --- dwl.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 29d4f3ccf..65b04649f 100644 --- a/dwl.c +++ b/dwl.c @@ -1457,7 +1457,7 @@ void 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); + Client *c = wl_container_of(listener, c, map), *sel = selclient(); int i; /* Create scene tree for this client and its border */ @@ -1499,6 +1499,10 @@ mapnotify(struct wl_listener *listener, void *data) if (c->isfullscreen) setfullscreen(c, 1); + /* Prevent new clients from stealing focus from fullscreen client + * on same monitor. */ + else if (sel && sel->isfullscreen && VISIBLEON(sel, c->mon)) + setfullscreen(sel, 0); c->mon->un_map = 1; } @@ -2047,6 +2051,9 @@ setfullscreen(Client *c, int fullscreen) c->prevalpha = c->alpha; c->alpha = 1; resize(c, c->mon->m.x, c->mon->m.y, c->mon->m.width, c->mon->m.height, 0, !smartborders); + + /* Ensure that the fullscreened client is the top client */ + wlr_scene_node_raise_to_top(c->scene); } else { c->alpha = c->prevalpha; /* restore previous size instead of arrange for floating windows since