labwc/src/decorations/xdg-deco.c
Consolatis 9e6aaa689a project wide: clean up event listeners on shutdown
This ensures all event listeners are removed before the emitting
wlroots object is being destroyed. This will be enforced with asserts
in wlroots 0.19 but there is no reason to not do it right now either.

This change in wlroots 0.19 is implemented via commit
8f56f7ca43257cc05c7c4eb57a0f541e05cf9a79
"Assert (almost all) signals have no attached listeners on destroy"
2025-03-13 17:33:51 +09:00

135 lines
4.3 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
#include <wlr/types/wlr_xdg_decoration_v1.h>
#include "common/mem.h"
#include "decorations.h"
#include "labwc.h"
#include "view.h"
struct xdg_deco {
struct wlr_xdg_toplevel_decoration_v1 *wlr_xdg_decoration;
enum wlr_xdg_toplevel_decoration_v1_mode client_mode;
struct view *view;
struct wl_listener destroy;
struct wl_listener request_mode;
struct wl_listener surface_commit;
};
static void
xdg_deco_destroy(struct wl_listener *listener, void *data)
{
struct xdg_deco *xdg_deco = wl_container_of(listener, xdg_deco, destroy);
wl_list_remove(&xdg_deco->destroy.link);
wl_list_remove(&xdg_deco->request_mode.link);
if (xdg_deco->surface_commit.notify) {
wl_list_remove(&xdg_deco->surface_commit.link);
xdg_deco->surface_commit.notify = NULL;
}
free(xdg_deco);
}
static void
handle_surface_commit(struct wl_listener *listener, void *data)
{
struct xdg_deco *xdg_deco = wl_container_of(listener, xdg_deco, surface_commit);
struct wlr_xdg_toplevel_decoration_v1 *deco = xdg_deco->wlr_xdg_decoration;
if (deco->toplevel->base->initial_commit) {
wlr_xdg_toplevel_decoration_v1_set_mode(deco, xdg_deco->client_mode);
wl_list_remove(&xdg_deco->surface_commit.link);
xdg_deco->surface_commit.notify = NULL;
}
}
static void
xdg_deco_request_mode(struct wl_listener *listener, void *data)
{
struct xdg_deco *xdg_deco = wl_container_of(listener, xdg_deco, request_mode);
enum wlr_xdg_toplevel_decoration_v1_mode client_mode =
xdg_deco->wlr_xdg_decoration->requested_mode;
switch (client_mode) {
case WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE:
xdg_deco->view->ssd_preference = LAB_SSD_PREF_SERVER;
break;
case WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE:
xdg_deco->view->ssd_preference = LAB_SSD_PREF_CLIENT;
break;
case WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_NONE:
xdg_deco->view->ssd_preference = LAB_SSD_PREF_UNSPEC;
client_mode = rc.xdg_shell_server_side_deco
? WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE
: WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
break;
default:
wlr_log(WLR_ERROR, "Unspecified xdg decoration variant "
"requested: %u", client_mode);
}
/*
* We may get multiple request_mode calls in an uninitialized state.
* Just update the last requested mode and only add the commit
* handler on the first uninitialized state call.
*/
xdg_deco->client_mode = client_mode;
if (xdg_deco->wlr_xdg_decoration->toplevel->base->initialized) {
wlr_xdg_toplevel_decoration_v1_set_mode(xdg_deco->wlr_xdg_decoration,
client_mode);
} else if (!xdg_deco->surface_commit.notify) {
xdg_deco->surface_commit.notify = handle_surface_commit;
wl_signal_add(
&xdg_deco->wlr_xdg_decoration->toplevel->base->surface->events.commit,
&xdg_deco->surface_commit);
}
if (client_mode == WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE) {
view_set_ssd_mode(xdg_deco->view, LAB_SSD_MODE_FULL);
} else {
view_set_ssd_mode(xdg_deco->view, LAB_SSD_MODE_NONE);
}
}
static void
xdg_toplevel_decoration(struct wl_listener *listener, void *data)
{
struct wlr_xdg_toplevel_decoration_v1 *wlr_xdg_decoration = data;
struct wlr_xdg_surface *xdg_surface = wlr_xdg_decoration->toplevel->base;
if (!xdg_surface || !xdg_surface->data) {
wlr_log(WLR_ERROR,
"Invalid surface supplied for xdg decorations");
return;
}
struct xdg_deco *xdg_deco = znew(*xdg_deco);
xdg_deco->wlr_xdg_decoration = wlr_xdg_decoration;
xdg_deco->view = (struct view *)xdg_surface->data;
wl_signal_add(&wlr_xdg_decoration->events.destroy, &xdg_deco->destroy);
xdg_deco->destroy.notify = xdg_deco_destroy;
wl_signal_add(&wlr_xdg_decoration->events.request_mode,
&xdg_deco->request_mode);
xdg_deco->request_mode.notify = xdg_deco_request_mode;
xdg_deco_request_mode(&xdg_deco->request_mode, wlr_xdg_decoration);
}
void
xdg_server_decoration_init(struct server *server)
{
struct wlr_xdg_decoration_manager_v1 *xdg_deco_mgr = NULL;
xdg_deco_mgr = wlr_xdg_decoration_manager_v1_create(server->wl_display);
if (!xdg_deco_mgr) {
wlr_log(WLR_ERROR, "unable to create the XDG deco manager");
exit(EXIT_FAILURE);
}
wl_signal_add(&xdg_deco_mgr->events.new_toplevel_decoration,
&server->xdg_toplevel_decoration);
server->xdg_toplevel_decoration.notify = xdg_toplevel_decoration;
}
void
xdg_server_decoration_finish(struct server *server)
{
wl_list_remove(&server->xdg_toplevel_decoration.link);
}