mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-02-22 10:12:10 +01:00
GApplication: Add RestartData property, and setter
The RestartData property on the org.gtk.Application interface can be used by session services to save some data to disk should the application disappear. The application would call g_application_set_restart_data() when the state of the application changes, with enough information for the application to restart itself in the same "position". FIXME: [ ] API docs [ ] note about restart data size [ ] have one example implementation of the session side One idea would be for xdg-desktop-portal to save a stringified RestartData to ~/.var/app/$ID/config/state.gvariant if none empty when the application disappears from the bus. Questions: - Can we enforce to only do this for unique applications? - Can we detect applications leaving the bus suddenly, or do we want to rely on them clearing RestartData if they're "done" (or should they always save state, *some* state) - Should we implement this via org.gnome.SessionManager.Client and the app calling out to gnome-session instead? - Or is this API fine, and gnome-session can do what xdg-desktop-portal does above?
This commit is contained in:
parent
741a561c4d
commit
3a113699d7
@ -3325,6 +3325,10 @@ g_application_set_option_context_description
|
||||
g_application_set_default
|
||||
g_application_get_default
|
||||
<SUBSECTION>
|
||||
g_application_get_supports_restart_data
|
||||
g_application_consume_restart_data
|
||||
g_application_notify_restart_data_changed
|
||||
<SUBSECTION>
|
||||
g_application_mark_busy
|
||||
g_application_unmark_busy
|
||||
g_application_get_is_busy
|
||||
|
@ -42,6 +42,11 @@
|
||||
#include "glibintl.h"
|
||||
#include "gmarshal-internal.h"
|
||||
|
||||
#ifdef G_OS_UNIX
|
||||
/* For g_unix_signal_source_new() */
|
||||
#include <glib-unix.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
@ -108,6 +113,20 @@
|
||||
* g_application_register()). Unfortunately, this means that you cannot use
|
||||
* g_application_get_is_remote() to decide if you want to register object paths.
|
||||
*
|
||||
* #GApplication supports ‘restart data’ if it is supported by the platform.
|
||||
* This is a way of saving application state at key times, so that the
|
||||
* application can be restored to a similar state the next time it is run. The
|
||||
* state would typically be saved before the user logs out, before the app is
|
||||
* stopped to save resources, or whenever the app significantly changes state
|
||||
* (such as opening a new document). In order to trigger saving state, call
|
||||
* g_application_notify_restart_data_changed(). This function is automatically
|
||||
* called when the process receives a `SIGTERM` signal (on Unix).
|
||||
*
|
||||
* In order to use restart data support, your application must fulfil a few
|
||||
* criteria — see g_application_get_supports_restart_data(). Some #GApplication
|
||||
* subclasses, such as #GtkApplication, may support this transparently by
|
||||
* default.
|
||||
*
|
||||
* GApplication also implements the #GActionGroup and #GActionMap
|
||||
* interfaces and lets you easily export actions by adding them with
|
||||
* g_action_map_add_action(). When invoking an action by calling
|
||||
@ -220,6 +239,10 @@
|
||||
* @handle_local_options: invoked locally after the parsing of the commandline
|
||||
* options has occurred. Since: 2.40
|
||||
* @name_lost: invoked when another instance is taking over the name. Since: 2.60
|
||||
* @build_restart_data: serialize the key parts of the application’s state so
|
||||
* that it can be saved as restart data. Since: 2.78
|
||||
* @consume_restart_data: apply the restart data to the application’s state, to
|
||||
* restore the saved state. Since: 2.78
|
||||
*
|
||||
* Virtual function table for #GApplication.
|
||||
*
|
||||
@ -261,6 +284,10 @@ struct _GApplicationPrivate
|
||||
|
||||
/* Allocated option strings, from g_application_add_main_option() */
|
||||
GSList *option_strings;
|
||||
|
||||
#ifdef G_OS_UNIX
|
||||
GSource *sigterm_source; /* (nullable) (owned) */
|
||||
#endif
|
||||
};
|
||||
|
||||
enum
|
||||
@ -1386,6 +1413,14 @@ g_application_finalize (GObject *object)
|
||||
{
|
||||
GApplication *application = G_APPLICATION (object);
|
||||
|
||||
#ifdef G_OS_UNIX
|
||||
if (application->priv->sigterm_source != NULL)
|
||||
{
|
||||
g_source_destroy (application->priv->sigterm_source);
|
||||
g_clear_pointer (&application->priv->sigterm_source, g_source_unref);
|
||||
}
|
||||
#endif /* G_OS_UNIX */
|
||||
|
||||
if (application->priv->inactivity_timeout_id)
|
||||
g_source_remove (application->priv->inactivity_timeout_id);
|
||||
|
||||
@ -2361,6 +2396,20 @@ g_application_open (GApplication *application,
|
||||
0, files, n_files, hint);
|
||||
}
|
||||
|
||||
#ifdef G_OS_UNIX
|
||||
static gboolean
|
||||
sigterm_cb (gpointer user_data)
|
||||
{
|
||||
GApplication *application = G_APPLICATION (user_data);
|
||||
|
||||
/* Give the application a chance to save its restart data before the SIGTERM
|
||||
* likely causes the application to exit. */
|
||||
g_application_notify_restart_data_changed (application);
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
#endif /* G_OS_UNIX */
|
||||
|
||||
/* Run {{{1 */
|
||||
/**
|
||||
* g_application_run:
|
||||
@ -2569,6 +2618,18 @@ g_application_run (GApplication *application,
|
||||
g_timeout_add (10000, inactivity_timeout_expired, application);
|
||||
}
|
||||
|
||||
#ifdef G_OS_UNIX
|
||||
if (application->priv->is_registered &&
|
||||
!application->priv->is_remote &&
|
||||
g_application_get_supports_restart_data (application))
|
||||
{
|
||||
application->priv->sigterm_source = g_unix_signal_source_new (SIGTERM);
|
||||
g_source_set_callback (application->priv->sigterm_source, sigterm_cb, application, NULL);
|
||||
g_source_attach (application->priv->sigterm_source, context);
|
||||
}
|
||||
#endif /* G_OS_UNIX */
|
||||
|
||||
/* Run until the application is told to quit. */
|
||||
while (application->priv->use_count || application->priv->inactivity_timeout_id)
|
||||
{
|
||||
if (application->priv->must_quit_now)
|
||||
@ -2578,8 +2639,17 @@ g_application_run (GApplication *application,
|
||||
status = 0;
|
||||
}
|
||||
|
||||
/* Shutdown */
|
||||
if (application->priv->is_registered && !application->priv->is_remote)
|
||||
{
|
||||
#ifdef G_OS_UNIX
|
||||
if (application->priv->sigterm_source != NULL)
|
||||
{
|
||||
g_source_destroy (application->priv->sigterm_source);
|
||||
g_clear_pointer (&application->priv->sigterm_source, g_source_unref);
|
||||
}
|
||||
#endif /* G_OS_UNIX */
|
||||
|
||||
g_signal_emit (application, g_application_signals[SIGNAL_SHUTDOWN], 0);
|
||||
|
||||
if (!application->priv->did_shutdown)
|
||||
@ -3140,5 +3210,137 @@ g_application_unbind_busy_property (GApplication *application,
|
||||
g_signal_handler_disconnect (object, handler_id);
|
||||
}
|
||||
|
||||
/* Session handling {{{1 */
|
||||
|
||||
/**
|
||||
* g_application_get_supports_restart_data:
|
||||
* @application: a #GApplication
|
||||
*
|
||||
* Get whether the @application supports saving and loading restart data to
|
||||
* restore its state after being restarted.
|
||||
*
|
||||
* As well as @application supporting restart data, the platform it’s running on
|
||||
* must also support it for the feature to work.
|
||||
*
|
||||
* A #GApplication supports restart data if it
|
||||
* - does not have the %G_APPLICATION_NON_UNIQUE flag,
|
||||
* - implements #GApplicationClass.build_restart_data, and
|
||||
* - implements #GApplicationClass.consume_restart_data.
|
||||
*
|
||||
* Returns: %TRUE if @application supports restart data, %FALSE otherwise
|
||||
* Since: 2.78
|
||||
*/
|
||||
gboolean
|
||||
g_application_get_supports_restart_data (GApplication *application)
|
||||
{
|
||||
GApplicationClass *klass;
|
||||
|
||||
g_return_val_if_fail (G_IS_APPLICATION (application), FALSE);
|
||||
|
||||
klass = G_APPLICATION_GET_CLASS (application);
|
||||
|
||||
return (!(application->priv->flags & G_APPLICATION_NON_UNIQUE) &&
|
||||
klass->build_restart_data != NULL &&
|
||||
klass->consume_restart_data != NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_application_consume_restart_data:
|
||||
* @application: a #GApplication
|
||||
* @tag: (nullable): tag for versioning the data, or %NULL to not version it
|
||||
* @data: (nullable): data to store for the next restart, or %NULL to clear it
|
||||
*
|
||||
* Set the restart data which will be used to restore the application’s state
|
||||
* next time it’s restarted.
|
||||
*
|
||||
* Applications may be restarted after being interrupted by the system to save
|
||||
* resources; or after they crash; or when restarting the computer. This list is
|
||||
* not exhaustive.
|
||||
*
|
||||
* Calling g_application_consume_restart_data() will cause a notification to be
|
||||
* sent to the session manager, which will cause the data to be saved at some
|
||||
* point in the near future. It’s not guaranteed that the data has been saved by
|
||||
* the time this function returns. Depending on system configuration, the data
|
||||
* may not be saved at all. If g_application_consume_restart_data() is called
|
||||
* multiple times, multiple notifications will be sent — this can be used to
|
||||
* request that the restart data is saved after key interactions in your
|
||||
* application’s interface (such as the user changing the set of open documents
|
||||
* or tabs).
|
||||
*
|
||||
* It is an error to call this function if @application does not support restart
|
||||
* data (see g_application_get_supports_restart_data()).
|
||||
*
|
||||
* If @tag is specified, it will be stored alongside the data and returned when
|
||||
* the data is restored. It can be used to detect incompatibilities between the
|
||||
* software and the data. For example, by setting it to the software version, a
|
||||
* restart of an upgraded software version using data stored by an older version
|
||||
* can be detected and handled. No format is mandated for @tag, other than that,
|
||||
* if non-%NULL, it must be non-empty and valid UTF-8.
|
||||
*
|
||||
* If @data is %NULL, any stored restart data will be cleared. @tag will be
|
||||
* ignored.
|
||||
*
|
||||
* If @data is non-%NULL, it may have any type. No type checks are performed on
|
||||
* the data when it is loaded or saved. Applications are responsible for doing
|
||||
* this if needed.
|
||||
*
|
||||
* The size of @data should be at most a few hundred kilobytes. If your
|
||||
* application needs to store more state than this, such as unsaved and
|
||||
* in-progress documents, they should be stored externally (in an autosave
|
||||
* folder, for example), and a path to them stored in @data.
|
||||
*
|
||||
* If @data is a floating #GVariant, it will be consumed.
|
||||
*
|
||||
* Since: 2.78
|
||||
*/
|
||||
void
|
||||
g_application_consume_restart_data (GApplication *application,
|
||||
const char *tag,
|
||||
GVariant *data)
|
||||
{
|
||||
GApplicationClass *klass;
|
||||
|
||||
g_return_if_fail (G_IS_APPLICATION (application));
|
||||
g_return_if_fail (g_application_get_supports_restart_data (application));
|
||||
g_return_if_fail (tag == NULL || *tag != '\0');
|
||||
g_return_if_fail (data != NULL);
|
||||
|
||||
/* Ensure the @data passed to consume_restart_data() is not floating. */
|
||||
g_variant_ref_sink (data);
|
||||
|
||||
klass = G_APPLICATION_GET_CLASS (application);
|
||||
klass->consume_restart_data (application, tag, data);
|
||||
|
||||
g_variant_unref (data);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_application_notify_restart_data_changed:
|
||||
* @application: a #GApplication
|
||||
*
|
||||
* Notify the platform that this application’s restart data has changed
|
||||
* significantly.
|
||||
*
|
||||
* This should be used to notify the platform of significant changes in the
|
||||
* state of the application, such as completing a setup process or changing the
|
||||
* set of open documents.
|
||||
*
|
||||
* The platform may then query the application for its updated restart data and
|
||||
* save it so that it can be loaded again when the application is next started.
|
||||
*
|
||||
* It is an error to call this function if @application does not support restart
|
||||
* data (see g_application_get_supports_restart_data()).
|
||||
*
|
||||
* Since: 2.78
|
||||
*/
|
||||
void
|
||||
g_application_notify_restart_data_changed (GApplication *application)
|
||||
{
|
||||
g_return_if_fail (G_IS_APPLICATION (application));
|
||||
g_return_if_fail (g_application_get_supports_restart_data (application));
|
||||
|
||||
g_application_impl_notify_restart_data_changed (application->priv->impl);
|
||||
}
|
||||
|
||||
/* Epilogue {{{1 */
|
||||
/* vim:set foldmethod=marker: */
|
||||
|
@ -119,8 +119,55 @@ struct _GApplicationClass
|
||||
GVariantDict *options);
|
||||
gboolean (* name_lost) (GApplication *application);
|
||||
|
||||
/**
|
||||
* GApplicationClass::build_restart_data:
|
||||
* @application: a #GApplication
|
||||
* @out_tag: (optional) (nullable) (out) (transfer full): return location for
|
||||
* a tag to version the restart data, or %NULL to ignore; the returned tag
|
||||
* may be %NULL if no restart data is being returned
|
||||
*
|
||||
* Serialize the key parts of the application’s state so that it can be saved
|
||||
* as restart data.
|
||||
*
|
||||
* The returned #GVariant can have any type which is valid to send over D-Bus.
|
||||
*
|
||||
* Returns: (transfer full) (nullable): restart data for @application; this
|
||||
* may be a floating #GVariant
|
||||
*
|
||||
* Since: 2.78
|
||||
*/
|
||||
GVariant *(* build_restart_data) (GApplication *application,
|
||||
char **out_tag);
|
||||
|
||||
/**
|
||||
* GApplicationClass::consume_restart_data:
|
||||
* @application: a #GApplication
|
||||
* @tag: (nullable): a tag to version the restart data, or %NULL if none was
|
||||
* set
|
||||
* @data: (not nullable): the restart data; this is guaranteed to not be
|
||||
* floating
|
||||
*
|
||||
* Apply the restart data to the application’s state, to restore the saved
|
||||
* state.
|
||||
*
|
||||
* This will typically be called early in the lifetime of @application, so
|
||||
* that it can initialize its state correctly according to @data.
|
||||
*
|
||||
* It will not be called if there is no restart data to load.
|
||||
*
|
||||
* Implementations should check that @tag matches what they expect (typically
|
||||
* the current application version), and may discard the @data if there is a
|
||||
* mismatch. This will typically mean that @data dates from a previous version
|
||||
* of the application, and may not be compatible.
|
||||
*
|
||||
* Since: 2.78
|
||||
*/
|
||||
void (* consume_restart_data)(GApplication *application,
|
||||
const char *tag,
|
||||
GVariant *data);
|
||||
|
||||
/*< private >*/
|
||||
gpointer padding[7];
|
||||
gpointer padding[5];
|
||||
};
|
||||
|
||||
GIO_AVAILABLE_IN_ALL
|
||||
@ -252,6 +299,15 @@ void g_application_unbind_busy_property (GApplic
|
||||
gpointer object,
|
||||
const gchar *property);
|
||||
|
||||
GIO_AVAILABLE_IN_2_78
|
||||
gboolean g_application_get_supports_restart_data (GApplication *application);
|
||||
GIO_AVAILABLE_IN_2_78
|
||||
void g_application_consume_restart_data (GApplication *application,
|
||||
const char *tag,
|
||||
GVariant *data);
|
||||
GIO_AVAILABLE_IN_2_78
|
||||
void g_application_notify_restart_data_changed (GApplication *application);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_APPLICATION_H__ */
|
||||
|
@ -67,7 +67,8 @@ static const gchar org_gtk_Application_xml[] =
|
||||
"<arg type='a{sv}' name='platform-data' direction='in'/>"
|
||||
"<arg type='i' name='exit-status' direction='out'/>"
|
||||
"</method>"
|
||||
"<property name='Busy' type='b' access='read'/>"
|
||||
"<property name='Busy' type='b' access='read'/>"
|
||||
"<property name='RestartData' type='sv' access='read'/>"
|
||||
"</interface>"
|
||||
"</node>";
|
||||
|
||||
@ -130,6 +131,8 @@ struct _GApplicationImpl
|
||||
|
||||
static GApplicationCommandLine *
|
||||
g_dbus_command_line_new (GDBusMethodInvocation *invocation);
|
||||
static GVariant *format_restart_data (const char *restart_data_tag,
|
||||
GVariant *restart_data);
|
||||
|
||||
static GVariant *
|
||||
g_application_impl_get_property (GDBusConnection *connection,
|
||||
@ -141,9 +144,32 @@ g_application_impl_get_property (GDBusConnection *connection,
|
||||
gpointer user_data)
|
||||
{
|
||||
GApplicationImpl *impl = user_data;
|
||||
GApplicationClass *class;
|
||||
|
||||
class = G_APPLICATION_GET_CLASS (impl->app);
|
||||
|
||||
if (strcmp (property_name, "Busy") == 0)
|
||||
return g_variant_new_boolean (impl->busy);
|
||||
else if (strcmp (property_name, "RestartData") == 0)
|
||||
{
|
||||
GVariant *restart_data = NULL;
|
||||
char *restart_data_tag = NULL;
|
||||
GVariant *out = NULL;
|
||||
|
||||
/* Interpret a o.fdo.DBus.Properties.Get() call on the property as an
|
||||
* explicit request to update the restart data. */
|
||||
if (class->build_restart_data != NULL)
|
||||
restart_data = class->build_restart_data (impl->app, &restart_data_tag);
|
||||
if (restart_data != NULL)
|
||||
g_variant_take_ref (restart_data);
|
||||
|
||||
out = format_restart_data (restart_data_tag, restart_data);
|
||||
|
||||
g_clear_pointer (&restart_data, g_variant_unref);
|
||||
g_clear_pointer (&restart_data_tag, g_free);
|
||||
|
||||
return g_steal_pointer (&out);
|
||||
}
|
||||
|
||||
g_assert_not_reached ();
|
||||
|
||||
@ -151,14 +177,16 @@ g_application_impl_get_property (GDBusConnection *connection,
|
||||
}
|
||||
|
||||
static void
|
||||
send_property_change (GApplicationImpl *impl)
|
||||
send_property_change (GApplicationImpl *impl,
|
||||
const char *property_name,
|
||||
GVariant *value)
|
||||
{
|
||||
GVariantBuilder builder;
|
||||
|
||||
g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
|
||||
g_variant_builder_add (&builder,
|
||||
"{sv}",
|
||||
"Busy", g_variant_new_boolean (impl->busy));
|
||||
property_name, value);
|
||||
|
||||
g_dbus_connection_emit_signal (impl->session_bus,
|
||||
NULL,
|
||||
@ -594,10 +622,45 @@ g_application_impl_set_busy_state (GApplicationImpl *impl,
|
||||
if (impl->busy != busy)
|
||||
{
|
||||
impl->busy = busy;
|
||||
send_property_change (impl);
|
||||
send_property_change (impl, "Busy", g_variant_new_boolean (impl->busy));
|
||||
}
|
||||
}
|
||||
|
||||
static GVariant *
|
||||
format_restart_data (const char *restart_data_tag,
|
||||
GVariant *restart_data)
|
||||
{
|
||||
/* The wire format of ‘no restart data’ is `("", <"">)`, since D-Bus doesn’t
|
||||
* currently support maybe types. */
|
||||
gboolean no_data = (restart_data == NULL);
|
||||
return g_variant_new ("(sv)",
|
||||
(no_data || restart_data_tag == NULL) ? "" : restart_data_tag,
|
||||
no_data ? g_variant_new_string ("") : restart_data);
|
||||
}
|
||||
|
||||
void
|
||||
g_application_impl_notify_restart_data_changed (GApplicationImpl *impl)
|
||||
{
|
||||
GApplicationClass *app_class = G_APPLICATION_GET_CLASS (impl->app);
|
||||
GVariant *restart_data = NULL;
|
||||
char *restart_data_tag = NULL;
|
||||
GVariant *out = NULL;
|
||||
|
||||
if (app_class->build_restart_data != NULL)
|
||||
restart_data = app_class->build_restart_data (impl->app, &restart_data_tag);
|
||||
if (restart_data != NULL)
|
||||
g_variant_take_ref (restart_data);
|
||||
|
||||
out = format_restart_data (restart_data_tag, restart_data);
|
||||
|
||||
g_clear_pointer (&restart_data, g_variant_unref);
|
||||
g_clear_pointer (&restart_data_tag, g_free);
|
||||
|
||||
send_property_change (impl, "RestartData", out);
|
||||
|
||||
g_variant_unref (out);
|
||||
}
|
||||
|
||||
void
|
||||
g_application_impl_destroy (GApplicationImpl *impl)
|
||||
{
|
||||
|
@ -63,3 +63,4 @@ const gchar * g_application_impl_get_dbus_object_path (GApplic
|
||||
|
||||
void g_application_impl_set_busy_state (GApplicationImpl *impl,
|
||||
gboolean busy);
|
||||
void g_application_impl_notify_restart_data_changed (GApplicationImpl *impl);
|
||||
|
Loading…
x
Reference in New Issue
Block a user