diff --git a/include/wlr/types/wlr_surface.h b/include/wlr/types/wlr_surface.h index d1127c86..0fa7e9c2 100644 --- a/include/wlr/types/wlr_surface.h +++ b/include/wlr/types/wlr_surface.h @@ -159,6 +159,14 @@ void wlr_surface_send_leave(struct wlr_surface *surface, void wlr_surface_send_frame_done(struct wlr_surface *surface, const struct timespec *when); +struct wlr_box; +/** + * Get the bounding box that contains the surface and all subsurfaces in + * surface coordinates. + * X and y may be negative, if there are subsurfaces with negative position. + */ +void wlr_surface_get_extends(struct wlr_surface *surface, struct wlr_box *box); + /** * Set a callback for surface commit that runs before all the other callbacks. * This is intended for use by the surface role. diff --git a/include/wlr/types/wlr_xdg_shell.h b/include/wlr/types/wlr_xdg_shell.h index 5eb30a16..6a967bc7 100644 --- a/include/wlr/types/wlr_xdg_shell.h +++ b/include/wlr/types/wlr_xdg_shell.h @@ -341,6 +341,15 @@ bool wlr_surface_is_xdg_surface(struct wlr_surface *surface); struct wlr_xdg_surface *wlr_xdg_surface_from_wlr_surface( struct wlr_surface *surface); +/** + * Get the surface geometry. + * This is either the geometry as set by the client, or defaulted to the bounds + * of the surface + the subsurfaces (as specified by the protocol). + * + * The x and y value can be <0 + */ +void wlr_xdg_surface_get_geometry(struct wlr_xdg_surface *surface, struct wlr_box *box); + /** * Call `iterator` on each surface in the xdg-surface tree, with the surface's * position relative to the root xdg-surface. The function is called from root to diff --git a/include/wlr/types/wlr_xdg_shell_v6.h b/include/wlr/types/wlr_xdg_shell_v6.h index 2fdf49e5..07c831ce 100644 --- a/include/wlr/types/wlr_xdg_shell_v6.h +++ b/include/wlr/types/wlr_xdg_shell_v6.h @@ -318,6 +318,15 @@ bool wlr_surface_is_xdg_surface_v6(struct wlr_surface *surface); struct wlr_xdg_surface_v6 *wlr_xdg_surface_v6_from_wlr_surface( struct wlr_surface *surface); +/** + * Get the surface geometry. + * This is either the geometry as set by the client, or defaulted to the bounds + * of the surface + the subsurfaces (as specified by the protocol). + * + * The x and y value can be <0 + */ +void wlr_xdg_surface_v6_get_geometry(struct wlr_xdg_surface_v6 *surface, struct wlr_box *box); + /** * Call `iterator` on each surface in the xdg-surface tree, with the surface's * position relative to the root xdg-surface. The function is called from root to diff --git a/rootston/xdg_shell.c b/rootston/xdg_shell.c index 03ae1dc6..805fb874 100644 --- a/rootston/xdg_shell.c +++ b/rootston/xdg_shell.c @@ -132,14 +132,10 @@ static void get_size(const struct roots_view *view, struct wlr_box *box) { assert(view->type == ROOTS_XDG_SHELL_VIEW); struct wlr_xdg_surface *surface = view->xdg_surface; - if (surface->geometry.width > 0 && surface->geometry.height > 0) { - box->width = surface->geometry.width; - box->height = surface->geometry.height; - } else { - assert(surface->surface); - box->width = surface->surface->current->width; - box->height = surface->surface->current->height; - } + struct wlr_box geo_box; + wlr_xdg_surface_get_geometry(surface, &geo_box); + box->width = geo_box.width; + box->height = geo_box.height; } static void activate(struct roots_view *view, bool active) { diff --git a/rootston/xdg_shell_v6.c b/rootston/xdg_shell_v6.c index 90b11690..02ad867d 100644 --- a/rootston/xdg_shell_v6.c +++ b/rootston/xdg_shell_v6.c @@ -133,14 +133,10 @@ static void get_size(const struct roots_view *view, struct wlr_box *box) { assert(view->type == ROOTS_XDG_SHELL_V6_VIEW); struct wlr_xdg_surface_v6 *surface = view->xdg_surface_v6; - if (surface->geometry.width > 0 && surface->geometry.height > 0) { - box->width = surface->geometry.width; - box->height = surface->geometry.height; - } else { - assert(surface->surface); - box->width = surface->surface->current->width; - box->height = surface->surface->current->height; - } + struct wlr_box geo_box; + wlr_xdg_surface_v6_get_geometry(surface, &geo_box); + box->width = geo_box.width; + box->height = geo_box.height; } static void activate(struct roots_view *view, bool active) { diff --git a/types/wlr_surface.c b/types/wlr_surface.c index 61284416..6df66f2c 100644 --- a/types/wlr_surface.c +++ b/types/wlr_surface.c @@ -16,6 +16,22 @@ #define SURFACE_VERSION 4 #define SUBSURFACE_VERSION 1 +static int min(int fst, int snd) { + if (fst < snd) { + return fst; + } else { + return snd; + } +} + +static int max(int fst, int snd) { + if (fst > snd) { + return fst; + } else { + return snd; + } +} + static void surface_state_reset_buffer(struct wlr_surface_state *state) { if (state->buffer) { wl_list_remove(&state->buffer_destroy_listener.link); @@ -1036,3 +1052,35 @@ void wlr_surface_for_each_surface(struct wlr_surface *surface, wlr_surface_iterator_func_t iterator, void *user_data) { surface_for_each_surface(surface, 0, 0, iterator, user_data); } + +struct bound_acc { + int32_t min_x, min_y; + int32_t max_x, max_y; +}; + +static void handle_bounding_box_surface(struct wlr_surface *surface, + int x, int y, void *data) { + struct bound_acc *acc = data; + + acc->min_x = min(x, acc->min_x); + acc->min_y = min(x, acc->min_y); + + acc->max_x = max(x + surface->current->width, acc->max_x); + acc->max_y = max(y + surface->current->height, acc->max_y); +} + +void wlr_surface_get_extends(struct wlr_surface *surface, struct wlr_box *box) { + struct bound_acc acc = { + .min_x = 0, + .min_y = 0, + .max_x = surface->current->width, + .max_y = surface->current->height, + }; + + wlr_surface_for_each_surface(surface, handle_bounding_box_surface, &acc); + + box->x = acc.min_x; + box->y = acc.min_y; + box->width = acc.max_x - acc.min_x; + box->height = acc.max_y - acc.min_y; +} diff --git a/types/xdg_shell/wlr_xdg_surface.c b/types/xdg_shell/wlr_xdg_surface.c index 6f0e7264..c5d177b2 100644 --- a/types/xdg_shell/wlr_xdg_surface.c +++ b/types/xdg_shell/wlr_xdg_surface.c @@ -251,6 +251,13 @@ static void xdg_surface_handle_set_window_geometry(struct wl_client *client, return; } + if (width <= 0 || height <= 0) { + wlr_log(L_ERROR, "Client tried to set invalid geometry"); + //XXX: Switch to the proper error value once available + wl_resource_post_error(resource, -1, "Tried to set invalid xdg-surface geometry"); + return; + } + surface->has_next_geometry = true; surface->next_geometry.height = height; surface->next_geometry.width = width; @@ -474,9 +481,11 @@ static void xdg_popup_get_position(struct wlr_xdg_popup *popup, double *popup_sx, double *popup_sy) { struct wlr_xdg_surface *parent = wlr_xdg_surface_from_wlr_surface(popup->parent); - *popup_sx = parent->geometry.x + popup->geometry.x - + struct wlr_box parent_geo; + wlr_xdg_surface_get_geometry(parent, &parent_geo); + *popup_sx = parent_geo.x + popup->geometry.x - popup->base->geometry.x; - *popup_sy = parent->geometry.y + popup->geometry.y - + *popup_sy = parent_geo.y + popup->geometry.y - popup->base->geometry.y; } @@ -546,3 +555,13 @@ void wlr_xdg_surface_for_each_surface(struct wlr_xdg_surface *surface, wlr_surface_iterator_func_t iterator, void *user_data) { xdg_surface_for_each_surface(surface, 0, 0, iterator, user_data); } + +void wlr_xdg_surface_get_geometry(struct wlr_xdg_surface *surface, struct wlr_box *box) { + wlr_surface_get_extends(surface->surface, box); + /* The client never set the geometry */ + if (!surface->geometry.width) { + return; + } + + wlr_box_intersection(&surface->geometry, box, box); +} diff --git a/types/xdg_shell_v6/wlr_xdg_surface_v6.c b/types/xdg_shell_v6/wlr_xdg_surface_v6.c index a1214a0a..e111adad 100644 --- a/types/xdg_shell_v6/wlr_xdg_surface_v6.c +++ b/types/xdg_shell_v6/wlr_xdg_surface_v6.c @@ -198,6 +198,12 @@ static void xdg_surface_handle_set_window_geometry(struct wl_client *client, return; } + if (width <= 0 || height <= 0) { + wlr_log(L_ERROR, "Client tried to set invalid geometry"); + wl_resource_post_error(resource, -1, "Tried to set invalid xdg-surface geometry"); + } + + surface->has_next_geometry = true; surface->next_geometry.height = height; surface->next_geometry.width = width; @@ -454,9 +460,11 @@ struct wlr_xdg_surface_v6 *create_xdg_surface_v6( static void xdg_popup_v6_get_position(struct wlr_xdg_popup_v6 *popup, double *popup_sx, double *popup_sy) { struct wlr_xdg_surface_v6 *parent = popup->parent; - *popup_sx = parent->geometry.x + popup->geometry.x - + struct wlr_box parent_geo; + wlr_xdg_surface_v6_get_geometry(parent, &parent_geo); + *popup_sx = parent_geo.x + popup->geometry.x - popup->base->geometry.x; - *popup_sy = parent->geometry.y + popup->geometry.y - + *popup_sy = parent_geo.y + popup->geometry.y - popup->base->geometry.y; } @@ -526,3 +534,13 @@ void wlr_xdg_surface_v6_for_each_surface(struct wlr_xdg_surface_v6 *surface, wlr_surface_iterator_func_t iterator, void *user_data) { xdg_surface_v6_for_each_surface(surface, 0, 0, iterator, user_data); } + +void wlr_xdg_surface_v6_get_geometry(struct wlr_xdg_surface_v6 *surface, struct wlr_box *box) { + wlr_surface_get_extends(surface->surface, box); + /* The client never set the geometry */ + if (!surface->geometry.width) { + return; + } + + wlr_box_intersection(&surface->geometry, box, box); +}