From 1c486fb8b83e9a4590e4768d604efd82b68e3e79 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Tue, 10 Jan 2012 00:59:16 -0500 Subject: [PATCH] Simplify logout notification api We don't expose ::quit-requested as API anymore. Instead, we expect users to register inhibitors when needed. Without quit-requested, there is no need for ::quit-cancelled and gtk_application_quit_response anymore. We still emit ::quit when the application is about to quit. --- docs/reference/gtk/gtk3-sections.txt | 1 - .../gtk/migrating-smclient-GtkApplication.xml | 11 +- gtk/gtk.symbols | 1 - gtk/gtkapplication.c | 277 +++++++----------- gtk/gtkapplication.h | 6 - tests/testlogout.c | 55 ---- 6 files changed, 104 insertions(+), 247 deletions(-) diff --git a/docs/reference/gtk/gtk3-sections.txt b/docs/reference/gtk/gtk3-sections.txt index 55049f740..83c134ca5 100644 --- a/docs/reference/gtk/gtk3-sections.txt +++ b/docs/reference/gtk/gtk3-sections.txt @@ -7009,7 +7009,6 @@ gtk_application_remove_window gtk_application_get_windows -gtk_application_quit_response GtkApplicationInhibitFlags gtk_application_inhibit gtk_application_uninhibit diff --git a/docs/reference/gtk/migrating-smclient-GtkApplication.xml b/docs/reference/gtk/migrating-smclient-GtkApplication.xml index 58cae9a1b..38bb417d5 100644 --- a/docs/reference/gtk/migrating-smclient-GtkApplication.xml +++ b/docs/reference/gtk/migrating-smclient-GtkApplication.xml @@ -21,8 +21,7 @@ Starting with GTK+ 3.4, #GtkApplication supports logout notification - and negotiation in the same way as EggSMClient, using the same - basic API: + and negotiation similar to EggSMClient. @@ -31,10 +30,10 @@ EggSMClientGtkApplication - EggSMClient::quit-requested#GtkApplication::quit-requested - EggSMClient::quit#GtkApplication::quit - EggSMClient::quit-cancelled#GtkApplication::quit-cancelled - egg_sm_client_will_quitgtk_application_quit_response() + EggSMClient::quit-requestedinstead of calling will_quit (FALSE,...) in response to this signal, install an inhibitor + EggSMClient::quitthe #GtkApplication::quit signal + EggSMClient::quit-cancelled + egg_sm_client_will_quitinstead of calling will_quit (FALSE,...), install an inhibitoregg_sm_client_end_sessiongtk_application_end_session() diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols index 9108ebcf7..8a83719e8 100644 --- a/gtk/gtk.symbols +++ b/gtk/gtk.symbols @@ -236,7 +236,6 @@ gtk_application_inhibit gtk_application_inhibit_flags_get_type gtk_application_is_inhibited gtk_application_new -gtk_application_quit_response gtk_application_remove_accelerator gtk_application_remove_window gtk_application_set_app_menu diff --git a/gtk/gtkapplication.c b/gtk/gtkapplication.c index e87dc4578..737c51d35 100644 --- a/gtk/gtkapplication.c +++ b/gtk/gtkapplication.c @@ -109,7 +109,7 @@ * life-cycle. * * An application can be informed when the session is about to end - * by connecting to the #GtkApplication::quit-requested signal. + * by connecting to the #GtkApplication::quit signal. * * An application can request the session to be ended by calling * gtk_application_end_session(). @@ -126,8 +126,6 @@ enum { WINDOW_ADDED, WINDOW_REMOVED, - QUIT_REQUESTED, - QUIT_CANCELLED, QUIT, LAST_SIGNAL }; @@ -146,7 +144,6 @@ struct _GtkApplicationPrivate GList *windows; gboolean register_session; - gboolean quit_requested; #ifdef GDK_WINDOWING_X11 GDBusConnection *session_bus; @@ -163,6 +160,9 @@ struct _GtkApplicationPrivate GActionMuxer *muxer; GMenu *combined; + GHashTable *inhibitors; + gint quit_inhibited; + guint next_cookie; AppleEvent quit_event, quit_reply; gboolean quitting; #endif @@ -297,6 +297,8 @@ gtk_application_shutdown_quartz (GtkApplication *application) g_object_unref (application->priv->muxer); application->priv->muxer = NULL; + + g_hash_table_unref (application->priv->inhibitors); } static void @@ -576,6 +578,13 @@ gtk_application_set_property (GObject *object, } } +static void +gtk_application_quit (GtkApplication *app) +{ + /* we are asked to quit, so don't linger */ + g_application_set_inactivity_timeout (G_APPLICATION (app), 0); +} + static void gtk_application_class_init (GtkApplicationClass *class) { @@ -594,6 +603,7 @@ gtk_application_class_init (GtkApplicationClass *class) class->window_added = gtk_application_window_added; class->window_removed = gtk_application_window_removed; + class->quit = gtk_application_quit; g_type_class_add_private (class, sizeof (GtkApplicationPrivate)); @@ -632,56 +642,6 @@ gtk_application_class_init (GtkApplicationClass *class) g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GTK_TYPE_WINDOW); - /** - * GtkApplication::quit-requested: - * @application: the #GtkApplication - * - * 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 the application must not attempt to perform - * any actions or interact with the user in response to - * this signal. Any actions required for a clean shutdown should take - * place in response to the #GtkApplication::quit signal. - * - * The application should limit its operations until either the - * #GApplication::quit or #GtkApplication::quit-cancelled signals is - * emitted. - * - * To receive this signal, you need to set the - * #GtkApplication::register-session property - * when creating the application object. - * - * Since: 3.4 - */ - gtk_application_signals[QUIT_REQUESTED] = - g_signal_new ("quit-requested", GTK_TYPE_APPLICATION, G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkApplicationClass, quit_requested), - NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - - /** - * GtkApplication::quit-cancelled: - * @application: the #GtkApplication - * - * 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 #GtkApplication::quit-requested signal. - * - * To receive this signal, you need to set the - * #GtkApplication::register-session property - * when creating the application object. - * - * Since: 3.4 - */ - gtk_application_signals[QUIT_CANCELLED] = - g_signal_new ("quit-cancelled", GTK_TYPE_APPLICATION, G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkApplicationClass, quit_cancelled), - NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - /** * GtkApplication::quit: * @application: the #GtkApplication @@ -691,9 +651,9 @@ gtk_application_class_init (GtkApplicationClass *class) * should exit as soon as possible after receiving this signal; if * it does not, the session manager may choose to forcibly kill it. * - * Normally, an application would only be sent a ::quit if it - * agreed to quit in response to a #GtkApplication::quit-requested - * signal. However, this is not guaranteed; in some situations the + * Normally, an application would only be sent a ::quit if there + * are no inhibitors (see gtk_application_inhibit()). + * However, this is not guaranteed; in some situations the * session manager may decide to end the session without giving * applications a chance to object. * @@ -704,7 +664,7 @@ gtk_application_class_init (GtkApplicationClass *class) * Since: 3.4 */ gtk_application_signals[QUIT] = - g_signal_new ("quit", GTK_TYPE_APPLICATION, G_SIGNAL_RUN_LAST, + g_signal_new ("quit", GTK_TYPE_APPLICATION, G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GtkApplicationClass, quit), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); @@ -1063,6 +1023,20 @@ unregister_client (GtkApplication *app) app->priv->client_path = NULL; } +static void +gtk_application_quit_response (GtkApplication *application, + gboolean will_quit, + const gchar *reason) +{ + g_debug ("Calling EndSessionResponse %d '%s'", will_quit, reason); + + g_dbus_proxy_call (application->priv->client_proxy, + "EndSessionResponse", + g_variant_new ("(bs)", will_quit, reason ? reason : ""), + G_DBUS_CALL_FLAGS_NONE, + G_MAXINT, + NULL, NULL, NULL); +} static void client_proxy_signal (GDBusProxy *proxy, const gchar *sender_name, @@ -1073,22 +1047,19 @@ client_proxy_signal (GDBusProxy *proxy, if (strcmp (signal_name, "QueryEndSession") == 0) { g_debug ("Received QueryEndSession"); - app->priv->quit_requested = TRUE; - g_signal_emit (app, gtk_application_signals[QUIT_REQUESTED], 0); + gtk_application_quit_response (app, TRUE, NULL); + } + else if (strcmp (signal_name, "CancelEndSession") == 0) + { + g_debug ("Received CancelEndSession"); } else if (strcmp (signal_name, "EndSession") == 0) { g_debug ("Received EndSession"); - app->priv->quit_requested = TRUE; gtk_application_quit_response (app, TRUE, NULL); unregister_client (app); g_signal_emit (app, gtk_application_signals[QUIT], 0); } - else if (strcmp (signal_name, "CancelEndSession") == 0) - { - g_debug ("Received CancelEndSession"); - g_signal_emit (app, gtk_application_signals[QUIT_CANCELLED], 0); - } else if (strcmp (signal_name, "Stop") == 0) { g_debug ("Received Stop"); @@ -1186,52 +1157,7 @@ gtk_application_startup_session_dbus (GtkApplication *app) g_signal_connect (app->priv->client_proxy, "g-signal", G_CALLBACK (client_proxy_signal), app); } -/** - * gtk_application_quit_response: - * @application: the #GtkApplication - * @will_quit: whether the application agrees to quit - * @reason: (allow-none): a short human-readable string that explains - * why quitting is not possible - * - * This function must be called in response to the - * #GtkApplication::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 applications should not assume - * that they have the ability to block logout or shutdown, - * even when %FALSE is passed for @will_quit. - * - * After calling this method, the application should wait to receive - * either #GtkApplication::quit-cancelled or #GtkApplication::quit. - * - * If the application does not connect to #GtkApplication::quit-requested, - * #GtkApplication will call this method on its behalf (passing %TRUE - * for @will_quit). - * - * Since: 3.4 - */ -void -gtk_application_quit_response (GtkApplication *application, - gboolean will_quit, - const gchar *reason) -{ - g_return_if_fail (GTK_IS_APPLICATION (application)); - g_return_if_fail (!g_application_get_is_remote (G_APPLICATION (application))); - g_return_if_fail (application->priv->client_proxy != NULL); - g_return_if_fail (application->priv->quit_requested); - - application->priv->quit_requested = FALSE; - g_debug ("Calling EndSessionResponse %d '%s'", will_quit, reason); - - g_dbus_proxy_call (application->priv->client_proxy, - "EndSessionResponse", - g_variant_new ("(bs)", will_quit, reason ? reason : ""), - G_DBUS_CALL_FLAGS_NONE, - G_MAXINT, - NULL, NULL, NULL); -} /** * GtkApplicationInhibitFlags: @@ -1417,8 +1343,8 @@ gtk_application_is_inhibited (GtkApplication *application, * Requests that the session manager end the current session. * @style indicates how the session should be ended, and * @request_confirmation indicates whether or not the user should be - * given a chance to confirm the action. Both of these flags are merely - * hints though; the session manager may choose to ignore them. + * given a chance to confirm the action. Both of these parameters are + * merely hints though; the session manager may choose to ignore them. * * Return value: %TRUE if the request was sent; %FALSE if it could not * be sent (eg, because it could not connect to the session manager) @@ -1462,46 +1388,6 @@ gtk_application_end_session (GtkApplication *application, /* OS X implementation copied from EggSMClient */ -static gboolean -idle_quit_requested (gpointer client) -{ - g_signal_emit (client, gtk_application_signals[QUIT_REQUESTED], 0); - - return FALSE; -} - -static pascal OSErr -quit_requested (const AppleEvent *aevt, - AppleEvent *reply, - long refcon) -{ - GtkApplication *app = GSIZE_TO_POINTER ((gsize)refcon); - - g_return_val_if_fail (!app->priv->quit_requested, userCanceledErr); - - /* FIXME AEInteractWithUser? */ - osx->quit_requested = TRUE; - AEDuplicateDesc (aevt, &app->priv->quit_event); - AEDuplicateDesc (reply, &app->priv->quit_reply); - AESuspendTheCurrentEvent (aevt); - - /* Don't emit the "quit_requested" signal immediately, since we're - * called from a weird point in the guts of gdkeventloop-quartz.c - */ - g_idle_add (idle_quit_requested, app); - - return noErr; -} - -static void -gtk_application_startup_session_quartz (GtkApplication *app) -{ - if (app->priv->register_session) - AEInstallEventHandler (kCoreEventClass, kAEQuitApplication, - NewAEEventHandlerUPP (quit_requested), - (long)GPOINTER_TO_SIZE (app), false); -} - static pascal OSErr quit_requested_resumed (const AppleEvent *aevt, AppleEvent *reply, @@ -1509,9 +1395,7 @@ quit_requested_resumed (const AppleEvent *aevt, { GtkApplication *app = GSIZE_TO_POINTER ((gsize)refcon); - app->priv->quit_requested = FALSE; - - return app->priv->quitting ? noErr : userCanceledErr; + return app->priv->quit_inhibit == 0 ? noErr : userCanceledErr; } static gboolean @@ -1529,29 +1413,41 @@ idle_will_quit (gpointer data) AEDisposeDesc (&app->quit->quit_event); AEDisposeDesc (&app->quit->quit_reply); - if (app->priv->quitting) + if (app->priv->quit_inhibit == 0) g_signal_emit (app, gtk_application_signals[QUIT], 0); return FALSE; } -void -gtk_application_quit_response (GtkApplication *application, - gboolean will_quit, - const gchar *reason) +static pascal OSErr +quit_requested (const AppleEvent *aevt, + AppleEvent *reply, + long refcon) { - g_return_if_fail (GTK_IS_APPLICATION (application)); - g_return_if_fail (!g_application_get_is_remote (G_APPLICATION (application))); - g_return_if_fail (application->priv->quit_requested); + GtkApplication *app = GSIZE_TO_POINTER ((gsize)refcon); - application->priv->quitting = will_quit; + /* FIXME AEInteractWithUser? */ + AEDuplicateDesc (aevt, &app->priv->quit_event); + AEDuplicateDesc (reply, &app->priv->quit_reply); + AESuspendTheCurrentEvent (aevt); - /* Finish in an idle handler since the caller might have called - * gtk_application_quit_response() from inside the ::quit-requested - * signal handler, but may not expect the ::quit signal to arrive - * during the gtk_application_quit_response() call. + /* Don't emit the "quit" signal immediately, since we're + * called from a weird point in the guts of gdkeventloop-quartz.c */ - g_idle_add (idle_will_quit, application); + g_idle_add (idle_will_quit, app); + + return noErr; +} + +static void +gtk_application_startup_session_quartz (GtkApplication *app) +{ + if (app->priv->register_session) + AEInstallEventHandler (kCoreEventClass, kAEQuitApplication, + NewAEEventHandlerUPP (quit_requested), + (long)GPOINTER_TO_SIZE (app), false); + + app->priv->inhibitors = g_hash_table_new (NULL, NULL); } guint @@ -1560,19 +1456,51 @@ gtk_application_inhibit (GtkApplication *application, GtkApplicationInhibitFlags flags, const gchar *reason) { - return 0; + guint cookie; + + g_return_val_if_fail (GTK_IS_APPLICATION (application), 0); + g_return_val_if_fail (flags != 0, 0); + + application->priv->next_cookie++; + cookie = application->priv->next_cookie; + + g_hash_table_insert (application->priv->inhibitors, + GUINT_TO_POINTER (cookie), + GUINT_TO_POINTER (flags)); + + if (flags & GTK_APPLICATION_INHIBIT_LOGOUT) + application->priv->quit_inhibit++; + + return cookie; } void gtk_application_uninhibit (GtkApplication *application, guint cookie) { + GApplicationInhibitFlags flags; + + flags = GPOINTER_TO_UINT (g_hash_table_lookup (application->priv->inhibitors, GUINT_TO_POINTER (cookie))); + + if (flags == 0) + { + g_warning ("Invalid inhibitor cookie"); + return; + } + + g_hash_table_remove (application->priv->inhibitors, GUINT_TO_POINTER (cookie)); + + if (flags & GTK_APPLICATION_INHIBIT_LOGOUT) + application->priv->quit_inhibit--; } gboolean gtk_application_is_inhibited (GtkApplication *application, GtkApplicationInhibitFlags flags) { + if (flags & GTK_APPLICATION_INHIBIT_LOGOUT) + return application->priv->quit_inhibit > 0; + return FALSE; } @@ -1636,13 +1564,6 @@ gtk_application_end_session (GtkApplication *application, * http://msdn.microsoft.com/en-us/library/ms700677%28VS.85%29.aspx */ -void -gtk_application_quit_response (GtkApplication *application, - gboolean will_quit, - const gchar *reason) -{ -} - guint gtk_application_inhibit (GtkApplication *application, GtkWindow *window, diff --git a/gtk/gtkapplication.h b/gtk/gtkapplication.h index 4e3844163..2d41c9fff 100644 --- a/gtk/gtkapplication.h +++ b/gtk/gtkapplication.h @@ -59,8 +59,6 @@ struct _GtkApplicationClass void (*window_removed) (GtkApplication *application, GtkWindow *window); - void (*quit_requested) (GtkApplication *application); - void (*quit_cancelled) (GtkApplication *application); void (*quit) (GtkApplication *application); /*< private >*/ @@ -95,10 +93,6 @@ void gtk_application_remove_accelerator (GtkApplication *application const gchar *action_name, GVariant *parameter); -void gtk_application_quit_response (GtkApplication *application, - gboolean will_quit, - const gchar *reason); - typedef enum { GTK_APPLICATION_INHIBIT_LOGOUT = (1 << 0), diff --git a/tests/testlogout.c b/tests/testlogout.c index a896bd458..a278c7b13 100644 --- a/tests/testlogout.c +++ b/tests/testlogout.c @@ -1,7 +1,5 @@ #include -static gboolean will_quit = TRUE; -static gchar *reason; static GtkWidget *inhibit_entry; static GtkWidget *inhibit_logout; static GtkWidget *inhibit_switch; @@ -35,19 +33,6 @@ end_session (GtkButton *button, GtkApplication *app) gtk_application_end_session (app, style, confirm); } -static void -toggle_will_quit (GtkToggleButton *button, gpointer data) -{ - will_quit = gtk_toggle_button_get_active (button); -} - -static void -reason_changed (GtkEntry *entry, GParamSpec *pspec, GtkApplication *app) -{ - g_free (reason); - reason = g_strdup (gtk_entry_get_text (entry)); -} - static void inhibitor_toggled (GtkToggleButton *button, GtkApplication *app) { @@ -118,7 +103,6 @@ activate (GtkApplication *app, GtkWidget *separator; GtkWidget *grid; GtkWidget *button; - GtkWidget *entry; GtkWidget *label; win = gtk_window_new (GTK_WINDOW_TOPLEVEL); @@ -168,26 +152,6 @@ activate (GtkApplication *app, gtk_container_add (GTK_CONTAINER (box), grid); - button = gtk_check_button_new_with_label ("Will quit"); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), will_quit); - g_signal_connect (button, "toggled", - G_CALLBACK (toggle_will_quit), NULL); - gtk_grid_attach (GTK_GRID (grid), button, 0, 0, 1, 1); - - entry = gtk_entry_new (); - g_signal_connect (entry, "notify::text", - G_CALLBACK (reason_changed), app); - gtk_grid_attach (GTK_GRID (grid), entry, 1, 0, 1, 1); - - separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL); - gtk_container_add (GTK_CONTAINER (box), separator); - - grid = gtk_grid_new (); - gtk_grid_set_row_spacing (GTK_GRID (grid), 6); - gtk_grid_set_column_spacing (GTK_GRID (grid), 6); - - gtk_container_add (GTK_CONTAINER (box), grid); - end_combo = gtk_combo_box_text_new (); gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (end_combo), "logout", "Logout"); gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (end_combo), "reboot", "Reboot"); @@ -210,14 +174,6 @@ activate (GtkApplication *app, gtk_application_add_window (app, GTK_WINDOW (win)); } -static void -quit_requested (GtkApplication *app, - gpointer data) -{ - g_print ("Received quit-requested, reply: %d, '%s'\n", will_quit, reason); - gtk_application_quit_response (app, will_quit, reason); -} - static void quit (GtkApplication *app, gpointer data) @@ -225,13 +181,6 @@ quit (GtkApplication *app, g_print ("Received quit\n"); } -static void -quit_cancelled (GtkApplication *app, - gpointer data) -{ - g_print ("received quit-cancelled\n"); -} - int main (int argc, char *argv[]) { @@ -242,12 +191,8 @@ main (int argc, char *argv[]) g_signal_connect (app, "activate", G_CALLBACK (activate), NULL); - g_signal_connect (app, "quit-requested", - G_CALLBACK (quit_requested), NULL); g_signal_connect (app, "quit", G_CALLBACK (quit), NULL); - g_signal_connect (app, "quit-cancelled", - G_CALLBACK (quit_cancelled), NULL); g_application_run (G_APPLICATION (app), argc, argv); -- 2.43.2