From 2a0a722c1346d87d47ffbdf7d516bbcd29548dd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 19 Dec 2021 21:22:51 +0100 Subject: [PATCH] bar/wayland: handle layer surface being closed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the output we’re mapped on is disabled (or disconnected), the compositor will unmap us. Up until now, our response was to simply shutdown. Now, we destroy the surface, remove all pending rendering buffers, and all further calls to commit() will return immediately, without doing anything. If the user has configured a specific monitor to use, we wait for that output to come back. When it does, we re-create the layer surface and then we’re up and running again. Bars running on the “default” monitor are handled in a similar way. Since we don’t have an output name from the configuration, we instead store the name of the output we were mapped on, when we’re either unmapped from that output, or that output global is destroyed. As soon as we see that output come back, we re-create the layer surface. --- bar/wayland.c | 245 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 170 insertions(+), 75 deletions(-) diff --git a/bar/wayland.c b/bar/wayland.c index a5a6731..89e5183 100644 --- a/bar/wayland.c +++ b/bar/wayland.c @@ -46,6 +46,7 @@ struct monitor { struct wl_output *output; struct zxdg_output_v1 *xdg; char *name; + uint32_t wl_name; int x; int y; @@ -96,6 +97,7 @@ struct wayland_backend { tll(struct monitor) monitors; const struct monitor *monitor; + char *last_mapped_monitor; int scale; @@ -113,6 +115,7 @@ struct wayland_backend { tll(struct buffer) buffers; /* List of SHM buffers */ struct buffer *next_buffer; /* Bar is rendering to this one */ struct buffer *pending_buffer; /* Finished, but not yet rendered */ + struct wl_callback *frame_callback; double aggregated_scroll; bool have_discrete; @@ -519,6 +522,9 @@ xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output, mon->height_px = height; } +static bool create_surface(struct wayland_backend *backend); +static void destroy_surface(struct wayland_backend *backend); + static void xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output) { @@ -531,13 +537,40 @@ xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output) struct wayland_backend *backend = mon->backend; struct private *bar = backend->bar->private; - if (bar->monitor != NULL && mon->name != NULL && - strcmp(bar->monitor, mon->name) == 0) - { - /* User specified a monitor, and this is one */ - backend->monitor = mon; + const bool is_mapped = backend->monitor != NULL; + if (is_mapped) { + assert(backend->surface != NULL); + assert(backend->last_mapped_monitor == NULL); + return; } + const bool output_is_our_configured_monitor = ( + bar->monitor != NULL && + mon->name != NULL && + strcmp(bar->monitor, mon->name) == 0); + + const bool output_is_last_mapped = ( + backend->last_mapped_monitor != NULL && + mon->name != NULL && + strcmp(backend->last_mapped_monitor, mon->name) == 0); + + if (output_is_our_configured_monitor) + LOG_DBG("%s: using this monitor (user configured)", mon->name); + else if (output_is_last_mapped) + LOG_DBG("%s: using this monitor (last mapped)", mon->name); + + if (output_is_our_configured_monitor || output_is_last_mapped) { + /* User specified a monitor, and this is one */ + backend->monitor = mon; + + free(backend->last_mapped_monitor); + backend->last_mapped_monitor = NULL; + + if (create_surface(backend) && update_size(backend)) { + if (backend->pipe_fds[1] >= 0) + refresh(backend->bar); + } + } } static void @@ -610,6 +643,7 @@ handle_global(void *data, struct wl_registry *registry, tll_push_back(backend->monitors, ((struct monitor){ .backend = backend, + .wl_name = name, .output = output})); struct monitor *mon = &tll_back(backend->monitors); @@ -679,9 +713,23 @@ handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) } } - LOG_WARN("unknown global removed: 0x%08x", name); + tll_foreach(backend->monitors, it) { + struct monitor *mon = &it->item; + if (mon->wl_name == name) { + LOG_INFO("%s disconnected/disabled", mon->name); - /* TODO: need to handle displays and seats */ + if (mon == backend->monitor) { + assert(backend->last_mapped_monitor == NULL); + backend->last_mapped_monitor = strdup(mon->name); + backend->monitor = NULL; + } + + tll_remove(backend->monitors, it); + return; + } + } + + LOG_WARN("unknown global removed: 0x%08x", name); } static const struct wl_registry_listener registry_listener = { @@ -703,22 +751,10 @@ layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface, static void layer_surface_closed(void *data, struct zwlr_layer_surface_v1 *surface) { + LOG_DBG("layer surface closed by compositor"); + struct wayland_backend *backend = data; - - /* - * Called e.g. when an output is disabled. We don't get a - * corresponding event if/when that same output re-appears. So, - * for now, we simply shut down. In the future, we _could_ maybe - * destroy the surface, listen for output events and re-create the - * surface if the same output re-appears. - */ - LOG_WARN("compositor requested surface be closed - shutting down"); - - if (write(backend->bar->abort_fd, &(uint64_t){1}, sizeof(uint64_t)) - != sizeof(uint64_t)) - { - LOG_ERRNO("failed to signal abort to modules"); - } + destroy_surface(backend); } static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { @@ -726,6 +762,82 @@ static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { .closed = &layer_surface_closed, }; +static const struct wl_surface_listener surface_listener; + +static bool +create_surface(struct wayland_backend *backend) +{ + assert(tll_length(backend->monitors) > 0); + assert(backend->surface == NULL); + assert(backend->layer_surface == NULL); + + struct bar *_bar = backend->bar; + struct private *bar = _bar->private; + + backend->surface = wl_compositor_create_surface(backend->compositor); + if (backend->surface == NULL) { + LOG_ERR("failed to create panel surface"); + return false; + } + + wl_surface_add_listener(backend->surface, &surface_listener, backend); + + enum zwlr_layer_shell_v1_layer layer = bar->layer == BAR_LAYER_BOTTOM + ? ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM + : ZWLR_LAYER_SHELL_V1_LAYER_TOP; + + backend->layer_surface = zwlr_layer_shell_v1_get_layer_surface( + backend->layer_shell, backend->surface, + backend->monitor != NULL ? backend->monitor->output : NULL, + layer, "panel"); + + if (backend->layer_surface == NULL) { + LOG_ERR("failed to create layer shell surface"); + return false; + } + + zwlr_layer_surface_v1_add_listener( + backend->layer_surface, &layer_surface_listener, backend); + + /* Aligned to top, maximum width */ + enum zwlr_layer_surface_v1_anchor top_or_bottom = bar->location == BAR_TOP + ? ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP + : ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; + + zwlr_layer_surface_v1_set_anchor( + backend->layer_surface, + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + top_or_bottom); + + return true; +} + +static void +destroy_surface(struct wayland_backend *backend) +{ + if (backend->layer_surface != NULL) + zwlr_layer_surface_v1_destroy(backend->layer_surface); + if (backend->surface != NULL) + wl_surface_destroy(backend->surface); + if (backend->frame_callback != NULL) + wl_callback_destroy(backend->frame_callback); + + if (backend->pending_buffer != NULL) + backend->pending_buffer->busy = false; + if (backend->next_buffer != NULL) + backend->next_buffer->busy = false; + + backend->layer_surface = NULL; + backend->surface = NULL; + backend->frame_callback = NULL; + backend->pending_buffer = NULL; + backend->next_buffer = NULL; + + backend->scale = 0; + backend->render_scheduled = false; +} + static void buffer_release(void *data, struct wl_buffer *wl_buffer) { @@ -892,6 +1004,8 @@ update_size(struct wayland_backend *backend) const struct monitor *mon = backend->monitor; const int scale = mon != NULL ? mon->scale : guess_scale(backend); + assert(backend->surface != NULL); + if (backend->scale == scale) return true; @@ -942,8 +1056,6 @@ update_size(struct wayland_backend *backend) return true; } -static const struct wl_surface_listener surface_listener; - static bool setup(struct bar *_bar) { @@ -989,45 +1101,14 @@ setup(struct bar *_bar) /* Trigger listeners registered in previous roundtrip */ wl_display_roundtrip(backend->display); - backend->surface = wl_compositor_create_surface(backend->compositor); - if (backend->surface == NULL) { - LOG_ERR("failed to create panel surface"); - return false; + if (backend->surface == NULL && backend->layer_surface == NULL) { + if (!create_surface(backend)) + return false; + + if (!update_size(backend)) + return false; } - wl_surface_add_listener(backend->surface, &surface_listener, backend); - - enum zwlr_layer_shell_v1_layer layer = bar->layer == BAR_LAYER_BOTTOM - ? ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM - : ZWLR_LAYER_SHELL_V1_LAYER_TOP; - - backend->layer_surface = zwlr_layer_shell_v1_get_layer_surface( - backend->layer_shell, backend->surface, - backend->monitor != NULL ? backend->monitor->output : NULL, - layer, "panel"); - - if (backend->layer_surface == NULL) { - LOG_ERR("failed to create layer shell surface"); - return false; - } - - zwlr_layer_surface_v1_add_listener( - backend->layer_surface, &layer_surface_listener, backend); - - /* Aligned to top, maximum width */ - enum zwlr_layer_surface_v1_anchor top_or_bottom = bar->location == BAR_TOP - ? ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP - : ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; - - zwlr_layer_surface_v1_set_anchor( - backend->layer_surface, - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | - top_or_bottom); - - if (!update_size(backend)) - return false; - assert(backend->monitor == NULL || backend->width / backend->monitor->scale <= backend->monitor->width_px); @@ -1051,16 +1132,6 @@ cleanup(struct bar *_bar) if (backend->pipe_fds[1] >= 0) close(backend->pipe_fds[1]); - tll_foreach(backend->buffers, it) { - if (it->item.wl_buf != NULL) - wl_buffer_destroy(it->item.wl_buf); - if (it->item.pix != NULL) - pixman_image_unref(it->item.pix); - - munmap(it->item.mmapped, it->item.size); - tll_remove(backend->buffers, it); - } - tll_foreach(backend->monitors, it) { struct monitor *mon = &it->item; free(mon->name); @@ -1071,6 +1142,7 @@ cleanup(struct bar *_bar) wl_output_release(mon->output); tll_remove(backend->monitors, it); } + free(backend->last_mapped_monitor); if (backend->xdg_output_manager != NULL) zxdg_output_manager_v1_destroy(backend->xdg_output_manager); @@ -1079,12 +1151,20 @@ cleanup(struct bar *_bar) seat_destroy(&it->item); tll_free(backend->seats); - if (backend->layer_surface != NULL) - zwlr_layer_surface_v1_destroy(backend->layer_surface); + destroy_surface(backend); + + tll_foreach(backend->buffers, it) { + if (it->item.wl_buf != NULL) + wl_buffer_destroy(it->item.wl_buf); + if (it->item.pix != NULL) + pixman_image_unref(it->item.pix); + + munmap(it->item.mmapped, it->item.size); + tll_remove(backend->buffers, it); + } + if (backend->layer_shell != NULL) zwlr_layer_shell_v1_destroy(backend->layer_shell); - if (backend->surface != NULL) - wl_surface_destroy(backend->surface); if (backend->compositor != NULL) wl_compositor_destroy(backend->compositor); if (backend->shm != NULL) @@ -1209,7 +1289,15 @@ surface_leave(void *data, struct wl_surface *wl_surface, struct wl_output *wl_output) { struct wayland_backend *backend = data; + const struct monitor *mon = backend->monitor; + + assert(mon != NULL); + assert(mon->output == wl_output); + backend->monitor = NULL; + + assert(backend->last_mapped_monitor == NULL); + backend->last_mapped_monitor = mon->name != NULL ? strdup(mon->name) : NULL; } static const struct wl_surface_listener surface_listener = { @@ -1233,7 +1321,9 @@ frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_da backend->render_scheduled = false; + assert(wl_callback == backend->frame_callback); wl_callback_destroy(wl_callback); + backend->frame_callback = NULL; if (backend->pending_buffer != NULL) { struct buffer *buffer = backend->pending_buffer; @@ -1248,6 +1338,7 @@ frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_da wl_surface_commit(backend->surface); wl_display_flush(backend->display); + backend->frame_callback = cb; backend->pending_buffer = NULL; backend->render_scheduled = true; } else @@ -1262,6 +1353,9 @@ commit(const struct bar *_bar) //printf("commit: %dxl%d\n", backend->width, backend->height); + if (backend->next_buffer == NULL) + return; + assert(backend->next_buffer != NULL); assert(backend->next_buffer->busy); @@ -1289,6 +1383,7 @@ commit(const struct bar *_bar) wl_display_flush(backend->display); backend->render_scheduled = true; + backend->frame_callback = cb; } backend->next_buffer = get_buffer(backend);