diff options
author | Matthias Clasen <mclasen@redhat.com> | 2011-12-17 15:37:29 -0500 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2011-12-17 20:33:26 -0500 |
commit | def83cd90925caf2137b5a2099f3eb17070e227d (patch) | |
tree | cf3c9777962547428705410cd29ee7b9ef4d5194 | |
parent | 0971d36e4b8338f4f3f96d751f5275517514d3b1 (diff) | |
download | glib-wip/session.tar.gz |
Add session supportwip/session
This is a port of the eggsmclient dbus implementation,
plus an inhibit api.
-rw-r--r-- | docs/reference/gio/gio-sections.txt | 6 | ||||
-rw-r--r-- | gio/gapplication.c | 223 | ||||
-rw-r--r-- | gio/gapplication.h | 18 | ||||
-rw-r--r-- | gio/gapplicationimpl-dbus.c | 268 | ||||
-rw-r--r-- | gio/gapplicationimpl.h | 30 | ||||
-rw-r--r-- | gio/gio.symbols | 5 | ||||
-rw-r--r-- | gio/gioenums.h | 26 |
7 files changed, 572 insertions, 4 deletions
diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt index 7acd2ce2f..3d94ea1bf 100644 --- a/docs/reference/gio/gio-sections.txt +++ b/docs/reference/gio/gio-sections.txt @@ -2834,6 +2834,12 @@ g_application_open g_application_run g_application_set_default g_application_get_default +<SUBSECTION> +g_application_quit_response +GApplicationInhibitFlags +g_application_inhibit +g_application_uninhibit +g_application_is_inhibited <SUBSECTION Standard> G_TYPE_APPLICATION G_APPLICATION diff --git a/gio/gapplication.c b/gio/gapplication.c index a4e6185e6..577056d3e 100644 --- a/gio/gapplication.c +++ b/gio/gapplication.c @@ -239,6 +239,9 @@ enum SIGNAL_OPEN, SIGNAL_ACTION, SIGNAL_COMMAND_LINE, + SIGNAL_QUIT_REQUESTED, + SIGNAL_QUIT_CANCELLED, + SIGNAL_QUIT, NR_SIGNALS }; @@ -353,6 +356,9 @@ static void g_application_real_startup (GApplication *application) { application->priv->did_startup = TRUE; + + if (!(application->priv->flags & G_APPLICATION_NO_SESSION)) + g_application_impl_session_startup (application->priv->impl); } static void @@ -952,6 +958,73 @@ g_application_class_init (GApplicationClass *class) NULL, G_TYPE_INT, 1, G_TYPE_APPLICATION_COMMAND_LINE); + /** + * GApplication::quit-requested: + * @application: the #GApplication + * + * Emitted when the session manager requests that the application + * exit (generally because the user is logging out). The application + * should decide whether or not it is willing to quit and then call + * g_application_quit_response(), passing %TRUE or %FALSE to give its + * answer to the session manager. It does not need to give an answer + * before returning from the signal handler; the answer can be given + * later on, but <emphasis>the application must not attempt to perform + * any actions or interact with the user</emphasis> in response to + * this signal. Any actions required for a clean shutdown should take + * place in response to the #GApplication::quit signal. + * + * The application should limit its operations until either the + * #GApplication::quit or #GApplication::quit-cancelled signals is + * emitted. + * + * If the application does not connect to this signal, then + * #GApplication will automatically return %TRUE on its behalf. + * + * Since: 2.32 + */ + g_application_signals[SIGNAL_QUIT_REQUESTED] = + g_signal_new ("quit-requested", G_TYPE_APPLICATION, G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GApplicationClass, quit_requested), + NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + + /** + * GApplication::quit-cancelled: + * @application: the #GApplication + * + * Emitted when the session manager decides to cancel a logout after + * the application has already agreed to quit. After receiving this + * signal, the application can go back to what it was doing before + * receiving the #GApplication::quit-requested signal. + * + * Since: 2.32 + */ + g_application_signals[SIGNAL_QUIT_CANCELLED] = + g_signal_new ("quit-cancelled", G_TYPE_APPLICATION, G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GApplicationClass, quit_cancelled), + NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + + /** + * GApplication::quit: + * @application: the #GApplication + * + * Emitted when the session manager wants the application to quit + * (generally because the user is logging out). The application + * should exit as soon as possible after receiving this signal; if + * it does not, the session manager may choose to forcibly kill it. + * + * Normally a GUI application would only be sent a ::quit if it + * agreed to quit in response to a #GApplication::quit-requested + * signal. However, this is not guaranteed; in some situations the + * session manager may decide to end the session without giving + * applications a chance to object. + * + * Since: 2.32 + */ + g_application_signals[SIGNAL_QUIT] = + g_signal_new ("quit", G_TYPE_APPLICATION, G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GApplicationClass, quit), + NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + g_type_class_add_private (class, sizeof (GApplicationPrivate)); } @@ -1810,6 +1883,156 @@ g_application_action_map_iface_init (GActionMapInterface *iface) iface->remove_action = g_application_remove_action; } +/* Session Management {{{1 */ + +void +g_application_emit_quit_requested (GApplication *application) +{ + if (!g_signal_has_handler_pending (application, g_application_signals[SIGNAL_QUIT_REQUESTED], 0, FALSE)) + { + g_debug ("Not emitting quit_requested because no one is listening"); + g_application_quit_response (application, TRUE, NULL); + return; + } + + g_debug ("Emitting quit-requested"); + g_signal_emit (application, g_application_signals[SIGNAL_QUIT_REQUESTED], 0); + g_debug ("Done emitting quit-requested"); +} + +void +g_application_emit_quit (GApplication *application) +{ + g_debug ("Emitting quit"); + g_signal_emit (application, g_application_signals[SIGNAL_QUIT], 0); + g_debug ("Done emitting quit"); +} + +void +g_application_emit_quit_cancelled (GApplication *application) +{ + g_debug ("Emitting quit-cancelled"); + g_signal_emit (application, g_application_signals[SIGNAL_QUIT_CANCELLED], 0); + g_debug ("Done emitting quit-cancelled"); +} + +/** + * g_application_quit_response: + * @application: the #GApplication + * @will_quit: whether the application agrees to quit + * @reason: (null-allowed): a short human-readable string that explains + * why quitting is not possible + * + * This function <emphasis>must</emphasis> be called in response to the + * #GApplication::quit-requested signal, to indicate whether or + * not the application is willing to quit. The application may call + * it either directly from the signal handler, or at some later point. + * + * It should be stressed that <emphasis>applications should not assume + * that they have the ability to block logout or shutdown</emphasis>, + * even when %FALSE is passed for @will_quit. + * + * After calling this method, the application should wait to receive + * either #GApplication::quit-cancelled or #GApplication::quit. + * + * If the application does not connect to #GApplication::quit-requested, + * #GApplication will call this method on its behalf (passing %TRUE + * for @will_quit). + * + * Since: 2.32 + */ +void +g_application_quit_response (GApplication *application, + gboolean will_quit, + const gchar *reason) +{ + g_return_if_fail (G_IS_APPLICATION (application)); + g_return_if_fail (!application->priv->is_remote); + + g_application_impl_quit_response (application->priv->impl, will_quit, reason); +} + +/** + * g_application_inhibit: + * @application: the #GApplication + * @flags: what types of actions should be inhibited + * @reason: (null-allowed): a short, human-readable string that explains + * why these operations are inhibited + * + * Inform the session manager that certain types of actions should be + * inhibited. + * + * Applications should invoke this method when they begin an operation + * that should not be interrupted, such as creating a CD or DVD. The + * types of actions that may be blocked are specified by the @flags + * parameter. When the application completes the operation it should + * call g_application_uninhibit() to remove the inhibitor. Inhibitors + * are also cleared when the application exits. + * + * Applications should not expect that they will always be able to block + * the action. In most cases, users will be given the option to force + * the action to take place. + * + * Reasons should be short and to the point. + * + * Returns: A cookie that is used to uniquely identify this request. + * It should be used as an argument to g_application_uninhibit() + * in order to remove the request. + * + * Since: 2.32 + */ +guint +g_application_inhibit (GApplication *application, + GApplicationInhibitFlags flags, + const gchar *reason) +{ + g_return_val_if_fail (G_IS_APPLICATION (application), 0); + g_return_val_if_fail (!application->priv->is_remote, 0); + + return g_application_impl_inhibit (application->priv->impl, flags, reason); +} + +/** + * g_application_uninhibit: + * @application: the #GApplication + * @cookie: a cookie that was returned by g_application_inhibit() + * + * Removes an inhibitor that has been established with g_application_inhibit(). + * + * Since: 2.32 + */ +void +g_application_uninhibit (GApplication *application, + guint cookie) +{ + g_return_if_fail (G_IS_APPLICATION (application)); + g_return_if_fail (!application->priv->is_remote); + + g_application_impl_uninhibit (application->priv->impl, cookie); +} + +/** + * g_application_is_inhibited: + * @application: the #GApplication + * @flags: what types of actions should be queried + * + * Determines if any of the actions specified in @flags are + * currently inhibited (possibly by another application). + * + * Returns: %TRUE if any of the actions specified in @flags are inhibited + * + * Since: 2.32 + */ +gboolean +g_application_is_inhibited (GApplication *application, + GApplicationInhibitFlags flags) +{ + g_return_val_if_fail (G_IS_APPLICATION (application), FALSE); + g_return_val_if_fail (!application->priv->is_remote, FALSE); + + return g_application_impl_is_inhibited (application->priv->impl, flags); +} + /* Default Application {{{1 */ static GApplication *default_app; diff --git a/gio/gapplication.h b/gio/gapplication.h index 76fcf935f..d0f9ed2c9 100644 --- a/gio/gapplication.h +++ b/gio/gapplication.h @@ -90,8 +90,12 @@ struct _GApplicationClass void (* run_mainloop) (GApplication *application); void (* shutdown) (GApplication *application); + void (*quit_requested) (GApplication *application); + void (*quit_cancelled) (GApplication *application); + void (*quit) (GApplication *application); + /*< private >*/ - gpointer padding[11]; + gpointer padding[8]; }; GType g_application_get_type (void) G_GNUC_CONST; @@ -146,6 +150,18 @@ int g_application_run (GApplic int argc, char **argv); +void g_application_quit_response (GApplication *application, + gboolean will_quit, + const gchar *reason); + +guint g_application_inhibit (GApplication *application, + GApplicationInhibitFlags flags, + const gchar *reason); +void g_application_uninhibit (GApplication *application, + guint cookie); +gboolean g_application_is_inhibited (GApplication *application, + GApplicationInhibitFlags flags); + GApplication * g_application_get_default (void); void g_application_set_default (GApplication *application); diff --git a/gio/gapplicationimpl-dbus.c b/gio/gapplicationimpl-dbus.c index d8999d7c5..a0212e3c4 100644 --- a/gio/gapplicationimpl-dbus.c +++ b/gio/gapplicationimpl-dbus.c @@ -30,6 +30,7 @@ #include "gdbusconnection.h" #include "gdbusintrospection.h" #include "gdbuserror.h" +#include "gdbusproxy.h" #include "gmenuexporter.h" #include <string.h> @@ -103,6 +104,11 @@ struct _GApplicationImpl gboolean properties_live; gboolean primary; gpointer app; + + gchar *app_id; + GDBusProxy *sm_proxy; + gchar *client_path; + GDBusProxy *client_proxy; }; @@ -467,11 +473,15 @@ g_application_impl_destroy (GApplicationImpl *impl) { g_application_impl_stop_primary (impl); - if (impl->session_bus) - g_object_unref (impl->session_bus); + g_clear_object (&impl->session_bus); g_free (impl->object_path); + g_clear_object (&impl->sm_proxy); + g_clear_object (&impl->client_proxy); + g_free (impl->client_path); + g_free (impl->app_id); + g_slice_free (GApplicationImpl, impl); } @@ -817,6 +827,260 @@ g_dbus_command_line_new (GDBusMethodInvocation *invocation) return G_APPLICATION_COMMAND_LINE (gdbcl); } +/* Session Management {{{1 */ + +static void +unregister_client (GApplicationImpl *impl) +{ + GError *error = NULL; + + g_debug ("Unregistering client\n"); + + g_dbus_proxy_call_sync (impl->sm_proxy, + "UnregisterClient", + g_variant_new ("(o)", impl->client_path), + 0, + G_MAXINT, + NULL, + &error); + + if (error) + { + g_warning ("Failed to unregister client: %s\n", error->message); + g_error_free (error); + } + + g_clear_object (&impl->client_proxy); + + g_free (impl->client_path); + impl->client_path = NULL; +} + +static void +client_proxy_signal (GDBusProxy *proxy, + const gchar *sender_name, + const gchar *signal_name, + GVariant *parameters, + GApplicationImpl *impl) +{ + if (strcmp (signal_name, "QueryEndSession") == 0) + { + g_debug ("Received QueryEndSession\n"); + g_application_emit_quit_requested (impl->app); + } + else if (strcmp (signal_name, "EndSession") == 0) + { + g_debug ("Received EndSession\n"); + g_application_impl_quit_response (impl, TRUE, NULL); + unregister_client (impl); + g_application_emit_quit (impl->app); + } + else if (strcmp (signal_name, "CancelEndSession") == 0) + { + g_debug ("Received CancelEndSession\n"); + g_application_emit_quit_cancelled (impl->app); + } + else if (strcmp (signal_name, "Stop") == 0) + { + g_debug ("Received Stop\n"); + unregister_client (impl); + g_application_emit_quit (impl->app); + } +} + +void +g_application_impl_session_startup (GApplicationImpl *impl) +{ + static gchar *client_id; + GError *error = NULL; + GVariant *res; + + if (client_id == NULL) + { + const gchar *desktop_autostart_id; + + desktop_autostart_id = g_getenv ("DESKTOP_AUTOSTART_ID"); + /* Unset DESKTOP_AUTOSTART_ID in order to avoid child processes to + * use the same client id. + */ + g_unsetenv ("DESKTOP_AUTOSTART_ID"); + client_id = g_strdup (desktop_autostart_id ? desktop_autostart_id : ""); + } + + g_debug ("Connecting to session manager\n"); + + impl->sm_proxy = g_dbus_proxy_new_sync (impl->session_bus, 0, + NULL, /* FIXME */ + "org.gnome.SessionManager", + "/org/gnome/SessionManager", + "org.gnome.SessionManager", + NULL, + &error); + if (error) + { + g_warning ("Failed to get a session proxy: %s", error->message); + g_error_free (error); + return; + } + + impl->app_id = g_strdup (g_get_prgname ()); + + g_debug ("Registering client '%s' '%s'\n", impl->app_id, client_id); + + res = g_dbus_proxy_call_sync (impl->sm_proxy, + "RegisterClient", + g_variant_new ("(ss)", impl->app_id, client_id), + 0, + -1, + NULL, + &error); + + if (error) + { + g_warning ("Failed to register client: %s\n", error->message); + g_error_free (error); + g_clear_object (&impl->sm_proxy); + return; + } + + g_variant_get (res, "(o)", &impl->client_path); + g_variant_unref (res); + + g_debug ("Registered client at '%s'\n", impl->client_path); + + impl->client_proxy = g_dbus_proxy_new_sync (impl->session_bus, 0, + NULL, /* FIXME */ + "org.gnome.SessionManager", + impl->client_path, + "org.gnome.SessionManager.ClientPrivate", + NULL, + &error); + if (error) + { + g_warning ("Failed to get client proxy: %s\n", error->message); + g_error_free (error); + g_clear_object (&impl->sm_proxy); + g_free (impl->client_path); + impl->client_path = NULL; + return; + } + + g_signal_connect (impl->client_proxy, "g-signal", G_CALLBACK (client_proxy_signal), impl); +} + +void +g_application_impl_quit_response (GApplicationImpl *impl, + gboolean will_quit, + const gchar *reason) +{ + g_return_if_fail (impl->client_proxy != NULL); + + g_debug ("Calling EndSessionResponse %d '%s'\n", will_quit, reason); + + g_dbus_proxy_call (impl->client_proxy, + "EndSessionResponse", + g_variant_new ("(bs)", will_quit, reason ? reason : ""), + 0, G_MAXINT, + NULL, NULL, NULL); +} + +guint +g_application_impl_inhibit (GApplicationImpl *impl, + GApplicationInhibitFlags flags, + const gchar *reason) +{ + GVariant *res; + GError *error = NULL; + guint cookie; + + g_return_val_if_fail (impl->sm_proxy != NULL, 0); + + g_debug ("Calling Inhibit\n"); + + res = g_dbus_proxy_call_sync (impl->sm_proxy, + "Inhibit", + g_variant_new ("(susu)", + impl->app_id, + 0, /* toplevel XID */ + reason, + flags), + 0, + -1, + NULL, + &error); + if (error) + { + g_warning ("Calling Inhibit failed: %s\n", error->message); + g_error_free (error); + return 0; + } + + g_variant_get (res, "(u)", &cookie); + g_variant_unref (res); + + return cookie; +} + +void +g_application_impl_uninhibit (GApplicationImpl *impl, + guint cookie) +{ + GVariant *res; + GError *error = NULL; + + g_return_if_fail (impl->sm_proxy != NULL); + + g_debug ("Calling Uninhibit\n"); + + res = g_dbus_proxy_call_sync (impl->sm_proxy, + "Uninhibit", + g_variant_new ("(u)", cookie), + 0, + -1, + NULL, + &error); + if (error) + { + g_warning ("Calling Uninhibit failed: %s\n", error->message); + g_error_free (error); + return; + } + + g_variant_unref (res); +} + +gboolean +g_application_impl_is_inhibited (GApplicationImpl *impl, + GApplicationInhibitFlags flags) +{ + GVariant *res; + GError *error = NULL; + gboolean inhibited; + + g_return_val_if_fail (impl->sm_proxy != NULL, FALSE); + + g_debug ("Calling IsInhibited\n"); + + res = g_dbus_proxy_call_sync (impl->sm_proxy, + "IsInhibited", + g_variant_new ("(u)", flags), + 0, + -1, + NULL, + &error); + if (error) + { + g_warning ("Calling IsInhibited failed: %s\n", error->message); + g_error_free (error); + return FALSE; + } + + inhibited = g_variant_get_boolean (res); + g_variant_unref (res); + + return inhibited; +} + /* Epilogue {{{1 */ /* vim:set foldmethod=marker: */ diff --git a/gio/gapplicationimpl.h b/gio/gapplicationimpl.h index ef25f2b2c..8910c949d 100644 --- a/gio/gapplicationimpl.h +++ b/gio/gapplicationimpl.h @@ -41,3 +41,33 @@ int g_application_impl_command_line (GApplic G_GNUC_INTERNAL void g_application_impl_flush (GApplicationImpl *impl); + +G_GNUC_INTERNAL +void g_application_impl_session_startup (GApplicationImpl *impl); + +G_GNUC_INTERNAL +void g_application_impl_quit_response (GApplicationImpl *impl, + gboolean will_quit, + const gchar *reason); + +G_GNUC_INTERNAL +guint g_application_impl_inhibit (GApplicationImpl *impl, + GApplicationInhibitFlags flags, + const gchar *reason); + +G_GNUC_INTERNAL +void g_application_impl_uninhibit (GApplicationImpl *impl, + guint cookie); + +G_GNUC_INTERNAL +gboolean g_application_impl_is_inhibited (GApplicationImpl *impl, + GApplicationInhibitFlags flags); + +G_GNUC_INTERNAL +void g_application_emit_quit_requested (GApplication *app); + +G_GNUC_INTERNAL +void g_application_emit_quit (GApplication *app); + +G_GNUC_INTERNAL +void g_application_emit_quit_cancelled (GApplication *app); diff --git a/gio/gio.symbols b/gio/gio.symbols index 74e7e844e..af60c2b7c 100644 --- a/gio/gio.symbols +++ b/gio/gio.symbols @@ -23,8 +23,12 @@ g_application_get_app_menu g_application_get_menubar g_application_hold g_application_id_is_valid +g_application_inhibit +g_application_inhibit_flags_get_type +g_application_is_inhibited g_application_new g_application_open +g_application_quit_response g_application_register g_application_release g_application_run @@ -35,6 +39,7 @@ g_application_set_application_id g_application_set_default g_application_set_flags g_application_set_inactivity_timeout +g_application_uninhibit g_application_command_line_get_arguments g_application_command_line_get_cwd g_application_command_line_get_environ diff --git a/gio/gioenums.h b/gio/gioenums.h index 47d72bd38..5e54c01fd 100644 --- a/gio/gioenums.h +++ b/gio/gioenums.h @@ -1274,6 +1274,8 @@ typedef enum * attempts to become the owner of the application ID nor does it * check if an existing owner already exists. Everything occurs in * the local process. Since: 2.30. + * @G_APPLICATION_NO_SESSION: Don't register with the session manager. + * Since: 2.32. * * Flags used to define the behaviour of a #GApplication. * @@ -1289,10 +1291,32 @@ typedef enum G_APPLICATION_HANDLES_COMMAND_LINE = (1 << 3), G_APPLICATION_SEND_ENVIRONMENT = (1 << 4), - G_APPLICATION_NON_UNIQUE = (1 << 5) + G_APPLICATION_NON_UNIQUE = (1 << 5), + + G_APPLICATION_NO_SESSION = (1 << 6) } GApplicationFlags; /** + * GApplicationInhibitFlags: + * @G_APPLICATION_INHIBIT_LOGOUT: Inhibit logging out + * @G_APPLICATION_INHIBIT_SWITCH: Inhibit user switching + * @G_APPLICATION_INHIBIT_SUSPEND: Inhibit suspending the + * session or computer + * @G_APPLICATION_INHIBIT_IDLE: Inhibit the session being + * marked as idle + * + * Types of actions that may be blocked by g_application_inhibit(). + * + * Since: 2.32 + */ +typedef enum { + G_APPLICATION_INHIBIT_LOGOUT = 1, + G_APPLICATION_INHIBIT_SWITCH = 2, + G_APPLICATION_INHIBIT_SUSPEND = 4, + G_APPLICATION_INHIBIT_IDLE = 8 +} GApplicationInhibitFlags; + +/** * GTlsError: * @G_TLS_ERROR_UNAVAILABLE: No TLS provider is available * @G_TLS_ERROR_MISC: Miscellaneous TLS error |