diff --git a/include/wlr/types/wlr_layer_shell.h b/include/wlr/types/wlr_layer_shell.h index 79b3a4ea..4b3e4e1c 100644 --- a/include/wlr/types/wlr_layer_shell.h +++ b/include/wlr/types/wlr_layer_shell.h @@ -59,6 +59,7 @@ struct wlr_layer_surface { struct wlr_output *output; struct wl_resource *resource; struct wlr_layer_shell *shell; + struct wl_list popups; // wlr_xdg_popup::link const char *namespace; enum zwlr_layer_shell_v1_layer layer; @@ -81,6 +82,7 @@ struct wlr_layer_surface { struct wl_signal destroy; struct wl_signal map; struct wl_signal unmap; + struct wl_signal new_popup; } events; void *data; diff --git a/include/wlr/types/wlr_xdg_shell.h b/include/wlr/types/wlr_xdg_shell.h index 5046339a..cf96df4a 100644 --- a/include/wlr/types/wlr_xdg_shell.h +++ b/include/wlr/types/wlr_xdg_shell.h @@ -32,15 +32,18 @@ struct wlr_xdg_client { struct wl_event_source *ping_timer; }; +struct wlr_xdg_positioner; + struct wlr_xdg_popup { struct wlr_xdg_surface *base; struct wl_list link; struct wl_resource *resource; bool committed; - struct wlr_xdg_surface *parent; + struct wlr_surface *parent; struct wlr_seat *seat; + struct wlr_xdg_positioner *positioner; // Position of the popup relative to the upper left corner of the window // geometry of the parent surface struct wlr_box geometry; @@ -178,6 +181,11 @@ struct wlr_xdg_toplevel_show_window_menu_event { struct wlr_xdg_shell *wlr_xdg_shell_create(struct wl_display *display); void wlr_xdg_shell_destroy(struct wlr_xdg_shell *xdg_shell); +struct wlr_xdg_surface *wlr_xdg_surface_from_resource( + struct wl_resource *resource); + +struct wlr_box wlr_xdg_popup_get_geometry(struct wlr_xdg_popup *popup); + /** * Send a ping to the surface. If the surface does not respond in a reasonable * amount of time, the ping_timeout event will be emitted. @@ -226,6 +234,7 @@ void wlr_xdg_surface_send_close(struct wlr_xdg_surface *surface); /** * Compute the popup position in its parent's surface-local coordinate system. + * This aborts if called for popups whose parent is not an xdg_surface. */ void wlr_xdg_surface_popup_get_position(struct wlr_xdg_surface *surface, double *popup_sx, double *popup_sy); diff --git a/types/wlr_layer_shell.c b/types/wlr_layer_shell.c index a567b8bc..7e603118 100644 --- a/types/wlr_layer_shell.c +++ b/types/wlr_layer_shell.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include "util/signal.h" #include "wlr-layer-shell-unstable-v1-protocol.h" @@ -131,8 +132,19 @@ static void layer_surface_handle_set_keyboard_interactivity( } static void layer_surface_handle_get_popup(struct wl_client *client, - struct wl_resource *resource, struct wl_resource *popup) { - // TODO + struct wl_resource *layer_resource, + struct wl_resource *popup_resource) { + struct wlr_layer_surface *parent = + layer_surface_from_resource(layer_resource); + struct wlr_xdg_surface *popup_surface = + wlr_xdg_surface_from_resource(popup_resource); + + assert(popup_surface->role == WLR_XDG_SURFACE_ROLE_POPUP); + struct wlr_xdg_popup *popup = popup_surface->popup; + popup->parent = parent->surface; + popup->geometry = wlr_xdg_popup_get_geometry(popup); + wl_list_insert(&parent->popups, &popup->link); + wlr_signal_emit_safe(&parent->events.new_popup, popup); } static const struct zwlr_layer_surface_v1_interface layer_surface_implementation = { @@ -343,6 +355,7 @@ static void layer_shell_handle_get_layer_surface(struct wl_client *wl_client, } wl_list_init(&surface->configure_list); + wl_list_init(&surface->popups); wl_signal_init(&surface->events.destroy); wl_signal_add(&surface->surface->events.destroy, @@ -350,6 +363,7 @@ static void layer_shell_handle_get_layer_surface(struct wl_client *wl_client, surface->surface_destroy_listener.notify = handle_wlr_surface_destroyed; wl_signal_init(&surface->events.map); wl_signal_init(&surface->events.unmap); + wl_signal_init(&surface->events.new_popup); wlr_surface_set_role_committed(surface->surface, handle_wlr_surface_committed, surface); diff --git a/types/wlr_xdg_shell.c b/types/wlr_xdg_shell.c index 8d424f3b..080d57a4 100644 --- a/types/wlr_xdg_shell.c +++ b/types/wlr_xdg_shell.c @@ -51,16 +51,6 @@ static void resource_handle_destroy(struct wl_client *client, wl_resource_destroy(resource); } -static struct wlr_xdg_surface *xdg_popup_grab_get_topmost( - struct wlr_xdg_popup_grab *grab) { - struct wlr_xdg_popup *popup; - wl_list_for_each(popup, &grab->popups, grab_link) { - return popup->base; - } - - return NULL; -} - static void xdg_pointer_grab_end(struct wlr_seat_pointer_grab *grab) { struct wlr_xdg_popup_grab *popup_grab = grab->data; @@ -447,9 +437,9 @@ static void xdg_shell_handle_create_positioner(struct wl_client *wl_client, positioner, xdg_positioner_destroy); } -static struct wlr_box xdg_positioner_get_geometry( - struct wlr_xdg_positioner *positioner, - struct wlr_xdg_surface *surface, struct wlr_xdg_surface *parent) { +struct wlr_box wlr_xdg_popup_get_geometry(struct wlr_xdg_popup *popup) { + assert(popup && popup->positioner); + struct wlr_xdg_positioner *positioner = popup->positioner; struct wlr_box geometry = { .x = positioner->offset.x, .y = positioner->offset.y, @@ -563,12 +553,7 @@ static void xdg_popup_handle_grab(struct wl_client *client, xdg_shell_popup_grab_from_seat(surface->client->shell, seat_client->seat); - struct wlr_xdg_surface *topmost = xdg_popup_grab_get_topmost(popup_grab); - bool parent_is_toplevel = - surface->popup->parent->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL; - - if ((topmost == NULL && !parent_is_toplevel) || - (topmost != NULL && topmost != surface->popup->parent)) { + if (!wl_list_empty(&surface->popups)) { wl_resource_post_error(surface->client->resource, XDG_WM_BASE_ERROR_NOT_THE_TOPMOST_POPUP, "xdg_popup was not created on the topmost popup"); @@ -616,8 +601,12 @@ static void xdg_popup_resource_destroy(struct wl_resource *resource) { static const struct xdg_surface_interface xdg_surface_implementation; -static struct wlr_xdg_surface *xdg_surface_from_resource( +struct wlr_xdg_surface *wlr_xdg_surface_from_resource( struct wl_resource *resource) { + // TODO: Double check that all of the callers can deal with NULL + if (!resource) { + return NULL; + } assert(wl_resource_instance_of(resource, &xdg_surface_interface, &xdg_surface_implementation)); return wl_resource_get_user_data(resource); @@ -628,9 +617,9 @@ static void xdg_surface_handle_get_popup(struct wl_client *client, struct wl_resource *parent_resource, struct wl_resource *positioner_resource) { struct wlr_xdg_surface *surface = - xdg_surface_from_resource(resource); + wlr_xdg_surface_from_resource(resource); struct wlr_xdg_surface *parent = - xdg_surface_from_resource(parent_resource); + wlr_xdg_surface_from_resource(parent_resource); struct wlr_xdg_positioner *positioner = xdg_positioner_from_resource(positioner_resource); @@ -663,16 +652,18 @@ static void xdg_surface_handle_get_popup(struct wl_client *client, surface->role = WLR_XDG_SURFACE_ROLE_POPUP; surface->popup->base = surface; - surface->popup->parent = parent; - surface->popup->geometry = - xdg_positioner_get_geometry(positioner, surface, parent); - wl_list_insert(&parent->popups, &surface->popup->link); + surface->popup->positioner = positioner; wl_resource_set_implementation(surface->popup->resource, &xdg_popup_implementation, surface, xdg_popup_resource_destroy); - wlr_signal_emit_safe(&parent->events.new_popup, surface->popup); + if (parent) { + surface->popup->parent = parent->surface; + surface->popup->geometry = wlr_xdg_popup_get_geometry(surface->popup); + wl_list_insert(&parent->popups, &surface->popup->link); + wlr_signal_emit_safe(&parent->events.new_popup, surface->popup); + } } @@ -913,7 +904,7 @@ static const struct xdg_toplevel_interface xdg_toplevel_implementation = { static void xdg_surface_resource_destroy(struct wl_resource *resource) { struct wlr_xdg_surface *surface = - xdg_surface_from_resource(resource); + wlr_xdg_surface_from_resource(resource); if (surface != NULL) { xdg_surface_destroy(surface); } @@ -929,7 +920,7 @@ static void xdg_toplevel_resource_destroy(struct wl_resource *resource) { static void xdg_surface_handle_get_toplevel(struct wl_client *client, struct wl_resource *resource, uint32_t id) { - struct wlr_xdg_surface *surface = xdg_surface_from_resource(resource); + struct wlr_xdg_surface *surface = wlr_xdg_surface_from_resource(resource); if (wlr_surface_set_role(surface->surface, wlr_desktop_xdg_toplevel_role, resource, XDG_WM_BASE_ERROR_ROLE)) { @@ -984,7 +975,7 @@ static void wlr_xdg_toplevel_ack_configure( static void xdg_surface_handle_ack_configure(struct wl_client *client, struct wl_resource *resource, uint32_t serial) { - struct wlr_xdg_surface *surface = xdg_surface_from_resource(resource); + struct wlr_xdg_surface *surface = wlr_xdg_surface_from_resource(resource); if (surface->role == WLR_XDG_SURFACE_ROLE_NONE) { wl_resource_post_error(surface->resource, @@ -1032,7 +1023,7 @@ static void xdg_surface_handle_ack_configure(struct wl_client *client, static void xdg_surface_handle_set_window_geometry(struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { - struct wlr_xdg_surface *surface = xdg_surface_from_resource(resource); + struct wlr_xdg_surface *surface = wlr_xdg_surface_from_resource(resource); if (surface->role == WLR_XDG_SURFACE_ROLE_NONE) { wl_resource_post_error(surface->resource, @@ -1050,7 +1041,7 @@ static void xdg_surface_handle_set_window_geometry(struct wl_client *client, static void xdg_surface_handle_destroy(struct wl_client *client, struct wl_resource *resource) { - struct wlr_xdg_surface *surface = xdg_surface_from_resource(resource); + struct wlr_xdg_surface *surface = wlr_xdg_surface_from_resource(resource); if (surface->role != WLR_XDG_SURFACE_ROLE_NONE) { wlr_log(L_ERROR, "Tried to destroy an xdg_surface before its role " @@ -1627,11 +1618,12 @@ void wlr_xdg_surface_send_close(struct wlr_xdg_surface *surface) { void wlr_xdg_surface_popup_get_position(struct wlr_xdg_surface *surface, double *popup_sx, double *popup_sy) { assert(surface->role == WLR_XDG_SURFACE_ROLE_POPUP); - struct wlr_xdg_surface *parent = surface->popup->parent; - *popup_sx = parent->geometry.x + surface->popup->geometry.x - - surface->geometry.x; - *popup_sy = parent->geometry.y + surface->popup->geometry.y - - surface->geometry.y; + struct wlr_xdg_popup *popup = surface->popup; + assert(strcmp(popup->parent->role, wlr_desktop_xdg_toplevel_role) == 0 + || strcmp(popup->parent->role, wlr_desktop_xdg_popup_role) == 0); + struct wlr_xdg_surface *parent = popup->parent->role_data; + *popup_sx = parent->geometry.x + popup->geometry.x - surface->geometry.x; + *popup_sy = parent->geometry.y + popup->geometry.y - surface->geometry.y; } struct wlr_surface *wlr_xdg_surface_surface_at(