wayland: Adjust the position of existing popups when resizing a window

When resizing a window, adjust the size of the anchor rect and ensure that existing popups remain in bounds/adjacent to avoid a protocol error.
main
Frank Praznik 2023-07-20 14:48:43 -04:00
parent 72a489e6be
commit 5c2fb6eb61
1 changed files with 78 additions and 73 deletions

View File

@ -231,6 +231,78 @@ static void SetMinMaxDimensions(SDL_Window *window)
}
}
static void EnsurePopupPositionIsValid(SDL_Window *window)
{
int adj_count = 0;
/* Per the xdg-positioner spec, child popup windows must intersect or at
* least be partially adjacent to the parent window.
*
* Failure to ensure this on a compositor that enforces this restriction
* can result in behavior ranging from the window being spuriously closed
* to a protocol violation.
*/
if (window->x + window->driverdata->wl_window_width < 0) {
window->x = -window->w;
++adj_count;
}
if (window->y + window->driverdata->wl_window_height < 0) {
window->y = -window->h;
++adj_count;
}
if (window->x > window->parent->driverdata->wl_window_width) {
window->x = window->parent->driverdata->wl_window_width;
++adj_count;
}
if (window->y > window->parent->driverdata->wl_window_height) {
window->y = window->parent->driverdata->wl_window_height;
++adj_count;
}
/* If adjustment was required on the x and y axes, the popup is aligned with
* the parent corner-to-corner and is neither overlapping nor adjacent, so it
* must be nudged by 1 to be considered adjacent.
*/
if (adj_count > 1) {
window->x += window->x < 0 ? 1 : -1;
}
}
static void GetPopupPosition(SDL_Window *popup, int x, int y, int *adj_x, int *adj_y)
{
/* Adjust the popup positioning, if necessary */
#ifdef HAVE_LIBDECOR_H
if (popup->parent->driverdata->shell_surface_type == WAYLAND_SURFACE_LIBDECOR) {
libdecor_frame_translate_coordinate(popup->parent->driverdata->shell_surface.libdecor.frame,
x, y, adj_x, adj_y);
} else
#endif
{
*adj_x = x;
*adj_y = y;
}
}
static void RepositionPopup(SDL_Window *window)
{
SDL_WindowData *wind = window->driverdata;
if (wind->shell_surface_type == WAYLAND_SURFACE_XDG_POPUP &&
wind->shell_surface.xdg.roleobj.popup.positioner &&
xdg_popup_get_version(wind->shell_surface.xdg.roleobj.popup.popup) >= XDG_POPUP_REPOSITION_SINCE_VERSION) {
int x, y;
EnsurePopupPositionIsValid(window);
GetPopupPosition(window, window->x, window->y, &x, &y);
xdg_positioner_set_anchor_rect(wind->shell_surface.xdg.roleobj.popup.positioner, 0, 0, window->parent->driverdata->wl_window_width, window->parent->driverdata->wl_window_height);
xdg_positioner_set_size(wind->shell_surface.xdg.roleobj.popup.positioner, wind->wl_window_width, wind->wl_window_height);
xdg_positioner_set_offset(wind->shell_surface.xdg.roleobj.popup.positioner, x, y);
xdg_popup_reposition(wind->shell_surface.xdg.roleobj.popup.popup,
wind->shell_surface.xdg.roleobj.popup.positioner,
0);
}
}
static void ConfigureWindowGeometry(SDL_Window *window)
{
SDL_WindowData *data = window->driverdata;
@ -316,7 +388,7 @@ static void ConfigureWindowGeometry(SDL_Window *window)
window_width = data->requested_window_width;
window_height = data->requested_window_height;
window_size_changed = window_width != window->w || window_height != window->h;
window_size_changed = window_width != data->wl_window_width || window_height != data->wl_window_height;
if (window_size_changed || drawable_size_changed) {
if (WindowNeedsViewport(window)) {
@ -358,6 +430,11 @@ static void ConfigureWindowGeometry(SDL_Window *window)
wl_region_destroy(region);
}
/* Ensure that child popup windows are still in bounds. */
for (SDL_Window *child = window->first_child; child != NULL; child = child->next_sibling) {
RepositionPopup(child);
}
if (data->confined_pointer) {
Wayland_input_confine_pointer(viddata->input, window);
}
@ -390,43 +467,6 @@ static void ConfigureWindowGeometry(SDL_Window *window)
}
}
static void EnsurePopupPositionIsValid(SDL_Window *window)
{
int adj_count = 0;
/* Per the xdg-positioner spec, child popup windows must intersect or at
* least be partially adjacent to the parent window.
*
* Failure to ensure this on a compositor that enforces this restriction
* can result in behavior ranging from the window being spuriously closed
* to a protocol violation.
*/
if (window->x + window->w < 0) {
window->x = -window->w;
++adj_count;
}
if (window->y + window->h < 0) {
window->y = -window->h;
++adj_count;
}
if (window->x > window->parent->w) {
window->x = window->parent->w;
++adj_count;
}
if (window->y > window->parent->h) {
window->y = window->parent->h;
++adj_count;
}
/* If adjustment was required on the x and y axes, the popup is aligned with
* the parent corner-to-corner and is neither overlapping nor adjacent, so it
* must be nudged by 1 to be considered adjacent.
*/
if (adj_count > 1) {
window->x += window->x < 0 ? 1 : -1;
}
}
static void CommitLibdecorFrame(SDL_Window *window)
{
#ifdef HAVE_LIBDECOR_H
@ -509,40 +549,6 @@ static void UpdateWindowFullscreen(SDL_Window *window, SDL_bool fullscreen)
}
}
static void GetPopupPosition(SDL_Window *popup, int x, int y, int *adj_x, int *adj_y)
{
/* Adjust the popup positioning, if necessary */
#ifdef HAVE_LIBDECOR_H
if (popup->parent->driverdata->shell_surface_type == WAYLAND_SURFACE_LIBDECOR) {
libdecor_frame_translate_coordinate(popup->parent->driverdata->shell_surface.libdecor.frame,
x, y, adj_x, adj_y);
} else
#endif
{
*adj_x = x;
*adj_y = y;
}
}
static void RepositionPopup(SDL_Window *window)
{
SDL_WindowData *wind = window->driverdata;
if (wind->shell_surface_type == WAYLAND_SURFACE_XDG_POPUP &&
wind->shell_surface.xdg.roleobj.popup.positioner &&
xdg_popup_get_version(wind->shell_surface.xdg.roleobj.popup.popup) >= XDG_POPUP_REPOSITION_SINCE_VERSION) {
int x, y;
EnsurePopupPositionIsValid(window);
GetPopupPosition(window, window->x, window->y, &x, &y);
xdg_positioner_set_size(wind->shell_surface.xdg.roleobj.popup.positioner, window->w, window->h);
xdg_positioner_set_offset(wind->shell_surface.xdg.roleobj.popup.positioner, x, y);
xdg_popup_reposition(wind->shell_surface.xdg.roleobj.popup.popup,
wind->shell_surface.xdg.roleobj.popup.positioner,
0);
}
}
static const struct wl_callback_listener surface_frame_listener;
static void surface_frame_done(void *data, struct wl_callback *cb, uint32_t time)
@ -2307,7 +2313,6 @@ void Wayland_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window)
wind->requested_window_height = window->windowed.h;
ConfigureWindowGeometry(window);
RepositionPopup(window);
}
/* Always commit, as this may be in response to a min/max limit change. */