From c9352cd0a8363e4508d19e66ccf373b2abb2b7d3 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 12 Jul 2013 02:05:35 -0500 Subject: If the Unity dialogs aren't available (such as in the greeter) but zenity is, use it to for confirmation dialogs: - add the implementation in actions.c - add actions.c to the i18n files list - sync the unit tests - suggest zenity in debian/control --- debian/control | 3 +- po/CMakeLists.txt | 2 +- src/backend-dbus/actions.c | 314 ++++++++++++++++++++++++++++++------- tests/backend-dbus/test-actions.cc | 74 ++++++--- 4 files changed, 313 insertions(+), 80 deletions(-) diff --git a/debian/control b/debian/control index a0dfdb1..522eeb1 100644 --- a/debian/control +++ b/debian/control @@ -26,7 +26,8 @@ Depends: ${shlibs:Depends}, gnome-settings-daemon, Recommends: indicator-applet (>= 0.2) | indicator-renderer, gnome-screensaver -Suggests: lightdm +Suggests: lightdm, + zenity Description: indicator showing session management, status and user switching This indicator is designed to be placed on the right side of a panel and give the user easy control for changing their instant message status. diff --git a/po/CMakeLists.txt b/po/CMakeLists.txt index 8325f6e..c47d584 100644 --- a/po/CMakeLists.txt +++ b/po/CMakeLists.txt @@ -1,3 +1,3 @@ include (Translations) add_translations_directory ("${GETTEXT_PACKAGE}") -add_translations_catalog ("${GETTEXT_PACKAGE}" ../src/) +add_translations_catalog ("${GETTEXT_PACKAGE}" ../src/ ../src/dbus-backend) diff --git a/src/backend-dbus/actions.c b/src/backend-dbus/actions.c index c095896..ed1e708 100644 --- a/src/backend-dbus/actions.c +++ b/src/backend-dbus/actions.c @@ -18,6 +18,7 @@ */ #include +#include #include "dbus-end-session-dialog.h" #include "dbus-login1-manager.h" @@ -39,6 +40,7 @@ struct _IndicatorSessionActionsDbusPriv GCancellable * cancellable; GSettings * lockdown_settings; + GSettings * indicator_settings; GnomeScreenSaver * screen_saver; GnomeSessionManager * session_manager; Login1Manager * login1_manager; @@ -48,6 +50,7 @@ struct _IndicatorSessionActionsDbusPriv GCancellable * dm_seat_cancellable; Webcredentials * webcredentials; EndSessionDialog * end_session_dialog; + char * zenity; gboolean can_suspend; gboolean can_hibernate; @@ -80,6 +83,44 @@ log_and_clear_error (GError ** err, const char * loc, const char * func) **** ***/ +typedef enum +{ + PROMPT_NONE, + PROMPT_WITH_ZENITY, + PROMPT_WITH_UNITY +} +prompt_status_t; + +static prompt_status_t +get_prompt_status (IndicatorSessionActionsDbus * self) +{ + prompt_status_t prompt = PROMPT_NONE; + const priv_t * p = self->priv; + + if (!g_settings_get_boolean (p->indicator_settings, "suppress-logout-restart-shutdown")) + { + /* can we use the Unity prompt? */ + if ((prompt == PROMPT_NONE) && p && p->end_session_dialog) + { + GDBusProxy * proxy = G_DBUS_PROXY (p->end_session_dialog); + char * name = g_dbus_proxy_get_name_owner (proxy); + if (name != NULL) + prompt = PROMPT_WITH_UNITY; + g_free (name); + } + + /* can we use zenity? */ + if ((prompt == PROMPT_NONE) && p && p->zenity) + prompt = PROMPT_WITH_ZENITY; + } + + return prompt; +} + +/*** +**** +***/ + static void on_seat_notify_multi_session (IndicatorSessionActionsDbus * self) { @@ -288,6 +329,7 @@ on_end_session_dialog_proxy_ready (GObject * o G_GNUC_UNUSED, GAsyncResult * res INDICATOR_SESSION_ACTIONS_DBUS(gself)->priv->end_session_dialog = end_session_dialog; indicator_session_actions_notify_can_prompt (INDICATOR_SESSION_ACTIONS(gself)); + indicator_session_actions_notify_can_reboot (INDICATOR_SESSION_ACTIONS(gself)); } log_and_clear_error (&err, G_STRLOC, G_STRFUNC); @@ -310,7 +352,31 @@ my_can_logout (IndicatorSessionActions * self) { priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv; - return !g_settings_get_boolean (p->lockdown_settings, "disable-log-out"); + if (g_settings_get_boolean (p->indicator_settings, "suppress-logout-menuitem")) + return FALSE; + + if (g_settings_get_boolean (p->lockdown_settings, "disable-log-out")) + return FALSE; + + return TRUE; +} + +static gboolean +my_can_reboot (IndicatorSessionActions * actions) +{ + IndicatorSessionActionsDbus * self = INDICATOR_SESSION_ACTIONS_DBUS(actions); + priv_t * p = self->priv; + + if (g_settings_get_boolean (p->indicator_settings, "suppress-restart-menuitem")) + return FALSE; + + /* Shutdown and Restart are the same dialog prompt in Unity, + so disable the redundant 'Restart' menuitem in that mode */ + if (!g_settings_get_boolean (p->indicator_settings, "suppress-shutdown-menuitem")) + if (get_prompt_status(self) == PROMPT_WITH_UNITY) + return FALSE; + + return TRUE; } static gboolean @@ -341,18 +407,7 @@ my_can_hibernate (IndicatorSessionActions * self) static gboolean my_can_prompt (IndicatorSessionActions * self) { - gboolean can_prompt = FALSE; - const priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv; - - if (p && p->end_session_dialog) - { - GDBusProxy * proxy = G_DBUS_PROXY (p->end_session_dialog); - char * name = g_dbus_proxy_get_name_owner (proxy); - can_prompt = name != NULL; - g_free (name); - } - - return can_prompt; + return get_prompt_status(INDICATOR_SESSION_ACTIONS_DBUS(self)) != PROMPT_NONE; } static gboolean @@ -396,50 +451,51 @@ my_hibernate (IndicatorSessionActions * self) ***/ static void -logout_now (IndicatorSessionActions * self, gboolean try_to_prompt) +logout_now (IndicatorSessionActionsDbus * self) { - priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv; - const int type = try_to_prompt ? 0 : 1; + priv_t * p = self->priv; g_return_if_fail (p->session_manager != NULL); gnome_session_manager_call_logout (p->session_manager, - type, + 1, /* don't prompt */ p->cancellable, NULL, NULL); } static void -logout_now_with_prompt (IndicatorSessionActions * self) +on_reboot_response (GObject * o, + GAsyncResult * res, + gpointer unused G_GNUC_UNUSED) { - logout_now (self, TRUE); -} - -static void -logout_now_quietly (IndicatorSessionActions * self) -{ - logout_now (self, FALSE); + GError * err = NULL; + login1_manager_call_reboot_finish (LOGIN1_MANAGER(o), res, &err); + if (err != NULL) + { + g_warning ("Unable to reboot: %s", err->message); + g_error_free (err); + } } static void -reboot_now (IndicatorSessionActions * self) +reboot_now (IndicatorSessionActionsDbus * self) { - priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv; + priv_t * p = self->priv; g_return_if_fail (p->login1_manager != NULL); login1_manager_call_reboot (p->login1_manager, FALSE, p->login1_manager_cancellable, - NULL, + on_reboot_response, NULL); } static void -power_off_now (IndicatorSessionActions * self) +power_off_now (IndicatorSessionActionsDbus * self) { - priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv; + priv_t * p = self->priv; g_return_if_fail (p->login1_manager != NULL); @@ -477,7 +533,7 @@ on_open_end_session_dialog_ready (GObject * o, } static void -show_end_session_dialog (IndicatorSessionActionsDbus * self, int type) +show_unity_end_session_dialog (IndicatorSessionActionsDbus * self, int type) { priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv; gpointer o = p->end_session_dialog; @@ -485,7 +541,7 @@ show_end_session_dialog (IndicatorSessionActionsDbus * self, int type) g_assert (o != NULL); - g_signal_connect_swapped (o, "confirmed-logout", G_CALLBACK(logout_now_quietly), self); + g_signal_connect_swapped (o, "confirmed-logout", G_CALLBACK(logout_now), self); g_signal_connect_swapped (o, "confirmed-reboot", G_CALLBACK(reboot_now), self); g_signal_connect_swapped (o, "confirmed-shutdown", G_CALLBACK(power_off_now), self); g_signal_connect_swapped (o, "canceled", G_CALLBACK(on_end_session_dialog_canceled), self); @@ -497,34 +553,140 @@ show_end_session_dialog (IndicatorSessionActionsDbus * self, int type) self); } -static void -my_logout (IndicatorSessionActions * self) -{ - if (my_can_prompt (self)) - show_end_session_dialog (INDICATOR_SESSION_ACTIONS_DBUS(self), END_SESSION_TYPE_LOGOUT); +static gboolean +zenity_question (IndicatorSessionActionsDbus * self, + const char * icon_name, + const char * title, + const char * text, + const char * ok_label, + const char * cancel_label) +{ + char * command_line; + int exit_status; + gboolean confirmed; + + command_line = g_strdup_printf ("%s" + " --question" + " --icon-name=\"%s\"" + " --title=\"%s\"" + " --text=\"%s\"" + " --ok-label=\"%s\"" + " --cancel-label=\"%s\"" + " --no-wrap", + self->priv->zenity, + icon_name, + title, + text, + ok_label, + cancel_label); + + exit_status = -1; + if (!g_spawn_command_line_sync (command_line, NULL, NULL, &exit_status, NULL)) + { + /* Treat failure-to-prompt as user confirmation. + Otherwise how will the user ever log out? */ + confirmed = TRUE; + } else - logout_now_with_prompt (self); + { + confirmed = exit_status == 0; + } + + g_free (command_line); + return confirmed; } +static void +my_logout (IndicatorSessionActions * actions) +{ + IndicatorSessionActionsDbus * self = INDICATOR_SESSION_ACTIONS_DBUS (actions); + + switch (get_prompt_status (self)) + { + case PROMPT_WITH_UNITY: + show_unity_end_session_dialog (self, END_SESSION_TYPE_LOGOUT); + break; + + case PROMPT_NONE: + logout_now (self); + break; + + case PROMPT_WITH_ZENITY: + { + const char * primary = _("Are you sure you want to close all programs and log out?"); + const char * secondary = _("Some software updates won't be applied until the computer next restarts."); + char * text = g_strdup_printf ("%s\n \n%s", primary, secondary); + + gboolean confirmed = zenity_question (self, + "system-log-out", + _("Log Out"), + text, + _("Log Out"), + _("Cancel")); + + g_free (text); + + if (confirmed) + logout_now (self); + break; + } + } +} static void -my_reboot (IndicatorSessionActions * self) +my_reboot (IndicatorSessionActions * actions) { - if (my_can_prompt (self)) - show_end_session_dialog (INDICATOR_SESSION_ACTIONS_DBUS(self), END_SESSION_TYPE_REBOOT); - else - reboot_now (self); + IndicatorSessionActionsDbus * self = INDICATOR_SESSION_ACTIONS_DBUS (actions); + + switch (get_prompt_status (self)) + { + case PROMPT_WITH_UNITY: + show_unity_end_session_dialog (self, END_SESSION_TYPE_REBOOT); + break; + + case PROMPT_NONE: + reboot_now (self); + break; + + case PROMPT_WITH_ZENITY: + if (zenity_question (self, + "system-restart", + _("Restart"), + _("Are you sure you want to close all programs and restart the computer?"), + _("Restart"), + _("Cancel"))) + reboot_now (self); + break; + } } static void -my_power_off (IndicatorSessionActions * self) +my_power_off (IndicatorSessionActions * actions) { - /* NB: TYPE_REBOOT instead of TYPE_SHUTDOWN because - the latter adds lock & logout options in Unity... */ - if (my_can_prompt (self)) - show_end_session_dialog (INDICATOR_SESSION_ACTIONS_DBUS(self), END_SESSION_TYPE_REBOOT); - else - power_off_now (self); + IndicatorSessionActionsDbus * self = INDICATOR_SESSION_ACTIONS_DBUS (actions); + + switch (get_prompt_status (self)) + { + case PROMPT_WITH_UNITY: + /* NB: TYPE_REBOOT instead of TYPE_SHUTDOWN because + the latter adds lock & logout options in Unity... */ + show_unity_end_session_dialog (self, END_SESSION_TYPE_REBOOT); + break; + + case PROMPT_WITH_ZENITY: + if (zenity_question (self, + "system-shutdown", + _("Shut Down"), + _("Are you sure you want to close all programs and shut down the computer?"), + _("Shut Down"), + _("Cancel"))) + power_off_now (self); + break; + + case PROMPT_NONE: + power_off_now (self); + break; + } } /*** @@ -620,11 +782,32 @@ my_dispose (GObject * o) g_clear_object (&p->cancellable); } - g_clear_object (&p->lockdown_settings); + if (p->indicator_settings != NULL) + { + g_signal_handlers_disconnect_by_data (p->indicator_settings, self); + g_clear_object (&p->indicator_settings); + } + + if (p->lockdown_settings != NULL) + { + g_signal_handlers_disconnect_by_data (p->lockdown_settings, self); + g_clear_object (&p->lockdown_settings); + } + + if (p->webcredentials != NULL) + { + g_signal_handlers_disconnect_by_data (p->webcredentials, self); + g_clear_object (&p->webcredentials); + } + + if (p->end_session_dialog != NULL) + { + stop_listening_to_dialog (self); + g_clear_object (&p->end_session_dialog); + } + g_clear_object (&p->screen_saver); g_clear_object (&p->session_manager); - g_clear_object (&p->webcredentials); - g_clear_object (&p->end_session_dialog); set_dm_seat (self, NULL); set_login1_manager (self, NULL); set_login1_seat (self, NULL); @@ -632,6 +815,16 @@ my_dispose (GObject * o) G_OBJECT_CLASS (indicator_session_actions_dbus_parent_class)->dispose (o); } +static void +my_finalize (GObject * o) +{ + IndicatorSessionActionsDbus * self = INDICATOR_SESSION_ACTIONS_DBUS (o); + priv_t * p = self->priv; + + g_free (p->zenity); +} + + /*** **** GObject Boilerplate ***/ @@ -645,10 +838,12 @@ indicator_session_actions_dbus_class_init (IndicatorSessionActionsDbusClass * kl object_class = G_OBJECT_CLASS (klass); object_class->dispose = my_dispose; + object_class->finalize = my_finalize; actions_class = INDICATOR_SESSION_ACTIONS_CLASS (klass); actions_class->can_lock = my_can_lock; actions_class->can_logout = my_can_logout; + actions_class->can_reboot = my_can_reboot; actions_class->can_switch = my_can_switch; actions_class->can_suspend = my_can_suspend; actions_class->can_hibernate = my_can_hibernate; @@ -684,6 +879,8 @@ indicator_session_actions_dbus_init (IndicatorSessionActionsDbus * self) p->seat_allows_activation = TRUE; self->priv = p; + p->zenity = g_find_program_in_path ("zenity"); + s = g_settings_new ("org.gnome.desktop.lockdown"); g_signal_connect_swapped (s, "changed::disable-lock-screen", G_CALLBACK(indicator_session_actions_notify_can_lock), self); @@ -693,6 +890,17 @@ indicator_session_actions_dbus_init (IndicatorSessionActionsDbus * self) G_CALLBACK(indicator_session_actions_notify_can_switch), self); p->lockdown_settings = s; + s = g_settings_new ("com.canonical.indicator.session"); + g_signal_connect_swapped (s, "changed::suppress-logout-restart-shutdown", + G_CALLBACK(indicator_session_actions_notify_can_prompt), self); + g_signal_connect_swapped (s, "changed::suppress-logout-restart-shutdown", + G_CALLBACK(indicator_session_actions_notify_can_reboot), self); + g_signal_connect_swapped (s, "changed::suppress-restart-menuitem", + G_CALLBACK(indicator_session_actions_notify_can_reboot), self); + g_signal_connect_swapped (s, "changed::suppress-shutdown-menuitem", + G_CALLBACK(indicator_session_actions_notify_can_reboot), self); + p->indicator_settings = s; + gnome_screen_saver_proxy_new_for_bus (G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, "org.gnome.ScreenSaver", diff --git a/tests/backend-dbus/test-actions.cc b/tests/backend-dbus/test-actions.cc index a7e7a5f..c0f8517 100644 --- a/tests/backend-dbus/test-actions.cc +++ b/tests/backend-dbus/test-actions.cc @@ -22,6 +22,8 @@ #include "backend.h" #include "backend-dbus/backend-dbus.h" +#define SUPPRESS_KEY "suppress-logout-restart-shutdown" + /*** **** ***/ @@ -36,12 +38,14 @@ class Actions: public GTestMockDBusFixture GCancellable * cancellable; IndicatorSessionActions * actions; + GSettings * indicator_settings; virtual void SetUp () { super :: SetUp (); // init 'actions' + indicator_settings = g_settings_new ("com.canonical.indicator.session"); cancellable = g_cancellable_new (); actions = 0; backend_get (cancellable, &actions, NULL, NULL); @@ -52,6 +56,7 @@ class Actions: public GTestMockDBusFixture virtual void TearDown () { g_cancellable_cancel (cancellable); + g_clear_object (&indicator_settings); g_clear_object (&cancellable); g_clear_object (&actions); @@ -187,6 +192,7 @@ TEST_F (Actions, CanHibernate) TEST_F (Actions, Reboot) { ASSERT_TRUE (login1_manager->last_action().empty()); + ASSERT_FALSE (g_settings_get_boolean (indicator_settings, SUPPRESS_KEY)); // confirm that user is prompted // and that no action is taken when the user cancels the dialog @@ -207,15 +213,18 @@ TEST_F (Actions, Reboot) ASSERT_EQ (login1_manager->last_action(), "reboot"); // confirm that we try to reboot w/o prompting - // if the EndSessionDialog isn't available - delete end_session_dialog; - end_session_dialog = 0; + // if prompting is disabled login1_manager->clear_last_action (); + ASSERT_EQ ("", login1_manager->last_action()); + g_settings_set_boolean (indicator_settings, SUPPRESS_KEY, TRUE); + wait_msec (50); ASSERT_TRUE (login1_manager->last_action().empty()); wait_msec (50); indicator_session_actions_reboot (actions); wait_msec (50); - ASSERT_EQ (login1_manager->last_action(), "reboot"); + ASSERT_EQ ("reboot", login1_manager->last_action()); + + g_settings_reset (indicator_settings, SUPPRESS_KEY); } TEST_F (Actions, PowerOff) @@ -242,13 +251,16 @@ TEST_F (Actions, PowerOff) // confirm that we try to shutdown w/o prompting // if the EndSessionDialog isn't available - delete end_session_dialog; - end_session_dialog = 0; + // if prompting is disabled login1_manager->clear_last_action (); + ASSERT_EQ ("", login1_manager->last_action()); + g_settings_set_boolean (indicator_settings, SUPPRESS_KEY, TRUE); wait_msec (50); indicator_session_actions_power_off (actions); wait_msec (50); ASSERT_EQ (login1_manager->last_action(), "power-off"); + + g_settings_reset (indicator_settings, SUPPRESS_KEY); } TEST_F (Actions, Logout) @@ -273,14 +285,17 @@ TEST_F (Actions, Logout) wait_msec (100); ASSERT_EQ (MockSessionManager::LogoutQuiet, session_manager->last_action ()); - // confirm that we try to call SessionManager::LogoutNormal - // if the EndSessionDialog isn't available - delete end_session_dialog; - end_session_dialog = 0; + // confirm that we try to call SessionManager::LogoutQuet + // when prompts are disabled + login1_manager->clear_last_action (); + ASSERT_EQ ("", login1_manager->last_action()); + g_settings_set_boolean (indicator_settings, SUPPRESS_KEY, TRUE); wait_msec (50); indicator_session_actions_logout (actions); wait_msec (50); - ASSERT_EQ (MockSessionManager::LogoutNormal, session_manager->last_action ()); + ASSERT_EQ (MockSessionManager::LogoutQuiet, session_manager->last_action ()); + + g_settings_reset (indicator_settings, SUPPRESS_KEY); } TEST_F (Actions, Suspend) @@ -389,22 +404,31 @@ TEST_F (Actions, HasOnlineAccountError) ASSERT_EQ (b, gb); } -TEST_F (Actions, CanPrompt) +namespace { - gboolean b; + static gboolean toggle_suppress (gpointer settings) + { + const char * key = SUPPRESS_KEY; + gboolean b = g_settings_get_boolean (G_SETTINGS(settings), key); + g_settings_set_boolean (G_SETTINGS(settings), key, !b); + return G_SOURCE_REMOVE; + } +} - ASSERT_TRUE (indicator_session_actions_can_prompt (actions)); +TEST_F (Actions, SuppressPrompts) +{ + for (int i=0; i<3; ++i) + { + bool b; + gboolean b2; - delete end_session_dialog; - end_session_dialog = 0; - wait_msec (50); - ASSERT_FALSE (indicator_session_actions_can_prompt (actions)); - g_object_get (actions, INDICATOR_SESSION_ACTIONS_PROP_CAN_PROMPT, &b, NULL); - ASSERT_FALSE (b); + b = indicator_session_actions_can_prompt (actions); + b2 = !g_settings_get_boolean (indicator_settings, SUPPRESS_KEY); + ASSERT_EQ (b, b2); + + g_idle_add (toggle_suppress, indicator_settings); + wait_for_signal (actions, "notify::" INDICATOR_SESSION_ACTIONS_PROP_CAN_PROMPT); + } - end_session_dialog = new MockEndSessionDialog (loop, conn); - wait_msec (50); - ASSERT_TRUE (indicator_session_actions_can_prompt (actions)); - g_object_get (actions, INDICATOR_SESSION_ACTIONS_PROP_CAN_PROMPT, &b, NULL); - ASSERT_TRUE (b); + g_settings_reset (indicator_settings, SUPPRESS_KEY); } -- cgit v1.2.3