From 511f137f8fb245e4877d83a0846294091373eba1 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Mon, 7 Feb 2022 14:22:48 +0300 Subject: [PATCH] xdg-positioner: rewrite unconstraining, untie from xdg-popup --- include/wlr/types/wlr_xdg_shell.h | 25 +-- types/xdg_shell/wlr_xdg_popup.c | 213 ++------------------ types/xdg_shell/wlr_xdg_positioner.c | 286 +++++++++++++++++++++++---- 3 files changed, 261 insertions(+), 263 deletions(-) diff --git a/include/wlr/types/wlr_xdg_shell.h b/include/wlr/types/wlr_xdg_shell.h index 72cd6f27..48bfbb97 100644 --- a/include/wlr/types/wlr_xdg_shell.h +++ b/include/wlr/types/wlr_xdg_shell.h @@ -363,13 +363,14 @@ void wlr_xdg_popup_get_position(struct wlr_xdg_popup *popup, * Get the geometry based on positioner rules. */ void wlr_xdg_positioner_rules_get_geometry( - struct wlr_xdg_positioner_rules *rules, struct wlr_box *box); + const struct wlr_xdg_positioner_rules *rules, struct wlr_box *box); /** - * Get the anchor point for this popup in the toplevel parent's coordinate system. + * Unconstrain the box from the constraint area according to positioner rules. */ -void wlr_xdg_popup_get_anchor_point(struct wlr_xdg_popup *popup, - int *toplevel_sx, int *toplevel_sy); +void wlr_xdg_positioner_rules_unconstrain_box( + const struct wlr_xdg_positioner_rules *rules, + const struct wlr_box *constraint, struct wlr_box *box); /** * Convert the given coordinates in the popup coordinate system to the toplevel @@ -384,21 +385,7 @@ void wlr_xdg_popup_get_toplevel_coords(struct wlr_xdg_popup *popup, * surface coordinate system. */ void wlr_xdg_popup_unconstrain_from_box(struct wlr_xdg_popup *popup, - const struct wlr_box *toplevel_sx_box); - -/** - Invert the top/bottom anchor and gravity for those positioner rules. - This can be used to "flip" the positioner around the anchor rect in - the x direction. - */ -void wlr_xdg_positioner_rules_invert_x(struct wlr_xdg_positioner_rules *rules); - -/** - Invert the top/bottom anchor and gravity for those positioner rules. - This can be used to "flip" the positioner around the anchor rect in - the y direction. - */ -void wlr_xdg_positioner_rules_invert_y(struct wlr_xdg_positioner_rules *rules); + const struct wlr_box *toplevel_space_box); /** * Find a surface within this xdg-surface tree at the given surface-local diff --git a/types/xdg_shell/wlr_xdg_popup.c b/types/xdg_shell/wlr_xdg_popup.c index e1e9653a..973a339b 100644 --- a/types/xdg_shell/wlr_xdg_popup.c +++ b/types/xdg_shell/wlr_xdg_popup.c @@ -409,45 +409,6 @@ void wlr_xdg_popup_destroy(struct wlr_xdg_popup *popup) { reset_xdg_surface(popup->base); } -void wlr_xdg_popup_get_anchor_point(struct wlr_xdg_popup *popup, - int *root_sx, int *root_sy) { - struct wlr_box rect = popup->positioner_rules.anchor_rect; - enum xdg_positioner_anchor anchor = popup->positioner_rules.anchor; - int sx = 0, sy = 0; - - if (anchor == XDG_POSITIONER_ANCHOR_NONE) { - sx = (rect.x + rect.width) / 2; - sy = (rect.y + rect.height) / 2; - } else if (anchor == XDG_POSITIONER_ANCHOR_TOP) { - sx = (rect.x + rect.width) / 2; - sy = rect.y; - } else if (anchor == XDG_POSITIONER_ANCHOR_BOTTOM) { - sx = (rect.x + rect.width) / 2; - sy = rect.y + rect.height; - } else if (anchor == XDG_POSITIONER_ANCHOR_LEFT) { - sx = rect.x; - sy = (rect.y + rect.height) / 2; - } else if (anchor == XDG_POSITIONER_ANCHOR_RIGHT) { - sx = rect.x + rect.width; - sy = (rect.y + rect.height) / 2; - } else if (anchor == XDG_POSITIONER_ANCHOR_TOP_LEFT) { - sx = rect.x; - sy = rect.y; - } else if (anchor == XDG_POSITIONER_ANCHOR_TOP_RIGHT) { - sx = rect.x + rect.width; - sy = rect.y; - } else if (anchor == XDG_POSITIONER_ANCHOR_BOTTOM_LEFT) { - sx = rect.x; - sy = rect.y + rect.height; - } else if (anchor == XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT) { - sx = rect.x + rect.width; - sy = rect.y + rect.height; - } - - *root_sx = sx; - *root_sy = sy; -} - void wlr_xdg_popup_get_toplevel_coords(struct wlr_xdg_popup *popup, int popup_sx, int popup_sy, int *toplevel_sx, int *toplevel_sy) { struct wlr_surface *parent = popup->parent; @@ -471,167 +432,17 @@ void wlr_xdg_popup_get_toplevel_coords(struct wlr_xdg_popup *popup, *toplevel_sy = popup_sy; } -static void xdg_popup_box_constraints(struct wlr_xdg_popup *popup, - const struct wlr_box *toplevel_sx_box, int *offset_x, int *offset_y) { - int popup_width = popup->geometry.width; - int popup_height = popup->geometry.height; - int anchor_sx = 0, anchor_sy = 0; - wlr_xdg_popup_get_anchor_point(popup, &anchor_sx, &anchor_sy); - int popup_sx = 0, popup_sy = 0; - wlr_xdg_popup_get_toplevel_coords(popup, popup->geometry.x, - popup->geometry.y, &popup_sx, &popup_sy); - *offset_x = 0, *offset_y = 0; - - if (popup_sx < toplevel_sx_box->x) { - *offset_x = toplevel_sx_box->x - popup_sx; - } else if (popup_sx + popup_width > - toplevel_sx_box->x + toplevel_sx_box->width) { - *offset_x = toplevel_sx_box->x + toplevel_sx_box->width - - (popup_sx + popup_width); - } - - if (popup_sy < toplevel_sx_box->y) { - *offset_y = toplevel_sx_box->y - popup_sy; - } else if (popup_sy + popup_height > - toplevel_sx_box->y + toplevel_sx_box->height) { - *offset_y = toplevel_sx_box->y + toplevel_sx_box->height - - (popup_sy + popup_height); - } -} - -static bool xdg_popup_unconstrain_flip(struct wlr_xdg_popup *popup, - const struct wlr_box *toplevel_sx_box) { - int offset_x = 0, offset_y = 0; - xdg_popup_box_constraints(popup, toplevel_sx_box, - &offset_x, &offset_y); - - if (!offset_x && !offset_y) { - return true; - } - - bool flip_x = offset_x && - (popup->positioner_rules.constraint_adjustment & - XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X); - - bool flip_y = offset_y && - (popup->positioner_rules.constraint_adjustment & - XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y); - - if (flip_x) { - wlr_xdg_positioner_rules_invert_x(&popup->positioner_rules); - } - if (flip_y) { - wlr_xdg_positioner_rules_invert_y(&popup->positioner_rules); - } - - wlr_xdg_positioner_rules_get_geometry( - &popup->positioner_rules, &popup->geometry); - - xdg_popup_box_constraints(popup, toplevel_sx_box, - &offset_x, &offset_y); - - if (!offset_x && !offset_y) { - // no longer constrained - return true; - } - - // revert the positioner back if it didn't fix it and go to the next part - if (offset_x && flip_x) { - wlr_xdg_positioner_rules_invert_x(&popup->positioner_rules); - } - if (offset_y && flip_y) { - wlr_xdg_positioner_rules_invert_y(&popup->positioner_rules); - } - - wlr_xdg_positioner_rules_get_geometry( - &popup->positioner_rules, &popup->geometry); - - return false; -} - -static bool xdg_popup_unconstrain_slide(struct wlr_xdg_popup *popup, - const struct wlr_box *toplevel_sx_box) { - int offset_x = 0, offset_y = 0; - xdg_popup_box_constraints(popup, toplevel_sx_box, - &offset_x, &offset_y); - - if (!offset_x && !offset_y) { - return true; - } - - bool slide_x = offset_x && - (popup->positioner_rules.constraint_adjustment & - XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X); - - bool slide_y = offset_y && - (popup->positioner_rules.constraint_adjustment & - XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y); - - if (slide_x) { - popup->geometry.x += offset_x; - } - - if (slide_y) { - popup->geometry.y += offset_y; - } - - int toplevel_x = 0, toplevel_y = 0; - wlr_xdg_popup_get_toplevel_coords(popup, popup->geometry.x, - popup->geometry.y, &toplevel_x, &toplevel_y); - - if (slide_x && toplevel_x < toplevel_sx_box->x) { - popup->geometry.x += toplevel_sx_box->x - toplevel_x; - } - if (slide_y && toplevel_y < toplevel_sx_box->y) { - popup->geometry.y += toplevel_sx_box->y - toplevel_y; - } - - xdg_popup_box_constraints(popup, toplevel_sx_box, - &offset_x, &offset_y); - - return !offset_x && !offset_y; -} - -static bool xdg_popup_unconstrain_resize(struct wlr_xdg_popup *popup, - const struct wlr_box *toplevel_sx_box) { - int offset_x, offset_y; - xdg_popup_box_constraints(popup, toplevel_sx_box, - &offset_x, &offset_y); - - if (!offset_x && !offset_y) { - return true; - } - - bool resize_x = offset_x && - (popup->positioner_rules.constraint_adjustment & - XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X); - - bool resize_y = offset_y && - (popup->positioner_rules.constraint_adjustment & - XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y); - - if (resize_x) { - popup->geometry.width -= offset_x; - } - if (resize_y) { - popup->geometry.height -= offset_y; - } - - xdg_popup_box_constraints(popup, toplevel_sx_box, - &offset_x, &offset_y); - - return !offset_x && !offset_y; -} - void wlr_xdg_popup_unconstrain_from_box(struct wlr_xdg_popup *popup, - const struct wlr_box *toplevel_sx_box) { - if (xdg_popup_unconstrain_flip(popup, toplevel_sx_box)) { - return; - } - if (xdg_popup_unconstrain_slide(popup, toplevel_sx_box)) { - return; - } - if (xdg_popup_unconstrain_resize(popup, toplevel_sx_box)) { - return; - } + const struct wlr_box *toplevel_space_box) { + int toplevel_sx, toplevel_sy; + wlr_xdg_popup_get_toplevel_coords(popup, + 0, 0, &toplevel_sx, &toplevel_sy); + struct wlr_box popup_constraint = { + .x = toplevel_space_box->x - toplevel_sx, + .y = toplevel_space_box->y - toplevel_sy, + .width = toplevel_space_box->width, + .height = toplevel_space_box->height, + }; + wlr_xdg_positioner_rules_unconstrain_box(&popup->positioner_rules, + &popup_constraint, &popup->geometry); } diff --git a/types/xdg_shell/wlr_xdg_positioner.c b/types/xdg_shell/wlr_xdg_positioner.c index d4c6f03a..6fa1ed99 100644 --- a/types/xdg_shell/wlr_xdg_positioner.c +++ b/types/xdg_shell/wlr_xdg_positioner.c @@ -1,5 +1,6 @@ #include #include +#include #include "types/wlr_xdg_shell.h" static const struct xdg_positioner_interface xdg_positioner_implementation; @@ -139,74 +140,79 @@ void create_xdg_positioner(struct wlr_xdg_client *client, uint32_t id) { positioner, xdg_positioner_handle_resource_destroy); } -static bool positioner_anchor_has_edge(enum xdg_positioner_anchor anchor, - enum xdg_positioner_anchor edge) { - switch (edge) { +static uint32_t xdg_positioner_anchor_to_wlr_edges( + enum xdg_positioner_anchor anchor) { + switch (anchor) { + case XDG_POSITIONER_ANCHOR_NONE: + return WLR_EDGE_NONE; case XDG_POSITIONER_ANCHOR_TOP: - return anchor == XDG_POSITIONER_ANCHOR_TOP || - anchor == XDG_POSITIONER_ANCHOR_TOP_LEFT || - anchor == XDG_POSITIONER_ANCHOR_TOP_RIGHT; + return WLR_EDGE_TOP; + case XDG_POSITIONER_ANCHOR_TOP_LEFT: + return WLR_EDGE_TOP | WLR_EDGE_LEFT; + case XDG_POSITIONER_ANCHOR_TOP_RIGHT: + return WLR_EDGE_TOP | WLR_EDGE_RIGHT; case XDG_POSITIONER_ANCHOR_BOTTOM: - return anchor == XDG_POSITIONER_ANCHOR_BOTTOM || - anchor == XDG_POSITIONER_ANCHOR_BOTTOM_LEFT || - anchor == XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT; + return WLR_EDGE_BOTTOM; + case XDG_POSITIONER_ANCHOR_BOTTOM_LEFT: + return WLR_EDGE_BOTTOM | WLR_EDGE_LEFT; + case XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT: + return WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT; case XDG_POSITIONER_ANCHOR_LEFT: - return anchor == XDG_POSITIONER_ANCHOR_LEFT || - anchor == XDG_POSITIONER_ANCHOR_TOP_LEFT || - anchor == XDG_POSITIONER_ANCHOR_BOTTOM_LEFT; + return WLR_EDGE_LEFT; case XDG_POSITIONER_ANCHOR_RIGHT: - return anchor == XDG_POSITIONER_ANCHOR_RIGHT || - anchor == XDG_POSITIONER_ANCHOR_TOP_RIGHT || - anchor == XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT; - default: - abort(); // unreachable + return WLR_EDGE_RIGHT; } + + abort(); // Unreachable } -static bool positioner_gravity_has_edge(enum xdg_positioner_gravity gravity, - enum xdg_positioner_gravity edge) { - // gravity and edge enums are the same - return positioner_anchor_has_edge((enum xdg_positioner_anchor)gravity, - (enum xdg_positioner_anchor)edge); +static uint32_t xdg_positioner_gravity_to_wlr_edges( + enum xdg_positioner_gravity gravity) { + // Gravity and edge enums are the same + return xdg_positioner_anchor_to_wlr_edges((enum xdg_positioner_anchor)gravity); } void wlr_xdg_positioner_rules_get_geometry( - struct wlr_xdg_positioner_rules *rules, struct wlr_box *box) { + const struct wlr_xdg_positioner_rules *rules, struct wlr_box *box) { box->x = rules->offset.x; box->y = rules->offset.y; box->width = rules->size.width; box->height = rules->size.height; - if (positioner_anchor_has_edge(rules->anchor, XDG_POSITIONER_ANCHOR_TOP)) { + uint32_t edges = xdg_positioner_anchor_to_wlr_edges(rules->anchor); + + if (edges & WLR_EDGE_TOP) { box->y += rules->anchor_rect.y; - } else if (positioner_anchor_has_edge(rules->anchor, XDG_POSITIONER_ANCHOR_BOTTOM)) { + } else if (edges & WLR_EDGE_BOTTOM) { box->y += rules->anchor_rect.y + rules->anchor_rect.height; } else { box->y += rules->anchor_rect.y + rules->anchor_rect.height / 2; } - if (positioner_anchor_has_edge(rules->anchor, XDG_POSITIONER_ANCHOR_LEFT)) { + if (edges & WLR_EDGE_LEFT) { box->x += rules->anchor_rect.x; - } else if (positioner_anchor_has_edge(rules->anchor, XDG_POSITIONER_ANCHOR_RIGHT)) { + } else if (edges & WLR_EDGE_RIGHT) { box->x += rules->anchor_rect.x + rules->anchor_rect.width; } else { box->x += rules->anchor_rect.x + rules->anchor_rect.width / 2; } - if (positioner_gravity_has_edge(rules->gravity, XDG_POSITIONER_GRAVITY_TOP)) { + edges = xdg_positioner_gravity_to_wlr_edges(rules->gravity); + + if (edges & WLR_EDGE_TOP) { box->y -= box->height; - } else if (!positioner_gravity_has_edge(rules->gravity, XDG_POSITIONER_GRAVITY_BOTTOM)) { + } else if (~edges & WLR_EDGE_BOTTOM) { box->y -= box->height / 2; } - if (positioner_gravity_has_edge(rules->gravity, XDG_POSITIONER_GRAVITY_LEFT)) { + if (edges & WLR_EDGE_LEFT) { box->x -= box->width; - } else if (!positioner_gravity_has_edge(rules->gravity, XDG_POSITIONER_GRAVITY_RIGHT)) { + } else if (~edges & WLR_EDGE_RIGHT) { box->x -= box->width / 2; } } -static enum xdg_positioner_anchor positioner_anchor_invert_x( +static enum xdg_positioner_anchor xdg_positioner_anchor_invert_x( enum xdg_positioner_anchor anchor) { switch (anchor) { case XDG_POSITIONER_ANCHOR_LEFT: @@ -226,14 +232,14 @@ static enum xdg_positioner_anchor positioner_anchor_invert_x( } } -static enum xdg_positioner_gravity positioner_gravity_invert_x( +static enum xdg_positioner_gravity xdg_positioner_gravity_invert_x( enum xdg_positioner_gravity gravity) { // gravity and edge enums are the same - return (enum xdg_positioner_gravity)positioner_anchor_invert_x( + return (enum xdg_positioner_gravity)xdg_positioner_anchor_invert_x( (enum xdg_positioner_anchor)gravity); } -static enum xdg_positioner_anchor positioner_anchor_invert_y( +static enum xdg_positioner_anchor xdg_positioner_anchor_invert_y( enum xdg_positioner_anchor anchor) { switch (anchor) { case XDG_POSITIONER_ANCHOR_TOP: @@ -253,19 +259,213 @@ static enum xdg_positioner_anchor positioner_anchor_invert_y( } } -static enum xdg_positioner_gravity positioner_gravity_invert_y( +static enum xdg_positioner_gravity xdg_positioner_gravity_invert_y( enum xdg_positioner_gravity gravity) { // gravity and edge enums are the same - return (enum xdg_positioner_gravity)positioner_anchor_invert_y( + return (enum xdg_positioner_gravity)xdg_positioner_anchor_invert_y( (enum xdg_positioner_anchor)gravity); } -void wlr_xdg_positioner_rules_invert_x(struct wlr_xdg_positioner_rules *rules) { - rules->anchor = positioner_anchor_invert_x(rules->anchor); - rules->gravity = positioner_gravity_invert_x(rules->gravity); +/** + * Distances from each edge of the box to the corresponding edge of + * the anchor rect. Each distance is positive if the edge is outside + * the anchor rect, and negative if the edge is inside it. + */ +struct constraint_offsets { + int top; + int bottom; + int left; + int right; +}; + +static bool is_unconstrained(const struct constraint_offsets *offsets) { + return offsets->top <= 0 && offsets->bottom <= 0 && + offsets->left <= 0 && offsets->right <= 0; +} + +static void get_constrained_box_offsets(const struct wlr_box *constraint, + const struct wlr_box *box, struct constraint_offsets *offsets) { + offsets->left = constraint->x - box->x; + offsets->right = box->x + box->width - constraint->x - constraint->width; + offsets->top = constraint->y - box->y; + offsets->bottom = box->y + box->height - constraint->y - constraint->height; +} + +static bool xdg_positioner_rules_unconstrain_by_flip( + const struct wlr_xdg_positioner_rules *rules, + const struct wlr_box *constraint, struct wlr_box *box, + struct constraint_offsets *offsets) { + // If none of the edges are constrained, no need to flip. + // If both edges are constrained, the box is bigger than + // the anchor rect and flipping won't help anyway. + bool flip_x = ((offsets->left > 0) ^ (offsets->right > 0)) && + (rules->constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X); + bool flip_y = ((offsets->top > 0) ^ (offsets->bottom > 0)) && + (rules->constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y); + + if (!flip_x && !flip_y) { + return false; + } + + struct wlr_xdg_positioner_rules flipped = *rules; + if (flip_x) { + flipped.anchor = xdg_positioner_anchor_invert_x(flipped.anchor); + flipped.gravity = xdg_positioner_gravity_invert_x(flipped.gravity); + } + if (flip_y) { + flipped.anchor = xdg_positioner_anchor_invert_y(flipped.anchor); + flipped.gravity = xdg_positioner_gravity_invert_y(flipped.gravity); + } + + struct wlr_box flipped_box; + wlr_xdg_positioner_rules_get_geometry(&flipped, &flipped_box); + struct constraint_offsets flipped_offsets; + get_constrained_box_offsets(constraint, &flipped_box, &flipped_offsets); + + // Only apply flipping if it helps + if (flipped_offsets.left <= 0 && flipped_offsets.right <= 0) { + box->x = flipped_box.x; + offsets->left = flipped_offsets.left; + offsets->right = flipped_offsets.right; + } + if (flipped_offsets.top <= 0 && flipped_offsets.bottom <= 0) { + box->y = flipped_box.y; + offsets->top = flipped_offsets.top; + offsets->bottom = flipped_offsets.bottom; + } + + return is_unconstrained(offsets); +} + +static bool xdg_positioner_rules_unconstrain_by_slide( + const struct wlr_xdg_positioner_rules *rules, + const struct wlr_box *constraint, struct wlr_box *box, + struct constraint_offsets *offsets) { + uint32_t gravity = xdg_positioner_gravity_to_wlr_edges(rules->gravity); + + // We can only slide if there is gravity on this axis + bool slide_x = (offsets->left > 0 || offsets->right > 0) && + (rules->constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X) && + (gravity & (WLR_EDGE_LEFT | WLR_EDGE_RIGHT)); + bool slide_y = (offsets->top > 0 || offsets->bottom > 0) && + (rules->constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y) && + (gravity & (WLR_EDGE_TOP | WLR_EDGE_BOTTOM)); + + if (!slide_x && !slide_y) { + return false; + } + + if (slide_x) { + if (offsets->left > 0 && offsets->right > 0) { + // The protocol states: "First try to slide towards the direction of + // the gravity [...] Then try to slide towards the opposite direction + // of the gravity". The only situation where this order matters is when + // the box is bigger than the anchor rect and completely includes it. + // In this case, the second slide will fail immediately, so simply + // slide towards the direction of the gravity. + if (gravity & WLR_EDGE_LEFT) { + box->x -= offsets->right; + } else if (gravity & WLR_EDGE_RIGHT) { + box->x += offsets->left; + } + } else { + // If at least one edge is already unconstrained, the order of slide + // attempts doesn't matter. Slide for the minimal distance needed to + // satisfy the requirement of constraining one edge or unconstraining + // another. + int abs_left = offsets->left > 0 ? offsets->left : -offsets->left; + int abs_right = offsets->right > 0 ? offsets->right : -offsets->right; + if (abs_left < abs_right) { + box->x += offsets->left; + } else { + box->x -= offsets->right; + } + } + } + if (slide_y) { + if (offsets->top > 0 && offsets->bottom > 0) { + if (gravity & WLR_EDGE_TOP) { + box->y -= offsets->bottom; + } else if (gravity & WLR_EDGE_BOTTOM) { + box->y += offsets->top; + } + } else { + int abs_top = offsets->top > 0 ? offsets->top : -offsets->top; + int abs_bottom = offsets->bottom > 0 ? offsets->bottom : -offsets->bottom; + if (abs_top < abs_bottom) { + box->y += offsets->top; + } else { + box->y -= offsets->bottom; + } + } + } + + get_constrained_box_offsets(constraint, box, offsets); + return is_unconstrained(offsets); +} + +static bool xdg_positioner_rules_unconstrain_by_resize( + const struct wlr_xdg_positioner_rules *rules, + const struct wlr_box *constraint, struct wlr_box *box, + struct constraint_offsets *offsets) { + bool resize_x = (offsets->left > 0 || offsets->right > 0) && + (rules->constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X); + bool resize_y = (offsets->top > 0 || offsets->bottom > 0) && + (rules->constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y); + + if (!resize_x && !resize_y) { + return false; + } + + if (offsets->left < 0) { + offsets->left = 0; + } + if (offsets->right < 0) { + offsets->right = 0; + } + if (offsets->top < 0) { + offsets->top = 0; + } + if (offsets->bottom < 0) { + offsets->bottom = 0; + } + + // Try to satisfy the constraints by clipping the box. + struct wlr_box resized_box = *box; + if (resize_x) { + resized_box.x += offsets->left; + resized_box.width -= offsets->left + offsets->right; + } + if (resize_y) { + resized_box.y += offsets->top; + resized_box.height -= offsets->top + offsets->bottom; + } + + if (wlr_box_empty(&resized_box)) { + return false; + } + + *box = resized_box; + get_constrained_box_offsets(constraint, box, offsets); + return is_unconstrained(offsets); } -void wlr_xdg_positioner_rules_invert_y(struct wlr_xdg_positioner_rules *rules) { - rules->anchor = positioner_anchor_invert_y(rules->anchor); - rules->gravity = positioner_gravity_invert_y(rules->gravity); +void wlr_xdg_positioner_rules_unconstrain_box( + const struct wlr_xdg_positioner_rules *rules, + const struct wlr_box *constraint, struct wlr_box *box) { + struct constraint_offsets offsets; + get_constrained_box_offsets(constraint, box, &offsets); + if (is_unconstrained(&offsets)) { + // Already unconstrained + return; + } + if (xdg_positioner_rules_unconstrain_by_flip(rules, constraint, box, &offsets)) { + return; + } + if (xdg_positioner_rules_unconstrain_by_slide(rules, constraint, box, &offsets)) { + return; + } + if (xdg_positioner_rules_unconstrain_by_resize(rules, constraint, box, &offsets)) { + return; + } }