wayland: Implement the popup window changes
diff --git a/src/video/wayland/SDL_waylanddyn.h b/src/video/wayland/SDL_waylanddyn.h index a900537..fa6b500 100644 --- a/src/video/wayland/SDL_waylanddyn.h +++ b/src/video/wayland/SDL_waylanddyn.h
@@ -160,6 +160,7 @@ #define libdecor_frame_set_parent (*WAYLAND_libdecor_frame_set_parent) #define libdecor_frame_get_xdg_surface (*WAYLAND_libdecor_frame_get_xdg_surface) #define libdecor_frame_get_xdg_toplevel (*WAYLAND_libdecor_frame_get_xdg_toplevel) +#define libdecor_frame_translate_coordinate (*WAYLAND_libdecor_frame_translate_coordinate) #define libdecor_frame_map (*WAYLAND_libdecor_frame_map) #define libdecor_state_new (*WAYLAND_libdecor_state_new) #define libdecor_state_free (*WAYLAND_libdecor_state_free)
diff --git a/src/video/wayland/SDL_waylandsym.h b/src/video/wayland/SDL_waylandsym.h index 481eeab..b0486a9 100644 --- a/src/video/wayland/SDL_waylandsym.h +++ b/src/video/wayland/SDL_waylandsym.h
@@ -206,6 +206,7 @@ struct libdecor_frame *)) SDL_WAYLAND_SYM(struct xdg_surface *, libdecor_frame_get_xdg_surface, (struct libdecor_frame *)) SDL_WAYLAND_SYM(struct xdg_toplevel *, libdecor_frame_get_xdg_toplevel, (struct libdecor_frame *)) +SDL_WAYLAND_SYM(void, libdecor_frame_translate_coordinate, (struct libdecor_frame *, int, int, int *, int *)) SDL_WAYLAND_SYM(void, libdecor_frame_map, (struct libdecor_frame *)) SDL_WAYLAND_SYM(struct libdecor_state *, libdecor_state_new, (int, int)) SDL_WAYLAND_SYM(void, libdecor_state_free, (struct libdecor_state *))
diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c index d706303..e3b5fc5 100644 --- a/src/video/wayland/SDL_waylandvideo.c +++ b/src/video/wayland/SDL_waylandvideo.c
@@ -238,6 +238,7 @@ device->RestoreWindow = Wayland_RestoreWindow; device->SetWindowBordered = Wayland_SetWindowBordered; device->SetWindowResizable = Wayland_SetWindowResizable; + device->SetWindowPosition = Wayland_SetWindowPosition; device->SetWindowSize = Wayland_SetWindowSize; device->SetWindowMinimumSize = Wayland_SetWindowMinimumSize; device->SetWindowMaximumSize = Wayland_SetWindowMaximumSize;
diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c index c84206b..b041405 100644 --- a/src/video/wayland/SDL_waylandwindow.c +++ b/src/video/wayland/SDL_waylandwindow.c
@@ -405,6 +405,21 @@ } } +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 const struct wl_callback_listener surface_damage_frame_listener; static void surface_damage_frame_done(void *data, struct wl_callback *cb, uint32_t time) @@ -590,7 +605,17 @@ int32_t width, int32_t height) { - /* No-op, we don't use x/y and width/height are fixed-size */ + /* Popups can't be resized, so only position data is sent. */ + SDL_WindowData *wind = (SDL_WindowData *)data; + int offset_x, offset_y; + + /* Adjust the position if it was offset for libdecor */ + GetPopupPosition(wind->sdlwindow, 0, 0, &offset_x, &offset_y); + x -= offset_x; + y -= offset_y; + + SDL_SendWindowEvent(wind->sdlwindow, SDL_EVENT_WINDOW_RESIZED, width, height); + SDL_SendWindowEvent(wind->sdlwindow, SDL_EVENT_WINDOW_MOVED, x, y); } static void handle_done_xdg_popup(void *data, struct xdg_popup *xdg_popup) @@ -612,27 +637,6 @@ handle_repositioned_xdg_popup }; -#define TOOLTIP_CURSOR_OFFSET 8 /* FIXME: Arbitrary, eyeballed from X tooltip */ - -static int Wayland_PopupWatch(void *data, SDL_Event *event) -{ - if (event->type == SDL_EVENT_MOUSE_MOTION) { - SDL_Window *window = (SDL_Window *)data; - SDL_WindowData *wind = window->driverdata; - - /* Coordinates might be relative to the popup, which we don't want */ - if (event->motion.windowID == wind->shell_surface.xdg.roleobj.popup.parentID) { - xdg_positioner_set_offset(wind->shell_surface.xdg.roleobj.popup.positioner, - event->motion.x + TOOLTIP_CURSOR_OFFSET, - event->motion.y + TOOLTIP_CURSOR_OFFSET); - xdg_popup_reposition(wind->shell_surface.xdg.roleobj.popup.popup, - wind->shell_surface.xdg.roleobj.popup.positioner, - 0); - } - } - return 1; -} - static void handle_configure_zxdg_decoration(void *data, struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1, uint32_t mode) @@ -1152,41 +1156,55 @@ } } else #endif - if (c->shell.xdg) { + if ((data->shell_surface_type == WAYLAND_SURFACE_XDG_TOPLEVEL || data->shell_surface_type == WAYLAND_SURFACE_XDG_POPUP) && c->shell.xdg) { data->shell_surface.xdg.surface = xdg_wm_base_get_xdg_surface(c->shell.xdg, data->surface); xdg_surface_set_user_data(data->shell_surface.xdg.surface, data); xdg_surface_add_listener(data->shell_surface.xdg.surface, &shell_surface_listener_xdg, data); if (data->shell_surface_type == WAYLAND_SURFACE_XDG_POPUP) { - SDL_Mouse *mouse = SDL_GetMouse(); - SDL_Window *focused = SDL_GetMouseFocus(); - SDL_WindowData *focuseddata = focused->driverdata; + SDL_Window *parent = window->parent; + SDL_WindowData *parent_data = parent->driverdata; + struct xdg_surface *parent_xdg_surface = NULL; + int position_x = 0, position_y = 0; - /* This popup may be a child of another popup! */ - data->shell_surface.xdg.roleobj.popup.parentID = SDL_GetWindowID(focused); - data->shell_surface.xdg.roleobj.popup.child = NULL; - if (focuseddata->shell_surface_type == WAYLAND_SURFACE_XDG_POPUP) { - SDL_assert(focuseddata->shell_surface.xdg.roleobj.popup.child == NULL); - focuseddata->shell_surface.xdg.roleobj.popup.child = window; + /* Configure the popup parameters */ +#ifdef HAVE_LIBDECOR_H + if (parent_data->shell_surface_type == WAYLAND_SURFACE_LIBDECOR) { + parent_xdg_surface = libdecor_frame_get_xdg_surface(parent_data->shell_surface.libdecor.frame); + } else +#endif + if (parent_data->shell_surface_type == WAYLAND_SURFACE_XDG_TOPLEVEL || + parent_data->shell_surface_type == WAYLAND_SURFACE_XDG_POPUP) { + parent_xdg_surface = parent_data->shell_surface.xdg.surface; } - /* Set up the positioner for the popup */ + /* Set up the positioner for the popup and configure the constraints */ data->shell_surface.xdg.roleobj.popup.positioner = xdg_wm_base_create_positioner(c->shell.xdg); - xdg_positioner_set_offset(data->shell_surface.xdg.roleobj.popup.positioner, - mouse->x + TOOLTIP_CURSOR_OFFSET, - mouse->y + TOOLTIP_CURSOR_OFFSET); + xdg_positioner_set_anchor(data->shell_surface.xdg.roleobj.popup.positioner, XDG_POSITIONER_ANCHOR_TOP_LEFT); + xdg_positioner_set_anchor_rect(data->shell_surface.xdg.roleobj.popup.positioner, 0, 0, parent->w, parent->h); + xdg_positioner_set_constraint_adjustment(data->shell_surface.xdg.roleobj.popup.positioner, + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y); + xdg_positioner_set_gravity(data->shell_surface.xdg.roleobj.popup.positioner, XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT); + xdg_positioner_set_size(data->shell_surface.xdg.roleobj.popup.positioner, window->w, window->h); + + /* Set the popup initial position */ + GetPopupPosition(window, window->x, window->y, &position_x, &position_y); + xdg_positioner_set_offset(data->shell_surface.xdg.roleobj.popup.positioner, position_x, position_y); /* Assign the popup role */ data->shell_surface.xdg.roleobj.popup.popup = xdg_surface_get_popup(data->shell_surface.xdg.surface, - focuseddata->shell_surface.xdg.surface, + parent_xdg_surface, data->shell_surface.xdg.roleobj.popup.positioner); xdg_popup_add_listener(data->shell_surface.xdg.roleobj.popup.popup, &popup_listener_xdg, data); - /* For tooltips, track mouse motion so it follows the cursor */ if (window->flags & SDL_WINDOW_TOOLTIP) { - if (xdg_popup_get_version(data->shell_surface.xdg.roleobj.popup.popup) >= 3) { - SDL_AddEventWatch(Wayland_PopupWatch, window); - } + struct wl_region *region; + + /* Tooltips can't be interacted with, so turn off the input region to avoid blocking anything behind them */ + region = wl_compositor_create_region(c->compositor); + wl_region_add(region, 0, 0, 0, 0); + wl_surface_set_input_region(data->surface, region); + wl_region_destroy(region); } } else { data->shell_surface.xdg.roleobj.toplevel = xdg_surface_get_toplevel(data->shell_surface.xdg.surface); @@ -1318,17 +1336,6 @@ return; } - /* Release the child _first_, otherwise a protocol error triggers */ - if (popupdata->shell_surface.xdg.roleobj.popup.child != NULL) { - Wayland_ReleasePopup(_this, popupdata->shell_surface.xdg.roleobj.popup.child); - popupdata->shell_surface.xdg.roleobj.popup.child = NULL; - } - - if (popup->flags & SDL_WINDOW_TOOLTIP) { - if (xdg_popup_get_version(popupdata->shell_surface.xdg.roleobj.popup.popup) >= 3) { - SDL_DelEventWatch(Wayland_PopupWatch, popup); - } - } xdg_popup_destroy(popupdata->shell_surface.xdg.roleobj.popup.popup); xdg_positioner_destroy(popupdata->shell_surface.xdg.roleobj.popup.positioner); popupdata->shell_surface.xdg.roleobj.popup.popup = NULL; @@ -1806,11 +1813,19 @@ } } +#define IS_POPUP(window) \ + (window->flags & (SDL_WINDOW_TOOLTIP | SDL_WINDOW_POPUP_MENU)) + int Wayland_CreateWindow(_THIS, SDL_Window *window) { SDL_WindowData *data; SDL_VideoData *c; + /* Popups must have a parent window */ + if (IS_POPUP(window) && !SDL_GetMouseFocus()) { + return -1; + } + data = SDL_calloc(1, sizeof *data); if (data == NULL) { return SDL_OutOfMemory(); @@ -1927,8 +1942,6 @@ /* We may need to create an idle inhibitor for this new window */ Wayland_SuspendScreenSaver(_this); -#define IS_POPUP(window) \ - (window->flags & (SDL_WINDOW_TOOLTIP | SDL_WINDOW_POPUP_MENU)) #ifdef HAVE_LIBDECOR_H if (c->shell.libdecor && !IS_POPUP(window)) { data->shell_surface_type = WAYLAND_SURFACE_LIBDECOR; @@ -1956,6 +1969,23 @@ SetMinMaxDimensions(window, SDL_TRUE); } +void Wayland_SetWindowPosition(_THIS, SDL_Window *window) +{ + SDL_WindowData *wind = window->driverdata; + + /* Only popup windows can be positioned relative to the parent. */ + if (wind->shell_surface_type == WAYLAND_SURFACE_XDG_POPUP && !(window->flags & SDL_WINDOW_HIDDEN) && + xdg_popup_get_version(wind->shell_surface.xdg.roleobj.popup.popup) >= XDG_POPUP_REPOSITION_SINCE_VERSION) { + int x, y; + + GetPopupPosition(window, window->x, window->y, &x, &y); + 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); + } +} + void Wayland_SetWindowSize(_THIS, SDL_Window *window) { SDL_WindowData *wind = window->driverdata; @@ -2059,7 +2089,7 @@ SDL_VideoData *data = _this->driverdata; SDL_WindowData *wind = window->driverdata; - if (data) { + if (data && wind) { #if SDL_VIDEO_OPENGL_EGL if (wind->egl_surface) { SDL_EGL_DestroySurface(_this, wind->egl_surface);
diff --git a/src/video/wayland/SDL_waylandwindow.h b/src/video/wayland/SDL_waylandwindow.h index 9a7cc13..fe0238d 100644 --- a/src/video/wayland/SDL_waylandwindow.h +++ b/src/video/wayland/SDL_waylandwindow.h
@@ -62,8 +62,6 @@ { struct xdg_popup *popup; struct xdg_positioner *positioner; - Uint32 parentID; - SDL_Window *child; } popup; } roleobj; SDL_bool initial_configure_seen; @@ -133,6 +131,7 @@ extern void Wayland_SetWindowBordered(_THIS, SDL_Window *window, SDL_bool bordered); extern void Wayland_SetWindowResizable(_THIS, SDL_Window *window, SDL_bool resizable); extern int Wayland_CreateWindow(_THIS, SDL_Window *window); +extern void Wayland_SetWindowPosition(_THIS, SDL_Window *window); extern void Wayland_SetWindowSize(_THIS, SDL_Window *window); extern void Wayland_SetWindowMinimumSize(_THIS, SDL_Window *window); extern void Wayland_SetWindowMaximumSize(_THIS, SDL_Window *window);