diff options
Diffstat (limited to 'src')
51 files changed, 5896 insertions, 4909 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..a00b6f1 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,27 @@ +add_subdirectory (backend-dbus) + +add_library (libindicatorsessionservice STATIC + actions.c + actions.h + guest.c + guest.h + service.c + service.h + users.c + users.h) +include_directories(${SERVICE_INCLUDE_DIRS}) +link_directories(${SERVICE_LIBRARY_DIRS}) + +set (SERVICE_EXEC "indicator-session-service") +set_property (SOURCE main.c + APPEND PROPERTY COMPILE_DEFINITIONS + GETTEXT_PACKAGE="${GETTEXT_PACKAGE}" + GNOMELOCALEDIR="@CMAKE_INSTALL_FULL_LOCALEDIR@") +add_executable (${SERVICE_EXEC} main.c) +target_link_libraries (${SERVICE_EXEC} libindicatorsessionservice backenddbus ${SERVICE_LIBRARIES} ${GCOV_LIBS}) +install (TARGETS ${SERVICE_EXEC} RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}) + +# common properties +set_property (TARGET libindicatorsessionservice ${SERVICE_EXEC} + APPEND_STRING PROPERTY COMPILE_FLAGS + " -g ${CC_WARNING_ARGS} ${GCOV_FLAGS}") diff --git a/src/Makefile.am b/src/Makefile.am deleted file mode 100644 index 1ab9b9b..0000000 --- a/src/Makefile.am +++ /dev/null @@ -1,193 +0,0 @@ -CLEANFILES = -EXTRA_DIST = - -libexec_PROGRAMS = \ - indicator-session-service - -if BUILD_GTKLOGOUTHELPER -libexec_PROGRAMS += \ - gtk-logout-helper -endif - -################### -# Indicator Stuff -################### - -CLEANFILES += .libs/*.gcda .libs/*.gcno *.gcda *.gcno - -sessionlibdir = $(INDICATORDIR) -sessionlib_LTLIBRARIES = libsession.la -libsession_la_SOURCES = \ - indicator-session.c \ - gen-session-dbus.xml.h \ - shared-names.h \ - user-widget.c \ - user-widget.h -libsession_la_CFLAGS = \ - $(APPLET_CFLAGS) \ - $(COVERAGE_CFLAGS) \ - -Wall -Wunused \ - -DG_LOG_DOMAIN=\"Indicator-Session\" -libsession_la_LIBADD = $(APPLET_LIBS) -libsession_la_LDFLAGS = \ - $(COVERAGE_LDFLAGS) \ - -module -avoid-version - -dbus_display_manager_sources = \ - dbus-display-manager.c \ - dbus-display-manager.h - -$(dbus_display_manager_sources): display-manager.xml - $(AM_V_GEN) gdbus-codegen \ - --interface-prefix org.freedesktop \ - --generate-c-code dbus-display-manager \ - $^ - -dbus_login1_manager_sources = \ - dbus-login1-manager.c \ - dbus-login1-manager.h - -$(dbus_login1_manager_sources): org.freedesktop.login1.Manager.xml - $(AM_V_GEN) gdbus-codegen \ - --interface-prefix org.freedesktop \ - --generate-c-code dbus-login1-manager \ - $^ - -dbus_login1_session_sources = \ - dbus-login1-session.c \ - dbus-login1-session.h - -$(dbus_login1_session_sources): org.freedesktop.login1.Session.xml - $(AM_V_GEN) gdbus-codegen \ - --interface-prefix org.freedesktop \ - --generate-c-code dbus-login1-session \ - $^ - -dbus_login1_user_sources = \ - dbus-login1-user.c \ - dbus-login1-user.h - -$(dbus_login1_user_sources): org.freedesktop.login1.User.xml - $(AM_V_GEN) gdbus-codegen \ - --interface-prefix org.freedesktop \ - --generate-c-code dbus-login1-user \ - $^ - -dbus_accounts_sources = \ - dbus-accounts.c \ - dbus-accounts.h - -$(dbus_accounts_sources): org.freedesktop.Accounts.xml - $(AM_V_GEN) gdbus-codegen \ - --interface-prefix org.freedesktop \ - --generate-c-code dbus-accounts \ - $^ - -dbus_user_sources = \ - dbus-user.c \ - dbus-user.h - -$(dbus_user_sources): org.freedesktop.Accounts.User.xml - $(AM_V_GEN) gdbus-codegen \ - --interface-prefix org.freedesktop \ - --generate-c-code dbus-user \ - $^ - -gen-%.xml.c: %.xml - @echo "Building $@ from $<" - @echo "const char * _$(subst -,_,$(subst .,_,$(basename $(notdir $<)))) = " > $@ - @sed -e "s:\":\\\\\":g" -e s:^:\": -e s:\$$:\\\\n\": $< >> $@ - @echo ";" >> $@ - -gen-%.xml.h: %.xml - @echo "Building $@ from $<" - @echo "extern const char * _$(subst -,_,$(subst .,_,$(basename $(notdir $<))));" > $@ - -################# -# Session Stuff -################# - -indicator_session_service_SOURCES = \ - $(dbus_accounts_sources) \ - $(dbus_login1_manager_sources) \ - $(dbus_login1_user_sources) \ - $(dbus_login1_session_sources) \ - $(dbus_display_manager_sources) \ - $(dbus_user_sources) \ - session-service.c \ - session-dbus.c \ - session-dbus.h \ - gen-session-dbus.xml.c \ - users-service-dbus.h \ - users-service-dbus.c \ - session-menu-mgr.h \ - session-menu-mgr.c \ - online-accounts-mgr.c \ - online-accounts-mgr.h - -indicator_session_service_CFLAGS = \ - $(SESSIONSERVICE_CFLAGS) \ - $(GCONF_CFLAGS) \ - -DLIBEXECDIR=\"$(libexecdir)\" \ - -Wall \ - -DG_LOG_DOMAIN=\"Indicator-Session\" \ - $(COVERAGE_CFLAGS) -indicator_session_service_LDADD = \ - $(SESSIONSERVICE_LIBS) \ - $(GCONF_LIBS) -indicator_session_service_LDFLAGS = \ - $(COVERAGE_LDFLAGS) - -################# -# GTK Logout Stuff -################# - -if BUILD_GTKLOGOUTHELPER -gtk_logout_helper_SOURCES = \ - $(dbus_login1_manager_sources) \ - gtk-logout-helper.c \ - dialog.c \ - dialog.h - -gtk_logout_helper_CFLAGS = \ - $(SESSIONSERVICE_CFLAGS) \ - $(GTKLOGOUTHELPER_CFLAGS) \ - $(GCONF_CFLAGS) \ - $(COVERAGE_CFLAGS) \ - -Wall \ - -DINDICATOR_ICONS_DIR="\"$(INDICATORICONSDIR)\"" - -gtk_logout_helper_LDADD = \ - $(SESSIONSERVICE_LIBS) \ - $(GTKLOGOUTHELPER_LIBS) \ - $(GCONF_LIBS) - -gtk_logout_helper_LDFLAGS = \ - $(COVERAGE_LDFLAGS) -endif - - -############### -# Other Stuff -############### - -BUILT_SOURCES = \ - $(dbus_accounts_sources) \ - $(dbus_login1_manager_sources) \ - $(dbus_login1_user_sources) \ - $(dbus_login1_session_sources) \ - $(dbus_display_manager_sources) \ - $(dbus_user_sources) \ - gen-session-dbus.xml.c \ - gen-session-dbus.xml.h - -EXTRA_DIST += \ - display-manager.xml \ - org.freedesktop.Accounts.User.xml \ - org.freedesktop.Accounts.xml \ - org.freedesktop.login1.Manager.xml \ - org.freedesktop.login1.Session.xml \ - org.freedesktop.login1.User.xml \ - session-dbus.xml - -CLEANFILES += $(BUILT_SOURCES) diff --git a/src/actions.c b/src/actions.c new file mode 100644 index 0000000..563f626 --- /dev/null +++ b/src/actions.c @@ -0,0 +1,417 @@ + +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "actions.h" + +/*** +**** GObject Boilerplate +***/ + +G_DEFINE_TYPE (IndicatorSessionActions, indicator_session_actions, G_TYPE_OBJECT) + +enum +{ + PROP_0, + PROP_CAN_SWITCH, + PROP_CAN_HIBERNATE, + PROP_CAN_SUSPEND, + PROP_CAN_LOCK, + PROP_CAN_LOGOUT, + PROP_CAN_REBOOT, + PROP_CAN_PROMPT, + PROP_HAS_ONLINE_ACCOUNT_ERROR, + PROP_LAST +}; + +static GParamSpec *properties[PROP_LAST]; + +static void +my_get_property (GObject * o, + guint property_id, + GValue * value, + GParamSpec * pspec) +{ + IndicatorSessionActions * self = INDICATOR_SESSION_ACTIONS (o); + + switch (property_id) + { + case PROP_CAN_SWITCH: + g_value_set_boolean (value, indicator_session_actions_can_switch (self)); + break; + + case PROP_CAN_HIBERNATE: + g_value_set_boolean (value, indicator_session_actions_can_hibernate (self)); + break; + + case PROP_CAN_SUSPEND: + g_value_set_boolean (value, indicator_session_actions_can_suspend (self)); + break; + + case PROP_CAN_LOCK: + g_value_set_boolean (value, indicator_session_actions_can_lock (self)); + break; + + case PROP_CAN_LOGOUT: + g_value_set_boolean (value, indicator_session_actions_can_logout (self)); + break; + + case PROP_CAN_REBOOT: + g_value_set_boolean (value, indicator_session_actions_can_reboot (self)); + break; + + case PROP_CAN_PROMPT: + g_value_set_boolean (value, indicator_session_actions_can_prompt (self)); + break; + + case PROP_HAS_ONLINE_ACCOUNT_ERROR: + g_value_set_boolean (value, indicator_session_actions_has_online_account_error (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec); + } +} + +static void +/* cppcheck-suppress unusedFunction */ +indicator_session_actions_class_init (IndicatorSessionActionsClass * klass) +{ + GObjectClass * object_class; + const GParamFlags flags = G_PARAM_READABLE | G_PARAM_STATIC_STRINGS; + + object_class = G_OBJECT_CLASS (klass); + object_class->get_property = my_get_property; + + klass->can_lock = NULL; + klass->can_logout = NULL; + klass->can_reboot = NULL; + klass->can_switch = NULL; + klass->can_suspend = NULL; + klass->can_hibernate = NULL; + klass->has_online_account_error = NULL; + klass->logout = NULL; + klass->suspend = NULL; + klass->hibernate = NULL; + klass->reboot = NULL; + klass->power_off = NULL; + klass->switch_to_screensaver = NULL; + klass->switch_to_greeter = NULL; + klass->switch_to_guest = NULL; + klass->switch_to_username = NULL; + + /* properties */ + + properties[PROP_0] = NULL; + + properties[PROP_CAN_SWITCH] = + g_param_spec_boolean (INDICATOR_SESSION_ACTIONS_PROP_CAN_SWITCH, + "Can Switch Sessions", + "Whether or not the system services allow session switching", + TRUE, flags); + + properties[PROP_CAN_HIBERNATE] = + g_param_spec_boolean (INDICATOR_SESSION_ACTIONS_PROP_CAN_HIBERNATE, + "Can Hibernate", + "Whether or not the system services allow the user to hibernate", + TRUE, flags); + + properties[PROP_CAN_SUSPEND] = + g_param_spec_boolean (INDICATOR_SESSION_ACTIONS_PROP_CAN_SUSPEND, + "Can Suspend", + "Whether or not the system services allow the user to suspend", + TRUE, flags); + + properties[PROP_CAN_LOCK] = + g_param_spec_boolean (INDICATOR_SESSION_ACTIONS_PROP_CAN_LOCK, + "Can Lock", + "Whether or not the system services allow the user to lock the screen", + TRUE, flags); + + properties[PROP_CAN_LOGOUT] = + g_param_spec_boolean (INDICATOR_SESSION_ACTIONS_PROP_CAN_LOGOUT, + "Can Logout", + "Whether or not the system services allow the user to logout", + TRUE, flags); + + properties[PROP_CAN_REBOOT] = + g_param_spec_boolean (INDICATOR_SESSION_ACTIONS_PROP_CAN_REBOOT, + "Can Reboot", + "Whether or not the system services allow the user to reboot", + TRUE, flags); + + properties[PROP_CAN_PROMPT] = + g_param_spec_boolean (INDICATOR_SESSION_ACTIONS_PROP_CAN_PROMPT, + "Can Show End Session Dialog", + "Whether or not we can show an End Session dialog", + TRUE, flags); + + properties[PROP_HAS_ONLINE_ACCOUNT_ERROR] = + g_param_spec_boolean (INDICATOR_SESSION_ACTIONS_PROP_HAS_ONLINE_ACCOUNT_ERROR, + "Has Online Account Error", + "Whether or not an online account setting requires attention from the user", + FALSE, flags); + + g_object_class_install_properties (object_class, PROP_LAST, properties); +} + +static void +/* cppcheck-suppress unusedFunction */ +indicator_session_actions_init (IndicatorSessionActions * self G_GNUC_UNUSED) +{ +} + +/*** +**** +***/ + +gboolean +indicator_session_actions_can_lock (IndicatorSessionActions * self) +{ + g_return_val_if_fail (INDICATOR_IS_SESSION_ACTIONS (self), FALSE); + + return INDICATOR_SESSION_ACTIONS_GET_CLASS (self)->can_lock (self); +} + +gboolean +indicator_session_actions_can_logout (IndicatorSessionActions * self) +{ + g_return_val_if_fail (INDICATOR_IS_SESSION_ACTIONS (self), FALSE); + + return INDICATOR_SESSION_ACTIONS_GET_CLASS (self)->can_logout (self); +} + +gboolean +indicator_session_actions_can_reboot (IndicatorSessionActions * self) +{ + g_return_val_if_fail (INDICATOR_IS_SESSION_ACTIONS (self), FALSE); + + return INDICATOR_SESSION_ACTIONS_GET_CLASS (self)->can_reboot (self); +} + +gboolean +indicator_session_actions_can_switch (IndicatorSessionActions * self) +{ + g_return_val_if_fail (INDICATOR_IS_SESSION_ACTIONS (self), FALSE); + + return INDICATOR_SESSION_ACTIONS_GET_CLASS (self)->can_switch (self); +} + +gboolean +indicator_session_actions_can_suspend (IndicatorSessionActions * self) +{ + g_return_val_if_fail (INDICATOR_IS_SESSION_ACTIONS (self), FALSE); + + return INDICATOR_SESSION_ACTIONS_GET_CLASS (self)->can_suspend (self); +} + +gboolean +indicator_session_actions_can_hibernate (IndicatorSessionActions * self) +{ + g_return_val_if_fail (INDICATOR_IS_SESSION_ACTIONS (self), FALSE); + + return INDICATOR_SESSION_ACTIONS_GET_CLASS (self)->can_hibernate (self); +} + +gboolean +indicator_session_actions_can_prompt (IndicatorSessionActions * self) +{ + g_return_val_if_fail (INDICATOR_IS_SESSION_ACTIONS (self), FALSE); + + return INDICATOR_SESSION_ACTIONS_GET_CLASS (self)->can_prompt (self); +} + +gboolean +indicator_session_actions_has_online_account_error (IndicatorSessionActions * self) +{ + g_return_val_if_fail (INDICATOR_IS_SESSION_ACTIONS (self), FALSE); + + return INDICATOR_SESSION_ACTIONS_GET_CLASS (self)->has_online_account_error (self); +} + +/*** +**** +***/ + +void +indicator_session_actions_online_accounts (IndicatorSessionActions * self) +{ + g_return_if_fail (INDICATOR_IS_SESSION_ACTIONS (self)); + + INDICATOR_SESSION_ACTIONS_GET_CLASS (self)->online_accounts (self); +} + +void +indicator_session_actions_settings (IndicatorSessionActions * self) +{ + g_return_if_fail (INDICATOR_IS_SESSION_ACTIONS (self)); + + INDICATOR_SESSION_ACTIONS_GET_CLASS (self)->settings (self); +} + +void +indicator_session_actions_logout (IndicatorSessionActions * self) +{ + g_return_if_fail (INDICATOR_IS_SESSION_ACTIONS (self)); + + INDICATOR_SESSION_ACTIONS_GET_CLASS (self)->logout (self); +} + +void +indicator_session_actions_power_off (IndicatorSessionActions * self) +{ + g_return_if_fail (INDICATOR_IS_SESSION_ACTIONS (self)); + + INDICATOR_SESSION_ACTIONS_GET_CLASS (self)->power_off (self); +} + +void +indicator_session_actions_help (IndicatorSessionActions * self) +{ + g_return_if_fail (INDICATOR_IS_SESSION_ACTIONS (self)); + + INDICATOR_SESSION_ACTIONS_GET_CLASS (self)->help (self); +} + +void +indicator_session_actions_about (IndicatorSessionActions * self) +{ + g_return_if_fail (INDICATOR_IS_SESSION_ACTIONS (self)); + + INDICATOR_SESSION_ACTIONS_GET_CLASS (self)->about (self); +} + +void +indicator_session_actions_reboot (IndicatorSessionActions * self) +{ + g_return_if_fail (INDICATOR_IS_SESSION_ACTIONS (self)); + + INDICATOR_SESSION_ACTIONS_GET_CLASS (self)->reboot (self); +} + +void +indicator_session_actions_suspend (IndicatorSessionActions * self) +{ + g_return_if_fail (INDICATOR_IS_SESSION_ACTIONS (self)); + + INDICATOR_SESSION_ACTIONS_GET_CLASS (self)->suspend (self); +} + +void +indicator_session_actions_hibernate (IndicatorSessionActions * self) +{ + g_return_if_fail (INDICATOR_IS_SESSION_ACTIONS (self)); + + INDICATOR_SESSION_ACTIONS_GET_CLASS (self)->hibernate (self); +} + +void +indicator_session_actions_switch_to_screensaver (IndicatorSessionActions * self) +{ + g_return_if_fail (INDICATOR_IS_SESSION_ACTIONS (self)); + + INDICATOR_SESSION_ACTIONS_GET_CLASS (self)->switch_to_screensaver (self); +} + +void +indicator_session_actions_switch_to_greeter (IndicatorSessionActions * self) +{ + g_return_if_fail (INDICATOR_IS_SESSION_ACTIONS (self)); + + INDICATOR_SESSION_ACTIONS_GET_CLASS (self)->switch_to_greeter (self); +} + +void +indicator_session_actions_switch_to_guest (IndicatorSessionActions * self) +{ + g_return_if_fail (INDICATOR_IS_SESSION_ACTIONS (self)); + + INDICATOR_SESSION_ACTIONS_GET_CLASS (self)->switch_to_guest (self); +} + +void +indicator_session_actions_switch_to_username (IndicatorSessionActions * self, + const gchar * username) +{ + g_return_if_fail (INDICATOR_IS_SESSION_ACTIONS (self)); + + INDICATOR_SESSION_ACTIONS_GET_CLASS (self)->switch_to_username (self, username); +} + +/*** +**** +***/ + +static void +notify_func (IndicatorSessionActions * self, int prop) +{ + g_return_if_fail (INDICATOR_IS_SESSION_ACTIONS (self)); + + g_debug ("%s %s emitting '%s' prop notify", G_STRLOC, G_STRFUNC, properties[prop]->name); + + g_object_notify_by_pspec (G_OBJECT(self), properties[prop]); +} + +void +indicator_session_actions_notify_can_lock (IndicatorSessionActions * self) +{ + notify_func (self, PROP_CAN_LOCK); +} + +void +indicator_session_actions_notify_can_logout (IndicatorSessionActions * self) +{ + notify_func (self, PROP_CAN_LOGOUT); +} + +void +indicator_session_actions_notify_can_reboot (IndicatorSessionActions * self) +{ + notify_func (self, PROP_CAN_REBOOT); +} + +void +indicator_session_actions_notify_can_switch (IndicatorSessionActions * self) +{ + notify_func (self, PROP_CAN_SWITCH); +} + +void +indicator_session_actions_notify_can_suspend (IndicatorSessionActions * self) +{ + notify_func (self, PROP_CAN_SUSPEND); +} + +void +indicator_session_actions_notify_can_hibernate (IndicatorSessionActions * self) +{ + notify_func (self, PROP_CAN_HIBERNATE); +} + +void +indicator_session_actions_notify_can_prompt (IndicatorSessionActions * self) +{ + notify_func (self, PROP_CAN_PROMPT); +} + +void +indicator_session_actions_notify_has_online_account_error (IndicatorSessionActions * self) +{ + notify_func (self, PROP_HAS_ONLINE_ACCOUNT_ERROR); +} diff --git a/src/actions.h b/src/actions.h new file mode 100644 index 0000000..e31685e --- /dev/null +++ b/src/actions.h @@ -0,0 +1,135 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __INDICATOR_SESSION_ACTIONS_H__ +#define __INDICATOR_SESSION_ACTIONS_H__ + +#include <glib.h> +#include <glib-object.h> + +G_BEGIN_DECLS + +/* standard GObject macros */ +#define INDICATOR_TYPE_SESSION_ACTIONS (indicator_session_actions_get_type()) +#define INDICATOR_SESSION_ACTIONS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_SESSION_ACTIONS, IndicatorSessionActions)) +#define INDICATOR_SESSION_ACTIONS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_SESSION_ACTIONS, IndicatorSessionActionsClass)) +#define INDICATOR_SESSION_ACTIONS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), INDICATOR_TYPE_SESSION_ACTIONS, IndicatorSessionActionsClass)) +#define INDICATOR_IS_SESSION_ACTIONS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_SESSION_ACTIONS)) + +typedef struct _IndicatorSessionActions IndicatorSessionActions; +typedef struct _IndicatorSessionActionsClass IndicatorSessionActionsClass; + +/* property keys */ +#define INDICATOR_SESSION_ACTIONS_PROP_CAN_LOCK "can-lock" +#define INDICATOR_SESSION_ACTIONS_PROP_CAN_LOGOUT "can-logout" +#define INDICATOR_SESSION_ACTIONS_PROP_CAN_REBOOT "can-reboot" +#define INDICATOR_SESSION_ACTIONS_PROP_CAN_SWITCH "can-switch" +#define INDICATOR_SESSION_ACTIONS_PROP_CAN_SUSPEND "can-suspend" +#define INDICATOR_SESSION_ACTIONS_PROP_CAN_HIBERNATE "can-hibernate" +#define INDICATOR_SESSION_ACTIONS_PROP_CAN_PROMPT "can-show-end-session-dialog" +#define INDICATOR_SESSION_ACTIONS_PROP_HAS_ONLINE_ACCOUNT_ERROR "has-online-account-error" + +/** + * A base class for invoking and getting state information on system actions. + * Use backend.h's get_backend() to get an instance. + */ +struct _IndicatorSessionActions +{ + /*< private >*/ + GObject parent; +}; + +struct _IndicatorSessionActionsClass +{ + GObjectClass parent_class; + + /* pure virtual functions */ + + gboolean (*can_lock) (IndicatorSessionActions * self); + gboolean (*can_logout) (IndicatorSessionActions * self); + gboolean (*can_reboot) (IndicatorSessionActions * self); + gboolean (*can_switch) (IndicatorSessionActions * self); + gboolean (*can_suspend) (IndicatorSessionActions * self); + gboolean (*can_hibernate) (IndicatorSessionActions * self); + gboolean (*can_prompt) (IndicatorSessionActions * self); + gboolean (*has_online_account_error) (IndicatorSessionActions * self); + + void (*suspend) (IndicatorSessionActions * self); + void (*hibernate) (IndicatorSessionActions * self); + void (*logout) (IndicatorSessionActions * self); + void (*reboot) (IndicatorSessionActions * self); + void (*power_off) (IndicatorSessionActions * self); + void (*help) (IndicatorSessionActions * self); + void (*about) (IndicatorSessionActions * self); + void (*settings) (IndicatorSessionActions * self); + void (*online_accounts) (IndicatorSessionActions * self); + + void (*switch_to_greeter) (IndicatorSessionActions * self); + void (*switch_to_screensaver) (IndicatorSessionActions * self); + void (*switch_to_guest) (IndicatorSessionActions * self); + void (*switch_to_username) (IndicatorSessionActions * self, + const gchar * username); +}; + +/*** +**** +***/ + +GType indicator_session_actions_get_type (void); + +gboolean indicator_session_actions_can_lock (IndicatorSessionActions * self); +gboolean indicator_session_actions_can_logout (IndicatorSessionActions * self); +gboolean indicator_session_actions_can_reboot (IndicatorSessionActions * self); +gboolean indicator_session_actions_can_switch (IndicatorSessionActions * self); +gboolean indicator_session_actions_can_suspend (IndicatorSessionActions * self); +gboolean indicator_session_actions_can_hibernate (IndicatorSessionActions * self); +gboolean indicator_session_actions_can_prompt (IndicatorSessionActions * self); +gboolean indicator_session_actions_has_online_account_error (IndicatorSessionActions * self); + + +void indicator_session_actions_notify_can_lock (IndicatorSessionActions * self); +void indicator_session_actions_notify_can_logout (IndicatorSessionActions * self); +void indicator_session_actions_notify_can_reboot (IndicatorSessionActions * self); +void indicator_session_actions_notify_can_switch (IndicatorSessionActions * self); +void indicator_session_actions_notify_can_suspend (IndicatorSessionActions * self); +void indicator_session_actions_notify_can_hibernate (IndicatorSessionActions * self); +void indicator_session_actions_notify_can_prompt (IndicatorSessionActions * self); +void indicator_session_actions_notify_has_online_account_error (IndicatorSessionActions * self); + +void indicator_session_actions_lock (IndicatorSessionActions * self); +void indicator_session_actions_suspend (IndicatorSessionActions * self); +void indicator_session_actions_hibernate (IndicatorSessionActions * self); +void indicator_session_actions_logout (IndicatorSessionActions * self); +void indicator_session_actions_reboot (IndicatorSessionActions * self); +void indicator_session_actions_power_off (IndicatorSessionActions * self); + +void indicator_session_actions_help (IndicatorSessionActions * self); +void indicator_session_actions_about (IndicatorSessionActions * self); +void indicator_session_actions_settings (IndicatorSessionActions * self); +void indicator_session_actions_online_accounts (IndicatorSessionActions * self); + +void indicator_session_actions_switch_to_screensaver (IndicatorSessionActions * self); +void indicator_session_actions_switch_to_greeter (IndicatorSessionActions * self); +void indicator_session_actions_switch_to_guest (IndicatorSessionActions * self); +void indicator_session_actions_switch_to_username (IndicatorSessionActions * self, + const gchar * username); + +G_END_DECLS + +#endif /* __INDICATOR_SESSION_ACTIONS_H__ */ diff --git a/src/backend-dbus/CMakeLists.txt b/src/backend-dbus/CMakeLists.txt new file mode 100644 index 0000000..fa41534 --- /dev/null +++ b/src/backend-dbus/CMakeLists.txt @@ -0,0 +1,57 @@ +include (GdbusCodegen) + +set(BACKEND_GENERATED_SOURCES +) + +add_gdbus_codegen (BACKEND_GENERATED_SOURCES dbus-display-manager + org.freedesktop + ${CMAKE_CURRENT_SOURCE_DIR}/org.freedesktop.DisplayManager.Seat.xml) + +add_gdbus_codegen (BACKEND_GENERATED_SOURCES dbus-webcredentials + com.canonical.indicators + ${CMAKE_CURRENT_SOURCE_DIR}/com.canonical.indicators.webcredentials.xml) + +add_gdbus_codegen (BACKEND_GENERATED_SOURCES dbus-accounts + org.freedesktop + ${CMAKE_CURRENT_SOURCE_DIR}/org.freedesktop.Accounts.xml) + +add_gdbus_codegen (BACKEND_GENERATED_SOURCES dbus-user + org.freedesktop + ${CMAKE_CURRENT_SOURCE_DIR}/org.freedesktop.Accounts.User.xml) + +add_gdbus_codegen (BACKEND_GENERATED_SOURCES dbus-login1-manager + org.freedesktop + ${CMAKE_CURRENT_SOURCE_DIR}/org.freedesktop.login1.Manager.xml) + +add_gdbus_codegen (BACKEND_GENERATED_SOURCES dbus-login1-seat + org.freedesktop + ${CMAKE_CURRENT_SOURCE_DIR}/org.freedesktop.login1.Seat.xml) + +add_gdbus_codegen (BACKEND_GENERATED_SOURCES dbus-login1-user + org.freedesktop + ${CMAKE_CURRENT_SOURCE_DIR}/org.freedesktop.login1.User.xml) + +add_gdbus_codegen (BACKEND_GENERATED_SOURCES gnome-screen-saver + org + ${CMAKE_CURRENT_SOURCE_DIR}/org.gnome.ScreenSaver.xml) + +add_gdbus_codegen (BACKEND_GENERATED_SOURCES gnome-session-manager + org + ${CMAKE_CURRENT_SOURCE_DIR}/org.gnome.SessionManager.xml) + +add_gdbus_codegen (BACKEND_GENERATED_SOURCES dbus-end-session-dialog + org.gnome.SessionManager + ${CMAKE_CURRENT_SOURCE_DIR}/org.gnome.SessionManager.EndSessionDialog.xml) + +set (SOURCES actions.c guest.c users.c backend-dbus.c utils.c) + +# add warnings/coverage info on handwritten files +# but not the autogenerated ones... +set_source_files_properties (${SOURCES} + PROPERTIES COMPILE_FLAGS " -g ${CC_WARNING_ARGS} ${GCOV_FLAGS}") + +# add the bin dir to our include path s.t. our code can find the autogenerated header files +include_directories (${CMAKE_CURRENT_BINARY_DIR} ${SERVICE_INCLUDE_DIRS}) + +add_library (backenddbus STATIC ${SOURCES} ${BACKEND_GENERATED_SOURCES}) + diff --git a/src/backend-dbus/actions.c b/src/backend-dbus/actions.c new file mode 100644 index 0000000..9030ca7 --- /dev/null +++ b/src/backend-dbus/actions.c @@ -0,0 +1,970 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <glib/gi18n.h> + +#include "dbus-end-session-dialog.h" +#include "dbus-login1-manager.h" +#include "dbus-webcredentials.h" +#include "gnome-screen-saver.h" +#include "gnome-session-manager.h" + +#include "actions.h" + +enum +{ + END_SESSION_TYPE_LOGOUT = 0, + END_SESSION_TYPE_SHUTDOWN, + END_SESSION_TYPE_REBOOT +}; + +struct _IndicatorSessionActionsDbusPriv +{ + GCancellable * cancellable; + + GSettings * lockdown_settings; + GSettings * indicator_settings; + GnomeScreenSaver * screen_saver; + GnomeSessionManager * session_manager; + Login1Manager * login1_manager; + GCancellable * login1_manager_cancellable; + Login1Seat * login1_seat; + DisplayManagerSeat * dm_seat; + GCancellable * dm_seat_cancellable; + Webcredentials * webcredentials; + EndSessionDialog * end_session_dialog; + char * zenity; + + gboolean can_suspend; + gboolean can_hibernate; + gboolean seat_allows_activation; +}; + +typedef IndicatorSessionActionsDbusPriv priv_t; + +G_DEFINE_TYPE (IndicatorSessionActionsDbus, + indicator_session_actions_dbus, + INDICATOR_TYPE_SESSION_ACTIONS) + +/*** +**** +***/ + +static void +log_and_clear_error (GError ** err, const char * loc, const char * func) +{ + if (*err) + { + if (!g_error_matches (*err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("%s %s: %s", loc, func, (*err)->message); + + g_clear_error (err); + } +} + +/*** +**** +***/ + +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) +{ + priv_t * p = self->priv; + gboolean b; + + b = login1_seat_get_can_multi_session (p->login1_seat); + + if (p->seat_allows_activation != b) + { + p->seat_allows_activation = b; + + indicator_session_actions_notify_can_switch (INDICATOR_SESSION_ACTIONS(self)); + } +} + +static void +set_login1_seat (IndicatorSessionActionsDbus * self, Login1Seat * seat) +{ + priv_t * p = self->priv; + + if (p->login1_seat != NULL) + { + g_signal_handlers_disconnect_by_data (p->login1_seat, self); + g_clear_object (&p->login1_seat); + } + + if (seat != NULL) + { + p->login1_seat = g_object_ref (seat); + + g_signal_connect_swapped (seat, "notify::can-multi-session", + G_CALLBACK(on_seat_notify_multi_session), self); + } +} + +/*** +**** +***/ + +static void +set_dm_seat (IndicatorSessionActionsDbus * self, DisplayManagerSeat * seat) +{ + priv_t * p = self->priv; + + if (p->dm_seat != NULL) + { + g_cancellable_cancel (p->dm_seat_cancellable); + g_clear_object (&p->dm_seat); + g_clear_object (&p->dm_seat); + } + + if (seat != NULL) + { + p->dm_seat = g_object_ref (seat); + p->dm_seat_cancellable = g_cancellable_new (); + } +} + +static void +on_screensaver_proxy_ready (GObject * o G_GNUC_UNUSED, GAsyncResult * res, gpointer gself) +{ + GError * err; + GnomeScreenSaver * ss; + + err = NULL; + ss = gnome_screen_saver_proxy_new_for_bus_finish (res, &err); + if (err == NULL) + { + INDICATOR_SESSION_ACTIONS_DBUS(gself)->priv->screen_saver = ss; + } + + log_and_clear_error (&err, G_STRLOC, G_STRFUNC); +} + +static void +on_can_suspend_ready (GObject * o, GAsyncResult * res, gpointer gself) +{ + char * str; + GError * err; + + str = NULL; + err = NULL; + login1_manager_call_can_suspend_finish (LOGIN1_MANAGER(o), &str, res, &err); + if (err == NULL) + { + priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(gself)->priv; + + const gboolean b = !g_strcmp0 (str, "yes"); + + if (p->can_suspend != b) + { + p->can_suspend = b; + indicator_session_actions_notify_can_suspend (gself); + } + + g_free (str); + } + + log_and_clear_error (&err, G_STRLOC, G_STRFUNC); +} + +static void +on_can_hibernate_ready (GObject * o, GAsyncResult * res, gpointer gself) +{ + gchar * str; + GError * err; + + str = NULL; + err = NULL; + login1_manager_call_can_hibernate_finish (LOGIN1_MANAGER(o), &str, res, &err); + if (err == NULL) + { + priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(gself)->priv; + + const gboolean b = !g_strcmp0 (str, "yes"); + + if (p->can_hibernate != b) + { + p->can_hibernate = b; + indicator_session_actions_notify_can_hibernate (gself); + } + + g_free (str); + } + + log_and_clear_error (&err, G_STRLOC, G_STRFUNC); +} + +static void +set_login1_manager (IndicatorSessionActionsDbus * self, + Login1Manager * login1_manager) +{ + priv_t * p = self->priv; + + if (p->login1_manager != NULL) + { + g_cancellable_cancel (p->login1_manager_cancellable); + g_clear_object (&p->login1_manager_cancellable); + g_clear_object (&p->login1_manager); + } + + if (login1_manager != NULL) + { + p->login1_manager_cancellable = g_cancellable_new (); + + p->login1_manager = g_object_ref (login1_manager); + + login1_manager_call_can_suspend (p->login1_manager, + p->login1_manager_cancellable, + on_can_suspend_ready, + self); + + login1_manager_call_can_hibernate (p->login1_manager, + p->login1_manager_cancellable, + on_can_hibernate_ready, + self); + } +} + +static void +on_session_manager_proxy_ready (GObject * o G_GNUC_UNUSED, GAsyncResult * res, gpointer gself) +{ + GError * err; + GnomeSessionManager * sm; + + err = NULL; + sm = gnome_session_manager_proxy_new_for_bus_finish (res, &err); + if (err == NULL) + { + INDICATOR_SESSION_ACTIONS_DBUS(gself)->priv->session_manager = sm; + } + + log_and_clear_error (&err, G_STRLOC, G_STRFUNC); +} + +static void +on_webcredentials_proxy_ready (GObject * o G_GNUC_UNUSED, GAsyncResult * res, gpointer gself) +{ + GError * err; + Webcredentials * webcredentials; + + err = NULL; + webcredentials = webcredentials_proxy_new_for_bus_finish (res, &err); + if (err == NULL) + { + INDICATOR_SESSION_ACTIONS_DBUS(gself)->priv->webcredentials = webcredentials; + + g_signal_connect_swapped (webcredentials, "notify::error-status", + G_CALLBACK(indicator_session_actions_notify_has_online_account_error), gself); + + if (webcredentials_get_error_status (webcredentials)) + indicator_session_actions_notify_has_online_account_error (gself); + } + + log_and_clear_error (&err, G_STRLOC, G_STRFUNC); +} + +static void +on_end_session_dialog_proxy_ready (GObject * o G_GNUC_UNUSED, GAsyncResult * res, gpointer gself) +{ + GError * err; + EndSessionDialog * end_session_dialog; + + err = NULL; + end_session_dialog = end_session_dialog_proxy_new_for_bus_finish (res, &err); + if (err == NULL) + { + 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); +} + +/*** +**** Virtual Functions +***/ + +static gboolean +my_can_lock (IndicatorSessionActions * self) +{ + priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv; + + return !g_settings_get_boolean (p->lockdown_settings, "disable-lock-screen"); +} + +static gboolean +my_can_logout (IndicatorSessionActions * self) +{ + priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv; + + 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 +my_can_switch (IndicatorSessionActions * self) +{ + const priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv; + + return p->seat_allows_activation + && !g_settings_get_boolean (p->lockdown_settings, "disable-user-switching"); +} + +static gboolean +my_can_suspend (IndicatorSessionActions * self) +{ + const priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv; + + return p && p->can_suspend; +} + +static gboolean +my_can_hibernate (IndicatorSessionActions * self) +{ + const priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv; + + return p && p->can_hibernate; +} + +static gboolean +my_can_prompt (IndicatorSessionActions * self) +{ + return get_prompt_status(INDICATOR_SESSION_ACTIONS_DBUS(self)) != PROMPT_NONE; +} + +static gboolean +my_has_online_account_error (IndicatorSessionActions * self) +{ + const priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv; + + return p && (p->webcredentials) && (webcredentials_get_error_status (p->webcredentials)); +} + +static void +my_suspend (IndicatorSessionActions * self) +{ + priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv; + + g_return_if_fail (p->login1_manager != NULL); + + login1_manager_call_suspend (p->login1_manager, + FALSE, + p->login1_manager_cancellable, + NULL, + NULL); +} + +static void +my_hibernate (IndicatorSessionActions * self) +{ + priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv; + + g_return_if_fail (p->login1_manager != NULL); + + login1_manager_call_hibernate (p->login1_manager, + FALSE, + p->login1_manager_cancellable, + NULL, + NULL); +} + +/*** +**** End Session Dialog +***/ + +static void +logout_now (IndicatorSessionActionsDbus * self) +{ + priv_t * p = self->priv; + + g_return_if_fail (p->session_manager != NULL); + + gnome_session_manager_call_logout (p->session_manager, + 1, /* don't prompt */ + p->cancellable, + NULL, + NULL); +} + +static void +on_reboot_response (GObject * o, + GAsyncResult * res, + gpointer unused G_GNUC_UNUSED) +{ + 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 (IndicatorSessionActionsDbus * self) +{ + 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, + on_reboot_response, + NULL); +} + +static void +power_off_now (IndicatorSessionActionsDbus * self) +{ + priv_t * p = self->priv; + + g_return_if_fail (p->login1_manager != NULL); + + login1_manager_call_power_off (p->login1_manager, + FALSE, + p->login1_manager_cancellable, + NULL, + NULL); +} + +static void +stop_listening_to_dialog (IndicatorSessionActionsDbus * self) +{ + g_signal_handlers_disconnect_by_data (self->priv->end_session_dialog, self); +} +static void +on_end_session_dialog_canceled (IndicatorSessionActionsDbus * self) +{ + stop_listening_to_dialog (self); +} +static void +on_end_session_dialog_closed (IndicatorSessionActionsDbus * self) +{ + stop_listening_to_dialog (self); +} + +static void +on_open_end_session_dialog_ready (GObject * o, + GAsyncResult * res, + gpointer gself G_GNUC_UNUSED) +{ + GError * err = NULL; + end_session_dialog_call_open_finish (END_SESSION_DIALOG(o), res, &err); + log_and_clear_error (&err, G_STRLOC, G_STRFUNC); +} + +static void +show_unity_end_session_dialog (IndicatorSessionActionsDbus * self, int type) +{ + priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv; + gpointer o = p->end_session_dialog; + const char * inhibitor_paths[] = { NULL }; + + g_assert (o != NULL); + + 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); + g_signal_connect_swapped (o, "closed", G_CALLBACK(on_end_session_dialog_closed), self); + + end_session_dialog_call_open (p->end_session_dialog, type, 0, 0, inhibitor_paths, + p->cancellable, + on_open_end_session_dialog_ready, + self); +} + +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 + { + 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 ("<big><b>%s</b></big>\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 * 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_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 * actions) +{ + 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; + } +} + +/*** +**** +***/ + +static void +run_outside_app (const char * cmd) +{ + GError * err = NULL; + g_debug ("%s calling \"%s\"", G_STRFUNC, cmd); + g_spawn_command_line_async (cmd, &err); + log_and_clear_error (&err, G_STRLOC, G_STRFUNC); +} + +static void +my_help (IndicatorSessionActions * self G_GNUC_UNUSED) +{ + run_outside_app ("yelp"); +} + +static void +my_settings (IndicatorSessionActions * self G_GNUC_UNUSED) +{ + run_outside_app ("gnome-control-center"); +} + +static void +my_online_accounts (IndicatorSessionActions * self G_GNUC_UNUSED) +{ + run_outside_app ("gnome-control-center credentials"); +} + +static void +my_about (IndicatorSessionActions * self G_GNUC_UNUSED) +{ + run_outside_app ("gnome-control-center info"); +} + +/*** +**** +***/ + +static void +my_switch_to_screensaver (IndicatorSessionActions * self) +{ + priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv; + + g_return_if_fail (p->screen_saver != NULL); + + gnome_screen_saver_call_lock (p->screen_saver, p->cancellable, NULL, NULL); +} + +static void +my_switch_to_greeter (IndicatorSessionActions * self) +{ + priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv; + + g_return_if_fail (p->dm_seat != NULL); + + display_manager_seat_call_switch_to_greeter (p->dm_seat, + p->dm_seat_cancellable, + NULL, NULL); +} + +static void +my_switch_to_guest (IndicatorSessionActions * self) +{ + priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv; + + g_return_if_fail (p->dm_seat != NULL); + + display_manager_seat_call_switch_to_guest (p->dm_seat, "", + p->dm_seat_cancellable, + NULL, NULL); +} + +static void +my_switch_to_username (IndicatorSessionActions * self, const char * username) +{ + priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv; + + g_return_if_fail (p->dm_seat != NULL); + + display_manager_seat_call_switch_to_user (p->dm_seat, username, "", + p->dm_seat_cancellable, + NULL, NULL); +} + +static void +my_dispose (GObject * o) +{ + IndicatorSessionActionsDbus * self = INDICATOR_SESSION_ACTIONS_DBUS (o); + priv_t * p = self->priv; + + if (p->cancellable != NULL) + { + g_cancellable_cancel (p->cancellable); + g_clear_object (&p->cancellable); + } + + 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); + set_dm_seat (self, NULL); + set_login1_manager (self, NULL); + set_login1_seat (self, NULL); + + 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 +***/ + +static void +/* cppcheck-suppress unusedFunction */ +indicator_session_actions_dbus_class_init (IndicatorSessionActionsDbusClass * klass) +{ + GObjectClass * object_class; + IndicatorSessionActionsClass * actions_class; + + 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; + actions_class->can_prompt = my_can_prompt; + actions_class->has_online_account_error = my_has_online_account_error; + actions_class->logout = my_logout; + actions_class->suspend = my_suspend; + actions_class->hibernate = my_hibernate; + actions_class->reboot = my_reboot; + actions_class->power_off = my_power_off; + actions_class->settings = my_settings; + actions_class->online_accounts = my_online_accounts; + actions_class->help = my_help; + actions_class->about = my_about; + actions_class->switch_to_screensaver = my_switch_to_screensaver; + actions_class->switch_to_greeter = my_switch_to_greeter; + actions_class->switch_to_guest = my_switch_to_guest; + actions_class->switch_to_username = my_switch_to_username; + + g_type_class_add_private (klass, sizeof (IndicatorSessionActionsDbusPriv)); +} + +static void +/* cppcheck-suppress unusedFunction */ +indicator_session_actions_dbus_init (IndicatorSessionActionsDbus * self) +{ + priv_t * p; + GSettings * s; + + p = G_TYPE_INSTANCE_GET_PRIVATE (self, + INDICATOR_TYPE_SESSION_ACTIONS_DBUS, + IndicatorSessionActionsDbusPriv); + p->cancellable = g_cancellable_new (); + 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); + g_signal_connect_swapped (s, "changed::disable-log-out", + G_CALLBACK(indicator_session_actions_notify_can_logout), self); + g_signal_connect_swapped (s, "changed::disable-user-switching", + 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", + "/org/gnome/ScreenSaver", + p->cancellable, + on_screensaver_proxy_ready, + self); + + gnome_session_manager_proxy_new_for_bus (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + "org.gnome.SessionManager", + "/org/gnome/SessionManager", + p->cancellable, + on_session_manager_proxy_ready, + self); + + webcredentials_proxy_new_for_bus (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, + "com.canonical.indicators.webcredentials", + "/com/canonical/indicators/webcredentials", + p->cancellable, + on_webcredentials_proxy_ready, + self); + + end_session_dialog_proxy_new_for_bus (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, + "com.canonical.Unity", + "/org/gnome/SessionManager/EndSessionDialog", + p->cancellable, + on_end_session_dialog_proxy_ready, + self); +} + +/*** +**** Public +***/ + +IndicatorSessionActions * +indicator_session_actions_dbus_new (void) +{ + gpointer o = g_object_new (INDICATOR_TYPE_SESSION_ACTIONS_DBUS, NULL); + + return INDICATOR_SESSION_ACTIONS (o); +} + +void +indicator_session_actions_dbus_set_proxies (IndicatorSessionActionsDbus * self, + Login1Manager * login1_manager, + Login1Seat * login1_seat, + DisplayManagerSeat * dm_seat) +{ + g_return_if_fail (INDICATOR_IS_SESSION_ACTIONS_DBUS(self)); + + set_login1_manager (self, login1_manager); + set_login1_seat (self, login1_seat); + set_dm_seat (self, dm_seat); +} diff --git a/src/backend-dbus/actions.h b/src/backend-dbus/actions.h new file mode 100644 index 0000000..d3d722d --- /dev/null +++ b/src/backend-dbus/actions.h @@ -0,0 +1,71 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __INDICATOR_SESSION_ACTIONS_DBUS_H__ +#define __INDICATOR_SESSION_ACTIONS_DBUS_H__ + +#include <glib.h> +#include <glib-object.h> + +#include "../actions.h" /* parent class */ +#include "dbus-login1-manager.h" +#include "dbus-login1-seat.h" +#include "dbus-display-manager.h" + + +G_BEGIN_DECLS + +#define INDICATOR_TYPE_SESSION_ACTIONS_DBUS (indicator_session_actions_dbus_get_type()) +#define INDICATOR_SESSION_ACTIONS_DBUS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_SESSION_ACTIONS_DBUS, IndicatorSessionActionsDbus)) +#define INDICATOR_SESSION_ACTIONS_DBUS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_SESSION_ACTIONS_DBUS, IndicatorSessionActionsDbusClass)) +#define INDICATOR_IS_SESSION_ACTIONS_DBUS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_SESSION_ACTIONS_DBUS)) + +typedef struct _IndicatorSessionActionsDbus IndicatorSessionActionsDbus; +typedef struct _IndicatorSessionActionsDbusPriv IndicatorSessionActionsDbusPriv; +typedef struct _IndicatorSessionActionsDbusClass IndicatorSessionActionsDbusClass; + +/** + * An implementation of IndicatorSessionActions that gets its user information + * from org.freedesktop.login1 org.freedesktop.DisplayManager over DBus. + */ +struct _IndicatorSessionActionsDbus +{ + /*< private >*/ + IndicatorSessionActions parent; + IndicatorSessionActionsDbusPriv * priv; +}; + +struct _IndicatorSessionActionsDbusClass +{ + IndicatorSessionActionsClass parent_class; +}; + +GType indicator_session_actions_dbus_get_type (void); + +IndicatorSessionActions * indicator_session_actions_dbus_new (void); + +void indicator_session_actions_dbus_set_proxies (IndicatorSessionActionsDbus * self, + Login1Manager * login1_manager, + Login1Seat * login1_seat, + DisplayManagerSeat * dm_seat); + + +G_END_DECLS + +#endif /* __INDICATOR_SESSION_ACTIONS_DBUS_H__ */ diff --git a/src/backend-dbus/backend-dbus.c b/src/backend-dbus/backend-dbus.c new file mode 100644 index 0000000..547c6ab --- /dev/null +++ b/src/backend-dbus/backend-dbus.c @@ -0,0 +1,115 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "actions.h" +#include "backend-dbus.h" +#include "guest.h" +#include "users.h" +#include "utils.h" + +struct dbus_world_data +{ + GCancellable * cancellable; + IndicatorSessionActionsDbus * actions; + IndicatorSessionUsersDbus * users; + IndicatorSessionGuestDbus * guest; +}; + +static void +on_proxies_ready (Login1Manager * login1_manager, + Login1Seat * login1_seat, + DisplayManagerSeat * display_manager_seat, + Accounts * account_manager, + GCancellable * cancellable, + gpointer gdata) +{ + struct dbus_world_data * data = gdata; + + if (!g_cancellable_is_cancelled (cancellable)) + { + if (data->actions != NULL) + indicator_session_actions_dbus_set_proxies (data->actions, + login1_manager, + login1_seat, + display_manager_seat); + + if (data->users != NULL) + indicator_session_users_dbus_set_proxies (data->users, + login1_manager, + login1_seat, + display_manager_seat, + account_manager); + + if (data->guest != NULL) + indicator_session_guest_dbus_set_proxies (data->guest, + login1_manager, + login1_seat, + display_manager_seat); + } + + g_free (data); +} + +/*** +**** +***/ + +void +backend_get (GCancellable * cancellable, + IndicatorSessionActions ** setme_actions, + IndicatorSessionUsers ** setme_users, + IndicatorSessionGuest ** setme_guest) +{ + struct dbus_world_data * data; + + data = g_new0 (struct dbus_world_data, 1); + + if (setme_actions != NULL) + { + IndicatorSessionActions * actions; + actions = indicator_session_actions_dbus_new (); + data->actions = INDICATOR_SESSION_ACTIONS_DBUS (actions); + + *setme_actions = actions; + } + + if (setme_users != NULL) + { + IndicatorSessionUsers * users; + users = indicator_session_users_dbus_new (); + data->users = INDICATOR_SESSION_USERS_DBUS (users); + + *setme_users = users; + } + + if (setme_guest != NULL) + { + IndicatorSessionGuest * guest; + guest = indicator_session_guest_dbus_new (); + data->guest = INDICATOR_SESSION_GUEST_DBUS (guest); + + *setme_guest = guest; + } + + data->cancellable = g_object_ref (cancellable); + + indicator_session_util_get_session_proxies (on_proxies_ready, + data->cancellable, + data); +} diff --git a/src/backend-dbus/backend-dbus.h b/src/backend-dbus/backend-dbus.h new file mode 100644 index 0000000..daf6ac3 --- /dev/null +++ b/src/backend-dbus/backend-dbus.h @@ -0,0 +1,38 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __INDICATOR_SESSION_BACKEND_DESKTOP_H__ +#define __INDICATOR_SESSION_BACKEND_DESKTOP_H__ + +#include <gio/gio.h> /* GCancellable */ + +#include "../actions.h" +#include "../guest.h" +#include "../users.h" + +G_BEGIN_DECLS + +void backend_get (GCancellable * cancellable, + IndicatorSessionActions ** setme_actions, + IndicatorSessionUsers ** setme_users, + IndicatorSessionGuest ** setme_guest); + +G_END_DECLS + +#endif diff --git a/src/backend-dbus/com.canonical.indicators.webcredentials.xml b/src/backend-dbus/com.canonical.indicators.webcredentials.xml new file mode 100644 index 0000000..d215081 --- /dev/null +++ b/src/backend-dbus/com.canonical.indicators.webcredentials.xml @@ -0,0 +1,82 @@ +<node> +<!-- + com.canonical.indicators.webcredentials: + @short_description: interface for handling login failures. + + The service implementing this interface keeps track of login failures. + Failures are reported (usually by signon-ui) using the ReportFailure method, + are listed in the Failures property and can be removed by calling + RemoveFailures. + + The ClearErrorStatus method can be called to clear the error indicator from + the system user menu. +--> +<interface name="com.canonical.indicators.webcredentials"> + <!-- + ReportFailure: + @account-id: the libaccounts ID of the account which failed to login. + @notification: dictionary of parameters for the OSD notification. + + Inform the service about a failing account. The @account-id is added to the + list of the accounts in the Failures property, and a notification might be + displayed to the user. + + The parameters currently recognized for the @notification argument are: + - DisplayName: string, description of the account (usually it's the + username) + --> + <method name="ReportFailure"> + <annotation name="com.trolltech.QtDBus.QtTypeName.In1" value="QVariantMap"/> + <arg name="account_id" type="u" direction="in"/> + <arg name="notification" type="a{sv}" direction="in"/> + </method> + + <!-- + RemoveFailures: + @account-ids: the libaccounts IDs of the accounts. + + Remove the given account IDs from the list of the failed accounts. + --> + <method name="RemoveFailures"> + <annotation name="com.trolltech.QtDBus.QtTypeName.In0" value="QSet<uint>"/> + <arg name="account_ids" type="au" direction="in"/> + </method> + + <!-- + ReauthenticateAccount: + @account-id: the libaccounts ID of the account. + @extra-parameters: dictionary of extra parameters (typically used to + specify a XWindowID). + @reauthenticated: %TRUE if the account could be reauthenticated and the + failure status has been cleared, %FALSE otherwise. + + Tries to replay the failed authentications on the account. If all of them + succeed, then the account failure is cleared. + --> + <method name="ReauthenticateAccount"> + <annotation name="com.trolltech.QtDBus.QtTypeName.In1" value="QVariantMap"/> + <arg name="account_id" type="u" direction="in"/> + <arg name="extra_parameters" type="a{sv}" direction="in"/> + <arg name="reauthenticated" type="b" direction="out"/> + </method> + + <!-- + ClearErrorStatus: + + Unsets the error indicator (if any) from the system user menu. + --> + <method name="ClearErrorStatus"/> + + <!-- + Failures: list of the libaccounts IDs of the failing accounts. + --> + <property name="Failures" type="au" access="read"> + <annotation name="com.trolltech.QtDBus.QtTypeName" value="QSet<uint>"/> + </property> + + <!-- + ErrorStatus: true if the indicator should display an error status. + --> + <property name="ErrorStatus" type="b" access="read"/> +</interface> +</node> diff --git a/src/backend-dbus/guest.c b/src/backend-dbus/guest.c new file mode 100644 index 0000000..3269097 --- /dev/null +++ b/src/backend-dbus/guest.c @@ -0,0 +1,394 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> + +#include "guest.h" + +struct _IndicatorSessionGuestDbusPriv +{ + GCancellable * cancellable; + + Login1Manager * login1_manager; + Login1Seat * login1_seat; + DisplayManagerSeat * dm_seat; + + gboolean guest_is_active; + gboolean guest_is_logged_in; + gboolean guest_is_allowed; +}; + +typedef IndicatorSessionGuestDbusPriv priv_t; + +G_DEFINE_TYPE (IndicatorSessionGuestDbus, + indicator_session_guest_dbus, + INDICATOR_TYPE_SESSION_GUEST) + +/*** +**** +***/ + +static void +set_guest_is_allowed_flag (IndicatorSessionGuestDbus * self, gboolean b) +{ + priv_t * p = self->priv; + + if (p->guest_is_allowed != b) + { + p->guest_is_allowed = b; + + indicator_session_guest_notify_allowed (INDICATOR_SESSION_GUEST (self)); + } +} + +static void +set_guest_is_logged_in_flag (IndicatorSessionGuestDbus * self, gboolean b) +{ + priv_t * p = self->priv; + + if (p->guest_is_logged_in != b) + { + p->guest_is_logged_in = b; + + indicator_session_guest_notify_logged_in (INDICATOR_SESSION_GUEST (self)); + } +} + +static void +set_guest_is_active_flag (IndicatorSessionGuestDbus * self, gboolean b) +{ + priv_t * p = self->priv; + + if (p->guest_is_active != b) + { + p->guest_is_active = b; + + indicator_session_guest_notify_active (INDICATOR_SESSION_GUEST(self)); + } +} + +/*** +**** +***/ + +/* walk the sessions to see if guest is logged in or active */ +static void +on_login1_manager_session_list_ready (GObject * o, + GAsyncResult * res, + gpointer gself) +{ + GVariant * sessions; + GError * err; + + sessions = NULL; + err = NULL; + login1_manager_call_list_sessions_finish (LOGIN1_MANAGER(o), + &sessions, + res, + &err); + if (err != NULL) + { + if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("%s: %s", G_STRFUNC, err->message); + + g_error_free (err); + } + else + { + const gchar * const current_seat_id = g_getenv ("XDG_SEAT"); + const gchar * const current_session_id = g_getenv ("XDG_SESSION_ID"); + gboolean is_logged_in = FALSE; + gboolean is_active = FALSE; + const gchar * session_id = NULL; + guint32 uid = 0; + const gchar * user_name = NULL; + const gchar * seat_id = NULL; + GVariantIter iter; + + g_variant_iter_init (&iter, sessions); + while (g_variant_iter_loop (&iter, "(&su&s&so)", &session_id, + &uid, + &user_name, + &seat_id, + NULL)) + { + gboolean is_current_session; + gboolean is_guest; + + is_current_session = !g_strcmp0 (current_seat_id, seat_id) + && !g_strcmp0 (current_session_id, session_id); + + is_guest = g_str_has_prefix (user_name, "guest-") + && (uid < 1000); + + if (is_guest) + { + is_logged_in = TRUE; + + is_active = is_current_session; + } + } + + set_guest_is_logged_in_flag (gself, is_logged_in); + set_guest_is_active_flag (gself, is_active); + + g_variant_unref (sessions); + } +} + +static void +update_session_list (IndicatorSessionGuestDbus * self) +{ + priv_t * p = self->priv; + + if (p->login1_manager != NULL) + { + login1_manager_call_list_sessions (p->login1_manager, + p->cancellable, + on_login1_manager_session_list_ready, + self); + } +} + +static void +set_login1_manager (IndicatorSessionGuestDbus * self, + Login1Manager * login1_manager) +{ + priv_t * p = self->priv; + + if (p->login1_manager != NULL) + { + g_signal_handlers_disconnect_by_data (p->login1_manager, self); + + g_clear_object (&p->login1_manager); + } + + if (login1_manager != NULL) + { + p->login1_manager = g_object_ref (login1_manager); + + g_signal_connect_swapped (login1_manager, "session-new", + G_CALLBACK(update_session_list), self); + g_signal_connect_swapped (login1_manager, "session-removed", + G_CALLBACK(update_session_list), self); + update_session_list (self); + } +} + +static void +set_login1_seat (IndicatorSessionGuestDbus * self, + Login1Seat * login1_seat) +{ + priv_t * p = self->priv; + + if (p->login1_seat != NULL) + { + g_signal_handlers_disconnect_by_data (p->login1_seat, self); + g_clear_object (&p->login1_seat); + } + + if (login1_seat != NULL) + { + p->login1_seat = g_object_ref (login1_seat); + + g_signal_connect_swapped (login1_seat, "notify::active-session", + G_CALLBACK(update_session_list), self); + update_session_list (self); + } +} + +/*** +**** +***/ + +static void +on_switch_to_guest_done (GObject * o, + GAsyncResult * res, + gpointer unused G_GNUC_UNUSED) +{ + GError * err; + + err = NULL; + display_manager_seat_call_switch_to_guest_finish (DISPLAY_MANAGER_SEAT(o), + res, + &err); + if (err != NULL) + { + if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("%s %s: %s", G_STRLOC, G_STRFUNC, err->message); + + g_error_free (err); + } +} + +static void +my_switch_to_guest (IndicatorSessionGuest * guest) +{ + IndicatorSessionGuestDbus * self = INDICATOR_SESSION_GUEST_DBUS(guest); + + g_return_if_fail (self != NULL); + g_return_if_fail (self->priv->dm_seat != NULL); + + display_manager_seat_call_switch_to_guest (self->priv->dm_seat, + "", + self->priv->cancellable, + on_switch_to_guest_done, + self); +} + +static void +on_notify_has_guest_account (DisplayManagerSeat * dm_seat, + GParamSpec * pspec G_GNUC_UNUSED, + IndicatorSessionGuestDbus * self) +{ + gboolean guest_exists = display_manager_seat_get_has_guest_account (dm_seat); + set_guest_is_allowed_flag (self, guest_exists); +} + +static void +set_display_manager_seat (IndicatorSessionGuestDbus * self, + DisplayManagerSeat * dm_seat) +{ + priv_t * p = self->priv; + + if (p->dm_seat != NULL) + { + g_signal_handlers_disconnect_by_data (p->dm_seat, self); + + g_clear_object (&p->dm_seat); + } + + if (dm_seat != NULL) + { + p->dm_seat = g_object_ref (dm_seat); + + g_signal_connect (dm_seat, "notify::has-guest-account", + G_CALLBACK(on_notify_has_guest_account), self); + on_notify_has_guest_account (dm_seat, NULL, self); + } +} + +/*** +**** IndiatorSessionGuest Virtual Functions +***/ + +static gboolean +my_is_allowed (IndicatorSessionGuest * self) +{ + g_return_val_if_fail (INDICATOR_IS_SESSION_GUEST_DBUS(self), FALSE); + + return INDICATOR_SESSION_GUEST_DBUS(self)->priv->guest_is_allowed; +} + +static gboolean +my_is_logged_in (IndicatorSessionGuest * self) +{ + g_return_val_if_fail (INDICATOR_IS_SESSION_GUEST_DBUS(self), FALSE); + + return INDICATOR_SESSION_GUEST_DBUS(self)->priv->guest_is_logged_in; +} + +static gboolean +my_is_active (IndicatorSessionGuest * self) +{ + g_return_val_if_fail (INDICATOR_IS_SESSION_GUEST_DBUS(self), FALSE); + + return INDICATOR_SESSION_GUEST_DBUS(self)->priv->guest_is_active; +} + +/*** +**** GObject Virtual Functions +***/ + +static void +my_dispose (GObject * o) +{ + IndicatorSessionGuestDbus * self = INDICATOR_SESSION_GUEST_DBUS (o); + + if (self->priv->cancellable != NULL) + { + g_cancellable_cancel (self->priv->cancellable); + g_clear_object (&self->priv->cancellable); + } + + set_login1_seat (self, NULL); + set_login1_manager (self, NULL); + set_display_manager_seat (self, NULL); + + G_OBJECT_CLASS (indicator_session_guest_dbus_parent_class)->dispose (o); +} + +/*** +**** Instantiation +***/ + +static void +indicator_session_guest_dbus_class_init (IndicatorSessionGuestDbusClass * klass) +{ + GObjectClass * object_class; + IndicatorSessionGuestClass * guest_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = my_dispose; + + guest_class = INDICATOR_SESSION_GUEST_CLASS (klass); + guest_class->is_allowed = my_is_allowed; + guest_class->is_logged_in = my_is_logged_in; + guest_class->is_active = my_is_active; + guest_class->switch_to_guest = my_switch_to_guest; + + g_type_class_add_private (klass, sizeof (IndicatorSessionGuestDbusPriv)); +} + +static void +indicator_session_guest_dbus_init (IndicatorSessionGuestDbus * self) +{ + priv_t * p; + + p = G_TYPE_INSTANCE_GET_PRIVATE (self, + INDICATOR_TYPE_SESSION_GUEST_DBUS, + IndicatorSessionGuestDbusPriv); + p->cancellable = g_cancellable_new (); + self->priv = p; +} + +/*** +**** Public +***/ + +IndicatorSessionGuest * +indicator_session_guest_dbus_new (void) +{ + gpointer o = g_object_new (INDICATOR_TYPE_SESSION_GUEST_DBUS, NULL); + + return INDICATOR_SESSION_GUEST (o); +} + +void +indicator_session_guest_dbus_set_proxies (IndicatorSessionGuestDbus * self, + Login1Manager * login1_manager, + Login1Seat * login1_seat, + DisplayManagerSeat * dm_seat) +{ + g_return_if_fail (INDICATOR_IS_SESSION_GUEST_DBUS(self)); + + set_login1_manager (self, login1_manager); + set_login1_seat (self, login1_seat); + set_display_manager_seat (self, dm_seat); +} diff --git a/src/backend-dbus/guest.h b/src/backend-dbus/guest.h new file mode 100644 index 0000000..73bb3ca --- /dev/null +++ b/src/backend-dbus/guest.h @@ -0,0 +1,70 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __GUEST_DBUS_H__ +#define __GUEST_DBUS_H__ + +#include <glib.h> +#include <glib-object.h> + +#include "../guest.h" /* parent class */ +#include "dbus-login1-manager.h" +#include "dbus-login1-seat.h" +#include "dbus-display-manager.h" + + +G_BEGIN_DECLS + +#define INDICATOR_TYPE_SESSION_GUEST_DBUS (indicator_session_guest_dbus_get_type()) +#define INDICATOR_SESSION_GUEST_DBUS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_SESSION_GUEST_DBUS, IndicatorSessionGuestDbus)) +#define INDICATOR_SESSION_GUEST_DBUS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_SESSION_GUEST_DBUS, IndicatorSessionGuestDbusClass)) +#define INDICATOR_IS_SESSION_GUEST_DBUS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_SESSION_GUEST_DBUS)) + +typedef struct _IndicatorSessionGuestDbus IndicatorSessionGuestDbus; +typedef struct _IndicatorSessionGuestDbusPriv IndicatorSessionGuestDbusPriv; +typedef struct _IndicatorSessionGuestDbusClass IndicatorSessionGuestDbusClass; + +/** + * An implementation of IndicatorSessionGuest that gets its user information + * from org.freedesktop.login1 and org.freedesktop.Accounts over DBus. + */ +struct _IndicatorSessionGuestDbus +{ + /*< private >*/ + IndicatorSessionGuest parent; + IndicatorSessionGuestDbusPriv * priv; +}; + +struct _IndicatorSessionGuestDbusClass +{ + IndicatorSessionGuestClass parent_class; +}; + +GType indicator_session_guest_dbus_get_type (void); + +IndicatorSessionGuest * indicator_session_guest_dbus_new (void); + +void indicator_session_guest_dbus_set_proxies (IndicatorSessionGuestDbus * self, + Login1Manager * login1_manager, + Login1Seat * login1_seat, + DisplayManagerSeat * display_manager_seat); + +G_END_DECLS + +#endif diff --git a/src/org.freedesktop.Accounts.User.xml b/src/backend-dbus/org.freedesktop.Accounts.User.xml index 53f54d4..53f54d4 100644 --- a/src/org.freedesktop.Accounts.User.xml +++ b/src/backend-dbus/org.freedesktop.Accounts.User.xml diff --git a/src/org.freedesktop.Accounts.xml b/src/backend-dbus/org.freedesktop.Accounts.xml index 9c19761..9c19761 100644 --- a/src/org.freedesktop.Accounts.xml +++ b/src/backend-dbus/org.freedesktop.Accounts.xml diff --git a/src/display-manager.xml b/src/backend-dbus/org.freedesktop.DisplayManager.Seat.xml index 07b5f29..07b5f29 100644 --- a/src/display-manager.xml +++ b/src/backend-dbus/org.freedesktop.DisplayManager.Seat.xml diff --git a/src/org.freedesktop.login1.Manager.xml b/src/backend-dbus/org.freedesktop.login1.Manager.xml index 91da5f2..91da5f2 100644 --- a/src/org.freedesktop.login1.Manager.xml +++ b/src/backend-dbus/org.freedesktop.login1.Manager.xml diff --git a/src/org.freedesktop.login1.Seat.xml b/src/backend-dbus/org.freedesktop.login1.Seat.xml index 92b62dd..b73f724 100644 --- a/src/org.freedesktop.login1.Seat.xml +++ b/src/backend-dbus/org.freedesktop.login1.Seat.xml @@ -4,10 +4,10 @@ <interface name="org.freedesktop.login1.Seat"> <method name="Terminate"/> <method name="ActivateSession"> - <arg name="id" type="s"/> + <arg name="id" direction="in" type="s"/> </method> <property name="Id" type="s" access="read"/> - <property name="ActiveSession" type="so" access="read"/> + <property name="ActiveSession" type="(so)" access="read"/> <property name="CanMultiSession" type="b" access="read"/> <property name="CanTTY" type="b" access="read"/> <property name="CanGraphical" type="b" access="read"/> diff --git a/src/org.freedesktop.login1.User.xml b/src/backend-dbus/org.freedesktop.login1.User.xml index 8253706..8253706 100644 --- a/src/org.freedesktop.login1.User.xml +++ b/src/backend-dbus/org.freedesktop.login1.User.xml diff --git a/src/backend-dbus/org.gnome.ScreenSaver.xml b/src/backend-dbus/org.gnome.ScreenSaver.xml new file mode 100644 index 0000000..c21fdc5 --- /dev/null +++ b/src/backend-dbus/org.gnome.ScreenSaver.xml @@ -0,0 +1,16 @@ +<!DOCTYPE node PUBLIC +"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" +"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd"> + + <interface name="org.gnome.ScreenSaver"> + + <method name="Lock"> + </method> + + <method name="SimulateUserActivity"> + </method> + + </interface> + +</node> diff --git a/src/backend-dbus/org.gnome.SessionManager.EndSessionDialog.xml b/src/backend-dbus/org.gnome.SessionManager.EndSessionDialog.xml new file mode 100644 index 0000000..5392de0 --- /dev/null +++ b/src/backend-dbus/org.gnome.SessionManager.EndSessionDialog.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd"> + <interface name="org.gnome.SessionManager.EndSessionDialog"> + <method name="Open"> + <arg type="u" name="type" direction="in"> + <doc:doc> + <doc:summary> + The type of dialog to show. + 0 for logout, 1 for shutdown, 2 for restart. + </doc:summary> + </doc:doc> + </arg> + <arg type="u" name="timestamp" direction="in"> + <doc:doc> + <doc:summary> + Timestamp of the user-initiated event which triggered + the call, or 0 if the call was not triggered by an event. + </doc:summary> + </doc:doc> + </arg> + <arg type="u" name="seconds_to_stay_open" direction="in"> + <doc:doc> + <doc:summary> + The number of seconds which the dialog should stay open + before automatic action is taken. + </doc:summary> + </doc:doc> + </arg> + <arg type="ao" name="inhibitor_object_paths" direction="in"> + <doc:doc> + <doc:summary> + The object paths of all inhibitors that are registered + for the action. + </doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:summary> + This function opens a dialog which asks the user for confirmation + of a logout, poweroff or reboot action. The dialog has a timeout + after which the action is automatically taken, and it should show + the inhibitors to the user. + </doc:summary> + </doc:doc> + </method> + <signal name="ConfirmedLogout" /> + <signal name="ConfirmedReboot" /> + <signal name="ConfirmedShutdown" /> + <signal name="Canceled" /> + <signal name="Closed" /> + </interface> +</node> diff --git a/src/backend-dbus/org.gnome.SessionManager.xml b/src/backend-dbus/org.gnome.SessionManager.xml new file mode 100644 index 0000000..eb69180 --- /dev/null +++ b/src/backend-dbus/org.gnome.SessionManager.xml @@ -0,0 +1,451 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd"> + <interface name="org.gnome.SessionManager"> + + <!-- Initialization phase interfaces --> + + <method name="Setenv"> + <arg name="variable" type="s" direction="in"> + <doc:doc> + <doc:summary>The variable name</doc:summary> + </doc:doc> + </arg> + <arg name="value" type="s" direction="in"> + <doc:doc> + <doc:summary>The value</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Adds the variable name to the application launch environment with the specified value. May only be used during the Session Manager initialization phase.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="GetLocale"> + <arg name="category" type="i" direction="in"> + <doc:doc> + <doc:summary>The locale category</doc:summary> + </doc:doc> + </arg> + <arg name="value" type="s" direction="out"> + <doc:doc> + <doc:summary>The value</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Reads the current state of the specific locale category.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="InitializationError"> + <arg name="message" type="s" direction="in"> + <doc:doc> + <doc:summary>The error message</doc:summary> + </doc:doc> + </arg> + <arg name="fatal" type="b" direction="in"> + <doc:doc> + <doc:summary>Whether the error should be treated as fatal</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>May be used by applications launched during the Session Manager initialization phase to indicate there was a problem.</doc:para> + </doc:description> + </doc:doc> + </method> + + <!-- Running phase interfaces --> + + <method name="RegisterClient"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg type="s" name="app_id" direction="in"> + <doc:doc> + <doc:summary>The application identifier</doc:summary> + </doc:doc> + </arg> + <arg type="s" name="client_startup_id" direction="in"> + <doc:doc> + <doc:summary>Client startup identifier</doc:summary> + </doc:doc> + </arg> + <arg type="o" name="client_id" direction="out"> + <doc:doc> + <doc:summary>The object path of the newly registered client</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Register the caller as a Session Management client.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="UnregisterClient"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg type="o" name="client_id" direction="in"> + <doc:doc> + <doc:summary>The object path of the client</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Unregister the specified client from Session Management.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="Inhibit"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg type="s" name="app_id" direction="in"> + <doc:doc> + <doc:summary>The application identifier</doc:summary> + </doc:doc> + </arg> + <arg type="u" name="toplevel_xid" direction="in"> + <doc:doc> + <doc:summary>The toplevel X window identifier</doc:summary> + </doc:doc> + </arg> + <arg type="s" name="reason" direction="in"> + <doc:doc> + <doc:summary>The reason for the inhibit</doc:summary> + </doc:doc> + </arg> + <arg type="u" name="flags" direction="in"> + <doc:doc> + <doc:summary>Flags that specify what should be inhibited</doc:summary> + </doc:doc> + </arg> + <arg type="u" name="inhibit_cookie" direction="out"> + <doc:doc> + <doc:summary>The cookie</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:summary> + Proactively indicates that the calling application is performing an action that should not be interrupted and sets a reason to be displayed to the user when an interruption is about to take placea. + </doc:summary> + <doc:description> + <doc:para>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 <doc:ref type="method" to="org.gnome.SessionManager.Uninhibit">Uninhibit()</doc:ref> + or disconnect from the session bus. + </doc:para> + <doc:para> + 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. + </doc:para> + <doc:para> + Reasons should be short and to the point. + </doc:para> + <doc:para> + The flags parameter must include at least one of the following: + <doc:list> + <doc:item> + <doc:term>1</doc:term> + <doc:definition>Inhibit logging out</doc:definition> + </doc:item> + <doc:item> + <doc:term>2</doc:term> + <doc:definition>Inhibit user switching</doc:definition> + </doc:item> + <doc:item> + <doc:term>4</doc:term> + <doc:definition>Inhibit suspending the session or computer</doc:definition> + </doc:item> + <doc:item> + <doc:term>8</doc:term> + <doc:definition>Inhibit the session being marked as idle</doc:definition> + </doc:item> + <doc:item> + <doc:term>16</doc:term> + <doc:definition>Inhibit auto-mounting removable media for the session</doc:definition> + </doc:item> + </doc:list> + Values for flags may be bitwise or'ed together. + </doc:para> + <doc:para> + The returned cookie is used to uniquely identify this request. It should be used + as an argument to <doc:ref type="method" to="org.gnome.SessionManager.Uninhibit">Uninhibit()</doc:ref> in + order to remove the request. + </doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="Uninhibit"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg type="u" name="inhibit_cookie" direction="in"> + <doc:doc> + <doc:summary>The cookie</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Cancel a previous call to <doc:ref type="method" to="org.gnome.SessionManager.Inhibit">Inhibit()</doc:ref> identified by the cookie.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="IsInhibited"> + <arg type="u" name="flags" direction="in"> + <doc:doc> + <doc:summary>Flags that spefify what should be inhibited</doc:summary> + </doc:doc> + </arg> + <arg type="b" name="is_inhibited" direction="out"> + <doc:doc> + <doc:summary>Returns TRUE if any of the operations in the bitfield flags are inhibited</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Determine if operation(s) specified by the flags + are currently inhibited. Flags are same as those accepted + by the + <doc:ref type="method" to="org.gnome.SessionManager.Inhibit">Inhibit()</doc:ref> + method.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="GetClients"> + <arg name="clients" direction="out" type="ao"> + <doc:doc> + <doc:summary>an array of client IDs</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>This gets a list of all the <doc:ref type="interface" to="org.gnome.SessionManager.Client">Clients</doc:ref> + that are currently known to the session manager.</doc:para> + <doc:para>Each Client ID is an D-Bus object path for the object that implements the + <doc:ref type="interface" to="org.gnome.SessionManager.Client">Client</doc:ref> interface.</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="org.gnome.SessionManager.Client">org.gnome.SessionManager.Client</doc:ref></doc:seealso> + </doc:doc> + </method> + + <method name="GetInhibitors"> + <arg name="inhibitors" direction="out" type="ao"> + <doc:doc> + <doc:summary>an array of inhibitor IDs</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>This gets a list of all the <doc:ref type="interface" to="org.gnome.SessionManager.Inhibitor">Inhibitors</doc:ref> + that are currently known to the session manager.</doc:para> + <doc:para>Each Inhibitor ID is an D-Bus object path for the object that implements the + <doc:ref type="interface" to="org.gnome.SessionManager.Inhibitor">Inhibitor</doc:ref> interface.</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="org.gnome.SessionManager.Inhibitor">org.gnome.SessionManager.Inhibitor</doc:ref></doc:seealso> + </doc:doc> + </method> + + + <method name="IsAutostartConditionHandled"> + <arg name="condition" direction="in" type="s"> + <doc:doc> + <doc:summary>The autostart condition string</doc:summary> + </doc:doc> + </arg> + <arg name="handled" direction="out" type="b"> + <doc:doc> + <doc:summary>True if condition is handled, false otherwise</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Allows the caller to determine whether the session manager is + handling changes to the specified autostart condition.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="Shutdown"> + <doc:doc> + <doc:description> + <doc:para>Request a shutdown dialog.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="Reboot"> + <doc:doc> + <doc:description> + <doc:para>Request a reboot dialog.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="CanShutdown"> + <arg name="is_available" direction="out" type="b"> + <doc:doc> + <doc:summary>True if shutdown is available to the user, false otherwise</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Allows the caller to determine whether or not it's okay to show + a shutdown option in the UI</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="Logout"> + <arg name="mode" type="u" direction="in"> + <doc:doc> + <doc:summary>The type of logout that is being requested</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Request a logout dialog</doc:para> + <doc:para> + Allowed values for the mode parameter are: + <doc:list> + <doc:item> + <doc:term>0</doc:term> + <doc:definition>Normal.</doc:definition> + </doc:item> + <doc:item> + <doc:term>1</doc:term> + <doc:definition>No confirmation inferface should be shown.</doc:definition> + </doc:item> + <doc:item> + <doc:term>2</doc:term> + <doc:definition>Forcefully logout. No confirmation will be shown and any inhibitors will be ignored.</doc:definition> + </doc:item> + </doc:list> + Values for flags may be bitwise or'ed together. + </doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="IsSessionRunning"> + <arg name="running" direction="out" type="b"> + <doc:doc> + <doc:summary>True if the session has entered the Running phase, false otherwise</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Allows the caller to determine whether the session manager + has entered the Running phase, in case the client missed the + SessionRunning signal.</doc:para> + </doc:description> + </doc:doc> + </method> + + <!-- Signals --> + + <signal name="ClientAdded"> + <arg name="id" type="o"> + <doc:doc> + <doc:summary>The object path for the added client</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Emitted when a client has been added to the session manager. + </doc:para> + </doc:description> + </doc:doc> + </signal> + <signal name="ClientRemoved"> + <arg name="id" type="o"> + <doc:doc> + <doc:summary>The object path for the removed client</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Emitted when a client has been removed from the session manager. + </doc:para> + </doc:description> + </doc:doc> + </signal> + + <signal name="InhibitorAdded"> + <arg name="id" type="o"> + <doc:doc> + <doc:summary>The object path for the added inhibitor</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Emitted when an inhibitor has been added to the session manager. + </doc:para> + </doc:description> + </doc:doc> + </signal> + <signal name="InhibitorRemoved"> + <arg name="id" type="o"> + <doc:doc> + <doc:summary>The object path for the removed inhibitor</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Emitted when an inhibitor has been removed from the session manager. + </doc:para> + </doc:description> + </doc:doc> + </signal> + + <signal name="SessionRunning"> + <doc:doc> + <doc:description> + <doc:para>Indicates the session has entered the Running phase.</doc:para> + </doc:description> + </doc:doc> + </signal> + + <signal name="SessionOver"> + <doc:doc> + <doc:description> + <doc:para>Indicates the session is about to end.</doc:para> + </doc:description> + </doc:doc> + </signal> + + <!-- Properties --> + + <property name="SessionName" type="s" access="read"> + <doc:doc> + <doc:description> + <doc:para>The name of the session that has been loaded.</doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="SessionIsActive" type="b" access="read"> + <doc:doc> + <doc:description> + <doc:para>If true, the session is currently in the + foreground and available for user input.</doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="InhibitedActions" type="u" access="read"> + <doc:doc> + <doc:description> + <doc:para>A bitmask of flags to indicate which actions + are inhibited. See the Inhibit() function's description + for a list of possible values.</doc:para> + </doc:description> + </doc:doc> + </property> + + </interface> +</node> diff --git a/src/backend-dbus/users.c b/src/backend-dbus/users.c new file mode 100644 index 0000000..f770695 --- /dev/null +++ b/src/backend-dbus/users.c @@ -0,0 +1,706 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "dbus-user.h" + +#include "users.h" + +struct _IndicatorSessionUsersDbusPriv +{ + Login1Manager * login1_manager; + Login1Seat * login1_seat; + DisplayManagerSeat * dm_seat; + Accounts * accounts; + + /* hash table of int uids to UserRecord* */ + GHashTable * uid_to_account; + + /* a hashset of int uids of users who are logged in */ + GHashTable * logins; + + /* the user-id of the owner of the active session */ + guint active_uid; + + /* true if this is a live session */ + gboolean is_live; + + GCancellable * cancellable; + + guint update_list_tag; +}; + +typedef IndicatorSessionUsersDbusPriv priv_t; + +G_DEFINE_TYPE (IndicatorSessionUsersDbus, + indicator_session_users_dbus, + INDICATOR_TYPE_SESSION_USERS) + +/*** +**** +***/ + +struct UserRecord +{ + AccountsUser * user; + + gulong signal_id; +}; + +struct UserRecord * +user_record_new (AccountsUser * user, gulong signal_id) +{ + struct UserRecord * rec; + rec = g_new (struct UserRecord, 1); + rec->user = g_object_ref (user); + rec->signal_id = signal_id; + return rec; +} + +static void +user_record_free (struct UserRecord * rec) +{ + g_signal_handler_disconnect (rec->user, rec->signal_id); + g_object_unref (G_OBJECT (rec->user)); + g_free (rec); +} + +/*** +**** +***/ + +/* get our private org.freedesktop.Accounts.User proxy for the given uid */ +static AccountsUser * +get_user_for_uid (IndicatorSessionUsersDbus * self, guint uid) +{ + struct UserRecord * rec; + + if ((rec = g_hash_table_lookup (self->priv->uid_to_account, + GUINT_TO_POINTER(uid)))) + return rec->user; + + return NULL; +} + +static gboolean +is_tracked_uid (IndicatorSessionUsersDbus * self, guint uid) +{ + return get_user_for_uid (self, uid) != NULL; +} + +static void +emit_user_added (IndicatorSessionUsersDbus * self, guint32 uid) +{ + if (is_tracked_uid (self, uid)) + indicator_session_users_added (INDICATOR_SESSION_USERS(self), uid); +} + +static void +emit_user_changed (IndicatorSessionUsersDbus * self, guint32 uid) +{ + if (is_tracked_uid (self, uid)) + indicator_session_users_changed (INDICATOR_SESSION_USERS(self), uid); +} + +static void +emit_user_removed (IndicatorSessionUsersDbus * self, guint32 uid) +{ + indicator_session_users_removed (INDICATOR_SESSION_USERS(self), uid); +} + +/*** +**** +***/ + +static void +set_is_live_session_flag (IndicatorSessionUsersDbus * self, gboolean b) +{ + priv_t * p = self->priv; + + if (p->is_live != b) + { + p->is_live = b; + + indicator_session_users_notify_is_live_session (INDICATOR_SESSION_USERS (self)); + } +} + +static void +set_active_uid (IndicatorSessionUsersDbus * self, guint uid) +{ + priv_t * p = self->priv; + + if (p->active_uid != uid) + { + const guint old_uid = p->active_uid; + + p->active_uid = uid; + + emit_user_changed (self, old_uid); + emit_user_changed (self, uid); + } +} + +static void +set_logins (IndicatorSessionUsersDbus * self, GHashTable * logins) +{ + GHashTable * old_logins = self->priv->logins; + gpointer uid; + GHashTableIter iter; + + self->priv->logins = g_hash_table_ref (logins); + + /* fire 'user changed' event for users who logged out */ + g_hash_table_iter_init (&iter, old_logins); + while ((g_hash_table_iter_next (&iter, &uid, NULL))) + if (!g_hash_table_contains (logins, uid)) + emit_user_changed (self, GPOINTER_TO_UINT(uid)); + + /* fire 'user changed' event for users who logged in */ + g_hash_table_iter_init (&iter, logins); + while ((g_hash_table_iter_next (&iter, &uid, NULL))) + if (!g_hash_table_contains (old_logins, uid)) + emit_user_changed (self, GPOINTER_TO_UINT(uid)); + + g_hash_table_destroy (old_logins); +} + +/*** +**** User Account Tracking +***/ + +static void create_user_proxy_for_path (IndicatorSessionUsersDbus *, const char *); + +/* called when a user proxy gets the 'Changed' signal */ +static void +on_user_changed (AccountsUser * user, gpointer gself) +{ + /* Accounts.User doesn't update properties in the standard way, + * so create a new proxy to pull in the new properties. + * The older proxy is freed when it's replaced in our accounts hash */ + const char * path = g_dbus_proxy_get_object_path (G_DBUS_PROXY(user)); + create_user_proxy_for_path (gself, path); +} + +static void +track_user (IndicatorSessionUsersDbus * self, + AccountsUser * user) +{ + const guint32 uid = accounts_user_get_uid (user); + priv_t * p = self->priv; + gulong id; + const gboolean already_had_user = is_tracked_uid (self, uid); + + id = g_signal_connect (user, "changed", G_CALLBACK(on_user_changed), self); + g_hash_table_insert (p->uid_to_account, + GUINT_TO_POINTER (uid), + user_record_new (user, id)); + + if (already_had_user) + emit_user_changed (self, uid); + else + emit_user_added (self, uid); +} + +static void +untrack_user (IndicatorSessionUsersDbus * self, + const gchar * path) +{ + guint uid; + gpointer key; + gpointer val; + GHashTableIter iter; + priv_t * p = self->priv; + + /* find the uid matching this object path */ + uid = 0; + g_hash_table_iter_init (&iter, p->uid_to_account); + while (!uid && g_hash_table_iter_next (&iter, &key, &val)) + { + struct UserRecord * rec = val; + GDBusProxy * proxy = G_DBUS_PROXY (rec->user); + if (!g_strcmp0 (path, g_dbus_proxy_get_object_path (proxy))) + uid = GPOINTER_TO_UINT (key); + } + + if (uid) + { + g_hash_table_remove (p->uid_to_account, GUINT_TO_POINTER(uid)); + + emit_user_removed (self, uid); + } +} + +/* We got a new org.freedesktop.Accounts.User proxy. + If it's one we want to remember, pass it to track_user() */ +static void +on_user_proxy_ready (GObject * o G_GNUC_UNUSED, + GAsyncResult * res, + gpointer self) +{ + GError * err; + AccountsUser * user; + + err = NULL; + user = accounts_user_proxy_new_for_bus_finish (res, &err); + if (err != NULL) + { + if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("%s: %s", G_STRFUNC, err->message); + + g_error_free (err); + } + else + { + if (!accounts_user_get_system_account (user)) + track_user (self, user); + + g_object_unref (user); + } +} + +static void +create_user_proxy_for_path (IndicatorSessionUsersDbus * self, + const char * path) +{ + accounts_user_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES, + "org.freedesktop.Accounts", + path, + self->priv->cancellable, + on_user_proxy_ready, self); +} + +/* create proxy objects for everything in Account's user-list */ +static void +on_user_list_ready (GObject * o, GAsyncResult * res, gpointer gself) +{ + GError * err; + gchar ** paths; + + err = NULL; + paths = NULL; + accounts_call_list_cached_users_finish (ACCOUNTS(o), &paths, res, &err); + if (err != NULL) + { + if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("%s %s: %s", G_STRLOC, G_STRFUNC, err->message); + + g_error_free (err); + } + else + { + int i; + + for (i=0; paths && paths[i]; ++i) + create_user_proxy_for_path (gself, paths[i]); + + g_strfreev (paths); + } +} + +static void +set_account_manager (IndicatorSessionUsersDbus * self, Accounts * a) +{ + priv_t * p = self->priv; + + if (p->accounts != NULL) + { + g_signal_handlers_disconnect_by_data (p->accounts, self); + g_clear_object (&p->accounts); + } + + if (a != NULL) + { + p->accounts = g_object_ref (a); + + accounts_call_list_cached_users (a, + self->priv->cancellable, + on_user_list_ready, + self); + + g_signal_connect_swapped (a, "user-added", + G_CALLBACK(create_user_proxy_for_path), self); + + g_signal_connect_swapped (a, "user-deleted", + G_CALLBACK(untrack_user), self); + } +} + +/*** +**** +***/ + +/* Based on the login1 manager's list of current sessions, + update our 'logins', 'is_live', and 'active_uid' fields */ +static void +on_login1_manager_session_list_ready (GObject * o, + GAsyncResult * res, + gpointer gself) +{ + GVariant * sessions; + GError * err; + + sessions = NULL; + err = NULL; + login1_manager_call_list_sessions_finish (LOGIN1_MANAGER(o), + &sessions, + res, + &err); + + if (err != NULL) + { + if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("%s: %s", G_STRFUNC, err->message); + + g_error_free (err); + } + else + { + const gchar * const current_seat_id = g_getenv ("XDG_SEAT"); + const gchar * const current_session_id = g_getenv ("XDG_SESSION_ID"); + IndicatorSessionUsersDbus * self = INDICATOR_SESSION_USERS_DBUS (gself); + const gchar * session_id = NULL; + guint32 uid = 0; + const gchar * user_name = NULL; + const gchar * seat_id = NULL; + const gchar * path = NULL; + gboolean is_live_session = FALSE; + GHashTable * logins = g_hash_table_new (g_direct_hash, g_direct_equal); + GVariantIter iter; + + g_variant_iter_init (&iter, sessions); + while (g_variant_iter_loop (&iter, "(&su&s&s&o)", &session_id, + &uid, + &user_name, + &seat_id, + &path)) + { + /* only track sessions on our seat */ + if (g_strcmp0 (seat_id, current_seat_id)) + continue; + + if (!g_strcmp0 (session_id, current_session_id)) + { + set_active_uid (self, uid); + + if ((uid==999) && !g_strcmp0 (user_name, "ubuntu")) + is_live_session = TRUE; + } + + g_hash_table_add (logins, GINT_TO_POINTER(uid)); + } + + set_is_live_session_flag (self, is_live_session); + set_logins (self, logins); + + g_hash_table_unref (logins); + g_variant_unref (sessions); + } +} + +static void +update_session_list (IndicatorSessionUsersDbus * self) +{ + priv_t * p = self->priv; + + if (p->login1_manager != NULL) + { + login1_manager_call_list_sessions (p->login1_manager, + p->cancellable, + on_login1_manager_session_list_ready, + self); + } +} + +static gboolean +on_update_session_list_timer (gpointer gself) +{ + IndicatorSessionUsersDbus * self = INDICATOR_SESSION_USERS_DBUS (gself); + + update_session_list (self); + + self->priv->update_list_tag = 0; + return G_SOURCE_REMOVE; +} + +/* A dead session can still show up in list-sessions for a few seconds. + So just to be safe, queue up a rebuild for a few seconds from now */ +static void +update_session_list_twice (IndicatorSessionUsersDbus * self) +{ + priv_t * p = self->priv; + + update_session_list (self); + + if (p->update_list_tag == 0) + p->update_list_tag = g_timeout_add_seconds (5, + on_update_session_list_timer, + self); +} + +static void +set_login1_manager (IndicatorSessionUsersDbus * self, + Login1Manager * login1_manager) +{ + priv_t * p = self->priv; + + if (p->login1_manager != NULL) + { + g_signal_handlers_disconnect_by_data (p->login1_manager, self); + + g_clear_object (&p->login1_manager); + } + + if (login1_manager != NULL) + { + p->login1_manager = g_object_ref (login1_manager); + + g_signal_connect_swapped (login1_manager, "session-new", + G_CALLBACK(update_session_list), self); + g_signal_connect_swapped (login1_manager, "session-removed", + G_CALLBACK(update_session_list_twice), self); + g_signal_connect_swapped (login1_manager, "user-new", + G_CALLBACK(update_session_list), self); + g_signal_connect_swapped (login1_manager, "user-removed", + G_CALLBACK(update_session_list_twice), self); + update_session_list (self); + } +} + +static void +set_login1_seat (IndicatorSessionUsersDbus * self, + Login1Seat * login1_seat) +{ + priv_t * p = self->priv; + + if (p->login1_seat != NULL) + { + g_signal_handlers_disconnect_by_data (p->login1_seat, self); + + g_clear_object (&p->login1_seat); + } + + if (login1_seat != NULL) + { + p->login1_seat = g_object_ref (login1_seat); + + g_signal_connect_swapped (login1_seat, "notify::active-session", + G_CALLBACK(update_session_list), self); + update_session_list (self); + } +} + +static void +set_display_manager_seat (IndicatorSessionUsersDbus * self, + DisplayManagerSeat * dm_seat) +{ + priv_t * p = self->priv; + + g_clear_object (&p->dm_seat); + + if (dm_seat != NULL) + p->dm_seat = g_object_ref (dm_seat); +} + +/*** +**** IndicatorSessionUsers virtual functions +***/ + +/* switch to (or create) a session for the specified user */ +static void +my_activate_user (IndicatorSessionUsers * users, guint uid) +{ + IndicatorSessionUsersDbus * self = INDICATOR_SESSION_USERS_DBUS(users); + priv_t * p = self->priv; + AccountsUser * au; + const char * username; + + au = get_user_for_uid (self, uid); + username = au ? accounts_user_get_user_name (au) : NULL; + + if (!username) + { + g_warning ("%s %s can't find user '%u'", G_STRLOC, G_STRFUNC, uid); + } + else + { + g_return_if_fail (p->dm_seat != NULL); + + display_manager_seat_call_switch_to_user (p->dm_seat, + username, + "", + p->cancellable, + NULL, + NULL); + } +} + +/* returns true if this is a live session */ +static gboolean +my_is_live_session (IndicatorSessionUsers * users) +{ + g_return_val_if_fail (INDICATOR_IS_SESSION_USERS_DBUS(users), FALSE); + + return INDICATOR_SESSION_USERS_DBUS(users)->priv->is_live; +} + +/* get a list of our user ids */ +static GList * +my_get_uids (IndicatorSessionUsers * users) +{ + IndicatorSessionUsersDbus * self = INDICATOR_SESSION_USERS_DBUS (users); + return g_hash_table_get_keys (self->priv->uid_to_account); +} + +/* build a new struct populated with info on the specified user */ +static IndicatorSessionUser * +my_get_user (IndicatorSessionUsers * users, guint uid) +{ + IndicatorSessionUsersDbus * self = INDICATOR_SESSION_USERS_DBUS (users); + priv_t * p = self->priv; + IndicatorSessionUser * ret; + AccountsUser * au; + + ret = NULL; + au = get_user_for_uid (self, uid); + if (au && !accounts_user_get_system_account(au)) + { + g_assert (uid == accounts_user_get_uid (au)); + + ret = g_new0 (IndicatorSessionUser, 1); + ret->uid = uid; + ret->user_name = g_strdup (accounts_user_get_user_name (au)); + ret->real_name = g_strdup (accounts_user_get_real_name (au)); + ret->icon_file = g_strdup (accounts_user_get_icon_file (au)); + ret->login_frequency = accounts_user_get_login_frequency (au); + ret->is_logged_in = g_hash_table_contains (p->logins, GINT_TO_POINTER(uid)); + ret->is_current_user = uid == p->active_uid; + } + + return ret; +} + +/*** +**** GObject virtual functions +***/ + +static void +my_dispose (GObject * o) +{ + IndicatorSessionUsersDbus * self = INDICATOR_SESSION_USERS_DBUS (o); + priv_t * p = self->priv; + + if (p->update_list_tag != 0) + { + g_source_remove (p->update_list_tag); + p->update_list_tag = 0; + } + + if (p->cancellable) + { + g_cancellable_cancel (p->cancellable); + g_clear_object (&p->cancellable); + } + + set_account_manager (self, NULL); + set_display_manager_seat (self, NULL); + set_login1_seat (self, NULL); + set_login1_manager (self, NULL); + + g_hash_table_remove_all (p->uid_to_account); + + G_OBJECT_CLASS (indicator_session_users_dbus_parent_class)->dispose (o); +} + +static void +my_finalize (GObject * o) +{ + IndicatorSessionUsersDbus * self = INDICATOR_SESSION_USERS_DBUS (o); + priv_t * p = self->priv; + + g_hash_table_destroy (p->logins); + g_hash_table_destroy (p->uid_to_account); + + G_OBJECT_CLASS (indicator_session_users_dbus_parent_class)->finalize (o); +} + +static void +indicator_session_users_dbus_class_init (IndicatorSessionUsersDbusClass * klass) +{ + GObjectClass * object_class; + IndicatorSessionUsersClass * users_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = my_dispose; + object_class->finalize = my_finalize; + + users_class = INDICATOR_SESSION_USERS_CLASS (klass); + users_class->is_live_session = my_is_live_session; + users_class->get_uids = my_get_uids; + users_class->get_user = my_get_user; + users_class->activate_user = my_activate_user; + + g_type_class_add_private (klass, sizeof (IndicatorSessionUsersDbusPriv)); +} + +static void +indicator_session_users_dbus_init (IndicatorSessionUsersDbus * self) +{ + priv_t * p; + + p = G_TYPE_INSTANCE_GET_PRIVATE (self, + INDICATOR_TYPE_SESSION_USERS_DBUS, + IndicatorSessionUsersDbusPriv); + self->priv = p; + p->cancellable = g_cancellable_new (); + + p->uid_to_account = g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + (GDestroyNotify)user_record_free); + + p->logins = g_hash_table_new (g_direct_hash, g_direct_equal); +} + +/*** +**** Public +***/ + +IndicatorSessionUsers * +indicator_session_users_dbus_new (void) +{ + gpointer o = g_object_new (INDICATOR_TYPE_SESSION_USERS_DBUS, NULL); + + return INDICATOR_SESSION_USERS (o); +} + +void +indicator_session_users_dbus_set_proxies (IndicatorSessionUsersDbus * self, + Login1Manager * login1_manager, + Login1Seat * login1_seat, + DisplayManagerSeat * dm_seat, + Accounts * accounts) +{ + g_return_if_fail (INDICATOR_IS_SESSION_USERS_DBUS (self)); + + set_login1_manager (self, login1_manager); + set_login1_seat (self, login1_seat); + set_display_manager_seat (self, dm_seat); + set_account_manager (self, accounts); +} diff --git a/src/backend-dbus/users.h b/src/backend-dbus/users.h new file mode 100644 index 0000000..d6c17df --- /dev/null +++ b/src/backend-dbus/users.h @@ -0,0 +1,73 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __USERS_DBUS_H__ +#define __USERS_DBUS_H__ + +#include <glib.h> +#include <glib-object.h> + +#include "../users.h" /* parent class */ +#include "dbus-accounts.h" +#include "dbus-login1-manager.h" +#include "dbus-login1-seat.h" +#include "dbus-display-manager.h" + +G_BEGIN_DECLS + +#define INDICATOR_TYPE_SESSION_USERS_DBUS (indicator_session_users_dbus_get_type()) +#define INDICATOR_SESSION_USERS_DBUS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_SESSION_USERS_DBUS, IndicatorSessionUsersDbus)) +#define INDICATOR_SESSION_USERS_DBUS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_SESSION_USERS_DBUS, IndicatorSessionUsersDbusClass)) +#define INDICATOR_IS_SESSION_USERS_DBUS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_SESSION_USERS_DBUS)) + +typedef struct _IndicatorSessionUsersDbus IndicatorSessionUsersDbus; +typedef struct _IndicatorSessionUsersDbusPriv IndicatorSessionUsersDbusPriv; +typedef struct _IndicatorSessionUsersDbusClass IndicatorSessionUsersDbusClass; + +/** + * An implementation of IndicatorSessionUsers that gets its user information + * from org.freedesktop.login1 and org.freedesktop.Accounts over DBus. + */ +struct _IndicatorSessionUsersDbus +{ + /*< private >*/ + IndicatorSessionUsers parent; + IndicatorSessionUsersDbusPriv * priv; +}; + +struct _IndicatorSessionUsersDbusClass +{ + IndicatorSessionUsersClass parent_class; +}; + +GType indicator_session_users_dbus_get_type (void); + +IndicatorSessionUsers * indicator_session_users_dbus_new (void); + +void indicator_session_users_dbus_set_proxies (IndicatorSessionUsersDbus *, + Login1Manager *, + Login1Seat *, + DisplayManagerSeat *, + Accounts *); + + + +G_END_DECLS + +#endif diff --git a/src/backend-dbus/utils.c b/src/backend-dbus/utils.c new file mode 100644 index 0000000..bc862e8 --- /dev/null +++ b/src/backend-dbus/utils.c @@ -0,0 +1,182 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "utils.h" + +/*** +**** indicator_session_util_get_session_proxies() +***/ + +struct session_proxy_data +{ + Login1Manager * login1_manager; + Login1Seat * login1_seat; + DisplayManagerSeat * dm_seat; + Accounts * account_manager; + + GCancellable * cancellable; + int pending; + + indicator_session_util_session_proxies_func callback; + gpointer user_data; +}; + + +static void +on_proxy_ready_impl (struct session_proxy_data * data, + gsize member_offset, + GError * err, + gpointer proxy) +{ + if (err != NULL) + { + if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("%s %s: %s", G_STRLOC, G_STRFUNC, err->message); + + g_error_free (err); + } + else + { + *((gpointer*)G_STRUCT_MEMBER_P(data, member_offset)) = proxy; + } + + if (!--data->pending) + { + data->callback (data->login1_manager, + data->login1_seat, + data->dm_seat, + data->account_manager, + data->cancellable, + data->user_data); + + g_clear_object (&data->login1_manager); + g_clear_object (&data->login1_seat); + g_clear_object (&data->dm_seat); + g_clear_object (&data->account_manager); + g_clear_object (&data->cancellable); + g_free (data); + } +} + +static void +on_display_manager_seat_proxy_ready (GObject * o G_GNUC_UNUSED, + GAsyncResult * res, + gpointer gdata) +{ + gsize offset = G_STRUCT_OFFSET (struct session_proxy_data, dm_seat); + GError * err = NULL; + gpointer proxy = display_manager_seat_proxy_new_for_bus_finish (res, &err); + on_proxy_ready_impl (gdata, offset, err, proxy); +} + +static void +on_login1_seat_ready (GObject * o G_GNUC_UNUSED, + GAsyncResult * res, + gpointer gdata) +{ + gsize offset = G_STRUCT_OFFSET (struct session_proxy_data, login1_seat); + GError * err = NULL; + gpointer proxy = login1_seat_proxy_new_for_bus_finish (res, &err); + on_proxy_ready_impl (gdata, offset, err, proxy); +} + +static void +on_login1_manager_ready (GObject * o G_GNUC_UNUSED, + GAsyncResult * res, + gpointer gdata) +{ + gsize offset = G_STRUCT_OFFSET (struct session_proxy_data, login1_manager); + GError * err = NULL; + gpointer proxy = login1_manager_proxy_new_for_bus_finish (res, &err); + on_proxy_ready_impl (gdata, offset, err, proxy); +} + +static void +on_accounts_proxy_ready (GObject * o G_GNUC_UNUSED, + GAsyncResult * res, + gpointer gdata) +{ + gsize offset = G_STRUCT_OFFSET (struct session_proxy_data, account_manager); + GError * err = NULL; + gpointer proxy = accounts_proxy_new_for_bus_finish (res, &err); + on_proxy_ready_impl (gdata, offset, err, proxy); +} + +/* helper utility to get the dbus proxies used by the backend-dbus classes */ +void +indicator_session_util_get_session_proxies ( + indicator_session_util_session_proxies_func func, + GCancellable * cancellable, + gpointer user_data) +{ + struct session_proxy_data * data; + const char * str; + + data = g_new0 (struct session_proxy_data, 1); + data->callback = func; + data->user_data = user_data; + data->cancellable = g_object_ref (cancellable); + + /* login1 */ + data->pending++; + login1_manager_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES, + "org.freedesktop.login1", + "/org/freedesktop/login1", + data->cancellable, + on_login1_manager_ready, data); + + /* login1 seat */ + if ((str = g_getenv ("XDG_SEAT"))) + { + char * path; + data->pending++; + path = g_strconcat ("/org/freedesktop/login1/seat/", str, NULL); + login1_seat_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES, + "org.freedesktop.login1", + path, + data->cancellable, + on_login1_seat_ready, + data); + g_free (path); + } + + /* Accounts */ + data->pending++; + accounts_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES, + "org.freedesktop.Accounts", + "/org/freedesktop/Accounts", + data->cancellable, + on_accounts_proxy_ready, data); + + /* DisplayManager seat */ + if ((str = g_getenv ("XDG_SEAT_PATH"))) + { + data->pending++; + display_manager_seat_proxy_new_for_bus ( + G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES, + "org.freedesktop.DisplayManager", + str, + data->cancellable, + on_display_manager_seat_proxy_ready, data); + } +} diff --git a/src/backend-dbus/utils.h b/src/backend-dbus/utils.h new file mode 100644 index 0000000..802dd5e --- /dev/null +++ b/src/backend-dbus/utils.h @@ -0,0 +1,49 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __DBUS_UTILS_H__ +#define __DBUS_UTILS_H__ + +#include <glib.h> +#include <glib-object.h> + +#include "dbus-accounts.h" +#include "dbus-display-manager.h" +#include "dbus-login1-manager.h" +#include "dbus-login1-seat.h" + +typedef void (*indicator_session_util_session_proxies_func)( + Login1Manager * login1_manager, + Login1Seat * login1_seat, + DisplayManagerSeat * display_manager_seat, + Accounts * account_manager, + GCancellable * cancellable, + gpointer user_data); + +/** + * Both users-dbus and guest-dbus need some of these proxies. + * Getting them all involves a lot of steps, so instead of repeating + * ourselves, the common dbus steps are extracted to this func. + */ +void indicator_session_util_get_session_proxies ( + indicator_session_util_session_proxies_func func, + GCancellable * cancellable, + gpointer user_data); + +#endif diff --git a/src/backend.h b/src/backend.h new file mode 100644 index 0000000..2df215d --- /dev/null +++ b/src/backend.h @@ -0,0 +1,47 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __INDICATOR_SESSION_BACKEND_H__ +#define __INDICATOR_SESSION_BACKEND_H__ + +#include <gio/gio.h> /* GCancellable */ + +#include "actions.h" +#include "guest.h" +#include "users.h" + +G_BEGIN_DECLS + +/** + * Gets instances of the backend abstract base classes. + * These are how IndicatorSystemService knows what's happening on the system. + * + * This function isn't defined in libindicatorsessionservice. + * Instead, one of two implementations is statically linked at build time: + * one for production in libbackenddbus (in src/backend-dbus/) or + * one for testing in libbackendmock (in tests/). + */ +void backend_get (GCancellable * cancellable, + IndicatorSessionActions ** setme_actions, + IndicatorSessionUsers ** setme_users, + IndicatorSessionGuest ** setme_guest); + +G_END_DECLS + +#endif diff --git a/src/dialog.c b/src/dialog.c deleted file mode 100644 index f97475e..0000000 --- a/src/dialog.c +++ /dev/null @@ -1,248 +0,0 @@ -/* -A dialog to ask the user about the various logout options that -are available. - -Copyright 2010 Canonical Ltd. - -Authors: - Ted Gould <ted@canonical.com> - -This program is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License version 3, as published -by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranties of -MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <glib/gi18n.h> - -#include "dbus-login1-manager.h" -#include "dialog.h" - -/* Strings */ - -static const gchar * title_strings[LOGOUT_DIALOG_TYPE_CNT] = { - /* LOGOUT_DIALOG_LOGOUT, */ NC_("title", "Log Out"), - /* LOGOUT_DIALOG_RESTART, */ NC_("title", "Restart"), - /* LOGOUT_DIALOG_SHUTDOWN, */ NC_("title", "Shut Down") -}; - -static const gchar * body_strings[LOGOUT_DIALOG_TYPE_CNT] = { - /* LOGOUT_DIALOG_LOGOUT, */ N_("Are you sure you want to close all programs and log out of the computer?"), - /* LOGOUT_DIALOG_RESTART, */ N_("Are you sure you want to close all programs and restart the computer?"), - /* LOGOUT_DIALOG_SHUTDOWN, */ N_("Are you sure you want to close all programs and shut down the computer?") -}; - -static const gchar * button_strings[LOGOUT_DIALOG_TYPE_CNT] = { - /* LOGOUT_DIALOG_LOGOUT, */ NC_("button", "Log Out"), - /* LOGOUT_DIALOG_RESTART, */ NC_("button", "Restart"), - /* LOGOUT_DIALOG_SHUTDOWN, */ NC_("button", "Shut Down") -}; - -/* TRANSLATORS: These strings have an ellipsis so that the user knows - they are also going to get a password dialog to do the action. */ -static const gchar * button_auth_strings[LOGOUT_DIALOG_TYPE_CNT] = { - /* LOGOUT_DIALOG_LOGOUT, */ NC_("button auth", "Log Out"), - /* LOGOUT_DIALOG_RESTART, */ NC_("button auth", "Restart…"), - /* LOGOUT_DIALOG_SHUTDOWN, */ NC_("button auth", "Shut Down…") -}; - -/* TRANSLATORS: This button appears on the logout dialog when - there are updates that require restart. It will do a restart - in place of a log out. */ -static const gchar * restart_updates = N_("Restart Instead"); -static const gchar * restart_auth = N_("Restart Instead…"); -static const gchar * body_logout_update = N_("Some software updates won’t apply until the computer next restarts."); - -static const gchar * icon_strings[LOGOUT_DIALOG_TYPE_CNT] = { - /* LOGOUT_DIALOG_LOGOUT, */ "system-log-out", - /* LOGOUT_DIALOG_RESTART, */ "system-restart", - /* LOGOUT_DIALOG_SHUTDOWN, */ "system-shutdown" -}; - - - -typedef struct _LogoutDialogPrivate LogoutDialogPrivate; -struct _LogoutDialogPrivate { - guint type; -}; - -#define LOGOUT_DIALOG_GET_PRIVATE(o) \ -(G_TYPE_INSTANCE_GET_PRIVATE ((o), LOGOUT_DIALOG_TYPE, LogoutDialogPrivate)) - -static void logout_dialog_class_init (LogoutDialogClass *klass); -static void logout_dialog_init (LogoutDialog *self); -static void logout_dialog_dispose (GObject *object); -static void logout_dialog_finalize (GObject *object); - -G_DEFINE_TYPE (LogoutDialog, logout_dialog, GTK_TYPE_MESSAGE_DIALOG); - -static void -logout_dialog_class_init (LogoutDialogClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - g_type_class_add_private (klass, sizeof (LogoutDialogPrivate)); - - object_class->dispose = logout_dialog_dispose; - object_class->finalize = logout_dialog_finalize; - - return; -} - -static void -logout_dialog_init (LogoutDialog *self) -{ - - return; -} - -static void -logout_dialog_dispose (GObject *object) -{ - - - G_OBJECT_CLASS (logout_dialog_parent_class)->dispose (object); - return; -} - -static void -logout_dialog_finalize (GObject *object) -{ - - - G_OBJECT_CLASS (logout_dialog_parent_class)->finalize (object); - return; -} - -/* Checks for updates that would signal that a restart is - required for them to apply */ -static gboolean -check_restart_required (void) -{ - return g_file_test("/var/run/reboot-required", G_FILE_TEST_EXISTS); -} - -/* Checks with logind to see if we can do what we want */ -static gboolean -logind_check_allowed (LogoutDialogType type) -{ - gchar * allowed = NULL; - - Login1Manager * manager_proxy = login1_manager_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_NONE, - "org.freedesktop.login1", - "/org/freedesktop/login1", - NULL, - NULL); - if (manager_proxy != NULL) - { - switch (type) { - case LOGOUT_DIALOG_TYPE_RESTART: - login1_manager_call_can_reboot_sync (manager_proxy, &allowed, NULL, NULL); - break; - case LOGOUT_DIALOG_TYPE_SHUTDOWN: - login1_manager_call_can_power_off_sync (manager_proxy, &allowed, NULL, NULL); - break; - default: - break; - } - - g_object_unref(manager_proxy); - } - - return g_strcmp0 (allowed, "yes") == 0; -} - -LogoutDialog * -logout_dialog_new (LogoutDialogType type) -{ - GtkWidget * image = gtk_image_new_from_icon_name(icon_strings[type], GTK_ICON_SIZE_DIALOG); - gtk_widget_show(image); - - LogoutDialog * dialog = LOGOUT_DIALOG(g_object_new(LOGOUT_DIALOG_TYPE, - /* Window */ - "icon-name", icon_strings[type], - "modal", TRUE, - "resizable", FALSE, - "title", g_dpgettext2 (NULL, "title", title_strings[type]), - "window-position", GTK_WIN_POS_CENTER_ALWAYS, - /* Message Dialog */ - "buttons", GTK_BUTTONS_NONE, - "image", image, - "message-type", GTK_MESSAGE_OTHER, - "text", _(body_strings[type]), - NULL)); - - gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE); - - gboolean allowed = FALSE; - if (type == LOGOUT_DIALOG_TYPE_LOG_OUT) { - allowed = logind_check_allowed(LOGOUT_DIALOG_TYPE_RESTART); - } else { - allowed = logind_check_allowed(type); - } - - gboolean restart_required = FALSE; - if (type == LOGOUT_DIALOG_TYPE_LOG_OUT) { - restart_required = check_restart_required(); - } - - const gchar * button_text; - if (allowed) { - button_text = g_dpgettext2 (NULL, "button", button_strings[type]); - } else { - button_text = g_dpgettext2 (NULL, "button auth", button_auth_strings[type]); - } - - if (restart_required) { - const gchar * restart_req; - if (allowed) { - restart_req = restart_updates; - } else { - restart_req = restart_auth; - } - - g_object_set(dialog, "secondary-text", _(body_logout_update), NULL); - - gtk_dialog_add_buttons(GTK_DIALOG(dialog), - _(restart_req), GTK_RESPONSE_HELP, - _("Cancel"), GTK_RESPONSE_CANCEL, - button_text, GTK_RESPONSE_OK, - NULL); - } else { - gtk_dialog_add_buttons(GTK_DIALOG(dialog), - _("Cancel"), GTK_RESPONSE_CANCEL, - button_text, GTK_RESPONSE_OK, - NULL); - } - - gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK); - - /* The following is a workaround to fix an issue in GtkMessageDialog - in which the user can tab through the text in addition to - the buttons. */ - GtkWidget *message_area = gtk_message_dialog_get_message_area(GTK_MESSAGE_DIALOG(dialog)); - GList *children = gtk_container_get_children(GTK_CONTAINER(message_area)); - GList *l; - - for (l = children; l != NULL; l = g_list_next (l)) - { - GtkWidget *child = l->data; - gtk_widget_set_can_focus(child, FALSE); - } - - g_list_free (children); - - return dialog; -} diff --git a/src/dialog.h b/src/dialog.h deleted file mode 100644 index 18a55a7..0000000 --- a/src/dialog.h +++ /dev/null @@ -1,64 +0,0 @@ -/* -A dialog to ask the user about the various logout options that -are available. - -Copyright 2010 Canonical Ltd. - -Authors: - Ted Gould <ted@canonical.com> - -This program is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License version 3, as published -by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranties of -MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef __LOGOUT_DIALOG_H__ -#define __LOGOUT_DIALOG_H__ - -#include <glib.h> -#include <glib-object.h> - -#include <gtk/gtk.h> - -G_BEGIN_DECLS - -#define LOGOUT_DIALOG_TYPE (logout_dialog_get_type ()) -#define LOGOUT_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), LOGOUT_DIALOG_TYPE, LogoutDialog)) -#define LOGOUT_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), LOGOUT_DIALOG_TYPE, LogoutDialogClass)) -#define IS_LOGOUT_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LOGOUT_DIALOG_TYPE)) -#define IS_LOGOUT_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), LOGOUT_DIALOG_TYPE)) -#define LOGOUT_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), LOGOUT_DIALOG_TYPE, LogoutDialogClass)) - -typedef enum _LogoutDialogType LogoutDialogType; -enum _LogoutDialogType { - LOGOUT_DIALOG_TYPE_LOG_OUT, - LOGOUT_DIALOG_TYPE_RESTART, - LOGOUT_DIALOG_TYPE_SHUTDOWN, - LOGOUT_DIALOG_TYPE_CNT -}; - -typedef struct _LogoutDialog LogoutDialog; -typedef struct _LogoutDialogClass LogoutDialogClass; - -struct _LogoutDialogClass { - GtkMessageDialogClass parent_class; -}; - -struct _LogoutDialog { - GtkMessageDialog parent; -}; - -GType logout_dialog_get_type (void); -LogoutDialog * logout_dialog_new (LogoutDialogType type); - -G_END_DECLS - -#endif diff --git a/src/gtk-logout-helper.c b/src/gtk-logout-helper.c deleted file mode 100644 index 12f2198..0000000 --- a/src/gtk-logout-helper.c +++ /dev/null @@ -1,264 +0,0 @@ -/* -A small wrapper utility to load indicators and put them as menu items -into the gnome-panel using it's applet interface. - -Copyright 2009 Canonical Ltd. - -Authors: - Ted Gould <ted@canonical.com> - Christoph Korn <c_korn@gmx.de> - -This program is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License version 3, as published -by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranties of -MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "config.h" - -#include <locale.h> -#include <glib.h> -#include <glib/gi18n.h> /* textdomain(), bindtextdomain() */ -#include <gtk/gtk.h> -#include "dialog.h" -#include "shared-names.h" - -static GVariant * -call_logind (const gchar *method, GVariant *parameters, GError **error) -{ - GDBusConnection * bus = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, error); - if (!bus) - { - g_variant_unref (parameters); - return NULL; - } - - GVariant *result = g_dbus_connection_call_sync(bus, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - method, - parameters, - NULL, - G_DBUS_CALL_FLAGS_NONE, - -1, - NULL, - error); - g_object_unref (bus); - - return result; -} - -static void -logind_fallback (LogoutDialogType action) -{ - GError * error = NULL; - GVariant *result = NULL; - - g_debug("Falling back to using logind for action"); - - switch (action) { - case LOGOUT_DIALOG_TYPE_LOG_OUT: - g_warning("Unable to fallback to logind for logout as it's a session issue. We need some sort of session handler."); - break; - case LOGOUT_DIALOG_TYPE_SHUTDOWN: - g_debug("Telling logind to 'PowerOff'"); - result = call_logind ("PowerOff", g_variant_new ("(b)", FALSE), &error); - break; - case LOGOUT_DIALOG_TYPE_RESTART: - g_debug("Telling logind to 'Reboot'"); - result = call_logind ("Reboot", g_variant_new ("(b)", FALSE), &error); - break; - default: - g_warning("Unknown action"); - break; - } - - if (!result) { - if (error != NULL) { - g_warning ("logind action failed: %s", error->message); - } else { - g_warning ("logind action failed: unknown error"); - } - } - else - g_variant_unref (result); - g_clear_error (&error); - - return; -} - -static GVariant * -call_gnome_session (const gchar *method, GVariant *parameters, GError **error) -{ - GDBusConnection * bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, error); - if (!bus) - { - g_variant_unref (parameters); - return NULL; - } - - GVariant *result = g_dbus_connection_call_sync(bus, - "org.gnome.SessionManager", - "/org/gnome/SessionManager", - "org.gnome.SessionManager", - method, - parameters, - NULL, - G_DBUS_CALL_FLAGS_NONE, - G_MAXINT, - NULL, - error); - g_object_unref (bus); - - return result; -} - -static void -session_action (LogoutDialogType action) -{ - GError * error = NULL; - GVariant *result = NULL; - - if (action == LOGOUT_DIALOG_TYPE_LOG_OUT) { - g_debug("Asking Session manager to 'Logout'"); - result = call_gnome_session ("Logout", g_variant_new ("(u)", 1), &error); - } else if (action == LOGOUT_DIALOG_TYPE_SHUTDOWN) { - g_debug("Asking Session manager to 'RequestShutdown'"); - result = call_gnome_session ("RequestShutdown", g_variant_new ("()"), &error); - } else if (action == LOGOUT_DIALOG_TYPE_RESTART) { - g_debug("Asking Session manager to 'RequestReboot'"); - result = call_gnome_session ("RequestReboot", g_variant_new ("()"), &error); - } else { - g_warning ("Unknown session action"); - } - - if (!result) { - if (error != NULL) { - g_warning ("SessionManager action failed: %s", error->message); - } else { - g_warning ("SessionManager action failed: unknown error"); - } - - logind_fallback(action); - } - else - g_variant_unref (result); - g_clear_error (&error); - - return; -} - -static LogoutDialogType type = LOGOUT_DIALOG_TYPE_LOG_OUT; - -static gboolean -option_logout (const gchar * arg, const gchar * value, gpointer data, GError * error) -{ - type = LOGOUT_DIALOG_TYPE_LOG_OUT; - g_debug("Dialog type: logout"); - return TRUE; -} - -static gboolean -option_shutdown (const gchar * arg, const gchar * value, gpointer data, GError * error) -{ - type = LOGOUT_DIALOG_TYPE_SHUTDOWN; - g_debug("Dialog type: shutdown"); - return TRUE; -} - -static gboolean -option_restart (const gchar * arg, const gchar * value, gpointer data, GError * error) -{ - type = LOGOUT_DIALOG_TYPE_RESTART; - g_debug("Dialog type: restart"); - return TRUE; -} - -static GOptionEntry options[] = { - {"logout", 'l', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, option_logout, "Log out of the current session", NULL}, - {"shutdown", 's', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, option_shutdown, "Switch off the entire system", NULL}, - {"restart", 'r', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, option_restart, "Restart the system", NULL}, - - {NULL} -}; - -static gboolean -suppress_confirmations (void) -{ - GSettings * s = g_settings_new (SESSION_SCHEMA); - const gboolean suppress = g_settings_get_boolean (s, SUPPRESS_KEY); - g_clear_object (&s); - return suppress; -} - - - -int -main (int argc, char * argv[]) -{ - gtk_init(&argc, &argv); - - /* Setting up i18n and gettext. Apparently, we need - all of these. */ - setlocale (LC_ALL, ""); - bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); - textdomain (GETTEXT_PACKAGE); - - GError * error = NULL; - GOptionContext * context = g_option_context_new(" - logout of the current session"); - g_option_context_add_main_entries(context, options, "gtk-logout-helper"); - g_option_context_add_group(context, gtk_get_option_group(TRUE)); - g_option_context_set_help_enabled(context, TRUE); - - if (!g_option_context_parse(context, &argc, &argv, &error)) { - g_debug("Option parsing failed: %s", error->message); - g_error_free(error); - return 1; - } - - /* Init some theme/icon stuff */ - gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), - INDICATOR_ICONS_DIR); - - GtkWidget * dialog = NULL; - if (!suppress_confirmations()) { - g_debug("Showing dialog to ask for user confirmation"); - dialog = GTK_WIDGET(logout_dialog_new(type)); - } - - if (dialog != NULL) { - GtkResponseType response = gtk_dialog_run(GTK_DIALOG(dialog)); - gtk_widget_hide(dialog); - - if (response == GTK_RESPONSE_OK) { - g_debug("Dialog return response: 'okay'"); - } else if (response == GTK_RESPONSE_HELP) { - g_debug("Dialog return response: 'help'"); - } else { - g_debug("Dialog return response: %d", response); - } - - if (response == GTK_RESPONSE_HELP) { - type = LOGOUT_DIALOG_TYPE_RESTART; - response = GTK_RESPONSE_OK; - } - - if (response != GTK_RESPONSE_OK) { - g_debug("Final response was not okay, quiting"); - return 0; - } - } - - session_action(type); - g_debug("Finished action, quiting"); - - return 0; -} diff --git a/src/guest.c b/src/guest.c new file mode 100644 index 0000000..bcbd384 --- /dev/null +++ b/src/guest.c @@ -0,0 +1,190 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "guest.h" + +G_DEFINE_TYPE (IndicatorSessionGuest, + indicator_session_guest, + G_TYPE_OBJECT) + +enum +{ + PROP_0, + PROP_ALLOWED, + PROP_LOGGED_IN, + PROP_ACTIVE, + PROP_LAST +}; + +static GParamSpec *properties[PROP_LAST]; + +static void +my_get_property (GObject * o, + guint property_id, + GValue * value, + GParamSpec * pspec) +{ + IndicatorSessionGuest * self = INDICATOR_SESSION_GUEST (o); + + switch (property_id) + { + case PROP_ALLOWED: + g_value_set_boolean (value, indicator_session_guest_is_allowed (self)); + break; + + case PROP_LOGGED_IN: + g_value_set_boolean (value, indicator_session_guest_is_logged_in (self)); + break; + + case PROP_ACTIVE: + g_value_set_boolean (value, indicator_session_guest_is_active (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec); + } +} + +static void +my_dispose (GObject *object) +{ + G_OBJECT_CLASS (indicator_session_guest_parent_class)->dispose (object); +} + +static void +/* cppcheck-suppress unusedFunction */ +indicator_session_guest_class_init (IndicatorSessionGuestClass * klass) +{ + GObjectClass * object_class; + const GParamFlags flags = G_PARAM_READABLE | G_PARAM_STATIC_STRINGS; + + object_class = G_OBJECT_CLASS (klass); + object_class->get_property = my_get_property; + object_class->dispose = my_dispose; + + klass->is_allowed = NULL; + klass->is_logged_in = NULL; + klass->is_active = NULL; + klass->switch_to_guest = NULL; + + properties[PROP_0] = NULL; + + properties[PROP_ALLOWED] = + g_param_spec_boolean (INDICATOR_SESSION_GUEST_PROPERTY_ALLOWED, + "Is Allowed", + "Whether or not a Guest user is allowed", + FALSE, flags); + + properties[PROP_LOGGED_IN] = + g_param_spec_boolean (INDICATOR_SESSION_GUEST_PROPERTY_LOGGED_IN, + "Is Logged In", + "Whether or not the Guest account is logged in", + FALSE, flags); + + properties[PROP_ACTIVE] = + g_param_spec_boolean (INDICATOR_SESSION_GUEST_PROPERTY_ACTIVE, + "Is Active", + "If the Guest account has the current session", + FALSE, flags); + + g_object_class_install_properties (object_class, PROP_LAST, properties); +} + +static void +/* cppcheck-suppress unusedFunction */ +indicator_session_guest_init (IndicatorSessionGuest *self G_GNUC_UNUSED) +{ +} + +/*** +**** +***/ + +gboolean +indicator_session_guest_is_active (IndicatorSessionGuest * self) +{ + g_return_val_if_fail (INDICATOR_IS_SESSION_GUEST (self), FALSE); + + return INDICATOR_SESSION_GUEST_GET_CLASS (self)->is_active (self); +} + +gboolean +indicator_session_guest_is_allowed (IndicatorSessionGuest * self) +{ + g_return_val_if_fail (INDICATOR_IS_SESSION_GUEST (self), FALSE); + + return INDICATOR_SESSION_GUEST_GET_CLASS (self)->is_allowed (self); +} + +gboolean +indicator_session_guest_is_logged_in (IndicatorSessionGuest * self) +{ + g_return_val_if_fail (INDICATOR_IS_SESSION_GUEST (self), FALSE); + + return INDICATOR_SESSION_GUEST_GET_CLASS (self)->is_logged_in (self); +} + +/*** +**** +***/ +static void +notify_func (IndicatorSessionGuest * self, int prop) +{ + g_return_if_fail (INDICATOR_IS_SESSION_GUEST (self)); + + g_debug ("%s %s emitting '%s' prop notify", G_STRLOC, G_STRFUNC, properties[prop]->name); + + g_object_notify_by_pspec (G_OBJECT(self), properties[prop]); +} + +void +indicator_session_guest_notify_active (IndicatorSessionGuest * self) +{ + notify_func (self, PROP_ACTIVE); +} + +void +indicator_session_guest_notify_allowed (IndicatorSessionGuest * self) +{ + notify_func (self, PROP_ALLOWED); +} + +void +indicator_session_guest_notify_logged_in (IndicatorSessionGuest * self) +{ + notify_func (self, PROP_LOGGED_IN); +} + +/*** +**** +***/ + +void +indicator_session_guest_switch_to_guest (IndicatorSessionGuest * self) +{ + gboolean allowed; + + g_return_if_fail (INDICATOR_IS_SESSION_GUEST (self)); + + g_object_get (self, INDICATOR_SESSION_GUEST_PROPERTY_ALLOWED, &allowed, NULL); + g_return_if_fail (allowed); + + INDICATOR_SESSION_GUEST_GET_CLASS (self)->switch_to_guest (self); +} + diff --git a/src/guest.h b/src/guest.h new file mode 100644 index 0000000..30947d5 --- /dev/null +++ b/src/guest.h @@ -0,0 +1,82 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __GUEST_H__ +#define __GUEST_H__ + +#include <glib.h> +#include <glib-object.h> + +G_BEGIN_DECLS + +#define INDICATOR_TYPE_SESSION_GUEST (indicator_session_guest_get_type()) +#define INDICATOR_SESSION_GUEST(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_SESSION_GUEST, IndicatorSessionGuest)) +#define INDICATOR_SESSION_GUEST_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_SESSION_GUEST, IndicatorSessionGuestClass)) +#define INDICATOR_SESSION_GUEST_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), INDICATOR_TYPE_SESSION_GUEST, IndicatorSessionGuestClass)) +#define INDICATOR_IS_SESSION_GUEST(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_SESSION_GUEST)) + +typedef struct _IndicatorSessionGuest IndicatorSessionGuest; +typedef struct _IndicatorSessionGuestClass IndicatorSessionGuestClass; + +GType indicator_session_guest_get_type (void); + +/** + * A base class for getting state information about the system's guest user. + * Use backend.h's get_backend() to get an instance. + */ +struct _IndicatorSessionGuest +{ + /*< private >*/ + GObject parent; +}; + +/* properties */ +#define INDICATOR_SESSION_GUEST_PROPERTY_ALLOWED "guest-is-allowed" +#define INDICATOR_SESSION_GUEST_PROPERTY_LOGGED_IN "guest-is-logged-in" +#define INDICATOR_SESSION_GUEST_PROPERTY_ACTIVE "guest-is-active-session" + +struct _IndicatorSessionGuestClass +{ + GObjectClass parent_class; + + /* virtual functions */ + gboolean (* is_allowed) (IndicatorSessionGuest * self); + gboolean (* is_logged_in) (IndicatorSessionGuest * self); + gboolean (* is_active) (IndicatorSessionGuest * self); + void (* switch_to_guest) (IndicatorSessionGuest * self); +}; + +gboolean indicator_session_guest_is_allowed (IndicatorSessionGuest * self); +gboolean indicator_session_guest_is_logged_in (IndicatorSessionGuest * self); +gboolean indicator_session_guest_is_active (IndicatorSessionGuest * self); + +void indicator_session_guest_switch_to_guest (IndicatorSessionGuest * self); + +/** + * Emit 'notify' signals for the corresponding properties. + * These functions should only be called by IndicatorSessionGuest implementations. + */ +void indicator_session_guest_notify_allowed (IndicatorSessionGuest * self); +void indicator_session_guest_notify_logged_in (IndicatorSessionGuest * self); +void indicator_session_guest_notify_active (IndicatorSessionGuest * self); + + +G_END_DECLS + +#endif diff --git a/src/indicator-session.c b/src/indicator-session.c deleted file mode 100644 index ada8c6e..0000000 --- a/src/indicator-session.c +++ /dev/null @@ -1,493 +0,0 @@ -/* -A small wrapper utility to load indicators and put them as menu items -into the gnome-panel using its applet interface. - -Copyright 2009 Canonical Ltd. - -Authors: - Ted Gould <ted@canonical.com> - Conor Curran <conor.curran@canonical.com> - -This program is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License version 3, as published -by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranties of -MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <glib.h> -#include <glib-object.h> -#include <glib/gi18n-lib.h> -#include <gtk/gtk.h> -#include <gio/gio.h> - -#include <libdbusmenu-gtk/menu.h> - -#include <libindicator/indicator.h> -#include <libindicator/indicator-object.h> -#include <libindicator/indicator-service-manager.h> - -#include "shared-names.h" -#include "user-widget.h" - -#define INDICATOR_SESSION_TYPE (indicator_session_get_type ()) -#define INDICATOR_SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), INDICATOR_SESSION_TYPE, IndicatorSession)) -#define INDICATOR_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), INDICATOR_SESSION_TYPE, IndicatorSessionClass)) -#define IS_INDICATOR_SESSION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), INDICATOR_SESSION_TYPE)) -#define IS_INDICATOR_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), INDICATOR_SESSION_TYPE)) -#define INDICATOR_SESSION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), INDICATOR_SESSION_TYPE, IndicatorSessionClass)) - -typedef struct _IndicatorSession IndicatorSession; -typedef struct _IndicatorSessionClass IndicatorSessionClass; - -struct _IndicatorSessionClass -{ - IndicatorObjectClass parent_class; -}; - -struct _IndicatorSession -{ - IndicatorObject parent; - IndicatorServiceManager * service; - IndicatorObjectEntry entry; - GCancellable * service_proxy_cancel; - GDBusProxy * service_proxy; - GSettings * settings; - DbusmenuClient * menu_client; - GtkIconTheme * icon_theme; -}; - -GType indicator_session_get_type (void); - -/* Indicator stuff */ -INDICATOR_SET_VERSION -INDICATOR_SET_TYPE(INDICATOR_SESSION_TYPE) - -/* Prototypes */ -static gboolean new_user_item (DbusmenuMenuitem * newitem, - DbusmenuMenuitem * parent, - DbusmenuClient * client, - gpointer user_data); -static void on_menu_layout_updated (DbusmenuClient * client, IndicatorSession * session); -static void indicator_session_update_icon_callback (GtkWidget * widget, gpointer callback_data); -static void indicator_session_update_icon_and_a11y (IndicatorSession * self); -static void indicator_session_update_users_label (IndicatorSession* self, - const gchar* name); -static void service_connection_cb (IndicatorServiceManager * sm, gboolean connected, gpointer user_data); -static void receive_signal (GDBusProxy * proxy, gchar * sender_name, gchar * signal_name, GVariant * parameters, gpointer user_data); -static void service_proxy_cb (GObject * object, GAsyncResult * res, gpointer user_data); -static void user_real_name_get_cb (GObject * obj, GAsyncResult * res, gpointer user_data); - -static void indicator_session_class_init (IndicatorSessionClass *klass); -static void indicator_session_init (IndicatorSession *self); -static void indicator_session_dispose (GObject *object); -static void indicator_session_finalize (GObject *object); -static GList* indicator_session_get_entries (IndicatorObject* obj); -static gint indicator_session_get_position (IndicatorObject * io); - -G_DEFINE_TYPE (IndicatorSession, indicator_session, INDICATOR_OBJECT_TYPE); - -static void -indicator_session_class_init (IndicatorSessionClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->dispose = indicator_session_dispose; - object_class->finalize = indicator_session_finalize; - - IndicatorObjectClass * io_class = INDICATOR_OBJECT_CLASS(klass); - io_class->get_entries = indicator_session_get_entries; - io_class->get_position = indicator_session_get_position; - return; -} - -static void -indicator_session_init (IndicatorSession *self) -{ - self->settings = g_settings_new ("com.canonical.indicator.session"); - - /* Now let's fire these guys up. */ - self->service = indicator_service_manager_new_version(INDICATOR_SESSION_DBUS_NAME, - INDICATOR_SESSION_DBUS_VERSION); - g_signal_connect (G_OBJECT(self->service), - INDICATOR_SERVICE_MANAGER_SIGNAL_CONNECTION_CHANGE, - G_CALLBACK(service_connection_cb), self); - - self->entry.name_hint = PACKAGE; - self->entry.label = GTK_LABEL (gtk_label_new ("User Name")); - self->entry.image = GTK_IMAGE (gtk_image_new()); - self->entry.menu = GTK_MENU (dbusmenu_gtkmenu_new(INDICATOR_SESSION_DBUS_NAME, - INDICATOR_SESSION_DBUS_OBJECT)); - /* We need to check if the current icon theme has the hard coded icons. - * If not, we'll fall back to a standard icon */ - self->icon_theme = gtk_icon_theme_get_default(); - g_signal_connect(G_OBJECT(self->icon_theme), - "changed", - G_CALLBACK(indicator_session_update_icon_callback), self); - - indicator_session_update_icon_and_a11y (self); - g_settings_bind (self->settings, "show-real-name-on-panel", - self->entry.label, "visible", - G_SETTINGS_BIND_GET); - - /* show-real-name-on-panel affects the a11y string */ - g_signal_connect_swapped (self->settings, - "notify::show-real-name-on-panel", - G_CALLBACK(indicator_session_update_icon_and_a11y), - self); - - gtk_widget_show (GTK_WIDGET(self->entry.menu)); - gtk_widget_show (GTK_WIDGET(self->entry.image)); - g_object_ref_sink (self->entry.menu); - g_object_ref_sink (self->entry.image); - - // set up the handlers - self->menu_client = DBUSMENU_CLIENT(dbusmenu_gtkmenu_get_client(DBUSMENU_GTKMENU(self->entry.menu))); - g_signal_connect (self->menu_client, "layout-updated", - G_CALLBACK(on_menu_layout_updated), self); - - dbusmenu_client_add_type_handler (self->menu_client, - USER_ITEM_TYPE, - new_user_item); - dbusmenu_gtkclient_set_accel_group (DBUSMENU_GTKCLIENT(self->menu_client), - gtk_accel_group_new()); -} - -static void -indicator_session_dispose (GObject *object) -{ - IndicatorSession * self = INDICATOR_SESSION(object); - - g_clear_object (&self->settings); - g_clear_object (&self->service); - g_clear_object (&self->service_proxy); - - if (self->service_proxy_cancel != NULL) - { - g_cancellable_cancel(self->service_proxy_cancel); - g_clear_object (&self->service_proxy_cancel); - } - - g_clear_object (&self->entry.menu); - - G_OBJECT_CLASS (indicator_session_parent_class)->dispose (object); -} - -static void -indicator_session_finalize (GObject *object) -{ - - G_OBJECT_CLASS (indicator_session_parent_class)->finalize (object); - return; -} - -static GList* -indicator_session_get_entries (IndicatorObject* obj) -{ - g_return_val_if_fail(IS_INDICATOR_SESSION(obj), NULL); - - IndicatorSession* self = INDICATOR_SESSION (obj); - return g_list_append (NULL, &self->entry); -} - -static gint -indicator_session_get_position (IndicatorObject * io) -{ - return 10; -} - -/* callback for the service manager state of being */ -static void -service_connection_cb (IndicatorServiceManager * sm, gboolean connected, gpointer user_data) -{ - IndicatorSession * self = INDICATOR_SESSION (user_data); - - if (connected) { - if (self->service_proxy != NULL){ - // Its a reconnect ! - // Fetch synchronisation data and return (proxy is still legit) - g_dbus_proxy_call (self->service_proxy, - "GetUserRealName", - NULL, - G_DBUS_CALL_FLAGS_NONE, - -1, - NULL, - user_real_name_get_cb, - user_data); - return; - } - - self->service_proxy_cancel = g_cancellable_new(); - g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION, - G_DBUS_PROXY_FLAGS_NONE, - NULL, - INDICATOR_SESSION_DBUS_NAME, - INDICATOR_SESSION_SERVICE_DBUS_OBJECT, - INDICATOR_SESSION_SERVICE_DBUS_IFACE, - self->service_proxy_cancel, - service_proxy_cb, - self); - } - return; -} - - -static void -service_proxy_cb (GObject * object, GAsyncResult * res, gpointer user_data) -{ - GError * error = NULL; - - IndicatorSession * self = INDICATOR_SESSION(user_data); - g_return_if_fail(self != NULL); - - GDBusProxy * proxy = g_dbus_proxy_new_for_bus_finish(res, &error); - - g_clear_object (&self->service_proxy_cancel); - - if (error != NULL) { - g_warning("Could not grab DBus proxy for %s: %s", INDICATOR_SESSION_DBUS_NAME, error->message); - g_error_free(error); - return; - } - - /* Okay, we're good to grab the proxy at this point, we're - sure that it's ours. */ - self->service_proxy = proxy; - - g_signal_connect(proxy, "g-signal", G_CALLBACK(receive_signal), self); - - // Fetch the user's real name for the user entry label - g_dbus_proxy_call (self->service_proxy, - "GetUserRealName", - NULL, - G_DBUS_CALL_FLAGS_NONE, - -1, - NULL, - user_real_name_get_cb, - user_data); - return; -} - - -static gboolean -new_user_item (DbusmenuMenuitem * newitem, - DbusmenuMenuitem * parent, - DbusmenuClient * client, - gpointer user_data) -{ - g_return_val_if_fail (DBUSMENU_IS_MENUITEM(newitem), FALSE); - g_return_val_if_fail (DBUSMENU_IS_GTKCLIENT(client), FALSE); - - GtkWidget * user_item = user_widget_new (newitem); - - GtkMenuItem *user_widget = GTK_MENU_ITEM(user_item); - - dbusmenu_gtkclient_newitem_base (DBUSMENU_GTKCLIENT(client), - newitem, - user_widget, - parent); - - g_debug ("%s (\"%s\")", __func__, - dbusmenu_menuitem_property_get (newitem, - USER_ITEM_PROP_NAME)); - return TRUE; -} - -static void -user_real_name_get_cb (GObject * obj, GAsyncResult * res, gpointer user_data) -{ - IndicatorSession * self = INDICATOR_SESSION(user_data); - - GError * error = NULL; - GVariant * result = g_dbus_proxy_call_finish(self->service_proxy, res, &error); - - if (error != NULL) - { - g_warning ("Unable to complete real name dbus query: %s", error->message); - g_clear_error (&error); - } - else - { - const gchar * username = NULL; - g_variant_get (result, "(&s)", &username); - indicator_session_update_users_label (self, username); - g_variant_unref (result); - } -} - -/* Receives all signals from the service, routed to the appropriate functions */ -static void -receive_signal (GDBusProxy * proxy, - gchar * sender_name, - gchar * signal_name, - GVariant * parameters, - gpointer user_data) -{ - IndicatorSession * self = INDICATOR_SESSION(user_data); - - if (!g_strcmp0(signal_name, "UserRealNameUpdated")) - { - const gchar * username = NULL; - g_variant_get (parameters, "(&s)", &username); - indicator_session_update_users_label (self, username); - } -} - -static void -indicator_session_update_users_label (IndicatorSession * self, - const gchar * name) -{ - gtk_label_set_text (self->entry.label, name ? name : ""); -} - -/*** -**** Disposition -***/ - -enum -{ - DISPOSITION_NORMAL, - DISPOSITION_INFO, - DISPOSITION_WARNING, - DISPOSITION_ALERT -}; - -static void -indicator_session_update_a11y_from_disposition (IndicatorSession * indicator, - int disposition) -{ - gchar * a11y; - const gchar * username = gtk_label_get_text (indicator->entry.label); - const gboolean need_attn = disposition != DISPOSITION_NORMAL; - const gboolean show_name = g_settings_get_boolean (indicator->settings, - "show-real-name-on-panel"); - - if (show_name && need_attn) - /* Translators: the name of the menu ("System"), followed by the user's name, - followed by a hint that an item in this menu requires an action from the user */ - a11y = g_strdup_printf (_("System %s (Attention Required)"), username); - else if (show_name) - /* Translators: the name of the menu ("System"), followed by the user's name */ - a11y = g_strdup_printf (_("System %s"), username); - else if (need_attn) - a11y = g_strdup (_("System (Attention Required)")); - else - a11y = g_strdup (_("System")); - - g_debug (G_STRLOC" setting a11y to \"%s\"", a11y); - g_clear_pointer (&indicator->entry.accessible_desc, g_free); - indicator->entry.accessible_desc = a11y; - g_signal_emit (indicator, - INDICATOR_OBJECT_SIGNAL_ACCESSIBLE_DESC_UPDATE_ID, - 0, - &indicator->entry); -} - -static void -indicator_session_update_icon_from_disposition (IndicatorSession * indicator, - int disposition) -{ - const gchar * icon; - - if (disposition == DISPOSITION_NORMAL) - icon = ICON_DEFAULT; - else if (disposition == DISPOSITION_INFO) - icon = ICON_INFO; - else - icon = ICON_ALERT; - - if (gtk_icon_theme_has_icon (indicator->icon_theme, icon) == FALSE) - icon = "gtk-missing-image"; - - g_debug (G_STRLOC" setting icon to \"%s\"", icon); - gtk_image_set_from_icon_name (GTK_IMAGE(indicator->entry.image), - icon, - GTK_ICON_SIZE_BUTTON); -} - -static int -calculate_disposition (IndicatorSession * indicator) -{ - GList * l; - DbusmenuMenuitem * root = dbusmenu_client_get_root (indicator->menu_client); - GList * children = dbusmenu_menuitem_get_children (root); - int ret = DISPOSITION_NORMAL; - - for (l=children; l!=NULL; l=l->next) - { - int val; - const gchar * key = DBUSMENU_MENUITEM_PROP_DISPOSITION; - const gchar * val_str = dbusmenu_menuitem_property_get (l->data, key); - - if (!g_strcmp0 (val_str, DBUSMENU_MENUITEM_DISPOSITION_ALERT)) - val = DISPOSITION_ALERT; - else if (!g_strcmp0 (val_str, DBUSMENU_MENUITEM_DISPOSITION_WARNING)) - val = DISPOSITION_WARNING; - else if (!g_strcmp0 (val_str, DBUSMENU_MENUITEM_DISPOSITION_INFORMATIVE)) - val = DISPOSITION_INFO; - else - val = DISPOSITION_NORMAL; - - if (ret < val) - ret = val; - } - - return ret; -} - -static void -indicator_session_update_icon_callback (GtkWidget * widget, gpointer callback_data) -{ - indicator_session_update_icon_and_a11y ((IndicatorSession *)callback_data); -} - -static void -indicator_session_update_icon_and_a11y (IndicatorSession * indicator) -{ - const int disposition = calculate_disposition (indicator); - indicator_session_update_a11y_from_disposition (indicator, disposition); - indicator_session_update_icon_from_disposition (indicator, disposition); -} - -static void -on_menuitem_property_changed (DbusmenuMenuitem * mi, - gchar * property, - GValue * value, - gpointer indicator) -{ - if (!g_strcmp0 (property, DBUSMENU_MENUITEM_PROP_DISPOSITION)) - indicator_session_update_icon_and_a11y (indicator); -} - -static void -on_menu_layout_updated (DbusmenuClient * client, IndicatorSession * session) -{ - GList * l; - DbusmenuMenuitem * root = dbusmenu_client_get_root (client); - GList * children = dbusmenu_menuitem_get_children (root); - static GQuark tag = 0; - - if (G_UNLIKELY (tag == 0)) - { - tag = g_quark_from_static_string ("x-tagged-by-indicator-session"); - } - - for (l=children; l!=NULL; l=l->next) - { - if (g_object_get_qdata (l->data, tag) == NULL) - { - g_object_set_qdata (l->data, tag, GINT_TO_POINTER(1)); - g_signal_connect (l->data, "property-changed", G_CALLBACK(on_menuitem_property_changed), session); - } - } -} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..45c5394 --- /dev/null +++ b/src/main.c @@ -0,0 +1,62 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <locale.h> +#include <stdlib.h> /* exit() */ + +#include <glib/gi18n.h> +#include <gio/gio.h> + +#include "service.h" + +/*** +**** +***/ + +static void +on_name_lost (gpointer instance G_GNUC_UNUSED, gpointer loop) +{ + g_warning ("exiting: service couldn't acquire, or lost ownership of, busname"); + + g_main_loop_quit (loop); +} + +int +main (int argc G_GNUC_UNUSED, char ** argv G_GNUC_UNUSED) +{ + GMainLoop * loop; + IndicatorSessionService * service; + + /* boilerplate i18n */ + setlocale (LC_ALL, ""); + bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); + textdomain (GETTEXT_PACKAGE); + + /* run */ + service = indicator_session_service_new (); + loop = g_main_loop_new (NULL, FALSE); + g_signal_connect (service, INDICATOR_SESSION_SERVICE_SIGNAL_NAME_LOST, + G_CALLBACK(on_name_lost), loop); + g_main_loop_run (loop); + + /* cleanup */ + g_clear_object (&service); + g_main_loop_unref (loop); + return 0; +} diff --git a/src/online-accounts-mgr.c b/src/online-accounts-mgr.c deleted file mode 100644 index 4abba00..0000000 --- a/src/online-accounts-mgr.c +++ /dev/null @@ -1,166 +0,0 @@ -/* -Copyright 2012 Canonical Ltd. - -Authors: - Alberto Mardegan <alberto.mardegan@canonical.com> - -This program is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License version 3, as published -by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranties of -MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <gio/gio.h> -#include <glib/gi18n.h> - -#include "online-accounts-mgr.h" - -#include <libdbusmenu-glib/client.h> - -struct _OnlineAccountsMgr -{ - GObject parent_instance; - GDBusProxy *proxy; - DbusmenuMenuitem *menu_item; -}; - -#define ONLINE_ACCOUNTS_OBJECT_PATH "/com/canonical/indicators/webcredentials" -#define ONLINE_ACCOUNTS_BUS_NAME "com.canonical.indicators.webcredentials" -#define ONLINE_ACCOUNTS_INTERFACE ONLINE_ACCOUNTS_BUS_NAME - -G_DEFINE_TYPE (OnlineAccountsMgr, online_accounts_mgr, G_TYPE_OBJECT); - -static void -update_disposition (OnlineAccountsMgr *self, GVariant *error_status_prop) -{ - gboolean error_status; - - error_status = g_variant_get_boolean (error_status_prop); - dbusmenu_menuitem_property_set (self->menu_item, - DBUSMENU_MENUITEM_PROP_DISPOSITION, - error_status ? - DBUSMENU_MENUITEM_DISPOSITION_ALERT : - DBUSMENU_MENUITEM_DISPOSITION_NORMAL); -} - -static void -on_properties_changed (GDBusProxy *proxy, - GVariant *changed_properties, - GStrv invalidated_properties, - OnlineAccountsMgr *self) -{ - if (g_variant_n_children (changed_properties) > 0) { - GVariantIter *iter; - const gchar *key; - GVariant *value; - - g_variant_get (changed_properties, "a{sv}", &iter); - while (g_variant_iter_loop (iter, "{&sv}", &key, &value)) { - if (g_strcmp0 (key, "ErrorStatus") == 0) { - update_disposition (self, value); - } - } - g_variant_iter_free (iter); - } -} - -static void -on_menu_item_activated (DbusmenuMenuitem *menu_item, - guint timestamp, - OnlineAccountsMgr *self) -{ - GError *error = NULL; - - if (!g_spawn_command_line_async("gnome-control-center credentials", &error)) - { - g_warning("Unable to show control center: %s", error->message); - g_error_free(error); - } -} - -static void -online_accounts_mgr_init (OnlineAccountsMgr *self) -{ - GError *error = NULL; - GVariant *error_status_prop; - - self->menu_item = dbusmenu_menuitem_new (); - dbusmenu_menuitem_property_set (self->menu_item, - DBUSMENU_MENUITEM_PROP_TYPE, - DBUSMENU_CLIENT_TYPES_DEFAULT); - dbusmenu_menuitem_property_set (self->menu_item, - DBUSMENU_MENUITEM_PROP_LABEL, - _("Online Accounts\342\200\246")); - g_signal_connect (self->menu_item, - DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, - G_CALLBACK (on_menu_item_activated), - self); - - self->proxy = - g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, - G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, - NULL, - ONLINE_ACCOUNTS_BUS_NAME, - ONLINE_ACCOUNTS_OBJECT_PATH, - ONLINE_ACCOUNTS_INTERFACE, - NULL, - &error); - if (G_UNLIKELY (error != NULL)) { - g_warning ("Couldn't create online_accounts proxy: %s", error->message); - g_clear_error (&error); - return; - } - - g_signal_connect (self->proxy, "g-properties-changed", - G_CALLBACK (on_properties_changed), self); - - error_status_prop = - g_dbus_proxy_get_cached_property (self->proxy, "ErrorStatus"); - if (error_status_prop != NULL) { - update_disposition (self, error_status_prop); - g_variant_unref (error_status_prop); - } -} - -static void -online_accounts_mgr_dispose (GObject *object) -{ - OnlineAccountsMgr *self = ONLINE_ACCOUNTS_MGR (object); - - if (self->proxy != NULL) { - g_object_unref (self->proxy); - self->proxy = NULL; - } - - if (self->menu_item != NULL) { - g_object_unref (self->menu_item); - self->menu_item = NULL; - } - - G_OBJECT_CLASS (online_accounts_mgr_parent_class)->dispose (object); -} - -static void -online_accounts_mgr_class_init (OnlineAccountsMgrClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - object_class->dispose = online_accounts_mgr_dispose; -} - -OnlineAccountsMgr *online_accounts_mgr_new () -{ - return g_object_new (ONLINE_ACCOUNTS_TYPE_MGR, NULL); -} - -DbusmenuMenuitem *online_accounts_mgr_get_menu_item (OnlineAccountsMgr *self) -{ - g_return_val_if_fail (ONLINE_ACCOUNTS_IS_MGR (self), NULL); - return self->menu_item; -} diff --git a/src/online-accounts-mgr.h b/src/online-accounts-mgr.h deleted file mode 100644 index 16c0461..0000000 --- a/src/online-accounts-mgr.h +++ /dev/null @@ -1,50 +0,0 @@ -/* -Copyright 2012 Canonical Ltd. - -Authors: - Alberto Mardegan <alberto.mardegan@canonical.com> - -This program is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License version 3, as published -by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranties of -MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef _ONLINE_ACCOUNTS_MGR_H_ -#define _ONLINE_ACCOUNTS_MGR_H_ - -#include <glib-object.h> -#include <libdbusmenu-glib/menuitem.h> - -G_BEGIN_DECLS - -#define ONLINE_ACCOUNTS_TYPE_MGR (online_accounts_mgr_get_type ()) -#define ONLINE_ACCOUNTS_MGR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ONLINE_ACCOUNTS_TYPE_MGR, OnlineAccountsMgr)) -#define ONLINE_ACCOUNTS_MGR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ONLINE_ACCOUNTS_TYPE_MGR, OnlineAccountsMgrClass)) -#define ONLINE_ACCOUNTS_IS_MGR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ONLINE_ACCOUNTS_TYPE_MGR)) -#define ONLINE_ACCOUNTS_IS_MGR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ONLINE_ACCOUNTS_TYPE_MGR)) -#define ONLINE_ACCOUNTS_MGR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ONLINE_ACCOUNTS_TYPE_MGR, OnlineAccountsMgrClass)) - -typedef struct _OnlineAccountsMgrClass OnlineAccountsMgrClass; -typedef struct _OnlineAccountsMgr OnlineAccountsMgr; - -struct _OnlineAccountsMgrClass -{ - GObjectClass parent_class; -}; - -GType online_accounts_mgr_get_type (void) G_GNUC_CONST; -OnlineAccountsMgr *online_accounts_mgr_new (void); - -DbusmenuMenuitem *online_accounts_mgr_get_menu_item (OnlineAccountsMgr *self); - -G_END_DECLS - -#endif /* _ONLINE_ACCOUNTS_MGR_H_ */ diff --git a/src/org.freedesktop.login1.Session.xml b/src/org.freedesktop.login1.Session.xml deleted file mode 100644 index 24a6fac..0000000 --- a/src/org.freedesktop.login1.Session.xml +++ /dev/null @@ -1,49 +0,0 @@ -<?xml version="1.0"?> -<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> -<node> - <interface name="org.freedesktop.login1.Session"> - <method name="Terminate"/> - <method name="Activate"/> - <method name="Lock"/> - <method name="Unlock"/> - <method name="SetIdleHint"> - <arg name="b" direction="in" type="b"/> - </method> - <method name="Kill"> - <arg name="who" direction="in" type="s"/> - <arg name="signal" direction="in" type="s"/> - </method> - <signal name="Lock"/> - <signal name="Unlock"/> - <property name="Id" type="s" access="read"/> - <property name="User" type="(uo)" access="read"> - <annotation name="org.qtproject.QtDBus.QtTypeName" value="UintPath"/> - </property> - <property name="Name" type="s" access="read"/> - <property name="Timestamp" type="t" access="read"/> - <property name="TimestampMonotonic" type="t" access="read"/> - <property name="DefaultControlGroup" type="s" access="read"/> - <property name="VTNr" type="u" access="read"/> - <property name="Seat" type="(so)" access="read"> - <annotation name="org.qtproject.QtDBus.QtTypeName" value="StringPath"/> - </property> - <property name="TTY" type="s" access="read"/> - <property name="Display" type="s" access="read"/> - <property name="Remote" type="b" access="read"/> - <property name="RemoteHost" type="s" access="read"/> - <property name="RemoteUser" type="s" access="read"/> - <property name="Service" type="s" access="read"/> - <property name="Leader" type="u" access="read"/> - <property name="Audit" type="u" access="read"/> - <property name="Type" type="s" access="read"/> - <property name="Class" type="s" access="read"/> - <property name="Active" type="b" access="read"/> - <property name="State" type="s" access="read"/> - <property name="Controllers" type="as" access="read"/> - <property name="ResetControllers" type="as" access="read"/> - <property name="KillProcesses" type="b" access="read"/> - <property name="IdleHint" type="b" access="read"/> - <property name="IdleSinceHint" type="t" access="read"/> - <property name="IdleSinceHintMonotonic" type="t" access="read"/> - </interface> -</node> diff --git a/src/service.c b/src/service.c new file mode 100644 index 0000000..74acd89 --- /dev/null +++ b/src/service.c @@ -0,0 +1,1182 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <glib/gi18n.h> +#include <gio/gio.h> + +#include "backend.h" +#include "service.h" + +#define BUS_NAME "com.canonical.indicator.session" +#define BUS_PATH "/com/canonical/indicator/session" + +#define ICON_DEFAULT "system-devices-panel" +#define ICON_INFO "system-devices-panel-information" +#define ICON_ALERT "system-devices-panel-alert" + +G_DEFINE_TYPE (IndicatorSessionService, + indicator_session_service, + G_TYPE_OBJECT) + +/* signals enum */ +enum +{ + NAME_LOST, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +enum +{ + PROP_0, + PROP_MAX_USERS, + PROP_LAST +}; + +static GParamSpec * properties[PROP_LAST]; + +enum +{ + SECTION_HEADER = (1<<0), + SECTION_ADMIN = (1<<1), + SECTION_SETTINGS = (1<<2), + SECTION_SWITCH = (1<<3), + SECTION_SESSION = (1<<4) +}; + +enum +{ + PROFILE_DESKTOP, + PROFILE_GREETER, + N_PROFILES +}; + +static const char * const menu_names[N_PROFILES] = +{ + "desktop", + "desktop_greeter" +}; + +struct ProfileMenuInfo +{ + /* the root level -- the header is the only child of this */ + GMenu * menu; + + /* parent of the sections. This is the header's submenu */ + GMenu * submenu; + + guint export_id; +}; + +struct _IndicatorSessionServicePrivate +{ + guint own_id; + guint max_users; + IndicatorSessionUsers * backend_users; + IndicatorSessionGuest * backend_guest; + IndicatorSessionActions * backend_actions; + GSettings * indicator_settings; + GSettings * keybinding_settings; + GSimpleActionGroup * actions; + guint actions_export_id; + struct ProfileMenuInfo menus[N_PROFILES]; + GSimpleAction * header_action; + GSimpleAction * user_switcher_action; + GSimpleAction * guest_switcher_action; + GHashTable * users; + guint rebuild_id; + int rebuild_flags; + GDBusConnection * conn; + GCancellable * cancellable; +}; + +typedef IndicatorSessionServicePrivate priv_t; + +static const char * get_current_real_name (IndicatorSessionService * self); + +/*** +**** +***/ + +static void rebuild_now (IndicatorSessionService * self, int section); +static void rebuild_soon (IndicatorSessionService * self, int section); + +static inline void +rebuild_header_soon (IndicatorSessionService * self) +{ + rebuild_soon (self, SECTION_HEADER); +} +static inline void +rebuild_switch_section_soon (IndicatorSessionService * self) +{ + rebuild_soon (self, SECTION_SWITCH); +} +static inline void +rebuild_session_section_soon (IndicatorSessionService * self) +{ + rebuild_soon (self, SECTION_SESSION); +} +static inline void +rebuild_settings_section_soon (IndicatorSessionService * self) +{ + rebuild_soon (self, SECTION_SETTINGS); +} + +/*** +**** +***/ + +static void +update_header_action (IndicatorSessionService * self) +{ + gchar * a11y; + gboolean need_attn; + gboolean show_name; + GVariant * variant; + const gchar * real_name; + const gchar * label; + const gchar * iconstr; + const priv_t * const p = self->priv; + + g_return_if_fail (p->header_action != NULL); + + if (indicator_session_actions_has_online_account_error (p->backend_actions)) + { + need_attn = TRUE; + iconstr = ICON_ALERT; + } + else + { + need_attn = FALSE; + iconstr = ICON_DEFAULT; + } + + show_name = g_settings_get_boolean (p->indicator_settings, + "show-real-name-on-panel"); + + real_name = get_current_real_name (self); + label = show_name && real_name ? real_name : ""; + + if (*label && need_attn) + { + /* Translators: the name of the menu ("System"), then the user's name, + then a hint that something in this menu requires user attention */ + a11y = g_strdup_printf (_("System, %s (Attention Required)"), real_name); + } + else if (*label) + { + /* Translators: the name of the menu ("System"), then the user's name */ + a11y = g_strdup_printf (_("System, %s"), label); + } + else if (need_attn) + { + a11y = g_strdup (_("System (Attention Required)")); + } + else + { + a11y = g_strdup (_("System")); + } + + variant = g_variant_new ("(sssb)", label, iconstr, a11y, TRUE); + g_simple_action_set_state (p->header_action, variant); + g_free (a11y); +} + +/*** +**** USERS +***/ + +static GMenuModel * create_switch_section (IndicatorSessionService * self); + +static void +add_user (IndicatorSessionService * self, guint uid) +{ + IndicatorSessionUser * u; + + if ((u = indicator_session_users_get_user (self->priv->backend_users, uid))) + { + /* update our user table */ + g_hash_table_insert (self->priv->users, GUINT_TO_POINTER(uid), u); + + /* queue rebuilds for the affected sections */ + rebuild_switch_section_soon (self); + if (u->is_current_user) + rebuild_header_soon (self); + } +} + +static void +on_user_added (IndicatorSessionUsers * backend_users G_GNUC_UNUSED, + guint uid, + gpointer gself) +{ + add_user (INDICATOR_SESSION_SERVICE(gself), uid); +} + +static void +on_user_changed (IndicatorSessionUsers * backend_users G_GNUC_UNUSED, + guint uid, + gpointer gself) +{ + add_user (INDICATOR_SESSION_SERVICE(gself), uid); +} + +static void +on_user_removed (IndicatorSessionUsers * backend_users G_GNUC_UNUSED, + guint uid, + gpointer gself) +{ + IndicatorSessionService * self = INDICATOR_SESSION_SERVICE (gself); + g_return_if_fail (self != NULL); + + /* update our user table */ + g_hash_table_remove (self->priv->users, GUINT_TO_POINTER(uid)); + + /* enqueue rebuilds for the affected sections */ + rebuild_switch_section_soon (self); +} + +static const char * +get_current_real_name (IndicatorSessionService * self) +{ + GHashTableIter iter; + gpointer key, value; + + /* is it the guest? */ + if (indicator_session_guest_is_active (self->priv->backend_guest)) + return _("Guest"); + + /* is it a user? */ + g_hash_table_iter_init (&iter, self->priv->users); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + IndicatorSessionUser * user = value; + if (user->is_current_user) + return user->real_name; + } + + return ""; +} + +/*** +**** +***/ + +static GMenuModel * +create_admin_section (void) +{ + GMenu * menu; + + menu = g_menu_new (); + g_menu_append (menu, _("About This Computer"), "indicator.about"); + g_menu_append (menu, _("Ubuntu Help"), "indicator.help"); + return G_MENU_MODEL (menu); +} + +static GMenuModel * +create_settings_section (IndicatorSessionService * self) +{ + GMenu * menu; + priv_t * p = self->priv; + + menu = g_menu_new (); + g_menu_append (menu, _("System Settings…"), "indicator.settings"); + if (indicator_session_actions_has_online_account_error (p->backend_actions)) + g_menu_append (menu, _("Online Accounts…"), "indicator.online-accounts"); + + return G_MENU_MODEL (menu); +} + +/** + * The switch-to-guest action's state is a dictionary with these entries: + * - "is-active" (boolean) + * - "is-logged-in" (boolean) + */ +static GVariant * +create_guest_switcher_state (IndicatorSessionService * self) +{ + GVariant * val; + GVariantBuilder b; + IndicatorSessionGuest * const g = self->priv->backend_guest; + + g_variant_builder_init (&b, G_VARIANT_TYPE ("a{sv}")); + val = g_variant_new_boolean (indicator_session_guest_is_active (g)); + g_variant_builder_add (&b, "{sv}", "is-active", val); + val = g_variant_new_boolean (indicator_session_guest_is_logged_in (g)); + g_variant_builder_add (&b, "{sv}", "is-logged-in", val); + return g_variant_builder_end (&b); +} + +/** + * The switch-to-user action's state is a dictionary with these entries: + * - "active-user" (username string) + * - "logged-in-users" (array of username strings) + */ +static GVariant * +create_user_switcher_state (IndicatorSessionService * self) +{ + GVariantBuilder a; + GVariantBuilder b; + GVariant * val; + GHashTableIter ht_iter; + gpointer ht_value; + const char * current_user; + + current_user = ""; + g_variant_builder_init (&a, G_VARIANT_TYPE("as")); + g_hash_table_iter_init (&ht_iter, self->priv->users); + while (g_hash_table_iter_next (&ht_iter, NULL, &ht_value)) + { + const IndicatorSessionUser * u = ht_value; + + if (u->is_current_user) + current_user = u->user_name; + + if (u->is_logged_in) + g_variant_builder_add (&a, "s", u->user_name); + } + + g_variant_builder_init (&b, G_VARIANT_TYPE("a{sv}")); + val = g_variant_new_string (current_user); + g_variant_builder_add (&b, "{sv}", "active-user", val); + val = g_variant_builder_end (&a); + g_variant_builder_add (&b, "{sv}", "logged-in-users", val); + return g_variant_builder_end (&b); +} + +static void +update_switch_actions (IndicatorSessionService * self) +{ + g_simple_action_set_state (self->priv->guest_switcher_action, + create_guest_switcher_state (self)); + + g_simple_action_set_state (self->priv->user_switcher_action, + create_user_switcher_state (self)); +} + +static gboolean +use_ellipsis (IndicatorSessionService * self) +{ + /* does the backend support confirmation prompts? */ + if (!indicator_session_actions_can_prompt (self->priv->backend_actions)) + return FALSE; + + /* has the user disabled prompts? */ + if (g_settings_get_boolean (self->priv->indicator_settings, + "suppress-logout-restart-shutdown")) + return FALSE; + + return TRUE; +} + +/* lower index == more useful. + When there are too many users for the menu, + we use this to decide which to cull. */ +static int +compare_users_by_usefulness (gconstpointer ga, gconstpointer gb) +{ + const IndicatorSessionUser * a = *(const IndicatorSessionUser**)ga; + const IndicatorSessionUser * b = *(const IndicatorSessionUser**)gb; + + if (a->is_current_user != b->is_current_user) + return a->is_current_user ? -1 : 1; + + if (a->is_logged_in != b->is_logged_in) + return a->is_logged_in ? -1 : 1; + + if (a->login_frequency != b->login_frequency) + return a->login_frequency > b->login_frequency ? -1 : 1; + + return 0; +} + +/* sorting them for display in the menu */ +static int +compare_users_by_label (gconstpointer ga, gconstpointer gb) +{ + int i; + const IndicatorSessionUser * a = *(const IndicatorSessionUser**)ga; + const IndicatorSessionUser * b = *(const IndicatorSessionUser**)gb; + + if ((i = g_strcmp0 (a->real_name, b->real_name))) + return i; + + return g_strcmp0 (a->user_name, b->user_name); +} + +static GMenuModel * +create_switch_section (IndicatorSessionService * self) +{ + gchar * str; + GMenu * menu; + GMenuItem * item; + guint i; + gpointer guser; + GHashTableIter iter; + GPtrArray * users; + const priv_t * const p = self->priv; + const gboolean ellipsis = use_ellipsis (self); + + menu = g_menu_new (); + + /* lockswitch */ + if (indicator_session_users_is_live_session (p->backend_users)) + { + const char * action = "indicator.switch-to-screensaver"; + item = g_menu_item_new (_("Start Screen Saver"), action); + } + else if (indicator_session_guest_is_active (p->backend_guest)) + { + const char * action = "indicator.switch-to-greeter"; + item = g_menu_item_new (ellipsis ? _("Switch Account…") + : _("Switch Account"), action); + } + else if (g_hash_table_size (p->users) == 1) + { + const char * action = "indicator.switch-to-greeter"; + item = g_menu_item_new (_("Lock"), action); + } + else + { + const char * action = "indicator.switch-to-greeter"; + item = g_menu_item_new (ellipsis ? _("Lock/Switch Account…") + : _("Lock/Switch Account"), action); + } + str = g_settings_get_string (p->keybinding_settings, "screensaver"); + g_menu_item_set_attribute (item, "accel", "s", str); + g_free (str); + g_menu_append_item (menu, item); + g_object_unref (item); + + if (indicator_session_guest_is_allowed (p->backend_guest)) + { + GMenuItem *item; + + item = g_menu_item_new (_("Guest Session"), "indicator.switch-to-guest"); + g_menu_item_set_attribute (item, "x-canonical-type", "s", "indicator.guest-menu-item"); + g_menu_append_item (menu, item); + + g_object_unref (item); + } + + /* build an array of all the users we know of */ + users = g_ptr_array_new (); + g_hash_table_iter_init (&iter, p->users); + while (g_hash_table_iter_next (&iter, NULL, &guser)) + g_ptr_array_add (users, guser); + + /* if there are too many users, cull out the less interesting ones */ + if (users->len > p->max_users) + { + g_ptr_array_sort (users, compare_users_by_usefulness); + g_ptr_array_set_size (users, p->max_users); + } + + /* sort the users by name */ + g_ptr_array_sort (users, compare_users_by_label); + + /* add the users */ + for (i=0; i<users->len; ++i) + { + const IndicatorSessionUser * u = g_ptr_array_index (users, i); + item = g_menu_item_new (u->real_name, NULL); + g_menu_item_set_action_and_target (item, "indicator.switch-to-user", "s", u->user_name); + g_menu_item_set_attribute (item, "x-canonical-type", "s", "indicator.user-menu-item"); + + if (u->icon_file != NULL) + { + GFile * file = g_file_new_for_path (u->icon_file); + GIcon * icon = g_file_icon_new (file); + g_menu_item_set_attribute_value (item, G_MENU_ATTRIBUTE_ICON, g_icon_serialize (icon)); + g_clear_object (&icon); + g_clear_object (&file); + } + + g_menu_append_item (menu, item); + g_object_unref (item); + } + + /* cleanup */ + g_ptr_array_free (users, TRUE); + return G_MENU_MODEL (menu); +} + +static GMenuModel * +create_session_section (IndicatorSessionService * self, int profile) +{ + GMenu * menu; + const priv_t * const p = self->priv; + GSettings * const s = p->indicator_settings; + const gboolean ellipsis = use_ellipsis (self); + + menu = g_menu_new (); + + if ((profile == PROFILE_DESKTOP) && + (indicator_session_actions_can_logout (p->backend_actions))) + { + const char * label = ellipsis ? _("Log Out…") : _("Log Out"); + g_menu_append (menu, label, "indicator.logout"); + } + + if (indicator_session_actions_can_suspend (p->backend_actions)) + g_menu_append (menu, _("Suspend"), "indicator.suspend"); + + if (indicator_session_actions_can_hibernate (p->backend_actions)) + g_menu_append (menu, _("Hibernate"), "indicator.hibernate"); + + if (indicator_session_actions_can_reboot (p->backend_actions)) + { + const char * label = ellipsis ? _("Restart…") : _("Restart"); + g_menu_append (menu, label, "indicator.reboot"); + } + + if (!g_settings_get_boolean (s, "suppress-shutdown-menuitem")) + { + const char * label = ellipsis ? _("Shut Down…") : _("Shut Down"); + g_menu_append (menu, label, "indicator.power-off"); + } + + return G_MENU_MODEL (menu); +} + +static void +create_menu (IndicatorSessionService * self, int profile) +{ + GMenu * menu; + GMenu * submenu; + GMenuItem * header; + GMenuModel * sections[16]; + int i; + int n = 0; + + g_assert (0<=profile && profile<N_PROFILES); + g_assert (self->priv->menus[profile].menu == NULL); + + if (profile == PROFILE_DESKTOP) + { + sections[n++] = create_admin_section (); + sections[n++] = create_settings_section (self); + sections[n++] = create_switch_section (self); + sections[n++] = create_session_section (self, profile); + } + else if (profile == PROFILE_GREETER) + { + sections[n++] = create_session_section (self, profile); + } + + /* add sections to the submenu */ + submenu = g_menu_new (); + for (i=0; i<n; ++i) + { + g_menu_append_section (submenu, NULL, sections[i]); + g_object_unref (sections[i]); + } + + /* add submenu to the header */ + header = g_menu_item_new (NULL, "indicator._header"); + g_menu_item_set_attribute (header, "x-canonical-type", "s", "com.canonical.indicator.root"); + g_menu_item_set_submenu (header, G_MENU_MODEL (submenu)); + g_object_unref (submenu); + + /* add header to the menu */ + menu = g_menu_new (); + g_menu_append_item (menu, header); + g_object_unref (header); + + self->priv->menus[profile].menu = menu; + self->priv->menus[profile].submenu = submenu; +} + +/*** +**** GActions +***/ + +static IndicatorSessionActions * +get_backend_actions (gpointer gself) +{ + return INDICATOR_SESSION_SERVICE(gself)->priv->backend_actions; +} + +static void +on_about_activated (GSimpleAction * a G_GNUC_UNUSED, + GVariant * param G_GNUC_UNUSED, + gpointer gself) +{ + indicator_session_actions_about (get_backend_actions(gself)); +} + +static void +on_online_accounts_activated (GSimpleAction * a G_GNUC_UNUSED, + GVariant * param G_GNUC_UNUSED, + gpointer gself) +{ + indicator_session_actions_online_accounts (get_backend_actions(gself)); +} + +static void +on_help_activated (GSimpleAction * a G_GNUC_UNUSED, + GVariant * param G_GNUC_UNUSED, + gpointer gself) +{ + indicator_session_actions_help (get_backend_actions(gself)); +} + +static void +on_settings_activated (GSimpleAction * a G_GNUC_UNUSED, + GVariant * param G_GNUC_UNUSED, + gpointer gself) +{ + indicator_session_actions_settings (get_backend_actions(gself)); +} + +static void +on_logout_activated (GSimpleAction * a G_GNUC_UNUSED, + GVariant * param G_GNUC_UNUSED, + gpointer gself) +{ + indicator_session_actions_logout (get_backend_actions(gself)); +} + +static void +on_suspend_activated (GSimpleAction * a G_GNUC_UNUSED, + GVariant * param G_GNUC_UNUSED, + gpointer gself) +{ + indicator_session_actions_suspend (get_backend_actions(gself)); +} + +static void +on_hibernate_activated (GSimpleAction * a G_GNUC_UNUSED, + GVariant * param G_GNUC_UNUSED, + gpointer gself) +{ + indicator_session_actions_hibernate (get_backend_actions(gself)); +} + +static void +on_reboot_activated (GSimpleAction * action G_GNUC_UNUSED, + GVariant * param G_GNUC_UNUSED, + gpointer gself) +{ + indicator_session_actions_reboot (get_backend_actions(gself)); +} + +static void +on_power_off_activated (GSimpleAction * a G_GNUC_UNUSED, + GVariant * param G_GNUC_UNUSED, + gpointer gself) +{ + indicator_session_actions_power_off (get_backend_actions(gself)); +} + +static void +on_guest_activated (GSimpleAction * a G_GNUC_UNUSED, + GVariant * param G_GNUC_UNUSED, + gpointer gself) +{ + indicator_session_actions_switch_to_guest (get_backend_actions(gself)); +} + +static void +on_screensaver_activated (GSimpleAction * a G_GNUC_UNUSED, + GVariant * param G_GNUC_UNUSED, + gpointer gself) +{ + indicator_session_actions_switch_to_screensaver (get_backend_actions(gself)); +} + +static void +on_greeter_activated (GSimpleAction * a G_GNUC_UNUSED, + GVariant * param G_GNUC_UNUSED, + gpointer gself) +{ + indicator_session_actions_switch_to_greeter (get_backend_actions(gself)); +} + +static void +on_user_activated (GSimpleAction * a G_GNUC_UNUSED, + GVariant * param, + gpointer gself) +{ + const char * username = g_variant_get_string (param, NULL); + indicator_session_actions_switch_to_username (get_backend_actions(gself), + username); +} + +static void +init_gactions (IndicatorSessionService * self) +{ + GVariant * v; + GSimpleAction * a; + priv_t * p = self->priv; + + GActionEntry entries[] = { + { "about", on_about_activated }, + { "help", on_help_activated }, + { "hibernate", on_hibernate_activated }, + { "logout", on_logout_activated }, + { "online-accounts", on_online_accounts_activated }, + { "reboot", on_reboot_activated }, + { "settings", on_settings_activated }, + { "switch-to-screensaver", on_screensaver_activated }, + { "switch-to-greeter", on_greeter_activated }, + { "suspend", on_suspend_activated }, + { "power-off", on_power_off_activated } + }; + + p->actions = g_simple_action_group_new (); + + g_action_map_add_action_entries (G_ACTION_MAP(p->actions), + entries, + G_N_ELEMENTS(entries), + self); + + /* add switch-to-guest action */ + v = create_guest_switcher_state (self); + a = g_simple_action_new_stateful ("switch-to-guest", NULL, v); + g_signal_connect (a, "activate", G_CALLBACK(on_guest_activated), self); + g_simple_action_group_insert (p->actions, G_ACTION(a)); + p->guest_switcher_action = a; + + /* add switch-to-user action... parameter is the uesrname */ + v = create_user_switcher_state (self); + a = g_simple_action_new_stateful ("switch-to-user", G_VARIANT_TYPE_STRING, v); + g_signal_connect (a, "activate", G_CALLBACK(on_user_activated), self); + g_simple_action_group_insert (p->actions, G_ACTION(a)); + p->user_switcher_action = a; + + /* add the header action */ + v = g_variant_new ("(sssb)", "label", ICON_DEFAULT, "a11y", TRUE); + a = g_simple_action_new_stateful ("_header", NULL, v); + g_simple_action_group_insert (p->actions, G_ACTION(a)); + p->header_action = a; + + rebuild_now (self, SECTION_HEADER); +} + +/*** +**** +***/ + +/** + * A small helper function for rebuild_now(). + * - removes the previous section + * - adds and unrefs the new section + */ +static void +rebuild_section (GMenu * parent, int pos, GMenuModel * new_section) +{ + g_menu_remove (parent, pos); + g_menu_insert_section (parent, pos, NULL, new_section); + g_object_unref (new_section); +} + +static void +rebuild_now (IndicatorSessionService * self, int sections) +{ + priv_t * p = self->priv; + struct ProfileMenuInfo * desktop = &p->menus[PROFILE_DESKTOP]; + struct ProfileMenuInfo * greeter = &p->menus[PROFILE_GREETER]; + + if (sections & SECTION_HEADER) + { + update_header_action (self); + } + + if (sections & SECTION_ADMIN) + { + rebuild_section (desktop->submenu, 0, create_admin_section()); + } + + if (sections & SECTION_SETTINGS) + { + rebuild_section (desktop->submenu, 1, create_settings_section(self)); + } + + if (sections & SECTION_SWITCH) + { + rebuild_section (desktop->submenu, 2, create_switch_section(self)); + update_switch_actions (self); + } + + if (sections & SECTION_SESSION) + { + rebuild_section (desktop->submenu, 3, create_session_section(self, PROFILE_DESKTOP)); + rebuild_section (greeter->submenu, 0, create_session_section(self, PROFILE_GREETER)); + } +} + +static int +rebuild_timeout_func (IndicatorSessionService * self) +{ + priv_t * p = self->priv; + rebuild_now (self, p->rebuild_flags); + p->rebuild_flags = 0; + p->rebuild_id = 0; + return G_SOURCE_REMOVE; +} + +static void +rebuild_soon (IndicatorSessionService * self, int section) +{ + priv_t * p = self->priv; + + p->rebuild_flags |= section; + + if (p->rebuild_id == 0) + { + /* Change events seem to come over the bus in small bursts. This msec + value is an arbitrary number that tries to be large enough to fold + multiple events into a single rebuild, but small enough that the + user won't notice any lag. */ + static const int REBUILD_INTERVAL_MSEC = 500; + + p->rebuild_id = g_timeout_add (REBUILD_INTERVAL_MSEC, + (GSourceFunc)rebuild_timeout_func, + self); + } +} + +/*** +**** GDBus +***/ + +static void +on_bus_acquired (GDBusConnection * connection, + const gchar * name, + gpointer gself) +{ + int i; + guint id; + GError * err = NULL; + IndicatorSessionService * self = INDICATOR_SESSION_SERVICE(gself); + priv_t * p = self->priv; + + g_debug ("bus acquired: %s", name); + + p->conn = g_object_ref (G_OBJECT (connection)); + + /* export the actions */ + if ((id = g_dbus_connection_export_action_group (connection, + BUS_PATH, + G_ACTION_GROUP (p->actions), + &err))) + { + p->actions_export_id = id; + } + else + { + g_warning ("cannot export action group: %s", err->message); + g_clear_error (&err); + } + + /* export the menus */ + for (i=0; i<N_PROFILES; ++i) + { + char * path = g_strdup_printf ("%s/%s", BUS_PATH, menu_names[i]); + struct ProfileMenuInfo * menu = &p->menus[i]; + + if (menu->menu == NULL) + create_menu (self, i); + + if ((id = g_dbus_connection_export_menu_model (connection, + path, + G_MENU_MODEL (menu->menu), + &err))) + { + menu->export_id = id; + } + else + { + g_warning ("cannot export %s menu: %s", menu_names[i], err->message); + g_clear_error (&err); + } + + g_free (path); + } +} + +static void +unexport (IndicatorSessionService * self) +{ + int i; + priv_t * p = self->priv; + + /* unexport the menus */ + for (i=0; i<N_PROFILES; ++i) + { + guint * id = &self->priv->menus[i].export_id; + + if (*id) + { + g_dbus_connection_unexport_menu_model (p->conn, *id); + *id = 0; + } + } + + /* unexport the actions */ + if (p->actions_export_id) + { + g_dbus_connection_unexport_action_group (p->conn, p->actions_export_id); + p->actions_export_id = 0; + } +} + +static void +on_name_lost (GDBusConnection * connection G_GNUC_UNUSED, + const gchar * name, + gpointer gself) +{ + IndicatorSessionService * self = INDICATOR_SESSION_SERVICE (gself); + + g_debug ("%s %s name lost %s", G_STRLOC, G_STRFUNC, name); + + unexport (self); + + g_signal_emit (self, signals[NAME_LOST], 0, NULL); +} + +/*** +**** +***/ + +static void +/* cppcheck-suppress unusedFunction */ +indicator_session_service_init (IndicatorSessionService * self) +{ + GList * l; + GList * uids; + priv_t * p; + gpointer gp; + + /* init our priv pointer */ + p = G_TYPE_INSTANCE_GET_PRIVATE (self, + INDICATOR_TYPE_SESSION_SERVICE, + IndicatorSessionServicePrivate); + p->indicator_settings = g_settings_new ("com.canonical.indicator.session"); + p->keybinding_settings = g_settings_new ("org.gnome.settings-daemon.plugins.media-keys"); + self->priv = p; + + /* init the backend objects */ + p->cancellable = g_cancellable_new (); + backend_get (p->cancellable, &p->backend_actions, + &p->backend_users, + &p->backend_guest); + + /* init our key-to-User table */ + p->users = g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + (GDestroyNotify)indicator_session_user_free); + uids = indicator_session_users_get_uids (p->backend_users); + for (l=uids; l!=NULL; l=l->next) + add_user (self, GPOINTER_TO_UINT(l->data)); + g_list_free (uids); + + init_gactions (self); + + /* watch for changes in backend_users */ + gp = p->backend_users; + g_signal_connect (gp, INDICATOR_SESSION_USERS_SIGNAL_USER_ADDED, + G_CALLBACK(on_user_added), self); + g_signal_connect (gp, INDICATOR_SESSION_USERS_SIGNAL_USER_CHANGED, + G_CALLBACK(on_user_changed), self); + g_signal_connect (gp, INDICATOR_SESSION_USERS_SIGNAL_USER_REMOVED, + G_CALLBACK(on_user_removed), self); + g_signal_connect_swapped (gp, "notify::is-live-session", + G_CALLBACK(rebuild_switch_section_soon), self); + + /* watch for changes in backend_guest */ + gp = p->backend_guest; + g_signal_connect_swapped (gp, "notify::guest-is-active-session", + G_CALLBACK(rebuild_header_soon), self); + g_signal_connect_swapped (gp, "notify", + G_CALLBACK(rebuild_switch_section_soon), self); + + /* watch for updates in backend_actions */ + gp = p->backend_actions; + g_signal_connect_swapped (gp, "notify", + G_CALLBACK(rebuild_switch_section_soon), self); + g_signal_connect_swapped (gp, "notify", + G_CALLBACK(rebuild_session_section_soon), self); + g_signal_connect_swapped (gp, "notify::has-online-account-error", + G_CALLBACK(rebuild_header_soon), self); + g_signal_connect_swapped (gp, "notify::has-online-account-error", + G_CALLBACK(rebuild_settings_section_soon), self); + + /* watch for changes in the indicator's settings */ + gp = p->indicator_settings; + g_signal_connect_swapped (gp, "changed::suppress-logout-restart-shutdown", + G_CALLBACK(rebuild_switch_section_soon), self); + g_signal_connect_swapped (gp, "changed::suppress-logout-restart-shutdown", + G_CALLBACK(rebuild_session_section_soon), self); + g_signal_connect_swapped (gp, "changed::suppress-shutdown-menuitem", + G_CALLBACK(rebuild_session_section_soon), self); + g_signal_connect_swapped (gp, "changed::show-real-name-on-panel", + G_CALLBACK(rebuild_header_soon), self); + + /* watch for changes to the lock keybinding */ + gp = p->keybinding_settings; + g_signal_connect_swapped (gp, "changed::screensaver", + G_CALLBACK(rebuild_switch_section_soon), self); + + self->priv->own_id = g_bus_own_name (G_BUS_TYPE_SESSION, + BUS_NAME, + G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT, + on_bus_acquired, + NULL, + on_name_lost, + self, + NULL); +} + +/*** +**** GObject plumbing: properties +***/ + +static void +my_get_property (GObject * o, + guint property_id, + GValue * value, + GParamSpec * pspec) +{ + IndicatorSessionService * self = INDICATOR_SESSION_SERVICE (o); + + switch (property_id) + { + case PROP_MAX_USERS: + g_value_set_uint (value, self->priv->max_users); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec); + } +} + +static void +my_set_property (GObject * o, + guint property_id, + const GValue * value, + GParamSpec * pspec) +{ + IndicatorSessionService * self = INDICATOR_SESSION_SERVICE (o); + + switch (property_id) + { + case PROP_MAX_USERS: + self->priv->max_users = g_value_get_uint (value); + rebuild_switch_section_soon (self); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec); + } +} + +/*** +**** GObject plumbing: life cycle +***/ + +static void +my_dispose (GObject * o) +{ + int i; + IndicatorSessionService * self = INDICATOR_SESSION_SERVICE(o); + priv_t * p = self->priv; + + if (p->own_id) + { + g_bus_unown_name (p->own_id); + p->own_id = 0; + } + + unexport (self); + + if (p->cancellable != NULL) + { + g_cancellable_cancel (p->cancellable); + g_clear_object (&p->cancellable); + } + + if (p->rebuild_id) + { + g_source_remove (p->rebuild_id); + p->rebuild_id = 0; + } + + g_clear_pointer (&p->users, g_hash_table_destroy); + g_clear_object (&p->backend_users); + g_clear_object (&p->backend_guest); + g_clear_object (&p->backend_actions); + g_clear_object (&p->indicator_settings); + g_clear_object (&p->keybinding_settings); + g_clear_object (&p->actions); + + for (i=0; i<N_PROFILES; ++i) + g_clear_object (&p->menus[i].menu); + + g_clear_object (&p->header_action); + g_clear_object (&p->user_switcher_action); + g_clear_object (&p->guest_switcher_action); + g_clear_object (&p->conn); + + G_OBJECT_CLASS (indicator_session_service_parent_class)->dispose (o); +} + +static void +/* cppcheck-suppress unusedFunction */ +indicator_session_service_class_init (IndicatorSessionServiceClass * klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = my_dispose; + object_class->get_property = my_get_property; + object_class->set_property = my_set_property; + + g_type_class_add_private (klass, sizeof (IndicatorSessionServicePrivate)); + + signals[NAME_LOST] = g_signal_new (INDICATOR_SESSION_SERVICE_SIGNAL_NAME_LOST, + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (IndicatorSessionServiceClass, name_lost), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + properties[PROP_0] = NULL; + + properties[PROP_MAX_USERS] = g_param_spec_uint ("max-users", + "Max Users", + "Max visible users", + 0, INT_MAX, 12, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, PROP_LAST, properties); +} + +IndicatorSessionService * +indicator_session_service_new (void) +{ + GObject * o = g_object_new (INDICATOR_TYPE_SESSION_SERVICE, NULL); + + return INDICATOR_SESSION_SERVICE (o); +} diff --git a/src/service.h b/src/service.h new file mode 100644 index 0000000..fa870e5 --- /dev/null +++ b/src/service.h @@ -0,0 +1,71 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __INDICATOR_SESSION_SERVICE_H__ +#define __INDICATOR_SESSION_SERVICE_H__ + +#include <glib.h> +#include <glib-object.h> + +G_BEGIN_DECLS + +/* standard GObject macros */ +#define INDICATOR_TYPE_SESSION_SERVICE (indicator_session_service_get_type()) +#define INDICATOR_SESSION_SERVICE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_SESSION_SERVICE, IndicatorSessionService)) +#define INDICATOR_SESSION_SERVICE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_SESSION_SERVICE, IndicatorSessionServiceClass)) +#define INDICATOR_SESSION_SERVICE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), INDICATOR_TYPE_SESSION_SERVICE, IndicatorSessionServiceClass)) +#define INDICATOR_IS_SESSION_SERVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_SESSION_SERVICE)) + +typedef struct _IndicatorSessionService IndicatorSessionService; +typedef struct _IndicatorSessionServiceClass IndicatorSessionServiceClass; +typedef struct _IndicatorSessionServicePrivate IndicatorSessionServicePrivate; + +/* signal keys */ +#define INDICATOR_SESSION_SERVICE_SIGNAL_NAME_LOST "name-lost" + +/** + * The Indicator Session Service. + */ +struct _IndicatorSessionService +{ + /*< private >*/ + GObject parent; + IndicatorSessionServicePrivate * priv; +}; + +struct _IndicatorSessionServiceClass +{ + GObjectClass parent_class; + + /* signals */ + + void (* name_lost)(IndicatorSessionService * self); +}; + +/*** +**** +***/ + +GType indicator_session_service_get_type (void); + +IndicatorSessionService * indicator_session_service_new (void); + +G_END_DECLS + +#endif /* __INDICATOR_SESSION_SERVICE_H__ */ diff --git a/src/session-dbus.c b/src/session-dbus.c deleted file mode 100644 index 4ece444..0000000 --- a/src/session-dbus.c +++ /dev/null @@ -1,282 +0,0 @@ -/* -The Dbus object on the bus for the indicator. - -Copyright 2010 Canonical Ltd. - -Authors: - Ted Gould <ted@canonical.com> - Conor Curran <conor.curran@canonical.com> - -This program is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License version 3, as published -by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranties of -MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <gio/gio.h> - -#include "session-dbus.h" -#include "shared-names.h" - -static GVariant * get_users_real_name (SessionDbus * service); -static void bus_get_cb (GObject * object, GAsyncResult * res, gpointer user_data); -static void bus_method_call (GDBusConnection * connection, const gchar * sender, const gchar * path, const gchar * interface, const gchar * method, GVariant * params, GDBusMethodInvocation * invocation, gpointer user_data); - -#include "gen-session-dbus.xml.h" - -typedef struct _SessionDbusPrivate SessionDbusPrivate; -struct _SessionDbusPrivate { - gchar * name; - GDBusConnection * bus; - GCancellable * bus_cancel; - guint dbus_registration; -}; - -/* GDBus Stuff */ -static GDBusNodeInfo * node_info = NULL; -static GDBusInterfaceInfo * interface_info = NULL; -static GDBusInterfaceVTable interface_table = { - method_call: bus_method_call, - get_property: NULL, /* No properties */ - set_property: NULL /* No properties */ -}; - -#define SESSION_DBUS_GET_PRIVATE(o) \ -(G_TYPE_INSTANCE_GET_PRIVATE ((o), SESSION_DBUS_TYPE, SessionDbusPrivate)) - -static void session_dbus_class_init (SessionDbusClass *klass); -static void session_dbus_init (SessionDbus *self); -static void session_dbus_dispose (GObject *object); -static void session_dbus_finalize (GObject *object); - -G_DEFINE_TYPE (SessionDbus, session_dbus, G_TYPE_OBJECT); - -static void -session_dbus_class_init (SessionDbusClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - g_type_class_add_private (klass, sizeof (SessionDbusPrivate)); - - object_class->dispose = session_dbus_dispose; - object_class->finalize = session_dbus_finalize; - - /* Setting up the DBus interfaces */ - if (node_info == NULL) { - GError * error = NULL; - - node_info = g_dbus_node_info_new_for_xml(_session_dbus, &error); - if (error != NULL) { - g_error("Unable to parse Session Service Interface description: %s", error->message); - g_error_free(error); - } - } - - if (interface_info == NULL) { - interface_info = g_dbus_node_info_lookup_interface(node_info, INDICATOR_SESSION_SERVICE_DBUS_IFACE); - - if (interface_info == NULL) { - g_error("Unable to find interface '" INDICATOR_SESSION_SERVICE_DBUS_IFACE "'"); - } - } - - return; -} - -static void -session_dbus_init (SessionDbus *self) -{ - SessionDbusPrivate * priv = SESSION_DBUS_GET_PRIVATE(self); - - priv->name = NULL; - priv->bus = NULL; - priv->bus_cancel = NULL; - priv->dbus_registration = 0; - - priv->bus_cancel = g_cancellable_new(); - g_bus_get(G_BUS_TYPE_SESSION, - priv->bus_cancel, - bus_get_cb, - self); - - return; -} - -static void -bus_get_cb (GObject * object, GAsyncResult * res, gpointer user_data) -{ - GError * error = NULL; - GDBusConnection * connection = g_bus_get_finish(res, &error); - - if (error != NULL) { - g_error("OMG! Unable to get a connection to DBus: %s", error->message); - g_error_free(error); - return; - } - - SessionDbusPrivate * priv = SESSION_DBUS_GET_PRIVATE(user_data); - - g_warn_if_fail(priv->bus == NULL); - priv->bus = connection; - - if (priv->bus_cancel != NULL) { - g_object_unref(priv->bus_cancel); - priv->bus_cancel = NULL; - } - - /* Now register our object on our new connection */ - priv->dbus_registration = g_dbus_connection_register_object(priv->bus, - INDICATOR_SESSION_SERVICE_DBUS_OBJECT, - interface_info, - &interface_table, - user_data, - NULL, - &error); - - if (error != NULL) { - g_error("Unable to register the object to DBus: %s", error->message); - g_error_free(error); - return; - } - - return; -} - -/* A method has been called from our dbus inteface. Figure out what it - is and dispatch it. */ -static void -bus_method_call (GDBusConnection * connection, const gchar * sender, - const gchar * path, const gchar * interface, - const gchar * method, GVariant * params, - GDBusMethodInvocation * invocation, gpointer user_data) -{ - SessionDbus * service = SESSION_DBUS (user_data); - - GVariant * retval = NULL; - - if (g_strcmp0(method, "GetUserRealName") == 0) { - retval = get_users_real_name (service); - } - else { - g_warning("Calling method '%s' on the indicator service and it's unknown", method); - } - g_dbus_method_invocation_return_value(invocation, retval); - return; -} - -static void -session_dbus_dispose (GObject *object) -{ - SessionDbusPrivate * priv = SESSION_DBUS_GET_PRIVATE(object); - - if (priv->dbus_registration != 0) { - g_dbus_connection_unregister_object(priv->bus, priv->dbus_registration); - /* Don't care if it fails, there's nothing we can do */ - priv->dbus_registration = 0; - } - - if (priv->bus != NULL) { - g_object_unref(priv->bus); - priv->bus = NULL; - } - - if (priv->bus_cancel != NULL) { - g_cancellable_cancel(priv->bus_cancel); - g_object_unref(priv->bus_cancel); - priv->bus_cancel = NULL; - } - - G_OBJECT_CLASS (session_dbus_parent_class)->dispose (object); - return; -} - -static void -session_dbus_finalize (GObject *object) -{ - SessionDbusPrivate * priv = SESSION_DBUS_GET_PRIVATE(object); - - g_clear_pointer (&priv->name, g_free); - - G_OBJECT_CLASS (session_dbus_parent_class)->finalize (object); - return; -} - -static GVariant * -get_users_real_name (SessionDbus * service) -{ - SessionDbusPrivate * priv = SESSION_DBUS_GET_PRIVATE(service); - return g_variant_new ("(s)", priv->name ? priv->name : ""); -} - -SessionDbus * -session_dbus_new (void) -{ - return SESSION_DBUS(g_object_new(SESSION_DBUS_TYPE, NULL)); -} - -void -session_dbus_set_name (SessionDbus * session, const gchar * name) -{ -} - -void -session_dbus_set_users_real_name (SessionDbus * session, const gchar * name) -{ - SessionDbusPrivate * priv = SESSION_DBUS_GET_PRIVATE(session); - GError * error = NULL; - - g_free (priv->name); - priv->name = g_strdup(name); - - if (priv->bus != NULL) { - g_dbus_connection_emit_signal (priv->bus, - NULL, - INDICATOR_SESSION_SERVICE_DBUS_OBJECT, - INDICATOR_SESSION_SERVICE_DBUS_IFACE, - "UserRealNameUpdated", - g_variant_new ("(s)", priv->name, NULL), - &error); - - if (error != NULL) { - g_warning("Unable to send UserRealNameUpdated signal: %s", error->message); - g_error_free(error); - return; - } - } - return; -} - -void session_dbus_restart_required (SessionDbus* session) -{ - SessionDbusPrivate * priv = SESSION_DBUS_GET_PRIVATE(session); - GError * error = NULL; - - if (priv->bus != NULL) { - g_debug("About to send RebootRequired signal"); - - g_dbus_connection_emit_signal (priv->bus, - NULL, - INDICATOR_SESSION_SERVICE_DBUS_OBJECT, - INDICATOR_SESSION_SERVICE_DBUS_IFACE, - "RestartRequired", - NULL, - &error); - - if (error != NULL) { - g_warning("Unable to send reboot-required signal: %s", error->message); - g_error_free(error); - } - } - -} diff --git a/src/session-dbus.h b/src/session-dbus.h deleted file mode 100644 index 7520f06..0000000 --- a/src/session-dbus.h +++ /dev/null @@ -1,56 +0,0 @@ -/* -The Dbus object on the bus for the indicator. - -Copyright 2010 Canonical Ltd. - -Authors: - Ted Gould <ted@canonical.com> - -This program is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License version 3, as published -by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranties of -MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef __SESSION_DBUS_H__ -#define __SESSION_DBUS_H__ - -#include <glib.h> -#include <glib-object.h> - -G_BEGIN_DECLS - -#define SESSION_DBUS_TYPE (session_dbus_get_type ()) -#define SESSION_DBUS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SESSION_DBUS_TYPE, SessionDbus)) -#define SESSION_DBUS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SESSION_DBUS_TYPE, SessionDbusClass)) -#define IS_SESSION_DBUS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SESSION_DBUS_TYPE)) -#define IS_SESSION_DBUS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SESSION_DBUS_TYPE)) -#define SESSION_DBUS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SESSION_DBUS_TYPE, SessionDbusClass)) - -typedef struct _SessionDbus SessionDbus; -typedef struct _SessionDbusClass SessionDbusClass; - -struct _SessionDbusClass { - GObjectClass parent_class; - void (*icon_updated) (SessionDbus * session, gchar * icon, gpointer user_data); -}; - -struct _SessionDbus { - GObject parent; -}; - -GType session_dbus_get_type (void); -SessionDbus * session_dbus_new (void); -void session_dbus_set_name (SessionDbus * session, const gchar * name); -void session_dbus_set_users_real_name (SessionDbus * session, const gchar * name); -void session_dbus_restart_required (SessionDbus* session); -G_END_DECLS - -#endif diff --git a/src/session-dbus.xml b/src/session-dbus.xml deleted file mode 100644 index 96e9837..0000000 --- a/src/session-dbus.xml +++ /dev/null @@ -1,20 +0,0 @@ -<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> -<node name="/com/canonical/indicator/session/service"> - <interface name="com.canonical.indicator.session.service"> - - <method name="GetUserRealName"> - <arg name="name" direction="out" type="s"/> - </method> - <method name="GetUserMenuVisibility"> - <arg name="update" direction="out" type="b"/> - </method> - <signal name="UserRealNameUpdated"> - <arg name="name" type="s"/> - </signal> - <signal name="UserMenuIsVisible"> - <arg name="update" type="b"/> - </signal> - <signal name="RestartRequired"> - </signal> - </interface> -</node> diff --git a/src/session-menu-mgr.c b/src/session-menu-mgr.c deleted file mode 100644 index c406009..0000000 --- a/src/session-menu-mgr.c +++ /dev/null @@ -1,1330 +0,0 @@ -/* -Copyright 2011 Canonical Ltd. - -Authors: - Charles Kerr <charles.kerr@canonical.com> - Conor Curran <conor.curran@canonical.com> - -This program is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License version 3, as published -by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranties of -MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "config.h" - -#include <sys/types.h> -#include <pwd.h> /* geteuid(), getpwuid() */ - -#include <glib.h> -#include <glib/gi18n.h> - -#include <libdbusmenu-glib/client.h> -#include <libdbusmenu-gtk/menuitem.h> - -#include "dbus-login1-manager.h" -#include "session-menu-mgr.h" -#include "shared-names.h" -#include "users-service-dbus.h" -#include "online-accounts-mgr.h" - -#define DEBUG_SHOW_ALL FALSE - -#define LOGIN1_MANAGER_ADDRESS "org.freedesktop.login1" -#define LOGIN1_MANAGER_PATH "/org/freedesktop/login1" - -#define CMD_HELP "yelp" -#define CMD_INFO "gnome-control-center info" -#define CMD_SYSTEM_SETTINGS "gnome-control-center" -#ifdef HAVE_GTKLOGOUTHELPER - #define HAVE_RESTART_CMD TRUE - #define CMD_RESTART LIBEXECDIR"/gtk-logout-helper --restart" - #define CMD_LOGOUT LIBEXECDIR"/gtk-logout-helper --logout" - #define CMD_SHUTDOWN LIBEXECDIR"/gtk-logout-helper --shutdown" -#else - #define HAVE_RESTART_CMD FALSE /* hmm, no gnome-session-quit --restart? */ - #define CMD_RESTART "" - #define CMD_LOGOUT "gnome-session-quit --logout" - #define CMD_SHUTDOWN "gnome-session-quit --power-off" -#endif - -/** - * Which switch menuitem to show -- based on lockdown settings, - * greeter mode, number of users in the system, and so on. - * See get_switcher_mode() - */ -typedef enum -{ - SWITCHER_MODE_SCREENSAVER, - SWITCHER_MODE_LOCK, - SWITCHER_MODE_SWITCH, - SWITCHER_MODE_SWITCH_OR_LOCK, - SWITCHER_MODE_NONE -} -SwitcherMode; - -/** - * Creates and manages the menumodel and associated actions for the - * session menu described at <https://wiki.ubuntu.com/SystemMenu>. - * - * This is a pretty straightforward class: it creates the menumodel - * and listens for events that can affect the model's properties. - * - * Simple event sources, such as GSettings and a logind DBus proxy, - * are handled here. More involved event sources are delegated to the - * UsersServiceDBus facade class. - */ -struct _SessionMenuMgr -{ - GObject parent_instance; - - DbusmenuMenuitem * top_mi; - DbusmenuMenuitem * screensaver_mi; - DbusmenuMenuitem * lock_mi; - DbusmenuMenuitem * lock_switch_mi; - DbusmenuMenuitem * guest_mi; - DbusmenuMenuitem * online_accounts_mi; - DbusmenuMenuitem * logout_mi; - DbusmenuMenuitem * suspend_mi; - DbusmenuMenuitem * hibernate_mi; - DbusmenuMenuitem * restart_mi; - DbusmenuMenuitem * shutdown_mi; - - GSList * user_menuitems; - gint user_menuitem_index; - - GSettings * lockdown_settings; - GSettings * indicator_settings; - GSettings * keybinding_settings; - - /* cached settings taken from the logind proxy */ - gboolean can_hibernate; - gboolean can_suspend; - - gboolean shell_mode; - gboolean greeter_mode; - - guint shell_name_watcher; - GCancellable * cancellable; - Login1Manager * login1_manager_proxy; - SessionDbus * session_dbus; - UsersServiceDbus * users_dbus_facade; - OnlineAccountsMgr * online_accounts_mgr; -}; - -static SwitcherMode get_switcher_mode (SessionMenuMgr *); - -static void init_login1_proxy (SessionMenuMgr *); -static void init_shell_watcher (SessionMenuMgr *); - -static void update_screensaver_shortcut (SessionMenuMgr *); -static void update_user_menuitems (SessionMenuMgr *); -static void update_session_menuitems (SessionMenuMgr *); -static void update_confirmation_labels (SessionMenuMgr *); - -static void action_func_lock (SessionMenuMgr *); -static void action_func_suspend (SessionMenuMgr *); -static void action_func_hibernate (SessionMenuMgr *); -static void action_func_shutdown (SessionMenuMgr *); -static void action_func_reboot (SessionMenuMgr *); -static void action_func_logout (SessionMenuMgr *); -static void action_func_switch_to_lockscreen (SessionMenuMgr *); -static void action_func_switch_to_greeter (SessionMenuMgr *); -static void action_func_switch_to_guest (SessionMenuMgr *); -static void action_func_switch_to_user (AccountsUser *); -static void action_func_spawn_async (const char * cmd); - -static gboolean is_this_guest_session (void); -static gboolean is_this_live_session (void); - -static void on_guest_logged_in_changed (UsersServiceDbus *, - SessionMenuMgr *); - -static void on_user_logged_in_changed (UsersServiceDbus *, - AccountsUser *, - SessionMenuMgr *); - -/** -*** GObject init / dispose -**/ - -G_DEFINE_TYPE (SessionMenuMgr, session_menu_mgr, G_TYPE_OBJECT); - -static void -session_menu_mgr_init (SessionMenuMgr *mgr) -{ - mgr->top_mi = dbusmenu_menuitem_new (); - - /* Lockdown settings */ - GSettings * s = g_settings_new ("org.gnome.desktop.lockdown"); - g_signal_connect_swapped (s, "changed::disable-log-out", - G_CALLBACK(update_session_menuitems), mgr); - g_signal_connect_swapped (s, "changed::disable-lock-screen", - G_CALLBACK(update_user_menuitems), mgr); - g_signal_connect_swapped (s, "changed::disable-user-switching", - G_CALLBACK(update_user_menuitems), mgr); - mgr->lockdown_settings = s; - - /* Indicator settings */ - s = g_settings_new ("com.canonical.indicator.session"); - g_signal_connect_swapped (s, "changed::suppress-logout-restart-shutdown", - G_CALLBACK(update_confirmation_labels), mgr); - g_signal_connect_swapped (s, "changed::suppress-logout-restart-shutdown", - G_CALLBACK(update_session_menuitems), mgr); - g_signal_connect_swapped (s, "changed::suppress-logout-menuitem", - G_CALLBACK(update_session_menuitems), mgr); - g_signal_connect_swapped (s, "changed::suppress-restart-menuitem", - G_CALLBACK(update_session_menuitems), mgr); - g_signal_connect_swapped (s, "changed::suppress-shutdown-menuitem", - G_CALLBACK(update_session_menuitems), mgr); - mgr->indicator_settings = s; - - /* Keybinding settings */ - s = g_settings_new ("org.gnome.settings-daemon.plugins.media-keys"); - g_signal_connect_swapped (s, "changed::screensaver", - G_CALLBACK(update_screensaver_shortcut), mgr); - mgr->keybinding_settings = s; - - /* listen for user events */ - mgr->users_dbus_facade = g_object_new (USERS_SERVICE_DBUS_TYPE, NULL); - g_signal_connect_swapped (mgr->users_dbus_facade, "user-list-changed", - G_CALLBACK (update_user_menuitems), mgr); - g_signal_connect (mgr->users_dbus_facade, "user-logged-in-changed", - G_CALLBACK(on_user_logged_in_changed), mgr); - g_signal_connect (mgr->users_dbus_facade, "guest-logged-in-changed", - G_CALLBACK(on_guest_logged_in_changed), mgr); - - init_login1_proxy (mgr); - init_shell_watcher (mgr); - - /* Online accounts menu item */ - mgr->online_accounts_mgr = online_accounts_mgr_new (); -} - -static void -session_menu_mgr_dispose (GObject *object) -{ - SessionMenuMgr * mgr = SESSION_MENU_MGR (object); - - if (mgr->cancellable != NULL) - { - g_cancellable_cancel (mgr->cancellable); - g_clear_object (&mgr->cancellable); - } - - g_clear_object (&mgr->indicator_settings); - g_clear_object (&mgr->lockdown_settings); - g_clear_object (&mgr->keybinding_settings); - g_clear_object (&mgr->login1_manager_proxy); - g_clear_object (&mgr->users_dbus_facade); - g_clear_object (&mgr->top_mi); - g_clear_object (&mgr->session_dbus); - g_clear_object (&mgr->online_accounts_mgr); - - g_slist_free (mgr->user_menuitems); - mgr->user_menuitems = NULL; - - if (mgr->shell_name_watcher) - { - g_bus_unwatch_name (mgr->shell_name_watcher); - } - - G_OBJECT_CLASS (session_menu_mgr_parent_class)->dispose (object); -} - -static void -session_menu_mgr_class_init (SessionMenuMgrClass * klass) -{ - GObjectClass* object_class = G_OBJECT_CLASS (klass); - object_class->dispose = session_menu_mgr_dispose; -} - -static gboolean -can_perform_operation (gchar * permission) -{ - return g_strcmp0 ("yes", permission) == 0; -} - -static void -init_login1_proxy (SessionMenuMgr * mgr) -{ - /* default values */ - mgr->can_suspend = TRUE; - mgr->can_hibernate = TRUE; - - gchar * can_suspend; - gchar * can_hibernate; - - mgr->cancellable = g_cancellable_new (); - - GError * error = NULL; - mgr->login1_manager_proxy = login1_manager_proxy_new_for_bus_sync ( - G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_NONE, - LOGIN1_MANAGER_ADDRESS, - LOGIN1_MANAGER_PATH, - NULL, - &error); - if (error != NULL) - { - g_warning ("Error creating logind proxy: %s", error->message); - g_clear_error (&error); - } - else - { - login1_manager_call_can_suspend_sync (mgr->login1_manager_proxy, - &can_suspend, - NULL, - &error); - if (error != NULL) - { - g_warning ("%s: %s", G_STRFUNC, error->message); - g_clear_error (&error); - } - else - { - mgr->can_suspend = can_perform_operation (can_suspend); - } - - login1_manager_call_can_hibernate_sync (mgr->login1_manager_proxy, - &can_hibernate, - NULL, - &error); - if (error != NULL) - { - g_warning ("%s: %s", G_STRFUNC, error->message); - g_clear_error (&error); - } - else - { - mgr->can_hibernate = can_perform_operation (can_hibernate); - } - - } -} - -static void -on_shell_name_appeared (GDBusConnection *connection, const gchar *name, - const gchar *name_owner, gpointer user_data) -{ - SessionMenuMgr * mgr = SESSION_MENU_MGR(user_data); - - g_debug("Shell appeared"); - mgr->shell_mode = TRUE; - update_session_menuitems (mgr); -} - -static void -on_shell_name_vanished (GDBusConnection *connection, const gchar *name, gpointer user_data) -{ - SessionMenuMgr * mgr = SESSION_MENU_MGR(user_data); - - g_debug("Shell vanished"); - mgr->shell_mode = FALSE; - update_session_menuitems (mgr); -} - -static void -init_shell_watcher (SessionMenuMgr * mgr) -{ - mgr->shell_name_watcher = g_bus_watch_name (G_BUS_TYPE_SESSION, "org.gnome.Shell", - G_BUS_NAME_WATCHER_FLAGS_NONE, - on_shell_name_appeared, - on_shell_name_vanished, - mgr, NULL); -} - -/*** -**** Menuitem Helpers -***/ - -static inline void -mi_set_label (DbusmenuMenuitem * mi, const char * str) -{ - dbusmenu_menuitem_property_set (mi, DBUSMENU_MENUITEM_PROP_LABEL, str); -} - -static inline void -mi_set_type (DbusmenuMenuitem * mi, const char * str) -{ - dbusmenu_menuitem_property_set (mi, DBUSMENU_MENUITEM_PROP_TYPE, str); -} - -static inline void -mi_set_visible (DbusmenuMenuitem * mi, gboolean b) -{ - dbusmenu_menuitem_property_set_bool (mi, DBUSMENU_MENUITEM_PROP_VISIBLE, - b || DEBUG_SHOW_ALL); -} - -static inline void -mi_set_logged_in (DbusmenuMenuitem * mi, gboolean b) -{ - dbusmenu_menuitem_property_set_bool (mi, USER_ITEM_PROP_LOGGED_IN, b); -} - -static DbusmenuMenuitem* -mi_new_separator (void) -{ - DbusmenuMenuitem * mi = dbusmenu_menuitem_new (); - mi_set_type (mi, DBUSMENU_CLIENT_TYPES_SEPARATOR); - return mi; -} - -static DbusmenuMenuitem* -mi_new (const char * label) -{ - DbusmenuMenuitem * mi = dbusmenu_menuitem_new (); - mi_set_label (mi, label); - return mi; -} - -static void -check_online_accounts_status (SessionMenuMgr * mgr, DbusmenuMenuitem * mi) -{ - const gchar *disposition; - gboolean on_alert; - - disposition = - dbusmenu_menuitem_property_get (mi, DBUSMENU_MENUITEM_PROP_DISPOSITION); - on_alert = g_strcmp0 (disposition, DBUSMENU_MENUITEM_DISPOSITION_ALERT) == 0; - - mi_set_visible (mi, on_alert); -} - -static void -on_online_accounts_changed (SessionMenuMgr * mgr, const gchar * property, - GVariant *value, DbusmenuMenuitem *mi) -{ - if (g_strcmp0 (property, DBUSMENU_MENUITEM_PROP_DISPOSITION) == 0) - { - check_online_accounts_status(mgr, mi); - } -} - -/*** -**** Admin Menuitems -**** <https://wiki.ubuntu.com/SystemMenu#Admin_items> -***/ - -static void -build_admin_menuitems (SessionMenuMgr * mgr) -{ - if (!mgr->greeter_mode) - { - DbusmenuMenuitem * mi; - const gboolean show_settings = !mgr->greeter_mode; - - mi = mi_new (_("About This Computer")); - dbusmenu_menuitem_child_append (mgr->top_mi, mi); - g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, - G_CALLBACK(action_func_spawn_async), CMD_INFO); - - mi = mi_new (_("Ubuntu Help")); - dbusmenu_menuitem_child_append (mgr->top_mi, mi); - g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, - G_CALLBACK(action_func_spawn_async), CMD_HELP); - - mi = mi_new_separator (); - mi_set_visible (mi, show_settings); - dbusmenu_menuitem_child_append (mgr->top_mi, mi); - - mi = mi_new (_("System Settings\342\200\246")); - mi_set_visible (mi, show_settings); - dbusmenu_menuitem_child_append (mgr->top_mi, mi); - g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, - G_CALLBACK(action_func_spawn_async), - CMD_SYSTEM_SETTINGS); - - mi = mgr->online_accounts_mi = - online_accounts_mgr_get_menu_item (mgr->online_accounts_mgr); - dbusmenu_menuitem_child_append (mgr->top_mi, mi); - g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, - G_CALLBACK(on_online_accounts_changed), - mgr); - check_online_accounts_status (mgr, mi); - - mi = mi_new_separator (); - dbusmenu_menuitem_child_append (mgr->top_mi, mi); - } -} - -/*** -**** Session Menuitems -**** <https://wiki.ubuntu.com/SystemMenu#Session_items> -***/ - -static void -update_session_menuitems (SessionMenuMgr * mgr) -{ - gboolean v; - GSettings * s = mgr->indicator_settings; - - v = !mgr->greeter_mode - && !is_this_live_session() - && !g_settings_get_boolean (mgr->lockdown_settings, "disable-log-out") - && !g_settings_get_boolean (s, "suppress-logout-menuitem"); - mi_set_visible (mgr->logout_mi, v); - - mi_set_visible (mgr->suspend_mi, mgr->can_suspend); - - mi_set_visible (mgr->hibernate_mi, mgr->can_hibernate); - - v = (!mgr->shell_mode || g_settings_get_boolean (s, "suppress-logout-restart-shutdown")) - && (HAVE_RESTART_CMD || mgr->shell_mode) - && !g_settings_get_boolean (s, "suppress-restart-menuitem"); - mi_set_visible (mgr->restart_mi, v); - - v = !g_settings_get_boolean (s, "suppress-shutdown-menuitem"); - mi_set_visible (mgr->shutdown_mi, v); -} - -/* Update the ellipses when the confirmation setting changes. - * - * <http://developer.gnome.org/hig-book/3.0/menus-design.html.en>: - * "Label the menu item with a trailing ellipsis ("...") only if the - * command requires further input from the user before it can be performed." - */ -static void -update_confirmation_labels (SessionMenuMgr * mgr) -{ - const gboolean confirm_needed = !g_settings_get_boolean ( - mgr->indicator_settings, - "suppress-logout-restart-shutdown"); - - mi_set_label (mgr->logout_mi, confirm_needed ? _("Log Out\342\200\246") - : _("Log Out")); - - mi_set_label (mgr->shutdown_mi, confirm_needed ? _("Shut Down\342\200\246") - : _("Shut Down")); - - mi_set_label (mgr->restart_mi, confirm_needed ? _("Restart\342\200\246") - : _("Restart")); -} - -static void -build_session_menuitems (SessionMenuMgr* mgr) -{ - DbusmenuMenuitem * mi; - - mi = mgr->logout_mi = mi_new (_("Log Out\342\200\246")); - dbusmenu_menuitem_child_append (mgr->top_mi, mi); - g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, - G_CALLBACK(action_func_logout), mgr); - - mi = mgr->suspend_mi = mi_new (_("Suspend")); - dbusmenu_menuitem_child_append (mgr->top_mi, mi); - g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, - G_CALLBACK(action_func_suspend), mgr); - - mi = mgr->hibernate_mi = mi_new (_("Hibernate")); - dbusmenu_menuitem_child_append (mgr->top_mi, mi); - g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, - G_CALLBACK(action_func_hibernate), mgr); - - mi = mgr->restart_mi = mi_new (_("Restart\342\200\246")); - dbusmenu_menuitem_child_append (mgr->top_mi, mi); - g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, - G_CALLBACK(action_func_reboot), mgr); - - mi = mgr->shutdown_mi = mi_new (_("Shut Down\342\200\246")); - dbusmenu_menuitem_child_append (mgr->top_mi, mi); - g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, - G_CALLBACK(action_func_shutdown), mgr); - - update_confirmation_labels (mgr); - update_session_menuitems (mgr); -} - -/**** -***** User Menuitems -***** https://wiki.ubuntu.com/SystemMenu#Account-switching_items -****/ - -/* Local extensions to AccountsUser */ - -static GQuark -get_menuitem_quark (void) -{ - static GQuark q = 0; - - if (G_UNLIKELY(!q)) - { - q = g_quark_from_static_string ("menuitem"); - } - - return q; -} - -static DbusmenuMenuitem* -user_get_menuitem (AccountsUser * user) -{ - return g_object_get_qdata (G_OBJECT(user), get_menuitem_quark()); -} - -static void -user_clear_menuitem (AccountsUser * user) -{ - g_object_steal_qdata (G_OBJECT(user), get_menuitem_quark()); -} - -static void -user_set_menuitem (AccountsUser * user, DbusmenuMenuitem * mi) -{ - g_object_set_qdata (G_OBJECT(user), get_menuitem_quark(), mi); - - g_object_weak_ref (G_OBJECT(mi), (GWeakNotify)user_clear_menuitem, user); -} - -/***/ - -static GQuark -get_mgr_quark (void) -{ - static GQuark q = 0; - - if (G_UNLIKELY(!q)) - { - q = g_quark_from_static_string ("session-menu-mgr"); - } - - return q; -} - -static SessionMenuMgr* -user_get_mgr (AccountsUser * user) -{ - return g_object_get_qdata (G_OBJECT(user), get_mgr_quark()); -} - -static void -user_set_mgr (AccountsUser * user, SessionMenuMgr * mgr) -{ - g_object_set_qdata (G_OBJECT(user), get_mgr_quark(), mgr); -} - -/***/ - -static GQuark -get_collision_quark (void) -{ - static GQuark q = 0; - - if (G_UNLIKELY(!q)) - { - q = g_quark_from_static_string ("name-collision"); - } - - return q; -} - -static gboolean -user_has_name_collision (AccountsUser * u) -{ - return g_object_get_qdata (G_OBJECT(u), get_collision_quark()) != NULL; -} - -static void -user_set_name_collision (AccountsUser * u, gboolean b) -{ - g_object_set_qdata (G_OBJECT(u), get_collision_quark(), GINT_TO_POINTER(b)); -} - -/*** -**** -***/ - -static void -on_guest_logged_in_changed (UsersServiceDbus * usd, - SessionMenuMgr * mgr) -{ - if (mgr->guest_mi != NULL) - { - mi_set_logged_in (mgr->guest_mi, - users_service_dbus_is_guest_logged_in (usd)); - } -} - -/* When a user's login state changes, - update the corresponding menuitem's LOGGED_IN property */ -static void -on_user_logged_in_changed (UsersServiceDbus * usd, - AccountsUser * user, - SessionMenuMgr * mgr) -{ - DbusmenuMenuitem * mi = user_get_menuitem (user); - - if (mi != NULL) - { - mi_set_logged_in (mi, users_service_dbus_is_user_logged_in (usd, user)); - } -} - -static void -update_screensaver_shortcut (SessionMenuMgr * mgr) -{ - gchar * s = g_settings_get_string (mgr->keybinding_settings, "screensaver"); - g_debug ("%s Screensaver shortcut changed to: '%s'", G_STRLOC, s); - - if (mgr->lock_mi != NULL) - { - dbusmenu_menuitem_property_set_shortcut_string (mgr->lock_mi, s); - } - - if (mgr->lock_switch_mi != NULL) - { - dbusmenu_menuitem_property_set_shortcut_string (mgr->lock_switch_mi, s); - } - - if (mgr->screensaver_mi != NULL) - { - dbusmenu_menuitem_property_set_shortcut_string (mgr->screensaver_mi, s); - } - - g_free (s); -} - -static void -update_user_menuitem_icon (DbusmenuMenuitem * mi, AccountsUser * user) -{ - const gchar * str = accounts_user_get_icon_file (user); - - if (!str || !*str) - { - str = USER_ITEM_ICON_DEFAULT; - } - - dbusmenu_menuitem_property_set (mi, USER_ITEM_PROP_ICON, str); -} - -static void -update_user_menuitem_name (DbusmenuMenuitem * mi, AccountsUser * user) -{ - GString * gstr = g_string_new (accounts_user_get_real_name (user)); - - if (user_has_name_collision (user)) - g_string_append_printf (gstr, " (%s)", accounts_user_get_user_name(user)); - - if (!gstr->len) - g_string_assign (gstr, accounts_user_get_user_name(user)); - - dbusmenu_menuitem_property_set (mi, USER_ITEM_PROP_NAME, gstr->str); - g_string_free (gstr, TRUE); -} - -static void -on_user_property_changed (AccountsUser * user, - GParamSpec * pspec, - DbusmenuMenuitem * mi) -{ - static const char * interned_icon_file = NULL; - static const char * interned_real_name = NULL; - static const char * interned_user_name = NULL; - - if (G_UNLIKELY (interned_icon_file == NULL)) - { - interned_icon_file = g_intern_static_string ("icon-file"); - interned_user_name = g_intern_static_string ("user-name"); - interned_real_name = g_intern_static_string ("real-name"); - } - - if (pspec->name == interned_icon_file) - { - update_user_menuitem_icon (mi, user); - } - else if ((pspec->name == interned_real_name) - || (pspec->name == interned_user_name)) - { - /* name changing can affect other menuitems too by invalidating - the sort order or name collision flags... so let's rebuild */ - update_user_menuitems (user_get_mgr (user)); - } -} - -typedef struct -{ - gpointer instance; - gulong handler_id; -} -SignalHandlerData; - -/* when a user menuitem is destroyed, - it should stop listening for its UserAccount's property changes */ -static void -on_user_menuitem_destroyed (SignalHandlerData * data) -{ - g_signal_handler_disconnect (data->instance, data->handler_id); - g_free (data); -} - -static DbusmenuMenuitem* -user_menuitem_new (AccountsUser * user, SessionMenuMgr * mgr) -{ - DbusmenuMenuitem * mi = dbusmenu_menuitem_new (); - mi_set_type (mi, USER_ITEM_TYPE); - - /* set the name & icon and listen for property changes */ - update_user_menuitem_name (mi, user); - update_user_menuitem_icon (mi, user); - SignalHandlerData * hd = g_new0 (SignalHandlerData, 1); - hd->instance = user; - hd->handler_id = g_signal_connect (user, "notify", - G_CALLBACK(on_user_property_changed), mi); - g_object_weak_ref (G_OBJECT(mi), (GWeakNotify)on_user_menuitem_destroyed, hd); - - /* set the logged-in property */ - mi_set_logged_in (mi, - users_service_dbus_is_user_logged_in (mgr->users_dbus_facade, user)); - - /* set the is-current-user property */ - const gboolean is_current_user = - !g_strcmp0 (g_get_user_name(), accounts_user_get_user_name(user)); - - dbusmenu_menuitem_property_set_bool (mi, - USER_ITEM_PROP_IS_CURRENT_USER, - is_current_user); - - /* set the switch-to-user action */ - g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, - G_CALLBACK (action_func_switch_to_user), user); - - /* give this AccountsUser a hook back to this menuitem */ - user_set_menuitem (user, mi); - user_set_mgr (user, mgr); - - return mi; -} - -/* for sorting AccountsUsers from most to least frequently used */ -static gint -compare_users_by_login_frequency (gconstpointer a, gconstpointer b) -{ - const guint64 a_freq = accounts_user_get_login_frequency (ACCOUNTS_USER(a)); - const guint64 b_freq = accounts_user_get_login_frequency (ACCOUNTS_USER(b)); - if (a_freq > b_freq) return -1; - if (a_freq < b_freq) return 1; - return 0; -} - -/* for sorting AccountsUsers alphabetically */ -static gint -compare_users_by_username (gconstpointer ga, gconstpointer gb) -{ - AccountsUser * a = ACCOUNTS_USER(ga); - AccountsUser * b = ACCOUNTS_USER(gb); - - const int ret = g_strcmp0 (accounts_user_get_real_name (a), - accounts_user_get_real_name (b)); - - if (!ret) /* names are the same, so both have a name collision */ - { - user_set_name_collision (a, TRUE); - user_set_name_collision (b, TRUE); - } - - return ret; -} - -static gboolean -is_user_switching_allowed (SessionMenuMgr * mgr) -{ - /* maybe it's locked down */ - if (g_settings_get_boolean (mgr->lockdown_settings, "disable-user-switching")) - { - return FALSE; - } - - /* maybe the seat doesn't support activation */ - - return TRUE; -} - -static void -build_user_menuitems (SessionMenuMgr * mgr) -{ - g_return_if_fail (!mgr->greeter_mode); - - DbusmenuMenuitem * mi; - GSList * items = NULL; - gint pos = mgr->user_menuitem_index; - const char * current_real_name = NULL; - - /** - *** Start Screen Saver - *** Switch Account... - *** Lock - *** Lock / Switch Account... - **/ - - const SwitcherMode mode = get_switcher_mode (mgr); - - mi = mgr->screensaver_mi = mi_new (_("Start Screen Saver")); - mi_set_visible (mi, mode == SWITCHER_MODE_SCREENSAVER); - dbusmenu_menuitem_child_add_position (mgr->top_mi, mi, pos++); - items = g_slist_prepend (items, mi); - g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, - G_CALLBACK (action_func_lock), mgr); - - mi = mi_new (_("Switch User Account\342\200\246")); - mi_set_visible (mi, mode == SWITCHER_MODE_SWITCH); - dbusmenu_menuitem_child_add_position (mgr->top_mi, mi, pos++); - items = g_slist_prepend (items, mi); - g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, - G_CALLBACK (action_func_switch_to_greeter), mgr); - - mi = mgr->lock_mi = mi_new (_("Lock")); - mi_set_visible (mi, mode == SWITCHER_MODE_LOCK); - dbusmenu_menuitem_child_add_position (mgr->top_mi, mi, pos++); - items = g_slist_prepend (items, mi); - g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, - G_CALLBACK (action_func_switch_to_lockscreen), mgr); - - mi = mgr->lock_switch_mi = mi_new (_("Lock/Switch Account\342\200\246")); - mi_set_visible (mi, mode == SWITCHER_MODE_SWITCH_OR_LOCK); - dbusmenu_menuitem_child_add_position (mgr->top_mi, mi, pos++); - items = g_slist_prepend (items, mi); - g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, - G_CALLBACK (action_func_switch_to_lockscreen), mgr); - - const gboolean is_guest = is_this_guest_session (); - const gboolean guest_allowed = - users_service_dbus_guest_session_enabled (mgr->users_dbus_facade); - mi = mgr->guest_mi = dbusmenu_menuitem_new (); - mi_set_type (mi, USER_ITEM_TYPE); - mi_set_visible (mi, !is_guest && guest_allowed); - dbusmenu_menuitem_property_set (mi, USER_ITEM_PROP_NAME, _("Guest Session")); - dbusmenu_menuitem_child_add_position (mgr->top_mi, mi, pos++); - on_guest_logged_in_changed (mgr->users_dbus_facade, mgr); - items = g_slist_prepend (items, mi); - g_signal_connect_swapped (mi, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, - G_CALLBACK (action_func_switch_to_guest), mgr); - - if (guest_allowed && is_guest) - { - current_real_name = _("Guest"); - } - - /*** - **** Users - ***/ - - /* if we can switch to another user account, show them here */ - const char * const username = g_get_user_name(); - GList * users = users_service_dbus_get_user_list (mgr->users_dbus_facade); - - /* since we're building (or rebuilding) from scratch, - clear the name collision flags */ - GList * u; - for (u=users; u!=NULL; u=u->next) - { - AccountsUser * user = ACCOUNTS_USER(u->data); - - user_set_name_collision (user, FALSE); - - if (!g_strcmp0 (username, accounts_user_get_user_name(user))) - { - current_real_name = accounts_user_get_real_name (user); - } - } - - if (is_user_switching_allowed (mgr)) - { - /* pick the most frequently used accounts */ - const int MAX_USERS = 12; /* this limit comes from the spec */ - if (g_list_length(users) > MAX_USERS) - { - users = g_list_sort (users, compare_users_by_login_frequency); - GList * last = g_list_nth (users, MAX_USERS-1); - GList * remainder = last->next; - last->next = NULL; - remainder->prev = NULL; - g_list_free (remainder); - } - - /* Sort the users by name for display */ - users = g_list_sort (users, compare_users_by_username); - - /* Create menuitems for them */ - int i; - for (i=0, u=users; i<MAX_USERS && u!=NULL; u=u->next, i++) - { - AccountsUser * user = u->data; - DbusmenuMenuitem * mi = user_menuitem_new (user, mgr); - dbusmenu_menuitem_child_add_position (mgr->top_mi, mi, pos++); - items = g_slist_prepend (items, mi); - } - } - - g_list_free (users); - - /* separator */ - if (mode != SWITCHER_MODE_SCREENSAVER && !is_guest && guest_allowed) - { - mi = mi_new_separator (); - dbusmenu_menuitem_child_add_position (mgr->top_mi, mi, pos++); - items = g_slist_prepend (items, mi); - } - - if (current_real_name != NULL) - { - session_dbus_set_users_real_name (mgr->session_dbus, - current_real_name); - } - - update_screensaver_shortcut (mgr); - mgr->user_menuitems = items; -} - -static void -update_user_menuitems (SessionMenuMgr * mgr) -{ - /* remove any previous user menuitems */ - GSList * l; - for (l=mgr->user_menuitems; l!=NULL; l=l->next) - { - dbusmenu_menuitem_child_delete (mgr->top_mi, l->data); - g_object_unref (l->data); - } - g_slist_free (mgr->user_menuitems); - mgr->user_menuitems = NULL; - - /* add fresh user menuitems */ - if (!mgr->greeter_mode) - { - build_user_menuitems (mgr); - } -} - -/*** -**** Actions! -***/ - -static void -action_func_spawn_async (const char * cmd) -{ - GError * error = NULL; - - g_debug ("%s calling \"%s\"", G_STRFUNC, cmd); - g_spawn_command_line_async (cmd, &error); - - if (error != NULL) - { - g_warning ("Unable to execute \"%s\": %s", cmd, error->message); - g_clear_error (&error); - } -} - -/* Calling "Lock" locks the screen & goes to black. - Calling "SimulateUserActivity" afterwards shows the Lock Screen. */ -static void -lock_helper (SessionMenuMgr * mgr, gboolean show_lock_screen) -{ - if (!g_settings_get_boolean (mgr->lockdown_settings, "disable-lock-screen")) - { - GError * error = NULL; - GDBusProxy * proxy = g_dbus_proxy_new_for_bus_sync ( - G_BUS_TYPE_SESSION, - G_DBUS_PROXY_FLAGS_NONE, - NULL, - "org.gnome.ScreenSaver", - "/org/gnome/ScreenSaver", - "org.gnome.ScreenSaver", - NULL, - &error); - - if (error == NULL) - { - g_dbus_proxy_call_sync (proxy, "Lock", - NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, - &error); - } - - if ((error == NULL) && show_lock_screen) - { - g_dbus_proxy_call_sync (proxy, "SimulateUserActivity", - NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, - &error); - } - - if (error != NULL) - { - g_warning ("Error locking screen: %s", error->message); - } - - g_clear_object (&proxy); - g_clear_error (&error); - } -} - -static void -action_func_lock (SessionMenuMgr * mgr) -{ - lock_helper (mgr, FALSE); -} - -static void -action_func_switch_to_lockscreen (SessionMenuMgr * mgr) -{ - lock_helper (mgr, TRUE); -} - -static void -action_func_switch_to_greeter (SessionMenuMgr * mgr) -{ - action_func_lock (mgr); - users_service_dbus_show_greeter (mgr->users_dbus_facade); -} - -static void -action_func_switch_to_user (AccountsUser * user) -{ - SessionMenuMgr * mgr = user_get_mgr (user); - - g_return_if_fail (mgr != NULL); - - if (g_strcmp0 (g_get_user_name(), accounts_user_get_user_name(user)) != 0) - { - action_func_lock (mgr); - users_service_dbus_activate_user_session (mgr->users_dbus_facade, user); - } -} - -static void -action_func_switch_to_guest (SessionMenuMgr * mgr) -{ - action_func_lock (mgr); - users_service_dbus_activate_guest_session (mgr->users_dbus_facade); -} - -static void -action_func_suspend (SessionMenuMgr * mgr) -{ - GError * error = NULL; - - login1_manager_call_suspend_sync (mgr->login1_manager_proxy, - TRUE, - mgr->cancellable, - &error); - - if (error != NULL) - { - g_warning ("%s: %s", G_STRFUNC, error->message); - g_clear_error (&error); - } -} - -static void -action_func_hibernate (SessionMenuMgr * mgr) -{ - GError * error = NULL; - - login1_manager_call_hibernate_sync (mgr->login1_manager_proxy, - TRUE, - mgr->cancellable, - &error); - - if (error != NULL) - { - g_warning ("%s: %s", G_STRFUNC, error->message); - g_clear_error (&error); - } -} - -static gboolean -call_session_manager_method (const gchar * method_name, GVariant * parameters) -{ - gboolean result = TRUE; - GError * error = NULL; - GDBusProxy * proxy = g_dbus_proxy_new_for_bus_sync ( - G_BUS_TYPE_SESSION, - G_DBUS_PROXY_FLAGS_NONE, - NULL, - "org.gnome.SessionManager", - "/org/gnome/SessionManager", - "org.gnome.SessionManager", - NULL, - &error); - - if (error == NULL) - { - g_dbus_proxy_call_sync (proxy, method_name, parameters, - G_DBUS_CALL_FLAGS_NONE, -1, NULL, - &error); - } - else if (parameters != NULL) - { - g_variant_unref (parameters); - } - - if (error != NULL) - { - result = FALSE; - g_warning ("Error shutting down: %s", error->message); - g_clear_error (&error); - } - - g_clear_object (&proxy); - - return result; -} - -static void -action_func_shutdown (SessionMenuMgr * mgr) -{ - gboolean result = FALSE; - - if (mgr->shell_mode) - { - if (g_settings_get_boolean (mgr->indicator_settings, - "suppress-logout-restart-shutdown")) - { - result = call_session_manager_method ("Shutdown", NULL); - } - else - { - /* We call 'Reboot' method instead of 'Shutdown' because - * Unity SessionManager handles the Shutdown request as a more - * general request as the default SessionManager dialog would do */ - result = call_session_manager_method ("Reboot", NULL); - } - } - - if (!result) - { - action_func_spawn_async (CMD_SHUTDOWN); - } -} - -static void -action_func_logout (SessionMenuMgr * mgr) -{ - gboolean result = FALSE; - - if (mgr->shell_mode) - { - guint interactive_mode = 0; - result = call_session_manager_method ("Logout", - g_variant_new ("(u)", interactive_mode)); - } - - if (!result) - { - action_func_spawn_async (CMD_LOGOUT); - } -} - -static void -action_func_reboot (SessionMenuMgr * mgr) -{ - gboolean result = FALSE; - - if (mgr->shell_mode) - { - result = call_session_manager_method ("Reboot", NULL); - } - - if (!result) - { - action_func_spawn_async (CMD_RESTART); - } -} - -/*** -**** -***/ - -static gboolean -is_this_guest_session (void) -{ - /* FIXME: this test has been here awhile and seems to work, - but seems brittle to me */ - return geteuid() < 500; -} - -static gboolean -is_this_live_session (void) -{ - const struct passwd * const pw = getpwuid (geteuid()); - return (pw->pw_uid==999) && !g_strcmp0("ubuntu",pw->pw_name); -} - -static SwitcherMode -get_switcher_mode (SessionMenuMgr * mgr) -{ - SwitcherMode mode; - - const gboolean can_lock = !g_settings_get_boolean (mgr->lockdown_settings, - "disable-lock-screen"); - const gboolean can_switch = is_user_switching_allowed (mgr); - - if (!can_lock && !can_switch) /* hmm, quite an extreme lockdown */ - { - mode = SWITCHER_MODE_NONE; - } - else if (is_this_live_session()) /* live sessions can't lock or switch */ - { - mode = SWITCHER_MODE_SCREENSAVER; - } - else if (!can_switch) /* switching's locked down */ - { - mode = SWITCHER_MODE_LOCK; - } - else if (is_this_guest_session ()) /* guest sessions can't lock */ - { - mode = SWITCHER_MODE_SWITCH; - } - else /* both locking & switching are allowed */ - { - GList * l = users_service_dbus_get_user_list (mgr->users_dbus_facade); - const size_t user_count = g_list_length (l); - g_list_free (l); - - /* only show switch mode if we have users to switch to */ - mode = user_count > (is_this_guest_session() ? 0 : 1) - ? SWITCHER_MODE_SWITCH_OR_LOCK - : SWITCHER_MODE_LOCK; - } - - return mode; -} - - -/*** -**** -***/ - -SessionMenuMgr* -session_menu_mgr_new (SessionDbus * session_dbus, - gboolean greeter_mode) -{ - SessionMenuMgr* mgr = g_object_new (SESSION_TYPE_MENU_MGR, NULL); - mgr->greeter_mode = greeter_mode; - mgr->session_dbus = g_object_ref (session_dbus); - build_admin_menuitems (mgr); - const guint n = g_list_length (dbusmenu_menuitem_get_children (mgr->top_mi)); - mgr->user_menuitem_index = n; - update_user_menuitems (mgr); - build_session_menuitems (mgr); - - return mgr; -} - -/** - * session_menu_mgr_get_menu: - * - * Returns: (transfer none): the manager's menu. - */ -DbusmenuMenuitem * -session_menu_mgr_get_menu (SessionMenuMgr * mgr) -{ - g_return_val_if_fail (IS_SESSION_MENU_MGR(mgr), NULL); - - return mgr->top_mi; -} diff --git a/src/session-menu-mgr.h b/src/session-menu-mgr.h deleted file mode 100644 index 5a173e1..0000000 --- a/src/session-menu-mgr.h +++ /dev/null @@ -1,55 +0,0 @@ -/* -Copyright 2011 Canonical Ltd. - -Authors: - Conor Curran <conor.curran@canonical.com> - -This program is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License version 3, as published -by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranties of -MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - - -#ifndef _SESSION_MENU_MGR_H_ -#define _SESSION_MENU_MGR_H_ - -#include <glib-object.h> - -#include "session-dbus.h" - -G_BEGIN_DECLS - -#define SESSION_TYPE_MENU_MGR (session_menu_mgr_get_type ()) -#define SESSION_MENU_MGR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SESSION_TYPE_MENU_MGR, SessionMenuMgr)) -#define SESSION_MENU_MGR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SESSION_TYPE_MENU_MGR, SessionMenuMgrClass)) -#define IS_SESSION_MENU_MGR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SESSION_TYPE_MENU_MGR)) -#define IS_SESSION_MENU_MGR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SESSION_TYPE_MENU_MGR)) -#define SESSION_MENU_MGR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SESSION_TYPE_MENU_MGR, SessionMenuMgrClass)) - -typedef struct _SessionMenuMgrClass SessionMenuMgrClass; -typedef struct _SessionMenuMgr SessionMenuMgr; - -struct _SessionMenuMgrClass -{ - GObjectClass parent_class; -}; - -GType session_menu_mgr_get_type (void) G_GNUC_CONST; - -SessionMenuMgr* session_menu_mgr_new (SessionDbus * session_dbus, - gboolean greeter_mode); - -DbusmenuMenuitem* session_menu_mgr_get_menu (SessionMenuMgr * mgr); - - -G_END_DECLS - -#endif /* _SESSION_MENU_MGR_H_ */ diff --git a/src/session-service.c b/src/session-service.c deleted file mode 100644 index 13438d3..0000000 --- a/src/session-service.c +++ /dev/null @@ -1,96 +0,0 @@ -/* -A small wrapper utility to load indicators and put them as menu items -into the gnome-panel using it's applet interface. - -Copyright 2009 Canonical Ltd. - -Authors: - Ted Gould <ted@canonical.com> - Christoph Korn <c_korn@gmx.de> - Cody Russell <crussell@canonical.com> - Conor Curran <conor.curran@canonical.com> - Charles Kerr <charles.kerr@canonical.com> - -This program is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License version 3, as published -by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranties of -MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "config.h" - -#include <unistd.h> -#include <locale.h> - -#include <glib/gi18n.h> -#include <gio/gio.h> -#include <gio/gdesktopappinfo.h> - -#include <libdbusmenu-glib/server.h> - -#include <gtk/gtk.h> - -#include <libindicator/indicator-service.h> - -#include "session-dbus.h" -#include "session-menu-mgr.h" -#include "shared-names.h" -#include "users-service-dbus.h" - -static SessionDbus * session_dbus = NULL; -static GMainLoop * mainloop = NULL; - - -/* When the service interface starts to shutdown, - we should follow it. */ -void -service_shutdown (IndicatorService * service, gpointer user_data) -{ - if (mainloop != NULL) - { - g_debug ("Service shutdown"); - g_main_loop_quit (mainloop); - } -} - -static inline gboolean -is_greeter_mode (void) -{ - return !g_strcmp0 (g_getenv ("INDICATOR_GREETER_MODE"), "1"); -} - -/* Main, is well, main. It brings everything up and throws - us into the mainloop of no return. */ -int -main (int argc, char ** argv) -{ - /* Setting up i18n and gettext. - Apparently we need all of these. */ - setlocale (LC_ALL, ""); - bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); - textdomain (GETTEXT_PACKAGE); - - IndicatorService * service = indicator_service_new_version (INDICATOR_SESSION_DBUS_NAME, - INDICATOR_SESSION_DBUS_VERSION); - g_signal_connect (G_OBJECT(service), INDICATOR_SERVICE_SIGNAL_SHUTDOWN, - G_CALLBACK(service_shutdown), NULL); - - session_dbus = session_dbus_new(); - - SessionMenuMgr * menu_mgr = session_menu_mgr_new (session_dbus, is_greeter_mode()); - DbusmenuServer* server = dbusmenu_server_new (INDICATOR_SESSION_DBUS_OBJECT); - dbusmenu_server_set_root (server, session_menu_mgr_get_menu (menu_mgr)); - - mainloop = g_main_loop_new(NULL, FALSE); - g_main_loop_run(mainloop); - - return 0; -} - diff --git a/src/shared-names.h b/src/shared-names.h deleted file mode 100644 index c08f98a..0000000 --- a/src/shared-names.h +++ /dev/null @@ -1,52 +0,0 @@ -/* -A small wrapper utility to load indicators and put them as menu items -into the gnome-panel using it's applet interface. - -Copyright 2009 Canonical Ltd. - -Authors: - Ted Gould <ted@canonical.com> - -This program is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License version 3, as published -by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranties of -MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifndef __DBUS_SHARED_NAMES_H__ -#define __DBUS_SHARED_NAMES_H__ - -#define INDICATOR_SESSION_DBUS_NAME "com.canonical.indicator.session" -#define INDICATOR_SESSION_DBUS_OBJECT "/com/canonical/indicator/session/menu" -#define INDICATOR_SESSION_DBUS_VERSION 0 - -#define INDICATOR_SESSION_SERVICE_DBUS_OBJECT "/com/canonical/indicator/session/service" -#define INDICATOR_SESSION_SERVICE_DBUS_IFACE "com.canonical.indicator.session.service" -#define USER_ITEM_TYPE "x-canonical-user-item" -#define USER_ITEM_PROP_NAME "user-item-name" -#define USER_ITEM_PROP_LOGGED_IN "user-item-logged-in" -#define USER_ITEM_PROP_IS_CURRENT_USER "user-item-is-current-user" -#define USER_ITEM_PROP_ICON "user-item-icon-path" -#define USER_ITEM_ICON_DEFAULT "avatar-default" - -#define ICON_DEFAULT "system-devices-panel" -#define ICON_INFO "system-devices-panel-information" -#define ICON_ALERT "system-devices-panel-alert" - -/* the session indicator's settings */ -#define SESSION_SCHEMA "com.canonical.indicator.session" -#define SUPPRESS_KEY "suppress-logout-restart-shutdown" -#define LOGOUT_KEY "suppress-logout-menuitem" -#define RESTART_KEY "suppress-restart-menuitem" -#define SHUTDOWN_KEY "suppress-shutdown-menuitem" -#define SHOW_USER_MENU "user-show-menu" - - -#endif /* __DBUS_SHARED_NAMES_H__ */ diff --git a/src/user-widget.c b/src/user-widget.c deleted file mode 100644 index 79b87b0..0000000 --- a/src/user-widget.c +++ /dev/null @@ -1,292 +0,0 @@ -/* -Copyright 2011 Canonical Ltd. - -Authors: - Conor Curran <conor.curran@canonical.com> - Mirco Müller <mirco.mueller@canonical.com> - Charles Kerr <charles.kerr@canonical.com> - -This program is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License version 3, as published -by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranties of -MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifdef HAVE_CONFIG_H - #include "config.h" -#endif - -#include <gtk/gtk.h> - -#include <libindicator/indicator-image-helper.h> - -#include "shared-names.h" -#include "user-widget.h" - - -typedef struct _UserWidgetPrivate UserWidgetPrivate; - -struct _UserWidgetPrivate -{ - DbusmenuMenuitem* twin_item; - GtkWidget* user_image; - GtkWidget* user_name; - GtkWidget* container; - GtkWidget* tick_icon; - gboolean logged_in; - gboolean sessions_active; -}; - -#define USER_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), USER_WIDGET_TYPE, UserWidgetPrivate)) - -/* Prototypes */ -static void user_widget_class_init (UserWidgetClass *klass); -static void user_widget_init (UserWidget *self); -static void user_widget_dispose (GObject *object); -static void user_widget_finalize (GObject *object); - -static void user_widget_set_twin_item (UserWidget* self, - DbusmenuMenuitem* twin_item); - -static gboolean user_widget_primitive_draw_cb_gtk_3 (GtkWidget *image, - cairo_t* cr, - gpointer user_data); - -G_DEFINE_TYPE (UserWidget, user_widget, GTK_TYPE_MENU_ITEM); - -static void -user_widget_class_init (UserWidgetClass *klass) -{ - GObjectClass * gobject_class = G_OBJECT_CLASS (klass); - - g_type_class_add_private (klass, sizeof (UserWidgetPrivate)); - - gobject_class->dispose = user_widget_dispose; - gobject_class->finalize = user_widget_finalize; -} - -static void -user_widget_init (UserWidget *self) -{ - self->priv = USER_WIDGET_GET_PRIVATE(self); - - UserWidgetPrivate * priv = self->priv; - - priv->user_image = NULL; - priv->user_name = NULL; - priv->logged_in = FALSE; - priv->sessions_active = FALSE; - priv->container = NULL; - priv->tick_icon = NULL; - - // Create the UI elements. - priv->user_image = gtk_image_new (); - gtk_misc_set_alignment(GTK_MISC(priv->user_image), 0.0, 0.0); - - priv->user_name = gtk_label_new (NULL); - - priv->container = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4); - - priv->tick_icon = gtk_image_new_from_icon_name ("account-logged-in", - GTK_ICON_SIZE_MENU); - gtk_misc_set_alignment(GTK_MISC(priv->tick_icon), 1.0, 0.5); - - // Pack it together - gtk_box_pack_start (GTK_BOX (priv->container), - priv->user_image, - FALSE, - FALSE, - 0); - gtk_box_pack_start (GTK_BOX (priv->container), - priv->user_name, - FALSE, - FALSE, - 3); - gtk_box_pack_end (GTK_BOX(priv->container), - priv->tick_icon, - FALSE, - FALSE, 5); - - gtk_widget_show_all (priv->container); - gtk_container_add (GTK_CONTAINER (self), priv->container); - gtk_widget_show_all (priv->tick_icon); - gtk_widget_set_no_show_all (priv->tick_icon, TRUE); - gtk_widget_hide (priv->tick_icon); - - - // Fetch the drawing context. - g_signal_connect_after (GTK_WIDGET(self), "draw", - G_CALLBACK(user_widget_primitive_draw_cb_gtk_3), - GTK_WIDGET(self)); -} - -static void -user_widget_dispose (GObject *object) -{ - G_OBJECT_CLASS (user_widget_parent_class)->dispose (object); -} - -// TODO tidy up image and name -static void -user_widget_finalize (GObject *object) -{ - G_OBJECT_CLASS (user_widget_parent_class)->finalize (object); -} - - -/*****************************************************************/ - -// TODO handle drawing of green check mark -static gboolean -user_widget_primitive_draw_cb_gtk_3 (GtkWidget *widget, - cairo_t* cr, - gpointer user_data) -{ - g_return_val_if_fail(IS_USER_WIDGET(user_data), FALSE); - UserWidget* meta = USER_WIDGET(user_data); - UserWidgetPrivate * priv = USER_WIDGET_GET_PRIVATE(meta); - - // Draw dot only when user is the current user. - if (dbusmenu_menuitem_property_get_bool (priv->twin_item, USER_ITEM_PROP_IS_CURRENT_USER)) - { - gdouble x, y; - GtkStyle * style = gtk_widget_get_style (widget); - - GtkAllocation allocation; - gtk_widget_get_allocation (widget, &allocation); - x = allocation.x + 13; - y = allocation.height / 2; - - cairo_arc (cr, x, y, 3.0, 0.0, 2 * G_PI); - - cairo_set_source_rgb (cr, style->fg[gtk_widget_get_state(widget)].red/65535.0, - style->fg[gtk_widget_get_state(widget)].green/65535.0, - style->fg[gtk_widget_get_state(widget)].blue/65535.0); - cairo_fill (cr); - } - - return FALSE; -} - -/*** -**** -***/ - -static void -update_icon (UserWidget * self, DbusmenuMenuitem * mi) -{ - gboolean updated = FALSE; - GtkImage * image = GTK_IMAGE(self->priv->user_image); - - /* first try the menuitem's icon property */ - const gchar * icon_name = dbusmenu_menuitem_property_get (mi, USER_ITEM_PROP_ICON); - if (icon_name != NULL) - { - int width = 18; /* arbitrary default values */ - int height = 18; - GError * err = NULL; - GdkPixbuf * pixbuf = NULL; - - /* load the image */ - gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &width, &height); - pixbuf = gdk_pixbuf_new_from_file_at_size (icon_name, width, height, &err); - if (err == NULL) - { - gtk_image_set_from_pixbuf (image, pixbuf); - g_object_unref (pixbuf); - updated = TRUE; - } - else - { - g_warning ("Couldn't load the image \"%s\": %s", icon_name, err->message); - g_clear_error (&err); - } - } - - /* as a fallback, use the default user icon */ - if (!updated) - { - gtk_image_set_from_icon_name (image, - USER_ITEM_ICON_DEFAULT, - GTK_ICON_SIZE_MENU); - } -} - -static void -update_logged_in (UserWidget * self, DbusmenuMenuitem * mi) -{ - const gboolean b = dbusmenu_menuitem_property_get_bool (mi, USER_ITEM_PROP_LOGGED_IN); - - g_debug ("User \"%s\" %s active sessions", - dbusmenu_menuitem_property_get (mi, USER_ITEM_PROP_NAME), - b ? "has" : "doesn't have"); - - gtk_widget_set_visible (self->priv->tick_icon, b); -} - -static void -update_name (UserWidget * self, DbusmenuMenuitem * mi) -{ - gtk_label_set_label (GTK_LABEL(self->priv->user_name), - dbusmenu_menuitem_property_get (mi, USER_ITEM_PROP_NAME)); -} - -static void -user_widget_property_update (DbusmenuMenuitem * mi, - const gchar * property, - GVariant * value, - UserWidget * self) -{ - g_return_if_fail (IS_USER_WIDGET (self)); - - if (!g_strcmp0 (property, USER_ITEM_PROP_LOGGED_IN)) - { - update_logged_in (self, mi); - } - else if (!g_strcmp0 (property, USER_ITEM_PROP_ICON)) - { - update_icon (self, mi); - } - else if (!g_strcmp0 (property, USER_ITEM_PROP_NAME)) - { - update_name (self, mi); - } - else - { - g_debug ("%s FIXME: unhandled property change %s", G_STRFUNC, property); - } -} - -static void -user_widget_set_twin_item (UserWidget * self, DbusmenuMenuitem * mi) -{ - self->priv->twin_item = mi; - - update_icon (self, mi); - update_name (self, mi); - update_logged_in (self, mi); - - g_signal_connect (G_OBJECT(mi), "property-changed", - G_CALLBACK(user_widget_property_update), self); -} - - /** - * user_widget_new: - * @item: the #DbusmenuMenuitem this widget will render. - * - * Returns: (transfer full): a new #UserWidget. - **/ -GtkWidget* -user_widget_new (DbusmenuMenuitem *item) -{ - GtkWidget* widget = g_object_new(USER_WIDGET_TYPE, NULL); - user_widget_set_twin_item ( USER_WIDGET(widget), item ); - return widget; -} diff --git a/src/user-widget.h b/src/user-widget.h deleted file mode 100644 index 0953e6c..0000000 --- a/src/user-widget.h +++ /dev/null @@ -1,55 +0,0 @@ -/* -Copyright 2011 Canonical Ltd. - -Authors: - Conor Curran <conor.curran@canonical.com> - -This program is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License version 3, as published -by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranties of -MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program. If not, see <http://www.gnu.org/licenses/>. -*/ -#ifndef __USER_WIDGET_H__ -#define __USER_WIDGET_H__ - -#include <gtk/gtk.h> -#include <libdbusmenu-gtk/menuitem.h> - -G_BEGIN_DECLS - -#define USER_WIDGET_TYPE (user_widget_get_type ()) -#define USER_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), USER_WIDGET_TYPE, UserWidget)) -#define USER_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), USER_WIDGET_TYPE, UserWidgetClass)) -#define IS_USER_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), USER_WIDGET_TYPE)) -#define IS_USER_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), USER_WIDGET_TYPE)) -#define USER_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), USER_WIDGET_TYPE, UserWidgetClass)) - -typedef struct _UserWidget UserWidget; -typedef struct _UserWidgetClass UserWidgetClass; -typedef struct _UserWidgetPrivate UserWidgetPrivate; - -struct _UserWidgetClass -{ - GtkMenuItemClass parent_class; -}; - -struct _UserWidget -{ - /*< private >*/ - GtkMenuItem parent; - UserWidgetPrivate * priv; -}; - -GType user_widget_get_type (void) G_GNUC_CONST; -GtkWidget* user_widget_new(DbusmenuMenuitem *twin_item); - -G_END_DECLS - -#endif diff --git a/src/users-service-dbus.c b/src/users-service-dbus.c deleted file mode 100644 index 00836de..0000000 --- a/src/users-service-dbus.c +++ /dev/null @@ -1,1046 +0,0 @@ -/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */ -/* - * Copyright 2009 Canonical Ltd. - * - * Authors: - * Cody Russell <crussell@canonical.com> - * Charles Kerr <charles.kerr@canonical.com> - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifdef HAVE_CONFIG_H - #include "config.h" -#endif - -#include <glib.h> - -#include <errno.h> - -#include <pwd.h> /* getpwuid() */ - -#include "dbus-accounts.h" -#include "dbus-login1-manager.h" -#include "dbus-login1-session.h" -#include "dbus-login1-user.h" -#include "dbus-display-manager.h" -#include "dbus-user.h" -#include "shared-names.h" -#include "users-service-dbus.h" - -#define LOGIND_ADDR "org.freedesktop.login1" - -/** -*** -**/ - -static void update_user_list (UsersServiceDbus * self); - -static gchar* get_seat (UsersServiceDbus * service); - -static void on_user_added (Accounts * o, - const gchar * user_object_path, - UsersServiceDbus * service); - -static void on_user_deleted (Accounts * o, - const gchar * user_object_path, - UsersServiceDbus * service); - -static void on_session_added (Login1Manager * proxy, - const gchar * ssid, - const gchar * path, - UsersServiceDbus * service); - -static void on_session_removed (Login1Manager * proxy, - const gchar * ssid, - const gchar * path, - UsersServiceDbus * service); - -static void on_session_list (Login1Manager * proxy, - GAsyncResult * result, - UsersServiceDbus * service); - -/*** -**** Priv Struct -***/ - -struct _UsersServiceDbusPrivate -{ - gchar * seat; - gchar * guest_ssid; - - /* ssid -> AccountsUser lookup */ - GHashTable * sessions; - - /* user object path -> AccountsUser lookup */ - GHashTable * users; - - GCancellable * cancellable; - Login1Manager * manager_proxy; - Accounts * accounts_proxy; -}; - -/*** -**** GObject -***/ - -enum -{ - USER_LIST_CHANGED, - USER_LOGGED_IN_CHANGED, - GUEST_LOGGED_IN_CHANGED, - N_SIGNALS -}; - -static guint signals[N_SIGNALS] = { 0 }; - -G_DEFINE_TYPE (UsersServiceDbus, users_service_dbus, G_TYPE_OBJECT); - -static void -users_service_dbus_dispose (GObject *object) -{ - UsersServiceDbusPrivate * priv = USERS_SERVICE_DBUS(object)->priv; - - g_clear_object (&priv->accounts_proxy); - g_clear_object (&priv->manager_proxy); - - if (priv->cancellable != NULL) - { - g_cancellable_cancel (priv->cancellable); - g_clear_object (&priv->cancellable); - } - - if (priv->users != NULL) - { - g_hash_table_destroy (priv->users); - priv->users = NULL; - } - - if (priv->sessions != NULL) - { - g_hash_table_destroy (priv->sessions); - priv->sessions = NULL; - } - - G_OBJECT_CLASS (users_service_dbus_parent_class)->dispose (object); -} - -static void -users_service_dbus_finalize (GObject *object) -{ - UsersServiceDbusPrivate * priv = USERS_SERVICE_DBUS(object)->priv; - - g_free (priv->guest_ssid); - g_free (priv->seat); - - G_OBJECT_CLASS (users_service_dbus_parent_class)->finalize (object); -} - -static void -users_service_dbus_class_init (UsersServiceDbusClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - g_type_class_add_private (object_class, sizeof (UsersServiceDbusPrivate)); - - object_class->dispose = users_service_dbus_dispose; - object_class->finalize = users_service_dbus_finalize; - - signals[USER_LIST_CHANGED] = g_signal_new ( - "user-list-changed", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (UsersServiceDbusClass, user_list_changed), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - signals[USER_LOGGED_IN_CHANGED] = g_signal_new ( - "user-logged-in-changed", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (UsersServiceDbusClass, user_logged_in_changed), - NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, 1, G_TYPE_OBJECT); - - signals[GUEST_LOGGED_IN_CHANGED] = g_signal_new ( - "guest-logged-in-changed", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (UsersServiceDbusClass, guest_logged_in_changed), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); -} - -static void -users_service_dbus_init (UsersServiceDbus *self) -{ - GError * error = NULL; - - self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, - USERS_SERVICE_DBUS_TYPE, - UsersServiceDbusPrivate); - - UsersServiceDbusPrivate * p = self->priv; - - p->cancellable = g_cancellable_new (); - - /* ssid -> AccountsUser */ - p->sessions = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - g_object_unref); - - /* user object path -> AccountsUser */ - p->users = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - g_object_unref); - - /** - *** create the logind manager proxy... - **/ - - p->manager_proxy = login1_manager_proxy_new_for_bus_sync ( - G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_NONE, - "org.freedesktop.login1", - "/org/freedesktop/login1", - NULL, - &error); - if (error != NULL) - { - g_warning ("%s: %s", G_STRLOC, error->message); - g_clear_error (&error); - } - - p->seat = get_seat (self); - - g_signal_connect (p->manager_proxy, "session-new", - G_CALLBACK (on_session_added), self); - g_signal_connect (p->manager_proxy, "session-removed", - G_CALLBACK (on_session_removed), self); - - login1_manager_call_list_sessions (p->manager_proxy, p->cancellable, - (GAsyncReadyCallback) on_session_list, self); - - /** - *** create the accounts manager proxy... - **/ - - Accounts * proxy = accounts_proxy_new_for_bus_sync ( - G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_NONE, - "org.freedesktop.Accounts", - "/org/freedesktop/Accounts", - NULL, - &error); - if (error != NULL) - { - g_warning ("%s: %s", G_STRFUNC, error->message); - g_clear_error (&error); - } - else - { - g_signal_connect (proxy, "user-added", G_CALLBACK(on_user_added), self); - g_signal_connect (proxy, "user-deleted", G_CALLBACK(on_user_deleted), self); - p->accounts_proxy = proxy; - update_user_list (self); - } -} - -/*** -**** -***/ - -static void -emit_user_list_changed (UsersServiceDbus * self) -{ - g_signal_emit (self, signals[USER_LIST_CHANGED], 0); -} - -static void -emit_user_login_changed (UsersServiceDbus * self, AccountsUser * user) -{ - g_signal_emit (self, signals[USER_LOGGED_IN_CHANGED], 0, user); -} - -static void -emit_guest_login_changed (UsersServiceDbus * self) -{ - g_signal_emit (self, signals[GUEST_LOGGED_IN_CHANGED], 0); -} - -/*** -**** -***/ - -static Login1User* -create_login1_user_proxy (const char * path) -{ - - GError * error = NULL; - - Login1User * p = login1_user_proxy_new_for_bus_sync ( - G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_NONE, - LOGIND_ADDR, - path, - NULL, - &error); - if (error != NULL) - { - g_warning ("%s: %s", G_STRLOC, error->message); - g_error_free (error); - } - - return p; -} - -static Login1User * -create_login1_user_proxy_from_uid (UsersServiceDbus * self, - guint64 uid) -{ - Login1Manager * manager = self->priv->manager_proxy; - Login1User * user_proxy = NULL; - gchar * user_object_path = NULL; - GError * error = NULL; - - login1_manager_call_get_user_sync (manager, uid, &user_object_path, NULL, - &error); - - if (error != NULL) - { - g_warning ("%s: %s", G_STRLOC, error->message); - g_error_free (error); - } - - if (user_object_path != NULL) - user_proxy = create_login1_user_proxy (user_object_path); - - return user_proxy; - -} - -static Login1Session * -create_login1_session_proxy (const char * path) -{ - GError * error = NULL; - - Login1Session * p = login1_session_proxy_new_for_bus_sync ( - G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_NONE, - LOGIND_ADDR, - path, - NULL, - &error); - if (error != NULL) - { - g_warning ("%s: %s", G_STRLOC, error->message); - g_error_free (error); - } - - return p; -} - - -static gchar * -get_seat_from_session_proxy (Login1Session * session_proxy) -{ - gchar * seat; - GVariant * seatobj = login1_session_get_seat (session_proxy); - - g_variant_get (seatobj, "(so)", &seat, NULL); - - return seat; -} - -static const gchar * -get_display_from_session_proxy (Login1Session * session_proxy) -{ - const gchar * display; - display = login1_session_get_display (session_proxy); - return display; -} - -static gchar * -get_seat (UsersServiceDbus *service) -{ - gchar * seat = NULL; - gchar * path = NULL; - GError * error = NULL; - UsersServiceDbusPrivate * priv = service->priv; - Login1Session * session_proxy = NULL; - pid_t pid = getpid(); - - login1_manager_call_get_session_by_pid_sync (priv->manager_proxy, - pid, - &path, - NULL, - &error); - - - if (error != NULL) - { - g_debug ("%s: %s", G_STRLOC, error->message); - g_error_free (error); - goto out; - } - - session_proxy = create_login1_session_proxy (path); - - if (!session_proxy) - { - g_debug ("%s: Could't get session proxy object", G_STRLOC); - } - - seat = get_seat_from_session_proxy (session_proxy); - -out: - g_object_unref (session_proxy); - return seat; -} - -/*** -**** AccountsUser add-ons for tracking sessions -***/ - -static GHashTable* -user_get_sessions_hashset (AccountsUser * user) -{ - static GQuark q = 0; - - if (G_UNLIKELY(!q)) - { - q = g_quark_from_static_string ("sessions"); - } - - GObject * o = G_OBJECT (user); - GHashTable * h = g_object_get_qdata (o, q); - if (h == NULL) - { - h = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - g_object_set_qdata_full (o, q, h, (GDestroyNotify)g_hash_table_destroy); - } - - return h; -} - -static void -user_add_session (AccountsUser * user, const char * ssid) -{ - g_hash_table_add (user_get_sessions_hashset(user), g_strdup(ssid)); -} - -static void -user_remove_session (AccountsUser * user, const char * ssid) -{ - g_hash_table_remove (user_get_sessions_hashset(user), ssid); -} - -static guint -user_count_sessions (AccountsUser * user) -{ - return g_hash_table_size (user_get_sessions_hashset(user)); -} - -/*** -**** Users -***/ - -/* adds this user session to the user's and service's session tables */ -static void -add_user_session (UsersServiceDbus * service, - AccountsUser * user, - const gchar * ssid, - const gchar * path) -{ - Login1Session * session_proxy = create_login1_session_proxy (path); - if (session_proxy != NULL) - { - UsersServiceDbusPrivate * priv = service->priv; - gchar * seat = get_seat_from_session_proxy (session_proxy); - - /* is this session in our seat? */ - if (seat && priv->seat && !g_strcmp0 (seat, priv->seat)) - { - /* does this session have a display? */ - const gchar * display = get_display_from_session_proxy (session_proxy); - const gboolean has_display = g_strcmp0 ("", display) != 0; - - if (has_display) - { - const gchar * username = accounts_user_get_user_name (user); - g_debug ("%s adding %s's session '%s' to our tables", - G_STRLOC, username, ssid); - - g_hash_table_insert (priv->sessions, - g_strdup (ssid), - g_object_ref (user)); - - user_add_session (user, ssid); - } - } - - g_free (seat); - g_object_unref (session_proxy); - } -} - -/* calls add_user_session() for each of this user's sessions */ -static void -add_user_sessions (UsersServiceDbus *self, AccountsUser * user) -{ - const guint64 uid = accounts_user_get_uid (user); - const char * username = accounts_user_get_user_name (user); - g_debug ("%s adding %s (%i)", G_STRLOC, username, (int)uid); - - GVariant * sessions = NULL; - - Login1User * user_proxy = create_login1_user_proxy_from_uid (self, uid); - - if (user_proxy != NULL) - { - sessions = login1_user_get_sessions (user_proxy); - } - - if (sessions != NULL) - { - GVariantIter iter; - g_variant_iter_init (&iter, sessions); - gchar * id; - gchar * object_path; - - while (g_variant_iter_loop (&iter, "(so)", &id, &object_path)) - { - g_debug ("%s adding %s's session %s", G_STRLOC, username, id); - add_user_session (self, user, id, object_path); - } - } - - g_object_unref (user_proxy); -} - -/* returns true if this property is one we use */ -static gboolean -is_interesting_user_property (const char * key) -{ - return !g_strcmp0 (key, "IconFile") - || !g_strcmp0 (key, "LoginFrequency") - || !g_strcmp0 (key, "RealName") - || !g_strcmp0 (key, "Uid") - || !g_strcmp0 (key, "UserName"); -} - -static void -sync_user_properties (GDBusProxy * source, GDBusProxy * target) -{ - gchar ** keys = g_dbus_proxy_get_cached_property_names (source); - - if (keys != NULL) - { - int i; - GVariantBuilder builder; - gboolean changed = FALSE; - g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); - - for (i=0; keys[i]; i++) - { - const gchar * const key = keys[i]; - - if (is_interesting_user_property (key)) - { - GVariant * oldval = g_dbus_proxy_get_cached_property (target, key); - GVariant * newval = g_dbus_proxy_get_cached_property (source, key); - - /* all the properties we're interested in are - basic types safe for g_variant_compare()... */ - g_assert (g_variant_type_is_basic(g_variant_get_type(newval))); - - if (g_variant_compare (oldval, newval)) - { - changed = TRUE; - g_dbus_proxy_set_cached_property (target, key, newval); - g_variant_builder_add (&builder, "{sv}", key, newval); - } - - g_variant_unref (newval); - g_variant_unref (oldval); - } - } - - if (changed) - { - g_signal_emit_by_name (target, "g-properties-changed", g_variant_builder_end(&builder), keys); - } - - g_variant_builder_clear (&builder); - g_strfreev (keys); - } -} - -/** - * The AccountsUserProxy's properties aren't being updated automatically - * for some reason... the only update we get is the 'changed' signal. - * This function is a workaround to update our User object's properties. - */ -static void -on_user_changed (AccountsUser * user, UsersServiceDbus * service) -{ - AccountsUser * tmp = accounts_user_proxy_new_for_bus_sync ( - G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_NONE, - "org.freedesktop.Accounts", - g_dbus_proxy_get_object_path (G_DBUS_PROXY(user)), - NULL, - NULL); - if (tmp != NULL) - { - sync_user_properties (G_DBUS_PROXY(tmp), G_DBUS_PROXY(user)); - g_object_unref (tmp); - } -} - -static void -add_user_from_object_path (UsersServiceDbus * self, - const char * user_object_path) -{ - GError * error = NULL; - - AccountsUser * user = accounts_user_proxy_new_for_bus_sync ( - G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_NONE, - "org.freedesktop.Accounts", - user_object_path, - NULL, - &error); - - if (error != NULL) - { - g_warning ("%s: %s", G_STRLOC, error->message); - g_clear_error (&error); - } - else - { - AccountsUser * prev = g_hash_table_lookup (self->priv->users, user_object_path); - - if (prev != NULL) /* we've already got this user... sync its properties */ - { - sync_user_properties (G_DBUS_PROXY(user), G_DBUS_PROXY(prev)); - g_object_unref (user); - user = prev; - } - else /* ooo, we got a new user */ - { - g_signal_connect (user, "changed", G_CALLBACK(on_user_changed), self); - g_hash_table_insert (self->priv->users, g_strdup(user_object_path), user); - } - - add_user_sessions (self, user); - } -} - - -/* asks org.freedesktop.Accounts for a list of users and - * calls add_user_from_object_path() on each of those users */ -static void -update_user_list (UsersServiceDbus *self) -{ - g_return_if_fail(IS_USERS_SERVICE_DBUS(self)); - - GError * error = NULL; - char ** object_paths = NULL; - UsersServiceDbusPrivate * priv = self->priv; - - accounts_call_list_cached_users_sync (priv->accounts_proxy, - &object_paths, - NULL, - &error); - - if (error != NULL) - { - g_warning ("%s: %s", G_STRFUNC, error->message); - g_clear_error (&error); - } - else if (object_paths != NULL) - { - gint i; - - for (i=0; object_paths[i] != NULL; ++i) - { - add_user_from_object_path (self, object_paths[i]); - } - - emit_user_list_changed (self); - - g_strfreev (object_paths); - } - - g_debug ("%s finished updating the user list", G_STRLOC); -} - -static void -on_user_added (Accounts * o G_GNUC_UNUSED, - const gchar * user_path G_GNUC_UNUSED, - UsersServiceDbus * service) -{ - /* We see a new user but we might not want to list it -- - for example, lightdm shows up when we switch to the greeter. - So instead of adding the user directly here, let's ask - org.freedesktop.Accounts for a fresh list of users - because it filters out special cases. */ - update_user_list (service); -} - -static void -on_user_deleted (Accounts * o G_GNUC_UNUSED, - const gchar * user_path, - UsersServiceDbus * service) -{ - AccountsUser * user = g_hash_table_lookup (service->priv->users, user_path); - - if (user != NULL) - { - GObject * o = g_object_ref (G_OBJECT(user)); - g_hash_table_remove (service->priv->users, user_path); - emit_user_list_changed (service); - g_object_unref (o); - } -} - -static AccountsUser * -find_user_from_username (UsersServiceDbus * self, - const gchar * username) -{ - AccountsUser * match = NULL; - - g_return_val_if_fail (IS_USERS_SERVICE_DBUS(self), match); - - gpointer user; - GHashTableIter iter; - g_hash_table_iter_init (&iter, self->priv->users); - while (!match && g_hash_table_iter_next (&iter, NULL, &user)) - { - if (!g_strcmp0 (username, accounts_user_get_user_name (user))) - { - match = user; - } - } - - return match; -} - -/*** -**** Sessions -***/ - -static void -on_session_removed (Login1Manager * proxy, - const gchar * ssid, - const gchar * path, - UsersServiceDbus * service) -{ - g_return_if_fail (IS_USERS_SERVICE_DBUS (service)); - - UsersServiceDbusPrivate * priv = service->priv; - g_debug ("%s %s() session removed %s", G_STRLOC, G_STRFUNC, ssid); - - if (!g_strcmp0 (ssid, priv->guest_ssid)) - { - g_debug ("%s removing guest session %s", G_STRLOC, ssid); - g_clear_pointer (&priv->guest_ssid, g_free); - emit_guest_login_changed (service); - } - else - { - AccountsUser * user = g_hash_table_lookup (priv->sessions, ssid); - if (user == NULL) - { - g_debug ("%s we're not tracking ssid %s", G_STRLOC, ssid); - } - else - { - GObject * o = g_object_ref (G_OBJECT(user)); - g_hash_table_remove (service->priv->users, ssid); - user_remove_session (user, ssid); - emit_user_login_changed (service, user); - g_object_unref (o); - } - } -} - -static gchar* -get_unix_username_from_path (UsersServiceDbus * self, - const gchar * path) -{ - - Login1Session * session_proxy = create_login1_session_proxy (path); - if (session_proxy != NULL) - { - gchar * username = g_strdup (login1_session_get_name (session_proxy)); - - g_debug ("%s Getting username for %s: %s", G_STRLOC, path, username); - - g_object_unref (session_proxy); - - return username; - } - else - { - return NULL; - } -} - -static gboolean -is_guest_username (const char * username) -{ - if (!g_strcmp0 (username, "guest")) - return TRUE; - - if (username && g_str_has_prefix (username, "guest-")) - return TRUE; - - return FALSE; -} - -/* If the new session belongs to 'guest', update our guest_ssid. - Otherwise, call add_user_session() to update our session tables */ -static void -on_session_added (Login1Manager * proxy G_GNUC_UNUSED, - const gchar * ssid, - const gchar * path, - UsersServiceDbus * service) -{ - g_return_if_fail (IS_USERS_SERVICE_DBUS(service)); - - gchar * username = get_unix_username_from_path (service, path); - g_debug ("%s %s() username %s has new session %s", G_STRLOC, G_STRFUNC, username, ssid); - - if (is_guest_username (username)) - { - /* handle guest as a special case -- it's not in the GDM - user tables and there isn't be an AccountsUser for it */ - g_debug("Found guest session: %s", ssid); - g_free (service->priv->guest_ssid); - service->priv->guest_ssid = g_strdup (ssid); - emit_guest_login_changed (service); - } - else - { - AccountsUser * user = find_user_from_username (service, username); - - if (user != NULL) - { - add_user_session (service, user, ssid, path); - emit_user_login_changed (service, user); - } - } - -} - -/* Receives a list of sessions and calls on_session_added() for each of them */ -static void -on_session_list (Login1Manager * proxy, - GAsyncResult * result, - UsersServiceDbus * self) -{ - GError * error = NULL; - GVariant * sessions; - g_debug ("%s bootstrapping the session list", G_STRLOC); - - login1_manager_call_list_sessions_finish (proxy, - &sessions, - result, - &error); - - if (error != NULL) - { - g_debug ("%s: %s", G_STRLOC, error->message); - g_error_free (error); - } - else - { - GVariantIter * iter; - gchar * seat; - gchar * path; - - g_variant_get (sessions, "a(susso)", &iter); - - while (g_variant_iter_loop (iter, - "(susso)", - NULL, - NULL, - NULL, - &seat, - &path)) - { - if (g_strcmp0 (seat, self->priv->seat) == 0) - { - g_debug ("%s adding initial session '%s'", G_STRLOC, path); - on_session_added (proxy, seat, path, self); - } - } - - g_variant_iter_free (iter); - g_variant_unref (sessions); - - } - - g_debug ("%s done bootstrapping the session list", G_STRLOC); -} - -static DisplayManagerSeat * -create_display_proxy (UsersServiceDbus * self) -{ - const gchar * const seat = g_getenv ("XDG_SEAT_PATH"); - g_debug ("%s creating a DisplayManager proxy for seat %s", G_STRLOC, seat); - - GError * error = NULL; - DisplayManagerSeat * p = display_manager_seat_proxy_new_for_bus_sync ( - G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_NONE, - "org.freedesktop.DisplayManager", - seat, - NULL, - &error); - - if (error != NULL) - { - g_warning ("%s: %s", G_STRLOC, error->message); - g_error_free (error); - } - - return p; -} - -/*** -**** Public API -***/ - -/** - * users_service_dbus_get_user_list: - * - * Returns: (transfer container): a list of AccountsUser objects - */ -GList * -users_service_dbus_get_user_list (UsersServiceDbus * self) -{ - g_return_val_if_fail(IS_USERS_SERVICE_DBUS(self), NULL); - - return g_hash_table_get_values (self->priv->users); -} - -/** - * users_service_dbus_show_greeter: - * - * Ask the Display Mnaager to switch to the greeter screen. - */ -void -users_service_dbus_show_greeter (UsersServiceDbus * self) -{ - DisplayManagerSeat * dp; - - g_return_if_fail (IS_USERS_SERVICE_DBUS(self)); - - dp = create_display_proxy (self); - if (dp != NULL) - { - display_manager_seat_call_switch_to_greeter_sync (dp, NULL, NULL); - g_clear_object (&dp); - } -} - -/** - * users_service_dbus_activate_guest_session: - * - * Activates the guest account. - */ -void -users_service_dbus_activate_guest_session (UsersServiceDbus * self) -{ - DisplayManagerSeat * dp; - - g_return_if_fail (IS_USERS_SERVICE_DBUS(self)); - - dp = create_display_proxy (self); - if (dp != NULL) - { - display_manager_seat_call_switch_to_guest_sync (dp, "", NULL, NULL); - g_clear_object (&dp); - } -} - -/** - * users_service_dbus_activate_user_session: - * - * Activates a specific user. - */ -void -users_service_dbus_activate_user_session (UsersServiceDbus * self, - AccountsUser * user) -{ - DisplayManagerSeat * dp; - - g_return_if_fail (IS_USERS_SERVICE_DBUS(self)); - - dp = create_display_proxy (self); - if (dp != NULL) - { - const char * const username = accounts_user_get_user_name (user); - display_manager_seat_call_switch_to_user_sync (dp, username, "", NULL, NULL); - g_clear_object (&dp); - } -} - -/** - * users_service_dbus_guest_session_enabled: - * - * Tells whether or not guest sessions are allowed. - */ -gboolean -users_service_dbus_guest_session_enabled (UsersServiceDbus * self) -{ - DisplayManagerSeat * dp; - gboolean enabled = FALSE; - - g_return_val_if_fail (IS_USERS_SERVICE_DBUS(self), enabled); - - dp = create_display_proxy (self); - if (dp != NULL) - { - enabled = display_manager_seat_get_has_guest_account (dp); - g_clear_object (&dp); - } - - return enabled; -} - -gboolean -users_service_dbus_is_guest_logged_in (UsersServiceDbus * self) -{ - g_return_val_if_fail (IS_USERS_SERVICE_DBUS(self), FALSE); - - return self->priv->guest_ssid != NULL; -} - -gboolean -users_service_dbus_is_user_logged_in (UsersServiceDbus * self, - AccountsUser * user) -{ - g_return_val_if_fail (IS_USERS_SERVICE_DBUS(self), FALSE); - g_return_val_if_fail (IS_ACCOUNTS_USER(user), FALSE); - - return user_count_sessions (user) > 0; -} diff --git a/src/users-service-dbus.h b/src/users-service-dbus.h deleted file mode 100644 index 3e5252d..0000000 --- a/src/users-service-dbus.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2009 Canonical Ltd. - * - * Authors: - * Cody Russell <crussell@canonical.com> - * Charles Kerr <charles.kerr@canonical.com> - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 3, as published - * by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranties of - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef __USERS_SERVICE_DBUS_H__ -#define __USERS_SERVICE_DBUS_H__ - -#include <glib.h> -#include <glib-object.h> - -#include "dbus-user.h" /* for AccountsUser */ - -G_BEGIN_DECLS - -#define USERS_SERVICE_DBUS_TYPE (users_service_dbus_get_type ()) -#define USERS_SERVICE_DBUS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), USERS_SERVICE_DBUS_TYPE, UsersServiceDbus)) -#define IS_USERS_SERVICE_DBUS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), USERS_SERVICE_DBUS_TYPE)) - -typedef struct _UsersServiceDbus UsersServiceDbus; -typedef struct _UsersServiceDbusClass UsersServiceDbusClass; -typedef struct _UsersServiceDbusPrivate UsersServiceDbusPrivate; - -/** - * A facade class which interacts with multiple DBus services to - * track info which is useful to the interactor's user menu: - * - * 1. A list of users to add to the user menu. - * - * Each user is an AccountsUser object, which is a GDBusProxy - * to an org.freedesktop.Accounts.User object. - * - * We initially build this list by calling org.freedesktop.Accounts' - * GetCachedUsers method. We also monitor o.f.Accounts' UserAdded - * and UserDeleted and update the list accordingly. - * - * 2. Track which users currently have X sessions. - * This is used for the menuitems' USER_ITEM_PROP_LOGGED_IN property. - * - * We initially build this list by calling org.freedesktop.login1's - * ListSessions method. We also monitor the seat for SessionNew and - * SessionRemoved and update the list accordingly. - * - * 3. Provide an API for user switching and guest sessions. - * These are typically pass-through functions to GDBusProxies. - * - */ -struct _UsersServiceDbus -{ - /*< private >*/ - GObject parent; - UsersServiceDbusPrivate * priv; -}; - -struct _UsersServiceDbusClass -{ - GObjectClass parent_class; - - /* Signals */ - void (* user_list_changed) (UsersServiceDbus*, gpointer); - void (* user_logged_in_changed) (UsersServiceDbus*, AccountsUser*, gpointer); - void (* guest_logged_in_changed) (UsersServiceDbus*, gpointer); -}; - -GType users_service_dbus_get_type (void) G_GNUC_CONST; - -GList * users_service_dbus_get_user_list (UsersServiceDbus * self); - -gboolean users_service_dbus_is_guest_logged_in (UsersServiceDbus * self); -gboolean users_service_dbus_is_user_logged_in (UsersServiceDbus * self, - AccountsUser * user); - -void users_service_dbus_show_greeter (UsersServiceDbus * self); -gboolean users_service_dbus_guest_session_enabled (UsersServiceDbus * self); -void users_service_dbus_activate_guest_session (UsersServiceDbus * self); -void users_service_dbus_activate_user_session (UsersServiceDbus * self, - AccountsUser * user); - -G_END_DECLS - -#endif diff --git a/src/users.c b/src/users.c new file mode 100644 index 0000000..5e4d910 --- /dev/null +++ b/src/users.c @@ -0,0 +1,198 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "users.h" + +/* signals enum */ +enum +{ + USER_ADDED, + USER_REMOVED, + USER_CHANGED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE (IndicatorSessionUsers, indicator_session_users, G_TYPE_OBJECT) + +enum +{ + PROP_0, + PROP_IS_LIVE_SESSION, + PROP_LAST +}; + +static GParamSpec *properties[PROP_LAST]; + +static void +my_get_property (GObject * o, + guint property_id, + GValue * value, + GParamSpec * pspec) +{ + IndicatorSessionUsers * self = INDICATOR_SESSION_USERS (o); + + switch (property_id) + { + case PROP_IS_LIVE_SESSION: + g_value_set_boolean (value, indicator_session_users_is_live_session (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec); + } +} + +static void +/* cppcheck-suppress unusedFunction */ +indicator_session_users_class_init (IndicatorSessionUsersClass * klass) +{ + GObjectClass * object_class; + const GParamFlags flags = G_PARAM_READABLE | G_PARAM_STATIC_STRINGS; + + object_class = G_OBJECT_CLASS (klass); + object_class->get_property = my_get_property; + + signals[USER_ADDED] = g_signal_new (INDICATOR_SESSION_USERS_SIGNAL_USER_ADDED, + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (IndicatorSessionUsersClass, user_added), + NULL, NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, G_TYPE_UINT); + + signals[USER_REMOVED] = g_signal_new (INDICATOR_SESSION_USERS_SIGNAL_USER_REMOVED, + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (IndicatorSessionUsersClass, user_removed), + NULL, NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, G_TYPE_UINT); + + signals[USER_CHANGED] = g_signal_new (INDICATOR_SESSION_USERS_SIGNAL_USER_CHANGED, + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (IndicatorSessionUsersClass, user_changed), + NULL, NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, G_TYPE_UINT); + + + properties[PROP_IS_LIVE_SESSION] = + g_param_spec_boolean (INDICATOR_SESSION_USERS_PROP_IS_LIVE_SESSION, + "Is Live Session", + "Whether or this is a 'live session', such as booting from a live CD", + FALSE, flags); + + g_object_class_install_properties (object_class, PROP_LAST, properties); + +} + +static void +/* cppcheck-suppress unusedFunction */ +indicator_session_users_init (IndicatorSessionUsers * self G_GNUC_UNUSED) +{ +} + +/*** +**** Virtual Functions +***/ + +GList * +indicator_session_users_get_uids (IndicatorSessionUsers * self) +{ + g_return_val_if_fail (INDICATOR_IS_SESSION_USERS (self), NULL); + + return INDICATOR_SESSION_USERS_GET_CLASS (self)->get_uids (self); +} + +IndicatorSessionUser * +indicator_session_users_get_user (IndicatorSessionUsers * self, + guint uid) +{ + g_return_val_if_fail (INDICATOR_IS_SESSION_USERS (self), NULL); + + return INDICATOR_SESSION_USERS_GET_CLASS (self)->get_user (self, uid); +} + +void +indicator_session_users_activate_user (IndicatorSessionUsers * self, + guint uid) +{ + g_return_if_fail (INDICATOR_IS_SESSION_USERS (self)); + + INDICATOR_SESSION_USERS_GET_CLASS (self)->activate_user (self, uid); +} + +gboolean +indicator_session_users_is_live_session (IndicatorSessionUsers * self) +{ + g_return_val_if_fail (INDICATOR_IS_SESSION_USERS (self), FALSE); + + return INDICATOR_SESSION_USERS_GET_CLASS (self)->is_live_session (self); +} + +void +indicator_session_user_free (IndicatorSessionUser * user) +{ + g_return_if_fail (user != NULL); + + g_free (user->real_name); + g_free (user->user_name); + g_free (user->icon_file); + g_free (user); +} + +/*** +**** Signal Convenience +***/ + +void +indicator_session_users_added (IndicatorSessionUsers * self, guint uid) +{ + g_return_if_fail (INDICATOR_IS_SESSION_USERS (self)); + + g_signal_emit (self, signals[USER_ADDED], 0, uid); +} + +void +indicator_session_users_removed (IndicatorSessionUsers * self, guint uid) +{ + g_return_if_fail (INDICATOR_IS_SESSION_USERS (self)); + + g_signal_emit (self, signals[USER_REMOVED], 0, uid); +} + +void +indicator_session_users_changed (IndicatorSessionUsers * self, guint uid) +{ + g_return_if_fail (INDICATOR_IS_SESSION_USERS (self)); + + g_signal_emit (self, signals[USER_CHANGED], 0, uid); +} + +void +indicator_session_users_notify_is_live_session (IndicatorSessionUsers * self) +{ + g_return_if_fail (INDICATOR_IS_SESSION_USERS (self)); + + g_object_notify_by_pspec (G_OBJECT(self), properties[PROP_IS_LIVE_SESSION]); +} + diff --git a/src/users.h b/src/users.h new file mode 100644 index 0000000..9871766 --- /dev/null +++ b/src/users.h @@ -0,0 +1,156 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __USERS_H__ +#define __USERS_H__ + +#include <glib.h> +#include <glib-object.h> + +G_BEGIN_DECLS + +#define INDICATOR_TYPE_SESSION_USERS (indicator_session_users_get_type()) +#define INDICATOR_SESSION_USERS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_SESSION_USERS, IndicatorSessionUsers)) +#define INDICATOR_SESSION_USERS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_SESSION_USERS, IndicatorSessionUsersClass)) +#define INDICATOR_SESSION_USERS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), INDICATOR_TYPE_SESSION_USERS, IndicatorSessionUsersClass)) +#define INDICATOR_IS_SESSION_USERS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_SESSION_USERS)) + + +typedef struct _IndicatorSessionUser IndicatorSessionUser; +typedef struct _IndicatorSessionUsers IndicatorSessionUsers; +typedef struct _IndicatorSessionUsersClass IndicatorSessionUsersClass; + +/** + * A base class for monitoring the system's users and active sessions. + * Use backend.h's get_backend() to get an instance. + */ +struct _IndicatorSessionUsers +{ + /*< private >*/ + GObject parent; +}; + +struct _IndicatorSessionUser +{ + gboolean is_current_user; + gboolean is_logged_in; + guint uid; + guint64 login_frequency; + gchar * user_name; + gchar * real_name; + gchar * icon_file; +}; + +/* signal keys */ +#define INDICATOR_SESSION_USERS_SIGNAL_USER_ADDED "user-added" +#define INDICATOR_SESSION_USERS_SIGNAL_USER_REMOVED "user-removed" +#define INDICATOR_SESSION_USERS_SIGNAL_USER_CHANGED "user-changed" + +/* property keys */ +#define INDICATOR_SESSION_USERS_PROP_IS_LIVE_SESSION "is-live-session" + +struct _IndicatorSessionUsersClass +{ + GObjectClass parent_class; + + /* signals */ + + void (* user_added) (IndicatorSessionUsers * self, + guint uid); + + void (* user_removed) (IndicatorSessionUsers * self, + guint uid); + + void (* user_changed) (IndicatorSessionUsers * self, + guint uid); + + + /* pure virtual functions */ + + gboolean (* is_live_session) (IndicatorSessionUsers * self); + + + GList* (* get_uids) (IndicatorSessionUsers * self); + + IndicatorSessionUser * (* get_user) (IndicatorSessionUsers * self, + guint uid); + + void ( * activate_user) (IndicatorSessionUsers * self, + guint uid); +}; + +/*** +**** +***/ + +GType indicator_session_users_get_type (void); + +/* emits the "user-added" signal */ +void indicator_session_users_added (IndicatorSessionUsers * self, + guint uid); + +/* emits the "user-removed" signal */ +void indicator_session_users_removed (IndicatorSessionUsers * self, + guint uid); + +/* emits the "user-changed" signal */ +void indicator_session_users_changed (IndicatorSessionUsers * self, + guint uid); + +/* notify listeners of a change to the 'is-live-session' property */ +void indicator_session_users_notify_is_live_session (IndicatorSessionUsers * self); + + + +/*** +**** +***/ + +gboolean indicator_session_users_is_live_session (IndicatorSessionUsers * users); + +/** + * Get a list of the users to show in the indicator + * + * Return value: (transfer container): a GList of guint user ids. + * Free with g_slist_free() when done. + */ +GList * indicator_session_users_get_uids (IndicatorSessionUsers * users); + +/** + * Get information about a particular user. + * + * Return value: (transfer full): an IndicatorSessionUser struct + * populated with information about the specified user. + * Free with indicator_session_user_free() when done. + */ +IndicatorSessionUser * +indicator_session_users_get_user (IndicatorSessionUsers * users, + guint uid); + +/* frees a IndicatorSessionUser struct */ +void indicator_session_user_free (IndicatorSessionUser * user); + +/* activate to a different session */ +void indicator_session_users_activate_user (IndicatorSessionUsers * self, + guint uid); + + +G_END_DECLS + +#endif |