diff --git a/src/xdg-popup.c b/src/xdg-popup.c index 7a466536..df500a8a 100644 --- a/src/xdg-popup.c +++ b/src/xdg-popup.c @@ -4,11 +4,24 @@ * * This file is only needed in support of * - unconstraining XDG popups + * - keeping non-layer-shell xdg-popups outside the layers.c code */ #include "labwc.h" -/* TODO: surely this ought to just move to xdg.c now??? */ +struct view_child { + struct wlr_surface *surface; + struct view *parent; +}; + +struct xdg_popup { + struct view_child view_child; + struct wlr_xdg_popup *wlr_popup; + + struct wl_listener destroy; + struct wl_listener new_popup; +}; + static void popup_unconstrain(struct view *view, struct wlr_xdg_popup *popup) { @@ -30,8 +43,56 @@ popup_unconstrain(struct view *view, struct wlr_xdg_popup *popup) wlr_xdg_popup_unconstrain_from_box(popup, &output_toplevel_box); } +static void +handle_xdg_popup_destroy(struct wl_listener *listener, void *data) +{ + struct xdg_popup *popup = wl_container_of(listener, popup, destroy); + wl_list_remove(&popup->destroy.link); + wl_list_remove(&popup->new_popup.link); + free(popup); +} + +static void +popup_handle_new_xdg_popup(struct wl_listener *listener, void *data) +{ + struct xdg_popup *popup = wl_container_of(listener, popup, new_popup); + struct wlr_xdg_popup *wlr_popup = data; + xdg_popup_create(popup->view_child.parent, wlr_popup); +} + void xdg_popup_create(struct view *view, struct wlr_xdg_popup *wlr_popup) { + struct xdg_popup *popup = calloc(1, sizeof(struct xdg_popup)); + if (!popup) { + return; + } + + popup->wlr_popup = wlr_popup; + popup->view_child.parent = view; + popup->view_child.surface = wlr_popup->base->surface; + + popup->destroy.notify = handle_xdg_popup_destroy; + wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); + popup->new_popup.notify = popup_handle_new_xdg_popup; + wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); + + /* + * We must add xdg popups to the scene graph so they get rendered. The + * wlroots scene graph provides a helper for this, but to use it we must + * provide the proper parent scene node of the xdg popup. To enable + * this, we always set the user data field of xdg_surfaces to the + * corresponding scene node. + */ + if (!wlr_surface_is_xdg_surface(wlr_popup->parent)) { + wlr_log(WLR_ERROR, "xdg_surface is not xdg"); + return; + } + struct wlr_xdg_surface *parent = + wlr_xdg_surface_from_wlr_surface(wlr_popup->parent); + struct wlr_scene_node *parent_node = parent->surface->data; + wlr_popup->base->surface->data = + wlr_scene_xdg_surface_create(parent_node, wlr_popup->base); + popup_unconstrain(view, wlr_popup); } diff --git a/src/xdg.c b/src/xdg.c index a820eacf..c3a8b0af 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -372,24 +372,13 @@ xdg_surface_new(struct wl_listener *listener, void *data) struct wlr_xdg_surface *xdg_surface = data; /* - * We must add xdg popups to the scene graph so they get rendered. The - * wlroots scene graph provides a helper for this, but to use it we must - * provide the proper parent scene node of the xdg popup. To enable - * this, we always set the user data field of xdg_surfaces to the - * corresponding scene node. + * We deal with popups in xdg-popup.c and layers.c as they have to be + * treated differently */ - if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { - struct wlr_xdg_surface *parent = - wlr_xdg_surface_from_wlr_surface( - xdg_surface->popup->parent); - struct wlr_scene_node *parent_node = parent->surface->data; - xdg_surface->surface->data = - wlr_scene_xdg_surface_create(parent_node, xdg_surface); - /* TODO: unconstrain here rather than in xdg-popup.c? */ + if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) { return; } - /* WLR_XDG_SURFACE_ROLE_TOPLEVEL */ wlr_xdg_surface_ping(xdg_surface); struct view *view = calloc(1, sizeof(struct view));