Session suspension and restoration protocol
Roman Gilg
subdiff at gmail.com
Mon Jun 18 15:05:25 UTC 2018
Hi all,
I have worked the past few days on a protocol for client session
suspension and restoration and I need to get some feedback if I'm
running in the right direction. I did get some input from Sway and
GNOME devs at the start on what they would want such a protocol to do
in general, and I did try to respect that when working on the
protocol. Main features of the protocol as pasted below are:
* provides an extensible object creation mechanism to let the
compositor announce support for a subset of three increasing levels of
suspension and let the client opt-in to some or all of these supported
suspension levels
* these three levels are:
1. sleep: allows destroying surfaces and their later restoration
2. quit: allows connection closing and restart of the client by the
compositor via an implementation-independent D-Bus interface
3. shutdown: client will get restored after a server shutdown in a
new Wayland session via the same D-Bus interface.
* using D-Bus interface only to secure against sandboxed clients
* if the client opts-in to level 2, 3 or both, the compositor might
try to also restore the client after a client or compositor crash
* the program flow would be always:
1. compositor announces supported suspension levels
2. client opts-in to one or several of the supported suspension
levels by creating protocol objects for the respective levels
3. when the compositor wants to suspend the client (for example on
low memory or shutdown) it sends an event to the respective
suspension object, which the client can acknowledge or it must
destroy the object to cancel the suspension.
So while client and server agree on a subset of usable suspension
levels, in the end only the compositor activates a suspension.
The protocol is written from scratch, but it shares some similarities
with Mike's proposed xdg-session-management protocol
(https://lists.freedesktop.org/archives/wayland-devel/2018-February/037236.html).
In advance thank you for your feedback!
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="session_suspension_v1">
<copyright>
Copyright © 2018 Roman Gilg
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="suspend and restore clients with their surfaces">
A protocol for suspending and restoring clients. Multiple modes allow
clients and server to match their common support for different levels of
suspension from suspending and restoring only the client's surfaces until
full client restoration after a system reboot.
Warning! The protocol described in this file is experimental and backward
incompatible changes may be made. Backward compatible changes may be added
together with the corresponding interface version bump. Backward
incompatible changes are done by bumping the version number in the protocol
and interface names and resetting the interface version. Once the protocol
is to be declared stable, the 'z' prefix and the version number in the
protocol and interface names are removed and the interface version number
is reset.
</description>
<interface name="zxdg_session_suspension_manager" version="1">
<description summary="restores clients for session restarts">
A compositor implementing this interface announces that it is capable of
some form of session suspension.
Clients can register themselves in order to get more information about
the available suspension levels and decide which ones of these they want
to support.
Clients register themselves in two steps by first sending the
register_session_suspension request and then creating other child
interfaces relative to which levels of restoration they and the
compositor agree on to support.
In the current version the protocol supports suspension of surfaces
and their subsequent restoration while keeping the Wayland connection
between client and server alive, restoration after the client quits the
connection voluntarily or by crashing and restoration at the beginning of
a new compositor session after a compositor shutdown by regular means or
because of a compositor crash.
Here a compositor session is defined as the full lifespan of the Wayland
server connection being available to clients, while a client session is
the period in which a specific client server connection exists.
</description>
<enum name="error">
<entry name="already_exists" value="0"
summary="an object for the reference already exists"/>
<entry name="defunct_children" value="1"
summary="parent object destroyed before child objects"/>
<entry name="suspension_violation" value="1"
summary="unallowed request sent while session suspended"/>
</enum>
<request name="register_session_suspension">
<description summary="client shows interest in session suspension">
Returns a zxdg_session_suspension object, which registers the client to
the session suspension and restoration mechanism of the compositor.
With its child interfaces it enables the client to register itself to
the different levels of restoration supported by the compositor.
In certain high levels the compositor will ask the client through a
special D-Bus interface to restore its internal state. This interface
must be made available by the client on the address specified by the
desktop_id argument. For this form of restoration a D-Bus activation
service should be provided by the client such that the compositor can
dbus-activate the client with a call to the interface. More information
on desktop file IDs and D-Bus Activation can be found in the Desktop
Entry Specification [0].
The special interface name for the D-Bus call is
zxdg_session_suspension_v1 and the method being called by the
compositor on this interface is simply called 'restore' which takes an
uint32 token set by the compositor, which is announced to the client
through the zxdg_session_suspension.registered event.
The client needs to remember this token, because by that it can identify
the internal data it needs to restore after the D-Bus call has been
received.
For example a client provides the desktop_id org.foo.bar and receives
the client_id 1234 through the zxdg_session_suspension.registered event.
If the compositor supports the shutdown level of restoration and the
client opts-in to it, then after the compositor shuts down the Wayland
server with the restoration information for this client being retained,
the compositor or some session manager process mandated by the
compositor will issue on the next Wayland server launch the D-Bus
command:
org.foo.bar /zxdg_session_suspension_v1 restore(1234)
On Wayland server launch the application org.foo.bar is most likely not
running, so it gets D-Bus activated through the respective service
file. After it has been activated, the method call will be delivered to
the application and it can act upon the restoration request by loading
former internal data and asking the compositor to restore its surfaces.
To restore a previous client session a restore_id argument can be
provided to this call, which must match the client_id received in the
last client session.
If for this client a zxdg_session_suspension object already exists,
calling this request will result in a protocol error.
[0] https://specifications.freedesktop.org/desktop-entry-spec
</description>
<arg name="id" type="new_id" interface="zxdg_session_suspension"
summary="register a new restore session"/>
<arg name="restore_id" type="uint"
summary="for restoration must match client_id of previous session"/>
<arg name="desktop_id" type="string" allow-null="true"
summary="desktop file id to restore the client via d-bus"/>
</request>
</interface>
<interface name="zxdg_session_suspension" version="1">
<description summary="show interest in session suspension">
The main session suspension object, created by the client in order opt-in
to the compositor suspension and restoration mechanism.
After the object has been created the server sends the registered event,
which tells the client what levels of suspension the server supports.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the object">
Used by the client to notify the server that it will no longer use this
object.
Sending this request before all child objects have been destroyed is
illegal and a protocol error will be raised.
</description>
</request>
<request name="register_sleep">
<description summary="opt-in to client session sleeping">
Creates a zxdg_session_sleep object. As long as this object exists, the
client is registered on the server side as being ready to go into the
sleep state.
</description>
<arg name="id" type="new_id" interface="zxdg_session_sleep"
summary="client session sleeping object"/>
</request>
<request name="register_quit">
<description summary="opt-in to client session quitting">
Creates a zxdg_session_quit object. As long as this object exists, the
client is registered on the server side as being ready to go into the
quit state, which means that it is able to quit its Wayland connection
and it will be restored via D-Bus when the server sees fit.
A client therfore should not call this request if it did not provide a
desktop_id when creating the zxdg_session_suspension interface.
</description>
<arg name="id" type="new_id" interface="zxdg_session_quit"
summary="client session quitting object"/>
</request>
<request name="register_shutdown">
<description summary="opt-in to compositor session shutdown restoration">
Creates a zxdg_session_shutdown object. As long as this object exists,
the client is registered on the server side as being ready to go into
the shutdown state when the compositor kills the Wayland server for
getting restored in a new compositor session via D-Bus.
A client therfore should not call this request if it did not provide a
desktop_id when creating the zxdg_session_suspension interface.
</description>
<arg name="id" type="new_id" interface="zxdg_session_shutdown"
summary="server shutdown restoration object"/>
</request>
<request name="register_surface">
<description summary="track the surface data for future restoration">
Asks the compositor to track the xdg_toplevel surface for restoration.
The compositor will provide a zxdg_session_surface object.
Calling this request while the client is in a suspension state results
in a protocol error.
Calling this request on an xdg_toplevel for which a zxdg_session_surface
object already exists results in a protocol error.
</description>
<arg name="id" type="new_id" interface="zxdg_session_surface"
summary="session surface object"/>
<arg name="surface" type="object" interface="xdg_toplevel"/>
</request>
<request name="restore_surface">
<description summary="ask to restore the surface">
Requests the provided xdg_toplevel surface to be restored with the data
the compositor has saved for the surface_restore_id in combination with
the restore_id set earlier in the call to create the
zxdg_session_suspension object.
If the compositor does not find any data for this restore_id,
surface_restore_id combination, it will ignore the request without
further notice.
This request must be sent before the first commit to the xdg_toplevel
and before a register_surface request for the surface has been sent,
otherwise the request will be ignored by the compositor.
Calling this request while the client is in a suspension state results
in a protocol error.
If the compositor applies the saved settings associated with the
surface_restore_id it will eventually only send a normal configure
event to the xdg_toplevel.
</description>
<arg name="surface" type="object" interface="xdg_toplevel"/>
<arg name="surface_restore_id" type="uint"
summary="must match surface_id from before suspension"/>
</request>
<enum name="suspension_level" bitfield="true">
<description summary="TODO">
Enum bitfield of session suspension levels. Used in the registered
event to indicate which levels the compositor supports.
</description>
<entry name="sleep" value="0">
</entry>
<entry name="quit" value="1">
</entry>
<entry name="shutdown" value="2">
</entry>
</enum>
<event name="registered">
<description summary="client state being tracked">
Announces the supported levels of session suspension.
Under normal circumstances also provies the unique identifier
client_id, that is used to restore the session via D-Bus.
But the compositor might at any time decide to ultimately no longer
allow this client to be suspended and restored through this object.
In this case the compositor will send this event with 0 as client_id
argument. Then the zxdg_session_suspension object and all child objects
are no longer functional and should be deleted. An active suspension is
broken and the restoration data is lost. Doing this is therefore a very
severe step to take and a compositor should in general try to avoid it
or do it directly on creation of the zxdg_session_suspension object.
In general client_id 0 will be sent, when the client is in a state,
that is unset through the D-Bus signal.
</description>
<arg name="client_id" type="uint"
summary="unique id larger 0 for later restore, 0 for defunct"/>
<arg name="supported_levels" type="uint" enum="level"
summary="bit mask of supported restoration levels"/>
</event>
</interface>
<interface name="zxdg_session_sleep" version="1">
<description summary="opt-in to client sleep">
While this object exists, the client is registered to the server as being
ready to go into the sleep suspension state.
The sleep suspension state is active after the client has received the
sleep event and untill it receives the wakeup event or it destroys this
object.
If the client registered to a higher level of suspension as well, it must
be ready to go directly from this state into the higher level without
receiving the wakeup event before and therefore without restoring its
surfaces in between. The sleep state is implicitly unset in this case.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the sleep object">
Used by the client to notify the server that it will no longer use this
object. That means the client can no longer go into the sleep state via
this object.
If the sleep state is active while this request is sent the client will
have the opportunity to recover its suspended surfaces.
A sleep event sent at the same time the client requests destruction has
no further implications besides the client being not in the sleep state
and not being ready for it anymore.
</description>
</request>
<event name="sleep">
<description summary="server wants the client to go to sleep">
By sending this event the server puts the client into the sleep state.
In the sleep state the client is encouraged to destroy its surfaces and
restore them again after the client left the sleep state either when
receiving the wakeup event or by destroying the object.
The reason for sending out this event might be that the client is at
the moment hidden and the server wants to save system ressources.
For example this might be sensible on an embedded system with a single
application interface or in a high powered system, but with multiple
virtual screens.
After this event has been received the client can destroy surfaces,
which had been registered through register_surface securely and expect
the compositor to restore their position and geometry after the
suspension has been reverted again.
</description>
</event>
<event name="wakeup">
<description summary="compositor needs client to be restored">
This event cancels the sleep state. Afterwards the client must restore
its destroyed surfaces. Otherwise its visual depiction to the user is
most likely broken.
</description>
</event>
</interface>
<interface name="zxdg_session_quit" version="1">
<description summary="opt-in to connection quitting">
While this object exists, the client is registered to the server as being
ready to go into the quit suspension state.
The quit state becomes active, when the client responds with an ack
request on receiving the quit event. The client can cancel going
into the quit state by sending the destroy request instead of ack. If the
client does not react to the quit event the compositor might deem it
broken and cancel any suspension by sending the client_id 0 through the
registered event of the zxdg_session_suspension interface. In order to
make sure that the server did not deem the client broken, the client
should therefore listen for one more event cycle that this did not
happen before destroying the zxdg_session_suspension interface.
In the quit state the client is only allowed any more to destroy objects
of the zxdg_session_suspension interface. Any other request on the
interface or on one of its child interfaces results in a protocol error.
At the moment the quit state becomes active, the compositor saves the
state of the registered surfaces for later restoration. Any later commits
will be lost.
The quit state ends when the compositor sends the D-Bus signal to
restart the client.
When this object exists, the compositor might try to restore a client via
the D-Bus signal in case of a client or compositor crash.
If the client was registered to the shutdown level of suspension as well,
it must be ready to go directly from this state into the shutdown state
and without receiving over events on the zxdg_session_suspension
interface and its child interfaces or a D-Bus signal to ressurect before
that. In this case the quit state is implicitly unset and the client is
restarted only in a new Wayland session and possibly after a reboot of
the whole system.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the quitting object">
Used by the client to notify the server that the client will no longer
use this object.
</description>
</request>
<request name="ack">
<description summary="acknowledge a quit event">
Respond to a quit event. At the moment this request is sent, the quit
state is entered.
It is up to the client to kill its Wayland connection after this
request has been sent.
</description>
</request>
<event name="quit">
<description summary="server wants client to quit and restore it later">
By sending this event the server tells the client that it wants the
client to go into the quit state. The client can accept this by
responding with the ack request or refuse by destroying the object.
Not reacting timely will result in the compositor deeming the client
broken and the cancellation of any suspension registration by sending
client_id 0 through the registered event of the zxdg_session_suspension
interface.
The reason for sending out this event might be that the client is at
the moment hidden and the server wants to save system ressources.
After this event has been sent the zxdg_session object and all its
child objects become defunct and should only be destroyed any more.
</description>
</event>
</interface>
<interface name="zxdg_session_shutdown" version="1">
<description summary="opt-in to restoration in new compositor session">
While this object exists, the client is registered to the server as being
ready to go into the shutdown suspension state.
The shutdown state becomes active, when the client responds with an ack
request on receiving the shutdown event. The client can cancel going
into the shutdown state by sending the destroy request instead of ack. If
the client does not react to the shutdown event the compositor might deem
it broken and cancel any suspension by sending the client_id 0 through
the registered event of the zxdg_session_suspension interface. In order
to make sure that the server did not deem the client broken, the client
should therefore listen for one more event cycle that this did not happen
before destroying the zxdg_session_suspension interface.
In the shutdown state the client is only allowed any more to destroy
objects of the zxdg_session_suspension interface or resend the ack event.
Any other request on the interface or one of its child interfaces results
in a protocol error.
At the moment the shutdown state becomes active, the compositor saves the
state of the registered surfaces for later restoration. Any later commits
will be lost.
The shutdown state ends when the compositor sends the D-Bus signal to
restart the client.
When this object exists, the compositor might try to restore a client via
the D-Bus signal in case of a client or compositor crash.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the suspension object">
Used by the client to notify the server that the client will no longer
use this object.
</description>
</request>
<enum name="desire">
<entry name="proceed" value="1"/>
<entry name="hold" value="2"/>
</enum>
<request name="ack">
<description summary="ask for shutdown delay">
Respond to a shutdown event. At the moment this request is sent, the
shutdown state is entered.
The client has the chance to delay the compositor killing the Wayland
server by replying with the hold enum value.
The client is expected to send ack again with the proceed value to let
the compositor know that it finished its remaining cleanup tasks and
the compositor can continue shutting down.
The compositor is not obliged to honor any hold request or wait till
the client has resent the signal with the proceed enum before shutting
down the connection. If the compositor overrules the hold, it should
resend the shutdown event to holding clients.
Holding the shutdown is not meant as a method to block for a prolonged
time, but only for a few seconds. Compositors are encouraged to
overrule any client holding the shutdown longer than 3-4 seconds.
</description>
<arg name="desire" type="uint" enum="desire"
summary="indicate desire to proceed or hold"/>
</request>
<event name="shutdown">
<description summary="server shutdown, ask for restoration">
Announces that the compositor will shutdown the server soon and that it
wants to restore the client on next server start with current surface
states being recoverable.
If there are registered clients for shutdown suspension after this
event has been sent the compositor is expected to stall the killing of
client connections for one second in order to give registered clients
the opportunity to send back their ack requests.
</description>
</event>
</interface>
<interface name="zxdg_session_surface" version="1">
<description summary="track a surface for future restoration">
Provides means to control the restoration of an xdg_toplevel through the
provided surface_id in combination with the client_id received by the
zxdg_session_suspension object.
If the associated xdg_toplevel is destroyed while this object persists
and no suspension state is active the object becomes defunct and no
restoration information is memorized by the compositor about the
surface. The client should destroy the object.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the session object">
Used by the client to notify the server that it will no longer use this
object. If no suspension state is set while this request is sent, no
restoration data about this surface is memorized by the compositor.
</description>
</request>
<event name="registered">
<description summary="surface state being tracked">
Informs the client under normal cirumstances that the xdg_toplevel
surface state will be tracked from now on. For that it provides the
unique identifier surface_id, which the client is supposed to use on
next launch in order to restore the surface state with the
restore_surface request.
But the compositor might at any time decide to ultimately no longer
track this surface. In this case the compositor will send the event
with surface_id 0. The client should destroy the object afterwards.
</description>
<arg name="surface_id" type="uint"
summary="unique id for later restore"/>
</event>
</interface>
</protocol>
More information about the wayland-devel
mailing list