Skip to content

Commit

Permalink
wayland: Implement the popup window changes
Browse files Browse the repository at this point in the history
  • Loading branch information
Kontrabant committed Mar 2, 2023
1 parent 56ed06e commit 233da68
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 58 deletions.
1 change: 1 addition & 0 deletions src/video/wayland/SDL_waylanddyn.h
Expand Up @@ -160,6 +160,7 @@ void SDL_WAYLAND_UnloadSymbols(void);
#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)
Expand Down
1 change: 1 addition & 0 deletions src/video/wayland/SDL_waylandsym.h
Expand Up @@ -206,6 +206,7 @@ SDL_WAYLAND_SYM(void, libdecor_frame_set_parent, (struct libdecor_frame *,\
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 *))
Expand Down
1 change: 1 addition & 0 deletions src/video/wayland/SDL_waylandvideo.c
Expand Up @@ -238,6 +238,7 @@ static SDL_VideoDevice *Wayland_CreateDevice(void)
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;
Expand Down
142 changes: 86 additions & 56 deletions src/video/wayland/SDL_waylandwindow.c
Expand Up @@ -405,6 +405,21 @@ 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 const struct wl_callback_listener surface_damage_frame_listener;

static void surface_damage_frame_done(void *data, struct wl_callback *cb, uint32_t time)
Expand Down Expand Up @@ -590,7 +605,17 @@ static void handle_configure_xdg_popup(void *data,
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)
Expand All @@ -612,27 +637,6 @@ static const struct xdg_popup_listener popup_listener_xdg = {
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)
Expand Down Expand Up @@ -1152,41 +1156,55 @@ void Wayland_ShowWindow(_THIS, SDL_Window *window)
}
} 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;

/* 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;
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;

/* 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);
Expand Down Expand Up @@ -1318,17 +1336,6 @@ static void Wayland_ReleasePopup(_THIS, SDL_Window *popup)
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;
Expand Down Expand Up @@ -1806,11 +1813,19 @@ void Wayland_SetWindowKeyboardGrab(_THIS, SDL_Window *window, SDL_bool grabbed)
}
}

#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();
Expand Down Expand Up @@ -1927,8 +1942,6 @@ int Wayland_CreateWindow(_THIS, SDL_Window *window)
/* 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;
Expand Down Expand Up @@ -1956,6 +1969,23 @@ void Wayland_SetWindowMaximumSize(_THIS, SDL_Window *window)
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;
Expand Down Expand Up @@ -2059,7 +2089,7 @@ void Wayland_DestroyWindow(_THIS, SDL_Window *window)
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);
Expand Down
3 changes: 1 addition & 2 deletions src/video/wayland/SDL_waylandwindow.h
Expand Up @@ -62,8 +62,6 @@ struct SDL_WindowData
{
struct xdg_popup *popup;
struct xdg_positioner *positioner;
Uint32 parentID;
SDL_Window *child;
} popup;
} roleobj;
SDL_bool initial_configure_seen;
Expand Down Expand Up @@ -133,6 +131,7 @@ extern void Wayland_RestoreWindow(_THIS, SDL_Window *window);
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);
Expand Down

0 comments on commit 233da68

Please sign in to comment.