Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DONOTMERGE] wayland: Implement support for surface suspension #4479

Closed
wants to merge 1 commit into from
Closed
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
8 changes: 8 additions & 0 deletions src/video/wayland/SDL_waylandopengles.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,13 @@ Wayland_GLES_SwapWindow(_THIS, SDL_Window *window)
SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
const int swap_interval = _this->egl_data->egl_swapinterval;

if (data->suspension) {
/* For systems with surface suspension exposed we can skip _all_ this
* crap below and go straight to SwapBuffers, EGL will know what to do.
*/
goto present;
}

/* For windows that we know are hidden, skip swaps entirely, if we don't do
* this compositors will intentionally stall us indefinitely and there's no
* way for an end user to show the window, unlike other situations (i.e.
Expand Down Expand Up @@ -143,6 +150,7 @@ Wayland_GLES_SwapWindow(_THIS, SDL_Window *window)
SDL_AtomicSet(&data->swap_interval_ready, 0);
}

present:
/* Feed the frame to Wayland. This will set it so the wl_surface_frame callback can fire again. */
if (!_this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, data->egl_surface)) {
return SDL_EGL_SetError("unable to show color buffer in an OS-native window", "eglSwapBuffers");
Expand Down
6 changes: 6 additions & 0 deletions src/video/wayland/SDL_waylandvideo.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
#include "keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
#include "idle-inhibit-unstable-v1-client-protocol.h"
#include "xdg-activation-v1-client-protocol.h"
#include "surface-suspension-v1-client-protocol.h"

#ifdef HAVE_LIBDECOR_H
#include <libdecor.h>
Expand Down Expand Up @@ -511,6 +512,8 @@ display_handle_global(void *data, struct wl_registry *registry, uint32_t id,
Wayland_add_data_device_manager(d, id, version);
} else if (SDL_strcmp(interface, "zxdg_decoration_manager_v1") == 0) {
d->decoration_manager = wl_registry_bind(d->registry, id, &zxdg_decoration_manager_v1_interface, 1);
} else if (strcmp(interface, "wp_surface_suspension_v1") == 0) {
d->suspension_manager = wl_registry_bind(d->registry, id, &wp_surface_suspension_manager_v1_interface, 1);

#ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
} else if (SDL_strcmp(interface, "qt_touch_extension") == 0) {
Expand Down Expand Up @@ -671,6 +674,9 @@ Wayland_VideoQuit(_THIS)
if (data->decoration_manager)
zxdg_decoration_manager_v1_destroy(data->decoration_manager);

if (data->suspension_manager)
wp_surface_suspension_manager_v1_destroy(data->suspension_manager);

#ifdef HAVE_LIBDECOR_H
if (data->shell.libdecor) {
libdecor_unref(data->shell.libdecor);
Expand Down
1 change: 1 addition & 0 deletions src/video/wayland/SDL_waylandvideo.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ typedef struct {
struct zwp_keyboard_shortcuts_inhibit_manager_v1 *key_inhibitor_manager;
struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager;
struct xdg_activation_v1 *activation_manager;
struct wp_surface_suspension_manager_v1 *suspension_manager;

EGLDisplay edpy;
EGLContext context;
Expand Down
43 changes: 37 additions & 6 deletions src/video/wayland/SDL_waylandwindow.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "xdg-decoration-unstable-v1-client-protocol.h"
#include "idle-inhibit-unstable-v1-client-protocol.h"
#include "xdg-activation-v1-client-protocol.h"
#include "surface-suspension-v1-client-protocol.h"

#ifdef HAVE_LIBDECOR_H
#include <libdecor.h>
Expand Down Expand Up @@ -154,6 +155,26 @@ static const struct wl_callback_listener surface_frame_listener = {
handle_surface_frame_done
};

static void
handle_surface_suspended(void *data, struct wp_surface_suspension_v1 *wp_surface_suspension_v1)
{
SDL_SendWindowEvent((SDL_Window *)data, SDL_WINDOWEVENT_HIDDEN, 0, 0);
}

static void
handle_surface_resumed(void *data, struct wp_surface_suspension_v1 *wp_surface_suspension_v1)
{
/* Send SHOWN, to remove the HIDDEN flag, and EXPOSED, to request a frame */
SDL_Window *window = (SDL_Window *)data;
SDL_SendWindowEvent(window, SDL_WINDOWEVENT_SHOWN, 0, 0);
SDL_SendWindowEvent(window, SDL_WINDOWEVENT_EXPOSED, 0, 0);
}

static const struct wp_surface_suspension_v1_listener suspension_listener = {
handle_surface_suspended,
handle_surface_resumed
};


static void Wayland_HandlePendingResize(SDL_Window *window);

Expand Down Expand Up @@ -1149,12 +1170,18 @@ int Wayland_CreateWindow(_THIS, SDL_Window *window)

SDL_WAYLAND_register_surface(data->surface);

/* Fire a callback when the compositor wants a new frame rendered.
* Right now this only matters for OpenGL; we use this callback to add a
* wait timeout that avoids getting deadlocked by the compositor when the
* window isn't visible.
*/
if (window->flags & SDL_WINDOW_OPENGL) {
if (c->suspension_manager) {
data->suspension = wp_surface_suspension_manager_v1_get_surface_suspension(c->suspension_manager,
data->surface);
wp_surface_suspension_v1_add_listener(data->suspension,
&suspension_listener,
window);
} else if (window->flags & SDL_WINDOW_OPENGL) {
/* Fire a callback when the compositor wants a new frame rendered.
* Right now this only matters for OpenGL; we use this callback to add a
* wait timeout that avoids getting deadlocked by the compositor when the
* window isn't visible.
*/
data->frame_callback = wl_surface_frame(data->surface);
wl_callback_add_listener(data->frame_callback, &surface_frame_listener, data);
}
Expand Down Expand Up @@ -1389,6 +1416,10 @@ void Wayland_DestroyWindow(_THIS, SDL_Window *window)
xdg_activation_token_v1_destroy(wind->activation_token);
}

if (wind->suspension) {
wp_surface_suspension_v1_destroy(wind->suspension);
}

SDL_free(wind->outputs);

if (wind->frame_callback) {
Expand Down
1 change: 1 addition & 0 deletions src/video/wayland/SDL_waylandwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ typedef struct {
struct zwp_keyboard_shortcuts_inhibitor_v1 *key_inhibitor;
struct zwp_idle_inhibitor_v1 *idle_inhibitor;
struct xdg_activation_token_v1 *activation_token;
struct wp_surface_suspension_v1 *suspension;

SDL_atomic_t swap_interval_ready;

Expand Down
102 changes: 102 additions & 0 deletions wayland-protocols/surface-suspension-v1.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="wp_surface_suspension_v1">
<!-- wrap:70 -->

<copyright>
Copyright © 2021 Joshua Ashton

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
</copyright>

<description summary="Surface suspension protocol">
This protocol provides the ability for clients to know when their surfaces
are suspended or resumed via a pair of events indicating surface
suspension and resumption suspension states.

Surfaces start out in the resumed suspension state at creation.

Surfaces may be put into the suspended state by a compositor issuing
the suspended event when the compositor believes they are likely not
to be sent any frame callbacks soon.
Surfaces in suspended state must not be sent any wl_surface.frame events.

Surfaces may be put into the resumed suspension state by a compositor issuing
the resume event.
Surfaces in resumed state may be sent wl_surface.frame events.

Subsurfaces are not affected by their parent surface's suspension state.

Client apps and their windowing/graphics APIs can listen to these events and
handle surface suspension accordingly (eg. releasing graphics resources
such as buffers or suspending work relating to rendering.)
</description>

<interface name="wp_surface_suspension_manager_v1" version="1">
<description summary="surface suspension manager">
The surface suspension manager is a singleton global object that
provides the ability to create wp_surface_suspension_v1 for a
given wl_surface.
</description>

<request name="destroy" type="destructor">
<description summary="destroy the surface suspension manager">
Destroy the surface suspension manager.

Child wp_surface_suspension_v1 objects are not affected
by the destruction of their suspension manager.
</description>
</request>

<request name="get_surface_suspension">
<description summary="get a surface suspension object">
Create a surface suspension interface for a given wl_surface object.
See the wp_surface_suspension_v1 interface for more details.
</description>
<arg name="id" type="new_id" interface="wp_surface_suspension_v1"/>
<arg name="surface" type="object" interface="wl_surface"/>
</request>
</interface>

<interface name="wp_surface_suspension_v1" version="1">
<request name="destroy" type="destructor">
<description summary="release the surface suspension object"/>
</request>

<event name="suspended">
<description summary="surface suspension event">
This event is sent whenever a surface has been suspended.

This puts the surface into the suspended suspension state.

If the surface is currently suspended at the time of the surface
suspension object's creation, this event will be fired immediately.
</description>
</event>

<event name="resumed">
<description summary="surface resumption event">
This event is sent whenever a surface has been resumed.

This puts the surface into the resumed suspension state.
</description>
</event>
</interface>

</protocol>