diff options
-rw-r--r-- | docs/reference/gio/gio-sections-common.txt | 4 | ||||
-rw-r--r-- | gio/gapplication.c | 178 | ||||
-rw-r--r-- | gio/gapplication.h | 63 | ||||
-rw-r--r-- | gio/gapplicationimpl-dbus.c | 75 | ||||
-rw-r--r-- | gio/gapplicationimpl.h | 4 |
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); |