Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 80 additions & 83 deletions src/video/wayland/SDL_waylandwindow.c
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ static void SetMinMaxDimensions(SDL_Window *window)
SDL_WindowData *wind = window->internal;
int min_width, min_height, max_width, max_height;

if ((window->flags & SDL_WINDOW_FULLSCREEN) || wind->fullscreen_deadline_count) {
if (window->flags & SDL_WINDOW_FULLSCREEN) {
min_width = 0;
min_height = 0;
max_width = 0;
Expand Down Expand Up @@ -486,38 +486,21 @@ static void CommitLibdecorFrame(SDL_Window *window)
#endif
}

static void fullscreen_deadline_handler(void *data, struct wl_callback *callback, uint32_t callback_data)
static void window_state_deadline_handler(void *data, struct wl_callback *callback, uint32_t callback_data)
{
// Get the window from the ID as it may have been destroyed
SDL_WindowID windowID = (SDL_WindowID)((uintptr_t)data);
SDL_Window *window = SDL_GetWindowFromID(windowID);

if (window && window->internal) {
window->internal->fullscreen_deadline_count--;
window->internal->window_state_deadline_count--;
}

wl_callback_destroy(callback);
}

static struct wl_callback_listener fullscreen_deadline_listener = {
fullscreen_deadline_handler
};

static void maximized_restored_deadline_handler(void *data, struct wl_callback *callback, uint32_t callback_data)
{
// Get the window from the ID as it may have been destroyed
SDL_WindowID windowID = (SDL_WindowID)((uintptr_t)data);
SDL_Window *window = SDL_GetWindowFromID(windowID);

if (window && window->internal) {
window->internal->maximized_restored_deadline_count--;
}

wl_callback_destroy(callback);
}

static struct wl_callback_listener maximized_restored_deadline_listener = {
maximized_restored_deadline_handler
static struct wl_callback_listener window_state_deadline_listener = {
window_state_deadline_handler
};

static void FlushPendingEvents(SDL_Window *window)
Expand All @@ -526,7 +509,7 @@ static void FlushPendingEvents(SDL_Window *window)
const bool last_position_pending = window->last_position_pending;
const bool last_size_pending = window->last_size_pending;

while (window->internal->fullscreen_deadline_count || window->internal->maximized_restored_deadline_count) {
while (window->internal->window_state_deadline_count) {
WAYLAND_wl_display_roundtrip(window->internal->waylandData->display);
}

Expand Down Expand Up @@ -601,11 +584,7 @@ static void SetFullscreen(SDL_Window *window, struct wl_output *output, bool ful
}

wind->fullscreen_exclusive = output ? window->fullscreen_exclusive : false;
++wind->fullscreen_deadline_count;
if (fullscreen) {
Wayland_SetWindowResizable(SDL_GetVideoDevice(), window, true);
wl_surface_commit(wind->surface);

libdecor_frame_set_fullscreen(wind->shell_surface.libdecor.frame, output);
} else {
libdecor_frame_unset_fullscreen(wind->shell_surface.libdecor.frame);
Expand All @@ -618,20 +597,17 @@ static void SetFullscreen(SDL_Window *window, struct wl_output *output, bool ful
}

wind->fullscreen_exclusive = output ? window->fullscreen_exclusive : false;
++wind->fullscreen_deadline_count;
if (fullscreen) {
Wayland_SetWindowResizable(SDL_GetVideoDevice(), window, true);
wl_surface_commit(wind->surface);

xdg_toplevel_set_fullscreen(wind->shell_surface.xdg.toplevel.xdg_toplevel, output);
} else {
xdg_toplevel_unset_fullscreen(wind->shell_surface.xdg.toplevel.xdg_toplevel);
}
}

// Queue a deadline event
++wind->window_state_deadline_count;
struct wl_callback *cb = wl_display_sync(viddata->display);
wl_callback_add_listener(cb, &fullscreen_deadline_listener, (void *)((uintptr_t)window->id));
wl_callback_add_listener(cb, &window_state_deadline_listener, (void *)((uintptr_t)window->id));
}

static void UpdateWindowFullscreen(SDL_Window *window, bool fullscreen)
Expand Down Expand Up @@ -680,43 +656,46 @@ static void surface_frame_done(void *data, struct wl_callback *cb, uint32_t time
{
SDL_WindowData *wind = (SDL_WindowData *)data;

/* XXX: This is needed to work around an Nvidia egl-wayland bug due to buffer coordinates
* being used with wl_surface_damage, which causes part of the output to not be
* updated when using a viewport with an output region larger than the source region.
*/
if (wl_compositor_get_version(wind->waylandData->compositor) >= WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION) {
wl_surface_damage_buffer(wind->surface, 0, 0, SDL_MAX_SINT32, SDL_MAX_SINT32);
} else {
wl_surface_damage(wind->surface, 0, 0, SDL_MAX_SINT32, SDL_MAX_SINT32);
}

wind->drop_interactive_resizes = false;
wind->pending_client_viewport_dimensions = false;

if (!(wind->sdlwindow->flags & SDL_WINDOW_EXTERNAL)) {
/* XXX: This is needed to work around an Nvidia egl-wayland bug due to buffer coordinates
* being used with wl_surface_damage, which causes part of the output to not be
* updated when using a viewport with an output region larger than the source region.
*/
if (wl_compositor_get_version(wind->waylandData->compositor) >= WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION) {
wl_surface_damage_buffer(wind->surface, 0, 0, SDL_MAX_SINT32, SDL_MAX_SINT32);
} else {
wl_surface_damage(wind->surface, 0, 0, SDL_MAX_SINT32, SDL_MAX_SINT32);
}

if (wind->shell_surface_status == WAYLAND_SHELL_SURFACE_STATUS_WAITING_FOR_FRAME) {
wind->shell_surface_status = WAYLAND_SHELL_SURFACE_STATUS_SHOWN;

// If any child windows are waiting on this window to be shown, show them now
for (SDL_Window *w = wind->sdlwindow->first_child; w; w = w->next_sibling) {
if (w->internal->shell_surface_status == WAYLAND_SHELL_SURFACE_STATUS_SHOW_PENDING) {
Wayland_ShowWindow(SDL_GetVideoDevice(), w);
} else if (w->internal->reparenting_required) {
Wayland_SetWindowParent(SDL_GetVideoDevice(), w, w->parent);
if (w->flags & SDL_WINDOW_MODAL) {
Wayland_SetWindowModal(SDL_GetVideoDevice(), w, true);
if (wind->shell_surface_status == WAYLAND_SHELL_SURFACE_STATUS_WAITING_FOR_FRAME) {
wind->shell_surface_status = WAYLAND_SHELL_SURFACE_STATUS_SHOWN;

// If any child windows are waiting on this window to be shown, show them now
for (SDL_Window *w = wind->sdlwindow->first_child; w; w = w->next_sibling) {
if (w->internal->shell_surface_status == WAYLAND_SHELL_SURFACE_STATUS_SHOW_PENDING) {
Wayland_ShowWindow(SDL_GetVideoDevice(), w);
} else if (w->internal->reparenting_required) {
Wayland_SetWindowParent(SDL_GetVideoDevice(), w, w->parent);
if (w->flags & SDL_WINDOW_MODAL) {
Wayland_SetWindowModal(SDL_GetVideoDevice(), w, true);
}
}
}
}

/* If the window was initially set to the suspended state, send the occluded event now,
* as we don't want to mark the window as occluded until at least one frame has been submitted.
*/
if (wind->suspended) {
SDL_SendWindowEvent(wind->sdlwindow, SDL_EVENT_WINDOW_OCCLUDED, 0, 0);
/* If the window was initially set to the suspended state, send the occluded event now,
* as we don't want to mark the window as occluded until at least one frame has been submitted.
*/
if (wind->suspended) {
SDL_SendWindowEvent(wind->sdlwindow, SDL_EVENT_WINDOW_OCCLUDED, 0, 0);
}
}
}

wl_callback_destroy(cb);
wind->surface_frame_callback = wl_surface_frame(wind->surface);
wind->surface_frame_callback = wl_surface_frame(wind->surface_wrapper);
wl_callback_add_listener(wind->surface_frame_callback, &surface_frame_listener, data);
}

Expand Down Expand Up @@ -2358,6 +2337,13 @@ SDL_FullscreenResult Wayland_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Win
output = NULL;
}
}

// Commit to preserve any pending client viewport dimensions before entering fullscreen.
if (fullscreen == SDL_FULLSCREEN_OP_ENTER && !(window->flags & SDL_WINDOW_MAXIMIZED) &&
wind->pending_client_viewport_dimensions) {
wind->pending_client_viewport_dimensions = false;
wl_surface_commit(wind->surface);
}
SetFullscreen(window, output, !!fullscreen);
} else if (wind->is_fullscreen) {
/*
Expand Down Expand Up @@ -2391,8 +2377,7 @@ void Wayland_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window)
}

// Not currently fullscreen or maximized, and no state pending; nothing to do.
if (!(window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED)) &&
!wind->fullscreen_deadline_count && !wind->maximized_restored_deadline_count) {
if (!(window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED)) && !wind->window_state_deadline_count) {
return;
}

Expand All @@ -2403,9 +2388,9 @@ void Wayland_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window)
}
libdecor_frame_unset_maximized(wind->shell_surface.libdecor.frame);

++wind->maximized_restored_deadline_count;
++wind->window_state_deadline_count;
struct wl_callback *cb = wl_display_sync(_this->internal->display);
wl_callback_add_listener(cb, &maximized_restored_deadline_listener, (void *)((uintptr_t)window->id));
wl_callback_add_listener(cb, &window_state_deadline_listener, (void *)((uintptr_t)window->id));
} else
#endif
// Note that xdg-shell does NOT provide a way to unset minimize!
Expand All @@ -2415,9 +2400,9 @@ void Wayland_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window)
}
xdg_toplevel_unset_maximized(wind->shell_surface.xdg.toplevel.xdg_toplevel);

++wind->maximized_restored_deadline_count;
++wind->window_state_deadline_count;
struct wl_callback *cb = wl_display_sync(_this->internal->display);
wl_callback_add_listener(cb, &maximized_restored_deadline_listener, (void *)((uintptr_t)window->id));
wl_callback_add_listener(cb, &window_state_deadline_listener, (void *)((uintptr_t)window->id));
}
}

Expand Down Expand Up @@ -2477,8 +2462,7 @@ void Wayland_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window)
}

// Not fullscreen, already maximized, and no state pending; nothing to do.
if (!(window->flags & SDL_WINDOW_FULLSCREEN) && (window->flags & SDL_WINDOW_MAXIMIZED) &&
!wind->fullscreen_deadline_count && !wind->maximized_restored_deadline_count) {
if (!(window->flags & SDL_WINDOW_FULLSCREEN) && (window->flags & SDL_WINDOW_MAXIMIZED) && !wind->window_state_deadline_count) {
return;
}

Expand All @@ -2488,27 +2472,33 @@ void Wayland_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window)
return; // Can't do anything yet, wait for ShowWindow
}

// Commit to preserve any pending size data.
wl_surface_commit(wind->surface);
// Commit to preserve any pending viewport size data.
if (wind->pending_client_viewport_dimensions) {
wind->pending_client_viewport_dimensions = false;
wl_surface_commit(wind->surface);
}
libdecor_frame_set_maximized(wind->shell_surface.libdecor.frame);

++wind->maximized_restored_deadline_count;
++wind->window_state_deadline_count;
struct wl_callback *cb = wl_display_sync(viddata->display);
wl_callback_add_listener(cb, &maximized_restored_deadline_listener, (void *)((uintptr_t)window->id));
wl_callback_add_listener(cb, &window_state_deadline_listener, (void *)((uintptr_t)window->id));
} else
#endif
if (wind->shell_surface_type == WAYLAND_SHELL_SURFACE_TYPE_XDG_TOPLEVEL) {
if (wind->shell_surface.xdg.toplevel.xdg_toplevel == NULL) {
return; // Can't do anything yet, wait for ShowWindow
}

// Commit to preserve any pending size data.
wl_surface_commit(wind->surface);
// Commit to preserve any pending viewport size data.
if (wind->pending_client_viewport_dimensions) {
wind->pending_client_viewport_dimensions = false;
wl_surface_commit(wind->surface);
}
xdg_toplevel_set_maximized(wind->shell_surface.xdg.toplevel.xdg_toplevel);

++wind->maximized_restored_deadline_count;
++wind->window_state_deadline_count;
struct wl_callback *cb = wl_display_sync(viddata->display);
wl_callback_add_listener(cb, &maximized_restored_deadline_listener, (void *)((uintptr_t)window->id));
wl_callback_add_listener(cb, &window_state_deadline_listener, (void *)((uintptr_t)window->id));
}
}

Expand Down Expand Up @@ -2745,12 +2735,10 @@ bool Wayland_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Proper
wl_callback_add_listener(data->gles_swap_frame_callback, &gles_swap_frame_listener, data);
}

// No frame callback on external surfaces as it may already have one attached.
if (!external_surface) {
// Fire a callback when the compositor wants a new frame to set the surface damage region.
data->surface_frame_callback = wl_surface_frame(data->surface);
wl_callback_add_listener(data->surface_frame_callback, &surface_frame_listener, data);
}
// Fire a callback when the compositor wants a new frame.
data->surface_wrapper = WAYLAND_wl_proxy_create_wrapper(data->surface);
data->surface_frame_callback = wl_surface_frame(data->surface_wrapper);
wl_callback_add_listener(data->surface_frame_callback, &surface_frame_listener, data);

if (window->flags & SDL_WINDOW_TRANSPARENT) {
if (_this->gl_config.alpha_size == 0) {
Expand Down Expand Up @@ -2882,6 +2870,11 @@ void Wayland_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window)
wind->requested.pixel_height = window->pending.h;
}

if (wind->requested.logical_width != wind->current.logical_width ||
wind->requested.logical_height != wind->current.logical_height) {
wind->pending_client_viewport_dimensions = true;
}

ConfigureWindowGeometry(window);
} else {
// Can't resize the window.
Expand Down Expand Up @@ -3066,7 +3059,7 @@ bool Wayland_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window)

do {
WAYLAND_wl_display_roundtrip(_this->internal->display);
} while (wind->fullscreen_deadline_count || wind->maximized_restored_deadline_count);
} while (wind->window_state_deadline_count);

return true;
}
Expand Down Expand Up @@ -3229,6 +3222,10 @@ void Wayland_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window)
wl_callback_destroy(wind->surface_frame_callback);
}

if (wind->surface_wrapper) {
WAYLAND_wl_proxy_wrapper_destroy(wind->surface_wrapper);
}

if (!(window->flags & SDL_WINDOW_EXTERNAL)) {
wl_surface_destroy(wind->surface);
} else {
Expand Down
5 changes: 3 additions & 2 deletions src/video/wayland/SDL_waylandwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ struct SDL_WindowData
SDL_Window *sdlwindow;
SDL_VideoData *waylandData;
struct wl_surface *surface;
struct wl_surface *surface_wrapper;
struct wl_callback *gles_swap_frame_callback;
struct wl_event_queue *gles_swap_frame_event_queue;
struct wl_surface *gles_swap_frame_surface_wrapper;
Expand Down Expand Up @@ -194,15 +195,15 @@ struct SDL_WindowData
} text_input_props;

SDL_DisplayID last_displayID;
int fullscreen_deadline_count;
int maximized_restored_deadline_count;
int window_state_deadline_count;
Uint64 last_focus_event_time_ns;
int icc_fd;
Uint32 icc_size;
bool floating;
bool suspended;
bool resizing;
bool active;
bool pending_client_viewport_dimensions;
bool drop_interactive_resizes;
bool is_fullscreen;
bool fullscreen_exclusive;
Expand Down
Loading