summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/reference/gio/gio-sections-common.txt4
-rw-r--r--gio/gapplication.c178
-rw-r--r--gio/gapplication.h63
-rw-r--r--gio/gapplicationimpl-dbus.c75
-rw-r--r--gio/gapplicationimpl.h4
5 files changed, 284 insertions, 40 deletions
diff --git a/docs/reference/gio/gio-sections-common.txt b/docs/reference/gio/gio-sections-common.txt
index 951d1477b..a4d19f5c3 100644
--- a/docs/reference/gio/gio-sections-common.txt
+++ b/docs/reference/gio/gio-sections-common.txt
@@ -3322,7 +3322,9 @@ g_application_set_option_context_description
g_application_set_default
g_application_get_default
<SUBSECTION>
-g_application_set_restart_data
+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
diff --git a/gio/gapplication.c b/gio/gapplication.c
index 0e4bd062f..1bb50daa2 100644
--- a/gio/gapplication.c
+++ b/gio/gapplication.c
@@ -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
@@ -216,6 +235,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.
*
@@ -257,6 +280,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
@@ -1382,6 +1409,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);
@@ -2357,6 +2392,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:
@@ -2565,6 +2614,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)
@@ -2574,8 +2635,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)
@@ -3139,7 +3209,39 @@ g_application_unbind_busy_property (GApplication *application,
/* Session handling {{{1 */
/**
- * g_application_set_restart_data:
+ * 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
@@ -3151,9 +3253,18 @@ g_application_unbind_busy_property (GApplication *application,
* resources; or after they crash; or when restarting the computer. This list is
* not exhaustive.
*
- * Applications which are not unique (%G_APPLICATION_NON_UNIQUE is set) do not
- * support restart data, and it is an error to call this function for such
- * applications.
+ * 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
@@ -3165,21 +3276,66 @@ g_application_unbind_busy_property (GApplication *application,
* If @data is %NULL, any stored restart data will be cleared. @tag will be
* ignored.
*
- * If @data is a floating #GVariant, it will be consumed.
+ * 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.72
+ * Since: 2.78
*/
void
-g_application_set_restart_data (GApplication *application,
- const gchar *tag,
- GVariant *data)
+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 (!(application->priv->flags & G_APPLICATION_NON_UNIQUE));
+ 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_set_restart_data (application->priv->impl, tag, data);
+ g_application_impl_notify_restart_data_changed (application->priv->impl);
}
/* Epilogue {{{1 */
diff --git a/gio/gapplication.h b/gio/gapplication.h
index 1c971e1d7..cdd3b799b 100644
--- a/gio/gapplication.h
+++ b/gio/gapplication.h
@@ -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
@@ -234,11 +281,6 @@ void g_application_unmark_busy (GApplic
GIO_AVAILABLE_IN_2_44
gboolean g_application_get_is_busy (GApplication *application);
-GIO_AVAILABLE_IN_2_72
-void g_application_set_restart_data (GApplication *application,
- const gchar *tag,
- GVariant *data);
-
GIO_AVAILABLE_IN_2_40
void g_application_send_notification (GApplication *application,
const gchar *id,
@@ -257,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__ */
diff --git a/gio/gapplicationimpl-dbus.c b/gio/gapplicationimpl-dbus.c
index 36f8b0fda..a0778a0b1 100644
--- a/gio/gapplicationimpl-dbus.c
+++ b/gio/gapplicationimpl-dbus.c
@@ -67,8 +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='RestartData' type='sv' access='read'/>"
+ "<property name='Busy' type='b' access='read'/>"
+ "<property name='RestartData' type='sv' access='read'/>"
"</interface>"
"</node>";
@@ -126,14 +126,13 @@ struct _GApplicationImpl
gboolean busy;
gboolean registered;
GApplication *app;
-
- gchar *restart_data_tag; /* (owned) (nullable) */
- GVariant *restart_data; /* (owned) (nullable) */
};
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,
@@ -145,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 ();
@@ -604,21 +626,39 @@ g_application_impl_set_busy_state (GApplicationImpl *impl,
}
}
+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_set_restart_data (GApplicationImpl *impl,
- const gchar *tag,
- GVariant *data)
+g_application_impl_notify_restart_data_changed (GApplicationImpl *impl)
{
- g_clear_pointer (&impl->restart_data_tag, g_free);
- g_clear_pointer (&impl->restart_data, g_variant_unref);
+ GApplicationClass *app_class = G_APPLICATION_GET_CLASS (impl->app);
+ GVariant *restart_data = NULL;
+ char *restart_data_tag = NULL;
+ GVariant *out = NULL;
- if (data != NULL && tag != NULL)
- impl->restart_data_tag = g_strdup (tag);
- if (data != NULL)
- impl->restart_data = g_variant_ref_sink (data);
+ 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);
- send_property_change (impl, "RestartData",
- g_variant_new ("sv", impl->restart_data_tag, impl->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
@@ -631,9 +671,6 @@ g_application_impl_destroy (GApplicationImpl *impl)
g_free (impl->object_path);
- g_free (impl->restart_data_tag);
- g_clear_pointer (&impl->restart_data, g_variant_unref);
-
g_slice_free (GApplicationImpl, impl);
}
diff --git a/gio/gapplicationimpl.h b/gio/gapplicationimpl.h
index 68d3e4559..46d7817d4 100644
--- a/gio/gapplicationimpl.h
+++ b/gio/gapplicationimpl.h
@@ -63,6 +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_set_restart_data (GApplicationImpl *impl,
- const gchar *tag,
- GVariant *data);
+void g_application_impl_notify_restart_data_changed (GApplicationImpl *impl);