Skip to content

Introduce save/restore portal#1818

Draft
AdrianVovk wants to merge 2 commits into
flatpak:mainfrom
AdrianVovk:save-restore-portal
Draft

Introduce save/restore portal#1818
AdrianVovk wants to merge 2 commits into
flatpak:mainfrom
AdrianVovk:save-restore-portal

Conversation

@AdrianVovk

@AdrianVovk AdrianVovk commented Sep 25, 2025

Copy link
Copy Markdown
Collaborator

Implements the portal proposed in #1698

Links to other MRs:

Comment thread data/org.freedesktop.portal.SaveRestore.xml Outdated
@Mikenux

Mikenux commented Sep 26, 2025

Copy link
Copy Markdown

About the naming...

Why SaveHint instead of Save, given that it's Save on the SaveRestore interface? Or SaveState?

@AdrianVovk

Copy link
Copy Markdown
Collaborator Author

Why SaveHint instead of Save

I renamed it to make it a bit more clear

given that it's Save on the SaveRestore interface

Sorry, where? It's SaveHint in all the dbus interfaces here

@Mikenux

Mikenux commented Sep 27, 2025

Copy link
Copy Markdown

Sorry, where? It's SaveHint in all the dbus interfaces here

Sorry, mistake.

I renamed it to make it a bit more clear

Relative to? If you are looking for something meaningful, I think SaveState or SaveAppState are better options. Same for the interface name.

@AdrianVovk

Copy link
Copy Markdown
Collaborator Author

Relative to? If you are looking for something meaningful, I think SaveState or SaveAppState are better options. Same for the interface name

Whoops didn't finish typing my comment. SaveHint makes it a bit more clear that it's just a hint and that the app shouldn't be using it as the primary source for deciding to save state. If it was just Save it might seem like the portal will emit the signal regularly or whatever. Anyway, not the most important thing in the world, but that's why I renamed it

@AdrianVovk

Copy link
Copy Markdown
Collaborator Author

CC @davidedmundson and @technobaboo as the two folks from other desktops who I've interacted with in the discussion

This is the real API I ended up implementing. PTAL and let me know if there's some blocker to getting something like this working in your desktop, or you see something missing, or etc.

For your reference, here's GNOME's implementation of this portal's backend: https://gitlab.gnome.org/GNOME/gnome-session/-/blob/avovk/session-save-portal/gnome-session/gsm-session-save.c

@technobaboo

Copy link
Copy Markdown

wait how does the app get the instance ID and know to restore its data using that ID? afaik there's no standard key name for it or such in the registration map it gets as a return arg for registering

@AdrianVovk

Copy link
Copy Markdown
Collaborator Author

how does the app get the instance ID and know to restore its data using that ID

It's there - it's the first arg called instance-id. And it knows to restore data using that ID via the second arg, restore-reason

I'm guessing you might have been looking at org.freedesktop.impl.portal.SaveRestore, which I don't document as thoroughly because I can link to the portal frontend dbus API instead. Check the docs for org.freedesktop.portal.SaveRestore, which should be more complete w.r.t. known keys in the various vardicts

@technobaboo

Copy link
Copy Markdown

okay that's nice to know, so if it's required why isn't it an argument instead of inside the map?

Comment thread data/org.freedesktop.portal.SaveRestore.xml Outdated
Comment thread data/org.freedesktop.portal.SaveRestore.xml Outdated
Comment thread data/org.freedesktop.portal.SaveRestore.xml
@davidedmundson

davidedmundson commented Oct 7, 2025

Copy link
Copy Markdown
Contributor

What's the goal of:

[session: Communicate close reason to impl]

to allow identify crash exits vs clean application shutdowns? Do any other portal impls gain anything from this? If not, we would be better off with a bespoke signal just on the session restore before Close.

@swick swick left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just looked through the PR to understand what is being proposed here and found a bunch of things.

Comment thread data/org.freedesktop.impl.portal.SaveRestore.xml Outdated
Comment thread data/org.freedesktop.impl.portal.Session.xml Outdated
Comment thread src/xdp-session.c Outdated
Comment on lines +215 to +217
else
xdp_dbus_impl_session_call_close_sync (session->impl_session,
NULL, &error);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
else
xdp_dbus_impl_session_call_close_sync (session->impl_session,
NULL, &error);
else
{
xdp_dbus_impl_session_call_close_sync (session->impl_session,
NULL, &error);
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue in multiple places

Comment thread src/xdp-session.c Outdated
Comment thread src/save-restore.h Outdated
Comment thread src/save-restore.c Outdated
Comment thread src/save-restore.c Outdated
Comment thread src/save-restore.c Outdated
Comment thread src/save-restore.c Outdated
{
XdpDbusImplSaveRestore *impl_proxy = XDP_DBUS_IMPL_SAVE_RESTORE (source_object);
g_autofree RegisterData *data = user_data;
g_autoptr (GError) error = NULL;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In xdp and flatpak for some reason the autoptr style is g_autoptr(GError).

This lets the impl differentiate between a session that's closing
because the app voluntarily called `Close()`, or because the app
disconnected from the bus / some other reason.
3 seconds of receiving this signal. The app may be transparently restarted
by the system at a later date.
-->
<signal name="Quit">

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm so I'm not really sure that this is actually necessary...

Instead of asking the app to quit, we can just put it in a cgroup freezer. This removes it from the run queue. We could then adjust the memory limits on the cgroup, which as far as I understand will force the app's memory out to swap and out of RAM. At that point, is there really any point to actually shutting down the app? We can leave the app "running", just frozen in a cgroup freezer and swapped out to disk. If the goal is to free up resources like memory and CPU, we've achieved that.

We can use something like SaveHint to ask the app to save its state just in case it somehow gets killed while in the freezer.

Alternatively, apps should just save their state immediately before quitting in response to something like SIGTERM. Then we could handle this simply by sending SIGTERM to the apps, rather than having them subscribe to this signal.

Comment thread src/save-restore.c
#define XDP_TYPE_SAVE_RESTORE (xdp_save_restore_get_type ())
G_DECLARE_FINAL_TYPE (XdpSaveRestore,
xdp_save_restore,
XDP, SAVE_RESTORE,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm so the USB portal names itself XdpUsb, but apparently most every other portal doesn't have an Xdp prefix. And apparently that was intentional

So, IDK am I supposed to rename this portal to just SaveRestore?

Perhaps if we want to maintain a separation, we could have a Portal prefix, so that all the portals are PortalAccounts, PortalSaveRestore, PortalUsb, etc and the utility classes/functions can keep XdpSession, XdpContext, etc

Comment thread src/save-restore.c

G_DEFINE_TYPE (XdpSaveRestoreSession,
xdp_save_restore_session,
xdp_session_get_type ())

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

XDP_TYPE_SESSION?

Comment thread src/save-restore.c
{
XdpSaveRestoreSession *save_restore_session = XDP_SAVE_RESTORE_SESSION (session);

save_restore_session->closed = TRUE;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a couple of the session implementations do nothing but keep track of this closed variable.

Maybe that should move up into the XdpSession object?

On the other hand, this class separation actually lets us check that the caller is giving us a session path to a session of a type we expect. So we'd need to have a subclass anyway. Or at the very least some sort of accounting of what portal created any given session

Comment thread src/save-restore.c
XdpDbusImplSaveRestore *impl = XDP_DBUS_IMPL_SAVE_RESTORE (source_object);
g_autoptr(RegisterData) data = user_data;
g_autoptr(GError) error = NULL;
g_autoptr(GVariant) out_restore = NULL;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I renamed this to registration

Comment thread src/save-restore.c
g_autoptr(GError) error = NULL;

impl_config = xdp_portal_config_find (config, SAVE_RESTORE_DBUS_IMPL_IFACE);
if (impl_config != NULL)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is backwards.

user opts into this behavior, then it's permissible for the app to
treat ``LAUNCH`` as equivalent to ``RESTORE``.

- ``RECOVER``: 2. This is the first launch after the app unexpectedly

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Split this into two different reasons: recover after compositor crash, and recover after app crash

object path element. See the :ref:`org.freedesktop.portal.Session` documentation for
more information about the session handle.

Supported keys in the @registration vardict include:

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some apps have built-in crash monitoring. Maybe it's useful to communicate to the compositor whether the app has its own crash monitoring

Apps that don't have their own crash monitoring may be auto-restarted by the session (or the session might offer to restart the app). But if apps have their own builtin crash monitoring then the session would just do nothing, under the assumption that the app will restart itself.

Builtin crash monitoring might seem niche, but in practice I suspect it'll increasingly become the norm: systemd has a Restart=on-failure feature for instance, and many apps (especially single instance apps) natrually map to systemd user services (especially on desktops like KDE, which uses transient systemd units for all apps). And desktops implementing a bespoke solution sounds suspiciously like reimplementing parts of systemd.

Perhaps this is better suited as a setting in the app's .desktop file? KDE would use the setting to decide whether Restart=on-failure is passed to the systemd transient unit

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't launching an app supposed to be handled by the system? If so, a system can then ask what to do when an app has crashed. This could also be useful if someone wants to propose a standardized crash reporting tool in the future.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But if apps have their own builtin crash monitoring then the session would just do nothing, under the assumption that the app will restart itself.

What about proper restoration? (position and size) Wayland?

app should periodically save its own state at moments where it makes
sense for the app.
-->
<signal name="SaveHint">

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After talking to David, we decided to drop the SaveHint and Quit methods. Instead, I'd extend the inhibit portal with signals that tell apps about upcoming system suspend and resume

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The inhibit portal allows an app to prevent changes in session status, including its suspension. It has nothing to do with saving and restoring an app's session. Furthermore, SaveHint is vague, implying to desktop environment developers that they can send it whenever they want. Moreover, it provides no information about a user's action (the Settings portal provide some appearance settings, but the less, the better).

Therefore, I don't understand the purpose of this change…

@AdrianVovk

Copy link
Copy Markdown
Collaborator Author

Wanted to document that I spoke to some of Red Hat's Firefox developers and they seemed to be on board with the rough shape of this API


Tells the app which state it needs to restore. Supported values:

- ``PRISTINE``: 0. The app should not attempt to restore any state.

@AdrianVovk AdrianVovk Mar 11, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pristine isn't the most obvious name in the world. Maybe "none" or "disabled" is better?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants