From f593820edee096dc62e296af9d7514cdf4268ce4 Mon Sep 17 00:00:00 2001 From: Alexander Courtis Date: Tue, 4 Aug 2020 11:53:23 +1000 Subject: [PATCH 001/173] #24 ensure that xwayland cursor defaults to left_ptr --- dwl.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/dwl.c b/dwl.c index fbdfbe0..fa59ede 100644 --- a/dwl.c +++ b/dwl.c @@ -243,7 +243,9 @@ static struct wl_list independents; static struct wlr_xdg_decoration_manager_v1 *xdeco_mgr; static struct wlr_cursor *cursor; +static struct wlr_xcursor *xcursor; static struct wlr_xcursor_manager *cursor_mgr; +static struct wlr_xcursor_manager *xcursor_mgr; static struct wlr_seat *seat; static struct wl_list keyboards; @@ -1588,6 +1590,20 @@ 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); + xcursor = wlr_xcursor_manager_get_xcursor(xcursor_mgr, "left_ptr", 1); + if (xcursor) { + 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, true); } else { fprintf(stderr, "failed to setup XWayland X server, continuing without it\n"); From 2616812a7a9b3a4fd72f782cf1bafea0742da590 Mon Sep 17 00:00:00 2001 From: Alexander Courtis Date: Tue, 11 Aug 2020 11:29:32 +1000 Subject: [PATCH 002/173] #24 ensure that xwayland cursor defaults to left_ptr --- dwl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 767b9af..4b0e144 100644 --- a/dwl.c +++ b/dwl.c @@ -251,9 +251,11 @@ static struct wl_list independents; static struct wlr_xdg_decoration_manager_v1 *xdeco_mgr; static struct wlr_cursor *cursor; -static struct wlr_xcursor *xcursor; 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; From 05df9201b3815b7cd8d639850513b80689dd6bfe Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Mon, 24 Aug 2020 07:04:34 +0200 Subject: [PATCH 003/173] layer shell initial attempt --- Makefile | 14 +- dwl.c | 365 ++++++++++++++++++++++ protocols/wlr-layer-shell-unstable-v1.xml | 311 ++++++++++++++++++ 3 files changed, 688 insertions(+), 2 deletions(-) create mode 100644 protocols/wlr-layer-shell-unstable-v1.xml diff --git a/Makefile b/Makefile index 56ab751..8022c9b 100644 --- a/Makefile +++ b/Makefile @@ -23,12 +23,22 @@ xdg-shell-protocol.c: xdg-shell-protocol.o: xdg-shell-protocol.h +wlr-layer-shell-unstable-v1-protocol.h: + $(WAYLAND_SCANNER) server-header \ + protocols/wlr-layer-shell-unstable-v1.xml $@ + +wlr-layer-shell-unstable-v1-protocol.c: + $(WAYLAND_SCANNER) private-code \ + protocols/wlr-layer-shell-unstable-v1.xml $@ + +wlr-layer-shell-unstable-v1-protocol.o: wlr-layer-shell-unstable-v1-protocol.h + config.h: | config.def.h cp config.def.h $@ -dwl.o: config.h xdg-shell-protocol.h +dwl.o: config.h xdg-shell-protocol.h wlr-layer-shell-unstable-v1-protocol.h -dwl: xdg-shell-protocol.o +dwl: xdg-shell-protocol.o wlr-layer-shell-unstable-v1-protocol.o clean: rm -f dwl *.o xdg-shell-protocol.h xdg-shell-protocol.c diff --git a/dwl.c b/dwl.c index 730e46a..e305da5 100644 --- a/dwl.c +++ b/dwl.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -128,6 +129,19 @@ typedef struct { struct wl_listener destroy; } Keyboard; +typedef struct { + struct wlr_layer_surface_v1 *layer_surface; + struct wl_list link; + + struct wl_listener destroy; + struct wl_listener map; + struct wl_listener unmap; + struct wl_listener surface_commit; + + struct wlr_box geo; + enum zwlr_layer_shell_v1_layer layer; +} Layer; + typedef struct { const char *symbol; void (*arrange)(Monitor *); @@ -140,6 +154,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]; // Layer::link const Layout *lt[2]; unsigned int seltags; unsigned int sellt; @@ -175,21 +190,30 @@ struct render_data { /* 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); static void applyrules(Client *c); static void arrange(Monitor *m); +static void arrangelayer(Monitor *m, struct wl_list *list, + struct wlr_box *usablearea, bool exclusive); +static void arrangelayers(Monitor *m); static void axisnotify(struct wl_listener *listener, void *data); static void buttonpress(struct wl_listener *listener, void *data); static void chvt(const Arg *arg); static void cleanup(void); static void cleanupkeyboard(struct wl_listener *listener, void *data); static void cleanupmon(struct wl_listener *listener, void *data); +static void commitlayernotify(struct wl_listener *listener, void *data); static void commitnotify(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 createlayer(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 destroylayernotify(struct wl_listener *listener, void *data); static void destroynotify(struct wl_listener *listener, void *data); static void destroyxdeco(struct wl_listener *listener, void *data); static Monitor *dirtomon(int dir); @@ -204,6 +228,7 @@ static int keybinding(uint32_t mods, xkb_keysym_t sym); static void keypress(struct wl_listener *listener, void *data); static void keypressmod(struct wl_listener *listener, void *data); static void killclient(const Arg *arg); +static void maplayernotify(struct wl_listener *listener, void *data); static void maprequest(struct wl_listener *listener, void *data); static void monocle(Monitor *m); static void motionabsolute(struct wl_listener *listener, void *data); @@ -236,7 +261,9 @@ static void tile(Monitor *m); static void togglefloating(const Arg *arg); static void toggletag(const Arg *arg); static void toggleview(const Arg *arg); +static void unmaplayer(Layer *layer); static void unmapnotify(struct wl_listener *listener, void *data); +static void unmaplayernotify(struct wl_listener *listener, void *data); static void view(const Arg *arg); static Client *xytoclient(double x, double y); static Monitor *xytomon(double x, double y); @@ -255,6 +282,7 @@ static struct wl_list fstack; /* focus order */ static struct wl_list stack; /* stacking z-order */ static struct wl_list independents; static struct wlr_xdg_decoration_manager_v1 *xdeco_mgr; +static struct wlr_layer_shell_v1 *layer_shell; static struct wlr_cursor *cursor; static struct wlr_xcursor_manager *cursor_mgr; @@ -280,6 +308,7 @@ static struct wl_listener new_input = {.notify = inputdevice}; 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 = createlayer}; 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}; @@ -322,6 +351,71 @@ applybounds(Client *c, struct wlr_box *bbox) 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) { + struct { + uint32_t singular_anchor; + uint32_t anchor_triplet; + int *positive_axis; + int *negative_axis; + int margin; + } edges[4]; + + if (exclusive <= 0) + return; + + // Top + edges[0].singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP; + edges[0].anchor_triplet = + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP; + edges[0].positive_axis = &usable_area->y; + edges[0].negative_axis = &usable_area->height; + edges[0].margin = margin_top; + // Bottom + edges[1].singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; + edges[1].anchor_triplet = + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; + edges[1].positive_axis = NULL; + edges[1].negative_axis = &usable_area->height; + edges[1].margin = margin_bottom; + // Left + edges[2].singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT; + edges[2].anchor_triplet = + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; + edges[2].positive_axis = &usable_area->x; + edges[2].negative_axis = &usable_area->width; + edges[2].margin = margin_left; + // Right + edges[3].singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + edges[3].anchor_triplet = + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; + edges[3].positive_axis = NULL; + edges[3].negative_axis = &usable_area->width; + edges[3].margin = margin_right; + + 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) { @@ -373,6 +467,152 @@ arrange(Monitor *m) /* XXX recheck pointer focus here... or in resize()? */ } +void +arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, bool exclusive) +{ + Layer *layer; + struct wlr_box full_area = { 0 }; + wlr_output_effective_resolution(m->wlr_output, + &full_area.width, &full_area.height); + + wl_list_for_each(layer, list, link) { + struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface; + struct wlr_layer_surface_v1_state *state = &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_close(layer_surface); + continue; + } + layer->geo = box; + + applyexclusive(usable_area, state->anchor, state->exclusive_zone, + state->margin.top, state->margin.right, + state->margin.bottom, state->margin.left); + wlr_layer_surface_v1_configure(layer_surface, box.width, box.height); + } +} + +void +arrangelayers(Monitor *m) +{ + struct wlr_box usable_area = { 0 }; + uint32_t layers_above_shell[] = { + ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, + ZWLR_LAYER_SHELL_V1_LAYER_TOP, + }; + size_t nlayers = LENGTH(layers_above_shell); + Layer *layer, *topmost = NULL; + + wlr_output_effective_resolution(m->wlr_output, + &usable_area.width, &usable_area.height); + + // Arrange exclusive surfaces from top->bottom + arrangelayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], + &usable_area, true); + arrangelayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], + &usable_area, true); + arrangelayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], + &usable_area, true); + arrangelayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], + &usable_area, true); + + /* XXX does wlr_output_effective_resolution return the same dimensions + as wlr_output_layout_get_box which is used to set m->m ? */ + if (memcmp(&usable_area, &m->w, sizeof(struct wlr_box))) { + memcpy(&m->w, &usable_area, sizeof(struct wlr_box)); + arrange(m); + } + + // Arrange non-exlusive surfaces from top->bottom + arrangelayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], + &usable_area, false); + arrangelayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], + &usable_area, false); + arrangelayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], + &usable_area, false); + arrangelayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], + &usable_area, false); + + // Find topmost keyboard interactive layer, if such a layer exists + for (size_t i = 0; i < nlayers; ++i) { + wl_list_for_each_reverse(layer, + &m->layers[layers_above_shell[i]], link) { + if (layer->layer_surface->current.keyboard_interactive && + layer->layer_surface->mapped) { + topmost = layer; + break; + } + } + if (topmost) { + break; + } + } + + if (topmost) + wlr_seat_keyboard_notify_enter(seat, + topmost->layer_surface->surface, NULL, 0, NULL); + else if ( + seat->keyboard_state.focused_surface + && wlr_surface_is_layer_surface(seat->keyboard_state.focused_surface) + && !wlr_layer_surface_v1_from_wlr_surface(seat->keyboard_state.focused_surface) + ->current.keyboard_interactive + ) + wlr_seat_keyboard_notify_clear_focus(seat); +} + void axisnotify(struct wl_listener *listener, void *data) { @@ -470,6 +710,28 @@ cleanupmon(struct wl_listener *listener, void *data) free(m); } +void +commitlayernotify(struct wl_listener *listener, void *data) +{ + Layer *layer = wl_container_of(listener, layer, surface_commit); + struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface; + struct wlr_output *wlr_output = layer_surface->output; + Monitor *m; + + if (!wlr_output) + return; + + m = wlr_output->data; + arrangelayers(m); + + if (layer->layer != layer_surface->current.layer) { + wl_list_remove(&layer->link); + wl_list_insert(&m->layers[layer_surface->current.layer], + &layer->link); + layer->layer = layer_surface->current.layer; + } +} + void commitnotify(struct wl_listener *listener, void *data) { @@ -522,6 +784,7 @@ createmon(struct wl_listener *listener, void *data) struct wlr_output *wlr_output = data; Monitor *m; const MonitorRule *r; + size_t len; /* The mode is a tuple of (width, height, refresh rate), and each * monitor supports only a specific set of modes. We just pick the @@ -567,6 +830,11 @@ createmon(struct wl_listener *listener, void *data) */ wlr_output_layout_add_auto(output_layout, wlr_output); sgeom = *wlr_output_layout_get_box(output_layout, NULL); + + len = LENGTH(m->layers); + for (size_t i = 0; i < len; ++i) { + wl_list_init(&m->layers[i]); + } } void @@ -600,6 +868,48 @@ createnotify(struct wl_listener *listener, void *data) wl_signal_add(&xdg_surface->events.destroy, &c->destroy); } +void +createlayer(struct wl_listener *listener, void *data) +{ + struct wlr_layer_surface_v1 *layer_surface = data; + Layer *layer; + Monitor *m; + struct wlr_layer_surface_v1_state old_state; + + if (!layer_surface->output) { + layer_surface->output = selmon->wlr_output; + } + + layer = calloc(1, sizeof(Layer)); + if (!layer) + return; + + layer->surface_commit.notify = commitlayernotify; + wl_signal_add(&layer_surface->surface->events.commit, + &layer->surface_commit); + + layer->destroy.notify = destroylayernotify; + wl_signal_add(&layer_surface->events.destroy, &layer->destroy); + layer->map.notify = maplayernotify; + wl_signal_add(&layer_surface->events.map, &layer->map); + layer->unmap.notify = unmaplayernotify; + wl_signal_add(&layer_surface->events.unmap, &layer->unmap); + + layer->layer_surface = layer_surface; + layer_surface->data = layer; + + m = layer_surface->output->data; + + wl_list_insert(&m->layers[layer_surface->client_pending.layer], &layer->link); + + // Temporarily set the layer's current state to client_pending + // so that we can easily arrange it + old_state = layer_surface->current; + layer_surface->current = layer_surface->client_pending; + arrangelayers(m); + layer_surface->current = old_state; +} + void createpointer(struct wlr_input_device *device) { @@ -636,6 +946,28 @@ cursorframe(struct wl_listener *listener, void *data) wlr_seat_pointer_notify_frame(seat); } +void +destroylayernotify(struct wl_listener *listener, void *data) +{ + Layer *layer = wl_container_of(listener, layer, destroy); + Monitor *m; + + if (layer->layer_surface->mapped) + unmaplayer(layer); + wl_list_remove(&layer->link); + wl_list_remove(&layer->destroy.link); + wl_list_remove(&layer->map.link); + wl_list_remove(&layer->unmap.link); + wl_list_remove(&layer->surface_commit.link); + if (layer->layer_surface->output) { + m = layer->layer_surface->output->data; + if (m) + arrangelayers(m); + layer->layer_surface->output = NULL; + } + free(layer); +} + void destroynotify(struct wl_listener *listener, void *data) { @@ -901,6 +1233,14 @@ killclient(const Arg *arg) wlr_xdg_toplevel_send_close(sel->surface.xdg); } +void +maplayernotify(struct wl_listener *listener, void *data) +{ + Layer *layer = wl_container_of(listener, layer, map); + wlr_surface_send_enter(layer->layer_surface->surface, layer->layer_surface->output); + // XXX recheck pointer focus +} + void maprequest(struct wl_listener *listener, void *data) { @@ -1521,6 +1861,10 @@ setup(void) wl_list_init(&fstack); wl_list_init(&stack); wl_list_init(&independents); + + 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); @@ -1703,6 +2047,20 @@ toggleview(const Arg *arg) } } +void +unmaplayer(Layer *layer) +{ + if ( + seat->keyboard_state.focused_surface + && wlr_surface_is_layer_surface(seat->keyboard_state.focused_surface) + && !wlr_layer_surface_v1_from_wlr_surface(seat->keyboard_state.focused_surface) + ->current.keyboard_interactive + ) + wlr_seat_keyboard_notify_clear_focus(seat); + + // XXX recheck pointer focus +} + void unmapnotify(struct wl_listener *listener, void *data) { @@ -1718,6 +2076,13 @@ unmapnotify(struct wl_listener *listener, void *data) wl_list_remove(&c->slink); } +void +unmaplayernotify(struct wl_listener *listener, void *data) +{ + Layer *layer = wl_container_of(listener, layer, unmap); + unmaplayer(layer); +} + void view(const Arg *arg) { diff --git a/protocols/wlr-layer-shell-unstable-v1.xml b/protocols/wlr-layer-shell-unstable-v1.xml new file mode 100644 index 0000000..fa67001 --- /dev/null +++ b/protocols/wlr-layer-shell-unstable-v1.xml @@ -0,0 +1,311 @@ + + + + Copyright © 2017 Drew DeVault + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + + Clients can use this interface to assign the surface_layer role to + wl_surfaces. Such surfaces are assigned to a "layer" of the output and + rendered with a defined z-depth respective to each other. They may also be + anchored to the edges and corners of a screen and specify input handling + semantics. This interface should be suitable for the implementation of + many desktop shell components, and a broad number of other applications + that interact with the desktop. + + + + + Create a layer surface for an existing surface. This assigns the role of + layer_surface, or raises a protocol error if another role is already + assigned. + + Creating a layer surface from a wl_surface which has a buffer attached + or committed is a client error, and any attempts by a client to attach + or manipulate a buffer prior to the first layer_surface.configure call + must also be treated as errors. + + You may pass NULL for output to allow the compositor to decide which + output to use. Generally this will be the one that the user most + recently interacted with. + + Clients can specify a namespace that defines the purpose of the layer + surface. + + + + + + + + + + + + + + + + + These values indicate which layers a surface can be rendered in. They + are ordered by z depth, bottom-most first. Traditional shell surfaces + will typically be rendered between the bottom and top layers. + Fullscreen shell surfaces are typically rendered at the top layer. + Multiple surfaces can share a single layer, and ordering within a + single layer is undefined. + + + + + + + + + + + + + This request indicates that the client will not use the layer_shell + object any more. Objects that have been created through this instance + are not affected. + + + + + + + An interface that may be implemented by a wl_surface, for surfaces that + are designed to be rendered as a layer of a stacked desktop-like + environment. + + Layer surface state (layer, size, anchor, exclusive zone, + margin, interactivity) is double-buffered, and will be applied at the + time wl_surface.commit of the corresponding wl_surface is called. + + + + + Sets the size of the surface in surface-local coordinates. The + compositor will display the surface centered with respect to its + anchors. + + If you pass 0 for either value, the compositor will assign it and + inform you of the assignment in the configure event. You must set your + anchor to opposite edges in the dimensions you omit; not doing so is a + protocol error. Both values are 0 by default. + + Size is double-buffered, see wl_surface.commit. + + + + + + + + Requests that the compositor anchor the surface to the specified edges + and corners. If two orthogonal edges are specified (e.g. 'top' and + 'left'), then the anchor point will be the intersection of the edges + (e.g. the top left corner of the output); otherwise the anchor point + will be centered on that edge, or in the center if none is specified. + + Anchor is double-buffered, see wl_surface.commit. + + + + + + + Requests that the compositor avoids occluding an area with other + surfaces. The compositor's use of this information is + implementation-dependent - do not assume that this region will not + actually be occluded. + + A positive value is only meaningful if the surface is anchored to one + edge or an edge and both perpendicular edges. If the surface is not + anchored, anchored to only two perpendicular edges (a corner), anchored + to only two parallel edges or anchored to all edges, a positive value + will be treated the same as zero. + + A positive zone is the distance from the edge in surface-local + coordinates to consider exclusive. + + Surfaces that do not wish to have an exclusive zone may instead specify + how they should interact with surfaces that do. If set to zero, the + surface indicates that it would like to be moved to avoid occluding + surfaces with a positive exclusive zone. If set to -1, the surface + indicates that it would not like to be moved to accommodate for other + surfaces, and the compositor should extend it all the way to the edges + it is anchored to. + + For example, a panel might set its exclusive zone to 10, so that + maximized shell surfaces are not shown on top of it. A notification + might set its exclusive zone to 0, so that it is moved to avoid + occluding the panel, but shell surfaces are shown underneath it. A + wallpaper or lock screen might set their exclusive zone to -1, so that + they stretch below or over the panel. + + The default value is 0. + + Exclusive zone is double-buffered, see wl_surface.commit. + + + + + + + Requests that the surface be placed some distance away from the anchor + point on the output, in surface-local coordinates. Setting this value + for edges you are not anchored to has no effect. + + The exclusive zone includes the margin. + + Margin is double-buffered, see wl_surface.commit. + + + + + + + + + + Set to 1 to request that the seat send keyboard events to this layer + surface. For layers below the shell surface layer, the seat will use + normal focus semantics. For layers above the shell surface layers, the + seat will always give exclusive keyboard focus to the top-most layer + which has keyboard interactivity set to true. + + Layer surfaces receive pointer, touch, and tablet events normally. If + you do not want to receive them, set the input region on your surface + to an empty region. + + Events is double-buffered, see wl_surface.commit. + + + + + + + This assigns an xdg_popup's parent to this layer_surface. This popup + should have been created via xdg_surface::get_popup with the parent set + to NULL, and this request must be invoked before committing the popup's + initial state. + + See the documentation of xdg_popup for more details about what an + xdg_popup is and how it is used. + + + + + + + When a configure event is received, if a client commits the + surface in response to the configure event, then the client + must make an ack_configure request sometime before the commit + request, passing along the serial of the configure event. + + If the client receives multiple configure events before it + can respond to one, it only has to ack the last configure event. + + A client is not required to commit immediately after sending + an ack_configure request - it may even ack_configure several times + before its next surface commit. + + A client may send multiple ack_configure requests before committing, but + only the last request sent before a commit indicates which configure + event the client really is responding to. + + + + + + + This request destroys the layer surface. + + + + + + The configure event asks the client to resize its surface. + + Clients should arrange their surface for the new states, and then send + an ack_configure request with the serial sent in this configure event at + some point before committing the new surface. + + The client is free to dismiss all but the last configure event it + received. + + The width and height arguments specify the size of the window in + surface-local coordinates. + + The size is a hint, in the sense that the client is free to ignore it if + it doesn't resize, pick a smaller size (to satisfy aspect ratio or + resize in steps of NxM pixels). If the client picks a smaller size and + is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the + surface will be centered on this axis. + + If the width or height arguments are zero, it means the client should + decide its own window dimension. + + + + + + + + + The closed event is sent by the compositor when the surface will no + longer be shown. The output may have been destroyed or the user may + have asked for it to be removed. Further changes to the surface will be + ignored. The client should destroy the resource after receiving this + event, and create a new surface if they so choose. + + + + + + + + + + + + + + + + + + + + + Change the layer that the surface is rendered on. + + Layer is double-buffered, see wl_surface.commit. + + + + + From 1e2dde66747e6fd542e52b2066bd96d42be919c6 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Wed, 26 Aug 2020 17:33:17 +0200 Subject: [PATCH 004/173] improve naming rename Layer to LayerSurface; layer should refer to overlay, top, bottom or background LayerSurface variables are always called layersurface wlr_layer_surface_v1 variables are always called wlr_layer_surface --- dwl.c | 125 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 63 insertions(+), 62 deletions(-) diff --git a/dwl.c b/dwl.c index e305da5..a72b50f 100644 --- a/dwl.c +++ b/dwl.c @@ -140,7 +140,7 @@ typedef struct { struct wlr_box geo; enum zwlr_layer_shell_v1_layer layer; -} Layer; +} LayerSurface; typedef struct { const char *symbol; @@ -154,7 +154,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]; // Layer::link + struct wl_list layers[4]; // LayerSurface::link const Layout *lt[2]; unsigned int seltags; unsigned int sellt; @@ -261,7 +261,7 @@ static void tile(Monitor *m); static void togglefloating(const Arg *arg); static void toggletag(const Arg *arg); static void toggleview(const Arg *arg); -static void unmaplayer(Layer *layer); +static void unmaplayer(LayerSurface *layersurface); static void unmapnotify(struct wl_listener *listener, void *data); static void unmaplayernotify(struct wl_listener *listener, void *data); static void view(const Arg *arg); @@ -470,14 +470,14 @@ arrange(Monitor *m) void arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, bool exclusive) { - Layer *layer; + LayerSurface *layersurface; struct wlr_box full_area = { 0 }; wlr_output_effective_resolution(m->wlr_output, &full_area.width, &full_area.height); - wl_list_for_each(layer, list, link) { - struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface; - struct wlr_layer_surface_v1_state *state = &layer_surface->current; + 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, @@ -533,15 +533,15 @@ arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, bool box.y -= state->margin.bottom; } if (box.width < 0 || box.height < 0) { - wlr_layer_surface_v1_close(layer_surface); + wlr_layer_surface_v1_close(wlr_layer_surface); continue; } - layer->geo = box; + layersurface->geo = box; applyexclusive(usable_area, state->anchor, state->exclusive_zone, state->margin.top, state->margin.right, state->margin.bottom, state->margin.left); - wlr_layer_surface_v1_configure(layer_surface, box.width, box.height); + wlr_layer_surface_v1_configure(wlr_layer_surface, box.width, box.height); } } @@ -554,7 +554,7 @@ arrangelayers(Monitor *m) ZWLR_LAYER_SHELL_V1_LAYER_TOP, }; size_t nlayers = LENGTH(layers_above_shell); - Layer *layer, *topmost = NULL; + LayerSurface *layersurface, *topmost = NULL; wlr_output_effective_resolution(m->wlr_output, &usable_area.width, &usable_area.height); @@ -588,11 +588,11 @@ arrangelayers(Monitor *m) // Find topmost keyboard interactive layer, if such a layer exists for (size_t i = 0; i < nlayers; ++i) { - wl_list_for_each_reverse(layer, + wl_list_for_each_reverse(layersurface, &m->layers[layers_above_shell[i]], link) { - if (layer->layer_surface->current.keyboard_interactive && - layer->layer_surface->mapped) { - topmost = layer; + if (layersurface->layer_surface->current.keyboard_interactive && + layersurface->layer_surface->mapped) { + topmost = layersurface; break; } } @@ -713,9 +713,9 @@ cleanupmon(struct wl_listener *listener, void *data) void commitlayernotify(struct wl_listener *listener, void *data) { - Layer *layer = wl_container_of(listener, layer, surface_commit); - struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface; - struct wlr_output *wlr_output = layer_surface->output; + LayerSurface *layersurface = wl_container_of(listener, layersurface, surface_commit); + struct wlr_layer_surface_v1 *wlr_layer_surface = layersurface->layer_surface; + struct wlr_output *wlr_output = wlr_layer_surface->output; Monitor *m; if (!wlr_output) @@ -724,11 +724,11 @@ commitlayernotify(struct wl_listener *listener, void *data) m = wlr_output->data; arrangelayers(m); - if (layer->layer != layer_surface->current.layer) { - wl_list_remove(&layer->link); - wl_list_insert(&m->layers[layer_surface->current.layer], - &layer->link); - layer->layer = layer_surface->current.layer; + if (layersurface->layer != wlr_layer_surface->current.layer) { + wl_list_remove(&layersurface->link); + wl_list_insert(&m->layers[wlr_layer_surface->current.layer], + &layersurface->link); + layersurface->layer = wlr_layer_surface->current.layer; } } @@ -871,43 +871,44 @@ createnotify(struct wl_listener *listener, void *data) void createlayer(struct wl_listener *listener, void *data) { - struct wlr_layer_surface_v1 *layer_surface = data; - Layer *layer; + struct wlr_layer_surface_v1 *wlr_layer_surface = data; + LayerSurface *layersurface; Monitor *m; struct wlr_layer_surface_v1_state old_state; - if (!layer_surface->output) { - layer_surface->output = selmon->wlr_output; + if (!wlr_layer_surface->output) { + wlr_layer_surface->output = selmon->wlr_output; } - layer = calloc(1, sizeof(Layer)); - if (!layer) + layersurface = calloc(1, sizeof(LayerSurface)); + if (!layersurface) return; - layer->surface_commit.notify = commitlayernotify; - wl_signal_add(&layer_surface->surface->events.commit, - &layer->surface_commit); + layersurface->surface_commit.notify = commitlayernotify; + wl_signal_add(&wlr_layer_surface->surface->events.commit, + &layersurface->surface_commit); - layer->destroy.notify = destroylayernotify; - wl_signal_add(&layer_surface->events.destroy, &layer->destroy); - layer->map.notify = maplayernotify; - wl_signal_add(&layer_surface->events.map, &layer->map); - layer->unmap.notify = unmaplayernotify; - wl_signal_add(&layer_surface->events.unmap, &layer->unmap); + layersurface->destroy.notify = destroylayernotify; + wl_signal_add(&wlr_layer_surface->events.destroy, &layersurface->destroy); + layersurface->map.notify = maplayernotify; + wl_signal_add(&wlr_layer_surface->events.map, &layersurface->map); + layersurface->unmap.notify = unmaplayernotify; + wl_signal_add(&wlr_layer_surface->events.unmap, &layersurface->unmap); - layer->layer_surface = layer_surface; - layer_surface->data = layer; + layersurface->layer_surface = wlr_layer_surface; + wlr_layer_surface->data = layersurface; - m = layer_surface->output->data; + m = wlr_layer_surface->output->data; - wl_list_insert(&m->layers[layer_surface->client_pending.layer], &layer->link); + wl_list_insert(&m->layers[wlr_layer_surface->client_pending.layer], + &layersurface->link); // Temporarily set the layer's current state to client_pending // so that we can easily arrange it - old_state = layer_surface->current; - layer_surface->current = layer_surface->client_pending; + old_state = wlr_layer_surface->current; + wlr_layer_surface->current = wlr_layer_surface->client_pending; arrangelayers(m); - layer_surface->current = old_state; + wlr_layer_surface->current = old_state; } void @@ -949,23 +950,23 @@ cursorframe(struct wl_listener *listener, void *data) void destroylayernotify(struct wl_listener *listener, void *data) { - Layer *layer = wl_container_of(listener, layer, destroy); + LayerSurface *layersurface = wl_container_of(listener, layersurface, destroy); Monitor *m; - if (layer->layer_surface->mapped) - unmaplayer(layer); - wl_list_remove(&layer->link); - wl_list_remove(&layer->destroy.link); - wl_list_remove(&layer->map.link); - wl_list_remove(&layer->unmap.link); - wl_list_remove(&layer->surface_commit.link); - if (layer->layer_surface->output) { - m = layer->layer_surface->output->data; + if (layersurface->layer_surface->mapped) + unmaplayer(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) { + m = layersurface->layer_surface->output->data; if (m) arrangelayers(m); - layer->layer_surface->output = NULL; + layersurface->layer_surface->output = NULL; } - free(layer); + free(layersurface); } void @@ -1236,8 +1237,8 @@ killclient(const Arg *arg) void maplayernotify(struct wl_listener *listener, void *data) { - Layer *layer = wl_container_of(listener, layer, map); - wlr_surface_send_enter(layer->layer_surface->surface, layer->layer_surface->output); + LayerSurface *layersurface = wl_container_of(listener, layersurface, map); + wlr_surface_send_enter(layersurface->layer_surface->surface, layersurface->layer_surface->output); // XXX recheck pointer focus } @@ -2048,7 +2049,7 @@ toggleview(const Arg *arg) } void -unmaplayer(Layer *layer) +unmaplayer(LayerSurface *layersurface) { if ( seat->keyboard_state.focused_surface @@ -2079,8 +2080,8 @@ unmapnotify(struct wl_listener *listener, void *data) void unmaplayernotify(struct wl_listener *listener, void *data) { - Layer *layer = wl_container_of(listener, layer, unmap); - unmaplayer(layer); + LayerSurface *layersurface = wl_container_of(listener, layersurface, unmap); + unmaplayer(layersurface); } void From b35182f5192b14f0b7869d3783db728919cc527a Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Wed, 26 Aug 2020 19:00:40 +0200 Subject: [PATCH 005/173] render layer surfaces --- dwl.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/dwl.c b/dwl.c index a72b50f..c59196d 100644 --- a/dwl.c +++ b/dwl.c @@ -240,6 +240,8 @@ static void pointerfocus(Client *c, struct wlr_surface *surface, static void quit(const Arg *arg); static void render(struct wlr_surface *surface, int sx, int sy, void *data); static void renderclients(Monitor *m, struct timespec *now); +static void renderlayer(Monitor *m, struct wl_list *layer_surfaces); +static void renderlayersurface(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); @@ -1555,6 +1557,43 @@ renderclients(Monitor *m, struct timespec *now) } } +void +renderlayer(Monitor *m, struct wl_list *layer_surfaces) +{ + LayerSurface *layersurface; + wl_list_for_each(layersurface, layer_surfaces, link) + wlr_surface_for_each_surface(layersurface->layer_surface->surface, + renderlayersurface, layersurface); +} + +void +renderlayersurface(struct wlr_surface *surface, int sx, int sy, void *data) +{ + LayerSurface *layersurface = data; + struct wlr_texture *texture = wlr_surface_get_texture(surface); + struct wlr_output *output; + double ox = 0, oy = 0; + enum wl_output_transform transform; + struct wlr_box box; + float matrix[9]; + struct timespec now; + + if (!texture) { + return; + } + + output = layersurface->layer_surface->output; + wlr_output_layout_output_coords(output_layout, output, &ox, &oy); + ox += layersurface->geo.x + sx, oy += layersurface->geo.y + sy; + transform = wlr_output_transform_invert(surface->current.transform); + memcpy(&box, &layersurface->geo, sizeof(struct wlr_box)); + wlr_matrix_project_box(matrix, &box, transform, 0, + output->transform_matrix); + wlr_render_texture_with_matrix(drw, texture, matrix, 1); + clock_gettime(CLOCK_MONOTONIC, &now); + wlr_surface_send_frame_done(surface, &now); +} + void rendermon(struct wl_listener *listener, void *data) { @@ -1585,7 +1624,11 @@ rendermon(struct wl_listener *listener, void *data) wlr_renderer_begin(drw, m->wlr_output->width, m->wlr_output->height); wlr_renderer_clear(drw, rootcolor); + renderlayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); + renderlayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); renderclients(m, &now); + renderlayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); + renderlayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); #ifdef XWAYLAND renderindependents(m->wlr_output, &now); #endif From e69c3d7336cb9789582f2faab117d9cfa03e9cc8 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Wed, 26 Aug 2020 19:16:13 +0200 Subject: [PATCH 006/173] remove extra space --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index c59196d..844cd26 100644 --- a/dwl.c +++ b/dwl.c @@ -407,7 +407,7 @@ applyexclusive(struct wlr_box *usable_area, edges[3].margin = margin_right; for (size_t i = 0; i < LENGTH(edges); ++i) { - if ((anchor == edges[i].singular_anchor || anchor == edges[i].anchor_triplet) + 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; From ce7bc8125d5739d38766c93e2630cd2696a4b415 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Wed, 26 Aug 2020 19:41:45 +0200 Subject: [PATCH 007/173] rename functions too --- dwl.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/dwl.c b/dwl.c index 844cd26..efe2dda 100644 --- a/dwl.c +++ b/dwl.c @@ -204,16 +204,16 @@ static void chvt(const Arg *arg); static void cleanup(void); static void cleanupkeyboard(struct wl_listener *listener, void *data); static void cleanupmon(struct wl_listener *listener, void *data); -static void commitlayernotify(struct wl_listener *listener, void *data); +static void commitlayersurfacenotify(struct wl_listener *listener, void *data); static void commitnotify(struct wl_listener *listener, void *data); static void createkeyboard(struct wlr_input_device *device); static void createmon(struct wl_listener *listener, void *data); static void createnotify(struct wl_listener *listener, void *data); -static void createlayer(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 destroylayernotify(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(int dir); @@ -228,7 +228,7 @@ static int keybinding(uint32_t mods, xkb_keysym_t sym); static void keypress(struct wl_listener *listener, void *data); static void keypressmod(struct wl_listener *listener, void *data); static void killclient(const Arg *arg); -static void maplayernotify(struct wl_listener *listener, void *data); +static void maplayersurfacenotify(struct wl_listener *listener, void *data); static void maprequest(struct wl_listener *listener, void *data); static void monocle(Monitor *m); static void motionabsolute(struct wl_listener *listener, void *data); @@ -263,9 +263,9 @@ static void tile(Monitor *m); static void togglefloating(const Arg *arg); static void toggletag(const Arg *arg); static void toggleview(const Arg *arg); -static void unmaplayer(LayerSurface *layersurface); +static void unmaplayersurface(LayerSurface *layersurface); static void unmapnotify(struct wl_listener *listener, void *data); -static void unmaplayernotify(struct wl_listener *listener, void *data); +static void unmaplayersurfacenotify(struct wl_listener *listener, void *data); static void view(const Arg *arg); static Client *xytoclient(double x, double y); static Monitor *xytomon(double x, double y); @@ -310,7 +310,7 @@ static struct wl_listener new_input = {.notify = inputdevice}; 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 = createlayer}; +static struct wl_listener new_layer_shell_surface = {.notify = createlayersurface}; 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}; @@ -713,7 +713,7 @@ cleanupmon(struct wl_listener *listener, void *data) } void -commitlayernotify(struct wl_listener *listener, void *data) +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; @@ -871,7 +871,7 @@ createnotify(struct wl_listener *listener, void *data) } void -createlayer(struct wl_listener *listener, void *data) +createlayersurface(struct wl_listener *listener, void *data) { struct wlr_layer_surface_v1 *wlr_layer_surface = data; LayerSurface *layersurface; @@ -886,15 +886,15 @@ createlayer(struct wl_listener *listener, void *data) if (!layersurface) return; - layersurface->surface_commit.notify = commitlayernotify; + layersurface->surface_commit.notify = commitlayersurfacenotify; wl_signal_add(&wlr_layer_surface->surface->events.commit, &layersurface->surface_commit); - layersurface->destroy.notify = destroylayernotify; + layersurface->destroy.notify = destroylayersurfacenotify; wl_signal_add(&wlr_layer_surface->events.destroy, &layersurface->destroy); - layersurface->map.notify = maplayernotify; + layersurface->map.notify = maplayersurfacenotify; wl_signal_add(&wlr_layer_surface->events.map, &layersurface->map); - layersurface->unmap.notify = unmaplayernotify; + layersurface->unmap.notify = unmaplayersurfacenotify; wl_signal_add(&wlr_layer_surface->events.unmap, &layersurface->unmap); layersurface->layer_surface = wlr_layer_surface; @@ -950,13 +950,13 @@ cursorframe(struct wl_listener *listener, void *data) } void -destroylayernotify(struct wl_listener *listener, void *data) +destroylayersurfacenotify(struct wl_listener *listener, void *data) { LayerSurface *layersurface = wl_container_of(listener, layersurface, destroy); Monitor *m; if (layersurface->layer_surface->mapped) - unmaplayer(layersurface); + unmaplayersurface(layersurface); wl_list_remove(&layersurface->link); wl_list_remove(&layersurface->destroy.link); wl_list_remove(&layersurface->map.link); @@ -1237,7 +1237,7 @@ killclient(const Arg *arg) } void -maplayernotify(struct wl_listener *listener, void *data) +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); @@ -2092,7 +2092,7 @@ toggleview(const Arg *arg) } void -unmaplayer(LayerSurface *layersurface) +unmaplayersurface(LayerSurface *layersurface) { if ( seat->keyboard_state.focused_surface @@ -2121,10 +2121,10 @@ unmapnotify(struct wl_listener *listener, void *data) } void -unmaplayernotify(struct wl_listener *listener, void *data) +unmaplayersurfacenotify(struct wl_listener *listener, void *data) { LayerSurface *layersurface = wl_container_of(listener, layersurface, unmap); - unmaplayer(layersurface); + unmaplayersurface(layersurface); } void From e13d19334614bfa3e2905a49103e6ad17bd2aabe Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Wed, 26 Aug 2020 19:52:03 +0200 Subject: [PATCH 008/173] correct variable name --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index efe2dda..3f10385 100644 --- a/dwl.c +++ b/dwl.c @@ -196,7 +196,7 @@ static void applyexclusive(struct wlr_box *usable_area, uint32_t anchor, static void applyrules(Client *c); static void arrange(Monitor *m); static void arrangelayer(Monitor *m, struct wl_list *list, - struct wlr_box *usablearea, bool exclusive); + struct wlr_box *usable_area, bool exclusive); static void arrangelayers(Monitor *m); static void axisnotify(struct wl_listener *listener, void *data); static void buttonpress(struct wl_listener *listener, void *data); From d61658bdd7704267ef7c6ffd7b965cfa8abdb445 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Wed, 26 Aug 2020 21:01:03 +0200 Subject: [PATCH 009/173] update make clean --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8022c9b..10c84b1 100644 --- a/Makefile +++ b/Makefile @@ -41,7 +41,7 @@ dwl.o: config.h xdg-shell-protocol.h wlr-layer-shell-unstable-v1-protocol.h dwl: xdg-shell-protocol.o wlr-layer-shell-unstable-v1-protocol.o clean: - rm -f dwl *.o xdg-shell-protocol.h xdg-shell-protocol.c + rm -f dwl *.o xdg-shell-protocol.h xdg-shell-protocol.c wlr-layer-shell-unstable-v1-protocol.h wlr-layer-shell-unstable-v1-protocol.c .DEFAULT_GOAL=dwl .PHONY: clean From b8a6d3e86a7c27929a84a8d3326a08c33f28454c Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Thu, 27 Aug 2020 05:03:01 +0200 Subject: [PATCH 010/173] render in the same order as sway --- dwl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index 3f10385..b4364a8 100644 --- a/dwl.c +++ b/dwl.c @@ -1627,11 +1627,11 @@ rendermon(struct wl_listener *listener, void *data) renderlayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); renderlayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); renderclients(m, &now); - renderlayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); - renderlayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); #ifdef XWAYLAND renderindependents(m->wlr_output, &now); #endif + renderlayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); + renderlayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); /* 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 From c9f92bdd0ed8163c55f56a49490c555a48dd0b6d Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Thu, 27 Aug 2020 05:22:27 +0200 Subject: [PATCH 011/173] set a monitor geometry only when creating it Lets layers with an exclusive area shrink the usable area --- dwl.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dwl.c b/dwl.c index b4364a8..d4170e5 100644 --- a/dwl.c +++ b/dwl.c @@ -461,9 +461,6 @@ applyrules(Client *c) void arrange(Monitor *m) { - /* Get effective monitor geometry to use for window area */ - m->m = *wlr_output_layout_get_box(output_layout, m->wlr_output); - m->w = m->m; if (m->lt[m->sellt]->arrange) m->lt[m->sellt]->arrange(m); /* XXX recheck pointer focus here... or in resize()? */ @@ -833,6 +830,10 @@ createmon(struct wl_listener *listener, void *data) wlr_output_layout_add_auto(output_layout, wlr_output); sgeom = *wlr_output_layout_get_box(output_layout, NULL); + /* Get effective monitor geometry to use for window area */ + m->m = *wlr_output_layout_get_box(output_layout, m->wlr_output); + m->w = m->m; + len = LENGTH(m->layers); for (size_t i = 0; i < len; ++i) { wl_list_init(&m->layers[i]); From 1473d0329369309ff2188016bdb133cedc4053a9 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Thu, 27 Aug 2020 06:04:57 +0200 Subject: [PATCH 012/173] remove comment wlr_output_layout_get_box internally calls wlr_output_effective_resolution --- dwl.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/dwl.c b/dwl.c index d4170e5..8007273 100644 --- a/dwl.c +++ b/dwl.c @@ -568,8 +568,6 @@ arrangelayers(Monitor *m) arrangelayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &usable_area, true); - /* XXX does wlr_output_effective_resolution return the same dimensions - as wlr_output_layout_get_box which is used to set m->m ? */ if (memcmp(&usable_area, &m->w, sizeof(struct wlr_box))) { memcpy(&m->w, &usable_area, sizeof(struct wlr_box)); arrange(m); From 03c020f0581af59f3eeeac3e54e174bf82883175 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Thu, 27 Aug 2020 06:39:44 +0200 Subject: [PATCH 013/173] reuse m->m --- dwl.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/dwl.c b/dwl.c index 8007273..d196ef6 100644 --- a/dwl.c +++ b/dwl.c @@ -547,7 +547,7 @@ arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, bool void arrangelayers(Monitor *m) { - struct wlr_box usable_area = { 0 }; + struct wlr_box usable_area = m->m; uint32_t layers_above_shell[] = { ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, ZWLR_LAYER_SHELL_V1_LAYER_TOP, @@ -555,9 +555,6 @@ arrangelayers(Monitor *m) size_t nlayers = LENGTH(layers_above_shell); LayerSurface *layersurface, *topmost = NULL; - wlr_output_effective_resolution(m->wlr_output, - &usable_area.width, &usable_area.height); - // Arrange exclusive surfaces from top->bottom arrangelayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], &usable_area, true); From 2c08d0b4213ede2b1156abb142254bfb869068d5 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Fri, 28 Aug 2020 09:00:03 +0200 Subject: [PATCH 014/173] Reuse render() and struct timespec *now --- dwl.c | 50 ++++++++++++++------------------------------------ 1 file changed, 14 insertions(+), 36 deletions(-) diff --git a/dwl.c b/dwl.c index d196ef6..4cca4f9 100644 --- a/dwl.c +++ b/dwl.c @@ -240,8 +240,7 @@ static void pointerfocus(Client *c, struct wlr_surface *surface, static void quit(const Arg *arg); static void render(struct wlr_surface *surface, int sx, int sy, void *data); static void renderclients(Monitor *m, struct timespec *now); -static void renderlayer(Monitor *m, struct wl_list *layer_surfaces); -static void renderlayersurface(struct wlr_surface *surface, int sx, int sy, void *data); +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); @@ -1554,40 +1553,19 @@ renderclients(Monitor *m, struct timespec *now) } void -renderlayer(Monitor *m, struct wl_list *layer_surfaces) +renderlayer(struct wl_list *layer_surfaces, struct timespec *now) { + struct render_data rdata; LayerSurface *layersurface; - wl_list_for_each(layersurface, layer_surfaces, link) + wl_list_for_each(layersurface, layer_surfaces, link) { + rdata.output = layersurface->layer_surface->output; + rdata.when = now; + rdata.x = layersurface->geo.x; + rdata.y = layersurface->geo.y; + wlr_surface_for_each_surface(layersurface->layer_surface->surface, - renderlayersurface, layersurface); -} - -void -renderlayersurface(struct wlr_surface *surface, int sx, int sy, void *data) -{ - LayerSurface *layersurface = data; - struct wlr_texture *texture = wlr_surface_get_texture(surface); - struct wlr_output *output; - double ox = 0, oy = 0; - enum wl_output_transform transform; - struct wlr_box box; - float matrix[9]; - struct timespec now; - - if (!texture) { - return; + render, &rdata); } - - output = layersurface->layer_surface->output; - wlr_output_layout_output_coords(output_layout, output, &ox, &oy); - ox += layersurface->geo.x + sx, oy += layersurface->geo.y + sy; - transform = wlr_output_transform_invert(surface->current.transform); - memcpy(&box, &layersurface->geo, sizeof(struct wlr_box)); - wlr_matrix_project_box(matrix, &box, transform, 0, - output->transform_matrix); - wlr_render_texture_with_matrix(drw, texture, matrix, 1); - clock_gettime(CLOCK_MONOTONIC, &now); - wlr_surface_send_frame_done(surface, &now); } void @@ -1620,14 +1598,14 @@ rendermon(struct wl_listener *listener, void *data) wlr_renderer_begin(drw, m->wlr_output->width, m->wlr_output->height); wlr_renderer_clear(drw, rootcolor); - renderlayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); - renderlayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); + 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, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); - renderlayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); + 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 From b237ea0e45cf9f4abfb35d7af609a4d0a7d1218f Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Fri, 28 Aug 2020 15:17:38 +0200 Subject: [PATCH 015/173] simplify make clean --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 10c84b1..3dc2336 100644 --- a/Makefile +++ b/Makefile @@ -41,7 +41,7 @@ dwl.o: config.h xdg-shell-protocol.h wlr-layer-shell-unstable-v1-protocol.h dwl: xdg-shell-protocol.o wlr-layer-shell-unstable-v1-protocol.o clean: - rm -f dwl *.o xdg-shell-protocol.h xdg-shell-protocol.c wlr-layer-shell-unstable-v1-protocol.h wlr-layer-shell-unstable-v1-protocol.c + rm -f dwl *.o *-protocol.h *-protocol.c .DEFAULT_GOAL=dwl .PHONY: clean From ddd3c2ad7ef15d14c920b0678a5a20755c07b4f4 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 29 Aug 2020 07:42:47 +0200 Subject: [PATCH 016/173] order variables more how like they are initialized --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 4cca4f9..f266ee2 100644 --- a/dwl.c +++ b/dwl.c @@ -282,8 +282,8 @@ 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_xdg_decoration_manager_v1 *xdeco_mgr; static struct wlr_layer_shell_v1 *layer_shell; +static struct wlr_xdg_decoration_manager_v1 *xdeco_mgr; static struct wlr_cursor *cursor; static struct wlr_xcursor_manager *cursor_mgr; From 3203d1bafd53cfbc84f5a6ab31d1438709e01d5d Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 29 Aug 2020 08:41:51 +0200 Subject: [PATCH 017/173] don't handle failed calloc be consistent with the rest of the code --- dwl.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/dwl.c b/dwl.c index f266ee2..a0772fe 100644 --- a/dwl.c +++ b/dwl.c @@ -878,8 +878,6 @@ createlayersurface(struct wl_listener *listener, void *data) } layersurface = calloc(1, sizeof(LayerSurface)); - if (!layersurface) - return; layersurface->surface_commit.notify = commitlayersurfacenotify; wl_signal_add(&wlr_layer_surface->surface->events.commit, From 5d4eadeb8e72fd0749d90714c010d1e9ac189f5d Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 29 Aug 2020 08:42:17 +0200 Subject: [PATCH 018/173] remove blank line --- dwl.c | 1 - 1 file changed, 1 deletion(-) diff --git a/dwl.c b/dwl.c index a0772fe..f9aec9c 100644 --- a/dwl.c +++ b/dwl.c @@ -882,7 +882,6 @@ createlayersurface(struct wl_listener *listener, void *data) layersurface->surface_commit.notify = commitlayersurfacenotify; wl_signal_add(&wlr_layer_surface->surface->events.commit, &layersurface->surface_commit); - layersurface->destroy.notify = destroylayersurfacenotify; wl_signal_add(&wlr_layer_surface->events.destroy, &layersurface->destroy); layersurface->map.notify = maplayersurfacenotify; From d74d40402624e17f408b07917916343611b8d778 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 29 Aug 2020 08:47:28 +0200 Subject: [PATCH 019/173] update README --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 3251ccf..fd83f92 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,6 @@ dwl is a work in progress, and it has not yet reached its feature goals in a num - XWayland support is new and could use testing - Urgent/attention/focus-request ([not yet supported](https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/9) by xdg-shell protocol) - Statusbar support (built-in or external) -- layer-shell - Damage tracking - Fullscreen/fixed windows (or whatever the Wayland analogues are) From b4d9a8662fc1e9524781af8fbcbd007705b63e8e Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Thu, 3 Sep 2020 18:34:41 +0200 Subject: [PATCH 020/173] send pressed keys to topmost layer surface --- dwl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index f9aec9c..08138a4 100644 --- a/dwl.c +++ b/dwl.c @@ -553,6 +553,7 @@ arrangelayers(Monitor *m) }; size_t nlayers = LENGTH(layers_above_shell); LayerSurface *layersurface, *topmost = NULL; + 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], @@ -595,8 +596,8 @@ arrangelayers(Monitor *m) } if (topmost) - wlr_seat_keyboard_notify_enter(seat, - topmost->layer_surface->surface, NULL, 0, NULL); + wlr_seat_keyboard_notify_enter(seat, topmost->layer_surface->surface, + kb->keycodes, kb->num_keycodes, &kb->modifiers); else if ( seat->keyboard_state.focused_surface && wlr_surface_is_layer_surface(seat->keyboard_state.focused_surface) From 4017a27b67574bc6eb68c964ad50fde8c3c1348c Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Thu, 3 Sep 2020 18:58:27 +0200 Subject: [PATCH 021/173] fix focus leave condition needs refactoring and testing --- dwl.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dwl.c b/dwl.c index 08138a4..ed5ec5b 100644 --- a/dwl.c +++ b/dwl.c @@ -2067,10 +2067,11 @@ void unmaplayersurface(LayerSurface *layersurface) { if ( - seat->keyboard_state.focused_surface + layersurface->layer_surface->current.keyboard_interactive + && seat->keyboard_state.focused_surface && wlr_surface_is_layer_surface(seat->keyboard_state.focused_surface) - && !wlr_layer_surface_v1_from_wlr_surface(seat->keyboard_state.focused_surface) - ->current.keyboard_interactive + && wlr_layer_surface_v1_from_wlr_surface(seat->keyboard_state.focused_surface) + == layersurface->layer_surface ) wlr_seat_keyboard_notify_clear_focus(seat); From b26ede4727ff5e7d9d6d22433edc2c51b7f109ba Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Thu, 3 Sep 2020 18:59:27 +0200 Subject: [PATCH 022/173] more TODOs --- dwl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index ed5ec5b..42e8198 100644 --- a/dwl.c +++ b/dwl.c @@ -605,6 +605,7 @@ arrangelayers(Monitor *m) ->current.keyboard_interactive ) wlr_seat_keyboard_notify_clear_focus(seat); + /* XXX recheck keyboard focus */ } void @@ -1234,7 +1235,7 @@ 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); - // XXX recheck pointer focus + /* XXX recheck pointer focus */ } void @@ -2075,7 +2076,7 @@ unmaplayersurface(LayerSurface *layersurface) ) wlr_seat_keyboard_notify_clear_focus(seat); - // XXX recheck pointer focus + /* XXX recheck keyboard and pointer focus */ } void From d98ca07a649407a2be8cae1e66265f1bba785b04 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Fri, 4 Sep 2020 07:38:14 +0200 Subject: [PATCH 023/173] enable pointer on layer surfaces --- dwl.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/dwl.c b/dwl.c index 42e8198..3b1b999 100644 --- a/dwl.c +++ b/dwl.c @@ -267,6 +267,8 @@ static void unmapnotify(struct wl_listener *listener, void *data); static void unmaplayersurfacenotify(struct wl_listener *listener, void *data); static void view(const Arg *arg); 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 Monitor *xytomon(double x, double y); static void zoom(const Arg *arg); @@ -1234,8 +1236,16 @@ void maplayersurfacenotify(struct wl_listener *listener, void *data) { LayerSurface *layersurface = wl_container_of(listener, layersurface, map); + double sx = 0.0, sy = 0.0; + struct wlr_surface *sub = wlr_layer_surface_v1_surface_at( + layersurface->layer_surface, + cursor->x - layersurface->geo.x, + cursor->y - layersurface->geo.y, + &sx, &sy); wlr_surface_send_enter(layersurface->layer_surface->surface, layersurface->layer_surface->output); - /* XXX recheck pointer focus */ + if (sub) + wlr_seat_pointer_notify_enter(seat, sub, sx, sy); + /* XXX check if the layer surface is below a client */ } void @@ -1306,7 +1316,7 @@ motionnotify(uint32_t time) { double sx = 0, sy = 0; struct wlr_surface *surface = NULL; - Client *c; + Client *c = NULL; /* Update selmon (even while dragging a window) */ if (sloppyfocus) @@ -1325,17 +1335,23 @@ 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. */ - if ((c = xytoindependent(cursor->x, cursor->y))) { + 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. */ - } else + } #endif - if ((c = xytoclient(cursor->x, cursor->y))) { + else if ((c = xytoclient(cursor->x, cursor->y))) { #ifdef XWAYLAND if (c->type != XDGShell) surface = wlr_surface_surface_at(c->surface.xwayland->surface, @@ -1347,6 +1363,13 @@ motionnotify(uint32_t time) 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 if ((surface = xytolayersurface(&selmon->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], + cursor->x, cursor->y, &sx, &sy))) { // gcc complains without these braces + ; + } /* 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. */ @@ -1424,6 +1447,9 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, * of its surfaces, and make keyboard focus follow if desired. */ wlr_seat_pointer_notify_enter(seat, surface, sx, sy); + if (!c) + return; + #if XWAYLAND if (c->type == X11Unmanaged) return; @@ -2126,6 +2152,24 @@ xytoclient(double x, double y) return NULL; } +struct wlr_surface * +xytolayersurface(struct wl_list *layer_surfaces, double x, double y, + double *sx, double *sy) +{ + LayerSurface *layersurface; + wl_list_for_each_reverse(layersurface, layer_surfaces, link) { + struct wlr_surface *sub = wlr_layer_surface_v1_surface_at( + layersurface->layer_surface, + x - layersurface->geo.x, + y - layersurface->geo.y, + sx, sy); + if (sub) + return sub; + + } + return NULL; +} + Monitor * xytomon(double x, double y) { From 35b93669f18a2e5ff414b2744e5d2013ad89553e Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Fri, 4 Sep 2020 15:35:04 +0200 Subject: [PATCH 024/173] reuse motionnotify() --- dwl.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/dwl.c b/dwl.c index 3b1b999..cd9b7ba 100644 --- a/dwl.c +++ b/dwl.c @@ -1236,16 +1236,8 @@ void maplayersurfacenotify(struct wl_listener *listener, void *data) { LayerSurface *layersurface = wl_container_of(listener, layersurface, map); - double sx = 0.0, sy = 0.0; - struct wlr_surface *sub = wlr_layer_surface_v1_surface_at( - layersurface->layer_surface, - cursor->x - layersurface->geo.x, - cursor->y - layersurface->geo.y, - &sx, &sy); wlr_surface_send_enter(layersurface->layer_surface->surface, layersurface->layer_surface->output); - if (sub) - wlr_seat_pointer_notify_enter(seat, sub, sx, sy); - /* XXX check if the layer surface is below a client */ + motionnotify(0); } void @@ -1317,6 +1309,11 @@ motionnotify(uint32_t time) double sx = 0, sy = 0; struct wlr_surface *surface = NULL; Client *c = NULL; + struct timespec now; + if (!time) { + clock_gettime(CLOCK_MONOTONIC, &now); + time = now.tv_sec * 1000 + now.tv_nsec / 1000000; + } /* Update selmon (even while dragging a window) */ if (sloppyfocus) @@ -2102,7 +2099,8 @@ unmaplayersurface(LayerSurface *layersurface) ) wlr_seat_keyboard_notify_clear_focus(seat); - /* XXX recheck keyboard and pointer focus */ + /* XXX recheck keyboard focus */ + motionnotify(0); // XXX why doesn't this work? } void From 5dc94600446c8e6847aed388a87a5bd2aecaacac Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Fri, 4 Sep 2020 16:37:22 +0200 Subject: [PATCH 025/173] simplify focus leave condition --- dwl.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/dwl.c b/dwl.c index cd9b7ba..8ca58f6 100644 --- a/dwl.c +++ b/dwl.c @@ -2090,13 +2090,8 @@ toggleview(const Arg *arg) void unmaplayersurface(LayerSurface *layersurface) { - if ( - layersurface->layer_surface->current.keyboard_interactive - && seat->keyboard_state.focused_surface - && wlr_surface_is_layer_surface(seat->keyboard_state.focused_surface) - && wlr_layer_surface_v1_from_wlr_surface(seat->keyboard_state.focused_surface) - == layersurface->layer_surface - ) + if (layersurface->layer_surface->surface == + seat->keyboard_state.focused_surface) wlr_seat_keyboard_notify_clear_focus(seat); /* XXX recheck keyboard focus */ From 4341deae8ff372fec198fb587f93613dc19cb4a2 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Fri, 4 Sep 2020 17:01:34 +0200 Subject: [PATCH 026/173] fix alhpabetical order --- dwl.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dwl.c b/dwl.c index 8ca58f6..529c0c7 100644 --- a/dwl.c +++ b/dwl.c @@ -263,8 +263,8 @@ static void togglefloating(const Arg *arg); static void toggletag(const Arg *arg); static void toggleview(const Arg *arg); static void unmaplayersurface(LayerSurface *layersurface); -static void unmapnotify(struct wl_listener *listener, void *data); static void unmaplayersurfacenotify(struct wl_listener *listener, void *data); +static void unmapnotify(struct wl_listener *listener, void *data); static void view(const Arg *arg); static Client *xytoclient(double x, double y); static struct wlr_surface *xytolayersurface(struct wl_list *layer_surfaces, @@ -2098,6 +2098,13 @@ unmaplayersurface(LayerSurface *layersurface) motionnotify(0); // XXX why doesn't this work? } +void +unmaplayersurfacenotify(struct wl_listener *listener, void *data) +{ + LayerSurface *layersurface = wl_container_of(listener, layersurface, unmap); + unmaplayersurface(layersurface); +} + void unmapnotify(struct wl_listener *listener, void *data) { @@ -2113,13 +2120,6 @@ unmapnotify(struct wl_listener *listener, void *data) wl_list_remove(&c->slink); } -void -unmaplayersurfacenotify(struct wl_listener *listener, void *data) -{ - LayerSurface *layersurface = wl_container_of(listener, layersurface, unmap); - unmaplayersurface(layersurface); -} - void view(const Arg *arg) { From 068352e88895d4a591643c55531135acdc52988a Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Fri, 4 Sep 2020 17:21:48 +0200 Subject: [PATCH 027/173] refocus old client When a layer surface is destroyed focus should be returned to the last client. Luckily if there are multiple overlays the previous overlay still gets focused. --- dwl.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dwl.c b/dwl.c index 529c0c7..76ba652 100644 --- a/dwl.c +++ b/dwl.c @@ -2092,9 +2092,7 @@ unmaplayersurface(LayerSurface *layersurface) { if (layersurface->layer_surface->surface == seat->keyboard_state.focused_surface) - wlr_seat_keyboard_notify_clear_focus(seat); - - /* XXX recheck keyboard focus */ + focusclient(NULL, selclient(), 1); motionnotify(0); // XXX why doesn't this work? } From 2d84c7465784d58f6d5a924a2d393055f2f710eb Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Fri, 4 Sep 2020 17:37:48 +0200 Subject: [PATCH 028/173] focus the previous client in the similar code too --- dwl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index 76ba652..d711361 100644 --- a/dwl.c +++ b/dwl.c @@ -606,8 +606,7 @@ arrangelayers(Monitor *m) && !wlr_layer_surface_v1_from_wlr_surface(seat->keyboard_state.focused_surface) ->current.keyboard_interactive ) - wlr_seat_keyboard_notify_clear_focus(seat); - /* XXX recheck keyboard focus */ + focusclient(NULL, selclient(), 1); } void From 8e81c90f31c5df365a7f684834ab6b50ba589468 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Fri, 4 Sep 2020 17:38:37 +0200 Subject: [PATCH 029/173] remove mysterious code Why would a surface that's not keyboard interactive get focused? Let's remove this for now and see if issues arise. --- dwl.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/dwl.c b/dwl.c index d711361..165a7c2 100644 --- a/dwl.c +++ b/dwl.c @@ -600,13 +600,6 @@ arrangelayers(Monitor *m) if (topmost) wlr_seat_keyboard_notify_enter(seat, topmost->layer_surface->surface, kb->keycodes, kb->num_keycodes, &kb->modifiers); - else if ( - seat->keyboard_state.focused_surface - && wlr_surface_is_layer_surface(seat->keyboard_state.focused_surface) - && !wlr_layer_surface_v1_from_wlr_surface(seat->keyboard_state.focused_surface) - ->current.keyboard_interactive - ) - focusclient(NULL, selclient(), 1); } void From 9308a90d11e1379de8759063b6495f8decc4cf03 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Fri, 4 Sep 2020 17:58:53 +0200 Subject: [PATCH 030/173] remove comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I don't know why it wasn't working before but now it does ¯\(ツ)/¯ (it wasn't caused by the just removed code either) --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 165a7c2..7c4fc0b 100644 --- a/dwl.c +++ b/dwl.c @@ -2085,7 +2085,7 @@ unmaplayersurface(LayerSurface *layersurface) if (layersurface->layer_surface->surface == seat->keyboard_state.focused_surface) focusclient(NULL, selclient(), 1); - motionnotify(0); // XXX why doesn't this work? + motionnotify(0); } void From 71572521e94be7859a467fb43730d43fae75d2b0 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Fri, 4 Sep 2020 18:36:27 +0200 Subject: [PATCH 031/173] improve code style --- dwl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dwl.c b/dwl.c index 7c4fc0b..34ca1e4 100644 --- a/dwl.c +++ b/dwl.c @@ -1355,10 +1355,10 @@ motionnotify(uint32_t time) else if ((surface = xytolayersurface(&selmon->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], cursor->x, cursor->y, &sx, &sy))) ; - else if ((surface = xytolayersurface(&selmon->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], - cursor->x, cursor->y, &sx, &sy))) { // gcc complains without these braces - ; - } + else + surface = xytolayersurface(&selmon->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], + cursor->x, cursor->y, &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. */ From 8de18f9bb4fe82d94fa76750fa398b28041f3fe3 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Fri, 4 Sep 2020 21:14:02 +0200 Subject: [PATCH 032/173] fix restoring pointer focus I don't know why I thought it was working before. Maybe I should go do something else. --- dwl.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 34ca1e4..d6974b0 100644 --- a/dwl.c +++ b/dwl.c @@ -140,6 +140,7 @@ typedef struct { struct wlr_box geo; enum zwlr_layer_shell_v1_layer layer; + bool unmapping; } LayerSurface; typedef struct { @@ -2082,6 +2083,7 @@ toggleview(const Arg *arg) void unmaplayersurface(LayerSurface *layersurface) { + layersurface->unmapping = true; if (layersurface->layer_surface->surface == seat->keyboard_state.focused_surface) focusclient(NULL, selclient(), 1); @@ -2141,7 +2143,10 @@ xytolayersurface(struct wl_list *layer_surfaces, double x, double y, { LayerSurface *layersurface; wl_list_for_each_reverse(layersurface, layer_surfaces, link) { - struct wlr_surface *sub = wlr_layer_surface_v1_surface_at( + struct wlr_surface *sub; + if (layersurface->unmapping) + continue; + sub = wlr_layer_surface_v1_surface_at( layersurface->layer_surface, x - layersurface->geo.x, y - layersurface->geo.y, From ae798c694ae0b9991d23dcd35b943967475ac2e5 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Fri, 4 Sep 2020 21:30:47 +0200 Subject: [PATCH 033/173] Don't let overlays lose focus if you open a new window while an overlay is mapped, the overlay should stay focused --- dwl.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index d6974b0..d65611d 100644 --- a/dwl.c +++ b/dwl.c @@ -256,6 +256,7 @@ static void setmfact(const Arg *arg); static void setmon(Client *c, Monitor *m, unsigned int newtags); static void setup(void); static void sigchld(int unused); +static bool shouldfocusclients(); static void spawn(const Arg *arg); static void tag(const Arg *arg); static void tagmon(const Arg *arg); @@ -1037,8 +1038,9 @@ focusclient(Client *old, Client *c, int lift) } /* Have a client, so focus its top-level wlr_surface */ - wlr_seat_keyboard_notify_enter(seat, WLR_SURFACE(c), - kb->keycodes, kb->num_keycodes, &kb->modifiers); + if (shouldfocusclients(c->mon)) + wlr_seat_keyboard_notify_enter(seat, WLR_SURFACE(c), + kb->keycodes, kb->num_keycodes, &kb->modifiers); /* Put the new client atop the focus stack and select its monitor */ wl_list_remove(&c->flink); @@ -1979,6 +1981,22 @@ sigchld(int unused) ; } +bool +shouldfocusclients(Monitor *m) +{ + LayerSurface *layersurface; + uint32_t layers_above_shell[] = { + ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, + ZWLR_LAYER_SHELL_V1_LAYER_TOP, + }; + for (unsigned int i = 0; i < LENGTH(layers_above_shell); ++i) + wl_list_for_each(layersurface, &m->layers[layers_above_shell[i]], link) + if (layersurface->layer_surface->current.keyboard_interactive && + !layersurface->unmapping) + return false; + return true; +} + void spawn(const Arg *arg) { From 8ee02008777e94e7c75f29635eead7da428dfdaa Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Fri, 4 Sep 2020 21:36:43 +0200 Subject: [PATCH 034/173] use unsigned int for loop indexes --- dwl.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dwl.c b/dwl.c index d65611d..5758db8 100644 --- a/dwl.c +++ b/dwl.c @@ -409,7 +409,7 @@ applyexclusive(struct wlr_box *usable_area, edges[3].negative_axis = &usable_area->width; edges[3].margin = margin_right; - for (size_t i = 0; i < LENGTH(edges); ++i) { + for (unsigned int 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) @@ -585,7 +585,7 @@ arrangelayers(Monitor *m) &usable_area, false); // Find topmost keyboard interactive layer, if such a layer exists - for (size_t i = 0; i < nlayers; ++i) { + for (unsigned int i = 0; i < nlayers; ++i) { wl_list_for_each_reverse(layersurface, &m->layers[layers_above_shell[i]], link) { if (layersurface->layer_surface->current.keyboard_interactive && @@ -827,7 +827,7 @@ createmon(struct wl_listener *listener, void *data) m->w = m->m; len = LENGTH(m->layers); - for (size_t i = 0; i < len; ++i) { + for (unsigned int i = 0; i < len; ++i) { wl_list_init(&m->layers[i]); } } From 6b25e7ef27392bf8f0617bb62918ec6a7198bce7 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 5 Sep 2020 06:58:54 +0200 Subject: [PATCH 035/173] simplify --- dwl.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/dwl.c b/dwl.c index 5758db8..b63480d 100644 --- a/dwl.c +++ b/dwl.c @@ -556,7 +556,7 @@ arrangelayers(Monitor *m) ZWLR_LAYER_SHELL_V1_LAYER_TOP, }; size_t nlayers = LENGTH(layers_above_shell); - LayerSurface *layersurface, *topmost = NULL; + LayerSurface *layersurface; struct wlr_keyboard *kb = wlr_seat_get_keyboard(seat); // Arrange exclusive surfaces from top->bottom @@ -590,18 +590,12 @@ arrangelayers(Monitor *m) &m->layers[layers_above_shell[i]], link) { if (layersurface->layer_surface->current.keyboard_interactive && layersurface->layer_surface->mapped) { - topmost = layersurface; - break; + wlr_seat_keyboard_notify_enter(seat, layersurface->layer_surface->surface, + kb->keycodes, kb->num_keycodes, &kb->modifiers); + return; } } - if (topmost) { - break; - } } - - if (topmost) - wlr_seat_keyboard_notify_enter(seat, topmost->layer_surface->surface, - kb->keycodes, kb->num_keycodes, &kb->modifiers); } void From 9743778d09b44e346f9a764499f1caa0dc0cf5c5 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 5 Sep 2020 14:14:19 +0200 Subject: [PATCH 036/173] rename variable and merge 2 lines --- dwl.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dwl.c b/dwl.c index b63480d..6e0bcb9 100644 --- a/dwl.c +++ b/dwl.c @@ -769,7 +769,7 @@ createmon(struct wl_listener *listener, void *data) struct wlr_output *wlr_output = data; Monitor *m; const MonitorRule *r; - size_t len; + size_t nlayers = LENGTH(m->layers); /* The mode is a tuple of (width, height, refresh rate), and each * monitor supports only a specific set of modes. We just pick the @@ -820,8 +820,7 @@ createmon(struct wl_listener *listener, void *data) m->m = *wlr_output_layout_get_box(output_layout, m->wlr_output); m->w = m->m; - len = LENGTH(m->layers); - for (unsigned int i = 0; i < len; ++i) { + for (unsigned int i = 0; i < nlayers; ++i) { wl_list_init(&m->layers[i]); } } From 52a4d3a1e54203c38fbc65345d19f2f1b704adb4 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 5 Sep 2020 18:29:39 +0200 Subject: [PATCH 037/173] use size_t for lengths --- dwl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dwl.c b/dwl.c index 6e0bcb9..8ddb24d 100644 --- a/dwl.c +++ b/dwl.c @@ -409,7 +409,7 @@ applyexclusive(struct wlr_box *usable_area, edges[3].negative_axis = &usable_area->width; edges[3].margin = margin_right; - for (unsigned int i = 0; i < LENGTH(edges); ++i) { + 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) @@ -585,7 +585,7 @@ arrangelayers(Monitor *m) &usable_area, false); // Find topmost keyboard interactive layer, if such a layer exists - for (unsigned int i = 0; i < nlayers; ++i) { + for (size_t i = 0; i < nlayers; ++i) { wl_list_for_each_reverse(layersurface, &m->layers[layers_above_shell[i]], link) { if (layersurface->layer_surface->current.keyboard_interactive && @@ -820,7 +820,7 @@ createmon(struct wl_listener *listener, void *data) m->m = *wlr_output_layout_get_box(output_layout, m->wlr_output); m->w = m->m; - for (unsigned int i = 0; i < nlayers; ++i) { + for (size_t i = 0; i < nlayers; ++i) { wl_list_init(&m->layers[i]); } } @@ -1982,7 +1982,7 @@ shouldfocusclients(Monitor *m) ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, ZWLR_LAYER_SHELL_V1_LAYER_TOP, }; - for (unsigned int i = 0; i < LENGTH(layers_above_shell); ++i) + for (size_t i = 0; i < LENGTH(layers_above_shell); ++i) wl_list_for_each(layersurface, &m->layers[layers_above_shell[i]], link) if (layersurface->layer_surface->current.keyboard_interactive && !layersurface->unmapping) From 79f3bbaf38a844f21ccc95d5dcdc60e871ac2840 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sun, 6 Sep 2020 19:59:58 +0200 Subject: [PATCH 038/173] remove variable --- dwl.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dwl.c b/dwl.c index 8ddb24d..bd7ea6c 100644 --- a/dwl.c +++ b/dwl.c @@ -140,7 +140,6 @@ typedef struct { struct wlr_box geo; enum zwlr_layer_shell_v1_layer layer; - bool unmapping; } LayerSurface; typedef struct { @@ -1985,7 +1984,7 @@ shouldfocusclients(Monitor *m) for (size_t i = 0; i < LENGTH(layers_above_shell); ++i) wl_list_for_each(layersurface, &m->layers[layers_above_shell[i]], link) if (layersurface->layer_surface->current.keyboard_interactive && - !layersurface->unmapping) + layersurface->layer_surface->mapped) return false; return true; } @@ -2094,7 +2093,7 @@ toggleview(const Arg *arg) void unmaplayersurface(LayerSurface *layersurface) { - layersurface->unmapping = true; + layersurface->layer_surface->mapped = false; if (layersurface->layer_surface->surface == seat->keyboard_state.focused_surface) focusclient(NULL, selclient(), 1); @@ -2155,7 +2154,7 @@ xytolayersurface(struct wl_list *layer_surfaces, double x, double y, LayerSurface *layersurface; wl_list_for_each_reverse(layersurface, layer_surfaces, link) { struct wlr_surface *sub; - if (layersurface->unmapping) + if (!layersurface->layer_surface->mapped) continue; sub = wlr_layer_surface_v1_surface_at( layersurface->layer_surface, From 69847872bb1d3ac7dd259facb97934da66c27cc5 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Tue, 8 Sep 2020 10:28:29 +0200 Subject: [PATCH 039/173] fix multi monitors If you don't recalculate the monitor's geometry before arranging, clients get arranged in the first monitor. I don't understand why this fixes the bug since tile() uses m->w rather than m->m, nor why it needs to be recalculated after creating the monitor but sway does it too. Although not necessary to fix the bug I also made arrangelayer() do like sway again and recalculate usable_area instead of reusing m->m, since m->m seems to be incorrect until it gets recalculated shortly after in arrange(), so I suspect that leaving usable_area = m->m will cause issues under certain circumstances. Someone with a multi-monitor setup or better knowledge of Wayland may be able to figure out the cause of the bug. For now, this makes layer shell work. --- dwl.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index bd7ea6c..c921fa9 100644 --- a/dwl.c +++ b/dwl.c @@ -463,6 +463,7 @@ applyrules(Client *c) void arrange(Monitor *m) { + m->m = *wlr_output_layout_get_box(output_layout, m->wlr_output); if (m->lt[m->sellt]->arrange) m->lt[m->sellt]->arrange(m); /* XXX recheck pointer focus here... or in resize()? */ @@ -549,7 +550,7 @@ arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, bool void arrangelayers(Monitor *m) { - struct wlr_box usable_area = m->m; + struct wlr_box usable_area = { 0 }; uint32_t layers_above_shell[] = { ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, ZWLR_LAYER_SHELL_V1_LAYER_TOP, @@ -558,6 +559,9 @@ arrangelayers(Monitor *m) LayerSurface *layersurface; struct wlr_keyboard *kb = wlr_seat_get_keyboard(seat); + wlr_output_effective_resolution(m->wlr_output, + &usable_area.width, &usable_area.height); + // Arrange exclusive surfaces from top->bottom arrangelayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], &usable_area, true); From 68412d8957ea31f92014dfc8813a1f294a55211b Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Tue, 8 Sep 2020 12:49:05 +0200 Subject: [PATCH 040/173] try to fix again Calculate x and y of usable_area, not just width and heigth. --- dwl.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/dwl.c b/dwl.c index c921fa9..e85b6d3 100644 --- a/dwl.c +++ b/dwl.c @@ -550,7 +550,7 @@ arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, bool void arrangelayers(Monitor *m) { - struct wlr_box usable_area = { 0 }; + struct wlr_box usable_area = *wlr_output_layout_get_box(output_layout, m->wlr_output); uint32_t layers_above_shell[] = { ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, ZWLR_LAYER_SHELL_V1_LAYER_TOP, @@ -559,9 +559,6 @@ arrangelayers(Monitor *m) LayerSurface *layersurface; struct wlr_keyboard *kb = wlr_seat_get_keyboard(seat); - wlr_output_effective_resolution(m->wlr_output, - &usable_area.width, &usable_area.height); - // Arrange exclusive surfaces from top->bottom arrangelayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], &usable_area, true); From 62250661cf0b6441e4a4179dfb7e63bd164a55e5 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Tue, 8 Sep 2020 13:36:46 +0200 Subject: [PATCH 041/173] remove unneeded line The bug was caused by usable_area's x and y not being set in arrangelayers. For example if on a 2nd HD monitor, x should be 1920 while the first one ends at 1919. So I don't see why m->m should be recalculated after creating the monitor. --- dwl.c | 1 - 1 file changed, 1 deletion(-) diff --git a/dwl.c b/dwl.c index e85b6d3..a191f5e 100644 --- a/dwl.c +++ b/dwl.c @@ -463,7 +463,6 @@ applyrules(Client *c) void arrange(Monitor *m) { - m->m = *wlr_output_layout_get_box(output_layout, m->wlr_output); if (m->lt[m->sellt]->arrange) m->lt[m->sellt]->arrange(m); /* XXX recheck pointer focus here... or in resize()? */ From 4915214e47d8f9233bfe4446a223300908455865 Mon Sep 17 00:00:00 2001 From: Bonicgamer <44382222+Bonicgamer@users.noreply.github.com> Date: Tue, 8 Sep 2020 14:53:34 -0400 Subject: [PATCH 042/173] Made scalebox the way sway does it --- dwl.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/dwl.c b/dwl.c index 730e46a..d54c702 100644 --- a/dwl.c +++ b/dwl.c @@ -50,6 +50,7 @@ #define LENGTH(X) (sizeof X / sizeof X[0]) #define END(A) ((A) + LENGTH(A)) #define TAGMASK ((1 << LENGTH(tags)) - 1) +#define ROUND(X) ((X)>=0?(long)((X)+0.5):(long)((X)-0.5)) #ifdef XWAYLAND #define WLR_SURFACE(C) ((C)->type != XDGShell ? (C)->surface.xwayland->surface : (C)->surface.xdg->surface) #else @@ -1344,10 +1345,10 @@ run(char *startup_cmd) void scalebox(struct wlr_box *box, float scale) { - box->x *= scale; - box->y *= scale; - box->width *= scale; - box->height *= scale; + box->width = ROUND((box->x + box->width) * scale) - ROUND(box->x * scale); + box->height = ROUND((box->y + box->height) * scale) - ROUND(box->y * scale); + box->x = ROUND(box->x * scale); + box->y = ROUND(box->y * scale); } Client * From 143dce094c14e9d3904ef7530f113c9a7e4ebe47 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Wed, 9 Sep 2020 17:13:30 +0200 Subject: [PATCH 043/173] fix multi monitors further Fix layer surfaces without an exculsive area by using the right x and y for the current monitor (by Stivvo). --- dwl.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dwl.c b/dwl.c index a191f5e..91239ca 100644 --- a/dwl.c +++ b/dwl.c @@ -472,9 +472,7 @@ void arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, bool exclusive) { LayerSurface *layersurface; - struct wlr_box full_area = { 0 }; - wlr_output_effective_resolution(m->wlr_output, - &full_area.width, &full_area.height); + struct wlr_box full_area = *wlr_output_layout_get_box(output_layout, m->wlr_output); wl_list_for_each(layersurface, list, link) { struct wlr_layer_surface_v1 *wlr_layer_surface = layersurface->layer_surface; From 8f0ce672d384759dd4a3cbcf2b0555b4a87edc7e Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Thu, 10 Sep 2020 06:01:18 +0200 Subject: [PATCH 044/173] simplify --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 91239ca..7bf8e5b 100644 --- a/dwl.c +++ b/dwl.c @@ -567,7 +567,7 @@ arrangelayers(Monitor *m) &usable_area, true); if (memcmp(&usable_area, &m->w, sizeof(struct wlr_box))) { - memcpy(&m->w, &usable_area, sizeof(struct wlr_box)); + m->w = usable_area; arrange(m); } From fbd905155aa1f7e33b3f7d4a54a3312166902335 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Fri, 11 Sep 2020 13:09:18 +0200 Subject: [PATCH 045/173] fix multi monitors even more When a monitor is created or removed, the geometries of the old ones must be updated. This is also more efficient than before since we calculate the monitor geometries only when creating and destroying monitors. arrangelayers() is needed to recalculate m->w. arrange() is so clients don't move to the left monitor when plugging or unplugging monitors (clients keep the same coordinates but the field below them changes). --- dwl.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/dwl.c b/dwl.c index 7bf8e5b..0d92d35 100644 --- a/dwl.c +++ b/dwl.c @@ -472,7 +472,7 @@ void arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, bool exclusive) { LayerSurface *layersurface; - struct wlr_box full_area = *wlr_output_layout_get_box(output_layout, m->wlr_output); + 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; @@ -547,7 +547,7 @@ arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, bool void arrangelayers(Monitor *m) { - struct wlr_box usable_area = *wlr_output_layout_get_box(output_layout, m->wlr_output); + struct wlr_box usable_area = m->m; uint32_t layers_above_shell[] = { ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, ZWLR_LAYER_SHELL_V1_LAYER_TOP, @@ -690,6 +690,12 @@ cleanupmon(struct wl_listener *listener, void *data) wl_list_remove(&m->destroy.link); free(m); + + wl_list_for_each(m, &mons, link) { + m->m = m->w = *wlr_output_layout_get_box(output_layout, m->wlr_output); + arrangelayers(m); + arrange(m); + } } void @@ -813,13 +819,16 @@ createmon(struct wl_listener *listener, void *data) wlr_output_layout_add_auto(output_layout, wlr_output); sgeom = *wlr_output_layout_get_box(output_layout, NULL); - /* Get effective monitor geometry to use for window area */ - m->m = *wlr_output_layout_get_box(output_layout, m->wlr_output); - m->w = m->m; - for (size_t i = 0; i < nlayers; ++i) { wl_list_init(&m->layers[i]); } + + /* Get effective monitor geometry to use for window area */ + wl_list_for_each(m, &mons, link) { + m->m = m->w = *wlr_output_layout_get_box(output_layout, m->wlr_output); + arrangelayers(m); + arrange(m); + } } void From e4d58c39e0b9e952e31661fc2a9d6e043928b729 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Fri, 11 Sep 2020 13:15:31 +0200 Subject: [PATCH 046/173] remove braces --- dwl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index 0d92d35..cb2c241 100644 --- a/dwl.c +++ b/dwl.c @@ -819,9 +819,8 @@ createmon(struct wl_listener *listener, void *data) wlr_output_layout_add_auto(output_layout, wlr_output); sgeom = *wlr_output_layout_get_box(output_layout, NULL); - for (size_t i = 0; i < nlayers; ++i) { + for (size_t i = 0; i < nlayers; ++i) wl_list_init(&m->layers[i]); - } /* Get effective monitor geometry to use for window area */ wl_list_for_each(m, &mons, link) { From 0bb25a73ecfbf4c8f613e1a1b96be5ea683bf12a Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Fri, 11 Sep 2020 14:24:39 +0200 Subject: [PATCH 047/173] extract function and comment it --- dwl.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/dwl.c b/dwl.c index cb2c241..414aa59 100644 --- a/dwl.c +++ b/dwl.c @@ -266,6 +266,7 @@ static void toggleview(const Arg *arg); 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(); static void view(const Arg *arg); static Client *xytoclient(double x, double y); static struct wlr_surface *xytolayersurface(struct wl_list *layer_surfaces, @@ -691,11 +692,7 @@ cleanupmon(struct wl_listener *listener, void *data) wl_list_remove(&m->destroy.link); free(m); - wl_list_for_each(m, &mons, link) { - m->m = m->w = *wlr_output_layout_get_box(output_layout, m->wlr_output); - arrangelayers(m); - arrange(m); - } + updatemons(); } void @@ -822,12 +819,8 @@ createmon(struct wl_listener *listener, void *data) for (size_t i = 0; i < nlayers; ++i) wl_list_init(&m->layers[i]); - /* Get effective monitor geometry to use for window area */ - wl_list_for_each(m, &mons, link) { - m->m = m->w = *wlr_output_layout_get_box(output_layout, m->wlr_output); - arrangelayers(m); - arrange(m); - } + /* When adding monitors, the geometries of all monitors must be updated */ + updatemons(); } void @@ -2128,6 +2121,20 @@ unmapnotify(struct wl_listener *listener, void *data) wl_list_remove(&c->slink); } +void +updatemons() +{ + Monitor *m; + wl_list_for_each(m, &mons, link) { + /* Get the effective monitor geometry to use for surfaces */ + m->m = m->w = *wlr_output_layout_get_box(output_layout, m->wlr_output); + /* Calculate the effective monitor geometry to use for clients */ + arrangelayers(m); + /* Don't move clients to the left output when plugging monitors */ + arrange(m); + } +} + void view(const Arg *arg) { From 537ad7e3fdc23602254ba00ba869eab891be7966 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sat, 12 Sep 2020 00:13:05 +0200 Subject: [PATCH 048/173] Restore floating win position after mon add Compensate the coordinate changes when adding a new monitor. Every test so far confirms that monitors are always added to the left, on top of the list, so every floating window's x coordinate has to be incremented by the width of the new monitor. --- dwl.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/dwl.c b/dwl.c index 414aa59..23a7211 100644 --- a/dwl.c +++ b/dwl.c @@ -195,6 +195,7 @@ static void applyexclusive(struct wlr_box *usable_area, uint32_t anchor, int32_t margin_bottom, int32_t margin_left); static void applyrules(Client *c); static void arrange(Monitor *m); +static void arrangefloat(Monitor *m, int sign); static void arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, bool exclusive); static void arrangelayers(Monitor *m); @@ -469,6 +470,17 @@ arrange(Monitor *m) /* XXX recheck pointer focus here... or in resize()? */ } +void +arrangefloat(Monitor *m, int sign) +{ + Client *c; + wl_list_for_each(c, &clients, link) { + if (c->isfloating) + resize(c, c->geom.x + m->w.width * sign, c->geom.y, + c->geom.width, c->geom.height, 0); + } +} + void arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, bool exclusive) { @@ -693,6 +705,7 @@ cleanupmon(struct wl_listener *listener, void *data) free(m); updatemons(); + arrangefloat(m, -1); } void @@ -821,6 +834,11 @@ createmon(struct wl_listener *listener, void *data) /* When adding monitors, the geometries of all monitors must be updated */ updatemons(); + wl_list_for_each(m, &mons, link) { + /* the first monitor in the list is the most recently added */ + arrangefloat(m, 1); + return; + } } void From a2e5c25e260e214f107fba7fcf86c6283abd99b7 Mon Sep 17 00:00:00 2001 From: Oyren Date: Mon, 14 Sep 2020 20:45:39 +0200 Subject: [PATCH 049/173] remove log flags from readme The following commit has removed the logs but they are still in the readme. https://github.com/djpohly/dwl/commit/3b1992ca91b9a468019165c985263f5b1cc78c2c --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index 3251ccf..849967c 100644 --- a/README.md +++ b/README.md @@ -49,12 +49,6 @@ dwl can be run as-is, with no arguments. In an existing Wayland or X11 session, You can also specify a startup program using the `-s` option. The argument to this option will be run at startup as a shell command (using `sh -c`) and can serve a similar function to `.xinitrc`: starting a service manager or other startup applications. Unlike `.xinitrc`, the display server will not shut down when this process terminates. Instead, as dwl is shutting down, it will send this process a SIGTERM and wait for it to terminate (if it hasn't already). This makes it ideal not only for initialization but also for execing into a user-level service manager like s6 or `systemd --user`. -More/less verbose output can be requested with flags as well: - -* `-q`: quiet (log level WLR_SILENT) -* `-v`: verbose (log level WLR_INFO) -* `-d`: debug (log level WLR_DEBUG) - Note: Wayland requires a valid `XDG_RUNTIME_DIR`, which is usually set up by a session manager such as `elogind` or `systemd-logind`. If your system doesn't do this automatically, you will need to configure it prior to launching `dwl`, e.g.: export XDG_RUNTIME_DIR=/tmp/xdg-runtime-$(id -u) From bece225934216f1d047ebecc5b574ed1b15886f2 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Mon, 14 Sep 2020 17:40:14 +0200 Subject: [PATCH 050/173] Handle monitor unplug Floating widndows with "x < removed monitor's width" aren't resized (they used to disappear in negative coordinates). Actually delete monitors when they are unplugged, recalculate sgeom and give a new monitor to clients that were on the removed one with setmon() arrangefloat() funcion has been exploded to save iterations in cleanupmon(). Also if a monitor that supports auto suspension is turned off, dwl will count it as unplugged (it will become unreachable and all clients will be moved to the leftmost monitor). However, if at least one monitor isn't plugged in, dwl will still crash the same as before. Unlike sway, when the output configuration is changed and restored, (unplug + plug the same monitor for example) previous application positions aren't kept. This is due to the fact that on sway every workspace is unique among all monitors. --- dwl.c | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/dwl.c b/dwl.c index 23a7211..6f2e800 100644 --- a/dwl.c +++ b/dwl.c @@ -195,7 +195,6 @@ static void applyexclusive(struct wlr_box *usable_area, uint32_t anchor, int32_t margin_bottom, int32_t margin_left); static void applyrules(Client *c); static void arrange(Monitor *m); -static void arrangefloat(Monitor *m, int sign); static void arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, bool exclusive); static void arrangelayers(Monitor *m); @@ -470,17 +469,6 @@ arrange(Monitor *m) /* XXX recheck pointer focus here... or in resize()? */ } -void -arrangefloat(Monitor *m, int sign) -{ - Client *c; - wl_list_for_each(c, &clients, link) { - if (c->isfloating) - resize(c, c->geom.x + m->w.width * sign, c->geom.y, - c->geom.width, c->geom.height, 0); - } -} - void arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, bool exclusive) { @@ -699,13 +687,29 @@ void cleanupmon(struct wl_listener *listener, void *data) { struct wlr_output *wlr_output = data; - Monitor *m = wlr_output->data; + Monitor *m = wlr_output->data, *newmon; + Client *c; wl_list_remove(&m->destroy.link); - free(m); + wl_list_remove(&m->frame.link); + wl_list_remove(&m->link); + wlr_output_layout_remove(output_layout, m->wlr_output); + sgeom = *wlr_output_layout_get_box(output_layout, NULL); updatemons(); - arrangefloat(m, -1); + + wl_list_for_each(newmon, &mons, link) { + wl_list_for_each(c, &clients, link) { + if (c->isfloating && c->geom.x > m->m.width) { + resize(c, c->geom.x - m->w.width, c->geom.y, + c->geom.width, c->geom.height, 0); + } + if (c->mon == m) + setmon(c, newmon, 0); + } + break; + } + free(m); } void @@ -836,7 +840,12 @@ createmon(struct wl_listener *listener, void *data) updatemons(); wl_list_for_each(m, &mons, link) { /* the first monitor in the list is the most recently added */ - arrangefloat(m, 1); + Client *c; + wl_list_for_each(c, &clients, link) { + if (c->isfloating) + resize(c, c->geom.x + m->w.width, c->geom.y, + c->geom.width, c->geom.height, 0); + } return; } } From d8f752c9b46f8ef2286cddd45628b6db576b8ddf Mon Sep 17 00:00:00 2001 From: Stivvo Date: Tue, 15 Sep 2020 12:11:40 +0200 Subject: [PATCH 051/173] Keep client tags on unplug When unplugging a monitor, each client is moved to the same tag number as before on the new monitor --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 6f2e800..bdd89fc 100644 --- a/dwl.c +++ b/dwl.c @@ -705,7 +705,7 @@ cleanupmon(struct wl_listener *listener, void *data) c->geom.width, c->geom.height, 0); } if (c->mon == m) - setmon(c, newmon, 0); + setmon(c, newmon, c->tags); } break; } From 598516d808784c928700d4416488f3574c55244d Mon Sep 17 00:00:00 2001 From: will Date: Sat, 17 Oct 2020 13:52:53 +0200 Subject: [PATCH 052/173] Added basic tap-to-click for touchpad users --- Makefile | 2 +- config.def.h | 4 ++++ dwl.c | 6 ++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 56ab751..2409aa4 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ CFLAGS += -I. -DWLR_USE_UNSTABLE -std=c99 -Werror=declaration-after-statement WAYLAND_PROTOCOLS=$(shell pkg-config --variable=pkgdatadir wayland-protocols) WAYLAND_SCANNER=$(shell pkg-config --variable=wayland_scanner wayland-scanner) -PKGS = wlroots wayland-server xcb xkbcommon +PKGS = wlroots wayland-server xcb xkbcommon libinput CFLAGS += $(foreach p,$(PKGS),$(shell pkg-config --cflags $(p))) LDLIBS += $(foreach p,$(PKGS),$(shell pkg-config --libs $(p))) diff --git a/config.def.h b/config.def.h index 53021cf..c1ad7d6 100644 --- a/config.def.h +++ b/config.def.h @@ -41,6 +41,10 @@ static const struct xkb_rule_names xkb_rules = { .options = "ctrl:nocaps", */ }; + +/* Trackpad */ +int tap_to_click = 1; + static const int repeat_rate = 25; static const int repeat_delay = 600; diff --git a/dwl.c b/dwl.c index 730e46a..69485ec 100644 --- a/dwl.c +++ b/dwl.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,7 @@ #include #include #include +#include #include #include #ifdef XWAYLAND @@ -603,6 +605,10 @@ createnotify(struct wl_listener *listener, void *data) void createpointer(struct wlr_input_device *device) { + struct libinput_device *libinput_device = (struct libinput_device*) + wlr_libinput_get_device_handle(device); + if (tap_to_click && libinput_device_config_tap_get_finger_count(libinput_device)) + libinput_device_config_tap_set_enabled(libinput_device, LIBINPUT_CONFIG_TAP_ENABLED); /* We don't do anything special with pointers. All of our pointer handling * is proxied through wlr_cursor. On another compositor, you might take this * opportunity to do libinput configuration on the device to set From 7ad14a966087178cee752ac4bb63b14bd64ce5ec Mon Sep 17 00:00:00 2001 From: will Date: Sat, 17 Oct 2020 16:18:44 +0200 Subject: [PATCH 053/173] Added support for natural scrolling --- config.def.h | 1 + dwl.c | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/config.def.h b/config.def.h index c1ad7d6..2c11fd3 100644 --- a/config.def.h +++ b/config.def.h @@ -44,6 +44,7 @@ static const struct xkb_rule_names xkb_rules = { /* Trackpad */ int tap_to_click = 1; +int natural_scrolling = 1; static const int repeat_rate = 25; static const int repeat_delay = 600; diff --git a/dwl.c b/dwl.c index 69485ec..5895c65 100644 --- a/dwl.c +++ b/dwl.c @@ -607,8 +607,13 @@ createpointer(struct wlr_input_device *device) { struct libinput_device *libinput_device = (struct libinput_device*) wlr_libinput_get_device_handle(device); + if (tap_to_click && libinput_device_config_tap_get_finger_count(libinput_device)) libinput_device_config_tap_set_enabled(libinput_device, LIBINPUT_CONFIG_TAP_ENABLED); + + if (libinput_device_config_scroll_has_natural_scroll(libinput_device)) + libinput_device_config_scroll_set_natural_scroll_enabled(libinput_device, natural_scrolling); + /* We don't do anything special with pointers. All of our pointer handling * is proxied through wlr_cursor. On another compositor, you might take this * opportunity to do libinput configuration on the device to set From b30e18fa204bab2a993ce4f0250728f01accc04f Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 17 Oct 2020 20:11:31 +0200 Subject: [PATCH 054/173] Implement the output management protocol It allows clients such as wlr-randr to configure the display. --- dwl.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/dwl.c b/dwl.c index 414aa59..23b627d 100644 --- a/dwl.c +++ b/dwl.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -235,6 +236,9 @@ static void motionabsolute(struct wl_listener *listener, void *data); static void motionnotify(uint32_t time); static void motionrelative(struct wl_listener *listener, void *data); static void moveresize(const Arg *arg); +static void outputmgrapply(struct wl_listener *listener, void *data); +static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test); +static void outputmgrtest(struct wl_listener *listener, void *data); static void pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, uint32_t time); static void quit(const Arg *arg); @@ -288,6 +292,7 @@ static struct wl_list stack; /* stacking z-order */ static struct wl_list independents; static struct wlr_layer_shell_v1 *layer_shell; static struct wlr_xdg_decoration_manager_v1 *xdeco_mgr; +static struct wlr_output_manager_v1 *output_mgr; static struct wlr_cursor *cursor; static struct wlr_xcursor_manager *cursor_mgr; @@ -314,6 +319,8 @@ static struct wl_listener new_output = {.notify = createmon}; static struct wl_listener new_xdeco = {.notify = createxdeco}; static struct wl_listener new_xdg_surface = {.notify = createnotify}; static struct wl_listener new_layer_shell_surface = {.notify = createlayersurface}; +static struct wl_listener output_mgr_apply = {.notify = outputmgrapply}; +static struct wl_listener output_mgr_test = {.notify = outputmgrtest}; static struct wl_listener request_cursor = {.notify = setcursor}; static struct wl_listener request_set_psel = {.notify = setpsel}; static struct wl_listener request_set_sel = {.notify = setsel}; @@ -1406,6 +1413,63 @@ moveresize(const Arg *arg) } } +void +outputmgrapply(struct wl_listener *listener, void *data) +{ + struct wlr_output_configuration_v1 *config = data; + outputmgrapplyortest(config, false); +} + +// apply_output_config +void +outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test) +{ + struct wlr_output_configuration_head_v1 *config_head; + bool ok = true; + + wl_list_for_each(config_head, &config->heads, link) { + struct wlr_output *wlr_output = config_head->state.output; + + wlr_output_enable(wlr_output, config_head->state.enabled); + + if (config_head->state.enabled) { + if (config_head->state.mode) + wlr_output_set_mode(wlr_output, config_head->state.mode); + else + wlr_output_set_custom_mode(wlr_output, + config_head->state.custom_mode.width, + config_head->state.custom_mode.height, + config_head->state.custom_mode.refresh); + + wlr_output_layout_move(output_layout, wlr_output, + config_head->state.x, config_head->state.y); + wlr_output_set_transform(wlr_output, config_head->state.transform); + wlr_output_set_scale(wlr_output, config_head->state.scale); + } + + if (test) { + ok &= wlr_output_test(wlr_output); + wlr_output_rollback(wlr_output); + } else + ok &= wlr_output_commit(wlr_output); + } + if (ok) { + wlr_output_configuration_v1_send_succeeded(config); + if (!test) + updatemons(); + } + else + wlr_output_configuration_v1_send_failed(config); + wlr_output_configuration_v1_destroy(config); +} + +void +outputmgrtest(struct wl_listener *listener, void *data) +{ + struct wlr_output_configuration_v1 *config = data; + outputmgrapplyortest(config, true); +} + void pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, uint32_t time) @@ -1946,6 +2010,10 @@ setup(void) wl_signal_add(&seat->events.request_set_primary_selection, &request_set_psel); + output_mgr = wlr_output_manager_v1_create(dpy); + wl_signal_add(&output_mgr->events.apply, &output_mgr_apply); + wl_signal_add(&output_mgr->events.test, &output_mgr_test); + #ifdef XWAYLAND /* * Initialise the XWayland X server. @@ -2124,15 +2192,28 @@ unmapnotify(struct wl_listener *listener, void *data) void updatemons() { + struct wlr_output_configuration_v1 *config = + wlr_output_configuration_v1_create(); Monitor *m; + wl_list_for_each(m, &mons, link) { + struct wlr_output_configuration_head_v1 *config_head = + wlr_output_configuration_head_v1_create(config, m->wlr_output); + /* Get the effective monitor geometry to use for surfaces */ m->m = m->w = *wlr_output_layout_get_box(output_layout, m->wlr_output); /* Calculate the effective monitor geometry to use for clients */ arrangelayers(m); /* Don't move clients to the left output when plugging monitors */ arrange(m); + + config_head->state.enabled = m->wlr_output->enabled; + config_head->state.mode = m->wlr_output->current_mode; + config_head->state.x = m->m.x; + config_head->state.y = m->m.y; } + + wlr_output_manager_v1_set_configuration(output_mgr, config); } void From f21d3796b842ea944fb05049cd0d6fa2614dce64 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 17 Oct 2020 20:15:59 +0200 Subject: [PATCH 055/173] Move sgeom assignment There is no need to repeat this. This needs to be reculalculated in my output-management implementation too, and since I'm already calling updatemons, this patch avoids having to repeat the assignment again. --- dwl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index bdd89fc..5d8a75c 100644 --- a/dwl.c +++ b/dwl.c @@ -695,7 +695,6 @@ cleanupmon(struct wl_listener *listener, void *data) wl_list_remove(&m->link); wlr_output_layout_remove(output_layout, m->wlr_output); - sgeom = *wlr_output_layout_get_box(output_layout, NULL); updatemons(); wl_list_for_each(newmon, &mons, link) { @@ -831,7 +830,6 @@ createmon(struct wl_listener *listener, void *data) * output (such as DPI, scale factor, manufacturer, etc). */ wlr_output_layout_add_auto(output_layout, wlr_output); - sgeom = *wlr_output_layout_get_box(output_layout, NULL); for (size_t i = 0; i < nlayers; ++i) wl_list_init(&m->layers[i]); @@ -2151,6 +2149,7 @@ unmapnotify(struct wl_listener *listener, void *data) void updatemons() { + sgeom = *wlr_output_layout_get_box(output_layout, NULL); Monitor *m; wl_list_for_each(m, &mons, link) { /* Get the effective monitor geometry to use for surfaces */ From 7017a0c64d3a58635cb5e088fab890c4efa02737 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sun, 18 Oct 2020 18:37:55 +0200 Subject: [PATCH 056/173] fix compile error mixed declaration --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 5d8a75c..48a4fbe 100644 --- a/dwl.c +++ b/dwl.c @@ -2149,8 +2149,8 @@ unmapnotify(struct wl_listener *listener, void *data) void updatemons() { - sgeom = *wlr_output_layout_get_box(output_layout, NULL); Monitor *m; + sgeom = *wlr_output_layout_get_box(output_layout, NULL); wl_list_for_each(m, &mons, link) { /* Get the effective monitor geometry to use for surfaces */ m->m = m->w = *wlr_output_layout_get_box(output_layout, m->wlr_output); From ef7043e4d1445142ab18bf7de886f1963fbade8b Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sat, 24 Oct 2020 23:40:19 +0200 Subject: [PATCH 057/173] closemon() Separate oputput movement from cleanupmon --- dwl.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/dwl.c b/dwl.c index 48a4fbe..9e8c527 100644 --- a/dwl.c +++ b/dwl.c @@ -204,6 +204,7 @@ static void chvt(const Arg *arg); static void cleanup(void); static void cleanupkeyboard(struct wl_listener *listener, void *data); static void cleanupmon(struct wl_listener *listener, void *data); +static void closemon(Monitor *m); static void commitlayersurfacenotify(struct wl_listener *listener, void *data); static void commitnotify(struct wl_listener *listener, void *data); static void createkeyboard(struct wlr_input_device *device); @@ -687,8 +688,7 @@ void cleanupmon(struct wl_listener *listener, void *data) { struct wlr_output *wlr_output = data; - Monitor *m = wlr_output->data, *newmon; - Client *c; + Monitor *m = wlr_output->data; wl_list_remove(&m->destroy.link); wl_list_remove(&m->frame.link); @@ -696,6 +696,16 @@ cleanupmon(struct wl_listener *listener, void *data) wlr_output_layout_remove(output_layout, m->wlr_output); updatemons(); + closemon(m); + free(m); +} + +void +closemon(Monitor *m) +{ + // move all the clients on a closed monitor to another one + Monitor *newmon; + Client *c; wl_list_for_each(newmon, &mons, link) { wl_list_for_each(c, &clients, link) { @@ -708,7 +718,6 @@ cleanupmon(struct wl_listener *listener, void *data) } break; } - free(m); } void From 4c0d59c1a724420bfd6d602c65d7ac6ab05e6eaf Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sun, 25 Oct 2020 11:16:37 +0100 Subject: [PATCH 058/173] Move clients away from a disabled monitor When a monitor is disabled with wlr_randr, all clients on that monitor aren't lost but they are moved to the leftmost monitor with the same method that handles monitor hot unplug --- dwl.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index dda407a..e168f58 100644 --- a/dwl.c +++ b/dwl.c @@ -1465,6 +1465,15 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test) struct wlr_output *wlr_output = config_head->state.output; wlr_output_enable(wlr_output, config_head->state.enabled); + if (!config_head->state.enabled) { + Monitor *m; + wl_list_for_each(m, &mons, link) { + if (m->wlr_output == wlr_output) { + closemon(m); + break; + } + } + } if (config_head->state.enabled) { if (config_head->state.mode) @@ -1491,8 +1500,7 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test) wlr_output_configuration_v1_send_succeeded(config); if (!test) updatemons(); - } - else + } else wlr_output_configuration_v1_send_failed(config); wlr_output_configuration_v1_destroy(config); } From 3c3714aac05643ce59692f2c2442db3def993b4d Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sun, 25 Oct 2020 11:56:21 +0100 Subject: [PATCH 059/173] Handle monitor enable --- dwl.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index e168f58..56801a0 100644 --- a/dwl.c +++ b/dwl.c @@ -1463,10 +1463,17 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test) wl_list_for_each(config_head, &config->heads, link) { struct wlr_output *wlr_output = config_head->state.output; + Monitor *m; wlr_output_enable(wlr_output, config_head->state.enabled); - if (!config_head->state.enabled) { - Monitor *m; + if (config_head->state.enabled) { + wl_list_for_each(m, &mons, link) { + if (m->wlr_output == wlr_output) { + wlr_output_set_mode(m->wlr_output, wlr_output_preferred_mode(m->wlr_output)); + break; + } + } + } else { wl_list_for_each(m, &mons, link) { if (m->wlr_output == wlr_output) { closemon(m); From 42aea0b17d6e221c8932b3c3ce63329c4b6e560d Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sun, 25 Oct 2020 11:56:21 +0100 Subject: [PATCH 060/173] Handle monitor enable --- dwl.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index e168f58..9fcd507 100644 --- a/dwl.c +++ b/dwl.c @@ -1463,10 +1463,17 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test) wl_list_for_each(config_head, &config->heads, link) { struct wlr_output *wlr_output = config_head->state.output; + Monitor *m; wlr_output_enable(wlr_output, config_head->state.enabled); - if (!config_head->state.enabled) { - Monitor *m; + if (!wlr_output->enabled) { + wl_list_for_each(m, &mons, link) { + if (m->wlr_output == wlr_output) { + wlr_output_set_mode(m->wlr_output, wlr_output_preferred_mode(m->wlr_output)); + break; + } + } + } else { wl_list_for_each(m, &mons, link) { if (m->wlr_output == wlr_output) { closemon(m); From 7d67b77a96260aa07d47a6d7ef414be1daa414b4 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Fri, 30 Oct 2020 16:21:00 +0100 Subject: [PATCH 061/173] Cleaner if statement --- dwl.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dwl.c b/dwl.c index 9e8c527..8035cc0 100644 --- a/dwl.c +++ b/dwl.c @@ -709,10 +709,9 @@ closemon(Monitor *m) wl_list_for_each(newmon, &mons, link) { wl_list_for_each(c, &clients, link) { - if (c->isfloating && c->geom.x > m->m.width) { + if (c->isfloating && c->geom.x > m->m.width) resize(c, c->geom.x - m->w.width, c->geom.y, - c->geom.width, c->geom.height, 0); - } + c->geom.width, c->geom.height, 0); if (c->mon == m) setmon(c, newmon, c->tags); } From 618972696de9499fa38b613a47799cf46b2b0135 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Fri, 30 Oct 2020 19:52:20 +0100 Subject: [PATCH 062/173] Fix crash when unplugging a focused monitor Just focus a "safe" monitor before trying to to anything risky --- dwl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dwl.c b/dwl.c index 8035cc0..461dd7b 100644 --- a/dwl.c +++ b/dwl.c @@ -707,6 +707,7 @@ closemon(Monitor *m) Monitor *newmon; Client *c; + focusclient(selclient(), focustop(dirtomon(-1)), 1); wl_list_for_each(newmon, &mons, link) { wl_list_for_each(c, &clients, link) { if (c->isfloating && c->geom.x > m->m.width) From 4deeddceffde88bd117411fb856e86a7d92e456a Mon Sep 17 00:00:00 2001 From: Stivvo Date: Fri, 30 Oct 2020 22:32:00 +0100 Subject: [PATCH 063/173] Actually move clients away from a disabled mon When using wlr-randr every monitor's configuration is reevaluated, so it must check which monitors are actually being disabled. The only way to correctly do that is to compare the names. --- dwl.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/dwl.c b/dwl.c index b70a217..715cb5e 100644 --- a/dwl.c +++ b/dwl.c @@ -1466,21 +1466,10 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test) Monitor *m; wlr_output_enable(wlr_output, config_head->state.enabled); - if (!wlr_output->enabled) { - wl_list_for_each(m, &mons, link) { - if (m->wlr_output == wlr_output) { - wlr_output_set_mode(m->wlr_output, wlr_output_preferred_mode(m->wlr_output)); - break; - } - } - } else { - wl_list_for_each(m, &mons, link) { - if (m->wlr_output == wlr_output) { + if (!config_head->state.enabled) + wl_list_for_each(m, &mons, link) + if (m->wlr_output->name == wlr_output->name) closemon(m); - break; - } - } - } if (config_head->state.enabled) { if (config_head->state.mode) From d4178b9d2a89c24b99e2b26aec8ac15ab5dcdd84 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Fri, 30 Oct 2020 23:22:55 +0100 Subject: [PATCH 064/173] Closemon(), newmon as parameter This allows to fix output-management: move clients to the monitor on the left of the disabled one, instead of the leftmost (which might happen to be the disabled one) Also using wl_list_foreach() and then brake after the first iteration is ugly and inefficient --- dwl.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/dwl.c b/dwl.c index 9e8c527..f688bf6 100644 --- a/dwl.c +++ b/dwl.c @@ -204,7 +204,7 @@ static void chvt(const Arg *arg); static void cleanup(void); static void cleanupkeyboard(struct wl_listener *listener, void *data); static void cleanupmon(struct wl_listener *listener, void *data); -static void closemon(Monitor *m); +static void closemon(Monitor *m, Monitor *newmon); static void commitlayersurfacenotify(struct wl_listener *listener, void *data); static void commitnotify(struct wl_listener *listener, void *data); static void createkeyboard(struct wlr_input_device *device); @@ -688,7 +688,7 @@ void cleanupmon(struct wl_listener *listener, void *data) { struct wlr_output *wlr_output = data; - Monitor *m = wlr_output->data; + Monitor *m = wlr_output->data, *newmon; wl_list_remove(&m->destroy.link); wl_list_remove(&m->frame.link); @@ -696,27 +696,22 @@ cleanupmon(struct wl_listener *listener, void *data) wlr_output_layout_remove(output_layout, m->wlr_output); updatemons(); - closemon(m); + closemon(m, wl_container_of(mons.next, newmon, link)); free(m); } void -closemon(Monitor *m) +closemon(Monitor *m, Monitor *newmon) { // move all the clients on a closed monitor to another one - Monitor *newmon; Client *c; - wl_list_for_each(newmon, &mons, link) { - wl_list_for_each(c, &clients, link) { - if (c->isfloating && c->geom.x > m->m.width) { - resize(c, c->geom.x - m->w.width, c->geom.y, - c->geom.width, c->geom.height, 0); - } - if (c->mon == m) - setmon(c, newmon, c->tags); - } - break; + wl_list_for_each(c, &clients, link) { + if (c->isfloating && c->geom.x > m->m.width) + resize(c, c->geom.x - m->w.width, c->geom.y, + c->geom.width, c->geom.height, 0); + if (c->mon == m) + setmon(c, newmon, c->tags); } } From 388ab9df2f655e7bd228b98ccf48ab612c0ffe4e Mon Sep 17 00:00:00 2001 From: Stivvo Date: Fri, 30 Oct 2020 23:33:10 +0100 Subject: [PATCH 065/173] Move disabled clients to the left To the nearest monitor to the left of the disabled one --- dwl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index 182c097..0432b42 100644 --- a/dwl.c +++ b/dwl.c @@ -1459,13 +1459,13 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test) wl_list_for_each(config_head, &config->heads, link) { struct wlr_output *wlr_output = config_head->state.output; - Monitor *m; + Monitor *m, *newmon; wlr_output_enable(wlr_output, config_head->state.enabled); if (!config_head->state.enabled) wl_list_for_each(m, &mons, link) if (m->wlr_output->name == wlr_output->name) - closemon(m); + closemon(m, wl_container_of(m->link.next, newmon, link)); if (config_head->state.enabled) { if (config_head->state.mode) From 62fb4c086ea5080977edc4ec017390aa924607ed Mon Sep 17 00:00:00 2001 From: Stivvo Date: Fri, 30 Oct 2020 23:49:01 +0100 Subject: [PATCH 066/173] Block access to disabled monitor Before this, pressing mod+comma or mod+period (focusmon function) moved the focus to disabed monitors. Now, all disabled monitors are skipped --- dwl.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/dwl.c b/dwl.c index 0432b42..6bbf8b9 100644 --- a/dwl.c +++ b/dwl.c @@ -1087,10 +1087,12 @@ focusclient(Client *old, Client *c, int lift) void focusmon(const Arg *arg) { - Client *sel = selclient(); - - selmon = dirtomon(arg->i); - focusclient(sel, focustop(selmon), 1); + Client *sel; + do { + sel = selclient(); + selmon = dirtomon(arg->i); + focusclient(sel, focustop(selmon), 1); + } while (!selmon->wlr_output->enabled); } void From fab42e7c41e8379168c49a7f427cdd3ffddd6e34 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sat, 31 Oct 2020 10:39:53 +0100 Subject: [PATCH 067/173] Fix crash unplugging a focused mon Focus newmon, which we know for sure that it isn't be unplugged or disabled --- dwl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 8852f8f..69deeb5 100644 --- a/dwl.c +++ b/dwl.c @@ -706,7 +706,8 @@ closemon(Monitor *m, Monitor *newmon) // move all the clients on a closed monitor to another one Client *c; - focusclient(selclient(), focustop(dirtomon(-1)), 1); + selmon = newmon; + focusclient(selclient(), focustop(newmon), 1); wl_list_for_each(c, &clients, link) { if (c->isfloating && c->geom.x > m->m.width) resize(c, c->geom.x - m->w.width, c->geom.y, From 5622dbdaf3eaa62183d81e9b545a92abcc1a704d Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sat, 31 Oct 2020 10:39:53 +0100 Subject: [PATCH 068/173] Fix crash unplugging focused mon 2 Focus the top client on newmon, which we know for sure that it isn't going to be unplugged or disabled and actually set that as the focused monitor to move the focus. This is necessary to prevent crash when disabling monitors with the output-management patch. --- dwl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 8852f8f..69deeb5 100644 --- a/dwl.c +++ b/dwl.c @@ -706,7 +706,8 @@ closemon(Monitor *m, Monitor *newmon) // move all the clients on a closed monitor to another one Client *c; - focusclient(selclient(), focustop(dirtomon(-1)), 1); + selmon = newmon; + focusclient(selclient(), focustop(newmon), 1); wl_list_for_each(c, &clients, link) { if (c->isfloating && c->geom.x > m->m.width) resize(c, c->geom.x - m->w.width, c->geom.y, From 25671d79051e9054f64de88e77e089a2daf7008f Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sat, 31 Oct 2020 12:43:07 +0100 Subject: [PATCH 069/173] Fix crash when disabling focused mon m->link.next leads to errors if the monitor to disable doesn't have a "next" (right) monitor and is currently focused. dirtmon() does more checks. In some previous commits m->link.next is told to be left monitor, which is wrong Also focusclient() explicitly checks for disabled monitors (this fixes in case of more than one disabled monitor) --- dwl.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/dwl.c b/dwl.c index a2d5092..ab17546 100644 --- a/dwl.c +++ b/dwl.c @@ -1459,16 +1459,26 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test) { struct wlr_output_configuration_head_v1 *config_head; bool ok = true; + Arg ar = {.i = -1}; wl_list_for_each(config_head, &config->heads, link) { struct wlr_output *wlr_output = config_head->state.output; Monitor *m, *newmon; + if (!config_head->state.enabled) { + wl_list_for_each(m, &mons, link) { + if (m->wlr_output->name == wlr_output->name) { + // make sure that the monitor to clean is focused + selmon = m; + focusclient(selclient(), focustop(selmon), 1); + + // focus the left monitor (relative to the current focus) + focusmon(&ar); + closemon(m, wl_container_of(&selmon->link, newmon, link)); + } + } + } wlr_output_enable(wlr_output, config_head->state.enabled); - if (!config_head->state.enabled) - wl_list_for_each(m, &mons, link) - if (m->wlr_output->name == wlr_output->name) - closemon(m, wl_container_of(m->link.next, newmon, link)); if (config_head->state.enabled) { if (config_head->state.mode) From 9f3f15b467a1ea723cd09d9943a27cfb50aa0af8 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sat, 31 Oct 2020 13:00:54 +0100 Subject: [PATCH 070/173] Disable mon faster Since focusmon() now never focuses disabled monitors, there's no need to focus the disabled monitor first --- dwl.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/dwl.c b/dwl.c index ab17546..9b8dda2 100644 --- a/dwl.c +++ b/dwl.c @@ -1463,22 +1463,18 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test) wl_list_for_each(config_head, &config->heads, link) { struct wlr_output *wlr_output = config_head->state.output; - Monitor *m, *newmon; + Monitor *m; + wlr_output_enable(wlr_output, config_head->state.enabled); if (!config_head->state.enabled) { wl_list_for_each(m, &mons, link) { if (m->wlr_output->name == wlr_output->name) { - // make sure that the monitor to clean is focused - selmon = m; - focusclient(selclient(), focustop(selmon), 1); - // focus the left monitor (relative to the current focus) focusmon(&ar); - closemon(m, wl_container_of(&selmon->link, newmon, link)); + closemon(m, selmon); } } } - wlr_output_enable(wlr_output, config_head->state.enabled); if (config_head->state.enabled) { if (config_head->state.mode) From 9f0b16868a082175508c0c747054f324595d5b67 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sat, 31 Oct 2020 13:19:31 +0100 Subject: [PATCH 071/173] Back to closemon() with one parameter With the recent changes in output-management, the extra argument in closemon() would be needed only when unplugging the monitor, so it isn't worth it anymore. Also now is more efficient. --- dwl.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/dwl.c b/dwl.c index 69deeb5..ad481fa 100644 --- a/dwl.c +++ b/dwl.c @@ -204,7 +204,7 @@ static void chvt(const Arg *arg); static void cleanup(void); static void cleanupkeyboard(struct wl_listener *listener, void *data); static void cleanupmon(struct wl_listener *listener, void *data); -static void closemon(Monitor *m, Monitor *newmon); +static void closemon(Monitor *m); static void commitlayersurfacenotify(struct wl_listener *listener, void *data); static void commitnotify(struct wl_listener *listener, void *data); static void createkeyboard(struct wlr_input_device *device); @@ -688,7 +688,7 @@ void cleanupmon(struct wl_listener *listener, void *data) { struct wlr_output *wlr_output = data; - Monitor *m = wlr_output->data, *newmon; + Monitor *m = wlr_output->data; wl_list_remove(&m->destroy.link); wl_list_remove(&m->frame.link); @@ -696,24 +696,26 @@ cleanupmon(struct wl_listener *listener, void *data) wlr_output_layout_remove(output_layout, m->wlr_output); updatemons(); - closemon(m, wl_container_of(mons.next, newmon, link)); + + selmon = wl_container_of(mons.next, selmon, link); + focusclient(selclient(), focustop(selmon), 1); + closemon(m); + free(m); } void -closemon(Monitor *m, Monitor *newmon) +closemon(Monitor *m) { - // move all the clients on a closed monitor to another one + // move closed monitor's clients to the focused one Client *c; - selmon = newmon; - focusclient(selclient(), focustop(newmon), 1); wl_list_for_each(c, &clients, link) { if (c->isfloating && c->geom.x > m->m.width) resize(c, c->geom.x - m->w.width, c->geom.y, c->geom.width, c->geom.height, 0); if (c->mon == m) - setmon(c, newmon, c->tags); + setmon(c, selmon, c->tags); } } From 934ce085b6f4784517d4111ec5c127553a19337d Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sat, 31 Oct 2020 16:18:23 +0100 Subject: [PATCH 072/173] Don't switch to disabled mons after unplug --- dwl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index eeecb75..78fac21 100644 --- a/dwl.c +++ b/dwl.c @@ -704,7 +704,9 @@ cleanupmon(struct wl_listener *listener, void *data) updatemons(); - selmon = wl_container_of(mons.next, selmon, link); + do // don't switch to disabled mons + selmon = wl_container_of(mons.next, selmon, link); + while (!selmon->wlr_output->enabled); focusclient(selclient(), focustop(selmon), 1); closemon(m); From 5221a329e254627c186ba8e4d68f9595d4e3158a Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sat, 31 Oct 2020 16:25:46 +0100 Subject: [PATCH 073/173] closemon() has now only 1 parameter --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 78fac21..ca352c4 100644 --- a/dwl.c +++ b/dwl.c @@ -1475,7 +1475,7 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test) if (m->wlr_output->name == wlr_output->name) { // focus the left monitor (relative to the current focus) focusmon(&ar); - closemon(m, selmon); + closemon(m); } } } From 80a685ee51da69ae0a7bae11620c5f33c3cd2de2 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sat, 31 Oct 2020 17:10:06 +0100 Subject: [PATCH 074/173] Fix crash with no monitors left When there's no monitors left, prevent the while in cleanupmon() to become an infinite loop Also switch to the left monitors instead of the right --- dwl.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dwl.c b/dwl.c index ca352c4..d875eaa 100644 --- a/dwl.c +++ b/dwl.c @@ -696,20 +696,19 @@ cleanupmon(struct wl_listener *listener, void *data) { struct wlr_output *wlr_output = data; Monitor *m = wlr_output->data; + int nmons = wl_list_length(&mons), i = 0; wl_list_remove(&m->destroy.link); wl_list_remove(&m->frame.link); wl_list_remove(&m->link); wlr_output_layout_remove(output_layout, m->wlr_output); - updatemons(); do // don't switch to disabled mons - selmon = wl_container_of(mons.next, selmon, link); - while (!selmon->wlr_output->enabled); + selmon = wl_container_of(mons.prev, selmon, link); + while (!selmon->wlr_output->enabled && i++ < nmons); focusclient(selclient(), focustop(selmon), 1); closemon(m); - free(m); } From d9ab75721af57ad6b19cb167d7e762b5496a275b Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sat, 31 Oct 2020 18:29:33 +0100 Subject: [PATCH 075/173] Don't switch to another disabled monitors Since wlr_output_enable doesn't have any effect before finishing all the procedure, a little hack allows to make use of focusmon(), which must know the latest in about which output is currently disabled Also improve performance in focusmon() and cleaner code in outputmgrapplyortest() --- dwl.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/dwl.c b/dwl.c index d875eaa..11e6ad1 100644 --- a/dwl.c +++ b/dwl.c @@ -1092,11 +1092,11 @@ void focusmon(const Arg *arg) { Client *sel; - do { - sel = selclient(); + do selmon = dirtomon(arg->i); - focusclient(sel, focustop(selmon), 1); - } while (!selmon->wlr_output->enabled); + while (!selmon->wlr_output->enabled); + sel = selclient(); + focusclient(sel, focustop(selmon), 1); } void @@ -1466,19 +1466,8 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test) wl_list_for_each(config_head, &config->heads, link) { struct wlr_output *wlr_output = config_head->state.output; - Monitor *m; wlr_output_enable(wlr_output, config_head->state.enabled); - if (!config_head->state.enabled) { - wl_list_for_each(m, &mons, link) { - if (m->wlr_output->name == wlr_output->name) { - // focus the left monitor (relative to the current focus) - focusmon(&ar); - closemon(m); - } - } - } - if (config_head->state.enabled) { if (config_head->state.mode) wlr_output_set_mode(wlr_output, config_head->state.mode); @@ -1492,6 +1481,17 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test) config_head->state.x, config_head->state.y); wlr_output_set_transform(wlr_output, config_head->state.transform); wlr_output_set_scale(wlr_output, config_head->state.scale); + } else { + Monitor *m; + wl_list_for_each(m, &mons, link) { + if (m->wlr_output->name == wlr_output->name) { + // focus the left monitor (relative to the current focus) + m->wlr_output->enabled = !m->wlr_output->enabled; + focusmon(&ar); + closemon(m); + m->wlr_output->enabled = !m->wlr_output->enabled; + } + } } if (test) { From 95788164874890ec99dbc23a4e460b1781cc7be3 Mon Sep 17 00:00:00 2001 From: Keating950 Date: Sun, 22 Nov 2020 12:58:49 -0500 Subject: [PATCH 076/173] add install target to Makefile and corresponding prefix variable to config.mk --- Makefile | 5 +++++ config.mk | 3 +++ 2 files changed, 8 insertions(+) diff --git a/Makefile b/Makefile index 56ab751..7f17cc6 100644 --- a/Makefile +++ b/Makefile @@ -33,5 +33,10 @@ dwl: xdg-shell-protocol.o clean: rm -f dwl *.o xdg-shell-protocol.h xdg-shell-protocol.c +install: dwl + mkdir -p $(PREFIX)/bin + cp -f dwl $(PREFIX)/bin + chmod 755 $(PREFIX)/bin/dwl + .DEFAULT_GOAL=dwl .PHONY: clean diff --git a/config.mk b/config.mk index a101f23..3958049 100644 --- a/config.mk +++ b/config.mk @@ -1,3 +1,6 @@ +# paths +PREFIX = /usr/local + # Default compile flags (overridable by environment) CFLAGS ?= -g -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare -Wno-error=unused-function From 67896e9d8b98f679faf4456e26e82057c1884789 Mon Sep 17 00:00:00 2001 From: Richard Ipsum Date: Mon, 23 Nov 2020 16:58:43 +0000 Subject: [PATCH 077/173] fix undeclared WLR_KEY_PRESSED --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 730e46a..00b544f 100644 --- a/dwl.c +++ b/dwl.c @@ -856,7 +856,7 @@ keypress(struct wl_listener *listener, void *data) int handled = 0; uint32_t mods = wlr_keyboard_get_modifiers(kb->device->keyboard); /* On _press_, attempt to process a compositor keybinding. */ - if (event->state == WLR_KEY_PRESSED) + if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) for (i = 0; i < nsyms; i++) handled = keybinding(mods, syms[i]) || handled; From ee5bd9a643cff9cceb8a34084da8f0657dc54359 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 19 Dec 2020 12:18:00 +0100 Subject: [PATCH 078/173] fix temporarily disabling a single monitor The code in this else completely freezes my system when I run the swayidle command to replicate xset dpms force off. No idea if it works on multiple monitors, but for now avoid running when there's 1 monitor. Also remove the comment with the function name in sway. --- dwl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index 11e6ad1..679a30b 100644 --- a/dwl.c +++ b/dwl.c @@ -1456,7 +1456,6 @@ outputmgrapply(struct wl_listener *listener, void *data) outputmgrapplyortest(config, false); } -// apply_output_config void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test) { @@ -1481,7 +1480,7 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test) config_head->state.x, config_head->state.y); wlr_output_set_transform(wlr_output, config_head->state.transform); wlr_output_set_scale(wlr_output, config_head->state.scale); - } else { + } else if (wl_list_length(&mons) > 1) { Monitor *m; wl_list_for_each(m, &mons, link) { if (m->wlr_output->name == wlr_output->name) { From 64faad7cb6dfc59832ef3249b606df89c23327f8 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Thu, 10 Dec 2020 12:56:35 +0100 Subject: [PATCH 079/173] implement the wlr-data-transfer protocol It makes wl-clipboard work properly in neovim, without having to create a transparent surface that steals focus and causes flickering. It's also required for clipman. --- dwl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dwl.c b/dwl.c index 679a30b..1b92a3e 100644 --- a/dwl.c +++ b/dwl.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -1971,6 +1972,7 @@ setup(void) compositor = wlr_compositor_create(dpy, drw); wlr_export_dmabuf_manager_v1_create(dpy); wlr_screencopy_manager_v1_create(dpy); + wlr_data_control_manager_v1_create(dpy); wlr_data_device_manager_create(dpy); wlr_gamma_control_manager_v1_create(dpy); wlr_primary_selection_v1_device_manager_create(dpy); From 1024928c15cebbeb1c652a6ba4a243f1c330bac8 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 19 Dec 2020 06:57:14 +0100 Subject: [PATCH 080/173] deactivate focused client when spawning a new one Because maprequest immediately calls wl_list_insert(&fstack, &c->flink), in the following call to setmon(), the selclient() which is passed to focusclient() as the old client is actually the newly mapped client, and the real old one is never deactivated. You can see this by, for example, opening Chromium's devtools, then spawning a terminal. The background of the focused line in the devtools doesn't change from light blue to grey. We can't just remove wl_list_insert(&fstack, &c->flink) from maprequest, because calling wl_list_remove in focusclient() with a client that has not been added to the list causes a segmentation fault. Therefore we fix the focusclient call by not passing it the old client every time, but instead using the wlroots function that gets the focused surface and deactivate that, like in TinyWL. This also avoids getting the selected client and passing it to focusclient() on every call unnecessarily, and will allow removing shouldfocusclients in a future commit by checking if old is a layer surface instead. --- dwl.c | 50 ++++++++++++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/dwl.c b/dwl.c index 1b92a3e..cb5341a 100644 --- a/dwl.c +++ b/dwl.c @@ -220,7 +220,7 @@ static void destroylayersurfacenotify(struct wl_listener *listener, void *data); static void destroynotify(struct wl_listener *listener, void *data); static void destroyxdeco(struct wl_listener *listener, void *data); static Monitor *dirtomon(int dir); -static void focusclient(Client *old, Client *c, int lift); +static void focusclient(Client *c, int lift); static void focusmon(const Arg *arg); static void focusstack(const Arg *arg); static Client *focustop(Monitor *m); @@ -630,7 +630,7 @@ buttonpress(struct wl_listener *listener, void *data) case WLR_BUTTON_PRESSED:; /* Change focus if the button was _pressed_ over a client */ if ((c = xytoclient(cursor->x, cursor->y))) - focusclient(selclient(), c, 1); + focusclient(c, 1); keyboard = wlr_seat_get_keyboard(seat); mods = wlr_keyboard_get_modifiers(keyboard); @@ -708,7 +708,7 @@ cleanupmon(struct wl_listener *listener, void *data) do // don't switch to disabled mons selmon = wl_container_of(mons.prev, selmon, link); while (!selmon->wlr_output->enabled && i++ < nmons); - focusclient(selclient(), focustop(selmon), 1); + focusclient(focustop(selmon), 1); closemon(m); free(m); } @@ -1039,9 +1039,10 @@ dirtomon(int dir) } void -focusclient(Client *old, Client *c, int lift) +focusclient(Client *c, int lift) { struct wlr_keyboard *kb = wlr_seat_get_keyboard(seat); + struct wlr_surface *old = seat->keyboard_state.focused_surface; /* Raise client in stacking order if requested */ if (c && lift) { @@ -1050,17 +1051,19 @@ focusclient(Client *old, Client *c, int lift) } /* Nothing else to do? */ - if (c == old) + if (c && WLR_SURFACE(c) == old) return; /* Deactivate old client if focus is changing */ - if (c != old && old) { + if (old && (!c || WLR_SURFACE(c) != old)) { + if (wlr_surface_is_xdg_surface(old)) + wlr_xdg_toplevel_set_activated( + wlr_xdg_surface_from_wlr_surface(old), false); #ifdef XWAYLAND - if (old->type != XDGShell) - wlr_xwayland_surface_activate(old->surface.xwayland, 0); - else + else if (wlr_surface_is_xwayland_surface(old)) + wlr_xwayland_surface_activate( + wlr_xwayland_surface_from_wlr_surface(old), false); #endif - wlr_xdg_toplevel_set_activated(old->surface.xdg, 0); } /* Update wlroots' keyboard focus */ @@ -1092,12 +1095,10 @@ focusclient(Client *old, Client *c, int lift) void focusmon(const Arg *arg) { - Client *sel; do selmon = dirtomon(arg->i); while (!selmon->wlr_output->enabled); - sel = selclient(); - focusclient(sel, focustop(selmon), 1); + focusclient(focustop(selmon), 1); } void @@ -1123,7 +1124,7 @@ focusstack(const Arg *arg) } } /* If only one client is visible on selmon, then c == sel */ - focusclient(sel, c, 1); + focusclient(c, 1); } Client * @@ -1549,7 +1550,7 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, #endif if (sloppyfocus) - focusclient(selclient(), c, 0); + focusclient(c, 0); } void @@ -1893,7 +1894,6 @@ void setmon(Client *c, Monitor *m, unsigned int newtags) { Monitor *oldmon = c->mon; - Client *oldsel = selclient(); if (oldmon == m) return; @@ -1911,7 +1911,7 @@ setmon(Client *c, Monitor *m, unsigned int newtags) c->tags = newtags ? newtags : m->tagset[m->seltags]; /* assign tags of target monitor */ arrange(m); } - focusclient(oldsel, focustop(selmon), 1); + focusclient(focustop(selmon), 1); } void @@ -2119,7 +2119,7 @@ tag(const Arg *arg) Client *sel = selclient(); if (sel && arg->ui & TAGMASK) { sel->tags = arg->ui & TAGMASK; - focusclient(sel, focustop(selmon), 1); + focusclient(focustop(selmon), 1); arrange(selmon); } } @@ -2186,7 +2186,7 @@ toggletag(const Arg *arg) newtags = sel->tags ^ (arg->ui & TAGMASK); if (newtags) { sel->tags = newtags; - focusclient(sel, focustop(selmon), 1); + focusclient(focustop(selmon), 1); arrange(selmon); } } @@ -2194,12 +2194,11 @@ toggletag(const Arg *arg) void toggleview(const Arg *arg) { - Client *sel = selclient(); unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); if (newtagset) { selmon->tagset[selmon->seltags] = newtagset; - focusclient(sel, focustop(selmon), 1); + focusclient(focustop(selmon), 1); arrange(selmon); } } @@ -2210,7 +2209,7 @@ unmaplayersurface(LayerSurface *layersurface) layersurface->layer_surface->mapped = false; if (layersurface->layer_surface->surface == seat->keyboard_state.focused_surface) - focusclient(NULL, selclient(), 1); + focusclient(selclient(), 1); motionnotify(0); } @@ -2266,13 +2265,12 @@ updatemons() void view(const Arg *arg) { - Client *sel = selclient(); if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) return; selmon->seltags ^= 1; /* toggle sel tagset */ if (arg->ui & TAGMASK) selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; - focusclient(sel, focustop(selmon), 1); + focusclient(focustop(selmon), 1); arrange(selmon); } @@ -2319,7 +2317,7 @@ xytomon(double x, double y) void zoom(const Arg *arg) { - Client *c, *sel = selclient(), *oldsel = sel; + Client *c, *sel = selclient(); if (!sel || !selmon->lt[selmon->sellt]->arrange || sel->isfloating) return; @@ -2344,7 +2342,7 @@ zoom(const Arg *arg) wl_list_remove(&sel->link); wl_list_insert(&clients, &sel->link); - focusclient(oldsel, sel, 1); + focusclient(sel, 1); arrange(selmon); } From 41cc23e1cfc248eb10878fd172ca969ed27a5f44 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Fri, 28 Aug 2020 20:18:42 +0200 Subject: [PATCH 081/173] Implement the idle protocol It allows clients such as swayidle and chat applications to monitor user idle time. --- Makefile | 14 +++++++++++-- dwl.c | 12 ++++++++++++ protocols/idle.xml | 49 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 protocols/idle.xml diff --git a/Makefile b/Makefile index 3dc2336..abbbe39 100644 --- a/Makefile +++ b/Makefile @@ -33,12 +33,22 @@ wlr-layer-shell-unstable-v1-protocol.c: wlr-layer-shell-unstable-v1-protocol.o: wlr-layer-shell-unstable-v1-protocol.h +idle-protocol.h: + $(WAYLAND_SCANNER) server-header \ + protocols/idle.xml $@ + +idle-protocol.c: + $(WAYLAND_SCANNER) private-code \ + protocols/idle.xml $@ + +idle-protocol.o: idle-protocol.h + config.h: | config.def.h cp config.def.h $@ -dwl.o: config.h xdg-shell-protocol.h wlr-layer-shell-unstable-v1-protocol.h +dwl.o: config.h xdg-shell-protocol.h wlr-layer-shell-unstable-v1-protocol.h idle-protocol.h -dwl: xdg-shell-protocol.o wlr-layer-shell-unstable-v1-protocol.o +dwl: xdg-shell-protocol.o wlr-layer-shell-unstable-v1-protocol.o idle-protocol.o clean: rm -f dwl *.o *-protocol.h *-protocol.c diff --git a/dwl.c b/dwl.c index cb5341a..1df7288 100644 --- a/dwl.c +++ b/dwl.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -292,6 +293,7 @@ static struct wl_list clients; /* tiling order */ static struct wl_list fstack; /* focus order */ static struct wl_list stack; /* stacking z-order */ static struct wl_list independents; +static struct wlr_idle *idle; static struct wlr_layer_shell_v1 *layer_shell; static struct wlr_xdg_decoration_manager_v1 *xdeco_mgr; static struct wlr_output_manager_v1 *output_mgr; @@ -611,6 +613,7 @@ axisnotify(struct wl_listener *listener, void *data) /* This event is forwarded by the cursor when a pointer emits an axis event, * for example when you move the scroll wheel. */ struct wlr_event_pointer_axis *event = data; + wlr_idle_notify_activity(idle, seat); /* Notify the client with pointer focus of the axis event. */ wlr_seat_pointer_notify_axis(seat, event->time_msec, event->orientation, event->delta, @@ -626,6 +629,8 @@ buttonpress(struct wl_listener *listener, void *data) Client *c; const Button *b; + wlr_idle_notify_activity(idle, seat); + switch (event->state) { case WLR_BUTTON_PRESSED:; /* Change focus if the button was _pressed_ over a client */ @@ -1217,6 +1222,9 @@ keypress(struct wl_listener *listener, void *data) int handled = 0; uint32_t mods = wlr_keyboard_get_modifiers(kb->device->keyboard); + + wlr_idle_notify_activity(idle, seat); + /* On _press_, attempt to process a compositor keybinding. */ if (event->state == WLR_KEY_PRESSED) for (i = 0; i < nsyms; i++) @@ -1346,6 +1354,8 @@ motionnotify(uint32_t time) time = now.tv_sec * 1000 + now.tv_nsec / 1000000; } + wlr_idle_notify_activity(idle, seat); + /* Update selmon (even while dragging a window) */ if (sloppyfocus) selmon = xytomon(cursor->x, cursor->y); @@ -1999,6 +2009,8 @@ setup(void) wl_list_init(&stack); wl_list_init(&independents); + idle = wlr_idle_create(dpy); + layer_shell = wlr_layer_shell_v1_create(dpy); wl_signal_add(&layer_shell->events.new_surface, &new_layer_shell_surface); diff --git a/protocols/idle.xml b/protocols/idle.xml new file mode 100644 index 0000000..92d9989 --- /dev/null +++ b/protocols/idle.xml @@ -0,0 +1,49 @@ + + + . + ]]> + + + This interface allows to monitor user idle time on a given seat. The interface + allows to register timers which trigger after no user activity was registered + on the seat for a given interval. It notifies when user activity resumes. + + This is useful for applications wanting to perform actions when the user is not + interacting with the system, e.g. chat applications setting the user as away, power + management features to dim screen, etc.. + + + + + + + + + + + + + + + + + + + + + + From 90cc3b1e2c824db74e932dbb9733d398619a037c Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Tue, 28 Jul 2020 09:54:08 +0200 Subject: [PATCH 082/173] Allow toggling the layout before selecting a different one --- dwl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 1df7288..497cfe5 100644 --- a/dwl.c +++ b/dwl.c @@ -825,7 +825,8 @@ createmon(struct wl_listener *listener, void *data) m->nmaster = r->nmaster; wlr_output_set_scale(wlr_output, r->scale); wlr_xcursor_manager_load(cursor_mgr, r->scale); - m->lt[0] = m->lt[1] = r->lt; + m->lt[0] = r->lt; + m->lt[1] = &layouts[LENGTH(layouts) > 1 && r->lt != &layouts[1]]; wlr_output_set_transform(wlr_output, r->rr); break; } From 0016a209e4ecb23159a887d6baf26063287a40a6 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Tue, 8 Dec 2020 12:17:14 +0100 Subject: [PATCH 083/173] implement the virtual keyboard protocol This is used by wtype. Also properly cleanup keyboards. Without wl_list_remove(&kb->link) dwl crashed after using wtype 2-3 times. --- dwl.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/dwl.c b/dwl.c index 497cfe5..d883d82 100644 --- a/dwl.c +++ b/dwl.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -275,6 +276,7 @@ static void unmaplayersurfacenotify(struct wl_listener *listener, void *data); static void unmapnotify(struct wl_listener *listener, void *data); static void updatemons(); static void view(const Arg *arg); +static void virtualkeyboard(struct wl_listener *listener, void *data); static Client *xytoclient(double x, double y); static struct wlr_surface *xytolayersurface(struct wl_list *layer_surfaces, double x, double y, double *sx, double *sy); @@ -297,6 +299,7 @@ static struct wlr_idle *idle; static struct wlr_layer_shell_v1 *layer_shell; static struct wlr_xdg_decoration_manager_v1 *xdeco_mgr; static struct wlr_output_manager_v1 *output_mgr; +static struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard_mgr; static struct wlr_cursor *cursor; static struct wlr_xcursor_manager *cursor_mgr; @@ -319,6 +322,7 @@ static struct wl_listener cursor_frame = {.notify = cursorframe}; static struct wl_listener cursor_motion = {.notify = motionrelative}; static struct wl_listener cursor_motion_absolute = {.notify = motionabsolute}; static struct wl_listener new_input = {.notify = inputdevice}; +static struct wl_listener new_virtual_keyboard = {.notify = virtualkeyboard}; static struct wl_listener new_output = {.notify = createmon}; static struct wl_listener new_xdeco = {.notify = createxdeco}; static struct wl_listener new_xdg_surface = {.notify = createnotify}; @@ -693,6 +697,9 @@ cleanupkeyboard(struct wl_listener *listener, void *data) struct wlr_input_device *device = data; Keyboard *kb = device->data; + wl_list_remove(&kb->link); + wl_list_remove(&kb->modifiers.link); + wl_list_remove(&kb->key.link); wl_list_remove(&kb->destroy.link); free(kb); } @@ -2062,6 +2069,9 @@ setup(void) */ wl_list_init(&keyboards); wl_signal_add(&backend->events.new_input, &new_input); + virtual_keyboard_mgr = wlr_virtual_keyboard_manager_v1_create(dpy); + wl_signal_add(&virtual_keyboard_mgr->events.new_virtual_keyboard, + &new_virtual_keyboard); seat = wlr_seat_create(dpy, "seat0"); wl_signal_add(&seat->events.request_set_cursor, &request_cursor); @@ -2287,6 +2297,14 @@ view(const Arg *arg) arrange(selmon); } +void +virtualkeyboard(struct wl_listener *listener, void *data) +{ + struct wlr_virtual_keyboard_v1 *keyboard = data; + struct wlr_input_device *device = &keyboard->input_device; + createkeyboard(device); +} + Client * xytoclient(double x, double y) { From 0f48c9552eaec29e1aac0165bc0e472b05e17ecd Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Tue, 8 Dec 2020 15:10:41 +0100 Subject: [PATCH 084/173] handle the x11 configure event This fixes the window size of old games in Wine. --- dwl.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/dwl.c b/dwl.c index d883d82..924823b 100644 --- a/dwl.c +++ b/dwl.c @@ -96,6 +96,7 @@ typedef struct { } surface; #ifdef XWAYLAND struct wl_listener activate; + struct wl_listener configure; #endif struct wl_listener commit; struct wl_listener map; @@ -335,6 +336,7 @@ static struct wl_listener request_set_sel = {.notify = setsel}; #ifdef XWAYLAND static void activatex11(struct wl_listener *listener, void *data); +static void configurex11(struct wl_listener *listener, void *data); static void createnotifyx11(struct wl_listener *listener, void *data); static Atom getatom(xcb_connection_t *xc, const char *name); static void renderindependents(struct wlr_output *output, struct timespec *now); @@ -2388,6 +2390,15 @@ activatex11(struct wl_listener *listener, void *data) wlr_xwayland_surface_activate(c->surface.xwayland, 1); } +void +configurex11(struct wl_listener *listener, void *data) +{ + Client *c = wl_container_of(listener, c, configure); + struct wlr_xwayland_surface_configure_event *event = data; + wlr_xwayland_surface_configure(c->surface.xwayland, + event->x, event->y, event->width, event->height); +} + void createnotifyx11(struct wl_listener *listener, void *data) { @@ -2407,6 +2418,8 @@ createnotifyx11(struct wl_listener *listener, void *data) wl_signal_add(&xwayland_surface->events.unmap, &c->unmap); c->activate.notify = activatex11; wl_signal_add(&xwayland_surface->events.request_activate, &c->activate); + c->configure.notify = configurex11; + wl_signal_add(&xwayland_surface->events.request_configure, &c->configure); c->destroy.notify = destroynotify; wl_signal_add(&xwayland_surface->events.destroy, &c->destroy); } From 0f04c76387f74827931db0c3692fbdc2f50fa8d8 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Fri, 4 Sep 2020 12:44:09 +0200 Subject: [PATCH 085/173] Basic fullscreen --- dwl.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/dwl.c b/dwl.c index 924823b..4c97cc4 100644 --- a/dwl.c +++ b/dwl.c @@ -102,6 +102,7 @@ typedef struct { struct wl_listener map; struct wl_listener unmap; struct wl_listener destroy; + struct wl_listener fullscreen; struct wlr_box geom; /* layout-relative, includes border */ Monitor *mon; #ifdef XWAYLAND @@ -222,6 +223,7 @@ static void cursorframe(struct wl_listener *listener, void *data); static void destroylayersurfacenotify(struct wl_listener *listener, void *data); static void destroynotify(struct wl_listener *listener, void *data); static void destroyxdeco(struct wl_listener *listener, void *data); +static void fullscreenotify(struct wl_listener *listener, void *data); static Monitor *dirtomon(int dir); static void focusclient(Client *c, int lift); static void focusmon(const Arg *arg); @@ -909,6 +911,9 @@ createnotify(struct wl_listener *listener, void *data) wl_signal_add(&xdg_surface->events.unmap, &c->unmap); c->destroy.notify = destroynotify; wl_signal_add(&xdg_surface->events.destroy, &c->destroy); + + wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, &c->fullscreen); + c->fullscreen.notify = fullscreenotify; } void @@ -1037,6 +1042,12 @@ destroyxdeco(struct wl_listener *listener, void *data) free(d); } +void +fullscreenotify(struct wl_listener *listener, void *data) { + Client *c = wl_container_of(listener, c, fullscreen); + wlr_xdg_toplevel_set_fullscreen(c->surface.xdg, true); +} + Monitor * dirtomon(int dir) { From dd3920e75d2e71c569c3781914cd388f69f45bc9 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Fri, 4 Sep 2020 14:04:19 +0200 Subject: [PATCH 086/173] Toggle fullscreen --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 4c97cc4..c650875 100644 --- a/dwl.c +++ b/dwl.c @@ -1045,7 +1045,7 @@ destroyxdeco(struct wl_listener *listener, void *data) void fullscreenotify(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, fullscreen); - wlr_xdg_toplevel_set_fullscreen(c->surface.xdg, true); + wlr_xdg_toplevel_set_fullscreen(c->surface.xdg, !c->surface.xdg->toplevel->current.fullscreen); } Monitor * From de6db9740104843fced2f7594f58948beb021da4 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Fri, 4 Sep 2020 17:09:12 +0200 Subject: [PATCH 087/173] No borders on fullscreen windows Some code has been borrowed from the smartBorders patch --- dwl.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index c650875..26d977d 100644 --- a/dwl.c +++ b/dwl.c @@ -1045,7 +1045,9 @@ destroyxdeco(struct wl_listener *listener, void *data) void fullscreenotify(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, fullscreen); - wlr_xdg_toplevel_set_fullscreen(c->surface.xdg, !c->surface.xdg->toplevel->current.fullscreen); + wlr_xdg_toplevel_set_fullscreen( + c->surface.xdg, !c->surface.xdg->toplevel->current.fullscreen); + c->bw = (int)c->surface.xdg->toplevel->current.fullscreen * borderpx; } Monitor * @@ -1670,6 +1672,10 @@ renderclients(Monitor *m, struct timespec *now) ox = c->geom.x, oy = c->geom.y; wlr_output_layout_output_coords(output_layout, m->wlr_output, &ox, &oy); + + if (c->bw == 0) + goto render; + w = surface->current.width; h = surface->current.height; borders = (struct wlr_box[4]) { @@ -1687,6 +1693,7 @@ renderclients(Monitor *m, struct timespec *now) m->wlr_output->transform_matrix); } + render: /* This calls our render function for each surface among the * xdg_surface's toplevel and popups. */ rdata.output = m->wlr_output; From 60a732ceb8a436ddc511d2be5feb31a6186f3bf6 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Fri, 4 Sep 2020 17:11:26 +0200 Subject: [PATCH 088/173] Restore windows after fullscreen Store position and size of windows before going fullscreen. This is more efficient than arrange() and also works with floating windows All the clients keep their original position because arrange() isn't used after quitting fullscreen --- dwl.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/dwl.c b/dwl.c index 26d977d..554b9a0 100644 --- a/dwl.c +++ b/dwl.c @@ -112,6 +112,10 @@ typedef struct { unsigned int tags; int isfloating; uint32_t resize; /* configure serial of a pending resize */ + int prevx; + int prevy; + int prevwidth; + int prevheight; } Client; typedef struct { @@ -1048,6 +1052,16 @@ fullscreenotify(struct wl_listener *listener, void *data) { wlr_xdg_toplevel_set_fullscreen( c->surface.xdg, !c->surface.xdg->toplevel->current.fullscreen); c->bw = (int)c->surface.xdg->toplevel->current.fullscreen * borderpx; + + if (c->surface.xdg->toplevel->current.fullscreen) { /* fullscreen off */ + resize(c, c->prevx, c->prevy, c->prevwidth, c->prevheight, 0); + } else { /* fullscreen on */ + c->prevx = c->geom.x; + c->prevy = c->geom.y; + c->prevheight = c->geom.height; + c->prevwidth = c->geom.width; + resize(c, c->mon->w.x, c->mon->w.y, c->mon->w.width, c->mon->w.height, 0); + } } Monitor * From 573535c89c5bd470d2e820851a0b5ef54395b4dc Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sat, 5 Sep 2020 10:37:59 +0200 Subject: [PATCH 089/173] Unlink fullscreen --- dwl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dwl.c b/dwl.c index 554b9a0..c5ed398 100644 --- a/dwl.c +++ b/dwl.c @@ -1026,6 +1026,7 @@ destroynotify(struct wl_listener *listener, void *data) wl_list_remove(&c->map.link); wl_list_remove(&c->unmap.link); wl_list_remove(&c->destroy.link); + wl_list_remove(&c->fullscreen.link); #ifdef XWAYLAND if (c->type == X11Managed) wl_list_remove(&c->activate.link); From 64113a682f64065d6cc5df186cbecdc28565152d Mon Sep 17 00:00:00 2001 From: Stivvo Date: Fri, 4 Sep 2020 19:20:07 +0200 Subject: [PATCH 090/173] Fullscreen xwayland --- dwl.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index c5ed398..bafc9b1 100644 --- a/dwl.c +++ b/dwl.c @@ -116,6 +116,7 @@ typedef struct { int prevy; int prevwidth; int prevheight; + bool isfullscreen; } Client; typedef struct { @@ -344,6 +345,7 @@ static struct wl_listener request_set_sel = {.notify = setsel}; static void activatex11(struct wl_listener *listener, void *data); static void configurex11(struct wl_listener *listener, void *data); static void createnotifyx11(struct wl_listener *listener, void *data); +static void fullscreenotifyx11(struct wl_listener *listener, void *data); static Atom getatom(xcb_connection_t *xc, const char *name); static void renderindependents(struct wlr_output *output, struct timespec *now); static void updatewindowtype(Client *c); @@ -916,8 +918,8 @@ createnotify(struct wl_listener *listener, void *data) c->destroy.notify = destroynotify; wl_signal_add(&xdg_surface->events.destroy, &c->destroy); - wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, &c->fullscreen); c->fullscreen.notify = fullscreenotify; + wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, &c->fullscreen); } void @@ -2455,6 +2457,34 @@ createnotifyx11(struct wl_listener *listener, void *data) wl_signal_add(&xwayland_surface->events.request_configure, &c->configure); c->destroy.notify = destroynotify; wl_signal_add(&xwayland_surface->events.destroy, &c->destroy); + + c->fullscreen.notify = fullscreenotifyx11; + wl_signal_add(&xwayland_surface->events.request_fullscreen, &c->fullscreen); + c->isfullscreen = false; +} + +void +fullscreenotifyx11(struct wl_listener *listener, void *data) { + FILE *xway = fopen("/tmp/dwl/xway", "a"); + Client *c; + c = wl_container_of(listener, c, fullscreen); + c->isfullscreen = !c->isfullscreen; + wlr_xwayland_surface_set_fullscreen( + c->surface.xwayland, c->isfullscreen); + c->bw = ((int)(!c->isfullscreen)) * borderpx; + + fprintf(xway, "fullscreen: %d\n", c->surface.xwayland->fullscreen); + fclose(xway); + + if (c->isfullscreen) { /* fullscreen off */ + c->prevx = c->geom.x; + c->prevy = c->geom.y; + c->prevheight = c->geom.height; + c->prevwidth = c->geom.width; + resize(c, c->mon->w.x, c->mon->w.y, c->mon->w.width, c->mon->w.height, 0); + } else { /* fullscreen on */ + resize(c, c->prevx, c->prevy, c->prevwidth, c->prevheight, 1); + } } Atom From af68b7109497853936ffabc3e6926095976b572f Mon Sep 17 00:00:00 2001 From: Stivvo Date: Fri, 4 Sep 2020 19:56:16 +0200 Subject: [PATCH 091/173] Same fscreen func for xdg and xwayland --- dwl.c | 46 ++++++++++++++-------------------------------- 1 file changed, 14 insertions(+), 32 deletions(-) diff --git a/dwl.c b/dwl.c index bafc9b1..44af864 100644 --- a/dwl.c +++ b/dwl.c @@ -345,7 +345,6 @@ static struct wl_listener request_set_sel = {.notify = setsel}; static void activatex11(struct wl_listener *listener, void *data); static void configurex11(struct wl_listener *listener, void *data); static void createnotifyx11(struct wl_listener *listener, void *data); -static void fullscreenotifyx11(struct wl_listener *listener, void *data); static Atom getatom(xcb_connection_t *xc, const char *name); static void renderindependents(struct wlr_output *output, struct timespec *now); static void updatewindowtype(Client *c); @@ -920,6 +919,7 @@ createnotify(struct wl_listener *listener, void *data) c->fullscreen.notify = fullscreenotify; wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, &c->fullscreen); + c->isfullscreen = false; } void @@ -1052,18 +1052,24 @@ destroyxdeco(struct wl_listener *listener, void *data) void fullscreenotify(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, fullscreen); - wlr_xdg_toplevel_set_fullscreen( - c->surface.xdg, !c->surface.xdg->toplevel->current.fullscreen); - c->bw = (int)c->surface.xdg->toplevel->current.fullscreen * borderpx; + c->isfullscreen = !c->isfullscreen; - if (c->surface.xdg->toplevel->current.fullscreen) { /* fullscreen off */ - resize(c, c->prevx, c->prevy, c->prevwidth, c->prevheight, 0); - } else { /* fullscreen on */ +#ifdef XWAYLAND + if (c->type == X11Managed) + wlr_xwayland_surface_set_fullscreen(c->surface.xwayland, c->isfullscreen); + else +#endif + wlr_xdg_toplevel_set_fullscreen(c->surface.xdg, c->isfullscreen); + + c->bw = ((int)(!c->isfullscreen)) * borderpx; + if (c->isfullscreen) { c->prevx = c->geom.x; c->prevy = c->geom.y; c->prevheight = c->geom.height; c->prevwidth = c->geom.width; resize(c, c->mon->w.x, c->mon->w.y, c->mon->w.width, c->mon->w.height, 0); + } else { + resize(c, c->prevx, c->prevy, c->prevwidth, c->prevheight, 0); } } @@ -2458,35 +2464,11 @@ createnotifyx11(struct wl_listener *listener, void *data) c->destroy.notify = destroynotify; wl_signal_add(&xwayland_surface->events.destroy, &c->destroy); - c->fullscreen.notify = fullscreenotifyx11; + c->fullscreen.notify = fullscreenotify; wl_signal_add(&xwayland_surface->events.request_fullscreen, &c->fullscreen); c->isfullscreen = false; } -void -fullscreenotifyx11(struct wl_listener *listener, void *data) { - FILE *xway = fopen("/tmp/dwl/xway", "a"); - Client *c; - c = wl_container_of(listener, c, fullscreen); - c->isfullscreen = !c->isfullscreen; - wlr_xwayland_surface_set_fullscreen( - c->surface.xwayland, c->isfullscreen); - c->bw = ((int)(!c->isfullscreen)) * borderpx; - - fprintf(xway, "fullscreen: %d\n", c->surface.xwayland->fullscreen); - fclose(xway); - - if (c->isfullscreen) { /* fullscreen off */ - c->prevx = c->geom.x; - c->prevy = c->geom.y; - c->prevheight = c->geom.height; - c->prevwidth = c->geom.width; - resize(c, c->mon->w.x, c->mon->w.y, c->mon->w.width, c->mon->w.height, 0); - } else { /* fullscreen on */ - resize(c, c->prevx, c->prevy, c->prevwidth, c->prevheight, 1); - } -} - Atom getatom(xcb_connection_t *xc, const char *name) { From 2abfd475dedf86c84fc66da42c51cff87251659f Mon Sep 17 00:00:00 2001 From: Stivvo Date: Fri, 4 Sep 2020 19:58:00 +0200 Subject: [PATCH 092/173] isfullscreen int --- dwl.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/dwl.c b/dwl.c index 44af864..25206d9 100644 --- a/dwl.c +++ b/dwl.c @@ -116,7 +116,7 @@ typedef struct { int prevy; int prevwidth; int prevheight; - bool isfullscreen; + int isfullscreen; } Client; typedef struct { @@ -919,7 +919,7 @@ createnotify(struct wl_listener *listener, void *data) c->fullscreen.notify = fullscreenotify; wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, &c->fullscreen); - c->isfullscreen = false; + c->isfullscreen = 0; } void @@ -1050,7 +1050,8 @@ destroyxdeco(struct wl_listener *listener, void *data) } void -fullscreenotify(struct wl_listener *listener, void *data) { +fullscreenotify(struct wl_listener *listener, void *data) +{ Client *c = wl_container_of(listener, c, fullscreen); c->isfullscreen = !c->isfullscreen; @@ -2466,7 +2467,7 @@ createnotifyx11(struct wl_listener *listener, void *data) c->fullscreen.notify = fullscreenotify; wl_signal_add(&xwayland_surface->events.request_fullscreen, &c->fullscreen); - c->isfullscreen = false; + c->isfullscreen = 0; } Atom From d41cc60ec102d06b6c3e41c587fbb05b3a45e05e Mon Sep 17 00:00:00 2001 From: Stivvo Date: Fri, 4 Sep 2020 23:32:29 +0200 Subject: [PATCH 093/173] Handle new windows Windows lose fullscreen state when a new window is created in the same tag --- dwl.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index 25206d9..bd87590 100644 --- a/dwl.c +++ b/dwl.c @@ -228,11 +228,11 @@ static void cursorframe(struct wl_listener *listener, void *data); static void destroylayersurfacenotify(struct wl_listener *listener, void *data); static void destroynotify(struct wl_listener *listener, void *data); static void destroyxdeco(struct wl_listener *listener, void *data); -static void fullscreenotify(struct wl_listener *listener, void *data); static Monitor *dirtomon(int dir); static void focusclient(Client *c, int lift); static void focusmon(const Arg *arg); static void focusstack(const Arg *arg); +static void fullscreenotify(struct wl_listener *listener, void *data); static Client *focustop(Monitor *m); static void getxdecomode(struct wl_listener *listener, void *data); static void incnmaster(const Arg *arg); @@ -251,6 +251,7 @@ static void moveresize(const Arg *arg); static void outputmgrapply(struct wl_listener *listener, void *data); static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test); static void outputmgrtest(struct wl_listener *listener, void *data); +static void quitfullscreen(Client *c); static void pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, uint32_t time); static void quit(const Arg *arg); @@ -887,6 +888,24 @@ createmon(struct wl_listener *listener, void *data) } } +void +quitfullscreen(Client *c) +{ + wl_list_for_each(c, &clients, link) { + if (c->isfullscreen && VISIBLEON(c, c->mon)) { +#ifdef XWAYLAND + if (c->type == X11Managed) + wlr_xwayland_surface_set_fullscreen(c->surface.xwayland, false); + else +#endif + wlr_xdg_toplevel_set_fullscreen(c->surface.xdg, false); + c->bw = borderpx; + resize(c, c->prevx, c->prevy, c->prevwidth, c->prevheight, 0); + c->isfullscreen = 0; + } + } +} + void createnotify(struct wl_listener *listener, void *data) { @@ -902,6 +921,7 @@ createnotify(struct wl_listener *listener, void *data) c = xdg_surface->data = calloc(1, sizeof(*c)); c->surface.xdg = xdg_surface; c->bw = borderpx; + quitfullscreen(c); /* Tell the client not to try anything fancy */ wlr_xdg_toplevel_set_tiled(c->surface.xdg, WLR_EDGE_TOP | @@ -986,7 +1006,6 @@ createxdeco(struct wl_listener *listener, void *data) getxdecomode(&d->request_mode, wlr_deco); } - void cursorframe(struct wl_listener *listener, void *data) { @@ -2452,6 +2471,7 @@ createnotifyx11(struct wl_listener *listener, void *data) c->surface.xwayland = xwayland_surface; c->type = xwayland_surface->override_redirect ? X11Unmanaged : X11Managed; c->bw = borderpx; + quitfullscreen(c); /* Listen to the various events it can emit */ c->map.notify = maprequest; From f125e1b9a4e05a5282765b0b699f8a097ea65783 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sat, 5 Sep 2020 11:22:24 +0200 Subject: [PATCH 094/173] Toggle fullscreen on all clients mod+e allows to toggle fullscreen any client, even those who don't support it themselves --- config.def.h | 1 + dwl.c | 21 ++++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/config.def.h b/config.def.h index 53021cf..d821a96 100644 --- a/config.def.h +++ b/config.def.h @@ -75,6 +75,7 @@ static const Key keys[] = { { MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} }, { MODKEY, XKB_KEY_space, setlayout, {0} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} }, + { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, { MODKEY, XKB_KEY_0, view, {.ui = ~0} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} }, { MODKEY, XKB_KEY_comma, focusmon, {.i = -1} }, diff --git a/dwl.c b/dwl.c index bd87590..bc2a3e8 100644 --- a/dwl.c +++ b/dwl.c @@ -267,6 +267,7 @@ static void setcursor(struct wl_listener *listener, void *data); static void setpsel(struct wl_listener *listener, void *data); static void setsel(struct wl_listener *listener, void *data); static void setfloating(Client *c, int floating); +static void setfullscreen(Client *c, int fullscreen); static void setlayout(const Arg *arg); static void setmfact(const Arg *arg); static void setmon(Client *c, Monitor *m, unsigned int newtags); @@ -278,6 +279,7 @@ static void tag(const Arg *arg); static void tagmon(const Arg *arg); static void tile(Monitor *m); static void togglefloating(const Arg *arg); +static void togglefullscreen(const Arg *arg); static void toggletag(const Arg *arg); static void toggleview(const Arg *arg); static void unmaplayersurface(LayerSurface *layersurface); @@ -1069,10 +1071,16 @@ destroyxdeco(struct wl_listener *listener, void *data) } void -fullscreenotify(struct wl_listener *listener, void *data) +togglefullscreen(const Arg *arg) { - Client *c = wl_container_of(listener, c, fullscreen); - c->isfullscreen = !c->isfullscreen; + Client *sel = selclient(); + setfullscreen(sel, !sel->isfullscreen); +} + +void +setfullscreen(Client *c, int fullscreen) +{ + c->isfullscreen = fullscreen; #ifdef XWAYLAND if (c->type == X11Managed) @@ -1093,6 +1101,13 @@ fullscreenotify(struct wl_listener *listener, void *data) } } +void +fullscreenotify(struct wl_listener *listener, void *data) +{ + Client *c = wl_container_of(listener, c, fullscreen); + setfullscreen(c, !c->isfullscreen); +} + Monitor * dirtomon(int dir) { From 5bd9be3a75b215b7f607ae46acf20765237925a8 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sun, 6 Sep 2020 10:27:24 +0200 Subject: [PATCH 095/173] Allow borderpx = 0 --- dwl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index bc2a3e8..85a27f2 100644 --- a/dwl.c +++ b/dwl.c @@ -1731,7 +1731,7 @@ renderclients(Monitor *m, struct timespec *now) wlr_output_layout_output_coords(output_layout, m->wlr_output, &ox, &oy); - if (c->bw == 0) + if (c->isfullscreen || borderpx == 0) goto render; w = surface->current.width; @@ -1751,7 +1751,7 @@ renderclients(Monitor *m, struct timespec *now) m->wlr_output->transform_matrix); } - render: +render: /* This calls our render function for each surface among the * xdg_surface's toplevel and popups. */ rdata.output = m->wlr_output; From 36b9831ffdec9c793a0f8afddbd9e9f9fd7f8865 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Thu, 10 Sep 2020 09:09:46 +0200 Subject: [PATCH 096/173] fix typo --- dwl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dwl.c b/dwl.c index 85a27f2..c051b5f 100644 --- a/dwl.c +++ b/dwl.c @@ -232,7 +232,7 @@ static Monitor *dirtomon(int dir); static void focusclient(Client *c, int lift); static void focusmon(const Arg *arg); static void focusstack(const Arg *arg); -static void fullscreenotify(struct wl_listener *listener, void *data); +static void fullscreennotify(struct wl_listener *listener, void *data); static Client *focustop(Monitor *m); static void getxdecomode(struct wl_listener *listener, void *data); static void incnmaster(const Arg *arg); @@ -939,7 +939,7 @@ createnotify(struct wl_listener *listener, void *data) c->destroy.notify = destroynotify; wl_signal_add(&xdg_surface->events.destroy, &c->destroy); - c->fullscreen.notify = fullscreenotify; + c->fullscreen.notify = fullscreennotify; wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, &c->fullscreen); c->isfullscreen = 0; } @@ -1102,7 +1102,7 @@ setfullscreen(Client *c, int fullscreen) } void -fullscreenotify(struct wl_listener *listener, void *data) +fullscreennotify(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, fullscreen); setfullscreen(c, !c->isfullscreen); @@ -2500,7 +2500,7 @@ createnotifyx11(struct wl_listener *listener, void *data) c->destroy.notify = destroynotify; wl_signal_add(&xwayland_surface->events.destroy, &c->destroy); - c->fullscreen.notify = fullscreenotify; + c->fullscreen.notify = fullscreennotify; wl_signal_add(&xwayland_surface->events.request_fullscreen, &c->fullscreen); c->isfullscreen = 0; } From cb9269df41c66cbd871fc9bc73657e736bee6a0d Mon Sep 17 00:00:00 2001 From: Stivvo Date: Wed, 16 Sep 2020 09:20:07 +0200 Subject: [PATCH 097/173] use m->m (fullscreen on top of layers) --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index c051b5f..ec8c7db 100644 --- a/dwl.c +++ b/dwl.c @@ -1095,7 +1095,7 @@ setfullscreen(Client *c, int fullscreen) c->prevy = c->geom.y; c->prevheight = c->geom.height; c->prevwidth = c->geom.width; - resize(c, c->mon->w.x, c->mon->w.y, c->mon->w.width, c->mon->w.height, 0); + resize(c, c->mon->m.x, c->mon->m.y, c->mon->m.width, c->mon->m.height, 0); } else { resize(c, c->prevx, c->prevy, c->prevwidth, c->prevheight, 0); } From 02a09cb85414df8002c61bd072c5870ab4f6a485 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Thu, 8 Oct 2020 21:04:28 +0200 Subject: [PATCH 098/173] Set fullscreen simpler --- dwl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dwl.c b/dwl.c index ec8c7db..e9daab6 100644 --- a/dwl.c +++ b/dwl.c @@ -1081,16 +1081,16 @@ void setfullscreen(Client *c, int fullscreen) { c->isfullscreen = fullscreen; + c->bw = (1 - fullscreen) * borderpx; #ifdef XWAYLAND if (c->type == X11Managed) - wlr_xwayland_surface_set_fullscreen(c->surface.xwayland, c->isfullscreen); + wlr_xwayland_surface_set_fullscreen(c->surface.xwayland, fullscreen); else #endif - wlr_xdg_toplevel_set_fullscreen(c->surface.xdg, c->isfullscreen); + wlr_xdg_toplevel_set_fullscreen(c->surface.xdg, fullscreen); - c->bw = ((int)(!c->isfullscreen)) * borderpx; - if (c->isfullscreen) { + if (fullscreen) { c->prevx = c->geom.x; c->prevy = c->geom.y; c->prevheight = c->geom.height; From 5a16649e799bab33babb935371253695d9dccc5c Mon Sep 17 00:00:00 2001 From: Stivvo Date: Thu, 8 Oct 2020 21:04:53 +0200 Subject: [PATCH 099/173] Keep windows fullscreen after redraw This fixes the bug that happens when changing workspace (or any time arrange() is called) where there are fullscreen windows, which are still fullscreen but leave the space for layer surfaces like waybar (which should be hidden when going fullscreen) Also as soon one fullscreen window is found hte function returns to improve efficiency --- dwl.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dwl.c b/dwl.c index e9daab6..8bf74b1 100644 --- a/dwl.c +++ b/dwl.c @@ -1405,6 +1405,10 @@ monocle(Monitor *m) wl_list_for_each(c, &clients, link) { if (!VISIBLEON(c, m) || c->isfloating) continue; + if (c->isfullscreen) { + resize(c, c->mon->m.x, c->mon->m.y, c->mon->m.width, c->mon->m.height, 0); + return; + } resize(c, m->w.x, m->w.y, m->w.width, m->w.height, 0); } } @@ -2254,6 +2258,10 @@ tile(Monitor *m) wl_list_for_each(c, &clients, link) { if (!VISIBLEON(c, m) || c->isfloating) continue; + if (c->isfullscreen) { + resize(c, c->mon->m.x, c->mon->m.y, c->mon->m.width, c->mon->m.height, 0); + return; + } if (i < m->nmaster) { h = (m->w.height - my) / (MIN(n, m->nmaster) - i); resize(c, m->w.x, m->w.y + my, mw, h, 0); From 32612c90b6314c99c07c52f9c3f0e4c4cd4f365c Mon Sep 17 00:00:00 2001 From: Stivvo Date: Wed, 14 Oct 2020 15:46:35 +0200 Subject: [PATCH 100/173] Delete quitfullscreen() quitfullscreen() was replicating the functionalities of setfullscreen(c, 0) Reusing setfullscreen() in quitfullscreen() leads to a 3 line function, which is useless since quitfullscreen() is used once anyway --- dwl.c | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/dwl.c b/dwl.c index 8bf74b1..7c5558a 100644 --- a/dwl.c +++ b/dwl.c @@ -251,7 +251,6 @@ static void moveresize(const Arg *arg); static void outputmgrapply(struct wl_listener *listener, void *data); static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test); static void outputmgrtest(struct wl_listener *listener, void *data); -static void quitfullscreen(Client *c); static void pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, uint32_t time); static void quit(const Arg *arg); @@ -890,24 +889,6 @@ createmon(struct wl_listener *listener, void *data) } } -void -quitfullscreen(Client *c) -{ - wl_list_for_each(c, &clients, link) { - if (c->isfullscreen && VISIBLEON(c, c->mon)) { -#ifdef XWAYLAND - if (c->type == X11Managed) - wlr_xwayland_surface_set_fullscreen(c->surface.xwayland, false); - else -#endif - wlr_xdg_toplevel_set_fullscreen(c->surface.xdg, false); - c->bw = borderpx; - resize(c, c->prevx, c->prevy, c->prevwidth, c->prevheight, 0); - c->isfullscreen = 0; - } - } -} - void createnotify(struct wl_listener *listener, void *data) { @@ -919,11 +900,14 @@ createnotify(struct wl_listener *listener, void *data) if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) return; + wl_list_for_each(c, &clients, link) + if (c->isfullscreen && VISIBLEON(c, c->mon)) + setfullscreen(c, 0); + /* Allocate a Client for this surface */ c = xdg_surface->data = calloc(1, sizeof(*c)); c->surface.xdg = xdg_surface; c->bw = borderpx; - quitfullscreen(c); /* Tell the client not to try anything fancy */ wlr_xdg_toplevel_set_tiled(c->surface.xdg, WLR_EDGE_TOP | From 1e134fde972597e2f10c2bdf40d1356dad5b2cb7 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Wed, 14 Oct 2020 17:28:51 +0200 Subject: [PATCH 101/173] Quit fullscreen on new x11 window After the removal of quitfullscreen() dwl wouldn't compile widh xwayland enabled because createnotifyx11 was still using the old function --- dwl.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dwl.c b/dwl.c index 7c5558a..32437b8 100644 --- a/dwl.c +++ b/dwl.c @@ -899,7 +899,6 @@ createnotify(struct wl_listener *listener, void *data) if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) return; - wl_list_for_each(c, &clients, link) if (c->isfullscreen && VISIBLEON(c, c->mon)) setfullscreen(c, 0); @@ -1719,7 +1718,7 @@ renderclients(Monitor *m, struct timespec *now) wlr_output_layout_output_coords(output_layout, m->wlr_output, &ox, &oy); - if (c->isfullscreen || borderpx == 0) + if (c->isfullscreen) goto render; w = surface->current.width; @@ -2472,13 +2471,15 @@ createnotifyx11(struct wl_listener *listener, void *data) { struct wlr_xwayland_surface *xwayland_surface = data; Client *c; + wl_list_for_each(c, &clients, link) + if (c->isfullscreen && VISIBLEON(c, c->mon)) + setfullscreen(c, 0); /* Allocate a Client for this surface */ c = xwayland_surface->data = calloc(1, sizeof(*c)); c->surface.xwayland = xwayland_surface; c->type = xwayland_surface->override_redirect ? X11Unmanaged : X11Managed; c->bw = borderpx; - quitfullscreen(c); /* Listen to the various events it can emit */ c->map.notify = maprequest; From 14ce0162136cd01064ae3bc650d9ffee36291de0 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Sat, 24 Oct 2020 16:51:22 +0200 Subject: [PATCH 102/173] Readme: achieve fullscreen + allow borderpx = 0 --- README.md | 1 - dwl.c | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fd83f92..43010dc 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,6 @@ dwl is a work in progress, and it has not yet reached its feature goals in a num - Urgent/attention/focus-request ([not yet supported](https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/9) by xdg-shell protocol) - Statusbar support (built-in or external) - Damage tracking -- Fullscreen/fixed windows (or whatever the Wayland analogues are) ## Acknowledgements diff --git a/dwl.c b/dwl.c index 32437b8..4c03717 100644 --- a/dwl.c +++ b/dwl.c @@ -1073,6 +1073,7 @@ setfullscreen(Client *c, int fullscreen) #endif wlr_xdg_toplevel_set_fullscreen(c->surface.xdg, fullscreen); + // restore previous size instead of arrange to work with floating windows if (fullscreen) { c->prevx = c->geom.x; c->prevy = c->geom.y; @@ -1717,8 +1718,7 @@ renderclients(Monitor *m, struct timespec *now) ox = c->geom.x, oy = c->geom.y; wlr_output_layout_output_coords(output_layout, m->wlr_output, &ox, &oy); - - if (c->isfullscreen) + if (c->bw == 0) goto render; w = surface->current.width; From c89de53de3e0867157730026a333e16430e71e5a Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 19 Dec 2020 18:23:23 +0100 Subject: [PATCH 103/173] remove togglefullscreen keybinding Distribute it as a patch like in dwm since graphical applications usually provide their own keybinding; I guess it's only for terminals. Note that even though these commits don't let you open multiple windows in fullscreen and cycle between them like in dwm, with just fullscreennotify spawning new windows or changing tag would still exit fullscreen automatically, but you would have to toggle fullscreen twice when switching back to the fullscreen window to enter fullscreen again, so this is better since it avoids that. --- config.def.h | 1 - dwl.c | 8 -------- 2 files changed, 9 deletions(-) diff --git a/config.def.h b/config.def.h index d821a96..53021cf 100644 --- a/config.def.h +++ b/config.def.h @@ -75,7 +75,6 @@ static const Key keys[] = { { MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} }, { MODKEY, XKB_KEY_space, setlayout, {0} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} }, - { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, { MODKEY, XKB_KEY_0, view, {.ui = ~0} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} }, { MODKEY, XKB_KEY_comma, focusmon, {.i = -1} }, diff --git a/dwl.c b/dwl.c index 4c03717..6d51236 100644 --- a/dwl.c +++ b/dwl.c @@ -278,7 +278,6 @@ static void tag(const Arg *arg); static void tagmon(const Arg *arg); static void tile(Monitor *m); static void togglefloating(const Arg *arg); -static void togglefullscreen(const Arg *arg); static void toggletag(const Arg *arg); static void toggleview(const Arg *arg); static void unmaplayersurface(LayerSurface *layersurface); @@ -1053,13 +1052,6 @@ destroyxdeco(struct wl_listener *listener, void *data) free(d); } -void -togglefullscreen(const Arg *arg) -{ - Client *sel = selclient(); - setfullscreen(sel, !sel->isfullscreen); -} - void setfullscreen(Client *c, int fullscreen) { From 9c2524b06a486cd01bfc06b73d10e2b903e3d82d Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 19 Dec 2020 18:31:26 +0100 Subject: [PATCH 104/173] s/prev/old Be consistent with the rest of the code and dwm --- dwl.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/dwl.c b/dwl.c index 6d51236..02efa43 100644 --- a/dwl.c +++ b/dwl.c @@ -112,10 +112,10 @@ typedef struct { unsigned int tags; int isfloating; uint32_t resize; /* configure serial of a pending resize */ - int prevx; - int prevy; - int prevwidth; - int prevheight; + int oldx; + int oldy; + int oldwidth; + int oldheight; int isfullscreen; } Client; @@ -1067,13 +1067,13 @@ setfullscreen(Client *c, int fullscreen) // restore previous size instead of arrange to work with floating windows if (fullscreen) { - c->prevx = c->geom.x; - c->prevy = c->geom.y; - c->prevheight = c->geom.height; - c->prevwidth = c->geom.width; + c->oldx = c->geom.x; + c->oldy = c->geom.y; + c->oldheight = c->geom.height; + c->oldwidth = c->geom.width; resize(c, c->mon->m.x, c->mon->m.y, c->mon->m.width, c->mon->m.height, 0); } else { - resize(c, c->prevx, c->prevy, c->prevwidth, c->prevheight, 0); + resize(c, c->oldx, c->oldy, c->oldwidth, c->oldheight, 0); } } From 679f6493c97df30cf6604778ac1ce6014dec952b Mon Sep 17 00:00:00 2001 From: Bonicgamer <44382222+Bonicgamer@users.noreply.github.com> Date: Tue, 8 Sep 2020 14:53:34 -0400 Subject: [PATCH 105/173] Made scalebox the way sway does it Scaling a wlr_box without rounding can sometimes make the borders not connected and nice. I noticed this when setting scale in monrules to 1.2 So I've went and copied what Sway did in desktop/output.c but without having a second function and now using a random rounding macro I found on the internet so as to not use round from math.h. --- dwl.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/dwl.c b/dwl.c index 02efa43..2257222 100644 --- a/dwl.c +++ b/dwl.c @@ -55,6 +55,7 @@ #define LENGTH(X) (sizeof X / sizeof X[0]) #define END(A) ((A) + LENGTH(A)) #define TAGMASK ((1 << LENGTH(tags)) - 1) +#define ROUND(X) ((X)>=0?(long)((X)+0.5):(long)((X)-0.5)) #ifdef XWAYLAND #define WLR_SURFACE(C) ((C)->type != XDGShell ? (C)->surface.xwayland->surface : (C)->surface.xdg->surface) #else @@ -1896,10 +1897,10 @@ run(char *startup_cmd) void scalebox(struct wlr_box *box, float scale) { - box->x *= scale; - box->y *= scale; - box->width *= scale; - box->height *= scale; + box->width = ROUND((box->x + box->width) * scale) - ROUND(box->x * scale); + box->height = ROUND((box->y + box->height) * scale) - ROUND(box->y * scale); + box->x = ROUND(box->x * scale); + box->y = ROUND(box->y * scale); } Client * From bfbfe9f2b2f2708c695d9b57590605802ca60f22 Mon Sep 17 00:00:00 2001 From: Oyren Date: Mon, 14 Sep 2020 20:45:39 +0200 Subject: [PATCH 106/173] remove log flags from readme The following commit has removed the logs but they are still in the readme. https://github.com/djpohly/dwl/commit/3b1992ca91b9a468019165c985263f5b1cc78c2c --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index 43010dc..1a8d8ec 100644 --- a/README.md +++ b/README.md @@ -49,12 +49,6 @@ dwl can be run as-is, with no arguments. In an existing Wayland or X11 session, You can also specify a startup program using the `-s` option. The argument to this option will be run at startup as a shell command (using `sh -c`) and can serve a similar function to `.xinitrc`: starting a service manager or other startup applications. Unlike `.xinitrc`, the display server will not shut down when this process terminates. Instead, as dwl is shutting down, it will send this process a SIGTERM and wait for it to terminate (if it hasn't already). This makes it ideal not only for initialization but also for execing into a user-level service manager like s6 or `systemd --user`. -More/less verbose output can be requested with flags as well: - -* `-q`: quiet (log level WLR_SILENT) -* `-v`: verbose (log level WLR_INFO) -* `-d`: debug (log level WLR_DEBUG) - Note: Wayland requires a valid `XDG_RUNTIME_DIR`, which is usually set up by a session manager such as `elogind` or `systemd-logind`. If your system doesn't do this automatically, you will need to configure it prior to launching `dwl`, e.g.: export XDG_RUNTIME_DIR=/tmp/xdg-runtime-$(id -u) From 4f1e557d3d26ee8359750dce8f370d404513a1ca Mon Sep 17 00:00:00 2001 From: will Date: Sat, 17 Oct 2020 13:52:53 +0200 Subject: [PATCH 107/173] Added basic tap-to-click for touchpad users --- Makefile | 2 +- config.def.h | 4 ++++ dwl.c | 6 ++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index abbbe39..bf0b957 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ CFLAGS += -I. -DWLR_USE_UNSTABLE -std=c99 -Werror=declaration-after-statement WAYLAND_PROTOCOLS=$(shell pkg-config --variable=pkgdatadir wayland-protocols) WAYLAND_SCANNER=$(shell pkg-config --variable=wayland_scanner wayland-scanner) -PKGS = wlroots wayland-server xcb xkbcommon +PKGS = wlroots wayland-server xcb xkbcommon libinput CFLAGS += $(foreach p,$(PKGS),$(shell pkg-config --cflags $(p))) LDLIBS += $(foreach p,$(PKGS),$(shell pkg-config --libs $(p))) diff --git a/config.def.h b/config.def.h index 53021cf..c1ad7d6 100644 --- a/config.def.h +++ b/config.def.h @@ -41,6 +41,10 @@ static const struct xkb_rule_names xkb_rules = { .options = "ctrl:nocaps", */ }; + +/* Trackpad */ +int tap_to_click = 1; + static const int repeat_rate = 25; static const int repeat_delay = 600; diff --git a/dwl.c b/dwl.c index 2257222..2907c5f 100644 --- a/dwl.c +++ b/dwl.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +39,7 @@ #include #include #include +#include #include #include #ifdef XWAYLAND @@ -970,6 +972,10 @@ createlayersurface(struct wl_listener *listener, void *data) void createpointer(struct wlr_input_device *device) { + struct libinput_device *libinput_device = (struct libinput_device*) + wlr_libinput_get_device_handle(device); + if (tap_to_click && libinput_device_config_tap_get_finger_count(libinput_device)) + libinput_device_config_tap_set_enabled(libinput_device, LIBINPUT_CONFIG_TAP_ENABLED); /* We don't do anything special with pointers. All of our pointer handling * is proxied through wlr_cursor. On another compositor, you might take this * opportunity to do libinput configuration on the device to set From aa679c4f29fd386e0bb89dd95ec2093f9c998ba8 Mon Sep 17 00:00:00 2001 From: will Date: Sat, 17 Oct 2020 16:18:44 +0200 Subject: [PATCH 108/173] Added support for natural scrolling --- config.def.h | 1 + dwl.c | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/config.def.h b/config.def.h index c1ad7d6..2c11fd3 100644 --- a/config.def.h +++ b/config.def.h @@ -44,6 +44,7 @@ static const struct xkb_rule_names xkb_rules = { /* Trackpad */ int tap_to_click = 1; +int natural_scrolling = 1; static const int repeat_rate = 25; static const int repeat_delay = 600; diff --git a/dwl.c b/dwl.c index 2907c5f..188639e 100644 --- a/dwl.c +++ b/dwl.c @@ -974,8 +974,13 @@ createpointer(struct wlr_input_device *device) { struct libinput_device *libinput_device = (struct libinput_device*) wlr_libinput_get_device_handle(device); + if (tap_to_click && libinput_device_config_tap_get_finger_count(libinput_device)) libinput_device_config_tap_set_enabled(libinput_device, LIBINPUT_CONFIG_TAP_ENABLED); + + if (libinput_device_config_scroll_has_natural_scroll(libinput_device)) + libinput_device_config_scroll_set_natural_scroll_enabled(libinput_device, natural_scrolling); + /* We don't do anything special with pointers. All of our pointer handling * is proxied through wlr_cursor. On another compositor, you might take this * opportunity to do libinput configuration on the device to set From feeacc88c41653937a2698fcb39a455cd41c44d8 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 19 Dec 2020 18:39:30 +0100 Subject: [PATCH 109/173] tweak trackpad variables Add static const and move them below in order to group the keyboard options. --- config.def.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/config.def.h b/config.def.h index 2c11fd3..fc30ba8 100644 --- a/config.def.h +++ b/config.def.h @@ -42,13 +42,13 @@ static const struct xkb_rule_names xkb_rules = { */ }; -/* Trackpad */ -int tap_to_click = 1; -int natural_scrolling = 1; - static const int repeat_rate = 25; static const int repeat_delay = 600; +/* Trackpad */ +static const int tap_to_click = 1; +static const int natural_scrolling = 1; + #define MODKEY WLR_MODIFIER_ALT #define TAGKEYS(KEY,SKEY,TAG) \ { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ From 05883b7b2f823cf292b2f27b8ee2c542e6a8eaa7 Mon Sep 17 00:00:00 2001 From: Keating950 Date: Sun, 22 Nov 2020 12:58:49 -0500 Subject: [PATCH 110/173] add install target to Makefile and corresponding prefix variable to config.mk --- Makefile | 5 +++++ config.mk | 3 +++ 2 files changed, 8 insertions(+) diff --git a/Makefile b/Makefile index bf0b957..3d3028f 100644 --- a/Makefile +++ b/Makefile @@ -53,5 +53,10 @@ dwl: xdg-shell-protocol.o wlr-layer-shell-unstable-v1-protocol.o idle-protocol.o clean: rm -f dwl *.o *-protocol.h *-protocol.c +install: dwl + mkdir -p $(PREFIX)/bin + cp -f dwl $(PREFIX)/bin + chmod 755 $(PREFIX)/bin/dwl + .DEFAULT_GOAL=dwl .PHONY: clean diff --git a/config.mk b/config.mk index a101f23..3958049 100644 --- a/config.mk +++ b/config.mk @@ -1,3 +1,6 @@ +# paths +PREFIX = /usr/local + # Default compile flags (overridable by environment) CFLAGS ?= -g -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare -Wno-error=unused-function From ee7e8688a71c8fc15c4e3f0da882bd24660efbf6 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 19 Dec 2020 18:51:22 +0100 Subject: [PATCH 111/173] s/maprequest/mapnotify This should be consistent with other function names instead of keeping the X name. --- dwl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dwl.c b/dwl.c index 188639e..d19d3ee 100644 --- a/dwl.c +++ b/dwl.c @@ -245,7 +245,7 @@ static void keypress(struct wl_listener *listener, void *data); static void keypressmod(struct wl_listener *listener, void *data); static void killclient(const Arg *arg); static void maplayersurfacenotify(struct wl_listener *listener, void *data); -static void maprequest(struct wl_listener *listener, void *data); +static void mapnotify(struct wl_listener *listener, void *data); static void monocle(Monitor *m); static void motionabsolute(struct wl_listener *listener, void *data); static void motionnotify(uint32_t time); @@ -917,7 +917,7 @@ createnotify(struct wl_listener *listener, void *data) /* Listen to the various events it can emit */ c->commit.notify = commitnotify; wl_signal_add(&xdg_surface->surface->events.commit, &c->commit); - c->map.notify = maprequest; + c->map.notify = mapnotify; wl_signal_add(&xdg_surface->events.map, &c->map); c->unmap.notify = unmapnotify; wl_signal_add(&xdg_surface->events.unmap, &c->unmap); @@ -1349,7 +1349,7 @@ maplayersurfacenotify(struct wl_listener *listener, void *data) } void -maprequest(struct wl_listener *listener, void *data) +mapnotify(struct wl_listener *listener, void *data) { /* Called when the surface is mapped, or ready to display on-screen. */ Client *c = wl_container_of(listener, c, map); @@ -2486,7 +2486,7 @@ createnotifyx11(struct wl_listener *listener, void *data) c->bw = borderpx; /* Listen to the various events it can emit */ - c->map.notify = maprequest; + c->map.notify = mapnotify; wl_signal_add(&xwayland_surface->events.map, &c->map); c->unmap.notify = unmapnotify; wl_signal_add(&xwayland_surface->events.unmap, &c->unmap); From 2eaa8c6de355aeec66d0a81ef8e4937d9aad797a Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 19 Dec 2020 18:56:42 +0100 Subject: [PATCH 112/173] remove useless assignment calloc already initializes ints to 0. --- dwl.c | 1 - 1 file changed, 1 deletion(-) diff --git a/dwl.c b/dwl.c index d19d3ee..689e56a 100644 --- a/dwl.c +++ b/dwl.c @@ -458,7 +458,6 @@ applyrules(Client *c) Monitor *mon = selmon, *m; /* rule matching */ - c->isfloating = 0; #ifdef XWAYLAND if (c->type != XDGShell) { updatewindowtype(c); From ba1540c3d006eb001e45e3b9315a00c9c599d2cc Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 19 Dec 2020 19:32:16 +0100 Subject: [PATCH 113/173] remove goto when the border is 0 Rendering 0-dimension rectangles no longer crashes wlroots. --- dwl.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/dwl.c b/dwl.c index 689e56a..ba0b32a 100644 --- a/dwl.c +++ b/dwl.c @@ -1721,8 +1721,6 @@ renderclients(Monitor *m, struct timespec *now) ox = c->geom.x, oy = c->geom.y; wlr_output_layout_output_coords(output_layout, m->wlr_output, &ox, &oy); - if (c->bw == 0) - goto render; w = surface->current.width; h = surface->current.height; @@ -1741,7 +1739,6 @@ renderclients(Monitor *m, struct timespec *now) m->wlr_output->transform_matrix); } -render: /* This calls our render function for each surface among the * xdg_surface's toplevel and popups. */ rdata.output = m->wlr_output; From 0b2f4f213dc142824aac31e78ee32fb888fbb765 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sat, 19 Dec 2020 19:50:01 +0100 Subject: [PATCH 114/173] remove -Werror=declaration-after-statement wtf is the point of this crap? It makes the code harder to follow, increases the line count and made me fail compilation a million times. We shouldn't blindy follow everything about suckless's style. --- Makefile | 2 +- dwl.c | 312 ++++++++++++++++++++++++++----------------------------- 2 files changed, 151 insertions(+), 163 deletions(-) diff --git a/Makefile b/Makefile index 3d3028f..620d287 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ include config.mk -CFLAGS += -I. -DWLR_USE_UNSTABLE -std=c99 -Werror=declaration-after-statement +CFLAGS += -I. -DWLR_USE_UNSTABLE -std=c99 WAYLAND_PROTOCOLS=$(shell pkg-config --variable=pkgdatadir wayland-protocols) WAYLAND_SCANNER=$(shell pkg-config --variable=wayland_scanner wayland-scanner) diff --git a/dwl.c b/dwl.c index ba0b32a..a4f8b6d 100644 --- a/dwl.c +++ b/dwl.c @@ -389,53 +389,61 @@ applyexclusive(struct wlr_box *usable_area, uint32_t anchor, int32_t exclusive, int32_t margin_top, int32_t margin_right, int32_t margin_bottom, int32_t margin_left) { + if (exclusive <= 0) + return; + struct { uint32_t singular_anchor; uint32_t anchor_triplet; int *positive_axis; int *negative_axis; int margin; - } edges[4]; - - if (exclusive <= 0) - return; - - // Top - edges[0].singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP; - edges[0].anchor_triplet = - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP; - edges[0].positive_axis = &usable_area->y; - edges[0].negative_axis = &usable_area->height; - edges[0].margin = margin_top; - // Bottom - edges[1].singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; - edges[1].anchor_triplet = - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; - edges[1].positive_axis = NULL; - edges[1].negative_axis = &usable_area->height; - edges[1].margin = margin_bottom; - // Left - edges[2].singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT; - edges[2].anchor_triplet = - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; - edges[2].positive_axis = &usable_area->x; - edges[2].negative_axis = &usable_area->width; - edges[2].margin = margin_left; - // Right - edges[3].singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; - edges[3].anchor_triplet = - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; - edges[3].positive_axis = NULL; - edges[3].negative_axis = &usable_area->width; - edges[3].margin = margin_right; + } edges[] = { + // Top + { + .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, + .anchor_triplet = + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, + .positive_axis = &usable_area->y, + .negative_axis = &usable_area->height, + .margin = margin_top, + }, + // Bottom + { + .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, + .anchor_triplet = + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, + .positive_axis = NULL, + .negative_axis = &usable_area->height, + .margin = margin_bottom, + }, + // Left + { + .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT, + .anchor_triplet = + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, + .positive_axis = &usable_area->x, + .negative_axis = &usable_area->width, + .margin = margin_left, + }, + // Right + { + .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT, + .anchor_triplet = + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, + .positive_axis = NULL, + .negative_axis = &usable_area->width, + .margin = margin_right, + }, + }; for (size_t i = 0; i < LENGTH(edges); ++i) { if ((anchor == edges[i].singular_anchor || anchor == edges[i].anchor_triplet) @@ -452,12 +460,8 @@ applyexclusive(struct wlr_box *usable_area, void applyrules(Client *c) { - const char *appid, *title; - unsigned int i, newtags = 0; - const Rule *r; - Monitor *mon = selmon, *m; - /* rule matching */ + const char *appid, *title; #ifdef XWAYLAND if (c->type != XDGShell) { updatewindowtype(c); @@ -474,6 +478,9 @@ applyrules(Client *c) if (!title) title = broken; + unsigned int i, newtags = 0; + const Rule *r; + Monitor *mon = selmon, *m; for (r = rules; r < END(rules); r++) { if ((!r->title || strstr(title, r->title)) && (!r->id || strstr(appid, r->id))) { @@ -505,22 +512,20 @@ arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, bool wl_list_for_each(layersurface, list, link) { struct wlr_layer_surface_v1 *wlr_layer_surface = layersurface->layer_surface; struct wlr_layer_surface_v1_state *state = &wlr_layer_surface->current; - struct wlr_box bounds; struct wlr_box box = { .width = state->desired_width, .height = state->desired_height }; - const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT - | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; - const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP - | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; if (exclusive != (state->exclusive_zone > 0)) continue; - bounds = state->exclusive_zone == -1 ? full_area : *usable_area; + struct wlr_box bounds = state->exclusive_zone == -1 ? + full_area : *usable_area; // Horizontal axis + const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT + | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; if ((state->anchor & both_horiz) && box.width == 0) { box.x = bounds.x; box.width = bounds.width; @@ -532,6 +537,8 @@ arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, bool box.x = bounds.x + ((bounds.width / 2) - (box.width / 2)); } // Vertical axis + const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP + | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; if ((state->anchor & both_vert) && box.height == 0) { box.y = bounds.y; box.height = bounds.height; @@ -576,13 +583,6 @@ void arrangelayers(Monitor *m) { struct wlr_box usable_area = m->m; - uint32_t layers_above_shell[] = { - ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, - ZWLR_LAYER_SHELL_V1_LAYER_TOP, - }; - size_t nlayers = LENGTH(layers_above_shell); - LayerSurface *layersurface; - struct wlr_keyboard *kb = wlr_seat_get_keyboard(seat); // Arrange exclusive surfaces from top->bottom arrangelayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], @@ -610,6 +610,13 @@ arrangelayers(Monitor *m) &usable_area, false); // Find topmost keyboard interactive layer, if such a layer exists + uint32_t layers_above_shell[] = { + ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, + ZWLR_LAYER_SHELL_V1_LAYER_TOP, + }; + size_t nlayers = LENGTH(layers_above_shell); + LayerSurface *layersurface; + struct wlr_keyboard *kb = wlr_seat_get_keyboard(seat); for (size_t i = 0; i < nlayers; ++i) { wl_list_for_each_reverse(layersurface, &m->layers[layers_above_shell[i]], link) { @@ -640,21 +647,19 @@ void buttonpress(struct wl_listener *listener, void *data) { struct wlr_event_pointer_button *event = data; - struct wlr_keyboard *keyboard; - uint32_t mods; - Client *c; - const Button *b; wlr_idle_notify_activity(idle, seat); switch (event->state) { case WLR_BUTTON_PRESSED:; /* Change focus if the button was _pressed_ over a client */ + Client *c; if ((c = xytoclient(cursor->x, cursor->y))) focusclient(c, 1); - keyboard = wlr_seat_get_keyboard(seat); - mods = wlr_keyboard_get_modifiers(keyboard); + struct wlr_keyboard* keyboard = wlr_seat_get_keyboard(seat); + uint32_t mods = wlr_keyboard_get_modifiers(keyboard); + const Button *b; for (b = buttons; b < END(buttons); b++) { if (CLEANMASK(mods) == CLEANMASK(b->mod) && event->button == b->button && b->func) { @@ -721,7 +726,6 @@ cleanupmon(struct wl_listener *listener, void *data) { struct wlr_output *wlr_output = data; Monitor *m = wlr_output->data; - int nmons = wl_list_length(&mons), i = 0; wl_list_remove(&m->destroy.link); wl_list_remove(&m->frame.link); @@ -729,6 +733,7 @@ cleanupmon(struct wl_listener *listener, void *data) wlr_output_layout_remove(output_layout, m->wlr_output); updatemons(); + int nmons = wl_list_length(&mons), i = 0; do // don't switch to disabled mons selmon = wl_container_of(mons.prev, selmon, link); while (!selmon->wlr_output->enabled && i++ < nmons); @@ -758,12 +763,11 @@ commitlayersurfacenotify(struct wl_listener *listener, void *data) LayerSurface *layersurface = wl_container_of(listener, layersurface, surface_commit); struct wlr_layer_surface_v1 *wlr_layer_surface = layersurface->layer_surface; struct wlr_output *wlr_output = wlr_layer_surface->output; - Monitor *m; if (!wlr_output) return; - m = wlr_output->data; + Monitor *m = wlr_output->data; arrangelayers(m); if (layersurface->layer != wlr_layer_surface->current.layer) { @@ -787,16 +791,12 @@ commitnotify(struct wl_listener *listener, void *data) void createkeyboard(struct wlr_input_device *device) { - struct xkb_context *context; - struct xkb_keymap *keymap; - Keyboard *kb; - - kb = device->data = calloc(1, sizeof(*kb)); + Keyboard *kb = device->data = calloc(1, sizeof(*kb)); kb->device = device; /* Prepare an XKB keymap and assign it to the keyboard. */ - context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - keymap = xkb_map_new_from_names(context, &xkb_rules, + struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + struct xkb_keymap *keymap = xkb_map_new_from_names(context, &xkb_rules, XKB_KEYMAP_COMPILE_NO_FLAGS); wlr_keyboard_set_keymap(device->keyboard, keymap); @@ -824,9 +824,6 @@ createmon(struct wl_listener *listener, void *data) /* This event is raised by the backend when a new output (aka a display or * monitor) becomes available. */ struct wlr_output *wlr_output = data; - Monitor *m; - const MonitorRule *r; - size_t nlayers = LENGTH(m->layers); /* The mode is a tuple of (width, height, refresh rate), and each * monitor supports only a specific set of modes. We just pick the @@ -835,9 +832,10 @@ createmon(struct wl_listener *listener, void *data) wlr_output_set_mode(wlr_output, wlr_output_preferred_mode(wlr_output)); /* Allocates and configures monitor state using configured rules */ - m = wlr_output->data = calloc(1, sizeof(*m)); + Monitor *m = wlr_output->data = calloc(1, sizeof(*m)); m->wlr_output = wlr_output; m->tagset[0] = m->tagset[1] = 1; + const MonitorRule *r; for (r = monrules; r < END(monrules); r++) { if (!r->name || strstr(wlr_output->name, r->name)) { m->mfact = r->mfact; @@ -873,6 +871,7 @@ createmon(struct wl_listener *listener, void *data) */ wlr_output_layout_add_auto(output_layout, wlr_output); + size_t nlayers = LENGTH(m->layers); for (size_t i = 0; i < nlayers; ++i) wl_list_init(&m->layers[i]); @@ -932,15 +931,12 @@ void createlayersurface(struct wl_listener *listener, void *data) { struct wlr_layer_surface_v1 *wlr_layer_surface = data; - LayerSurface *layersurface; - Monitor *m; - struct wlr_layer_surface_v1_state old_state; if (!wlr_layer_surface->output) { wlr_layer_surface->output = selmon->wlr_output; } - layersurface = calloc(1, sizeof(LayerSurface)); + LayerSurface *layersurface = calloc(1, sizeof(LayerSurface)); layersurface->surface_commit.notify = commitlayersurfacenotify; wl_signal_add(&wlr_layer_surface->surface->events.commit, @@ -955,14 +951,14 @@ createlayersurface(struct wl_listener *listener, void *data) layersurface->layer_surface = wlr_layer_surface; wlr_layer_surface->data = layersurface; - m = wlr_layer_surface->output->data; + Monitor *m = wlr_layer_surface->output->data; wl_list_insert(&m->layers[wlr_layer_surface->client_pending.layer], &layersurface->link); // Temporarily set the layer's current state to client_pending // so that we can easily arrange it - old_state = wlr_layer_surface->current; + struct wlr_layer_surface_v1_state old_state = wlr_layer_surface->current; wlr_layer_surface->current = wlr_layer_surface->client_pending; arrangelayers(m); wlr_layer_surface->current = old_state; @@ -1016,7 +1012,6 @@ void destroylayersurfacenotify(struct wl_listener *listener, void *data) { LayerSurface *layersurface = wl_container_of(listener, layersurface, destroy); - Monitor *m; if (layersurface->layer_surface->mapped) unmaplayersurface(layersurface); @@ -1026,7 +1021,7 @@ destroylayersurfacenotify(struct wl_listener *listener, void *data) wl_list_remove(&layersurface->unmap.link); wl_list_remove(&layersurface->surface_commit.link); if (layersurface->layer_surface->output) { - m = layersurface->layer_surface->output->data; + Monitor *m = layersurface->layer_surface->output->data; if (m) arrangelayers(m); layersurface->layer_surface->output = NULL; @@ -1114,15 +1109,14 @@ dirtomon(int dir) void focusclient(Client *c, int lift) { - struct wlr_keyboard *kb = wlr_seat_get_keyboard(seat); - struct wlr_surface *old = seat->keyboard_state.focused_surface; - /* Raise client in stacking order if requested */ if (c && lift) { wl_list_remove(&c->slink); wl_list_insert(&stack, &c->slink); } + struct wlr_surface *old = seat->keyboard_state.focused_surface; + /* Nothing else to do? */ if (c && WLR_SURFACE(c) == old) return; @@ -1147,6 +1141,7 @@ focusclient(Client *c, int lift) } /* Have a client, so focus its top-level wlr_surface */ + struct wlr_keyboard *kb = wlr_seat_get_keyboard(seat); if (shouldfocusclients(c->mon)) wlr_seat_keyboard_notify_enter(seat, WLR_SURFACE(c), kb->keycodes, kb->num_keycodes, &kb->modifiers); @@ -1231,7 +1226,6 @@ inputdevice(struct wl_listener *listener, void *data) /* This event is raised by the backend when a new input device becomes * available. */ struct wlr_input_device *device = data; - uint32_t caps; switch (device->type) { case WLR_INPUT_DEVICE_KEYBOARD: createkeyboard(device); @@ -1247,7 +1241,7 @@ inputdevice(struct wl_listener *listener, void *data) * communiciated to the client. In dwl we always have a cursor, even if * there are no pointer devices, so we always include that capability. */ /* XXX do we actually require a cursor? */ - caps = WL_SEAT_CAPABILITY_POINTER; + uint32_t caps = WL_SEAT_CAPABILITY_POINTER; if (!wl_list_empty(&keyboards)) caps |= WL_SEAT_CAPABILITY_KEYBOARD; wlr_seat_set_capabilities(seat, caps); @@ -1279,7 +1273,6 @@ keypress(struct wl_listener *listener, void *data) /* This event is raised when a key is pressed or released. */ Keyboard *kb = wl_container_of(listener, kb, key); struct wlr_event_keyboard_key *event = data; - int i; /* Translate libinput keycode -> xkbcommon */ uint32_t keycode = event->keycode + 8; @@ -1295,7 +1288,7 @@ keypress(struct wl_listener *listener, void *data) /* On _press_, attempt to process a compositor keybinding. */ if (event->state == WLR_KEY_PRESSED) - for (i = 0; i < nsyms; i++) + for (int i = 0; i < nsyms; i++) handled = keybinding(mods, syms[i]) || handled; if (!handled) { @@ -1417,9 +1410,6 @@ motionabsolute(struct wl_listener *listener, void *data) void motionnotify(uint32_t time) { - double sx = 0, sy = 0; - struct wlr_surface *surface = NULL; - Client *c = NULL; struct timespec now; if (!time) { clock_gettime(CLOCK_MONOTONIC, &now); @@ -1445,6 +1435,9 @@ motionnotify(uint32_t time) return; } + struct wlr_surface *surface = NULL; + double sx = 0, sy = 0; + Client *c = NULL; if ((surface = xytolayersurface(&selmon->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], cursor->x, cursor->y, &sx, &sy))) ; @@ -1545,7 +1538,6 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test) { struct wlr_output_configuration_head_v1 *config_head; bool ok = true; - Arg ar = {.i = -1}; wl_list_for_each(config_head, &config->heads, link) { struct wlr_output *wlr_output = config_head->state.output; @@ -1570,6 +1562,7 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, bool test) if (m->wlr_output->name == wlr_output->name) { // focus the left monitor (relative to the current focus) m->wlr_output->enabled = !m->wlr_output->enabled; + Arg ar = {.i = -1}; focusmon(&ar); closemon(m); m->wlr_output->enabled = !m->wlr_output->enabled; @@ -1647,10 +1640,6 @@ render(struct wlr_surface *surface, int sx, int sy, void *data) /* This function is called for every surface that needs to be rendered. */ struct render_data *rdata = data; struct wlr_output *output = rdata->output; - double ox = 0, oy = 0; - struct wlr_box obox; - float matrix[9]; - enum wl_output_transform transform; /* We first obtain a wlr_texture, which is a GPU resource. wlroots * automatically handles negotiating these with the client. The underlying @@ -1665,14 +1654,17 @@ render(struct wlr_surface *surface, int sx, int sy, void *data) * one next to the other, both 1080p, a client on the rightmost display might * have layout coordinates of 2000,100. We need to translate that to * output-local coordinates, or (2000 - 1920). */ + double ox = 0, oy = 0; wlr_output_layout_output_coords(output_layout, output, &ox, &oy); /* We also have to apply the scale factor for HiDPI outputs. This is only * part of the puzzle, dwl does not fully support HiDPI. */ - obox.x = ox + rdata->x + sx; - obox.y = oy + rdata->y + sy; - obox.width = surface->current.width; - obox.height = surface->current.height; + struct wlr_box obox = { + .x = ox + rdata->x + sx, + .y = oy + rdata->y + sy, + .width = surface->current.width, + .height = surface->current.height, + }; scalebox(&obox, output->scale); /* @@ -1686,7 +1678,9 @@ render(struct wlr_surface *surface, int sx, int sy, void *data) * Naturally you can do this any way you like, for example to make a 3D * compositor. */ - transform = wlr_output_transform_invert(surface->current.transform); + float matrix[9]; + enum wl_output_transform transform = wlr_output_transform_invert( + surface->current.transform); wlr_matrix_project_box(matrix, &obox, transform, 0, output->transform_matrix); @@ -1703,12 +1697,6 @@ void renderclients(Monitor *m, struct timespec *now) { Client *c, *sel = selclient(); - const float *color; - double ox, oy; - int i, w, h; - struct render_data rdata; - struct wlr_box *borders; - struct wlr_surface *surface; /* Each subsequent window we render is rendered on top of the last. Because * our stacking list is ordered front-to-back, we iterate over it backwards. */ wl_list_for_each_reverse(c, &stack, slink) { @@ -1717,14 +1705,14 @@ renderclients(Monitor *m, struct timespec *now) output_layout, m->wlr_output, &c->geom)) continue; - surface = WLR_SURFACE(c); - ox = c->geom.x, oy = c->geom.y; + struct wlr_surface *surface = WLR_SURFACE(c); + double ox = c->geom.x, oy = c->geom.y; wlr_output_layout_output_coords(output_layout, m->wlr_output, &ox, &oy); - w = surface->current.width; - h = surface->current.height; - borders = (struct wlr_box[4]) { + int w = surface->current.width; + int h = surface->current.height; + struct wlr_box *borders = (struct wlr_box[4]) { {ox, oy, w + 2 * c->bw, c->bw}, /* top */ {ox, oy + c->bw, c->bw, h}, /* left */ {ox + c->bw + w, oy + c->bw, c->bw, h}, /* right */ @@ -1732,8 +1720,8 @@ renderclients(Monitor *m, struct timespec *now) }; /* Draw window borders */ - color = (c == sel) ? focuscolor : bordercolor; - for (i = 0; i < 4; i++) { + const float *color = (c == sel) ? focuscolor : bordercolor; + for (int i = 0; i < 4; i++) { scalebox(&borders[i], m->wlr_output->scale); wlr_render_rect(drw, &borders[i], color, m->wlr_output->transform_matrix); @@ -1741,10 +1729,12 @@ renderclients(Monitor *m, struct timespec *now) /* This calls our render function for each surface among the * xdg_surface's toplevel and popups. */ - rdata.output = m->wlr_output; - rdata.when = now; - rdata.x = c->geom.x + c->bw; - rdata.y = c->geom.y + c->bw; + struct render_data rdata = { + .output = m->wlr_output, + .when = now, + .x = c->geom.x + c->bw, + .y = c->geom.y + c->bw, + }; #ifdef XWAYLAND if (c->type != XDGShell) wlr_surface_for_each_surface(c->surface.xwayland->surface, render, &rdata); @@ -1757,13 +1747,14 @@ renderclients(Monitor *m, struct timespec *now) void renderlayer(struct wl_list *layer_surfaces, struct timespec *now) { - struct render_data rdata; LayerSurface *layersurface; wl_list_for_each(layersurface, layer_surfaces, link) { - rdata.output = layersurface->layer_surface->output; - rdata.when = now; - rdata.x = layersurface->geo.x; - rdata.y = layersurface->geo.y; + struct render_data rdata = { + .output = layersurface->layer_surface->output, + .when = now, + .x = layersurface->geo.x, + .y = layersurface->geo.y, + }; wlr_surface_for_each_surface(layersurface->layer_surface->surface, render, &rdata); @@ -1773,9 +1764,6 @@ renderlayer(struct wl_list *layer_surfaces, struct timespec *now) void rendermon(struct wl_listener *listener, void *data) { - Client *c; - int render = 1; - /* This function is called every time an output is ready to display a frame, * generally at the output's refresh rate (e.g. 60Hz). */ Monitor *m = wl_container_of(listener, m, frame); @@ -1784,6 +1772,8 @@ rendermon(struct wl_listener *listener, void *data) clock_gettime(CLOCK_MONOTONIC, &now); /* Do not render if any XDG clients have an outstanding resize. */ + Client *c; + int render = 1; wl_list_for_each(c, &stack, slink) { if (c->resize) { wlr_surface_send_frame_done(WLR_SURFACE(c), &now); @@ -1833,11 +1823,11 @@ resize(Client *c, int x, int y, int w, int h, int interact) * compositor, you'd wait for the client to prepare a buffer at * the new size, then commit any movement that was prepared. */ - struct wlr_box *bbox = interact ? &sgeom : &c->mon->w; c->geom.x = x; c->geom.y = y; c->geom.width = w; c->geom.height = h; + struct wlr_box *bbox = interact ? &sgeom : &c->mon->w; applybounds(c, bbox); /* wlroots makes this a no-op if size hasn't changed */ #ifdef XWAYLAND @@ -1854,8 +1844,6 @@ resize(Client *c, int x, int y, int w, int h, int interact) void run(char *startup_cmd) { - pid_t startup_pid = -1; - /* Add a Unix socket to the Wayland display. */ const char *socket = wl_display_add_socket_auto(dpy); if (!socket) @@ -1880,8 +1868,10 @@ run(char *startup_cmd) /* Set the WAYLAND_DISPLAY environment variable to our socket and run the * startup command if requested. */ setenv("WAYLAND_DISPLAY", socket, 1); + + pid_t startup_pid = -1; if (startup_cmd) { - startup_pid = fork(); + pid_t startup_pid = fork(); if (startup_pid < 0) EBARF("startup: fork"); if (startup_pid == 0) { @@ -1889,6 +1879,7 @@ run(char *startup_cmd) EBARF("startup: execl"); } } + /* Run the Wayland event loop. This does not return until you exit the * compositor. Starting the backend rigged up all of the necessary event * loop configuration to listen to libinput events, DRM events, generate @@ -1962,11 +1953,9 @@ setlayout(const Arg *arg) void setmfact(const Arg *arg) { - float f; - if (!arg || !selmon->lt[selmon->sellt]->arrange) return; - f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; + float f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; if (f < 0.1 || f > 0.9) return; selmon->mfact = f; @@ -2271,11 +2260,10 @@ togglefloating(const Arg *arg) void toggletag(const Arg *arg) { - unsigned int newtags; Client *sel = selclient(); if (!sel) return; - newtags = sel->tags ^ (arg->ui & TAGMASK); + unsigned int newtags = sel->tags ^ (arg->ui & TAGMASK); if (newtags) { sel->tags = newtags; focusclient(focustop(selmon), 1); @@ -2469,13 +2457,13 @@ configurex11(struct wl_listener *listener, void *data) void createnotifyx11(struct wl_listener *listener, void *data) { - struct wlr_xwayland_surface *xwayland_surface = data; Client *c; wl_list_for_each(c, &clients, link) if (c->isfullscreen && VISIBLEON(c, c->mon)) setfullscreen(c, 0); /* Allocate a Client for this surface */ + struct wlr_xwayland_surface *xwayland_surface = data; c = xwayland_surface->data = calloc(1, sizeof(*c)); c->surface.xwayland = xwayland_surface; c->type = xwayland_surface->override_redirect ? X11Unmanaged : X11Managed; @@ -2502,10 +2490,8 @@ Atom getatom(xcb_connection_t *xc, const char *name) { Atom atom = 0; - xcb_intern_atom_cookie_t cookie; xcb_intern_atom_reply_t *reply; - - cookie = xcb_intern_atom(xc, 0, strlen(name), name); + xcb_intern_atom_cookie_t cookie = xcb_intern_atom(xc, 0, strlen(name), name); if ((reply = xcb_intern_atom_reply(xc, cookie, NULL))) atom = reply->atom; free(reply); @@ -2517,23 +2503,25 @@ void renderindependents(struct wlr_output *output, struct timespec *now) { Client *c; - struct render_data rdata; - struct wlr_box geom; wl_list_for_each_reverse(c, &independents, link) { - geom.x = c->surface.xwayland->x; - geom.y = c->surface.xwayland->y; - geom.width = c->surface.xwayland->width; - geom.height = c->surface.xwayland->height; + struct wlr_box geom = { + .x = c->surface.xwayland->x, + .y = c->surface.xwayland->y, + .width = c->surface.xwayland->width, + .height = c->surface.xwayland->height, + }; /* Only render visible clients which show on this output */ if (!wlr_output_layout_intersects(output_layout, output, &geom)) continue; - rdata.output = output; - rdata.when = now; - rdata.x = c->surface.xwayland->x; - rdata.y = c->surface.xwayland->y; + struct render_data rdata = { + .output = output, + .when = now, + .x = c->surface.xwayland->x, + .y = c->surface.xwayland->y, + }; wlr_surface_for_each_surface(c->surface.xwayland->surface, render, &rdata); } @@ -2542,8 +2530,7 @@ renderindependents(struct wlr_output *output, struct timespec *now) void updatewindowtype(Client *c) { - size_t i; - for (i = 0; i < c->surface.xwayland->window_type_len; i++) + for (size_t i = 0; i < c->surface.xwayland->window_type_len; i++) if (c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeDialog] || c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeSplash] || c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeToolbar] || @@ -2583,12 +2570,13 @@ xytoindependent(double x, double y) * client loses focus, which ensures that unmanaged are only visible on * the current tag. */ Client *c; - struct wlr_box geom; wl_list_for_each_reverse(c, &independents, link) { - geom.x = c->surface.xwayland->x; - geom.y = c->surface.xwayland->y; - geom.width = c->surface.xwayland->width; - geom.height = c->surface.xwayland->height; + struct wlr_box geom = { + .x = c->surface.xwayland->x, + .y = c->surface.xwayland->y, + .width = c->surface.xwayland->width, + .height = c->surface.xwayland->height, + }; if (wlr_box_contains_point(&geom, x, y)) return c; } From 6b47e2bb621dfba333fb9bd9839b8128c88e58c2 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sun, 20 Dec 2020 13:51:40 +0100 Subject: [PATCH 115/173] use bool Because it's 2020. Passing integers to wlroots variables and functions with bool in their signature is silly. --- config.def.h | 6 +-- dwl.c | 104 ++++++++++++++++++++++++++------------------------- 2 files changed, 56 insertions(+), 54 deletions(-) diff --git a/config.def.h b/config.def.h index fc30ba8..5e33204 100644 --- a/config.def.h +++ b/config.def.h @@ -1,5 +1,5 @@ /* appearance */ -static const int sloppyfocus = 1; /* focus follows mouse */ +static const bool sloppyfocus = true; /* focus follows mouse */ static const unsigned int borderpx = 1; /* border pixel of windows */ static const float rootcolor[] = {0.3, 0.3, 0.3, 1.0}; static const float bordercolor[] = {0.5, 0.5, 0.5, 1.0}; @@ -46,8 +46,8 @@ static const int repeat_rate = 25; static const int repeat_delay = 600; /* Trackpad */ -static const int tap_to_click = 1; -static const int natural_scrolling = 1; +static const bool tap_to_click = true; +static const bool natural_scrolling = true; #define MODKEY WLR_MODIFIER_ALT #define TAGKEYS(KEY,SKEY,TAG) \ diff --git a/dwl.c b/dwl.c index a4f8b6d..39c2bf1 100644 --- a/dwl.c +++ b/dwl.c @@ -113,13 +113,13 @@ typedef struct { #endif int bw; unsigned int tags; - int isfloating; + bool isfloating; uint32_t resize; /* configure serial of a pending resize */ int oldx; int oldy; int oldwidth; int oldheight; - int isfullscreen; + bool isfullscreen; } Client; typedef struct { @@ -190,7 +190,7 @@ typedef struct { const char *id; const char *title; unsigned int tags; - int isfloating; + bool isfloating; int monitor; } Rule; @@ -232,7 +232,7 @@ static void destroylayersurfacenotify(struct wl_listener *listener, void *data); static void destroynotify(struct wl_listener *listener, void *data); static void destroyxdeco(struct wl_listener *listener, void *data); static Monitor *dirtomon(int dir); -static void focusclient(Client *c, int lift); +static void focusclient(Client *c, bool lift); static void focusmon(const Arg *arg); static void focusstack(const Arg *arg); static void fullscreennotify(struct wl_listener *listener, void *data); @@ -240,7 +240,7 @@ static Client *focustop(Monitor *m); static void getxdecomode(struct wl_listener *listener, void *data); static void incnmaster(const Arg *arg); static void inputdevice(struct wl_listener *listener, void *data); -static int keybinding(uint32_t mods, xkb_keysym_t sym); +static bool keybinding(uint32_t mods, xkb_keysym_t sym); static void keypress(struct wl_listener *listener, void *data); static void keypressmod(struct wl_listener *listener, void *data); static void killclient(const Arg *arg); @@ -261,15 +261,15 @@ static void render(struct wlr_surface *surface, int sx, int sy, void *data); static void renderclients(Monitor *m, struct timespec *now); static void renderlayer(struct wl_list *layer_surfaces, struct timespec *now); static void rendermon(struct wl_listener *listener, void *data); -static void resize(Client *c, int x, int y, int w, int h, int interact); +static void resize(Client *c, int x, int y, int w, int h, bool interact); static void run(char *startup_cmd); static void scalebox(struct wlr_box *box, float scale); static Client *selclient(void); static void setcursor(struct wl_listener *listener, void *data); static void setpsel(struct wl_listener *listener, void *data); static void setsel(struct wl_listener *listener, void *data); -static void setfloating(Client *c, int floating); -static void setfullscreen(Client *c, int fullscreen); +static void setfloating(Client *c, bool floating); +static void setfullscreen(Client *c, bool fullscreen); static void setlayout(const Arg *arg); static void setmfact(const Arg *arg); static void setmon(Client *c, Monitor *m, unsigned int newtags); @@ -655,7 +655,7 @@ buttonpress(struct wl_listener *listener, void *data) /* Change focus if the button was _pressed_ over a client */ Client *c; if ((c = xytoclient(cursor->x, cursor->y))) - focusclient(c, 1); + focusclient(c, true); struct wlr_keyboard* keyboard = wlr_seat_get_keyboard(seat); uint32_t mods = wlr_keyboard_get_modifiers(keyboard); @@ -737,7 +737,7 @@ cleanupmon(struct wl_listener *listener, void *data) do // don't switch to disabled mons selmon = wl_container_of(mons.prev, selmon, link); while (!selmon->wlr_output->enabled && i++ < nmons); - focusclient(focustop(selmon), 1); + focusclient(focustop(selmon), true); closemon(m); free(m); } @@ -751,7 +751,7 @@ closemon(Monitor *m) wl_list_for_each(c, &clients, link) { if (c->isfloating && c->geom.x > m->m.width) resize(c, c->geom.x - m->w.width, c->geom.y, - c->geom.width, c->geom.height, 0); + c->geom.width, c->geom.height, false); if (c->mon == m) setmon(c, selmon, c->tags); } @@ -856,7 +856,7 @@ createmon(struct wl_listener *listener, void *data) wl_list_insert(&mons, &m->link); - wlr_output_enable(wlr_output, 1); + wlr_output_enable(wlr_output, true); if (!wlr_output_commit(wlr_output)) return; @@ -883,7 +883,7 @@ createmon(struct wl_listener *listener, void *data) wl_list_for_each(c, &clients, link) { if (c->isfloating) resize(c, c->geom.x + m->w.width, c->geom.y, - c->geom.width, c->geom.height, 0); + c->geom.width, c->geom.height, false); } return; } @@ -901,7 +901,7 @@ createnotify(struct wl_listener *listener, void *data) return; wl_list_for_each(c, &clients, link) if (c->isfullscreen && VISIBLEON(c, c->mon)) - setfullscreen(c, 0); + setfullscreen(c, false); /* Allocate a Client for this surface */ c = xdg_surface->data = calloc(1, sizeof(*c)); @@ -924,7 +924,7 @@ createnotify(struct wl_listener *listener, void *data) c->fullscreen.notify = fullscreennotify; wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, &c->fullscreen); - c->isfullscreen = 0; + c->isfullscreen = false; } void @@ -1059,7 +1059,7 @@ destroyxdeco(struct wl_listener *listener, void *data) } void -setfullscreen(Client *c, int fullscreen) +setfullscreen(Client *c, bool fullscreen) { c->isfullscreen = fullscreen; c->bw = (1 - fullscreen) * borderpx; @@ -1077,9 +1077,10 @@ setfullscreen(Client *c, int fullscreen) c->oldy = c->geom.y; c->oldheight = c->geom.height; c->oldwidth = c->geom.width; - resize(c, c->mon->m.x, c->mon->m.y, c->mon->m.width, c->mon->m.height, 0); + resize(c, c->mon->m.x, c->mon->m.y, + c->mon->m.width, c->mon->m.height, false); } else { - resize(c, c->oldx, c->oldy, c->oldwidth, c->oldheight, 0); + resize(c, c->oldx, c->oldy, c->oldwidth, c->oldheight, false); } } @@ -1107,7 +1108,7 @@ dirtomon(int dir) } void -focusclient(Client *c, int lift) +focusclient(Client *c, bool lift) { /* Raise client in stacking order if requested */ if (c && lift) { @@ -1154,10 +1155,10 @@ focusclient(Client *c, int lift) /* Activate the new client */ #ifdef XWAYLAND if (c->type != XDGShell) - wlr_xwayland_surface_activate(c->surface.xwayland, 1); + wlr_xwayland_surface_activate(c->surface.xwayland, true); else #endif - wlr_xdg_toplevel_set_activated(c->surface.xdg, 1); + wlr_xdg_toplevel_set_activated(c->surface.xdg, true); } void @@ -1166,7 +1167,7 @@ focusmon(const Arg *arg) do selmon = dirtomon(arg->i); while (!selmon->wlr_output->enabled); - focusclient(focustop(selmon), 1); + focusclient(focustop(selmon), true); } void @@ -1192,7 +1193,7 @@ focusstack(const Arg *arg) } } /* If only one client is visible on selmon, then c == sel */ - focusclient(c, 1); + focusclient(c, true); } Client * @@ -1247,7 +1248,7 @@ inputdevice(struct wl_listener *listener, void *data) wlr_seat_set_capabilities(seat, caps); } -int +bool keybinding(uint32_t mods, xkb_keysym_t sym) { /* @@ -1255,13 +1256,13 @@ keybinding(uint32_t mods, xkb_keysym_t sym) * processing keys, rather than passing them on to the client for its own * processing. */ - int handled = 0; + bool handled = false; const Key *k; for (k = keys; k < END(keys); k++) { if (CLEANMASK(mods) == CLEANMASK(k->mod) && sym == k->keysym && k->func) { k->func(&k->arg); - handled = 1; + handled = true; } } return handled; @@ -1281,7 +1282,7 @@ keypress(struct wl_listener *listener, void *data) int nsyms = xkb_state_key_get_syms( kb->device->keyboard->xkb_state, keycode, &syms); - int handled = 0; + bool handled = false; uint32_t mods = wlr_keyboard_get_modifiers(kb->device->keyboard); wlr_idle_notify_activity(idle, seat); @@ -1386,10 +1387,10 @@ monocle(Monitor *m) if (!VISIBLEON(c, m) || c->isfloating) continue; if (c->isfullscreen) { - resize(c, c->mon->m.x, c->mon->m.y, c->mon->m.width, c->mon->m.height, 0); + resize(c, c->mon->m.x, c->mon->m.y, c->mon->m.width, c->mon->m.height, false); return; } - resize(c, m->w.x, m->w.y, m->w.width, m->w.height, 0); + resize(c, m->w.x, m->w.y, m->w.width, m->w.height, false); } } @@ -1426,12 +1427,12 @@ motionnotify(uint32_t time) if (cursor_mode == CurMove) { /* Move the grabbed client to the new position. */ resize(grabc, cursor->x - grabcx, cursor->y - grabcy, - grabc->geom.width, grabc->geom.height, 1); + grabc->geom.width, grabc->geom.height, true); return; } else if (cursor_mode == CurResize) { resize(grabc, grabc->geom.x, grabc->geom.y, cursor->x - grabc->geom.x, - cursor->y - grabc->geom.y, 1); + cursor->y - grabc->geom.y, true); return; } @@ -1507,7 +1508,7 @@ moveresize(const Arg *arg) return; /* Float the window and tell motionnotify to grab it */ - setfloating(grabc, 1); + setfloating(grabc, true); switch (cursor_mode = arg->ui) { case CurMove: grabcx = cursor->x - grabc->geom.x; @@ -1625,7 +1626,7 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, #endif if (sloppyfocus) - focusclient(c, 0); + focusclient(c, false); } void @@ -1773,11 +1774,11 @@ rendermon(struct wl_listener *listener, void *data) /* Do not render if any XDG clients have an outstanding resize. */ Client *c; - int render = 1; + bool render = true; wl_list_for_each(c, &stack, slink) { if (c->resize) { wlr_surface_send_frame_done(WLR_SURFACE(c), &now); - render = 0; + render = false; } } @@ -1816,7 +1817,7 @@ rendermon(struct wl_listener *listener, void *data) } void -resize(Client *c, int x, int y, int w, int h, int interact) +resize(Client *c, int x, int y, int w, int h, bool interact) { /* * Note that I took some shortcuts here. In a more fleshed-out @@ -1930,7 +1931,7 @@ setcursor(struct wl_listener *listener, void *data) } void -setfloating(Client *c, int floating) +setfloating(Client *c, bool floating) { if (c->isfloating == floating) return; @@ -1983,7 +1984,7 @@ setmon(Client *c, Monitor *m, unsigned int newtags) c->tags = newtags ? newtags : m->tagset[m->seltags]; /* assign tags of target monitor */ arrange(m); } - focusclient(focustop(selmon), 1); + focusclient(focustop(selmon), true); } void @@ -2196,7 +2197,7 @@ tag(const Arg *arg) Client *sel = selclient(); if (sel && arg->ui & TAGMASK) { sel->tags = arg->ui & TAGMASK; - focusclient(focustop(selmon), 1); + focusclient(focustop(selmon), true); arrange(selmon); } } @@ -2231,16 +2232,17 @@ tile(Monitor *m) if (!VISIBLEON(c, m) || c->isfloating) continue; if (c->isfullscreen) { - resize(c, c->mon->m.x, c->mon->m.y, c->mon->m.width, c->mon->m.height, 0); + resize(c, c->mon->m.x, c->mon->m.y, + c->mon->m.width, c->mon->m.height, false); return; } if (i < m->nmaster) { h = (m->w.height - my) / (MIN(n, m->nmaster) - i); - resize(c, m->w.x, m->w.y + my, mw, h, 0); + resize(c, m->w.x, m->w.y + my, mw, h, false); my += c->geom.height; } else { h = (m->w.height - ty) / (n - i); - resize(c, m->w.x + mw, m->w.y + ty, m->w.width - mw, h, 0); + resize(c, m->w.x + mw, m->w.y + ty, m->w.width - mw, h, false); ty += c->geom.height; } i++; @@ -2266,7 +2268,7 @@ toggletag(const Arg *arg) unsigned int newtags = sel->tags ^ (arg->ui & TAGMASK); if (newtags) { sel->tags = newtags; - focusclient(focustop(selmon), 1); + focusclient(focustop(selmon), true); arrange(selmon); } } @@ -2278,7 +2280,7 @@ toggleview(const Arg *arg) if (newtagset) { selmon->tagset[selmon->seltags] = newtagset; - focusclient(focustop(selmon), 1); + focusclient(focustop(selmon), true); arrange(selmon); } } @@ -2289,7 +2291,7 @@ unmaplayersurface(LayerSurface *layersurface) layersurface->layer_surface->mapped = false; if (layersurface->layer_surface->surface == seat->keyboard_state.focused_surface) - focusclient(selclient(), 1); + focusclient(selclient(), true); motionnotify(0); } @@ -2350,7 +2352,7 @@ view(const Arg *arg) selmon->seltags ^= 1; /* toggle sel tagset */ if (arg->ui & TAGMASK) selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; - focusclient(focustop(selmon), 1); + focusclient(focustop(selmon), true); arrange(selmon); } @@ -2430,7 +2432,7 @@ zoom(const Arg *arg) wl_list_remove(&sel->link); wl_list_insert(&clients, &sel->link); - focusclient(sel, 1); + focusclient(sel, true); arrange(selmon); } @@ -2442,7 +2444,7 @@ activatex11(struct wl_listener *listener, void *data) /* Only "managed" windows can be activated */ if (c->type == X11Managed) - wlr_xwayland_surface_activate(c->surface.xwayland, 1); + wlr_xwayland_surface_activate(c->surface.xwayland, true); } void @@ -2460,7 +2462,7 @@ createnotifyx11(struct wl_listener *listener, void *data) Client *c; wl_list_for_each(c, &clients, link) if (c->isfullscreen && VISIBLEON(c, c->mon)) - setfullscreen(c, 0); + setfullscreen(c, false); /* Allocate a Client for this surface */ struct wlr_xwayland_surface *xwayland_surface = data; @@ -2483,7 +2485,7 @@ createnotifyx11(struct wl_listener *listener, void *data) c->fullscreen.notify = fullscreennotify; wl_signal_add(&xwayland_surface->events.request_fullscreen, &c->fullscreen); - c->isfullscreen = 0; + c->isfullscreen = false; } Atom @@ -2535,7 +2537,7 @@ updatewindowtype(Client *c) c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeSplash] || c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeToolbar] || c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeUtility]) - c->isfloating = 1; + c->isfloating = true; } void From 444a5f9dec52161f61d8a42644f7b6625d9aabcd Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Fri, 11 Dec 2020 10:55:33 +0100 Subject: [PATCH 116/173] enable adaptive sync The comment in this function's declaration says the backend is free to ignore this setting, so maybe there's no need to make it configurable? --- dwl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dwl.c b/dwl.c index 39c2bf1..773e04a 100644 --- a/dwl.c +++ b/dwl.c @@ -848,6 +848,7 @@ createmon(struct wl_listener *listener, void *data) break; } } + wlr_output_enable_adaptive_sync(wlr_output, true); /* Set up event listeners */ m->frame.notify = rendermon; wl_signal_add(&wlr_output->events.frame, &m->frame); From f80f08848b8b7e08ed2f20c7f0a8675a3b4df394 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sun, 20 Dec 2020 14:29:45 +0100 Subject: [PATCH 117/173] ensure that xwayland cursor defaults to left_ptr Don't show an X cursor when closing an Xwayland window or with certain dropdowns. Based on https://github.com/djpohly/dwl/pull/32 --- dwl.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dwl.c b/dwl.c index 773e04a..7a12817 100644 --- a/dwl.c +++ b/dwl.c @@ -2561,6 +2561,13 @@ xwaylandready(struct wl_listener *listener, void *data) /* assign the one and only seat */ wlr_xwayland_set_seat(xwayland, seat); + /* Set the default XWayland cursor to match the rest of dwl. */ + struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor(cursor_mgr, "left_ptr", 1); + wlr_xwayland_set_cursor(xwayland, + xcursor->images[0]->buffer, xcursor->images[0]->width * 4, + xcursor->images[0]->width, xcursor->images[0]->height, + xcursor->images[0]->hotspot_x, xcursor->images[0]->hotspot_y); + xcb_disconnect(xc); } From 39946e07f2b2ac09cfa622668585f2223fc277d4 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sun, 20 Dec 2020 15:34:36 +0100 Subject: [PATCH 118/173] fix keyboard focus with overlays Don't let internal calls to motionnotify(0) meant to update the pointer focus from maplayersurfacenotify and destroylayersurfacenotify also shift the keyboard focus to the surface under the cursor with sloppyfocus. --- dwl.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/dwl.c b/dwl.c index 7a12817..46ba04a 100644 --- a/dwl.c +++ b/dwl.c @@ -1412,12 +1412,6 @@ motionabsolute(struct wl_listener *listener, void *data) void motionnotify(uint32_t time) { - struct timespec now; - if (!time) { - clock_gettime(CLOCK_MONOTONIC, &now); - time = now.tv_sec * 1000 + now.tv_nsec / 1000000; - } - wlr_idle_notify_activity(idle, seat); /* Update selmon (even while dragging a window) */ @@ -1608,6 +1602,13 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, return; } + bool internal_call = !time; + if (!time) { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + time = now.tv_sec * 1000 + now.tv_nsec / 1000000; + } + /* If surface is already focused, only notify of motion */ if (surface == seat->pointer_state.focused_surface) { wlr_seat_pointer_notify_motion(seat, time, sx, sy); @@ -1626,7 +1627,7 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, return; #endif - if (sloppyfocus) + if (sloppyfocus && !internal_call) focusclient(c, false); } From a571ea465c545f662c4bc9899a31150e045074d0 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sun, 20 Dec 2020 15:19:34 +0100 Subject: [PATCH 119/173] replace shouldfocusclients with checking old And don't activate clients while an overlay is focused. --- dwl.c | 51 ++++++++++++++++++++++++--------------------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/dwl.c b/dwl.c index 46ba04a..888001c 100644 --- a/dwl.c +++ b/dwl.c @@ -275,7 +275,6 @@ static void setmfact(const Arg *arg); static void setmon(Client *c, Monitor *m, unsigned int newtags); static void setup(void); static void sigchld(int unused); -static bool shouldfocusclients(); static void spawn(const Arg *arg); static void tag(const Arg *arg); static void tagmon(const Arg *arg); @@ -1119,10 +1118,16 @@ focusclient(Client *c, bool lift) struct wlr_surface *old = seat->keyboard_state.focused_surface; - /* Nothing else to do? */ if (c && WLR_SURFACE(c) == old) return; + /* Put the new client atop the focus stack and select its monitor */ + if (c) { + wl_list_remove(&c->flink); + wl_list_insert(&fstack, &c->flink); + selmon = c->mon; + } + /* Deactivate old client if focus is changing */ if (old && (!c || WLR_SURFACE(c) != old)) { if (wlr_surface_is_xdg_surface(old)) @@ -1133,9 +1138,23 @@ focusclient(Client *c, bool lift) wlr_xwayland_surface_activate( wlr_xwayland_surface_from_wlr_surface(old), false); #endif + /* If an overlay is focused, don't focus or activate the client, + * but only update its position in fstack to render its border with focuscolor + * and focus it after the overlay is closed. + * It's probably pointless to check if old is a layer surface + * since it can't be anything else at this point. */ + else if (wlr_surface_is_layer_surface(old)) { + struct wlr_layer_surface_v1 *wlr_layer_surface = + wlr_layer_surface_v1_from_wlr_surface(old); + + if (wlr_layer_surface->mapped && ( + wlr_layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP || + wlr_layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY + )) + return; + } } - /* Update wlroots' keyboard focus */ if (!c) { /* With no client, all we have left is to clear focus */ wlr_seat_keyboard_notify_clear_focus(seat); @@ -1144,14 +1163,8 @@ focusclient(Client *c, bool lift) /* Have a client, so focus its top-level wlr_surface */ struct wlr_keyboard *kb = wlr_seat_get_keyboard(seat); - if (shouldfocusclients(c->mon)) - wlr_seat_keyboard_notify_enter(seat, WLR_SURFACE(c), - kb->keycodes, kb->num_keycodes, &kb->modifiers); - - /* Put the new client atop the focus stack and select its monitor */ - wl_list_remove(&c->flink); - wl_list_insert(&fstack, &c->flink); - selmon = c->mon; + wlr_seat_keyboard_notify_enter(seat, WLR_SURFACE(c), + kb->keycodes, kb->num_keycodes, &kb->modifiers); /* Activate the new client */ #ifdef XWAYLAND @@ -2167,22 +2180,6 @@ sigchld(int unused) ; } -bool -shouldfocusclients(Monitor *m) -{ - LayerSurface *layersurface; - uint32_t layers_above_shell[] = { - ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, - ZWLR_LAYER_SHELL_V1_LAYER_TOP, - }; - for (size_t i = 0; i < LENGTH(layers_above_shell); ++i) - wl_list_for_each(layersurface, &m->layers[layers_above_shell[i]], link) - if (layersurface->layer_surface->current.keyboard_interactive && - layersurface->layer_surface->mapped) - return false; - return true; -} - void spawn(const Arg *arg) { From 13c7e039bbd823df6ed8475c1ee1cc90a43d729a Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sun, 20 Dec 2020 15:55:52 +0100 Subject: [PATCH 120/173] deactivate the focused client on overlay focus --- dwl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dwl.c b/dwl.c index 888001c..4fdcc3a 100644 --- a/dwl.c +++ b/dwl.c @@ -621,6 +621,8 @@ arrangelayers(Monitor *m) &m->layers[layers_above_shell[i]], link) { if (layersurface->layer_surface->current.keyboard_interactive && layersurface->layer_surface->mapped) { + // Deactivate the focused client. + focusclient(NULL, false); wlr_seat_keyboard_notify_enter(seat, layersurface->layer_surface->surface, kb->keycodes, kb->num_keycodes, &kb->modifiers); return; From b161b5d8f4f635ad960d629fa2355c71c231e38a Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sun, 20 Dec 2020 16:04:13 +0100 Subject: [PATCH 121/173] don't notify of activity ...or update selmon when we just want to restore pointer focus. --- dwl.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/dwl.c b/dwl.c index 4fdcc3a..cbf0c11 100644 --- a/dwl.c +++ b/dwl.c @@ -1427,11 +1427,14 @@ motionabsolute(struct wl_listener *listener, void *data) void motionnotify(uint32_t time) { - wlr_idle_notify_activity(idle, seat); + // time is 0 in internal calls meant to restore pointer focus. + if (time) { + wlr_idle_notify_activity(idle, seat); - /* Update selmon (even while dragging a window) */ - if (sloppyfocus) - selmon = xytomon(cursor->x, cursor->y); + /* Update selmon (even while dragging a window) */ + if (sloppyfocus) + selmon = xytomon(cursor->x, cursor->y); + } /* If we are currently grabbing the mouse, handle and return */ if (cursor_mode == CurMove) { From 5ed227384b7f5069b2a9d6cb653f08799488c6d8 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sun, 20 Dec 2020 16:06:17 +0100 Subject: [PATCH 122/173] rename drw It's impossible to understand that this stands from drawable if you're not familiar with dwm's code. --- dwl.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/dwl.c b/dwl.c index cbf0c11..a2f3823 100644 --- a/dwl.c +++ b/dwl.c @@ -298,7 +298,7 @@ static void zoom(const Arg *arg); static const char broken[] = "broken"; static struct wl_display *dpy; static struct wlr_backend *backend; -static struct wlr_renderer *drw; +static struct wlr_renderer *renderer; static struct wlr_compositor *compositor; static struct wlr_xdg_shell *xdg_shell; @@ -1707,7 +1707,7 @@ render(struct wlr_surface *surface, int sx, int sy, void *data) /* This takes our matrix, the texture, and an alpha, and performs the actual * rendering on the GPU. */ - wlr_render_texture_with_matrix(drw, texture, matrix, 1); + wlr_render_texture_with_matrix(renderer, texture, matrix, 1); /* This lets the client know that we've displayed that frame and it can * prepare another one now if it likes. */ @@ -1744,7 +1744,7 @@ renderclients(Monitor *m, struct timespec *now) const float *color = (c == sel) ? focuscolor : bordercolor; for (int i = 0; i < 4; i++) { scalebox(&borders[i], m->wlr_output->scale); - wlr_render_rect(drw, &borders[i], color, + wlr_render_rect(renderer, &borders[i], color, m->wlr_output->transform_matrix); } @@ -1808,8 +1808,8 @@ rendermon(struct wl_listener *listener, void *data) if (render) { /* Begin the renderer (calls glViewport and some other GL sanity checks) */ - wlr_renderer_begin(drw, m->wlr_output->width, m->wlr_output->height); - wlr_renderer_clear(drw, rootcolor); + wlr_renderer_begin(renderer, m->wlr_output->width, m->wlr_output->height); + wlr_renderer_clear(renderer, rootcolor); renderlayer(&m->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &now); renderlayer(&m->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], &now); @@ -1830,7 +1830,7 @@ rendermon(struct wl_listener *listener, void *data) /* Conclude rendering and swap the buffers, showing the final frame * on-screen. */ - wlr_renderer_end(drw); + wlr_renderer_end(renderer); } wlr_output_commit(m->wlr_output); @@ -2053,8 +2053,8 @@ setup(void) /* If we don't provide a renderer, autocreate makes a GLES2 renderer for us. * The renderer is responsible for defining the various pixel formats it * supports for shared memory, this configures that for clients. */ - drw = wlr_backend_get_renderer(backend); - wlr_renderer_init_wl_display(drw, dpy); + renderer = wlr_backend_get_renderer(backend); + wlr_renderer_init_wl_display(renderer, dpy); /* This creates some hands-off wlroots interfaces. The compositor is * necessary for clients to allocate surfaces and the data device manager @@ -2062,7 +2062,7 @@ setup(void) * to dig your fingers in and play with their behavior if you want. Note that * the clients cannot set the selection directly without compositor approval, * see the setsel() function. */ - compositor = wlr_compositor_create(dpy, drw); + compositor = wlr_compositor_create(dpy, renderer); wlr_export_dmabuf_manager_v1_create(dpy); wlr_screencopy_manager_v1_create(dpy); wlr_data_control_manager_v1_create(dpy); From 499cb2c2b64fcc118f162d20910ae3a24281e0f0 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Sun, 20 Dec 2020 16:07:12 +0100 Subject: [PATCH 123/173] say TODO just wtf is XXX supposed to be? It sounds like a pornographic thing. --- dwl.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dwl.c b/dwl.c index a2f3823..2511dd4 100644 --- a/dwl.c +++ b/dwl.c @@ -499,7 +499,7 @@ arrange(Monitor *m) { if (m->lt[m->sellt]->arrange) m->lt[m->sellt]->arrange(m); - /* XXX recheck pointer focus here... or in resize()? */ + /* TODO recheck pointer focus here... or in resize()? */ } void @@ -671,7 +671,7 @@ buttonpress(struct wl_listener *listener, void *data) break; case WLR_BUTTON_RELEASED: /* If you released any buttons, we exit interactive move/resize mode. */ - /* XXX should reset to the pointer focus's current setcursor */ + /* TODO should reset to the pointer focus's current setcursor */ if (cursor_mode != CurNormal) { wlr_xcursor_manager_set_cursor_image(cursor_mgr, "left_ptr", cursor); @@ -1251,13 +1251,13 @@ inputdevice(struct wl_listener *listener, void *data) createpointer(device); break; default: - /* XXX handle other input device types */ + /* TODO handle other input device types */ break; } /* We need to let the wlr_seat know what our capabilities are, which is * communiciated to the client. In dwl we always have a cursor, even if * there are no pointer devices, so we always include that capability. */ - /* XXX do we actually require a cursor? */ + /* TODO do we actually require a cursor? */ uint32_t caps = WL_SEAT_CAPABILITY_POINTER; if (!wl_list_empty(&keyboards)) caps |= WL_SEAT_CAPABILITY_KEYBOARD; @@ -1879,7 +1879,7 @@ run(char *startup_cmd) * cursor position, and set default cursor image */ selmon = xytomon(cursor->x, cursor->y); - /* XXX hack to get cursor to display in its initial location (100, 100) + /* TODO hack to get cursor to display in its initial location (100, 100) * instead of (0, 0) and then jumping. still may not be fully * initialized, as the image/coordinates are not transformed for the * monitor when displayed here */ @@ -1937,7 +1937,7 @@ setcursor(struct wl_listener *listener, void *data) /* This event is raised by the seat when a client provides a cursor image */ struct wlr_seat_pointer_request_set_cursor_event *event = data; /* If we're "grabbing" the cursor, don't use the client's image */ - /* XXX still need to save the provided surface to restore later */ + /* TODO still need to save the provided surface to restore later */ if (cursor_mode != CurNormal) return; /* This can be sent by any client, so we check to make sure this one is @@ -1966,7 +1966,7 @@ setlayout(const Arg *arg) selmon->sellt ^= 1; if (arg && arg->v) selmon->lt[selmon->sellt] = (Layout *)arg->v; - /* XXX change layout symbol? */ + /* TODO change layout symbol? */ arrange(selmon); } @@ -1992,7 +1992,7 @@ setmon(Client *c, Monitor *m, unsigned int newtags) return; c->mon = m; - /* XXX leave/enter is not optimal but works */ + /* TODO leave/enter is not optimal but works */ if (oldmon) { wlr_surface_send_leave(WLR_SURFACE(c), oldmon->wlr_output); arrange(oldmon); From c9964016b8d4904be483f7c1a2fdd82ea089ef54 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Mon, 21 Dec 2020 03:41:30 +0100 Subject: [PATCH 124/173] update README.md - A maximum SLOC can't be reasonably determined before implementing the missing protocols, so not any time soon - dwl definitely isn't a simple as dwm since it must implement lots of Wayland protocols and not just manage windows. The status bar integration, layer shell popups, damage tracking and IME are gonna require hundreds more lines each. - "Buffering of input when spawning a client so you don't have to wait for the window (use `wl_client_get_credentials` to get the PID) - would this require passing through something like dmenu? Extension protocol?" This sounds exoteric, if anything this should be patch. - Can dwl really be started from within an X session? When I do it from dwm it crashes. - A window's texture is scaled for its "home" monitor only (noticeable when window sits across a monitor boundary) Gonna open a ticket for this rather than keep it in the README. --- README.md | 55 +++++++++++++++++++++++++------------------------------ 1 file changed, 25 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 1a8d8ec..1f757d5 100644 --- a/README.md +++ b/README.md @@ -1,71 +1,66 @@ # dwl - dwm for Wayland -dwl is a compact, hackable compositor for Wayland based on [wlroots](https://github.com/swaywm/wlroots). It is intended to fill the same space in the Wayland world that dwm does in X11, primarily in terms of philosophy, and secondarily in terms of functionality. Like dwm, dwl is: +dwl is a compact, hackable compositor for Wayland based on [wlroots](https://github.com/swaywm/wlroots). It is intended to fill the same space in the Wayland world that dwm does in X11, primarily in terms of philosophy, and secondarily in terms of functionality. Like dwm, dwl is: - Easy to understand, hack on, and extend with patches -- One C source file (or a very small number) configurable via `config.h` -- Limited to a maximum number of SLOC (to be determined) +- One C source file configurable via `config.h` - Tied to as few external dependencies as possible +dwl is not meant to provide every feature under the sun. Instead, like dwm, it sticks to features which are necessary, simple, and straightforward to implement given the base on which it is built. Implemented default features are: -dwl is not meant to provide every feature under the sun. Instead, like dwm, it sticks to features which are necessary, simple, and straightforward to implement given the base on which it is built. Since wlroots provides a number of features that are more complicated to accomplish with Xlib and select extensions, dwl can be in some ways more featureful than dwm *while remaining just as simple.* Intended default features are: - -- Any features provided by dwm/Xlib: simple window borders, tags, keybindings, client rules, mouse move/resize (see below for why the built-in status bar is a possible exception) +- Any features provided by dwm/Xlib: simple window borders, tags, keybindings, client rules, mouse move/resize. The built-in status bar is an exception to avoid taking a dependency on FreeType or Pango and increasing the SLOC - Configurable multi-monitor layout support, including position and rotation - Configurable HiDPI/multi-DPI support -- Wayland protocols needed for daily life in the tiling world: at a minimum, xdg-shell and layer-shell (for bars/menus). Protocols trivially provided by wlroots may also be added. +- Various Wayland protocols - XWayland support as provided by wlroots - Zero flickering - Wayland users naturally expect that "every frame is perfect" -- Basic yes/no damage tracking to avoid needless redraws (if it can be done simply and has an impact on power consumption) +Features yet to be implemented are: -Other features under consideration are: - -- Additional Wayland compositor protocols which are trivially provided by wlroots or can be conditionally included via `config.h` settings (e.g. screen capture) -- External bar support instead of a built-in status bar, to avoid taking a dependency on FreeType or Pango -- Buffering of input when spawning a client so you don't have to wait for the window (use `wl_client_get_credentials` to get the PID) - would this require passing through something like dmenu? Extension protocol? -- More in-depth damage region tracking - +- Write a dwl-status protocol that bars can implement to show tags. You can already use Waybar or yambar, but without tag information +- Implement the input-inhibitor protocol to support screen lockers +- Implement the idle-inhibit protocol which the lets application such as mpv disable idle monitoring, and distribute it as a patch +- Layer shell popups (used by Waybar) +- Basic yes/no damage tracking to avoid needless redraws +- More in-depth damage region tracking (this should be worth it according to https://mozillagfx.wordpress.com/2019/10/22/dramatically-reduced-power-usage-in-firefox-70-on-macos-with-core-animation/) +- Implement the text-input and input-method protocols to support IME once ibus implements input-method v2 (see https://github.com/ibus/ibus/pull/2256 and https://github.com/djpohly/dwl/pull/12) +- Implement urgent/attention/focus-request once it's part of the xdg-shell protocol (https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/9) Feature *non-goals* include: - Client-side decoration (any more than is necessary to tell the clients not to) - Client-initiated window management, such as move, resize, and close, which can be done through the compositor - ## Building dwl -dwl has only two dependencies: wlroots (git version currently required) and wayland-protocols. Simply install these and run `make`. +dwl has only two dependencies: wlroots 0.12 and wayland-protocols. Simply install these and run `sudo make install`. +To enable XWayland, you should also install xorg-xwayland and uncomment its flag in `config.mk`. ## Configuration -All configuration is done by editing `config.h` and recompiling, in the same manner as dwm. There is no way to separately restart the window manager in Wayland without restarting the entire display server, so any changes will take effect the next time dwl is executed. - +All configuration is done by editing `config.h` and recompiling, in the same manner as dwm. There is no way to separately restart the window manager in Wayland without restarting the entire display server, so any changes will take effect the next time dwl is executed. ## Running dwl -dwl can be run as-is, with no arguments. In an existing Wayland or X11 session, this will open a window to act as a virtual display. When run from a TTY, the Wayland server will take over the entire virtual terminal. Clients started by dwl will have `WAYLAND_DISPLAY` set in their environment, and other clients can be started from outside the session by setting this variable accordingly. +dwl can be run as-is, with no arguments. In an existing Wayland, this will open a window to act as a virtual display. When run from a TTY, the Wayland server will take over the entire virtual terminal. Clients started by dwl will have `WAYLAND_DISPLAY` set in their environment, and other clients can be started from outside the session by setting this variable accordingly. -You can also specify a startup program using the `-s` option. The argument to this option will be run at startup as a shell command (using `sh -c`) and can serve a similar function to `.xinitrc`: starting a service manager or other startup applications. Unlike `.xinitrc`, the display server will not shut down when this process terminates. Instead, as dwl is shutting down, it will send this process a SIGTERM and wait for it to terminate (if it hasn't already). This makes it ideal not only for initialization but also for execing into a user-level service manager like s6 or `systemd --user`. +You can also specify a startup program using the `-s` option. The argument to this option will be run at startup as a shell command (using `sh -c`) and can serve a similar function to `.xinitrc`: starting a service manager or other startup applications. Unlike `.xinitrc`, the display server will not shut down when this process terminates. Instead, as dwl is shutting down, it will send this process a SIGTERM and wait for it to terminate (if it hasn't already). This makes it ideal not only for initialization but also for execing into a user-level service manager like s6 or `systemd --user`. Note: Wayland requires a valid `XDG_RUNTIME_DIR`, which is usually set up by a session manager such as `elogind` or `systemd-logind`. If your system doesn't do this automatically, you will need to configure it prior to launching `dwl`, e.g.: export XDG_RUNTIME_DIR=/tmp/xdg-runtime-$(id -u) mkdir -p $XDG_RUNTIME_DIR +## Replacements for X applications -## Known limitations and issues +You can find a [list of Wayland applications on the sway wiki](https://github.com/swaywm/sway/wiki/i3-Migration-Guide). -dwl is a work in progress, and it has not yet reached its feature goals in a number of ways: - -- A window's texture is scaled for its "home" monitor only (noticeable when window sits across a monitor boundary) -- XWayland support is new and could use testing -- Urgent/attention/focus-request ([not yet supported](https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/9) by xdg-shell protocol) -- Statusbar support (built-in or external) -- Damage tracking +## IRC channel +dwl's IRC channel is #dwl on irc.freenode.net. ## Acknowledgements -dwl began by extending the TinyWL example provided (CC0) by the sway/wlroots developers. This was made possible in many cases by looking at how sway accomplished something, then trying to do the same in as suckless a way as possible. Speaking of which, many thanks to suckless.org and the dwm developers and community for the inspiration. +dwl began by extending the TinyWL example provided (CC0) by the sway/wlroots developers. This was made possible in many cases by looking at how sway accomplished something, then trying to do the same in as suckless a way as possible. +Many thanks to suckless.org and the dwm developers and community for the inspiration, and to Devin J. Pohly for creating dwl. From cf7c5eae214609eddbfb98de856d33c26f8964b9 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Mon, 21 Dec 2020 08:05:40 +0100 Subject: [PATCH 125/173] don't reset the cursor image ...in internal calls to restore pointer focus. Necessary for the unclutter patch, and there's no harm in avoiding this call even in mainline; might prevents issues in same edge cases. --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 2511dd4..16f0a88 100644 --- a/dwl.c +++ b/dwl.c @@ -1490,7 +1490,7 @@ motionnotify(uint32_t time) /* If there's no client surface under the cursor, set the cursor image to a * default. This is what makes the cursor image appear when you move it * off of a client or over its border. */ - if (!surface) + if (!surface && time) wlr_xcursor_manager_set_cursor_image(cursor_mgr, "left_ptr", cursor); From 5668c616161d451e6f20be29b31bbf03f0f398a5 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Fri, 18 Sep 2020 21:45:35 +0200 Subject: [PATCH 126/173] Define monitor order with monrules[] The order in which monitors are defined in monrules[] actually matters. Monotors that aren't configured in monrules[], it will always be the leftmost. --- config.def.h | 3 +++ dwl.c | 30 ++++++++++++++++++++++++++---- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/config.def.h b/config.def.h index 5e33204..8d76be4 100644 --- a/config.def.h +++ b/config.def.h @@ -32,6 +32,9 @@ static const MonitorRule monrules[] = { */ /* defaults */ { NULL, 0.55, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL }, + /* with the outputOder patch, the order in which every monitor is defined + * defines its actual position. Non configured monitor, are always added to + * the left */ }; /* keyboard */ diff --git a/dwl.c b/dwl.c index 16f0a88..d54311d 100644 --- a/dwl.c +++ b/dwl.c @@ -175,6 +175,7 @@ struct Monitor { unsigned int tagset[2]; double mfact; int nmaster; + int position; }; typedef struct { @@ -836,8 +837,8 @@ createmon(struct wl_listener *listener, void *data) Monitor *m = wlr_output->data = calloc(1, sizeof(*m)); m->wlr_output = wlr_output; m->tagset[0] = m->tagset[1] = 1; - const MonitorRule *r; - for (r = monrules; r < END(monrules); r++) { + m->position = -1; + for (const MonitorRule *r = monrules; r < END(monrules); r++) { if (!r->name || strstr(wlr_output->name, r->name)) { m->mfact = r->mfact; m->nmaster = r->nmaster; @@ -846,6 +847,7 @@ createmon(struct wl_listener *listener, void *data) m->lt[0] = r->lt; m->lt[1] = &layouts[LENGTH(layouts) > 1 && r->lt != &layouts[1]]; wlr_output_set_transform(wlr_output, r->rr); + m->position = r - monrules; break; } } @@ -856,7 +858,19 @@ createmon(struct wl_listener *listener, void *data) m->destroy.notify = cleanupmon; wl_signal_add(&wlr_output->events.destroy, &m->destroy); - wl_list_insert(&mons, &m->link); + Monitor *moni, *insertmon = NULL; + int x = 0; + wl_list_for_each(moni, &mons, link) + if (m->position > moni->position) + insertmon = moni; + if (insertmon) { + x = insertmon->w.x + insertmon->w.width; + wl_list_insert(&insertmon->link, &m->link); + fprintf(stderr, "%s inserted in pos %d\n", m->wlr_output->name, m->position); + } else { + wl_list_insert(&mons, &m->link); + fprintf(stderr, "%s defaulting\n", m->wlr_output->name); + } wlr_output_enable(wlr_output, true); if (!wlr_output_commit(wlr_output)) @@ -871,7 +885,15 @@ createmon(struct wl_listener *listener, void *data) * display, which Wayland clients can see to find out information about the * output (such as DPI, scale factor, manufacturer, etc). */ - wlr_output_layout_add_auto(output_layout, wlr_output); + wlr_output_layout_add(output_layout, wlr_output, x, 0); + wl_list_for_each_reverse(moni, &mons, link) { + /* all monitors that on the right of the new one must be moved */ + if (moni == m) + break; + wlr_output_layout_move(output_layout, moni->wlr_output, moni->w.x + m->wlr_output->width, 0); + fprintf(stderr, "moved %s to %d", moni->wlr_output->name, moni->w.x + m->wlr_output->width); + } + sgeom = *wlr_output_layout_get_box(output_layout, NULL); size_t nlayers = LENGTH(m->layers); for (size_t i = 0; i < nlayers; ++i) From 33e8a3f1f3382322180c6b80bc48cb2ab4965bcf Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Mon, 21 Dec 2020 11:21:59 +0100 Subject: [PATCH 127/173] update comments and remove debugging printf --- config.def.h | 7 +++---- dwl.c | 12 +++--------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/config.def.h b/config.def.h index 8d76be4..4ab746b 100644 --- a/config.def.h +++ b/config.def.h @@ -24,7 +24,9 @@ static const Layout layouts[] = { { "[M]", monocle }, }; -/* monitors */ +/* monitors + * The order in which monitors are defined determines their position. + * Non-configured monitors are always added to the left. */ static const MonitorRule monrules[] = { /* name mfact nmaster scale layout rotate/reflect */ /* example of a HiDPI laptop monitor: @@ -32,9 +34,6 @@ static const MonitorRule monrules[] = { */ /* defaults */ { NULL, 0.55, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL }, - /* with the outputOder patch, the order in which every monitor is defined - * defines its actual position. Non configured monitor, are always added to - * the left */ }; /* keyboard */ diff --git a/dwl.c b/dwl.c index d54311d..2128751 100644 --- a/dwl.c +++ b/dwl.c @@ -866,20 +866,15 @@ createmon(struct wl_listener *listener, void *data) if (insertmon) { x = insertmon->w.x + insertmon->w.width; wl_list_insert(&insertmon->link, &m->link); - fprintf(stderr, "%s inserted in pos %d\n", m->wlr_output->name, m->position); } else { wl_list_insert(&mons, &m->link); - fprintf(stderr, "%s defaulting\n", m->wlr_output->name); } wlr_output_enable(wlr_output, true); if (!wlr_output_commit(wlr_output)) return; - /* Adds this to the output layout. The add_auto function arranges outputs - * from left-to-right in the order they appear. A more sophisticated - * compositor would let the user configure the arrangement of outputs in the - * layout. + /* Adds this to the output layout in the order it was configured in. * * The output layout utility automatically adds a wl_output global to the * display, which Wayland clients can see to find out information about the @@ -887,11 +882,10 @@ createmon(struct wl_listener *listener, void *data) */ wlr_output_layout_add(output_layout, wlr_output, x, 0); wl_list_for_each_reverse(moni, &mons, link) { - /* all monitors that on the right of the new one must be moved */ + /* All monitors to the right of the new one must be moved */ if (moni == m) break; wlr_output_layout_move(output_layout, moni->wlr_output, moni->w.x + m->wlr_output->width, 0); - fprintf(stderr, "moved %s to %d", moni->wlr_output->name, moni->w.x + m->wlr_output->width); } sgeom = *wlr_output_layout_get_box(output_layout, NULL); @@ -902,7 +896,7 @@ createmon(struct wl_listener *listener, void *data) /* When adding monitors, the geometries of all monitors must be updated */ updatemons(); wl_list_for_each(m, &mons, link) { - /* the first monitor in the list is the most recently added */ + /* The first monitor in the list is the most recently added */ Client *c; wl_list_for_each(c, &clients, link) { if (c->isfloating) From bcf9d8fb9a2f30e5fc0283637ee63350bf78e696 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Mon, 21 Dec 2020 13:06:06 +0100 Subject: [PATCH 128/173] disable natural scrolling by default This inverts the scroll even on regular mice. --- config.def.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.def.h b/config.def.h index 4ab746b..b7e067d 100644 --- a/config.def.h +++ b/config.def.h @@ -49,7 +49,7 @@ static const int repeat_delay = 600; /* Trackpad */ static const bool tap_to_click = true; -static const bool natural_scrolling = true; +static const bool natural_scrolling = false; #define MODKEY WLR_MODIFIER_ALT #define TAGKEYS(KEY,SKEY,TAG) \ From 572ccd92c43b83b677e87c3926d0f5703224c2d8 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Mon, 21 Dec 2020 21:39:26 +0100 Subject: [PATCH 129/173] remove github directory --- .github/ISSUE_TEMPLATE/bug_report.md | 10 ---------- .github/ISSUE_TEMPLATE/enhancement-idea.md | 10 ---------- 2 files changed, 20 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md delete mode 100644 .github/ISSUE_TEMPLATE/enhancement-idea.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 2a18c75..0000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Bug report -about: Something in dwl isn't working correctly -title: '' -labels: 'Type: bug' -assignees: '' - ---- - - diff --git a/.github/ISSUE_TEMPLATE/enhancement-idea.md b/.github/ISSUE_TEMPLATE/enhancement-idea.md deleted file mode 100644 index 92c6c8c..0000000 --- a/.github/ISSUE_TEMPLATE/enhancement-idea.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Enhancement idea -about: Suggest a feature or improvement -title: '' -labels: 'Type: enhancement' -assignees: '' - ---- - - From 3695ac643fb621f9339137afbc274c82ab46d088 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Tue, 22 Dec 2020 09:13:18 +0100 Subject: [PATCH 130/173] spacing --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1f757d5..55eb370 100644 --- a/README.md +++ b/README.md @@ -63,4 +63,5 @@ dwl's IRC channel is #dwl on irc.freenode.net. ## Acknowledgements dwl began by extending the TinyWL example provided (CC0) by the sway/wlroots developers. This was made possible in many cases by looking at how sway accomplished something, then trying to do the same in as suckless a way as possible. + Many thanks to suckless.org and the dwm developers and community for the inspiration, and to Devin J. Pohly for creating dwl. From 41d7fdbd604ebb28a9ba01ba1662c70e9d72135c Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Thu, 24 Dec 2020 19:07:38 -0500 Subject: [PATCH 131/173] use the install command to install --- Makefile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 16428ff..b34b909 100644 --- a/Makefile +++ b/Makefile @@ -34,9 +34,7 @@ clean: rm -f dwl *.o xdg-shell-protocol.h xdg-shell-protocol.c install: dwl - mkdir -p $(PREFIX)/bin - cp -f dwl $(PREFIX)/bin - chmod 755 $(PREFIX)/bin/dwl + install -D dwl $(PREFIX)/bin/dwl .DEFAULT_GOAL=dwl .PHONY: clean From 5f3a7887f398385d93f61294c6fe876e6abb7c5d Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Thu, 24 Dec 2020 20:28:08 -0500 Subject: [PATCH 132/173] Same target as dwm sounds like a good goal --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c60b4b3..2a1742f 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ dwl is a compact, hackable compositor for Wayland based on [wlroots](https://git - Easy to understand, hack on, and extend with patches - One C source file (or a very small number) configurable via `config.h` -- Limited to a maximum number of SLOC (to be determined) +- Limited to 2000 SLOC to promote hackability - Tied to as few external dependencies as possible From d94266df915bf6b0747858aa9f51cb02a481cfaa Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Thu, 24 Dec 2020 21:15:40 -0500 Subject: [PATCH 133/173] fix crash on non-libinput pointers (e.g. X11 backend) --- dwl.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/dwl.c b/dwl.c index d25b5df..0c28cc0 100644 --- a/dwl.c +++ b/dwl.c @@ -899,14 +899,16 @@ createlayersurface(struct wl_listener *listener, void *data) void createpointer(struct wlr_input_device *device) { - struct libinput_device *libinput_device = (struct libinput_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 From 7803022d331c87a4d0ec827af3735c0f8b206676 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Thu, 24 Dec 2020 21:37:21 -0500 Subject: [PATCH 134/173] simplify ROUND macro --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index ed4325d..8a03247 100644 --- a/dwl.c +++ b/dwl.c @@ -53,7 +53,7 @@ #define LENGTH(X) (sizeof X / sizeof X[0]) #define END(A) ((A) + LENGTH(A)) #define TAGMASK ((1 << LENGTH(tags)) - 1) -#define ROUND(X) ((X)>=0?(long)((X)+0.5):(long)((X)-0.5)) +#define ROUND(X) ((int)((X)+0.5)) #ifdef XWAYLAND #define WLR_SURFACE(C) ((C)->type != XDGShell ? (C)->surface.xwayland->surface : (C)->surface.xdg->surface) #else From 57d07606357d4ce3b1c1a6461a5b7c6c90c1df9e Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Thu, 24 Dec 2020 22:21:00 -0500 Subject: [PATCH 135/173] let's not alienate the dwm folks Fix style. --- config.mk | 2 +- dwl.c | 242 ++++++++++++++++++++++++++++-------------------------- 2 files changed, 128 insertions(+), 116 deletions(-) diff --git a/config.mk b/config.mk index 3958049..d84f14d 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-error=unused-function +CFLAGS ?= -g -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare -Wno-error=unused-function -Wdeclaration-after-statement # Uncomment to build XWayland support #CFLAGS += -DXWAYLAND diff --git a/dwl.c b/dwl.c index 336c939..e57928f 100644 --- a/dwl.c +++ b/dwl.c @@ -156,6 +156,14 @@ typedef struct { enum zwlr_layer_shell_v1_layer layer; } 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 *); @@ -396,61 +404,46 @@ 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[4]; + if (exclusive <= 0) return; - struct { - uint32_t singular_anchor; - uint32_t anchor_triplet; - int *positive_axis; - int *negative_axis; - int margin; - } 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, - }, - }; + // Top + edges[0].singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP; + edges[0].anchor_triplet = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP; + edges[0].positive_axis = &usable_area->y; + edges[0].negative_axis = &usable_area->height; + edges[0].margin = margin_top; + + // Bottom + edges[1].singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; + edges[1].anchor_triplet = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; + edges[1].positive_axis = NULL; + edges[1].negative_axis = &usable_area->height; + edges[1].margin = margin_bottom; + + // Left + edges[2].singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT; + edges[2].anchor_triplet = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; + edges[2].positive_axis = &usable_area->x; + edges[2].negative_axis = &usable_area->width; + edges[2].margin = margin_left; + + // Right + edges[3].singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + edges[3].anchor_triplet = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; + edges[3].positive_axis = NULL; + edges[3].negative_axis = &usable_area->width; + edges[3].margin = margin_right; for (size_t i = 0; i < LENGTH(edges); ++i) { if ((anchor == edges[i].singular_anchor || anchor == edges[i].anchor_triplet) @@ -469,6 +462,9 @@ applyrules(Client *c) { /* rule matching */ const char *appid, *title; + unsigned int i, newtags = 0; + const Rule *r; + Monitor *mon = selmon, *m; #ifdef XWAYLAND if (c->type != XDGShell) { updatewindowtype(c); @@ -485,9 +481,6 @@ applyrules(Client *c) if (!title) title = broken; - unsigned int i, newtags = 0; - const Rule *r; - Monitor *mon = selmon, *m; for (r = rules; r < END(rules); r++) { if ((!r->title || strstr(title, r->title)) && (!r->id || strstr(appid, r->id))) { @@ -521,20 +514,22 @@ arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, int 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; - struct wlr_box bounds = state->exclusive_zone == -1 ? - full_area : *usable_area; + bounds = state->exclusive_zone == -1 ? full_area : *usable_area; // Horizontal axis - const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT - | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; if ((state->anchor & both_horiz) && box.width == 0) { box.x = bounds.x; box.width = bounds.width; @@ -546,8 +541,6 @@ arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, int box.x = bounds.x + ((bounds.width / 2) - (box.width / 2)); } // Vertical axis - const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP - | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; if ((state->anchor & both_vert) && box.height == 0) { box.y = bounds.y; box.height = bounds.height; @@ -592,6 +585,13 @@ void arrangelayers(Monitor *m) { struct wlr_box usable_area = m->m; + uint32_t layers_above_shell[] = { + ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, + ZWLR_LAYER_SHELL_V1_LAYER_TOP, + }; + size_t nlayers = LENGTH(layers_above_shell); + LayerSurface *layersurface; + struct wlr_keyboard *kb = wlr_seat_get_keyboard(seat); // Arrange exclusive surfaces from top->bottom arrangelayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], @@ -619,13 +619,6 @@ arrangelayers(Monitor *m) &usable_area, 0); // Find topmost keyboard interactive layer, if such a layer exists - uint32_t layers_above_shell[] = { - ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, - ZWLR_LAYER_SHELL_V1_LAYER_TOP, - }; - size_t nlayers = LENGTH(layers_above_shell); - LayerSurface *layersurface; - struct wlr_keyboard *kb = wlr_seat_get_keyboard(seat); for (size_t i = 0; i < nlayers; ++i) { wl_list_for_each_reverse(layersurface, &m->layers[layers_above_shell[i]], link) { @@ -658,19 +651,21 @@ void buttonpress(struct wl_listener *listener, void *data) { struct wlr_event_pointer_button *event = data; + struct wlr_keyboard *keyboard; + uint32_t mods; + Client *c; + const Button *b; wlr_idle_notify_activity(idle, seat); switch (event->state) { case WLR_BUTTON_PRESSED:; /* Change focus if the button was _pressed_ over a client */ - Client *c; if ((c = xytoclient(cursor->x, cursor->y))) focusclient(c, 1); - struct wlr_keyboard* keyboard = wlr_seat_get_keyboard(seat); - uint32_t mods = wlr_keyboard_get_modifiers(keyboard); - const Button *b; + keyboard = wlr_seat_get_keyboard(seat); + mods = wlr_keyboard_get_modifiers(keyboard); for (b = buttons; b < END(buttons); b++) { if (CLEANMASK(mods) == CLEANMASK(b->mod) && event->button == b->button && b->func) { @@ -737,6 +732,7 @@ cleanupmon(struct wl_listener *listener, void *data) { struct wlr_output *wlr_output = data; Monitor *m = wlr_output->data; + int nmons, i = 0; wl_list_remove(&m->destroy.link); wl_list_remove(&m->frame.link); @@ -744,7 +740,7 @@ cleanupmon(struct wl_listener *listener, void *data) wlr_output_layout_remove(output_layout, m->wlr_output); updatemons(); - int nmons = wl_list_length(&mons), i = 0; + 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); @@ -774,11 +770,12 @@ commitlayersurfacenotify(struct wl_listener *listener, void *data) LayerSurface *layersurface = wl_container_of(listener, layersurface, surface_commit); struct wlr_layer_surface_v1 *wlr_layer_surface = layersurface->layer_surface; struct wlr_output *wlr_output = wlr_layer_surface->output; + Monitor *m; if (!wlr_output) return; - Monitor *m = wlr_output->data; + m = wlr_output->data; arrangelayers(m); if (layersurface->layer != wlr_layer_surface->current.layer) { @@ -802,12 +799,14 @@ commitnotify(struct wl_listener *listener, void *data) void createkeyboard(struct wlr_input_device *device) { + struct xkb_context *context; + struct xkb_keymap *keymap; Keyboard *kb = device->data = calloc(1, sizeof(*kb)); kb->device = device; /* Prepare an XKB keymap and assign it to the keyboard. */ - struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - struct xkb_keymap *keymap = xkb_map_new_from_names(context, &xkb_rules, + context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + keymap = xkb_map_new_from_names(context, &xkb_rules, XKB_KEYMAP_COMPILE_NO_FLAGS); wlr_keyboard_set_keymap(device->keyboard, keymap); @@ -835,6 +834,11 @@ createmon(struct wl_listener *listener, void *data) /* This event is raised by the backend when a new output (aka a display or * monitor) becomes available. */ struct wlr_output *wlr_output = data; + Monitor *m; + const MonitorRule *r; + size_t nlayers; + Monitor *moni, *insertmon = NULL; + int x = 0; /* The mode is a tuple of (width, height, refresh rate), and each * monitor supports only a specific set of modes. We just pick the @@ -843,11 +847,11 @@ createmon(struct wl_listener *listener, void *data) wlr_output_set_mode(wlr_output, wlr_output_preferred_mode(wlr_output)); /* Allocates and configures monitor state using configured rules */ - Monitor *m = wlr_output->data = calloc(1, sizeof(*m)); + m = wlr_output->data = calloc(1, sizeof(*m)); m->wlr_output = wlr_output; m->tagset[0] = m->tagset[1] = 1; m->position = -1; - for (const MonitorRule *r = monrules; r < END(monrules); r++) { + for (r = monrules; r < END(monrules); r++) { if (!r->name || strstr(wlr_output->name, r->name)) { m->mfact = r->mfact; m->nmaster = r->nmaster; @@ -867,8 +871,6 @@ createmon(struct wl_listener *listener, void *data) m->destroy.notify = cleanupmon; wl_signal_add(&wlr_output->events.destroy, &m->destroy); - Monitor *moni, *insertmon = NULL; - int x = 0; wl_list_for_each(moni, &mons, link) if (m->position > moni->position) insertmon = moni; @@ -898,7 +900,7 @@ createmon(struct wl_listener *listener, void *data) } sgeom = *wlr_output_layout_get_box(output_layout, NULL); - size_t nlayers = LENGTH(m->layers); + nlayers = LENGTH(m->layers); for (size_t i = 0; i < nlayers; ++i) wl_list_init(&m->layers[i]); @@ -958,13 +960,15 @@ 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 *layersurface = calloc(1, sizeof(LayerSurface)); - + layersurface = calloc(1, sizeof(LayerSurface)); layersurface->surface_commit.notify = commitlayersurfacenotify; wl_signal_add(&wlr_layer_surface->surface->events.commit, &layersurface->surface_commit); @@ -978,14 +982,13 @@ createlayersurface(struct wl_listener *listener, void *data) layersurface->layer_surface = wlr_layer_surface; wlr_layer_surface->data = layersurface; - Monitor *m = wlr_layer_surface->output->data; - + m = wlr_layer_surface->output->data; wl_list_insert(&m->layers[wlr_layer_surface->client_pending.layer], &layersurface->link); // Temporarily set the layer's current state to client_pending // so that we can easily arrange it - struct wlr_layer_surface_v1_state old_state = wlr_layer_surface->current; + old_state = wlr_layer_surface->current; wlr_layer_surface->current = wlr_layer_surface->client_pending; arrangelayers(m); wlr_layer_surface->current = old_state; @@ -1156,14 +1159,15 @@ dirtomon(int dir) void focusclient(Client *c, int lift) { + struct wlr_surface *old = seat->keyboard_state.focused_surface; + struct wlr_keyboard *kb; + /* Raise client in stacking order if requested */ if (c && lift) { wl_list_remove(&c->slink); wl_list_insert(&stack, &c->slink); } - struct wlr_surface *old = seat->keyboard_state.focused_surface; - if (c && WLR_SURFACE(c) == old) return; @@ -1208,7 +1212,7 @@ focusclient(Client *c, int lift) } /* Have a client, so focus its top-level wlr_surface */ - struct wlr_keyboard *kb = wlr_seat_get_keyboard(seat); + kb = wlr_seat_get_keyboard(seat); wlr_seat_keyboard_notify_enter(seat, WLR_SURFACE(c), kb->keycodes, kb->num_keycodes, &kb->modifiers); @@ -1287,6 +1291,8 @@ 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); @@ -1298,11 +1304,12 @@ inputdevice(struct wl_listener *listener, void *data) /* 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? */ - uint32_t caps = WL_SEAT_CAPABILITY_POINTER; + caps = WL_SEAT_CAPABILITY_POINTER; if (!wl_list_empty(&keyboards)) caps |= WL_SEAT_CAPABILITY_KEYBOARD; wlr_seat_set_capabilities(seat, caps); @@ -1479,6 +1486,10 @@ motionabsolute(struct wl_listener *listener, void *data) void motionnotify(uint32_t time) { + double sx = 0, sy = 0; + struct wlr_surface *surface = NULL; + Client *c = NULL; + // time is 0 in internal calls meant to restore pointer focus. if (time) { wlr_idle_notify_activity(idle, seat); @@ -1501,9 +1512,6 @@ motionnotify(uint32_t time) return; } - struct wlr_surface *surface = NULL; - double sx = 0, sy = 0; - Client *c = NULL; if ((surface = xytolayersurface(&selmon->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], cursor->x, cursor->y, &sx, &sy))) ; @@ -1628,8 +1636,7 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test) if (m->wlr_output->name == wlr_output->name) { // focus the left monitor (relative to the current focus) m->wlr_output->enabled = !m->wlr_output->enabled; - Arg ar = {.i = -1}; - focusmon(&ar); + focusmon(&(Arg) {.i = -1}); closemon(m); m->wlr_output->enabled = !m->wlr_output->enabled; } @@ -1662,6 +1669,9 @@ void pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, uint32_t time) { + struct timespec now; + int internal_call = !time; + /* Use top level surface if nothing more specific given */ if (c && !surface) surface = WLR_SURFACE(c); @@ -1672,9 +1682,7 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, return; } - int internal_call = !time; - if (!time) { - struct timespec now; + if (internal_call) { clock_gettime(CLOCK_MONOTONIC, &now); time = now.tv_sec * 1000 + now.tv_nsec / 1000000; } @@ -1713,6 +1721,10 @@ 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 @@ -1727,17 +1739,14 @@ render(struct wlr_surface *surface, int sx, int sy, void *data) * one next to the other, both 1080p, a client on the rightmost display might * have layout coordinates of 2000,100. We need to translate that to * output-local coordinates, or (2000 - 1920). */ - double ox = 0, oy = 0; wlr_output_layout_output_coords(output_layout, output, &ox, &oy); /* We also have to apply the scale factor for HiDPI outputs. This is only * part of the puzzle, dwl does not fully support HiDPI. */ - struct wlr_box obox = { - .x = ox + rdata->x + sx, - .y = oy + rdata->y + sy, - .width = surface->current.width, - .height = surface->current.height, - }; + 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); /* @@ -1751,9 +1760,7 @@ render(struct wlr_surface *surface, int sx, int sy, void *data) * Naturally you can do this any way you like, for example to make a 3D * compositor. */ - float matrix[9]; - enum wl_output_transform transform = wlr_output_transform_invert( - surface->current.transform); + transform = wlr_output_transform_invert(surface->current.transform); wlr_matrix_project_box(matrix, &obox, transform, 0, output->transform_matrix); @@ -1843,6 +1850,9 @@ renderlayer(struct wl_list *layer_surfaces, struct timespec *now) void rendermon(struct wl_listener *listener, void *data) { + Client *c; + int render = 1; + /* This function is called every time an output is ready to display a frame, * generally at the output's refresh rate (e.g. 60Hz). */ Monitor *m = wl_container_of(listener, m, frame); @@ -1851,8 +1861,6 @@ rendermon(struct wl_listener *listener, void *data) clock_gettime(CLOCK_MONOTONIC, &now); /* Do not render if any XDG clients have an outstanding resize. */ - Client *c; - int render = 1; wl_list_for_each(c, &stack, slink) { if (c->resize) { wlr_surface_send_frame_done(WLR_SURFACE(c), &now); @@ -1902,11 +1910,11 @@ resize(Client *c, int x, int y, int w, int h, int interact) * compositor, you'd wait for the client to prepare a buffer at * the new size, then commit any movement that was prepared. */ + struct wlr_box *bbox = interact ? &sgeom : &c->mon->w; c->geom.x = x; c->geom.y = y; c->geom.width = w; c->geom.height = h; - struct wlr_box *bbox = interact ? &sgeom : &c->mon->w; applybounds(c, bbox); /* wlroots makes this a no-op if size hasn't changed */ #ifdef XWAYLAND @@ -1923,6 +1931,8 @@ resize(Client *c, int x, int y, int w, int h, int interact) void run(char *startup_cmd) { + pid_t startup_pid = -1; + /* Add a Unix socket to the Wayland display. */ const char *socket = wl_display_add_socket_auto(dpy); if (!socket) @@ -1948,9 +1958,8 @@ run(char *startup_cmd) * startup command if requested. */ setenv("WAYLAND_DISPLAY", socket, 1); - pid_t startup_pid = -1; if (startup_cmd) { - pid_t startup_pid = fork(); + startup_pid = fork(); if (startup_pid < 0) EBARF("startup: fork"); if (startup_pid == 0) { @@ -2030,9 +2039,11 @@ setlayout(const Arg *arg) void setmfact(const Arg *arg) { + float f; + if (!arg || !selmon->lt[selmon->sellt]->arrange) return; - float f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; + f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; if (f < 0.1 || f > 0.9) return; selmon->mfact = f; @@ -2333,10 +2344,11 @@ togglefloating(const Arg *arg) void toggletag(const Arg *arg) { + unsigned int newtags; Client *sel = selclient(); if (!sel) return; - unsigned int newtags = sel->tags ^ (arg->ui & TAGMASK); + newtags = sel->tags ^ (arg->ui & TAGMASK); if (newtags) { sel->tags = newtags; focusclient(focustop(selmon), 1); From 1678b059055e6b0686724ac3a4233c98b95c205a Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Thu, 24 Dec 2020 22:26:44 -0500 Subject: [PATCH 136/173] don't call applyexclusive just to return --- dwl.c | 86 +++++++++++++++++++++++++++++------------------------------ 1 file changed, 42 insertions(+), 44 deletions(-) diff --git a/dwl.c b/dwl.c index e57928f..97cb4b2 100644 --- a/dwl.c +++ b/dwl.c @@ -404,47 +404,44 @@ 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[4]; - - if (exclusive <= 0) - return; - - // Top - edges[0].singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP; - edges[0].anchor_triplet = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP; - edges[0].positive_axis = &usable_area->y; - edges[0].negative_axis = &usable_area->height; - edges[0].margin = margin_top; - - // Bottom - edges[1].singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; - edges[1].anchor_triplet = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; - edges[1].positive_axis = NULL; - edges[1].negative_axis = &usable_area->height; - edges[1].margin = margin_bottom; - - // Left - edges[2].singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT; - edges[2].anchor_triplet = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; - edges[2].positive_axis = &usable_area->x; - edges[2].negative_axis = &usable_area->width; - edges[2].margin = margin_left; - - // Right - edges[3].singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; - edges[3].anchor_triplet = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; - edges[3].positive_axis = NULL; - edges[3].negative_axis = &usable_area->width; - edges[3].margin = margin_right; - + 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) { @@ -574,9 +571,10 @@ arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, int } layersurface->geo = box; - applyexclusive(usable_area, state->anchor, state->exclusive_zone, - state->margin.top, state->margin.right, - state->margin.bottom, state->margin.left); + 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_layer_surface_v1_configure(wlr_layer_surface, box.width, box.height); } } From 5ba09b56f3a7a9717e1f874b693dc08d2d50d7f8 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Thu, 24 Dec 2020 22:36:49 -0500 Subject: [PATCH 137/173] missed a few of these --- config.def.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config.def.h b/config.def.h index 64dfea8..8c1f5ba 100644 --- a/config.def.h +++ b/config.def.h @@ -1,5 +1,5 @@ /* appearance */ -static const bool sloppyfocus = true; /* focus follows mouse */ +static const int sloppyfocus = 1; /* focus follows mouse */ static const unsigned int borderpx = 1; /* border pixel of windows */ static const float rootcolor[] = {0.3, 0.3, 0.3, 1.0}; static const float bordercolor[] = {0.5, 0.5, 0.5, 1.0}; @@ -48,8 +48,8 @@ static const int repeat_rate = 25; static const int repeat_delay = 600; /* Trackpad */ -static const bool tap_to_click = 1; -static const bool natural_scrolling = 1; +static const int tap_to_click = 1; +static const int natural_scrolling = 1; #define MODKEY WLR_MODIFIER_ALT #define TAGKEYS(KEY,SKEY,TAG) \ From efe2a97bcf75136e813ba9dfbb932a9cc87c47d6 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Thu, 24 Dec 2020 22:39:45 -0500 Subject: [PATCH 138/173] fix build with -DXWAYLAND --- dwl.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/dwl.c b/dwl.c index 97cb4b2..c4fb02e 100644 --- a/dwl.c +++ b/dwl.c @@ -2540,13 +2540,13 @@ configurex11(struct wl_listener *listener, void *data) void createnotifyx11(struct wl_listener *listener, void *data) { + struct wlr_xwayland_surface *xwayland_surface = data; Client *c; wl_list_for_each(c, &clients, link) if (c->isfullscreen && VISIBLEON(c, c->mon)) setfullscreen(c, 0); /* Allocate a Client for this surface */ - struct wlr_xwayland_surface *xwayland_surface = data; c = xwayland_surface->data = calloc(1, sizeof(*c)); c->surface.xwayland = xwayland_surface; c->type = xwayland_surface->override_redirect ? X11Unmanaged : X11Managed; @@ -2586,26 +2586,23 @@ 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) { - struct wlr_box geom = { - .x = c->surface.xwayland->x, - .y = c->surface.xwayland->y, - .width = c->surface.xwayland->width, - .height = c->surface.xwayland->height, - }; + 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; - struct render_data rdata = { - .output = output, - .when = now, - .x = c->surface.xwayland->x, - .y = c->surface.xwayland->y, - }; - + 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); } } @@ -2624,6 +2621,7 @@ updatewindowtype(Client *c) 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) { @@ -2642,7 +2640,7 @@ xwaylandready(struct wl_listener *listener, void *data) wlr_xwayland_set_seat(xwayland, seat); /* Set the default XWayland cursor to match the rest of dwl. */ - struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor(cursor_mgr, "left_ptr", 1); + 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, From bac3221a038d8f0b49f28a4046a89e58646e8622 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Fri, 25 Dec 2020 00:37:26 -0500 Subject: [PATCH 139/173] Stivvo deserves a shout-out too! --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9596ac1..9c491b9 100644 --- a/README.md +++ b/README.md @@ -75,3 +75,4 @@ Many thanks to suckless.org and the dwm developers and community for the inspira - Alexander Courtis for the XWayland implementation - Guido Cella for the layer-shell protocol implementation and for helping to keep the project running +- Stivvo for output management and fullscreen support From 388c5580cbb032c9e5c3a9b2b578f0e0e27fc97b Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Fri, 25 Dec 2020 01:33:55 -0500 Subject: [PATCH 140/173] consolidate some of the ugliness into a separate file Similar to Linux kernel approach, encapsulate some of the uglier conditional compilation into inline functions in header files. The goal is to make dwl.c more attractive to people who embrace the suckless philosophy - simple, short, hackable, and easy to understand. We want dwm users to feel comfortable here, not scare them off. Plus, if we do this right, the main dwl.c code should require only minimal changes once XWayland is no longer a necessary evil. According to `cloc`, this also brings dwl.c down below 2000 lines of non-blank, non-comment code. --- client.h | 163 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ config.mk | 4 +- dwl.c | 160 ++++++++++++----------------------------------------- 3 files changed, 200 insertions(+), 127 deletions(-) create mode 100644 client.h diff --git a/client.h b/client.h new file mode 100644 index 0000000..f4735c2 --- /dev/null +++ b/client.h @@ -0,0 +1,163 @@ +/* + * Attempt to consolidate unavoidable suck into one file, away from dwl.c. This + * file is not meant to be pretty. We use a .h file with static inline + * functions instead of a separate .c module, or function pointers like sway, so + * that they will simply compile out if the chosen #defines leave them unused. + */ + +/* Leave this function first; it's used in the others */ +static inline int +client_is_x11(Client *c) +{ +#ifdef XWAYLAND + return c->type == X11Managed || c->type == X11Unmanaged; +#else + return 0; +#endif +} + +/* The others */ +static inline void +client_activate_surface(struct wlr_surface *s, int activated) +{ +#ifdef XWAYLAND + if (wlr_surface_is_xwayland_surface(s)) { + wlr_xwayland_surface_activate( + wlr_xwayland_surface_from_wlr_surface(s), activated); + return; + } +#endif + if (wlr_surface_is_xdg_surface(s)) + wlr_xdg_toplevel_set_activated( + wlr_xdg_surface_from_wlr_surface(s), activated); +} + +static inline void +client_for_each_surface(Client *c, wlr_surface_iterator_func_t fn, void *data) +{ +#ifdef XWAYLAND + if (client_is_x11(c)) { + wlr_surface_for_each_surface(c->surface.xwayland->surface, + fn, data); + return; + } +#endif + wlr_xdg_surface_for_each_surface(c->surface.xdg, fn, data); +} + +static inline const char * +client_get_appid(Client *c) +{ +#ifdef XWAYLAND + if (client_is_x11(c)) + return c->surface.xwayland->class; +#endif + return c->surface.xdg->toplevel->app_id; +} + +static inline void +client_get_geometry(Client *c, struct wlr_box *geom) +{ +#ifdef XWAYLAND + if (client_is_x11(c)) { + geom->x = c->surface.xwayland->x; + geom->y = c->surface.xwayland->y; + geom->width = c->surface.xwayland->width; + geom->height = c->surface.xwayland->height; + return; + } +#endif + wlr_xdg_surface_get_geometry(c->surface.xdg, geom); +} + +static inline const char * +client_get_title(Client *c) +{ +#ifdef XWAYLAND + if (client_is_x11(c)) + return c->surface.xwayland->title; +#endif + return c->surface.xdg->toplevel->title; +} + +static inline int +client_is_float_type(Client *c) +{ +#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]) + return 1; +#endif + return 0; +} + +static inline int +client_is_unmanaged(Client *c) +{ +#ifdef XWAYLAND + return c->type == X11Unmanaged; +#endif + return 0; +} + +static inline void +client_send_close(Client *c) +{ +#ifdef XWAYLAND + if (client_is_x11(c)) { + wlr_xwayland_surface_close(c->surface.xwayland); + return; + } +#endif + wlr_xdg_toplevel_send_close(c->surface.xdg); +} + +static inline void +client_set_fullscreen(Client *c, int fullscreen) +{ +#ifdef XWAYLAND + if (client_is_x11(c)) { + wlr_xwayland_surface_set_fullscreen(c->surface.xwayland, fullscreen); + return; + } +#endif + wlr_xdg_toplevel_set_fullscreen(c->surface.xdg, fullscreen); +} + +static inline uint32_t +client_set_size(Client *c, uint32_t width, uint32_t height) +{ +#ifdef XWAYLAND + if (client_is_x11(c)) { + wlr_xwayland_surface_configure(c->surface.xwayland, + c->geom.x, c->geom.y, width, height); + return 0; + } +#endif + return wlr_xdg_toplevel_set_size(c->surface.xdg, width, height); +} + +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) +{ +#ifdef XWAYLAND + if (client_is_x11(c)) + return wlr_surface_surface_at(c->surface.xwayland->surface, + cx, cy, sx, sy); +#endif + return wlr_xdg_surface_surface_at(c->surface.xdg, cx, cy, sx, sy); +} diff --git a/config.mk b/config.mk index d84f14d..9a886cc 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-error=unused-function -Wdeclaration-after-statement +CFLAGS ?= -g -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare -Wno-unused-function -Wno-unused-variable -Wdeclaration-after-statement # Uncomment to build XWayland support -#CFLAGS += -DXWAYLAND +CFLAGS += -DXWAYLAND diff --git a/dwl.c b/dwl.c index c4fb02e..fa6379a 100644 --- a/dwl.c +++ b/dwl.c @@ -58,11 +58,6 @@ #define END(A) ((A) + LENGTH(A)) #define TAGMASK ((1 << LENGTH(tags)) - 1) #define ROUND(X) ((int)((X)+0.5)) -#ifdef XWAYLAND -#define WLR_SURFACE(C) ((C)->type != XDGShell ? (C)->surface.xwayland->surface : (C)->surface.xdg->surface) -#else -#define WLR_SURFACE(C) ((C)->surface.xdg->surface) -#endif /* enums */ enum { CurNormal, CurMove, CurResize }; /* cursor */ @@ -93,14 +88,8 @@ typedef struct { struct wl_list slink; union { struct wlr_xdg_surface *xdg; -#ifdef XWAYLAND struct wlr_xwayland_surface *xwayland; -#endif } surface; -#ifdef XWAYLAND - struct wl_listener activate; - struct wl_listener configure; -#endif struct wl_listener commit; struct wl_listener map; struct wl_listener unmap; @@ -110,6 +99,8 @@ typedef struct { Monitor *mon; #ifdef XWAYLAND unsigned int type; + struct wl_listener activate; + struct wl_listener configure; #endif int bw; unsigned int tags; @@ -326,10 +317,8 @@ 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; @@ -366,7 +355,6 @@ 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 updatewindowtype(Client *c); 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}; @@ -378,6 +366,9 @@ static Atom netatom[NetLast]; /* configuration, allows nested code to access above variables */ #include "config.h" +/* attempt to encapsulate suck into one file */ +#include "client.h" + /* compile-time check if all tags fit into an unsigned int bit array. */ struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; @@ -462,20 +453,11 @@ applyrules(Client *c) unsigned int i, newtags = 0; const Rule *r; Monitor *mon = selmon, *m; -#ifdef XWAYLAND - if (c->type != XDGShell) { - updatewindowtype(c); - appid = c->surface.xwayland->class; - title = c->surface.xwayland->title; - } else -#endif - { - appid = c->surface.xdg->toplevel->app_id; - title = c->surface.xdg->toplevel->title; - } - if (!appid) + + c->isfloating = client_is_float_type(c); + if (!(appid = client_get_appid(c))) appid = broken; - if (!title) + if (!(title = client_get_title(c))) title = broken; for (r = rules; r < END(rules); r++) { @@ -1107,13 +1089,7 @@ setfullscreen(Client *c, int fullscreen) { c->isfullscreen = fullscreen; c->bw = (1 - fullscreen) * borderpx; - -#ifdef XWAYLAND - if (c->type == X11Managed) - wlr_xwayland_surface_set_fullscreen(c->surface.xwayland, fullscreen); - else -#endif - wlr_xdg_toplevel_set_fullscreen(c->surface.xdg, fullscreen); + client_set_fullscreen(c, fullscreen); if (fullscreen) { c->prevx = c->geom.x; @@ -1166,7 +1142,7 @@ focusclient(Client *c, int lift) wl_list_insert(&stack, &c->slink); } - if (c && WLR_SURFACE(c) == old) + if (c && client_surface(c) == old) return; /* Put the new client atop the focus stack and select its monitor */ @@ -1177,21 +1153,13 @@ focusclient(Client *c, int lift) } /* Deactivate old client if focus is changing */ - if (old && (!c || WLR_SURFACE(c) != old)) { - if (wlr_surface_is_xdg_surface(old)) - wlr_xdg_toplevel_set_activated( - wlr_xdg_surface_from_wlr_surface(old), 0); -#ifdef XWAYLAND - else if (wlr_surface_is_xwayland_surface(old)) - wlr_xwayland_surface_activate( - wlr_xwayland_surface_from_wlr_surface(old), 0); -#endif + 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. */ - else if (wlr_surface_is_layer_surface(old)) { + if (wlr_surface_is_layer_surface(old)) { struct wlr_layer_surface_v1 *wlr_layer_surface = wlr_layer_surface_v1_from_wlr_surface(old); @@ -1200,6 +1168,8 @@ focusclient(Client *c, int lift) wlr_layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY )) return; + } else { + client_activate_surface(old, 0); } } @@ -1211,16 +1181,11 @@ focusclient(Client *c, int lift) /* Have a client, so focus its top-level wlr_surface */ kb = wlr_seat_get_keyboard(seat); - wlr_seat_keyboard_notify_enter(seat, WLR_SURFACE(c), + wlr_seat_keyboard_notify_enter(seat, client_surface(c), kb->keycodes, kb->num_keycodes, &kb->modifiers); /* Activate the new client */ -#ifdef XWAYLAND - if (c->type != XDGShell) - wlr_xwayland_surface_activate(c->surface.xwayland, 1); - else -#endif - wlr_xdg_toplevel_set_activated(c->surface.xdg, 1); + client_activate_surface(client_surface(c), 1); } void @@ -1390,13 +1355,7 @@ killclient(const Arg *arg) Client *sel = selclient(); if (!sel) return; - -#ifdef XWAYLAND - if (sel->type != XDGShell) - wlr_xwayland_surface_close(sel->surface.xwayland); - else -#endif - wlr_xdg_toplevel_send_close(sel->surface.xdg); + client_send_close(sel); } void @@ -1413,32 +1372,20 @@ 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), *oldfocus = selclient(); -#ifdef XWAYLAND - if (c->type == X11Unmanaged) { + if (client_is_unmanaged(c)) { /* Insert this independent into independents lists. */ wl_list_insert(&independents, &c->link); return; } -#endif /* Insert this client into client lists. */ wl_list_insert(&clients, &c->link); wl_list_insert(&fstack, &c->flink); wl_list_insert(&stack, &c->slink); -#ifdef XWAYLAND - if (c->type != XDGShell) { - c->geom.x = c->surface.xwayland->x; - c->geom.y = c->surface.xwayland->y; - c->geom.width = c->surface.xwayland->width + 2 * c->bw; - c->geom.height = c->surface.xwayland->height + 2 * c->bw; - } else -#endif - { - wlr_xdg_surface_get_geometry(c->surface.xdg, &c->geom); - c->geom.width += 2 * c->bw; - c->geom.height += 2 * c->bw; - } + client_get_geometry(c, &c->geom); + c->geom.width += 2 * c->bw; + c->geom.height += 2 * c->bw; /* Set initial monitor, tags, floating status, and focus */ applyrules(c); @@ -1527,16 +1474,8 @@ motionnotify(uint32_t time) } #endif else if ((c = xytoclient(cursor->x, cursor->y))) { -#ifdef XWAYLAND - if (c->type != XDGShell) - surface = wlr_surface_surface_at(c->surface.xwayland->surface, - cursor->x - c->geom.x - c->bw, - cursor->y - c->geom.y - c->bw, &sx, &sy); - else -#endif - surface = wlr_xdg_surface_surface_at(c->surface.xdg, - cursor->x - c->geom.x - c->bw, - cursor->y - c->geom.y - c->bw, &sx, &sy); + 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))) @@ -1672,7 +1611,7 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, /* Use top level surface if nothing more specific given */ if (c && !surface) - surface = WLR_SURFACE(c); + surface = client_surface(c); /* If surface is NULL, clear pointer focus */ if (!surface) { @@ -1695,14 +1634,9 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, * of its surfaces, and make keyboard focus follow if desired. */ wlr_seat_pointer_notify_enter(seat, surface, sx, sy); - if (!c) + if (!c || client_is_unmanaged(c)) return; -#if XWAYLAND - if (c->type == X11Unmanaged) - return; -#endif - if (sloppyfocus && !internal_call) focusclient(c, 0); } @@ -1789,7 +1723,7 @@ renderclients(Monitor *m, struct timespec *now) output_layout, m->wlr_output, &c->geom)) continue; - surface = WLR_SURFACE(c); + surface = client_surface(c); ox = c->geom.x, oy = c->geom.y; wlr_output_layout_output_coords(output_layout, m->wlr_output, &ox, &oy); @@ -1819,12 +1753,7 @@ renderclients(Monitor *m, struct timespec *now) rdata.when = now; rdata.x = c->geom.x + c->bw; rdata.y = c->geom.y + c->bw; -#ifdef XWAYLAND - if (c->type != XDGShell) - wlr_surface_for_each_surface(c->surface.xwayland->surface, render, &rdata); - else -#endif - wlr_xdg_surface_for_each_surface(c->surface.xdg, render, &rdata); + client_for_each_surface(c, render, &rdata); } } @@ -1861,7 +1790,7 @@ rendermon(struct wl_listener *listener, void *data) /* 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(WLR_SURFACE(c), &now); + wlr_surface_send_frame_done(client_surface(c), &now); render = 0; } } @@ -1915,15 +1844,8 @@ resize(Client *c, int x, int y, int w, int h, int interact) c->geom.height = h; applybounds(c, bbox); /* wlroots makes this a no-op if size hasn't changed */ -#ifdef XWAYLAND - if (c->type != XDGShell) - wlr_xwayland_surface_configure(c->surface.xwayland, - c->geom.x, c->geom.y, - c->geom.width - 2 * c->bw, c->geom.height - 2 * c->bw); - else -#endif - c->resize = wlr_xdg_toplevel_set_size(c->surface.xdg, - c->geom.width - 2 * c->bw, c->geom.height - 2 * c->bw); + c->resize = client_set_size(c, c->geom.width - 2 * c->bw, + c->geom.height - 2 * c->bw); } void @@ -2059,13 +1981,13 @@ setmon(Client *c, Monitor *m, unsigned int newtags) /* TODO leave/enter is not optimal but works */ if (oldmon) { - wlr_surface_send_leave(WLR_SURFACE(c), oldmon->wlr_output); + wlr_surface_send_leave(client_surface(c), oldmon->wlr_output); arrange(oldmon); } if (m) { /* Make sure window actually overlaps with the monitor */ applybounds(c, &m->m); - wlr_surface_send_enter(WLR_SURFACE(c), m->wlr_output); + 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); } @@ -2389,10 +2311,9 @@ 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); wl_list_remove(&c->link); -#ifdef XWAYLAND - if (c->type == X11Unmanaged) + if (client_is_unmanaged(c)) return; -#endif + setmon(c, NULL, 0); wl_list_remove(&c->flink); wl_list_remove(&c->slink); @@ -2607,17 +2528,6 @@ renderindependents(struct wlr_output *output, struct timespec *now) } } -void -updatewindowtype(Client *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]) - c->isfloating = 1; -} - void xwaylandready(struct wl_listener *listener, void *data) { From ef76c921ed9dfef892202342bc4b9c31cc16a0c9 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Fri, 25 Dec 2020 01:38:24 -0500 Subject: [PATCH 141/173] no need to cast NULL to void * --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index fa6379a..518e544 100644 --- a/dwl.c +++ b/dwl.c @@ -1883,7 +1883,7 @@ run(char *startup_cmd) if (startup_pid < 0) EBARF("startup: fork"); if (startup_pid == 0) { - execl("/bin/sh", "/bin/sh", "-c", startup_cmd, (void *)NULL); + execl("/bin/sh", "/bin/sh", "-c", startup_cmd, NULL); EBARF("startup: execl"); } } From 848d89b7f37803e579e54a314f36bd6b5d7a35c1 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Fri, 25 Dec 2020 02:10:50 -0500 Subject: [PATCH 142/173] XWayland still buggy, disable by default --- config.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.mk b/config.mk index 9a886cc..cd4e821 100644 --- a/config.mk +++ b/config.mk @@ -5,4 +5,4 @@ PREFIX = /usr/local CFLAGS ?= -g -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare -Wno-unused-function -Wno-unused-variable -Wdeclaration-after-statement # Uncomment to build XWayland support -CFLAGS += -DXWAYLAND +#CFLAGS += -DXWAYLAND From e0a48accc5142e351314375ead3e4559677ef49b Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Fri, 25 Dec 2020 11:27:17 -0500 Subject: [PATCH 143/173] making this a team --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index b23aa92..e4bb015 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ dwl - dwm for Wayland -Copyright © 2020 Devin J. Pohly +Copyright © 2020 dwl team See also the files LICENSE.tinywl and LICENSE.dwm. From 2230f92ef008e0c8d92acb8fee701fefc91939dd Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Fri, 25 Dec 2020 12:07:43 -0500 Subject: [PATCH 144/173] add "all" target --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index cee8963..3d6ace5 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,7 @@ PKGS = wlroots wayland-server xcb xkbcommon libinput CFLAGS += $(foreach p,$(PKGS),$(shell pkg-config --cflags $(p))) LDLIBS += $(foreach p,$(PKGS),$(shell pkg-config --libs $(p))) +all: dwl # 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 dfc347af2fb9f7c4109b4e15492f742a844fa688 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Fri, 25 Dec 2020 12:26:21 -0500 Subject: [PATCH 145/173] add Discord link --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 9c491b9..aa70dbe 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # dwl - dwm for Wayland +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: - Easy to understand, hack on, and extend with patches From df610157e54e82b69b0e2d0698aa952f4c584f1a Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Fri, 25 Dec 2020 15:51:12 -0500 Subject: [PATCH 146/173] update README based on contributions --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index aa70dbe..4296e44 100644 --- a/README.md +++ b/README.md @@ -18,14 +18,14 @@ dwl is not meant to provide every feature under the sun. Instead, like dwm, it s - XWayland support as provided by wlroots - Zero flickering - Wayland users naturally expect that "every frame is perfect" -Features yet to be implemented are: +Features yet to be implemented (possibly as patches) are: -- Write a dwl-status protocol that bars can implement to show tags. You can already use Waybar or yambar, but without tag information +- Communication from the compositor to status bars. One possibility is to create a dwl-status protocol that bars can implement to be notified of compositor information. You can already use Waybar or yambar, but without tag information - Implement the input-inhibitor protocol to support screen lockers -- Implement the idle-inhibit protocol which the lets application such as mpv disable idle monitoring, and distribute it as a patch +- 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 (this should be worth it according to https://mozillagfx.wordpress.com/2019/10/22/dramatically-reduced-power-usage-in-firefox-70-on-macos-with-core-animation/) +- 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) From 167dc0300480ecabdb690b978a6554ea45b84f1b Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Fri, 25 Dec 2020 16:04:07 -0500 Subject: [PATCH 147/173] credit for patch maintenance as well --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4296e44..e44a2eb 100644 --- a/README.md +++ b/README.md @@ -76,5 +76,5 @@ dwl began by extending the TinyWL example provided (CC0) by the sway/wlroots dev Many thanks to suckless.org and the dwm developers and community for the inspiration, and to the various contributors to the project, including: - Alexander Courtis for the XWayland implementation -- Guido Cella for the layer-shell protocol implementation and for helping to keep the project running -- Stivvo for output management and fullscreen support +- Guido Cella for the layer-shell protocol implementation, patch maintenance, and for helping to keep the project running +- Stivvo for output management and fullscreen support, and patch maintenance From 7ed4956b2380fc58219a3f51dd8f74e94ae09cf4 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sun, 27 Dec 2020 12:54:39 -0500 Subject: [PATCH 148/173] Revert "remove github directory" This reverts commit 572ccd92c43b83b677e87c3926d0f5703224c2d8. --- .github/ISSUE_TEMPLATE/bug_report.md | 10 ++++++++++ .github/ISSUE_TEMPLATE/enhancement-idea.md | 10 ++++++++++ 2 files changed, 20 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/enhancement-idea.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..2a18c75 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,10 @@ +--- +name: Bug report +about: Something in dwl isn't working correctly +title: '' +labels: 'Type: bug' +assignees: '' + +--- + + diff --git a/.github/ISSUE_TEMPLATE/enhancement-idea.md b/.github/ISSUE_TEMPLATE/enhancement-idea.md new file mode 100644 index 0000000..92c6c8c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/enhancement-idea.md @@ -0,0 +1,10 @@ +--- +name: Enhancement idea +about: Suggest a feature or improvement +title: '' +labels: 'Type: enhancement' +assignees: '' + +--- + + From 7b320a195ca343f90f4da7687f06d5b1561d3f53 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sun, 27 Dec 2020 13:28:37 -0500 Subject: [PATCH 149/173] macroize event handlers This was a prime candidate for reducing the verbosity of some of our code --- dwl.c | 84 +++++++++++++++++++++++------------------------------------ 1 file changed, 33 insertions(+), 51 deletions(-) diff --git a/dwl.c b/dwl.c index 518e544..8a4e6ca 100644 --- a/dwl.c +++ b/dwl.c @@ -58,6 +58,7 @@ #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 */ @@ -795,12 +796,9 @@ createkeyboard(struct wlr_input_device *device) wlr_keyboard_set_repeat_info(device->keyboard, repeat_rate, repeat_delay); /* Here we set up listeners for keyboard events. */ - kb->modifiers.notify = keypressmod; - wl_signal_add(&device->keyboard->events.modifiers, &kb->modifiers); - kb->key.notify = keypress; - wl_signal_add(&device->keyboard->events.key, &kb->key); - kb->destroy.notify = cleanupkeyboard; - wl_signal_add(&device->events.destroy, &kb->destroy); + 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); @@ -846,10 +844,8 @@ createmon(struct wl_listener *listener, void *data) } wlr_output_enable_adaptive_sync(wlr_output, 1); /* Set up event listeners */ - m->frame.notify = rendermon; - wl_signal_add(&wlr_output->events.frame, &m->frame); - m->destroy.notify = cleanupmon; - wl_signal_add(&wlr_output->events.destroy, &m->destroy); + LISTEN(&wlr_output->events.frame, &m->frame, rendermon); + LISTEN(&wlr_output->events.destroy, &m->destroy, cleanupmon); wl_list_for_each(moni, &mons, link) if (m->position > moni->position) @@ -921,18 +917,12 @@ createnotify(struct wl_listener *listener, void *data) wlr_xdg_toplevel_set_tiled(c->surface.xdg, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT); - /* Listen to the various events it can emit */ - c->commit.notify = commitnotify; - wl_signal_add(&xdg_surface->surface->events.commit, &c->commit); - c->map.notify = mapnotify; - wl_signal_add(&xdg_surface->events.map, &c->map); - c->unmap.notify = unmapnotify; - wl_signal_add(&xdg_surface->events.unmap, &c->unmap); - c->destroy.notify = destroynotify; - wl_signal_add(&xdg_surface->events.destroy, &c->destroy); - - c->fullscreen.notify = fullscreennotify; - wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, &c->fullscreen); + 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.request_fullscreen, &c->fullscreen, + fullscreennotify); c->isfullscreen = 0; } @@ -949,15 +939,14 @@ createlayersurface(struct wl_listener *listener, void *data) } layersurface = calloc(1, sizeof(LayerSurface)); - layersurface->surface_commit.notify = commitlayersurfacenotify; - wl_signal_add(&wlr_layer_surface->surface->events.commit, - &layersurface->surface_commit); - layersurface->destroy.notify = destroylayersurfacenotify; - wl_signal_add(&wlr_layer_surface->events.destroy, &layersurface->destroy); - layersurface->map.notify = maplayersurfacenotify; - wl_signal_add(&wlr_layer_surface->events.map, &layersurface->map); - layersurface->unmap.notify = unmaplayersurfacenotify; - wl_signal_add(&wlr_layer_surface->events.unmap, &layersurface->unmap); + 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; @@ -1001,10 +990,8 @@ 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)); - wl_signal_add(&wlr_deco->events.request_mode, &d->request_mode); - d->request_mode.notify = getxdecomode; - wl_signal_add(&wlr_deco->events.destroy, &d->destroy); - d->destroy.notify = destroyxdeco; + LISTEN(&wlr_deco->events.request_mode, &d->request_mode, getxdecomode); + LISTEN(&wlr_deco->events.destroy, &d->destroy, destroyxdeco); getxdecomode(&d->request_mode, wlr_deco); } @@ -2117,8 +2104,7 @@ setup(void) * 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.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); @@ -2472,22 +2458,18 @@ createnotifyx11(struct wl_listener *listener, void *data) c->surface.xwayland = xwayland_surface; c->type = xwayland_surface->override_redirect ? X11Unmanaged : X11Managed; c->bw = borderpx; + c->isfullscreen = 0; /* Listen to the various events it can emit */ - c->map.notify = mapnotify; - wl_signal_add(&xwayland_surface->events.map, &c->map); - c->unmap.notify = unmapnotify; - wl_signal_add(&xwayland_surface->events.unmap, &c->unmap); - c->activate.notify = activatex11; - wl_signal_add(&xwayland_surface->events.request_activate, &c->activate); - c->configure.notify = configurex11; - wl_signal_add(&xwayland_surface->events.request_configure, &c->configure); - c->destroy.notify = destroynotify; - wl_signal_add(&xwayland_surface->events.destroy, &c->destroy); - - c->fullscreen.notify = fullscreennotify; - wl_signal_add(&xwayland_surface->events.request_fullscreen, &c->fullscreen); - c->isfullscreen = 0; + 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.destroy, &c->destroy, destroynotify); + LISTEN(&xwayland_surface->events.request_fullscreen, &c->fullscreen, + fullscreennotify); } Atom From 7b1b5c75e9498cb21f9d71aee36c8021c5cb23d3 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sun, 27 Dec 2020 14:00:07 -0500 Subject: [PATCH 150/173] fix segfault in xwayland cursor manager --- dwl.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/dwl.c b/dwl.c index 8a4e6ca..065e775 100644 --- a/dwl.c +++ b/dwl.c @@ -2148,13 +2148,11 @@ setup(void) */ xcursor_mgr = wlr_xcursor_manager_create(NULL, 24); wlr_xcursor_manager_load(xcursor_mgr, 1); - xcursor = wlr_xcursor_manager_get_xcursor(xcursor_mgr, "left_ptr", 1); - if (xcursor) { + 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 { @@ -2532,11 +2530,11 @@ xwaylandready(struct wl_listener *listener, void *data) wlr_xwayland_set_seat(xwayland, seat); /* Set the default XWayland cursor to match the rest of dwl. */ - 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); + 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); } From 0090b6bed61c21aae2a30ff5f2556baa97be5069 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sun, 27 Dec 2020 17:15:21 -0500 Subject: [PATCH 151/173] clarify intention of feature suggestions --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e44a2eb..7a4ce86 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,10 @@ dwl is not meant to provide every feature under the sun. Instead, like dwm, it s - XWayland support as provided by wlroots - Zero flickering - Wayland users naturally expect that "every frame is perfect" -Features yet to be implemented (possibly as patches) are: +Features under consideration (possibly as patches) are: -- Communication from the compositor to status bars. One possibility is to create a dwl-status protocol that bars can implement to be notified of compositor information. You can already use Waybar or yambar, but without tag information +- Protocols made trivial by wlroots +- Communication from the compositor to status bars. A straightforward possibility would be to use stdout or a provided file descriptor. - 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) From a2a20cca42f392fa57a3e2432ae9c11c0b15f04a Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sun, 27 Dec 2020 17:15:34 -0500 Subject: [PATCH 152/173] remove limitation notes (tracked in Issues) --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index 7a4ce86..dee0d22 100644 --- a/README.md +++ b/README.md @@ -60,12 +60,6 @@ Note: Wayland requires a valid `XDG_RUNTIME_DIR`, which is usually set up by a s You can find a [list of Wayland applications on the sway wiki](https://github.com/swaywm/sway/wiki/i3-Migration-Guide). -dwl is a work in progress, and it has not yet reached its feature goals in a number of ways: - -- A window's texture is scaled for its "home" monitor only (noticeable when window sits across a monitor boundary) -- Urgent/attention/focus-request ([not yet supported](https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/9) by xdg-shell protocol) -- Damage tracking - ## IRC channel dwl's IRC channel is #dwl on irc.freenode.net. From 313d1f7ecc59ba981fb708a3e564af1733e3289c Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sun, 27 Dec 2020 20:04:05 -0500 Subject: [PATCH 153/173] flesh out cleanup This is the order of *_destroy calls which resulted in the fewest errors/leaks detected by Valgrind. Most of the errors come from the gbm_allocator code - will have to figure out which destroy call is still missing. --- dwl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 065e775..df6e4fb 100644 --- a/dwl.c +++ b/dwl.c @@ -688,11 +688,13 @@ cleanup(void) wlr_xwayland_destroy(xwayland); #endif wl_display_destroy_clients(dpy); - 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 From 33c36be2fc7df3815abfcd8e0bc0bc2b65554ec5 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Mon, 28 Dec 2020 15:51:04 -0500 Subject: [PATCH 154/173] Revert "Allow toggling the layout before selecting a different one" This reverts commit 90cc3b1e2c824db74e932dbb9733d398619a037c. Didn't mean to merge this change. --- dwl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index df6e4fb..ef49b99 100644 --- a/dwl.c +++ b/dwl.c @@ -837,8 +837,7 @@ createmon(struct wl_listener *listener, void *data) m->nmaster = r->nmaster; wlr_output_set_scale(wlr_output, r->scale); wlr_xcursor_manager_load(cursor_mgr, r->scale); - m->lt[0] = r->lt; - m->lt[1] = &layouts[LENGTH(layouts) > 1 && r->lt != &layouts[1]]; + m->lt[0] = m->lt[1] = r->lt; wlr_output_set_transform(wlr_output, r->rr); m->position = r - monrules; break; From fa782896f874cd3398940e71c6adbde411343483 Mon Sep 17 00:00:00 2001 From: Stivvo Date: Wed, 30 Dec 2020 16:50:15 +0100 Subject: [PATCH 155/173] Define monitor's x,y at compile time Replaces the outputOrder patch. This avoids recalculating positions and allows to arrange monitors in any order, not just from left to right. The order in which monitors are defined in config.h still matters but it's just the order in the list, not the actual position. --- config.def.h | 8 +++++--- dwl.c | 21 +++++++-------------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/config.def.h b/config.def.h index 8c1f5ba..982c870 100644 --- a/config.def.h +++ b/config.def.h @@ -28,12 +28,14 @@ static const Layout layouts[] = { * The order in which monitors are defined determines their position. * Non-configured monitors are always added to the left. */ static const MonitorRule monrules[] = { - /* name mfact nmaster scale layout rotate/reflect */ + /* 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 }, + { "eDP-1", 0.5, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, 0, 0 }, */ + /* the order in which monitors are defined here affects the order in which + * focusmon and tagmon cycle trough the monitors */ /* defaults */ - { NULL, 0.55, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL }, + { NULL, 0.55, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, 0, 0 }, }; /* keyboard */ diff --git a/dwl.c b/dwl.c index ef49b99..a7dcc6d 100644 --- a/dwl.c +++ b/dwl.c @@ -186,6 +186,8 @@ typedef struct { float scale; const Layout *lt; enum wl_output_transform rr; + int x; + int y; } MonitorRule; typedef struct { @@ -814,11 +816,9 @@ createmon(struct wl_listener *listener, void *data) /* This event is raised by the backend when a new output (aka a display or * monitor) becomes available. */ struct wlr_output *wlr_output = data; - Monitor *m; const MonitorRule *r; size_t nlayers; - Monitor *moni, *insertmon = NULL; - int x = 0; + Monitor *m, *moni, *insertmon = NULL; /* The mode is a tuple of (width, height, refresh rate), and each * monitor supports only a specific set of modes. We just pick the @@ -851,12 +851,11 @@ createmon(struct wl_listener *listener, void *data) wl_list_for_each(moni, &mons, link) if (m->position > moni->position) insertmon = moni; - if (insertmon) { - x = insertmon->w.x + insertmon->w.width; + + if (insertmon) /* insertmon is the leftmost monitor to m */ wl_list_insert(&insertmon->link, &m->link); - } else { + else wl_list_insert(&mons, &m->link); - } wlr_output_enable(wlr_output, 1); if (!wlr_output_commit(wlr_output)) @@ -868,13 +867,7 @@ 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, x, 0); - wl_list_for_each_reverse(moni, &mons, link) { - /* All monitors to the right of the new one must be moved */ - if (moni == m) - break; - wlr_output_layout_move(output_layout, moni->wlr_output, moni->w.x + m->wlr_output->width, 0); - } + wlr_output_layout_add(output_layout, wlr_output, r->x, r->y); sgeom = *wlr_output_layout_get_box(output_layout, NULL); nlayers = LENGTH(m->layers); From 9677f99dc3d5cb93ee6797609fd358fdfd3b3a3a Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sat, 2 Jan 2021 19:56:40 -0500 Subject: [PATCH 156/173] document that we currently follow wlroots-git Closes #73. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dee0d22..9ae7411 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Feature *non-goals* include: ## Building dwl -dwl has only two dependencies: wlroots 0.12 and wayland-protocols. Simply install these and run `sudo make install`. +dwl has only two dependencies: wlroots-git and wayland-protocols. Simply install these and run `sudo make install`. To enable XWayland, you should also install xorg-xwayland and uncomment its flag in `config.mk`. From 1f97655d5bb8a5e8bfd1b165e88b9f6e60f027ba Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sat, 2 Jan 2021 19:58:46 -0500 Subject: [PATCH 157/173] never ever ever tell anyone to sudo make I am embarrassed to have let this slip through someone's merge. Anybody who genuinely needs to `sudo make` will know it; everyone else should use a proper package manager and build system. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9ae7411..aabaf06 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Feature *non-goals* include: ## Building dwl -dwl has only two dependencies: wlroots-git and wayland-protocols. Simply install these and run `sudo make install`. +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 d7601cd48a3a261f438a8fb2bcef8b839921a8d8 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Sat, 2 Jan 2021 20:02:44 -0500 Subject: [PATCH 158/173] can run nested in Wayland or X --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aabaf06..d3c4a65 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ All configuration is done by editing `config.h` and recompiling, in the same man ## Running dwl -dwl can be run as-is, with no arguments. In an existing Wayland, this will open a window to act as a virtual display. When run from a TTY, the Wayland server will take over the entire virtual terminal. Clients started by dwl will have `WAYLAND_DISPLAY` set in their environment, and other clients can be started from outside the session by setting this variable accordingly. +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. 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`. From 0ff13cf216056a36a261f4eed53c6a864989a9fb Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Mon, 4 Jan 2021 13:55:24 -0500 Subject: [PATCH 159/173] remove EGL parameter from backend_autocreate Tracking with breaking change in swaywm/wlroots#2593. --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index ef49b99..29778d1 100644 --- a/dwl.c +++ b/dwl.c @@ -2022,7 +2022,7 @@ setup(void) * 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, NULL))) + 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. From e8ca8a8ac722d8db819280487a69e08cf3a76573 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Wed, 6 Jan 2021 17:19:44 -0500 Subject: [PATCH 160/173] use output layout for dirtomon No need to track our own order; wlroots has a reasonable default for us already. --- config.def.h | 10 ++++------ dwl.c | 24 +++++++++++------------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/config.def.h b/config.def.h index 982c870..4aefa2b 100644 --- a/config.def.h +++ b/config.def.h @@ -32,8 +32,6 @@ static const MonitorRule monrules[] = { /* example of a HiDPI laptop monitor: { "eDP-1", 0.5, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, 0, 0 }, */ - /* the order in which monitors are defined here affects the order in which - * focusmon and tagmon cycle trough the monitors */ /* defaults */ { NULL, 0.55, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, 0, 0 }, }; @@ -87,10 +85,10 @@ static const Key keys[] = { { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, { MODKEY, XKB_KEY_0, view, {.ui = ~0} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} }, - { MODKEY, XKB_KEY_comma, focusmon, {.i = -1} }, - { MODKEY, XKB_KEY_period, focusmon, {.i = +1} }, - { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_less, tagmon, {.i = -1} }, - { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_greater, tagmon, {.i = +1} }, + { MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} }, + { MODKEY, XKB_KEY_period, focusmon, {.i = WLR_DIRECTION_RIGHT} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_less, tagmon, {.i = WLR_DIRECTION_LEFT} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_greater, tagmon, {.i = WLR_DIRECTION_RIGHT} }, TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0), TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1), TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2), diff --git a/dwl.c b/dwl.c index c09f598..f16f0ed 100644 --- a/dwl.c +++ b/dwl.c @@ -235,7 +235,7 @@ static void cursorframe(struct wl_listener *listener, void *data); static void destroylayersurfacenotify(struct wl_listener *listener, void *data); static void destroynotify(struct wl_listener *listener, void *data); static void destroyxdeco(struct wl_listener *listener, void *data); -static Monitor *dirtomon(int dir); +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); @@ -1096,19 +1096,17 @@ fullscreennotify(struct wl_listener *listener, void *data) } Monitor * -dirtomon(int dir) +dirtomon(enum wlr_direction dir) { - Monitor *m; - - if (dir > 0) { - if (selmon->link.next == &mons) - return wl_container_of(mons.next, m, link); - return wl_container_of(selmon->link.next, m, link); - } else { - if (selmon->link.prev == &mons) - return wl_container_of(mons.prev, m, link); - return wl_container_of(selmon->link.prev, m, link); - } + 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 From 6267593cc2725d13b814fc710b8c8c4ee9ff5599 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Wed, 6 Jan 2021 17:23:35 -0500 Subject: [PATCH 161/173] remove now-unneeded monitor position field --- dwl.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/dwl.c b/dwl.c index f16f0ed..d6bcf39 100644 --- a/dwl.c +++ b/dwl.c @@ -176,7 +176,6 @@ struct Monitor { double mfact; int nmaster; Client *fullscreenclient; - int position; }; typedef struct { @@ -830,7 +829,6 @@ createmon(struct wl_listener *listener, void *data) m = wlr_output->data = calloc(1, sizeof(*m)); m->wlr_output = wlr_output; m->tagset[0] = m->tagset[1] = 1; - m->position = -1; for (r = monrules; r < END(monrules); r++) { if (!r->name || strstr(wlr_output->name, r->name)) { m->mfact = r->mfact; @@ -839,7 +837,6 @@ createmon(struct wl_listener *listener, void *data) wlr_xcursor_manager_load(cursor_mgr, r->scale); m->lt[0] = m->lt[1] = r->lt; wlr_output_set_transform(wlr_output, r->rr); - m->position = r - monrules; break; } } @@ -848,15 +845,7 @@ 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_for_each(moni, &mons, link) - if (m->position > moni->position) - insertmon = moni; - - if (insertmon) /* insertmon is the leftmost monitor to m */ - wl_list_insert(&insertmon->link, &m->link); - else - wl_list_insert(&mons, &m->link); - + wl_list_insert(&mons, &m->link); wlr_output_enable(wlr_output, 1); if (!wlr_output_commit(wlr_output)) return; From 3d696dfb7deb26d87d71a61af52192800e0d56a1 Mon Sep 17 00:00:00 2001 From: sam-barr Date: Mon, 11 Jan 2021 07:47:04 -0500 Subject: [PATCH 162/173] Fix Initialization of NetWM Atoms Additionally, variables xcursor and xcursor_mgr are only used when xwayland is defined, so I make the variables declaration contingent on whether xwayland is being used --- dwl.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index c09f598..4613caa 100644 --- a/dwl.c +++ b/dwl.c @@ -320,8 +320,10 @@ 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; @@ -2517,8 +2519,8 @@ xwaylandready(struct wl_listener *listener, void *data) * not detect that window type. */ netatom[NetWMWindowTypeDialog] = getatom(xc, "_NET_WM_WINDOW_TYPE_DIALOG"); netatom[NetWMWindowTypeSplash] = getatom(xc, "_NET_WM_WINDOW_TYPE_SPLASH"); - netatom[NetWMWindowTypeUtility] = getatom(xc, "_NET_WM_WINDOW_TYPE_TOOLBAR"); - netatom[NetWMWindowTypeToolbar] = getatom(xc, "_NET_WM_WINDOW_TYPE_UTILITY"); + 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); From a58adbb7b99edb8173fc438c189b86118ef83d58 Mon Sep 17 00:00:00 2001 From: meutraa Date: Wed, 13 Jan 2021 15:25:24 +0000 Subject: [PATCH 163/173] Add config.h to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5c28541..0dde90e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ dwl *-protocol.c *-protocol.h .ccls-cache +config.h From 7fe7be5fb868cb39d3ab6d3f5619a3ad36b48777 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Wed, 13 Jan 2021 12:18:27 -0600 Subject: [PATCH 164/173] update layer-shell protocol to version 4 Tracking with breaking change from swaywm/wlroots@b7dc4f2. --- protocols/wlr-layer-shell-unstable-v1.xml | 97 ++++++++++++++++++++--- 1 file changed, 88 insertions(+), 9 deletions(-) diff --git a/protocols/wlr-layer-shell-unstable-v1.xml b/protocols/wlr-layer-shell-unstable-v1.xml index fa67001..d62fd51 100644 --- a/protocols/wlr-layer-shell-unstable-v1.xml +++ b/protocols/wlr-layer-shell-unstable-v1.xml @@ -25,7 +25,7 @@ THIS SOFTWARE. - + Clients can use this interface to assign the surface_layer role to wl_surfaces. Such surfaces are assigned to a "layer" of the output and @@ -47,6 +47,12 @@ or manipulate a buffer prior to the first layer_surface.configure call must also be treated as errors. + After creating a layer_surface object and setting it up, the client + must perform an initial commit without any buffer attached. + The compositor will reply with a layer_surface.configure event. + The client must acknowledge it and is then allowed to attach a buffer + to map the surface. + You may pass NULL for output to allow the compositor to decide which output to use. Generally this will be the one that the user most recently interacted with. @@ -94,7 +100,7 @@ - + An interface that may be implemented by a wl_surface, for surfaces that are designed to be rendered as a layer of a stacked desktop-like @@ -103,6 +109,14 @@ Layer surface state (layer, size, anchor, exclusive zone, margin, interactivity) is double-buffered, and will be applied at the time wl_surface.commit of the corresponding wl_surface is called. + + Attaching a null buffer to a layer surface unmaps it. + + Unmapping a layer_surface means that the surface cannot be shown by the + compositor until it is explicitly mapped again. The layer_surface + returns to the state it had right after layer_shell.get_layer_surface. + The client can re-map the surface by performing a commit without any + buffer attached, waiting for a configure event and handling it as usual. @@ -189,21 +203,85 @@ + + + Types of keyboard interaction possible for layer shell surfaces. The + rationale for this is twofold: (1) some applications are not interested + in keyboard events and not allowing them to be focused can improve the + desktop experience; (2) some applications will want to take exclusive + keyboard focus. + + + + + This value indicates that this surface is not interested in keyboard + events and the compositor should never assign it the keyboard focus. + + This is the default value, set for newly created layer shell surfaces. + + This is useful for e.g. desktop widgets that display information or + only have interaction with non-keyboard input devices. + + + + + Request exclusive keyboard focus if this surface is above the shell surface layer. + + For the top and overlay layers, the seat will always give + exclusive keyboard focus to the top-most layer which has keyboard + interactivity set to exclusive. If this layer contains multiple + surfaces with keyboard interactivity set to exclusive, the compositor + determines the one receiving keyboard events in an implementation- + defined manner. In this case, no guarantee is made when this surface + will receive keyboard focus (if ever). + + For the bottom and background layers, the compositor is allowed to use + normal focus semantics. + + This setting is mainly intended for applications that need to ensure + they receive all keyboard events, such as a lock screen or a password + prompt. + + + + + This requests the compositor to allow this surface to be focused and + unfocused by the user in an implementation-defined manner. The user + should be able to unfocus this surface even regardless of the layer + it is on. + + Typically, the compositor will want to use its normal mechanism to + manage keyboard focus between layer shell surfaces with this setting + and regular toplevels on the desktop layer (e.g. click to focus). + Nevertheless, it is possible for a compositor to require a special + interaction to focus or unfocus layer shell surfaces (e.g. requiring + a click even if focus follows the mouse normally, or providing a + keybinding to switch focus between layers). + + This setting is mainly intended for desktop shell components (e.g. + panels) that allow keyboard interaction. Using this option can allow + implementing a desktop shell that can be fully usable without the + mouse. + + + + - Set to 1 to request that the seat send keyboard events to this layer - surface. For layers below the shell surface layer, the seat will use - normal focus semantics. For layers above the shell surface layers, the - seat will always give exclusive keyboard focus to the top-most layer - which has keyboard interactivity set to true. + Set how keyboard events are delivered to this surface. By default, + layer shell surfaces do not receive keyboard events; this request can + be used to change this. + + This setting is inherited by child surfaces set by the get_popup + request. Layer surfaces receive pointer, touch, and tablet events normally. If you do not want to receive them, set the input region on your surface to an empty region. - Events is double-buffered, see wl_surface.commit. + Keyboard interactivity is double-buffered, see wl_surface.commit. - + @@ -288,6 +366,7 @@ + From a5210f7d77a4849a09eec13e7cf3f9d5c844cf4b Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Mon, 18 Jan 2021 14:12:00 -0600 Subject: [PATCH 165/173] cleanup on aisle createmon() --- dwl.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/dwl.c b/dwl.c index eeb1210..5030a2c 100644 --- a/dwl.c +++ b/dwl.c @@ -818,18 +818,12 @@ createmon(struct wl_listener *listener, void *data) * monitor) becomes available. */ struct wlr_output *wlr_output = data; const MonitorRule *r; - size_t nlayers; - Monitor *m, *moni, *insertmon = NULL; - - /* 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)); - - /* Allocates and configures monitor state using configured rules */ - m = wlr_output->data = calloc(1, sizeof(*m)); + Monitor *m = wlr_output->data = calloc(1, sizeof(*m)); m->wlr_output = wlr_output; + + /* 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 (r = monrules; r < END(monrules); r++) { if (!r->name || strstr(wlr_output->name, r->name)) { @@ -842,7 +836,14 @@ createmon(struct wl_listener *listener, void *data) 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); @@ -861,10 +862,6 @@ 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); - nlayers = LENGTH(m->layers); - for (size_t i = 0; i < nlayers; ++i) - wl_list_init(&m->layers[i]); - /* When adding monitors, the geometries of all monitors must be updated */ updatemons(); wl_list_for_each(m, &mons, link) { From 81d35aaccfdc75a149cf90254938b86ff6435f40 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Mon, 18 Jan 2021 14:12:38 -0600 Subject: [PATCH 166/173] remove unnecessary variable --- dwl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dwl.c b/dwl.c index 5030a2c..2c80209 100644 --- a/dwl.c +++ b/dwl.c @@ -573,7 +573,6 @@ arrangelayers(Monitor *m) ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, ZWLR_LAYER_SHELL_V1_LAYER_TOP, }; - size_t nlayers = LENGTH(layers_above_shell); LayerSurface *layersurface; struct wlr_keyboard *kb = wlr_seat_get_keyboard(seat); @@ -603,7 +602,7 @@ arrangelayers(Monitor *m) &usable_area, 0); // Find topmost keyboard interactive layer, if such a layer exists - for (size_t i = 0; i < nlayers; ++i) { + 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 && From c63d4c41aba02f04c372e4c350a2675f16e6eb4d Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Mon, 18 Jan 2021 14:14:19 -0600 Subject: [PATCH 167/173] unify increment style while we're here --- dwl.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dwl.c b/dwl.c index 2c80209..95a001f 100644 --- a/dwl.c +++ b/dwl.c @@ -437,7 +437,7 @@ applyexclusive(struct wlr_box *usable_area, .margin = margin_right, } }; - for (size_t i = 0; i < LENGTH(edges); ++i) { + 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) @@ -602,7 +602,7 @@ arrangelayers(Monitor *m) &usable_area, 0); // Find topmost keyboard interactive layer, if such a layer exists - for (size_t i = 0; i < LENGTH(layers_above_shell); ++i) { + 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 && @@ -821,7 +821,7 @@ createmon(struct wl_listener *listener, void *data) m->wlr_output = wlr_output; /* Initialize monitor state using configured rules */ - for (size_t i = 0; i < LENGTH(m->layers); ++i) + for (size_t i = 0; i < LENGTH(m->layers); i++) wl_list_init(&m->layers[i]); m->tagset[0] = m->tagset[1] = 1; for (r = monrules; r < END(monrules); r++) { From 43f1076833ac3a46ed3ebbdf25cc04b6aeb394c2 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Mon, 18 Jan 2021 14:16:24 -0600 Subject: [PATCH 168/173] remember to add client.h in Makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3d6ace5..a0d1cc3 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 xdg-shell-protocol.h wlr-layer-shell-unstable-v1-protocol.h idle-protocol.h +dwl.o: 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 5eefb51feab42add01c25a51ef50f296c721da04 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Mon, 18 Jan 2021 14:34:05 -0600 Subject: [PATCH 169/173] use updatemons as handler for output_layout.change This should end up firing precisely when we need to adjust our geometry, rather than us guessing about it based on requests. --- dwl.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/dwl.c b/dwl.c index 95a001f..d3d0d5e 100644 --- a/dwl.c +++ b/dwl.c @@ -290,7 +290,7 @@ static void toggleview(const Arg *arg); 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(); +static void updatemons(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); @@ -341,6 +341,7 @@ 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 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}; @@ -723,7 +724,6 @@ 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); - updatemons(); nmons = wl_list_length(&mons); do // don't switch to disabled mons @@ -862,7 +862,6 @@ createmon(struct wl_listener *listener, void *data) sgeom = *wlr_output_layout_get_box(output_layout, NULL); /* When adding monitors, the geometries of all monitors must be updated */ - updatemons(); wl_list_for_each(m, &mons, link) { /* The first monitor in the list is the most recently added */ Client *c; @@ -1552,11 +1551,9 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test) } else ok &= wlr_output_commit(wlr_output); } - if (ok) { + if (ok) wlr_output_configuration_v1_send_succeeded(config); - if (!test) - updatemons(); - } else + else wlr_output_configuration_v1_send_failed(config); wlr_output_configuration_v1_destroy(config); } @@ -2027,6 +2024,7 @@ setup(void) /* 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 @@ -2283,7 +2281,7 @@ unmapnotify(struct wl_listener *listener, void *data) } void -updatemons() +updatemons(struct wl_listener *listener, void *data) { struct wlr_output_configuration_v1 *config = wlr_output_configuration_v1_create(); From ab032822958dd3041f478c48498f17e5d259dd6f Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Mon, 18 Jan 2021 15:02:15 -0600 Subject: [PATCH 170/173] commit entire output config, or fail and rollback The wlr-output-management protocol requires that either all of the changes from an apply request be applied successfully, in which case a "succeeded" event is sent, or all of the changes are reverted and a "failed" event is sent. As written, this could partially commit changes, then fail. Test the changes first (even for an "apply" event), then commit or rollback as appropriate. --- dwl.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/dwl.c b/dwl.c index d3d0d5e..ef51b0c 100644 --- a/dwl.c +++ b/dwl.c @@ -1545,11 +1545,14 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test) } } - if (test) { - ok &= wlr_output_test(wlr_output); - wlr_output_rollback(wlr_output); - } else - ok &= wlr_output_commit(wlr_output); + 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); From a9e1cd42017e6e9a4fa7008e04a8aa66b7ac5a68 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Mon, 18 Jan 2021 15:38:25 -0600 Subject: [PATCH 171/173] remove hacky code from output-management handler This functionality belongs in updatemons(), where it can hopefully be written a bit more elegantly. --- dwl.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/dwl.c b/dwl.c index ef51b0c..7f8b719 100644 --- a/dwl.c +++ b/dwl.c @@ -1532,17 +1532,6 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test) config_head->state.x, config_head->state.y); wlr_output_set_transform(wlr_output, config_head->state.transform); wlr_output_set_scale(wlr_output, config_head->state.scale); - } else if (wl_list_length(&mons) > 1) { - Monitor *m; - wl_list_for_each(m, &mons, link) { - if (m->wlr_output->name == wlr_output->name) { - // focus the left monitor (relative to the current focus) - m->wlr_output->enabled = !m->wlr_output->enabled; - focusmon(&(Arg) {.i = -1}); - closemon(m); - m->wlr_output->enabled = !m->wlr_output->enabled; - } - } } if (!(ok = wlr_output_test(wlr_output))) @@ -2294,6 +2283,9 @@ updatemons(struct wl_listener *listener, void *data) 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); /* Calculate the effective monitor geometry to use for clients */ From 9c1943ade5d9e6e83870219bd0c05820460d7de4 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Mon, 18 Jan 2021 15:51:32 -0600 Subject: [PATCH 172/173] add some explanation of output reconfiguration Hopefully this will help "future us" understand what's going on. --- dwl.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/dwl.c b/dwl.c index 7f8b719..5b5e35e 100644 --- a/dwl.c +++ b/dwl.c @@ -1512,6 +1512,12 @@ outputmgrapply(struct wl_listener *listener, void *data) 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; @@ -2275,6 +2281,13 @@ unmapnotify(struct wl_listener *listener, void *data) 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; From a4c0b914f09b40e27df535dc20913d0fddcc2c62 Mon Sep 17 00:00:00 2001 From: "Devin J. Pohly" Date: Tue, 19 Jan 2021 02:44:00 -0600 Subject: [PATCH 173/173] Link patches page in the README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index d3c4a65..2777613 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,8 @@ To enable XWayland, you should also install xorg-xwayland and uncomment its flag All configuration is done by editing `config.h` and recompiling, in the same manner as dwm. There is no way to separately restart the window manager in Wayland without restarting the entire display server, so any changes will take effect the next time dwl is executed. +As in the dwm community, we encourage users to share patches they have created. Check out the [patches page on our wiki](https://github.com/djpohly/dwl/wiki/Patches)! + ## 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.