aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS2
-rw-r--r--CMakeLists.txt68
-rw-r--r--ChangeLog2
-rw-r--r--INSTALL86
-rw-r--r--Makefile.am43
-rw-r--r--Makefile.am.coverage48
-rw-r--r--README47
-rwxr-xr-xautogen.sh11
-rwxr-xr-xbuild.sh18
-rw-r--r--cmake/FindIntltool.cmake23
-rw-r--r--cmake/GCov.cmake50
-rw-r--r--cmake/GdbusCodegen.cmake36
-rw-r--r--cmake/Translations.cmake41
-rw-r--r--cmake/UseGSettings.cmake23
-rw-r--r--configure.ac220
-rw-r--r--data/CMakeLists.txt74
-rw-r--r--data/Makefile.am27
-rw-r--r--data/com.canonical.indicator.session11
-rw-r--r--data/icons/16x16/Makefile.am1
-rw-r--r--data/icons/16x16/actions/Makefile.am9
-rw-r--r--data/icons/16x16/status/Makefile.am7
-rw-r--r--data/icons/22x22/Makefile.am1
-rw-r--r--data/icons/22x22/actions/Makefile.am9
-rw-r--r--data/icons/22x22/status/Makefile.am7
-rw-r--r--data/icons/24x24/Makefile.am1
-rw-r--r--data/icons/24x24/actions/Makefile.am9
-rw-r--r--data/icons/24x24/status/Makefile.am7
-rw-r--r--data/icons/32x32/Makefile.am1
-rw-r--r--data/icons/32x32/actions/Makefile.am8
-rw-r--r--data/icons/32x32/status/Makefile.am7
-rw-r--r--data/icons/Makefile.am15
-rw-r--r--data/icons/scalable/Makefile.am1
-rw-r--r--data/icons/scalable/actions/Makefile.am9
-rw-r--r--data/icons/scalable/status/Makefile.am7
-rw-r--r--data/indicator-session.conf.in11
-rw-r--r--debian/changelog114
-rw-r--r--debian/control23
-rw-r--r--debian/rules8
-rw-r--r--m4/gcov.m486
-rw-r--r--m4/gtest.m463
-rw-r--r--po/CMakeLists.txt3
-rw-r--r--po/LINGUAS99
-rw-r--r--po/POTFILES.in14
-rw-r--r--src/CMakeLists.txt27
-rw-r--r--src/Makefile.am193
-rw-r--r--src/actions.c417
-rw-r--r--src/actions.h135
-rw-r--r--src/backend-dbus/CMakeLists.txt57
-rw-r--r--src/backend-dbus/actions.c978
-rw-r--r--src/backend-dbus/actions.h71
-rw-r--r--src/backend-dbus/backend-dbus.c115
-rw-r--r--src/backend-dbus/backend-dbus.h38
-rw-r--r--src/backend-dbus/com.canonical.indicators.webcredentials.xml82
-rw-r--r--src/backend-dbus/guest.c394
-rw-r--r--src/backend-dbus/guest.h70
-rw-r--r--src/backend-dbus/org.freedesktop.Accounts.User.xml (renamed from src/org.freedesktop.Accounts.User.xml)0
-rw-r--r--src/backend-dbus/org.freedesktop.Accounts.xml (renamed from src/org.freedesktop.Accounts.xml)0
-rw-r--r--src/backend-dbus/org.freedesktop.DisplayManager.Seat.xml (renamed from src/display-manager.xml)0
-rw-r--r--src/backend-dbus/org.freedesktop.login1.Manager.xml (renamed from src/org.freedesktop.login1.Manager.xml)0
-rw-r--r--src/backend-dbus/org.freedesktop.login1.Seat.xml (renamed from src/org.freedesktop.login1.Seat.xml)4
-rw-r--r--src/backend-dbus/org.freedesktop.login1.User.xml (renamed from src/org.freedesktop.login1.User.xml)0
-rw-r--r--src/backend-dbus/org.gnome.ScreenSaver.xml16
-rw-r--r--src/backend-dbus/org.gnome.SessionManager.EndSessionDialog.xml53
-rw-r--r--src/backend-dbus/org.gnome.SessionManager.xml451
-rw-r--r--src/backend-dbus/users.c706
-rw-r--r--src/backend-dbus/users.h73
-rw-r--r--src/backend-dbus/utils.c182
-rw-r--r--src/backend-dbus/utils.h49
-rw-r--r--src/backend.h47
-rw-r--r--src/dialog.c248
-rw-r--r--src/dialog.h64
-rw-r--r--src/gtk-logout-helper.c264
-rw-r--r--src/guest.c190
-rw-r--r--src/guest.h82
-rw-r--r--src/indicator-session.c495
-rw-r--r--src/main.c62
-rw-r--r--src/online-accounts-mgr.c166
-rw-r--r--src/online-accounts-mgr.h50
-rw-r--r--src/org.freedesktop.login1.Session.xml49
-rw-r--r--src/service.c1198
-rw-r--r--src/service.h71
-rw-r--r--src/session-dbus.c282
-rw-r--r--src/session-dbus.h56
-rw-r--r--src/session-dbus.xml20
-rw-r--r--src/session-menu-mgr.c1329
-rw-r--r--src/session-menu-mgr.h55
-rw-r--r--src/session-service.c96
-rw-r--r--src/shared-names.h52
-rw-r--r--src/user-widget.c292
-rw-r--r--src/user-widget.h55
-rw-r--r--src/users-service-dbus.c1046
-rw-r--r--src/users-service-dbus.h96
-rw-r--r--src/users.c198
-rw-r--r--src/users.h156
-rw-r--r--tests/CMakeLists.txt53
-rw-r--r--tests/Makefile.am51
-rw-r--r--tests/Makefile.am.strings38
-rw-r--r--tests/backend-dbus/CMakeLists.txt60
-rw-r--r--tests/backend-dbus/gtest-mock-dbus-fixture.h125
-rw-r--r--tests/backend-dbus/mock-accounts.cc143
-rw-r--r--tests/backend-dbus/mock-accounts.h73
-rw-r--r--tests/backend-dbus/mock-display-manager-seat.cc139
-rw-r--r--tests/backend-dbus/mock-display-manager-seat.h72
-rw-r--r--tests/backend-dbus/mock-end-session-dialog.cc61
-rw-r--r--tests/backend-dbus/mock-end-session-dialog.h67
-rw-r--r--tests/backend-dbus/mock-login1-manager.cc241
-rw-r--r--tests/backend-dbus/mock-login1-manager.h74
-rw-r--r--tests/backend-dbus/mock-login1-seat.cc244
-rw-r--r--tests/backend-dbus/mock-login1-seat.h76
-rw-r--r--tests/backend-dbus/mock-object.cc122
-rw-r--r--tests/backend-dbus/mock-object.h62
-rw-r--r--tests/backend-dbus/mock-screen-saver.cc71
-rw-r--r--tests/backend-dbus/mock-screen-saver.h53
-rw-r--r--tests/backend-dbus/mock-session-manager.cc66
-rw-r--r--tests/backend-dbus/mock-session-manager.h50
-rw-r--r--tests/backend-dbus/mock-user.cc123
-rw-r--r--tests/backend-dbus/mock-user.h57
-rw-r--r--tests/backend-dbus/mock-webcredentials.cc39
-rw-r--r--tests/backend-dbus/mock-webcredentials.h42
-rw-r--r--tests/backend-dbus/test-actions.cc434
-rw-r--r--tests/backend-dbus/test-guest.cc193
-rw-r--r--tests/backend-dbus/test-users.cc381
-rw-r--r--tests/backend-mock-actions.c243
-rw-r--r--tests/backend-mock-actions.h60
-rw-r--r--tests/backend-mock-guest.c133
-rw-r--r--tests/backend-mock-guest.h60
-rw-r--r--tests/backend-mock-users.c183
-rw-r--r--tests/backend-mock-users.h68
-rw-r--r--tests/backend-mock.c44
-rw-r--r--tests/backend-mock.h38
-rw-r--r--tests/com.canonical.indicator.session.backendmock.gschema.xml45
-rw-r--r--tests/com.canonical.indicator.session.gschema.xml32
-rw-r--r--tests/gtest-dbus-fixture.h134
-rw-r--r--tests/indicator-session.service.in4
-rw-r--r--tests/test-service.cc868
-rwxr-xr-xtrim-lcov.py53
136 files changed, 11517 insertions, 5838 deletions
diff --git a/AUTHORS b/AUTHORS
deleted file mode 100644
index a741f25..0000000
--- a/AUTHORS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Generated by Makefile
-
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..69ac18e
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,68 @@
+project (indicator-session C CXX)
+cmake_minimum_required (VERSION 2.8.9)
+
+list (APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
+
+set (PROJECT_VERSION "13.10.1")
+set (PACKAGE ${CMAKE_PROJECT_NAME})
+set (GETTEXT_PACKAGE ${CMAKE_PROJECT_NAME})
+
+option (enable_tests "Build the package's automatic tests." ON)
+option (enable_lcov "Generate lcov code coverage reports." ON)
+
+##
+## GNU standard installation directories
+##
+include (GNUInstallDirs)
+if (EXISTS "/etc/debian_version") # Workaround for libexecdir on debian
+ set (CMAKE_INSTALL_LIBEXECDIR "${CMAKE_INSTALL_LIBDIR}")
+ set (CMAKE_INSTALL_FULL_LIBEXECDIR "${CMAKE_INSTALL_FULL_LIBDIR}")
+endif ()
+set (CMAKE_INSTALL_PKGLIBEXECDIR "${CMAKE_INSTALL_LIBEXECDIR}/${CMAKE_PROJECT_NAME}")
+set (CMAKE_INSTALL_FULL_PKGLIBEXECDIR "${CMAKE_INSTALL_FULL_LIBEXECDIR}/${CMAKE_PROJECT_NAME}")
+
+
+##
+## Check for prerequisites
+##
+find_package (PkgConfig REQUIRED)
+include (FindPkgConfig)
+pkg_check_modules (SERVICE REQUIRED
+ glib-2.0>=2.36
+ gio-unix-2.0>=2.36)
+include_directories (${SERVICE_INCLUDE_DIRS})
+
+set (CC_WARNING_ARGS " -Wall -pedantic -Wextra -Wno-missing-field-initializers")
+
+set (ARCHIVE_NAME ${CMAKE_PROJECT_NAME}-${PROJECT_VERSION})
+add_custom_target (dist
+ COMMAND bzr export --root=${ARCHIVE_NAME} ${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}.tar.gz
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
+
+add_custom_target (clean-coverage
+ COMMAND find ${CMAKE_BINARY_DIR} -name '*.gcda' | xargs rm -f)
+
+add_custom_target (cppcheck COMMAND cppcheck --enable=all -q --error-exitcode=2 --inline-suppr
+ ${CMAKE_SOURCE_DIR}/src
+ ${CMAKE_SOURCE_DIR}/tests)
+
+include_directories (${CMAKE_CURRENT_SOURCE_DIR}/src)
+include_directories (${CMAKE_CURRENT_BINARY_DIR}/src)
+
+# testing & coverage
+if (${enable_tests})
+ set (GTEST_SOURCE_DIR /usr/src/gtest/src)
+ set (GTEST_INCLUDE_DIR ${GTEST_SOURCE_DIR}/..)
+ set (GTEST_LIBS -lpthread)
+ enable_testing ()
+ if (${enable_lcov})
+ include(GCov)
+ endif ()
+endif ()
+
+add_subdirectory (src)
+add_subdirectory (data)
+add_subdirectory (po)
+if (${enable_tests})
+ add_subdirectory (tests)
+endif ()
diff --git a/ChangeLog b/ChangeLog
deleted file mode 100644
index a741f25..0000000
--- a/ChangeLog
+++ /dev/null
@@ -1,2 +0,0 @@
-# Generated by Makefile
-
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..772eae0
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,86 @@
+#
+# Copyright (C) 2013 Canonical Ltd
+#
+# 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 warranty of
+# MERCHANTABILITY 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/>.
+#
+
+Compile-time build dependencies
+-------------------------------
+ - gettext (gettext 0.18.1.1-10ubuntu3 or later)
+ - glib (libglib2.0, 2.35.4 or later)
+ - cmake (cmake, 2.8.9 or later)
+ - gcovr (gcovr, 2.4 or later)
+ - lcov (lcov, 1.9 or later)
+ - google test (libgtest-dev, 1.6.0 or later)
+ - cppcheck (cppcheck)
+
+Runtime DBus dependencies
+-------------------------
+ - com.canonical.indicators.webcredentials
+ - org.freedesktop.Accounts
+ - org.freedesktop.Accounts.User
+ - org.freedesktop.DisplayManager.Seat
+ - org.freedesktop.login1.Manager
+ - org.freedesktop.login1.Seat
+ - org.freedesktop.login1.User
+ - org.gnome.ScreenSaver
+ - org.gnome.SessionManager
+ - org.gnome.SessionManager.EndSessionDialog
+
+Building the code
+-----------------
+The simplest case is:
+ $ cd indicator-session-X.Y.Z
+ $ mkdir build
+ $ cd build
+ $ cmake ..
+ $ make
+
+Running the tests
+-----------------
+ $ cd indicator-session-X.Y.Z
+ $ mkdir build
+ $ cd build
+ $ cmake ..
+ $ make
+ $ make test
+ $ make cppcheck
+
+Generating Test Coverage Reports
+--------------------------------
+ $ cd indicator-session-X.Y.Z
+ $ mkdir build-coverage
+ $ cd build-coverage
+ $ cmake -DCMAKE_BUILD_TYPE=coverage ..
+ $ make
+ $ make coverage-html
+
+Installation
+------------
+
+what gets installed
+LC_ALL=C /usr/bin/intltool-merge -x -u --no-translations com.canonical.indicator.session.gschema.xml.in com.canonical.indicator.session.gschema.xml
+
+
+
+
+FIXME: not tested
+To get files that form part of an installation, run a "make install"
+in the build directory. By default, this installs them in the "install"
+subdirectory of the build directory. If you want to install into a
+different directory, use
+
+$ cmake -DCMAKE_INSTALL_PREFIX=/usr/local # Or wherever...
+$ make release
+$ make install
+
diff --git a/Makefile.am b/Makefile.am
deleted file mode 100644
index 8fb5d77..0000000
--- a/Makefile.am
+++ /dev/null
@@ -1,43 +0,0 @@
-
-SUBDIRS = \
- src \
- data \
- po
-
-if BUILD_TESTS
-SUBDIRS += tests
-# build src first
-tests: src
-endif
-
-EXTRA_DIST = autogen.sh
-
-DISTCHECK_CONFIGURE_FLAGS = --enable-localinstall
-
-dist-hook:
- @if test -d "$(top_srcdir)/.bzr"; \
- then \
- echo Creating ChangeLog && \
- ( cd "$(top_srcdir)" && \
- echo '# Generated by Makefile. Do not edit.'; echo; \
- $(top_srcdir)/missing --run bzr log --gnu-changelog ) > ChangeLog.tmp \
- && mv -f ChangeLog.tmp $(top_distdir)/ChangeLog \
- || (rm -f ChangeLog.tmp; \
- echo Failed to generate ChangeLog >&2 ); \
- else \
- echo Failed to generate ChangeLog: not a branch >&2; \
- fi
- @if test -d "$(top_srcdir)/.bzr"; \
- then \
- echo Creating AUTHORS && \
- ( cd "$(top_srcdir)" && \
- echo '# Generated by Makefile. Do not edit.'; echo; \
- $(top_srcdir)/missing --run bzr log --long --levels=0 | grep -e "^\s*author:" -e "^\s*committer:" | cut -d ":" -f 2 | cut -d "<" -f 1 | sort -u) > AUTHORS.tmp \
- && mv -f AUTHORS.tmp $(top_distdir)/AUTHORS \
- || (rm -f AUTHORS.tmp; \
- echo Failed to generate AUTHORS >&2 ); \
- else \
- echo Failed to generate AUTHORS: not a branch >&2; \
- fi
-
-include $(top_srcdir)/Makefile.am.coverage
diff --git a/Makefile.am.coverage b/Makefile.am.coverage
deleted file mode 100644
index fb97747..0000000
--- a/Makefile.am.coverage
+++ /dev/null
@@ -1,48 +0,0 @@
-
-# Coverage targets
-
-.PHONY: clean-gcno clean-gcda \
- coverage-html generate-coverage-html clean-coverage-html \
- coverage-gcovr generate-coverage-gcovr clean-coverage-gcovr
-
-clean-local: clean-gcno clean-coverage-html clean-coverage-gcovr
-
-if HAVE_GCOV
-
-clean-gcno:
- @echo Removing old coverage instrumentation
- -find -name '*.gcno' -print | xargs -r rm
-
-clean-gcda:
- @echo Removing old coverage results
- -find -name '*.gcda' -print | xargs -r rm
-
-coverage-html: clean-gcda
- -$(MAKE) $(AM_MAKEFLAGS) -k check
- $(MAKE) $(AM_MAKEFLAGS) generate-coverage-html
-
-generate-coverage-html:
- @echo Collecting coverage data
- $(LCOV) --directory $(top_builddir) --capture --output-file coverage.info --no-checksum --compat-libtool
- LANG=C $(GENHTML) --prefix $(top_builddir) --output-directory coveragereport --title "Code Coverage" --legend --show-details coverage.info
-
-clean-coverage-html: clean-gcda
- -$(LCOV) --directory $(top_builddir) -z
- -rm -rf coverage.info coveragereport
-
-if HAVE_GCOVR
-
-coverage-gcovr: clean-gcda
- -$(MAKE) $(AM_MAKEFLAGS) -k check
- $(MAKE) $(AM_MAKEFLAGS) generate-coverage-gcovr
-
-generate-coverage-gcovr:
- @echo Generating coverage GCOVR report
- $(GCOVR) -x -r $(top_builddir) -o $(top_builddir)/coverage.xml
-
-clean-coverage-gcovr: clean-gcda
- -rm -rf $(top_builddir)/coverage.xml
-
-endif # HAVE_GCOVR
-
-endif # HAVE_GCOV
diff --git a/README b/README
index e69de29..addf3bb 100644
--- a/README
+++ b/README
@@ -0,0 +1,47 @@
+Indicator-Session is the the session menu indicator for Unity.
+Its behavior and features are listed at https://wiki.ubuntu.com/SystemMenu.
+
+For instructions on building and running built-in tests, see the INSTALL file.
+
+
+Notes for Client Renderers
+--------------------------
+
+Indicator-Session has two custom menuitems: the Guest and User switchers.
+As per the https://wiki.ubuntu.com/SystemMenu specification, both need four
+visual components: (1) an Active Session Mark, the user's (2) icon, (3) name,
+and (4) a Logged In Mark.
+
+== User menuitems have "x-canonical-type" set to "indicator.user-menu-item"
+ Their four visual components are determined by:
+
+ 1. You can test for the Action Session Mark by checking the action's state.
+ The state is a dicionary whose "active-user" key yields the current
+ session's owner's username. If it matches the username in this menuitem's
+ "target" attribute, show the Active Session Mark.
+
+ 2. The icon is stored in the menuitem's "icon" attribute. If none is set,
+ the client should use a fallback icon such as "avatar-default."
+
+ 3. The name is stored in the menuitem's "label" attribute.
+
+ 4. You can test for the Logged In Mark by checking the action's state.
+ The state is a dictionary whose "logged-in-users" key will give
+ an array of usernames. If the array contains the username in this
+ menuitem's "target" attribute, show the Logged In Mark.
+
+== The Guest switcher has "x-canonical-type" set to "indicator.guest-menu-item"
+ action. Its four visual components are determined by:
+
+ 1. You can test for the Active Session Mark by checking the action's state.
+ The state is a dictionary whose "is-active" key yields a boolean.
+ If the boolean is true, show the Active Session Mark.
+
+ 2. The guest user should use a fallback icon such as "avatar-default."
+
+ 3. The name ("Guest") is stored in the menuitem's "label" attribute.
+
+ 4. You can test for the Logged In Mark by checking the action's state.
+ The state is a dictionary whose "is-logged-in" key yields a boolean.
+ If the boolean is true, show the Logged In Mark.
+
diff --git a/autogen.sh b/autogen.sh
deleted file mode 100755
index 7c01ea7..0000000
--- a/autogen.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/sh
-
-PKG_NAME="indicator-session"
-
-which gnome-autogen.sh || {
- echo "You need gnome-common from GNOME SVN"
- exit 1
-}
-
-USE_GNOME2_MACROS=1 \
-. gnome-autogen.sh
diff --git a/build.sh b/build.sh
new file mode 100755
index 0000000..b69687c
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,18 @@
+#! /bin/bash
+set -e
+
+mkdir -p build
+
+if [ -f "/usr/bin/ninja" ] ; then
+ EXTRA_ARGS="-G Ninja"
+ BUILD_COMMAND="ninja"
+else
+ BUILD_COMMAND="make"
+fi
+
+echo "Using $BUILD_COMMAND to build"
+(
+ cd build
+ cmake .. $EXTRA_ARGS -DCMAKE_INSTALL_PREFIX=../../install -DCMAKE_BUILD_TYPE=Debug
+ $BUILD_COMMAND
+)
diff --git a/cmake/FindIntltool.cmake b/cmake/FindIntltool.cmake
new file mode 100644
index 0000000..45318c4
--- /dev/null
+++ b/cmake/FindIntltool.cmake
@@ -0,0 +1,23 @@
+# FindIntltool.cmake
+#
+# Jim Nelson <jim@yorba.org>
+# Copyright 2012 Yorba Foundation
+
+find_program (INTLTOOL_MERGE_EXECUTABLE intltool-merge)
+
+if (INTLTOOL_MERGE_EXECUTABLE)
+ set (INTLTOOL_MERGE_FOUND TRUE)
+else (INTLTOOL_MERGE_EXECUTABLE)
+ set (INTLTOOL_MERGE_FOUND FALSE)
+endif (INTLTOOL_MERGE_EXECUTABLE)
+
+if (INTLTOOL_MERGE_FOUND)
+ macro (INTLTOOL_MERGE_DESKTOP desktop_id po_dir)
+ add_custom_target (geary.desktop ALL
+ ${INTLTOOL_MERGE_EXECUTABLE} --desktop-style ${CMAKE_SOURCE_DIR}/${po_dir}
+ ${CMAKE_CURRENT_SOURCE_DIR}/${desktop_id}.desktop.in ${desktop_id}.desktop
+ )
+ install (FILES ${CMAKE_CURRENT_BINARY_DIR}/geary.desktop DESTINATION /usr/share/applications)
+ endmacro (INTLTOOL_MERGE_DESKTOP desktop_id po_dir)
+endif (INTLTOOL_MERGE_FOUND)
+
diff --git a/cmake/GCov.cmake b/cmake/GCov.cmake
new file mode 100644
index 0000000..8df10ee
--- /dev/null
+++ b/cmake/GCov.cmake
@@ -0,0 +1,50 @@
+if (CMAKE_BUILD_TYPE MATCHES coverage)
+ set(GCOV_FLAGS "${GCOV_FLAGS} --coverage")
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GCOV_FLAGS}")
+ set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${GCOV_FLAGS}")
+ set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${GCOV_FLAGS}")
+ set(GCOV_LIBS ${GCOV_LIBS} gcov)
+
+ find_program(GCOVR_EXECUTABLE gcovr HINTS ${GCOVR_ROOT} "${GCOVR_ROOT}/bin")
+ if (NOT GCOVR_EXECUTABLE)
+ message(STATUS "Gcovr binary was not found, can not generate XML coverage info.")
+ else ()
+ message(STATUS "Gcovr found, can generate XML coverage info.")
+ add_custom_target (coverage-xml
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+ COMMAND "${GCOVR_EXECUTABLE}" --exclude="test.*" -x -r "${CMAKE_SOURCE_DIR}"
+ --object-directory=${CMAKE_BINARY_DIR} -o coverage.xml)
+ endif()
+
+ find_program(LCOV_EXECUTABLE lcov HINTS ${LCOV_ROOT} "${GCOVR_ROOT}/bin")
+ find_program(GENHTML_EXECUTABLE genhtml HINTS ${GENHTML_ROOT})
+ if (NOT LCOV_EXECUTABLE)
+ message(STATUS "Lcov binary was not found, can not generate HTML coverage info.")
+ else ()
+ if(NOT GENHTML_EXECUTABLE)
+ message(STATUS "Genthml binary not found, can not generate HTML coverage info.")
+ else()
+ message(STATUS "Lcov and genhtml found, can generate HTML coverage info.")
+ add_custom_target (coverage-html
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+ COMMAND "${CMAKE_CTEST_COMMAND}" --force-new-ctest-process --verbose
+ COMMAND "${LCOV_EXECUTABLE}" --directory ${CMAKE_BINARY_DIR} --capture | ${CMAKE_SOURCE_DIR}/trim-lcov.py > dconf-lcov.info
+ COMMAND LANG=C "${GENHTML_EXECUTABLE}" --prefix ${CMAKE_BINARY_DIR} --output-directory lcov-html --legend --show-details dconf-lcov.info
+ COMMAND ${CMAKE_COMMAND} -E echo ""
+ COMMAND ${CMAKE_COMMAND} -E echo "file://${CMAKE_BINARY_DIR}/lcov-html/index.html"
+ COMMAND ${CMAKE_COMMAND} -E echo "")
+ #COMMAND "${LCOV_EXECUTABLE}" --directory ${CMAKE_BINARY_DIR} --capture --output-file coverage.info --no-checksum
+ #COMMAND "${GENHTML_EXECUTABLE}" --prefix ${CMAKE_BINARY_DIR} --output-directory coveragereport --title "Code Coverage" --legend --show-details coverage.info
+ #COMMAND ${CMAKE_COMMAND} -E echo "\\#define foo \\\"bar\\\""
+ #)
+ endif()
+ endif()
+endif()
+
+
+ #$(MAKE) $(AM_MAKEFLAGS) check
+ #lcov --directory $(top_builddir) --capture --test-name dconf | $(top_srcdir)/trim-lcov.py > dconf-lcov.info
+ #LANG=C genhtml --prefix $(top_builddir) --output-directory lcov-html --legend --show-details dconf-lcov.info
+ #@echo
+ #@echo " file://$(abs_top_builddir)/lcov-html/index.html"
+ #@echo
diff --git a/cmake/GdbusCodegen.cmake b/cmake/GdbusCodegen.cmake
new file mode 100644
index 0000000..ddb2995
--- /dev/null
+++ b/cmake/GdbusCodegen.cmake
@@ -0,0 +1,36 @@
+cmake_minimum_required(VERSION 2.6)
+if(POLICY CMP0011)
+ cmake_policy(SET CMP0011 NEW)
+endif(POLICY CMP0011)
+
+find_program(GDBUS_CODEGEN NAMES gdbus-codegen DOC "gdbus-codegen executable")
+if(NOT GDBUS_CODEGEN)
+ message(FATAL_ERROR "Excutable gdbus-codegen not found")
+endif()
+
+macro(add_gdbus_codegen outfiles name prefix service_xml)
+ add_custom_command(
+ OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${name}.h" "${CMAKE_CURRENT_BINARY_DIR}/${name}.c"
+ COMMAND "${GDBUS_CODEGEN}"
+ --interface-prefix "${prefix}"
+ --generate-c-code "${name}"
+ "${service_xml}"
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+ DEPENDS ${ARGN} "${service_xml}"
+ )
+ list(APPEND ${outfiles} "${CMAKE_CURRENT_BINARY_DIR}/${name}.c")
+endmacro(add_gdbus_codegen)
+
+macro(add_gdbus_codegen_with_namespace outfiles name prefix namespace service_xml)
+ add_custom_command(
+ OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${name}.h" "${CMAKE_CURRENT_BINARY_DIR}/${name}.c"
+ COMMAND "${GDBUS_CODEGEN}"
+ --interface-prefix "${prefix}"
+ --generate-c-code "${name}"
+ --c-namespace "${namespace}"
+ "${service_xml}"
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+ DEPENDS ${ARGN} "${service_xml}"
+ )
+ list(APPEND ${outfiles} "${CMAKE_CURRENT_BINARY_DIR}/${name}.c")
+endmacro(add_gdbus_codegen_with_namespace)
diff --git a/cmake/Translations.cmake b/cmake/Translations.cmake
new file mode 100644
index 0000000..178bded
--- /dev/null
+++ b/cmake/Translations.cmake
@@ -0,0 +1,41 @@
+# Translations.cmake, CMake macros written for Marlin, feel free to re-use them
+
+macro(add_translations_directory NLS_PACKAGE)
+ add_custom_target (i18n ALL)
+ find_program (MSGFMT_EXECUTABLE msgfmt)
+ file (GLOB PO_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.po)
+ foreach (PO_INPUT ${PO_FILES})
+ get_filename_component (PO_INPUT_BASE ${PO_INPUT} NAME_WE)
+ set (MO_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${PO_INPUT_BASE}.mo)
+ add_custom_command (TARGET i18n COMMAND ${MSGFMT_EXECUTABLE} -o ${MO_OUTPUT} ${PO_INPUT})
+
+ install (FILES ${MO_OUTPUT} DESTINATION
+ ${CMAKE_INSTALL_LOCALEDIR}/LC_MESSAGES
+ RENAME ${NLS_PACKAGE}.mo)
+ endforeach (PO_INPUT ${PO_FILES})
+endmacro(add_translations_directory)
+
+
+macro(add_translations_catalog NLS_PACKAGE)
+ add_custom_target (pot COMMENT “Building translation catalog.”)
+ find_program (XGETTEXT_EXECUTABLE xgettext)
+
+
+ set(C_SOURCE "")
+
+ foreach(FILES_INPUT ${ARGN})
+ file (GLOB_RECURSE SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/${FILES_INPUT}/*.c)
+ foreach(C_FILE ${SOURCE_FILES})
+ set(C_SOURCE ${C_SOURCE} ${C_FILE})
+ endforeach()
+ file (GLOB_RECURSE SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/${FILES_INPUT}/*.vala)
+ foreach(C_FILE ${SOURCE_FILES})
+ set(C_SOURCE ${C_SOURCE} ${C_FILE})
+ endforeach()
+ endforeach()
+
+ add_custom_command (TARGET pot COMMAND
+ ${XGETTEXT_EXECUTABLE} -d ${NLS_PACKAGE} -o ${CMAKE_CURRENT_SOURCE_DIR}/${NLS_PACKAGE}.pot
+ ${VALA_SOURCE} ${C_SOURCE} --keyword="_" --keyword="N_" --from-code=UTF-8
+ )
+endmacro()
diff --git a/cmake/UseGSettings.cmake b/cmake/UseGSettings.cmake
new file mode 100644
index 0000000..3b61523
--- /dev/null
+++ b/cmake/UseGSettings.cmake
@@ -0,0 +1,23 @@
+# GSettings.cmake, CMake macros written for Marlin, feel free to re-use them.
+
+macro(add_schema SCHEMA_NAME)
+
+ set(PKG_CONFIG_EXECUTABLE pkg-config)
+ set(GSETTINGS_DIR "${CMAKE_INSTALL_FULL_DATAROOTDIR}/glib-2.0/schemas")
+
+ # Run the validator and error if it fails
+ execute_process (COMMAND ${PKG_CONFIG_EXECUTABLE} gio-2.0 --variable glib_compile_schemas OUTPUT_VARIABLE _glib_compile_schemas OUTPUT_STRIP_TRAILING_WHITESPACE)
+ execute_process (COMMAND ${_glib_compile_schemas} --dry-run --schema-file=${SCHEMA_NAME} ERROR_VARIABLE _schemas_invalid OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+ if (_schemas_invalid)
+ message (SEND_ERROR "Schema validation error: ${_schemas_invalid}")
+ endif (_schemas_invalid)
+
+ # Actually install and recomple schemas
+ message (STATUS "${GSETTINGS_DIR} is the GSettings install dir")
+ install (FILES ${SCHEMA_NAME} DESTINATION ${GSETTINGS_DIR} OPTIONAL)
+
+ install (CODE "message (STATUS \"Compiling GSettings schemas\")")
+ install (CODE "execute_process (COMMAND ${_glib_compile_schemas} ${GSETTINGS_DIR})")
+endmacro()
+
diff --git a/configure.ac b/configure.ac
deleted file mode 100644
index c3424a6..0000000
--- a/configure.ac
+++ /dev/null
@@ -1,220 +0,0 @@
-AC_INIT([indicator-session],[12.10.4])
-AC_CONFIG_SRCDIR([src/session-menu-mgr.c])
-AM_INIT_AUTOMAKE([check-news])
-AC_CONFIG_HEADERS([config.h])
-
-AM_MAINTAINER_MODE
-
-GLIB_GSETTINGS
-
-IT_PROG_INTLTOOL([0.35.0])
-
-AC_ISC_POSIX
-AC_PROG_CC
-AC_PROG_CXX
-AM_PROG_CC_C_O
-AC_HEADER_STDC
-LT_INIT
-
-AC_SUBST(VERSION)
-AC_CONFIG_MACRO_DIR([m4])
-
-m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
-
-###########################
-# Dependencies
-###########################
-
-GIO_REQUIRED_VERSION=2.33
-GLIB_REQUIRED_VERSION=2.35.4
-GTK_REQUIRED_VERSION=3.0
-INDICATOR_REQUIRED_VERSION=0.3.19
-DBUSMENUGTK_REQUIRED_VERSION=0.5.90
-DBUSTEST_REQUIRED_VERSION=0.0.5
-DBUSMENUGLIB_REQUIRED_VERSION=0.1.1
-
-
-PKG_CHECK_MODULES(APPLET, glib-2.0 >= $GLIB_REQUIRED_VERSION
- gtk+-3.0 >= $GTK_REQUIRED_VERSION
- indicator3-0.4 >= $INDICATOR_REQUIRED_VERSION
- dbusmenu-gtk3-0.4 >= $DBUSMENUGTK_REQUIRED_VERSION)
-AC_SUBST(APPLET_CFLAGS)
-AC_SUBST(APPLET_LIBS)
-
-
-PKG_CHECK_MODULES(SESSIONSERVICE, glib-2.0 >= $GLIB_REQUIRED_VERSION
- dbusmenu-glib-0.4 >= $DBUSMENUGLIB_REQUIRED_VERSION
- dbusmenu-gtk3-0.4 >= $DBUSMENUGTK_REQUIRED_VERSION
- gio-unix-2.0
- indicator3-0.4 >= $INDICATOR_REQUIRED_VERSION)
-AC_SUBST(SESSIONERVICE_CFLAGS)
-AC_SUBST(SESSIONERVICE_LIBS)
-
-AC_SUBST(GUDEV_CFLAGS)
-AC_SUBST(GUDEV_LIBS)
-
-###########################
-# GTK Logout Helper
-###########################
-
-AC_ARG_ENABLE([gtklogouthelper],
- [AS_HELP_STRING([--enable-gtklogouthelper], [enable GTK Logout Helper])],
- [],
- [enable_gtklogouthelper=auto])
-
-if test x"$enable_gtklogouthelper" != x"no" ; then
- PKG_CHECK_MODULES([GTKLOGOUTHELPER],
- [gtk+-3.0 >= $GTK_REQUIRED_VERSION],
- [have_gtklogouthelper=yes],
- [have_gtklogouthelper=no])
- if test x${have_gtklogouthelper} = xyes; then
- AC_DEFINE(HAVE_GTKLOGOUTHELPER, 1, [Define to 1 to enable GTK Logout Helper])
- fi
- if test x${enable_gtklogouthelper} = xyes && test x${have_gtklogouthelper} = xno; then
- AC_MSG_ERROR([GTK Logout Helper configured but polkit-gobject not found])
- fi
-else
- have_gtklogouthelper=no
-fi
-AM_CONDITIONAL(BUILD_GTKLOGOUTHELPER, test x${have_gtklogouthelper} = xyes)
-
-AC_SUBST(GTKLOGOUTHELPER_CFLAGS)
-AC_SUBST(GTKLOGOUTHELPER_LIBS)
-
-###########################
-# Check to see if we're local
-###########################
-
-with_localinstall="no"
-AC_ARG_ENABLE(localinstall, AS_HELP_STRING([--enable-localinstall], [install all of the files localy instead of system directories (for distcheck)]), with_localinstall=$enableval, with_localinstall=no)
-
-###########################
-# Indicator Info
-###########################
-
-if test "x$with_localinstall" = "xyes"; then
- INDICATORDIR="${libdir}/indicators/2/"
- INDICATORICONSDIR="${datadir}/indicator-applet/icons/"
-elif test "x$with_gtk" = x2; then
- INDICATORDIR=`$PKG_CONFIG --variable=indicatordir indicator-0.4`
- INDICATORICONSDIR=`$PKG_CONFIG --variable=iconsdir indicator-0.4`
-else
- INDICATORDIR=`$PKG_CONFIG --variable=indicatordir indicator3-0.4`
- INDICATORICONSDIR=`$PKG_CONFIG --variable=iconsdir indicator3-0.4`
-fi
-AC_SUBST(INDICATORDIR)
-AC_SUBST(INDICATORICONSDIR)
-
-###########################
-# Google Test framework
-###########################
-
-AC_ARG_ENABLE([tests],
- [AS_HELP_STRING([--disable-tests], [Disable test scripts and tools (default=auto)])],
- [enable_tests=${enableval}],
- [enable_tests=auto])
-if test "x$enable_tests" != "xno"; then
- m4_include([m4/gtest.m4])
- CHECK_GTEST
- CHECK_XORG_GTEST
- if test "x$enable_tests" = "xauto"; then
- enable_tests=${have_gtest}
- elif test "x$enable_tests" = "xyes" && test "x$have_gtest" != "xyes"; then
- AC_MSG_ERROR([tests were requested but gtest is not installed.])
- fi
- if test "x$enable_tests" = "xyes"; then
- PKG_CHECK_MODULES([TEST_SERVICE],[glib-2.0 >= $GLIB_REQUIRED_VERSION
- gio-2.0 >= $GIO_REQUIRED_VERSION],
- [enable_tests="yes"],
- [enable_tests="no"])
- fi
-fi
-AM_CONDITIONAL([BUILD_TESTS],[test "x$enable_tests" = "xyes"])
-AC_SUBST([TEST_SERVICE_CFLAGS])
-AC_SUBST([TEST_SERVICE_LIBS])
-
-##############################
-# Custom Junk
-##############################
-
-AC_DEFUN([AC_DEFINE_PATH], [
- test "x$prefix" = xNONE && prefix="$ac_default_prefix"
- test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
- ac_define_path=`eval echo [$]$2`
- ac_define_path=`eval echo [$]ac_define_path`
- $1="$ac_define_path"
- AC_SUBST($1)
- ifelse($3, ,
- AC_DEFINE_UNQUOTED($1, "$ac_define_path"),
- AC_DEFINE_UNQUOTED($1, "$ac_define_path", $3))
-])
-
-###########################
-# Internationalization
-###########################
-
-GETTEXT_PACKAGE=indicator-session
-AC_SUBST(GETTEXT_PACKAGE)
-AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE", [Name of the default get text domain])
-AC_DEFINE_PATH(GNOMELOCALEDIR, "${datadir}/locale", [locale directory])
-
-AM_GLIB_GNU_GETTEXT
-
-###########################
-# gcov coverage reporting
-###########################
-
-m4_include([m4/gcov.m4])
-AC_TDD_GCOV
-AM_CONDITIONAL([HAVE_GCOV], [test "x$ac_cv_check_gcov" = xyes])
-AM_CONDITIONAL([HAVE_LCOV], [test "x$ac_cv_check_lcov" = xyes])
-AM_CONDITIONAL([HAVE_GCOVR], [test "x$ac_cv_check_gcovr" = xyes])
-AC_SUBST(COVERAGE_CFLAGS)
-AC_SUBST(COVERAGE_LDFLAGS)
-
-###########################
-# Files
-###########################
-
-AC_CONFIG_FILES([
-Makefile
-src/Makefile
-data/Makefile
-data/icons/Makefile
-data/icons/16x16/Makefile
-data/icons/16x16/actions/Makefile
-data/icons/16x16/status/Makefile
-data/icons/22x22/Makefile
-data/icons/22x22/actions/Makefile
-data/icons/22x22/status/Makefile
-data/icons/24x24/Makefile
-data/icons/24x24/actions/Makefile
-data/icons/24x24/status/Makefile
-data/icons/32x32/Makefile
-data/icons/32x32/actions/Makefile
-data/icons/32x32/status/Makefile
-data/icons/scalable/Makefile
-data/icons/scalable/actions/Makefile
-data/icons/scalable/status/Makefile
-data/extra-sessions/Makefile
-tests/Makefile
-tests/indicator-session.service
-po/Makefile.in
-])
-
-AC_OUTPUT
-
-###########################
-# Results
-###########################
-
-AC_MSG_NOTICE([
-
-SUS Indicator Configuration:
-
- Prefix: $prefix
- Indicator Dir: $INDICATORDIR
- Logout Helper: $have_gtklogouthelper
- Unit Tests: $enable_tests
- Coverage reporting: $use_gcov
-])
diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt
new file mode 100644
index 0000000..d52d388
--- /dev/null
+++ b/data/CMakeLists.txt
@@ -0,0 +1,74 @@
+include (UseGSettings)
+
+##
+## GSettings schema
+##
+
+set (SCHEMA_NAME "com.canonical.indicator.session.gschema.xml")
+set (SCHEMA_FILE "${CMAKE_CURRENT_BINARY_DIR}/${SCHEMA_NAME}")
+set (SCHEMA_FILE_IN "${CMAKE_CURRENT_SOURCE_DIR}/${SCHEMA_NAME}.in")
+
+# generate the .xml file using intltool
+set (ENV{LC_ALL} "C")
+execute_process (COMMAND intltool-merge -quiet --xml-style --utf8 --no-translations "${SCHEMA_FILE_IN}" "${SCHEMA_FILE}")
+
+# let UseGSettings do the rest
+add_schema (${SCHEMA_FILE})
+
+
+##
+## Upstart Config File
+##
+
+# where to install
+set (UPSTART_JOB_DIR "${CMAKE_INSTALL_FULL_DATADIR}/upstart/sessions")
+message (STATUS "${UPSTART_JOB_DIR} is the Upstart Job install dir")
+
+set (UPSTART_JOB_NAME "${CMAKE_PROJECT_NAME}.conf")
+set (UPSTART_JOB_FILE "${CMAKE_CURRENT_BINARY_DIR}/${UPSTART_JOB_NAME}")
+set (UPSTART_JOB_FILE_IN "${CMAKE_CURRENT_SOURCE_DIR}/${UPSTART_JOB_NAME}.in")
+
+# build it
+set (pkglibexecdir "${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}")
+configure_file ("${UPSTART_JOB_FILE_IN}" "${UPSTART_JOB_FILE}")
+
+# install it
+install (FILES "${UPSTART_JOB_FILE}"
+ DESTINATION "${UPSTART_JOB_DIR}")
+
+
+##
+## Unity Indicator File
+##
+
+# where to install
+set (UNITY_INDICATOR_DIR "${CMAKE_INSTALL_FULL_DATAROOTDIR}/unity/indicators")
+message (STATUS "${UNITY_INDICATOR_DIR} is the Unity Indicator install dir")
+
+set (UNITY_INDICATOR_NAME "com.canonical.indicator.session")
+set (UNITY_INDICATOR_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${UNITY_INDICATOR_NAME}")
+
+install (FILES "${UNITY_INDICATOR_FILE}"
+ DESTINATION "${UNITY_INDICATOR_DIR}")
+
+
+##
+## Icons
+##
+
+# where to install
+set (ICON_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/icons/hicolor")
+message (STATUS "${ICON_DIR} is the Icon install dir")
+
+install (DIRECTORY icons
+ icons/16x16
+ icons/22x22
+ icons/24x24
+ icons/32x32
+ icons/scalable
+ DESTINATION "${ICON_DIR}"
+ FILES_MATCHING PATTERN "*.png" PATTERN "*.svg")
+
+install(CODE "execute_process (COMMAND gtk-update-icon-cache -t -f ${ICON_DIR})"
+ CODE "message (STATUS \"Updating icon cache\")")
+
diff --git a/data/Makefile.am b/data/Makefile.am
deleted file mode 100644
index a1a1c3b..0000000
--- a/data/Makefile.am
+++ /dev/null
@@ -1,27 +0,0 @@
-SUBDIRS = \
- icons \
- extra-sessions
-
-upstart_jobsdir = $(datadir)/upstart/sessions/
-upstart_jobs_in_files = indicator-session.conf.in
-upstart_jobs_DATA = $(upstart_jobs_in_files:.conf.in=.conf)
-
-%.conf: %.conf.in
- sed -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@
-
-@INTLTOOL_SCHEMAS_RULE@
-@INTLTOOL_XML_NOMERGE_RULE@
-
-@GSETTINGS_RULES@
-gsettings_SCHEMAS = com.canonical.indicator.session.gschema.xml
-
-convertdir = $(datadir)/GConf/gsettings
-dist_convert_DATA = indicator-session.convert
-
-EXTRA_DIST = \
- $(upstart_jobs_in_files) \
- $(gsettings_SCHEMAS:.xml=.xml.in)
-
-CLEANFILES = \
- $(upstart_jobs_DATA) \
- $(gsettings_SCHEMAS)
diff --git a/data/com.canonical.indicator.session b/data/com.canonical.indicator.session
new file mode 100644
index 0000000..7b1b39e
--- /dev/null
+++ b/data/com.canonical.indicator.session
@@ -0,0 +1,11 @@
+[Indicator Service]
+Name=indicator-session
+ObjectPath=/com/canonical/indicator/session
+Position=10
+
+[desktop]
+ObjectPath=/com/canonical/indicator/session/desktop
+
+[desktop_greeter]
+ObjectPath=/com/canonical/indicator/session/desktop_greeter
+
diff --git a/data/icons/16x16/Makefile.am b/data/icons/16x16/Makefile.am
deleted file mode 100644
index c163076..0000000
--- a/data/icons/16x16/Makefile.am
+++ /dev/null
@@ -1 +0,0 @@
-SUBDIRS = actions status
diff --git a/data/icons/16x16/actions/Makefile.am b/data/icons/16x16/actions/Makefile.am
deleted file mode 100644
index 5aa276c..0000000
--- a/data/icons/16x16/actions/Makefile.am
+++ /dev/null
@@ -1,9 +0,0 @@
-
-iconsdir = $(INDICATORICONSDIR)/hicolor/16x16/actions
-
-icons_DATA = \
- system-shutdown.png \
- system-restart.png \
- system-log-out.png
-
-EXTRA_DIST = $(icons_DATA)
diff --git a/data/icons/16x16/status/Makefile.am b/data/icons/16x16/status/Makefile.am
deleted file mode 100644
index c0af60e..0000000
--- a/data/icons/16x16/status/Makefile.am
+++ /dev/null
@@ -1,7 +0,0 @@
-
-iconsdir = $(INDICATORICONSDIR)/hicolor/16x16/status
-
-icons_DATA = \
- account-logged-in.png
-
-EXTRA_DIST = $(icons_DATA)
diff --git a/data/icons/22x22/Makefile.am b/data/icons/22x22/Makefile.am
deleted file mode 100644
index c163076..0000000
--- a/data/icons/22x22/Makefile.am
+++ /dev/null
@@ -1 +0,0 @@
-SUBDIRS = actions status
diff --git a/data/icons/22x22/actions/Makefile.am b/data/icons/22x22/actions/Makefile.am
deleted file mode 100644
index 73cdd09..0000000
--- a/data/icons/22x22/actions/Makefile.am
+++ /dev/null
@@ -1,9 +0,0 @@
-
-iconsdir = $(INDICATORICONSDIR)/hicolor/22x22/actions
-
-icons_DATA = \
- system-shutdown.png \
- system-restart.png \
- system-log-out.png
-
-EXTRA_DIST = $(icons_DATA)
diff --git a/data/icons/22x22/status/Makefile.am b/data/icons/22x22/status/Makefile.am
deleted file mode 100644
index 1ce0fa1..0000000
--- a/data/icons/22x22/status/Makefile.am
+++ /dev/null
@@ -1,7 +0,0 @@
-
-iconsdir = $(INDICATORICONSDIR)/hicolor/22x22/status
-
-icons_DATA = \
- account-logged-in.png
-
-EXTRA_DIST = $(icons_DATA)
diff --git a/data/icons/24x24/Makefile.am b/data/icons/24x24/Makefile.am
deleted file mode 100644
index c163076..0000000
--- a/data/icons/24x24/Makefile.am
+++ /dev/null
@@ -1 +0,0 @@
-SUBDIRS = actions status
diff --git a/data/icons/24x24/actions/Makefile.am b/data/icons/24x24/actions/Makefile.am
deleted file mode 100644
index 55c63d1..0000000
--- a/data/icons/24x24/actions/Makefile.am
+++ /dev/null
@@ -1,9 +0,0 @@
-
-iconsdir = $(INDICATORICONSDIR)/hicolor/24x24/actions
-
-icons_DATA = \
- system-shutdown.png \
- system-restart.png \
- system-log-out.png
-
-EXTRA_DIST = $(icons_DATA)
diff --git a/data/icons/24x24/status/Makefile.am b/data/icons/24x24/status/Makefile.am
deleted file mode 100644
index 4fa065a..0000000
--- a/data/icons/24x24/status/Makefile.am
+++ /dev/null
@@ -1,7 +0,0 @@
-
-iconsdir = $(INDICATORICONSDIR)/hicolor/24x24/status
-
-icons_DATA = \
- account-logged-in.png
-
-EXTRA_DIST = $(icons_DATA)
diff --git a/data/icons/32x32/Makefile.am b/data/icons/32x32/Makefile.am
deleted file mode 100644
index c163076..0000000
--- a/data/icons/32x32/Makefile.am
+++ /dev/null
@@ -1 +0,0 @@
-SUBDIRS = actions status
diff --git a/data/icons/32x32/actions/Makefile.am b/data/icons/32x32/actions/Makefile.am
deleted file mode 100644
index 74c2c8d..0000000
--- a/data/icons/32x32/actions/Makefile.am
+++ /dev/null
@@ -1,8 +0,0 @@
-
-iconsdir = $(INDICATORICONSDIR)/hicolor/32x32/actions
-
-icons_DATA = \
- system-restart.png \
- system-log-out.png
-
-EXTRA_DIST = $(icons_DATA)
diff --git a/data/icons/32x32/status/Makefile.am b/data/icons/32x32/status/Makefile.am
deleted file mode 100644
index b852725..0000000
--- a/data/icons/32x32/status/Makefile.am
+++ /dev/null
@@ -1,7 +0,0 @@
-
-iconsdir = $(INDICATORICONSDIR)/hicolor/32x32/status
-
-icons_DATA = \
- account-logged-in.png
-
-EXTRA_DIST = $(icons_DATA)
diff --git a/data/icons/Makefile.am b/data/icons/Makefile.am
deleted file mode 100644
index 7394c73..0000000
--- a/data/icons/Makefile.am
+++ /dev/null
@@ -1,15 +0,0 @@
-SUBDIRS = scalable 16x16 22x22 24x24 32x32
-
-gtk_update_icon_cache = gtk-update-icon-cache -f -t $(INDICATORICONSDIR)/hicolor
-
-install-data-hook: update-icon-cache
-uninstall-hook: update-icon-cache
-update-icon-cache:
- @-if test -z "$(DESTDIR)"; then \
- echo "Updating Gtk icon cache."; \
- $(gtk_update_icon_cache); \
- else \
- echo "*** Icon cache not updated. After (un)install, run this:"; \
- echo "*** $(gtk_update_icon_cache)"; \
- fi
-
diff --git a/data/icons/scalable/Makefile.am b/data/icons/scalable/Makefile.am
deleted file mode 100644
index c163076..0000000
--- a/data/icons/scalable/Makefile.am
+++ /dev/null
@@ -1 +0,0 @@
-SUBDIRS = actions status
diff --git a/data/icons/scalable/actions/Makefile.am b/data/icons/scalable/actions/Makefile.am
deleted file mode 100644
index 39b4177..0000000
--- a/data/icons/scalable/actions/Makefile.am
+++ /dev/null
@@ -1,9 +0,0 @@
-
-iconsdir = $(INDICATORICONSDIR)/hicolor/scalable/actions
-
-icons_DATA = \
- system-restart.svg \
- system-log-out.svg \
- system-shutdown.svg
-
-EXTRA_DIST = $(icons_DATA)
diff --git a/data/icons/scalable/status/Makefile.am b/data/icons/scalable/status/Makefile.am
deleted file mode 100644
index cb006e2..0000000
--- a/data/icons/scalable/status/Makefile.am
+++ /dev/null
@@ -1,7 +0,0 @@
-
-iconsdir = $(INDICATORICONSDIR)/hicolor/scalable/status
-
-icons_DATA = \
- account-logged-in.svg
-
-EXTRA_DIST = $(icons_DATA)
diff --git a/data/indicator-session.conf.in b/data/indicator-session.conf.in
index 3a512df..d5c4c8c 100644
--- a/data/indicator-session.conf.in
+++ b/data/indicator-session.conf.in
@@ -1,15 +1,14 @@
description "Indicator Session Service"
-author "Ted Gould <ted@canonical.com>"
-# NOTE: Limiting only to Unity 7 right now as it's still using
-# dbusmenu. That can be lifted after it is ported to GMenu
+# NOTE: Limiting only to Unity 7 right now as it's only needed
+# on the desktop
-start on indicators-loaded and xsession SESSION=ubuntu
-stop on desktop-end
+start on (indicators-loaded or indicator-services-start) and xsession SESSION=ubuntu
+stop on desktop-end or indicator-services-end
env G_MESSAGES_DEBUG=all
export G_MESSAGES_DEBUG
respawn
-exec @libexecdir@/indicator-session-service
+exec @pkglibexecdir@/indicator-session-service
diff --git a/debian/changelog b/debian/changelog
index 32a029d..d4f1a25 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,117 @@
+indicator-session (12.10.5+13.10.20130823-0ubuntu1) saucy; urgency=low
+
+ [ Charles Kerr ]
+ * use an a{sv}, rather than the obsoleted (sssb), for the root
+ action's state.
+
+ [ Ubuntu daily release ]
+ * Automatic snapshot from revision 410
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Fri, 23 Aug 2013 22:07:25 +0000
+
+indicator-session (12.10.5+13.10.20130822-0ubuntu1) saucy; urgency=low
+
+ [ Charles Kerr ]
+ * remove deprecated GSimpleActionGroup API use.
+
+ [ Ubuntu daily release ]
+ * Automatic snapshot from revision 408
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 22 Aug 2013 20:30:19 +0000
+
+indicator-session (12.10.5+13.10.20130821-0ubuntu1) saucy; urgency=low
+
+ [ Charles Kerr ]
+ * Lock the current session before switching to the guest session. This
+ is done by modifying IndicatorSessionActionsDbus::switch_to_guest()
+ to call org.gnome.ScreenSaver's Lock function before switching to
+ the guest session. (LP: #1205273)
+
+ [ Ritesh Khadgaray ]
+ * Lock the current session before switching to the guest session. This
+ is done by modifying IndicatorSessionActionsDbus::switch_to_guest()
+ to call org.gnome.ScreenSaver's Lock function before switching to
+ the guest session. (LP: #1205273)
+
+ [ Ubuntu daily release ]
+ * Automatic snapshot from revision 406
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 21 Aug 2013 06:07:34 +0000
+
+indicator-session (12.10.5+13.10.20130812-0ubuntu1) saucy; urgency=low
+
+ [ Charles Kerr ]
+ * Ensure that GCov CFLAGS & LIBS are set before cmake traverses into
+ the src/ directory.
+
+ [ Ubuntu daily release ]
+ * Automatic snapshot from revision 404
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 12 Aug 2013 18:39:36 +0000
+
+indicator-session (12.10.5+13.10.20130717-0ubuntu1) saucy; urgency=low
+
+ [ Charles Kerr ]
+ * Fix a test failure on PPC architectures.
+
+ [ Ubuntu daily release ]
+ * Automatic snapshot from revision 402
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 17 Jul 2013 02:01:46 +0000
+
+indicator-session (12.10.5+13.10.20130716-0ubuntu1) saucy; urgency=low
+
+ [ Charles Kerr ]
+ * This is the GMenu, login1 version of indicator-session. This
+ resubmission removes the prerequisite branch because the entire diff
+ is contained in this ng-login1 branch.
+
+ [ Lars Uebernickel ]
+ * This is the GMenu, login1 version of indicator-session. This
+ resubmission removes the prerequisite branch because the entire diff
+ is contained in this ng-login1 branch.
+
+ [ Łukasz 'sil2100' Zemczak ]
+ * Add the python build-dep, as gdbus-codegen needs it to work
+ properly.
+
+ [ Ubuntu daily release ]
+ * Automatic snapshot from revision 400
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 16 Jul 2013 02:02:05 +0000
+
+indicator-session (12.10.5+13.10.20130703.1-0ubuntu1) saucy; urgency=low
+
+ [ Lars Uebernickel ]
+ * session-menu-mgr.c: don't leak user menu items. (LP: #1195595)
+ * I know the new session indicator is coming soon, but I think having
+ this for the next week or so is worthwhile anyway. Not having the
+ session indicator on the trailing edge of the panel is unnerving :).
+
+ [ Ubuntu daily release ]
+ * Automatic snapshot from revision 397
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 03 Jul 2013 14:17:07 +0000
+
+indicator-session (12.10.5daily13.06.19-0ubuntu2) saucy; urgency=low
+
+ * Backport a fix for high resources usage issue, thanks Lars (lp: #1195595)
+
+ -- Sebastien Bacher <seb128@ubuntu.com> Mon, 01 Jul 2013 13:05:15 +0200
+
+indicator-session (12.10.5daily13.06.19-0ubuntu1) saucy; urgency=low
+
+ [ Mathieu Trudel-Lapierre ]
+ * Fix linking with pthreads for gtest.
+
+ [ Jeremy Bicha ]
+ * recommend gnome-screensaver for the Lock Screen feature.
+
+ [ Ubuntu daily release ]
+ * Automatic snapshot from revision 393
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 19 Jun 2013 02:01:25 +0000
+
indicator-session (12.10.5daily13.05.06.1-0ubuntu1) saucy; urgency=low
[ Iain Lane ]
diff --git a/debian/control b/debian/control
index f65df35..9772a81 100644
--- a/debian/control
+++ b/debian/control
@@ -2,21 +2,14 @@ Source: indicator-session
Section: gnome
Priority: optional
Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
-Build-Depends: debhelper (>= 9),
- dh-autoreconf,
+Build-Depends: cmake,
+ dbus,
+ debhelper (>= 9),
dh-translations,
intltool,
- gnome-common,
+ libglib2.0-dev (>= 2.36),
libgtest-dev,
- libxorg-gtest-dev,
- libdbustest1-dev,
- dbus-test-runner,
- libgtk-3-dev,
- libglib2.0-dev (>= 2.35.4),
- gnome-doc-utils,
- libindicator3-dev (>= 0.3.90),
- libdbusmenu-glib-dev (>= 0.5.90),
- libdbusmenu-gtk3-dev (>= 0.5.90),
+ python,
Standards-Version: 3.9.3
Homepage: https://launchpad.net/indicator-session
# If you aren't a member of ~indicator-applet-developers but need to upload
@@ -32,8 +25,10 @@ Depends: ${shlibs:Depends},
${misc:Depends},
systemd-services,
gnome-settings-daemon,
-Recommends: indicator-applet (>= 0.2) | indicator-renderer,
-Suggests: lightdm
+Recommends: indicator-applet (>= 0.2) | indicator-renderer,
+ gnome-screensaver
+Suggests: lightdm,
+ zenity
Description: indicator showing session management, status and user switching
This indicator is designed to be placed on the right side of a panel and
give the user easy control for changing their instant message status.
diff --git a/debian/rules b/debian/rules
index f679369..e64aff1 100644
--- a/debian/rules
+++ b/debian/rules
@@ -3,19 +3,13 @@
export DPKG_GENSYMBOLS_CHECK_LEVEL=4
%:
- dh $@ --with translations,autoreconf
-
-override_dh_autoreconf:
- NOCONFIGURE=1 dh_autoreconf ./autogen.sh
+ dh $@ --with translations
override_dh_install:
find debian/indicator-session/usr/lib -name *.la -delete
find debian/indicator-session/usr/lib -name *.a -delete
dh_install --fail-missing
-override_dh_auto_configure:
- dh_auto_configure -- --libexecdir="\$${prefix}/lib/indicator-session"
-
# Hack as it seems it's not possible to easy run that under dbus-test-runner
override_dh_auto_test:
env -u LD_PRELOAD dh_auto_test
diff --git a/m4/gcov.m4 b/m4/gcov.m4
deleted file mode 100644
index 3163584..0000000
--- a/m4/gcov.m4
+++ /dev/null
@@ -1,86 +0,0 @@
-# Checks for existence of coverage tools:
-# * gcov
-# * lcov
-# * genhtml
-# * gcovr
-#
-# Sets ac_cv_check_gcov to yes if tooling is present
-# and reports the executables to the variables LCOV, GCOVR and GENHTML.
-AC_DEFUN([AC_TDD_GCOV],
-[
- AC_ARG_ENABLE(gcov,
- AS_HELP_STRING([--enable-gcov],
- [enable coverage testing with gcov]),
- [use_gcov=$enableval], [use_gcov=no])
-
- if test "x$use_gcov" = "xyes"; then
- # we need gcc:
- if test "$GCC" != "yes"; then
- AC_MSG_ERROR([GCC is required for --enable-gcov])
- fi
-
- # Check if ccache is being used
- AC_CHECK_PROG(SHTOOL, shtool, shtool)
- case `$SHTOOL path $CC` in
- *ccache*[)] gcc_ccache=yes;;
- *[)] gcc_ccache=no;;
- esac
-
- if test "$gcc_ccache" = "yes" && (test -z "$CCACHE_DISABLE" || test "$CCACHE_DISABLE" != "1"); then
- AC_MSG_ERROR([ccache must be disabled when --enable-gcov option is used. You can disable ccache by setting environment variable CCACHE_DISABLE=1.])
- fi
-
- lcov_version_list="1.6 1.7 1.8 1.9"
- AC_CHECK_PROG(LCOV, lcov, lcov)
- AC_CHECK_PROG(GENHTML, genhtml, genhtml)
-
- if test "$LCOV"; then
- AC_CACHE_CHECK([for lcov version], glib_cv_lcov_version, [
- glib_cv_lcov_version=invalid
- lcov_version=`$LCOV -v 2>/dev/null | $SED -e 's/^.* //'`
- for lcov_check_version in $lcov_version_list; do
- if test "$lcov_version" = "$lcov_check_version"; then
- glib_cv_lcov_version="$lcov_check_version (ok)"
- fi
- done
- ])
- else
- lcov_msg="To enable code coverage reporting you must have one of the following lcov versions installed: $lcov_version_list"
- AC_MSG_ERROR([$lcov_msg])
- fi
-
- case $glib_cv_lcov_version in
- ""|invalid[)]
- lcov_msg="You must have one of the following versions of lcov: $lcov_version_list (found: $lcov_version)."
- AC_MSG_ERROR([$lcov_msg])
- LCOV="exit 0;"
- ;;
- esac
-
- if test -z "$GENHTML"; then
- AC_MSG_ERROR([Could not find genhtml from the lcov package])
- fi
-
- ac_cv_check_gcov=yes
- ac_cv_check_lcov=yes
-
- # Remove all optimization flags from CFLAGS
- changequote({,})
- CFLAGS=`echo "$CFLAGS" | $SED -e 's/-O[0-9]*//g'`
- changequote([,])
-
- # Add the special gcc flags
- COVERAGE_CFLAGS="-O0 -fprofile-arcs -ftest-coverage"
- COVERAGE_CXXFLAGS="-O0 -fprofile-arcs -ftest-coverage"
- COVERAGE_LDFLAGS="-lgcov"
-
- # Check availability of gcovr
- AC_CHECK_PROG(GCOVR, gcovr, gcovr)
- if test -z "$GCOVR"; then
- ac_cv_check_gcovr=no
- else
- ac_cv_check_gcovr=yes
- fi
-
-fi
-]) # AC_TDD_GCOV
diff --git a/m4/gtest.m4 b/m4/gtest.m4
deleted file mode 100644
index 2de334c..0000000
--- a/m4/gtest.m4
+++ /dev/null
@@ -1,63 +0,0 @@
-# Copyright (C) 2012 Canonical, Ltd.
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice (including the next
-# paragraph) shall be included in all copies or substantial portions of the
-# Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-# SOFTWARE.
-
-# Checks whether the gtest source is available on the system. Allows for
-# adjusting the include and source path. Sets have_gtest=yes if the source is
-# present. Sets GTEST_CPPFLAGS and GTEST_SOURCE to the preprocessor flags and
-# source location respectively.
-AC_DEFUN([CHECK_GTEST],
-[
- AC_ARG_WITH([gtest-include-path],
- [AS_HELP_STRING([--with-gtest-include-path],
- [location of the Google test headers])],
- [GTEST_CPPFLAGS="-I$withval"])
-
- AC_ARG_WITH([gtest-source-path],
- [AS_HELP_STRING([--with-gtest-source-path],
- [location of the Google test sources, defaults to /usr/src/gtest])],
- [GTEST_SOURCE="$withval"],
- [GTEST_SOURCE="/usr/src/gtest"])
-
- GTEST_CPPFLAGS="$GTEST_CPPFLAGS -I$GTEST_SOURCE"
-
- AC_LANG_PUSH([C++])
-
- tmp_CPPFLAGS="$CPPFLAGS"
- CPPFLAGS="$CPPFLAGS $GTEST_CPPFLAGS"
-
- AC_CHECK_HEADER([gtest/gtest.h])
-
- CPPFLAGS="$tmp_CPPFLAGS"
-
- AC_LANG_POP
-
- AC_CHECK_FILES([$GTEST_SOURCE/src/gtest-all.cc]
- [$GTEST_SOURCE/src/gtest_main.cc],
- [have_gtest_source=yes],
- [have_gtest_source=no])
-
- AS_IF([test "x$ac_cv_header_gtest_gtest_h" = xyes -a \
- "x$have_gtest_source" = xyes],
- [have_gtest=yes]
- [AC_SUBST(GTEST_CPPFLAGS)]
- [AC_SUBST(GTEST_SOURCE)],
- [have_gtest=no])
-]) # CHECK_GTEST
diff --git a/po/CMakeLists.txt b/po/CMakeLists.txt
new file mode 100644
index 0000000..c47d584
--- /dev/null
+++ b/po/CMakeLists.txt
@@ -0,0 +1,3 @@
+include (Translations)
+add_translations_directory ("${GETTEXT_PACKAGE}")
+add_translations_catalog ("${GETTEXT_PACKAGE}" ../src/ ../src/dbus-backend)
diff --git a/po/LINGUAS b/po/LINGUAS
deleted file mode 100644
index 2f49802..0000000
--- a/po/LINGUAS
+++ /dev/null
@@ -1,99 +0,0 @@
-af
-am
-an
-ar
-ast
-az
-be
-bg
-bn
-bo
-br
-bs
-ca
-ca@valencia
-crh
-csb
-cs
-cv
-cy
-da
-de
-dv
-el
-en_AU
-en_CA
-en_GB
-eo
-es
-et
-eu
-fa
-fi
-fr
-fur
-fy
-gd
-gl
-gu
-gv
-he
-hi
-hr
-hu
-hy
-id
-is
-it
-ja
-ka
-kk
-km
-kn
-ko
-ku
-ky
-lb
-lt
-lv
-mg
-mk
-ml
-mr
-ms
-my
-nb
-ne
-nl
-nn
-ny
-oc
-os
-pa
-pl
-pt_BR
-pt
-ro
-ru
-sc
-sd
-si
-sk
-sl
-sq
-sr
-sv
-ta_LK
-ta
-te
-th
-tr
-tt
-ug
-uk
-ur
-vec
-vi
-zh_CN
-zh_HK
-zh_TW
diff --git a/po/POTFILES.in b/po/POTFILES.in
deleted file mode 100644
index f2cafb0..0000000
--- a/po/POTFILES.in
+++ /dev/null
@@ -1,14 +0,0 @@
-[encoding: UTF-8]
-data/com.canonical.indicator.session.gschema.xml.in
-data/extra-sessions/classic-desktop.desktop.in.in
-src/dialog.c
-src/gen-session-dbus.xml.c
-src/gtk-logout-helper.c
-src/indicator-session.c
-src/online-accounts-mgr.c
-src/session-dbus.c
-src/session-menu-mgr.c
-src/session-service.c
-src/users-service-dbus.c
-src/user-widget.c
-
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..fcf850d
--- /dev/null
+++ b/src/backend-dbus/actions.c
@@ -0,0 +1,978 @@
+/*
+ * 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_cancellable);
+ 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
+lock_current_session (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_screensaver (IndicatorSessionActions * self)
+{
+ lock_current_session (self);
+}
+
+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);
+
+ lock_current_session (self);
+
+ 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&lt;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&lt;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 431292e..0000000
--- a/src/indicator-session.c
+++ /dev/null
@@ -1,495 +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 guint indicator_session_get_location (IndicatorObject * io,
- IndicatorObjectEntry * entry);
-
-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_location = indicator_session_get_location;
- 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 guint
-indicator_session_get_location (IndicatorObject * io,
- IndicatorObjectEntry * entry)
-{
- return 0;
-}
-
-/* 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..b7b1ba2
--- /dev/null
+++ b/src/service.c
@@ -0,0 +1,1198 @@
+/*
+ * 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 GVariant *
+action_state_for_header (IndicatorSessionService * self)
+{
+ const priv_t * const p = self->priv;
+ gboolean need_attn;
+ GIcon * icon;
+ gboolean show_name;
+ const gchar * real_name;
+ const gchar * label;
+ gchar * a11y;
+ GVariantBuilder b;
+ GVariant * state;
+
+ if (indicator_session_actions_has_online_account_error (p->backend_actions))
+ {
+ need_attn = TRUE;
+ icon = g_themed_icon_new (ICON_ALERT);
+ }
+ else
+ {
+ need_attn = FALSE;
+ icon = g_themed_icon_new (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"));
+ }
+
+ /* build the state */
+ g_variant_builder_init (&b, G_VARIANT_TYPE("a{sv}"));
+ g_variant_builder_add (&b, "{sv}", "accessible-desc", g_variant_new_string (a11y));
+ g_variant_builder_add (&b, "{sv}", "icon", g_icon_serialize (icon));
+ if (label && *label)
+ g_variant_builder_add (&b, "{sv}", "label", g_variant_new_string (label));
+ g_variant_builder_add (&b, "{sv}", "visible", g_variant_new_boolean (TRUE));
+ state = g_variant_builder_end (&b);
+
+ /* cleanup */
+ g_free (a11y);
+ g_object_unref (G_OBJECT (icon));
+
+ return state;
+}
+
+static void
+update_header_action (IndicatorSessionService * self)
+{
+ g_simple_action_set_state (self->priv->header_action, action_state_for_header (self));
+}
+
+/***
+**** 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_action_map_add_action (G_ACTION_MAP (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_action_map_add_action (G_ACTION_MAP (p->actions), G_ACTION(a));
+ p->user_switcher_action = a;
+
+ /* add the header action */
+ a = g_simple_action_new_stateful ("_header", NULL,
+ action_state_for_header (self));
+ g_action_map_add_action (G_ACTION_MAP (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 22f70e5..0000000
--- a/src/session-menu-mgr.c
+++ /dev/null
@@ -1,1329 +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_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
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644
index 0000000..33009a3
--- /dev/null
+++ b/tests/CMakeLists.txt
@@ -0,0 +1,53 @@
+# build the necessary schemas
+set_directory_properties (PROPERTIES
+ ADDITIONAL_MAKE_CLEAN_FILES gschemas.compiled)
+set_source_files_properties (gschemas.compiled GENERATED)
+
+# GSettings:
+# compile the schemas our tests use (indicator-session's, lockdown, media-keys)
+# into a gschemas.compiled file in this directory, and help the tests to find
+# that file by setting -DSCHEMA_DIR
+set (SCHEMA_DIR ${CMAKE_CURRENT_BINARY_DIR})
+add_definitions(-DSCHEMA_DIR="${SCHEMA_DIR}")
+execute_process (COMMAND ${PKG_CONFIG_EXECUTABLE} gio-2.0 --variable glib_compile_schemas
+ OUTPUT_VARIABLE COMPILE_SCHEMA_EXECUTABLE
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+add_custom_command (OUTPUT gschemas.compiled
+ DEPENDS ${CMAKE_BINARY_DIR}/data/com.canonical.indicator.session.gschema.xml
+ ${CMAKE_SOURCE_DIR}/tests/com.canonical.indicator.session.backendmock.gschema.xml
+ ${CMAKE_SOURCE_DIR}/tests/org.gnome.desktop.lockdown.gschema.xml
+ ${CMAKE_SOURCE_DIR}/tests/org.gnome.settings-daemon.plugins.media-keys.gschema.xml
+ COMMAND cp -f ${CMAKE_BINARY_DIR}/data/*gschema.xml ${SCHEMA_DIR}
+ COMMAND cp -f ${CMAKE_SOURCE_DIR}/tests/*gschema.xml ${SCHEMA_DIR}
+ COMMAND ${COMPILE_SCHEMA_EXECUTABLE} ${SCHEMA_DIR})
+
+# DBus Activation
+configure_file (indicator-session.service.in indicator-session.service)
+add_definitions(-DINDICATOR_SERVICE_DIR="${CMAKE_CURRENT_BINARY_DIR}")
+
+# look for hearder in our src dir, and also in the directories where we autogenerate files...
+include_directories (${CMAKE_SOURCE_DIR}/src)
+include_directories (${CMAKE_CURRENT_BINARY_DIR} ${SERVICE_INCLUDE_DIRS})
+
+# backendmock
+add_library (backendmock STATIC
+ backend-mock-actions.c
+ backend-mock-actions.h
+ backend-mock.c
+ backend-mock.h
+ backend-mock-guest.c
+ backend-mock-guest.h
+ backend-mock-users.c
+ backend-mock-users.h)
+set_target_properties (backendmock PROPERTIES COMPILE_FLAGS " ${CC_WARNING_ARGS} -g")
+
+# test-service
+add_executable (test-service
+ test-service.cc
+ gschemas.compiled)
+set_target_properties (test-service PROPERTIES COMPILE_FLAGS " ${CC_WARNING_ARGS} -std=c++0x -g")
+add_test (test-service test-service)
+add_dependencies (test-service libindicatorsessionservice backendmock)
+target_link_libraries (test-service libindicatorsessionservice backendmock gtest ${SERVICE_LIBRARIES} ${GTEST_LIBS})
+
+add_subdirectory (backend-dbus)
diff --git a/tests/Makefile.am b/tests/Makefile.am
deleted file mode 100644
index 071f684..0000000
--- a/tests/Makefile.am
+++ /dev/null
@@ -1,51 +0,0 @@
-TESTS =
-CLEANFILES =
-BUILT_SOURCES =
-check_PROGRAMS =
-
-integrationcheckdir = .
-integrationcheck_PROGRAMS =
-
-###
-###
-###
-
-# stock UMB tests on user-visible strings
-include $(srcdir)/Makefile.am.strings
-
-check_LIBRARIES = libgtest.a
-nodist_libgtest_a_SOURCES = \
- $(GTEST_SOURCE)/gtest-all.cc \
- $(GTEST_SOURCE)/gtest_main.cc
-
-AM_CPPFLAGS = $(GTEST_CPPFLAGS) -I${top_srcdir}/src -Wall -Werror
-AM_CXXFLAGS = $(GTEST_CXXFLAGS)
-
-###
-###
-###
-
-BUILT_SOURCES += gschemas.compiled
-CLEANFILES += gschemas.compiled
-gschemas.compiled: Makefile
- $(AM_V_at) cp -f $(top_builddir)/data/*gschema.xml .
- $(AM_V_GEN) $(GLIB_COMPILE_SCHEMAS) --targetdir=. .
-
-
-integrationcheck:
- ./test-service
-
-integrationcheck_PROGRAMS += test-service
-test_service_SOURCES = test-service.cc
-test_service_LDADD = \
- $(TEST_SERVICE_LIBS) \
- $(XORG_GTEST_LDFLAGS) \
- libgtest.a
-test_service_CPPFLAGS = \
- -DSCHEMA_DIR="\"$(top_builddir)/tests/\"" \
- -DINDICATOR_SERVICE_DIR="\"$(abs_builddir)\"" \
- -DINDICATOR_SERVICE_PATH="\"$(top_builddir)/src/indicator-session-service\"" \
- $(TEST_SERVICE_CFLAGS) \
- $(AM_CPPFLAGS)
-
-
diff --git a/tests/Makefile.am.strings b/tests/Makefile.am.strings
deleted file mode 100644
index 26a23a8..0000000
--- a/tests/Makefile.am.strings
+++ /dev/null
@@ -1,38 +0,0 @@
-TESTS += \
- test-ellipsis \
- test-space-ellipsis \
- test-ascii-quotes
-
-#####
-# Tests for there being proper ellipsis instead of three periods in a row
-#####
-test-ellipsis: $(top_srcdir)/po
- @echo "#!/bin/bash" > $@
- @echo "(cd $(top_srcdir)/po && make $(GETTEXT_PACKAGE).pot)" >> $@
- @echo "grep -c -e \"^msgid.*\.\.\.\\\"\" $(top_srcdir)/po/$(GETTEXT_PACKAGE).pot > /dev/null && echo \"Ellipsis found in user visible strings\" >&2 && exit 1" >> $@
- @echo "exit 0" >> $@
- @chmod +x $@
-
-#####
-# Tests for there being a space before an ellipsis
-#####
-test-space-ellipsis: $(top_srcdir)/po
- @echo "#!/bin/bash" > $@
- @echo "(cd $(top_srcdir)/po && make $(GETTEXT_PACKAGE).pot)" >> $@
- @echo "grep -c -e \"^msgid.* …\\\"\" $(top_srcdir)/po/$(GETTEXT_PACKAGE).pot > /dev/null && echo \"Space before ellipsis found in user visible strings\" >&2 && exit 1" >> $@
- @echo "exit 0" >> $@
- @chmod +x $@
-
-#####
-# Tests for ASCII quote types
-#####
-test-ascii-quotes: $(top_srcdir)/po
- @echo "#!/bin/bash" > $@
- @echo "(cd $(top_srcdir)/po && make $(GETTEXT_PACKAGE).pot)" >> $@
- @echo "grep -c -e \"^msgid \\\".*'.*\\\"\" $(top_srcdir)/po/$(GETTEXT_PACKAGE).pot > /dev/null && echo \"ASCII apostrophy found in user visible strings\" >&2 && exit 1" >> $@
- @echo "grep -c -e \"^msgid \\\".*\\\".*\\\"\" $(top_srcdir)/po/$(GETTEXT_PACKAGE).pot > /dev/null && echo \"ASCII quote found in user visible strings\" >&2 && exit 1" >> $@
- @echo "grep -c -e \"^msgid \\\".*\\\`.*\\\"\" $(top_srcdir)/po/$(GETTEXT_PACKAGE).pot > /dev/null && echo \"ASCII backtick found in user visible strings\" >&2 && exit 1" >> $@
- @echo "exit 0" >> $@
- @chmod +x $@
-
-CLEANFILES += $(TESTS)
diff --git a/tests/backend-dbus/CMakeLists.txt b/tests/backend-dbus/CMakeLists.txt
new file mode 100644
index 0000000..e28d8e6
--- /dev/null
+++ b/tests/backend-dbus/CMakeLists.txt
@@ -0,0 +1,60 @@
+# build libgtest
+add_library (gtest STATIC
+ ${GTEST_SOURCE_DIR}/gtest-all.cc
+ ${GTEST_SOURCE_DIR}/gtest_main.cc)
+set_target_properties (gtest PROPERTIES INCLUDE_DIRECTORIES
+ ${GTEST_INCLUDE_DIR})
+
+SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -g ${CC_WARNING_ARGS}")
+
+# build desktopmock
+add_library (desktopmock STATIC
+ mock-accounts.cc
+ mock-accounts.h
+ mock-login1-manager.cc
+ mock-login1-manager.h
+ mock-login1-seat.cc
+ mock-login1-seat.h
+ mock-display-manager-seat.cc
+ mock-display-manager-seat.h
+ mock-end-session-dialog.cc
+ mock-end-session-dialog.h
+ mock-object.cc
+ mock-object.h
+ mock-screen-saver.cc
+ mock-screen-saver.h
+ mock-session-manager.cc
+ mock-session-manager.h
+ mock-user.cc
+ mock-user.h
+ mock-webcredentials.cc
+ mock-webcredentials.h)
+
+include_directories (${SERVICE_INCLUDE_DIRS})
+include_directories (${CMAKE_SOURCE_DIR}/src)
+include_directories (${CMAKE_BINARY_DIR}/src)
+include_directories (${CMAKE_SOURCE_DIR}/tests)
+
+# test the Actions class
+add_executable (test-actions
+ test-actions.cc)
+add_test (test-actions test-actions)
+set_tests_properties (test-actions PROPERTIES COMPILE_FLAGS "${SERVICE_CFLAGS}")
+target_link_libraries (test-actions desktopmock backenddbus libindicatorsessionservice gtest ${SERVICE_LDFLAGS} ${GTEST_LIBS} ${GCOV_LIBS})
+
+# test the Guest class
+add_executable (test-guest
+ test-guest.cc)
+add_test (test-guest test-guest)
+set_tests_properties (test-guest PROPERTIES COMPILE_FLAGS "${SERVICE_CFLAGS}")
+target_link_libraries (test-guest desktopmock backenddbus libindicatorsessionservice gtest ${SERVICE_LDFLAGS} ${GTEST_LIBS} ${GCOV_LIBS})
+
+# test the Users class
+add_executable (test-users
+ test-users.cc)
+add_test (test-users test-users)
+set_tests_properties (test-users PROPERTIES COMPILE_FLAGS "${SERVICE_CFLAGS}")
+target_link_libraries (test-users desktopmock backenddbus libindicatorsessionservice gtest ${SERVICE_LDFLAGS} ${GTEST_LIBS} ${GCOV_LIBS})
+
+
+
diff --git a/tests/backend-dbus/gtest-mock-dbus-fixture.h b/tests/backend-dbus/gtest-mock-dbus-fixture.h
new file mode 100644
index 0000000..bab82bf
--- /dev/null
+++ b/tests/backend-dbus/gtest-mock-dbus-fixture.h
@@ -0,0 +1,125 @@
+/*
+ * 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 "gtest-dbus-fixture.h"
+
+#include "mock-accounts.h"
+#include "mock-login1-manager.h"
+#include "mock-login1-seat.h"
+#include "mock-display-manager-seat.h"
+#include "mock-end-session-dialog.h"
+#include "mock-screen-saver.h"
+#include "mock-session-manager.h"
+#include "mock-user.h"
+#include "mock-webcredentials.h"
+
+/***
+****
+***/
+
+class GTestMockDBusFixture: public GTestDBusFixture
+{
+ private:
+
+ typedef GTestDBusFixture super;
+
+ protected:
+
+ MockScreenSaver * screen_saver;
+ MockSessionManager * session_manager;
+ MockDisplayManagerSeat * dm_seat;
+ MockAccounts * accounts;
+ MockLogin1Manager * login1_manager;
+ MockLogin1Seat * login1_seat;
+ MockEndSessionDialog * end_session_dialog;
+ MockWebcredentials * webcredentials;
+
+ protected:
+
+ virtual void SetUp ()
+ {
+ super :: SetUp ();
+
+ webcredentials = new MockWebcredentials (loop, conn);
+ end_session_dialog = new MockEndSessionDialog (loop, conn);
+ session_manager = new MockSessionManager (loop, conn);
+ screen_saver = new MockScreenSaver (loop, conn);
+ dm_seat = new MockDisplayManagerSeat (loop, conn);
+ g_setenv ("XDG_SEAT_PATH", dm_seat->path(), TRUE);
+ dm_seat->set_guest_allowed (false);
+ login1_manager = new MockLogin1Manager (loop, conn);
+ login1_seat = new MockLogin1Seat (loop, conn, true);
+ g_setenv ("XDG_SEAT", login1_seat->seat_id(), TRUE);
+ login1_manager->add_seat (login1_seat);
+ accounts = build_accounts_mock ();
+ MockUser * user = accounts->find_by_username ("msmith");
+ const int session_tag = login1_manager->add_session (login1_seat, user);
+ dm_seat->set_login1_seat (login1_seat);
+ dm_seat->switch_to_user (user->username());
+ ASSERT_EQ (session_tag, login1_seat->active_session());
+ }
+
+ protected:
+
+ virtual void TearDown ()
+ {
+ delete accounts;
+ delete login1_manager;
+ delete dm_seat;
+ delete screen_saver;
+ delete session_manager;
+ delete end_session_dialog;
+ delete webcredentials;
+
+ super :: TearDown ();
+ }
+
+ private:
+
+ MockAccounts * build_accounts_mock ()
+ {
+ struct {
+ guint64 login_frequency;
+ const gchar * user_name;
+ const gchar * real_name;
+ } users[] = {
+ { 134, "whartnell", "First Doctor" },
+ { 119, "ptroughton", "Second Doctor" },
+ { 128, "jpertwee", "Third Doctor" },
+ { 172, "tbaker", "Fourth Doctor" },
+ { 69, "pdavison", "Fifth Doctor" },
+ { 31, "cbaker", "Sixth Doctor" },
+ { 42, "smccoy", "Seventh Doctor" },
+ { 1, "pmcgann", "Eigth Doctor" },
+ { 13, "ceccleston", "Ninth Doctor" },
+ { 47, "dtennant", "Tenth Doctor" },
+ { 34, "msmith", "Eleventh Doctor" },
+ { 1, "rhurndall", "First Doctor" }
+ };
+
+ MockAccounts * a = new MockAccounts (loop, conn);
+ for (int i=0, n=G_N_ELEMENTS(users); i<n; ++i)
+ a->add_user (new MockUser (loop, conn,
+ users[i].user_name,
+ users[i].real_name,
+ users[i].login_frequency));
+ return a;
+ }
+};
+
diff --git a/tests/backend-dbus/mock-accounts.cc b/tests/backend-dbus/mock-accounts.cc
new file mode 100644
index 0000000..16e37d2
--- /dev/null
+++ b/tests/backend-dbus/mock-accounts.cc
@@ -0,0 +1,143 @@
+/*
+ * 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 "mock-accounts.h"
+#include "mock-user.h"
+
+namespace
+{
+ const char * const DBUS_ACCOUNTS_NAME = "org.freedesktop.Accounts";
+
+ const char * const DBUS_ACCOUNTS_PATH = "/org/freedesktop/Accounts";
+}
+
+/***
+****
+***/
+
+void
+MockAccounts :: add_user (MockUser * user)
+{
+ g_assert (my_users.count(user) == 0);
+
+ my_users.insert (user);
+ my_uid_to_user[user->uid()] = user;
+ my_path_to_user[user->path()] = user;
+ my_username_to_user[user->username()] = user;
+
+ accounts_emit_user_added (my_skeleton, user->path());
+}
+
+void
+MockAccounts :: remove_user (MockUser * user)
+{
+ g_assert (my_users.count(user) == 1);
+
+ my_users.erase (user);
+ my_uid_to_user.erase (user->uid());
+ my_path_to_user.erase (user->path());
+ my_username_to_user.erase (user->username());
+
+ accounts_emit_user_deleted (my_skeleton, user->path());
+}
+
+MockUser *
+MockAccounts :: find_by_uid (guint64 uid)
+{
+ const uid_to_user_t::iterator it (my_uid_to_user.find(uid));
+
+ if (it != my_uid_to_user.end())
+ return it->second;
+
+ g_warn_if_reached ();
+ return 0;
+}
+
+MockUser *
+MockAccounts :: find_by_username (const char * username)
+{
+ const username_to_user_t::iterator it (my_username_to_user.find(username));
+
+ if (it != my_path_to_user.end())
+ return it->second;
+
+ g_warn_if_reached ();
+ return 0;
+}
+
+/***
+****
+***/
+
+gboolean
+MockAccounts :: on_find_user_by_id_static (Accounts * a,
+ GDBusMethodInvocation * invocation,
+ guint64 uid,
+ gpointer gself)
+{
+ MockUser * user = static_cast<MockAccounts*>(gself)->find_by_uid (uid);
+ accounts_complete_find_user_by_id (a, invocation, user ? user->path() : "");
+ return true;
+}
+
+gboolean
+MockAccounts :: on_list_cached_users_static (Accounts * a,
+ GDBusMethodInvocation * invocation,
+ gpointer gself)
+{
+ int i;
+ const char ** paths;
+ const users_t& users = static_cast<MockAccounts*>(gself)->my_users;
+
+ i = 0;
+ paths = g_new0 (const char*, users.size() + 1);
+ for (auto it : users)
+ paths[i++] = it->path();
+ accounts_complete_list_cached_users (a, invocation, paths);
+ g_free (paths);
+
+ return true;
+}
+
+/***
+****
+***/
+
+MockAccounts :: MockAccounts (GMainLoop * loop,
+ GDBusConnection * bus_connection):
+ MockObject (loop, bus_connection, DBUS_ACCOUNTS_NAME, DBUS_ACCOUNTS_PATH),
+ my_skeleton (accounts_skeleton_new ())
+{
+ g_signal_connect (my_skeleton, "handle-list-cached-users",
+ G_CALLBACK(on_list_cached_users_static), this);
+ g_signal_connect (my_skeleton, "handle-find-user-by-id",
+ G_CALLBACK(on_find_user_by_id_static), this);
+
+ set_skeleton (G_DBUS_INTERFACE_SKELETON(my_skeleton));
+}
+
+MockAccounts :: ~MockAccounts ()
+{
+ for (users_t::iterator it(my_users.begin()),
+ end(my_users.end()); it!=end; ++it)
+ delete *it;
+
+ g_signal_handlers_disconnect_by_data (my_skeleton, this);
+ g_clear_object (&my_skeleton);
+}
diff --git a/tests/backend-dbus/mock-accounts.h b/tests/backend-dbus/mock-accounts.h
new file mode 100644
index 0000000..8032441
--- /dev/null
+++ b/tests/backend-dbus/mock-accounts.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 MOCK_ACCOUNTS_H
+#define MOCK_ACCOUNTS_H
+
+#include <set>
+#include <string>
+#include <map>
+#include "mock-object.h"
+#include "backend-dbus/dbus-accounts.h" // struct Accounts
+
+class MockUser;
+
+class MockAccounts: public MockObject
+{
+ public:
+
+ MockAccounts (GMainLoop * loop,
+ GDBusConnection * bus_connection);
+ virtual ~MockAccounts ();
+
+ void add_user (MockUser * user);
+ void remove_user (MockUser * user);
+ size_t size() const { return my_users.size(); }
+ MockUser * find_by_uid (guint64 uid);
+ MockUser * find_by_username (const char * username);
+
+ private:
+
+ Accounts * my_skeleton;
+
+ typedef std::set<MockUser*> users_t;
+ users_t my_users;
+
+ typedef std::map<guint,MockUser*> uid_to_user_t;
+ uid_to_user_t my_uid_to_user;
+
+ typedef std::map<std::string,MockUser*> path_to_user_t;
+ path_to_user_t my_path_to_user;
+
+ typedef std::map<std::string,MockUser*> username_to_user_t;
+ username_to_user_t my_username_to_user;
+
+ private:
+
+ static gboolean on_find_user_by_id_static (Accounts *,
+ GDBusMethodInvocation *,
+ guint64,
+ gpointer);
+
+ static gboolean on_list_cached_users_static (Accounts *,
+ GDBusMethodInvocation *,
+ gpointer);
+};
+
+#endif // #ifndef MOCK_ACCOUNTS_H
diff --git a/tests/backend-dbus/mock-display-manager-seat.cc b/tests/backend-dbus/mock-display-manager-seat.cc
new file mode 100644
index 0000000..c8a4857
--- /dev/null
+++ b/tests/backend-dbus/mock-display-manager-seat.cc
@@ -0,0 +1,139 @@
+/*
+ * 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 "mock-display-manager-seat.h"
+#include "mock-login1-seat.h"
+
+namespace
+{
+ const char * const DISPLAY_MANAGER_NAME = "org.freedesktop.DisplayManager";
+
+ std::string
+ next_unique_path ()
+ {
+ static int id = 12; // arbitrary; doesn't matter
+
+ char * tmp;
+ std::string ret;
+
+ tmp = g_strdup_printf ("/org/freedesktop/DisplayManager/Seat%d", id++);
+ ret = tmp;
+ g_free (tmp);
+ return ret;
+ }
+}
+
+/***
+****
+***/
+
+void
+MockDisplayManagerSeat :: switch_to_greeter ()
+{
+ my_last_action = GREETER;
+}
+
+gboolean
+MockDisplayManagerSeat :: handle_switch_to_greeter (DisplayManagerSeat * o,
+ GDBusMethodInvocation * inv,
+ gpointer gself)
+{
+ static_cast<MockDisplayManagerSeat*>(gself)->switch_to_greeter ();
+ display_manager_seat_complete_switch_to_greeter (o, inv);
+ return true;
+}
+
+void
+MockDisplayManagerSeat :: set_guest_allowed (bool b)
+{
+ display_manager_seat_set_has_guest_account (my_skeleton, b);
+}
+
+gboolean
+MockDisplayManagerSeat :: handle_switch_to_guest (DisplayManagerSeat * o,
+ GDBusMethodInvocation * inv,
+ const gchar * session_name G_GNUC_UNUSED,
+ gpointer gself)
+{
+ static_cast<MockDisplayManagerSeat*>(gself)->switch_to_guest ();
+ display_manager_seat_complete_switch_to_guest (o, inv);
+ return true;
+}
+
+void
+MockDisplayManagerSeat :: switch_to_guest ()
+{
+ g_assert (my_login1_seat != 0);
+
+ my_last_action = GUEST;
+ my_login1_seat->switch_to_guest ();
+}
+
+gboolean
+MockDisplayManagerSeat :: handle_switch_to_user (DisplayManagerSeat * o,
+ GDBusMethodInvocation * inv,
+ const gchar * username,
+ const gchar * session_name G_GNUC_UNUSED,
+ gpointer gself)
+{
+ static_cast<MockDisplayManagerSeat*>(gself)->switch_to_user (username);
+ display_manager_seat_complete_switch_to_user (o, inv);
+ return true;
+}
+
+void
+MockDisplayManagerSeat :: switch_to_user (const char * username)
+{
+ g_assert (my_login1_seat != 0);
+
+ my_last_action = USER;
+ my_login1_seat->switch_to_user (username);
+}
+
+void
+MockDisplayManagerSeat :: set_login1_seat (MockLogin1Seat * seat)
+{
+ my_login1_seat = seat;
+}
+
+/***
+****
+***/
+
+MockDisplayManagerSeat :: MockDisplayManagerSeat (GMainLoop * loop,
+ GDBusConnection * connection):
+ MockObject (loop, connection, DISPLAY_MANAGER_NAME, next_unique_path()),
+ my_skeleton (display_manager_seat_skeleton_new ()),
+ my_last_action (NONE)
+{
+ g_signal_connect (my_skeleton, "handle-switch-to-guest",
+ G_CALLBACK(handle_switch_to_guest), this);
+ g_signal_connect (my_skeleton, "handle-switch-to-user",
+ G_CALLBACK(handle_switch_to_user), this);
+ g_signal_connect (my_skeleton, "handle-switch-to-greeter",
+ G_CALLBACK(handle_switch_to_greeter), this);
+
+ set_skeleton (G_DBUS_INTERFACE_SKELETON(my_skeleton));
+}
+
+MockDisplayManagerSeat :: ~MockDisplayManagerSeat ()
+{
+ //g_signal_handlers_disconnect_by_data (my_skeleton, this);
+ g_clear_object (&my_skeleton);
+}
diff --git a/tests/backend-dbus/mock-display-manager-seat.h b/tests/backend-dbus/mock-display-manager-seat.h
new file mode 100644
index 0000000..fcdd17a
--- /dev/null
+++ b/tests/backend-dbus/mock-display-manager-seat.h
@@ -0,0 +1,72 @@
+/*
+ * 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 MOCK_DISPLAY_MANAGER_SEAT_H
+#define MOCK_DISPLAY_MANAGER_SEAT_H
+
+#include "mock-object.h" // parent class
+#include "backend-dbus/dbus-display-manager.h"
+
+class MockLogin1Seat;
+
+class MockDisplayManagerSeat: public MockObject
+{
+ public:
+
+ MockDisplayManagerSeat (GMainLoop * loop,
+ GDBusConnection * bus_connection);
+ virtual ~MockDisplayManagerSeat ();
+
+ void set_guest_allowed (bool b);
+
+ void set_login1_seat (MockLogin1Seat * login1_seat);
+
+ void switch_to_guest ();
+
+ void switch_to_greeter ();
+
+ void switch_to_user (const char * username);
+
+ public:
+
+ enum Action { NONE, GUEST, GREETER, USER };
+
+ Action last_action () const { return my_last_action; }
+
+ private:
+
+ static gboolean handle_switch_to_greeter (DisplayManagerSeat *o,
+ GDBusMethodInvocation *inv,
+ gpointer gself);
+ static gboolean handle_switch_to_guest (DisplayManagerSeat *o,
+ GDBusMethodInvocation *inv,
+ const gchar *session_name,
+ gpointer gself);
+ static gboolean handle_switch_to_user (DisplayManagerSeat * o,
+ GDBusMethodInvocation * inv,
+ const gchar * username,
+ const gchar * session_name,
+ gpointer gself);
+
+ DisplayManagerSeat * my_skeleton;
+ MockLogin1Seat * my_login1_seat;
+ Action my_last_action;
+};
+
+#endif // #ifndef MOCK_DISPLAY_MANAGER_SEAT_H
diff --git a/tests/backend-dbus/mock-end-session-dialog.cc b/tests/backend-dbus/mock-end-session-dialog.cc
new file mode 100644
index 0000000..2772423
--- /dev/null
+++ b/tests/backend-dbus/mock-end-session-dialog.cc
@@ -0,0 +1,61 @@
+/*
+ * 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 "mock-end-session-dialog.h"
+
+gboolean
+MockEndSessionDialog :: handle_open (EndSessionDialog * object,
+ GDBusMethodInvocation * invocation,
+ guint arg_type G_GNUC_UNUSED,
+ guint arg_timestamp G_GNUC_UNUSED,
+ guint arg_seconds_to_stay_open G_GNUC_UNUSED,
+ const gchar * const * inhibitor_paths G_GNUC_UNUSED,
+ gpointer gself)
+{
+ static_cast<MockEndSessionDialog*>(gself)->my_isOpen = true;
+ end_session_dialog_complete_open (object, invocation);
+ return true;
+}
+
+/***
+****
+***/
+
+namespace
+{
+ const char * const MY_NAME = "com.canonical.Unity";
+ const char * const MY_PATH = "/org/gnome/SessionManager/EndSessionDialog";
+}
+
+MockEndSessionDialog :: MockEndSessionDialog (GMainLoop * loop,
+ GDBusConnection * bus_connection):
+ MockObject (loop, bus_connection, MY_NAME, MY_PATH),
+ my_skeleton (end_session_dialog_skeleton_new ()),
+ my_isOpen (false)
+{
+ g_signal_connect (my_skeleton, "handle-open",
+ G_CALLBACK(handle_open), this);
+
+ set_skeleton (G_DBUS_INTERFACE_SKELETON(my_skeleton));
+}
+
+MockEndSessionDialog :: ~MockEndSessionDialog ()
+{
+ g_clear_object (&my_skeleton);
+}
diff --git a/tests/backend-dbus/mock-end-session-dialog.h b/tests/backend-dbus/mock-end-session-dialog.h
new file mode 100644
index 0000000..468715c
--- /dev/null
+++ b/tests/backend-dbus/mock-end-session-dialog.h
@@ -0,0 +1,67 @@
+/*
+ * 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 MOCK_END_SESSION_DIALOG_H
+#define MOCK_END_SESSION_DIALOG_H
+
+#include "mock-object.h" // parent class
+#include "backend-dbus/dbus-end-session-dialog.h" // EndSessionDialog
+
+class MockEndSessionDialog: public MockObject
+{
+ public:
+
+ MockEndSessionDialog (GMainLoop * loop,
+ GDBusConnection * bus_connection);
+ virtual ~MockEndSessionDialog ();
+
+ bool is_open () const { return my_isOpen; }
+
+ void cancel () { my_isOpen = false; end_session_dialog_emit_canceled (my_skeleton); }
+ void confirm_logout () { my_isOpen = false; end_session_dialog_emit_confirmed_logout (my_skeleton); }
+ void confirm_reboot () { my_isOpen = false; end_session_dialog_emit_confirmed_reboot (my_skeleton); }
+ void confirm_shutdown () { my_isOpen = false; end_session_dialog_emit_confirmed_shutdown (my_skeleton); }
+ void close () { my_isOpen = false; end_session_dialog_emit_closed (my_skeleton); }
+
+ private:
+
+ EndSessionDialog * my_skeleton;
+
+ bool my_isOpen;
+
+ static gboolean handle_open (EndSessionDialog *,
+ GDBusMethodInvocation *,
+ guint,
+ guint,
+ guint,
+ const gchar * const *,
+ gpointer);
+
+
+#if 0
+ static gboolean handle_lock (GnomeScreenSaver *,
+ GDBusMethodInvocation *,
+ gpointer);
+ static gboolean handle_simulate_user_activity (GnomeScreenSaver *,
+ GDBusMethodInvocation *,
+ gpointer);
+#endif
+};
+
+#endif
diff --git a/tests/backend-dbus/mock-login1-manager.cc b/tests/backend-dbus/mock-login1-manager.cc
new file mode 100644
index 0000000..4461125
--- /dev/null
+++ b/tests/backend-dbus/mock-login1-manager.cc
@@ -0,0 +1,241 @@
+/*
+ * 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 "mock-login1-manager.h"
+#include "mock-login1-seat.h"
+#include "mock-user.h"
+
+namespace
+{
+ const char * const BUS_NAME = "org.freedesktop.login1";
+
+ const char * const BUS_PATH = "/org/freedesktop/login1";
+}
+
+/***
+****
+***/
+
+void
+MockLogin1Manager :: emit_session_new (MockLogin1Seat * seat, int tag) const
+{
+ std::string id;
+ std::string path;
+ seat->get_session_id_and_path_for_tag (tag, id, path);
+
+ login1_manager_emit_session_new (my_skeleton, id.c_str(), path.c_str());
+}
+
+int
+MockLogin1Manager :: add_session (MockLogin1Seat * seat, MockUser * user)
+{
+ g_assert (my_seats.count(seat) == 1);
+
+ const int session_tag = seat->add_session (user);
+ emit_session_new (seat, session_tag);
+ return session_tag;
+}
+
+void
+MockLogin1Manager :: emit_session_removed (MockLogin1Seat * seat, int tag) const
+{
+ std::string id;
+ std::string path;
+ seat->get_session_id_and_path_for_tag (tag, id, path);
+
+ login1_manager_emit_session_removed (my_skeleton, id.c_str(), path.c_str());
+}
+
+void
+MockLogin1Manager :: remove_session (MockLogin1Seat * seat, int session_tag)
+{
+ seat->remove_session (session_tag);
+ emit_session_removed (seat, session_tag);
+}
+
+void
+MockLogin1Manager :: add_seat (MockLogin1Seat * seat)
+{
+ g_assert (my_seats.count(seat) == 0);
+
+ my_seats.insert (seat);
+ std::set<int> sessions = seat->sessions ();
+ for (auto tag : sessions)
+ emit_session_new (seat, tag);
+}
+
+/***
+****
+***/
+
+GVariant *
+MockLogin1Manager :: list_sessions () const
+{
+ GVariantBuilder b;
+
+ g_variant_builder_init (&b, G_VARIANT_TYPE("a(susso)"));
+
+ for (auto seat : my_seats)
+ {
+ GVariant * seat_sessions = seat->list_sessions ();
+
+ GVariantIter iter;
+ g_variant_iter_init (&iter, seat_sessions);
+ GVariant * child;
+ while ((child = g_variant_iter_next_value (&iter)))
+ {
+ g_variant_builder_add_value (&b, child);
+ g_variant_unref (child);
+ }
+ }
+
+ return g_variant_builder_end (&b);
+}
+
+/***
+**** Skeleton Handlers
+***/
+
+gboolean
+MockLogin1Manager :: handle_list_sessions (Login1Manager * m,
+ GDBusMethodInvocation * inv,
+ gpointer gself)
+{
+ GVariant * sessions = static_cast<MockLogin1Manager*>(gself)->list_sessions();
+ login1_manager_complete_list_sessions (m, inv, sessions);
+ return true;
+}
+
+gboolean
+MockLogin1Manager :: handle_can_suspend (Login1Manager * m,
+ GDBusMethodInvocation * inv,
+ gpointer gself)
+{
+ const std::string& s = static_cast<MockLogin1Manager*>(gself)->can_suspend();
+ login1_manager_complete_can_suspend (m, inv, s.c_str());
+ return true;
+}
+
+gboolean
+MockLogin1Manager :: handle_can_hibernate (Login1Manager * m,
+ GDBusMethodInvocation * inv,
+ gpointer gself)
+{
+ const std::string& s = static_cast<MockLogin1Manager*>(gself)->can_hibernate();
+ login1_manager_complete_can_hibernate (m, inv, s.c_str());
+ return true;
+}
+
+gboolean
+MockLogin1Manager :: handle_reboot (Login1Manager * m,
+ GDBusMethodInvocation * inv,
+ gboolean interactive G_GNUC_UNUSED,
+ gpointer gself)
+{
+ static_cast<MockLogin1Manager*>(gself)->my_last_action = "reboot";
+ login1_manager_complete_reboot (m, inv);
+ return true;
+}
+
+gboolean
+MockLogin1Manager :: handle_power_off (Login1Manager * m,
+ GDBusMethodInvocation * inv,
+ gboolean interactive G_GNUC_UNUSED,
+ gpointer gself)
+{
+ static_cast<MockLogin1Manager*>(gself)->my_last_action = "power-off";
+ login1_manager_complete_power_off (m, inv);
+ return true;
+}
+
+gboolean
+MockLogin1Manager :: handle_suspend (Login1Manager * m,
+ GDBusMethodInvocation * inv,
+ gboolean interactive G_GNUC_UNUSED,
+ gpointer gself)
+{
+ static_cast<MockLogin1Manager*>(gself)->my_last_action = "suspend";
+ login1_manager_complete_suspend (m, inv);
+ return true;
+}
+
+gboolean
+MockLogin1Manager :: handle_hibernate (Login1Manager * m,
+ GDBusMethodInvocation * inv,
+ gboolean interactive G_GNUC_UNUSED,
+ gpointer gself)
+{
+ static_cast<MockLogin1Manager*>(gself)->my_last_action = "hibernate";
+ login1_manager_complete_hibernate (m, inv);
+ return true;
+}
+
+/***
+****
+***/
+
+const std::string&
+MockLogin1Manager :: can_suspend () const
+{
+ return my_can_suspend;
+}
+
+const std::string&
+MockLogin1Manager :: can_hibernate () const
+{
+ return my_can_hibernate;
+}
+
+/***
+****
+***/
+
+MockLogin1Manager :: MockLogin1Manager (GMainLoop * loop,
+ GDBusConnection * conn):
+ MockObject (loop, conn, BUS_NAME, BUS_PATH),
+ my_skeleton (login1_manager_skeleton_new ()),
+ my_can_suspend ("yes"),
+ my_can_hibernate ("yes")
+{
+ g_signal_connect (my_skeleton, "handle-can-suspend",
+ G_CALLBACK(handle_can_suspend), this);
+ g_signal_connect (my_skeleton, "handle-can-hibernate",
+ G_CALLBACK(handle_can_hibernate), this);
+ g_signal_connect (my_skeleton, "handle_reboot",
+ G_CALLBACK(handle_reboot), this);
+ g_signal_connect (my_skeleton, "handle-power-off",
+ G_CALLBACK(handle_power_off), this);
+ g_signal_connect (my_skeleton, "handle-suspend",
+ G_CALLBACK(handle_suspend), this);
+ g_signal_connect (my_skeleton, "handle-hibernate",
+ G_CALLBACK(handle_hibernate), this);
+ g_signal_connect (my_skeleton, "handle-list-sessions",
+ G_CALLBACK(handle_list_sessions), this);
+
+ set_skeleton (G_DBUS_INTERFACE_SKELETON(my_skeleton));
+}
+
+MockLogin1Manager :: ~MockLogin1Manager ()
+{
+ for (auto seat : my_seats)
+ delete seat;
+
+ g_signal_handlers_disconnect_by_data (my_skeleton, this);
+ g_clear_object (&my_skeleton);
+}
diff --git a/tests/backend-dbus/mock-login1-manager.h b/tests/backend-dbus/mock-login1-manager.h
new file mode 100644
index 0000000..f630329
--- /dev/null
+++ b/tests/backend-dbus/mock-login1-manager.h
@@ -0,0 +1,74 @@
+/*
+ * 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 MOCK_LOGIN1_MANAGER_H
+#define MOCK_LOGIN1_MANAGER_H
+
+#include <set>
+#include <string>
+#include "mock-object.h"
+#include "backend-dbus/dbus-login1-manager.h"
+
+class MockLogin1Seat;
+class MockUser;
+
+class MockLogin1Manager: public MockObject
+{
+ public:
+
+ MockLogin1Manager (GMainLoop * loop,
+ GDBusConnection * bus_connection);
+ virtual ~MockLogin1Manager ();
+
+ int add_session (MockLogin1Seat * seat, MockUser * user);
+ void remove_session (MockLogin1Seat * seat, int session_tag);
+
+ void add_seat (MockLogin1Seat * seat);
+
+ const std::string& can_suspend () const;
+ const std::string& can_hibernate () const;
+
+ const std::string& last_action () const { return my_last_action; }
+ void clear_last_action () { my_last_action.clear(); }
+
+ private:
+
+ void emit_session_new (MockLogin1Seat * seat, int tag) const;
+ void emit_session_removed (MockLogin1Seat * seat, int tag) const;
+
+ GVariant * list_sessions () const;
+
+ static gboolean handle_list_sessions (Login1Manager *, GDBusMethodInvocation *, gpointer);
+ static gboolean handle_can_suspend (Login1Manager *, GDBusMethodInvocation *, gpointer);
+ static gboolean handle_can_hibernate (Login1Manager *, GDBusMethodInvocation *, gpointer);
+ static gboolean handle_reboot (Login1Manager *, GDBusMethodInvocation *, gboolean, gpointer);
+ static gboolean handle_power_off (Login1Manager *, GDBusMethodInvocation *, gboolean, gpointer);
+ static gboolean handle_suspend (Login1Manager *, GDBusMethodInvocation *, gboolean, gpointer);
+ static gboolean handle_hibernate (Login1Manager *, GDBusMethodInvocation *, gboolean, gpointer);
+
+ private:
+
+ Login1Manager * my_skeleton;
+ std::set<MockLogin1Seat*> my_seats;
+ std::string my_can_suspend;
+ std::string my_can_hibernate;
+ std::string my_last_action;
+};
+
+#endif // #ifndef MOCK_LOGIN1_MANAGER_H
diff --git a/tests/backend-dbus/mock-login1-seat.cc b/tests/backend-dbus/mock-login1-seat.cc
new file mode 100644
index 0000000..49d7fb6
--- /dev/null
+++ b/tests/backend-dbus/mock-login1-seat.cc
@@ -0,0 +1,244 @@
+/*
+ * 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 "mock-login1-seat.h"
+
+#include "mock-object.h"
+#include "mock-user.h"
+
+namespace
+{
+ const char * BUS_NAME = "org.freedesktop.login1";
+
+ std::string next_unique_sid ()
+ {
+ static int id = 1;
+
+ char * tmp;
+ std::string ret;
+
+ tmp = g_strdup_printf ("/org/freedesktop/login1/seat/seat%d", id++);
+ ret = tmp;
+ g_free (tmp);
+ return ret;
+ }
+
+ static int next_session_tag = 1;
+}
+
+void
+MockLogin1Seat :: get_session_id_and_path_for_tag (int tag,
+ std::string & id,
+ std::string & path)
+{
+ if (tag)
+ {
+ char tmp[80];
+
+ g_snprintf (tmp, sizeof(tmp), "c%d", tag);
+ id = tmp;
+
+ g_snprintf (tmp, sizeof(tmp), "/org/freedesktop/login1/session/%s", id.c_str());
+ path = tmp;
+ }
+ else
+ {
+ id = "";
+ path = "";
+ }
+}
+
+
+/***
+****
+***/
+
+void
+MockLogin1Seat :: update_sessions_property ()
+{
+ GVariantBuilder b;
+
+ g_variant_builder_init (&b, G_VARIANT_TYPE("a(so)"));
+ for (const auto& it : my_sessions)
+ {
+ std::string id, path;
+ get_session_id_and_path_for_tag (it.first, id, path);
+ g_variant_builder_add (&b, "(so)", id.c_str(), path.c_str());
+ }
+
+ GVariant * v = g_variant_builder_end (&b);
+ g_object_set (my_skeleton, "sessions", v, NULL);
+}
+
+void
+MockLogin1Seat :: update_active_session_property ()
+{
+ std::string id;
+ std::string path;
+ get_session_id_and_path_for_tag (my_active_session, id, path);
+
+ GVariant * v = g_variant_new ("(so)", id.c_str(), path.c_str());
+ g_object_set (my_skeleton, "active-session", v, NULL);
+}
+
+void
+MockLogin1Seat :: update_can_multi_session_property ()
+{
+ g_object_set (my_skeleton, "can-multi-session", my_can_multi_session, NULL);
+}
+
+/***
+****
+***/
+
+/* lists this seat's sessions in the format of Login1Manager::ListSessions() */
+GVariant *
+MockLogin1Seat :: list_sessions ()
+{
+ GVariantBuilder b;
+ g_variant_builder_init (&b, G_VARIANT_TYPE("a(susso)"));
+ for (auto it : my_sessions)
+ {
+ std::string id, path;
+ get_session_id_and_path_for_tag (it.first, id, path);
+ g_variant_builder_add (&b, "(susso)",
+ id.c_str(),
+ uint32_t(it.second->uid()),
+ it.second->username(),
+ seat_id(),
+ path.c_str());
+ }
+
+ return g_variant_builder_end (&b);
+}
+
+/***
+****
+***/
+
+std::set<int>
+MockLogin1Seat :: sessions () const
+{
+ std::set<int> ret;
+
+ for (auto it : my_sessions)
+ ret.insert (it.first);
+
+ return ret;
+}
+
+int
+MockLogin1Seat :: add_session (MockUser * user)
+{
+ const int tag = next_session_tag++;
+
+ my_sessions[tag] = user;
+ update_sessions_property ();
+
+ return tag;
+}
+
+void
+MockLogin1Seat :: remove_session (int session_tag)
+{
+ my_sessions.erase (session_tag);
+ update_sessions_property ();
+}
+
+/***
+****
+***/
+
+std::string
+MockLogin1Seat :: user_state (unsigned int uid) const
+{
+ for (auto it : my_sessions)
+ if (it.second->uid() == uid)
+ return it.first == my_active_session ? "active" : "online";
+
+ return "offline"; // no matching session
+}
+
+void
+MockLogin1Seat :: activate_session (int session_tag)
+{
+ g_assert (my_sessions.count(session_tag) == 1);
+
+ if (my_active_session != session_tag)
+ {
+ std::string id, path;
+ my_active_session = session_tag;
+ get_session_id_and_path_for_tag (session_tag, id, path);
+ g_setenv ("XDG_SESSION_ID", id.c_str(), true);
+ update_active_session_property ();
+
+ }
+}
+
+void
+MockLogin1Seat :: switch_to_guest ()
+{
+ for (const auto& it : my_sessions)
+ {
+ if (it.second->is_guest())
+ {
+ activate_session (it.first);
+ return;
+ }
+ }
+
+ g_warn_if_reached ();
+}
+
+void
+MockLogin1Seat :: switch_to_user (const char * username)
+{
+ for (const auto& it : my_sessions)
+ {
+ if (!g_strcmp0 (username, it.second->username()))
+ {
+ activate_session (it.first);
+ return;
+ }
+ }
+
+ g_warn_if_reached ();
+}
+
+/***
+**** Life Cycle
+***/
+
+MockLogin1Seat :: MockLogin1Seat (GMainLoop * loop,
+ GDBusConnection * bus_connection,
+ bool can_activate_sessions):
+ MockObject (loop, bus_connection, BUS_NAME, next_unique_sid()),
+ my_skeleton (login1_seat_skeleton_new ()),
+ my_active_session (0),
+ my_can_multi_session (can_activate_sessions)
+
+{
+ set_skeleton (G_DBUS_INTERFACE_SKELETON(my_skeleton));
+ update_can_multi_session_property ();
+}
+
+MockLogin1Seat :: ~MockLogin1Seat ()
+{
+ g_clear_object (&my_skeleton);
+}
diff --git a/tests/backend-dbus/mock-login1-seat.h b/tests/backend-dbus/mock-login1-seat.h
new file mode 100644
index 0000000..254ebe9
--- /dev/null
+++ b/tests/backend-dbus/mock-login1-seat.h
@@ -0,0 +1,76 @@
+/*
+ * 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 MOCK_LOGIN1_SEAT_H
+#define MOCK_LOGIN1_SEAT_H
+
+#include <cstring> /* strrchr */
+#include <map>
+#include <set>
+#include <string>
+#include "backend-dbus/dbus-login1-seat.h"
+#include "mock-object.h"
+
+class MockUser;
+class MockLogin1Session;
+
+class MockLogin1Seat: public MockObject
+{
+ public:
+
+ MockLogin1Seat (GMainLoop * loop,
+ GDBusConnection * bus_connection,
+ bool can_activate_sessions);
+
+ virtual ~MockLogin1Seat ();
+
+ const char * seat_id() const { return strrchr(path(),'/')+1; }
+
+ int add_session (MockUser * user);
+ void remove_session (int session_tag);
+ std::set<int> sessions () const;
+ int active_session () const { return my_active_session; }
+
+ std::string user_state (unsigned int uid) const;
+
+ bool can_activate_sessions () const { return my_can_multi_session; }
+ void activate_session (int session_tag);
+ void switch_to_guest ();
+ void switch_to_user (const char * username);
+
+ //const char * sid() { return path(); }
+ //MockLogin1Session * find (const char * ssid);
+
+ GVariant * list_sessions ();
+
+ static void get_session_id_and_path_for_tag (int tag, std::string& id, std::string& path);
+
+ private:
+ void update_sessions_property ();
+ void update_active_session_property ();
+ void update_can_multi_session_property ();
+
+ private:
+ Login1Seat * my_skeleton;
+ std::map<int,MockUser*> my_sessions;
+ int my_active_session;
+ bool my_can_multi_session;
+};
+
+#endif // #ifndef MOCK_LOGIN1_SEAT_H
diff --git a/tests/backend-dbus/mock-object.cc b/tests/backend-dbus/mock-object.cc
new file mode 100644
index 0000000..af9330b
--- /dev/null
+++ b/tests/backend-dbus/mock-object.cc
@@ -0,0 +1,122 @@
+/*
+ * 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 <gio/gio.h>
+
+#include "mock-object.h"
+
+namespace
+{
+ const int TIMEOUT_SECONDS = 5;
+
+ gboolean on_timeout_reached (gpointer loop)
+ {
+ g_main_loop_quit (static_cast<GMainLoop*>(loop));
+ return G_SOURCE_REMOVE;
+ }
+
+ void on_name_acquired (GDBusConnection * connection G_GNUC_UNUSED,
+ const char * name G_GNUC_UNUSED,
+ gpointer loop)
+ {
+ //g_debug ("name '%s' acquired", name);
+ g_main_loop_quit (static_cast<GMainLoop*>(loop));
+ }
+
+ void on_name_lost (GDBusConnection * connection G_GNUC_UNUSED,
+ const char * name G_GNUC_UNUSED,
+ gpointer loop)
+ {
+ //g_debug ("name '%s' lost", name);
+ g_main_loop_quit (static_cast<GMainLoop*>(loop));
+ }
+}
+
+void
+MockObject :: set_skeleton (GDBusInterfaceSkeleton * skeleton)
+{
+ g_assert (skeleton != NULL);
+ g_assert (my_skeleton == NULL);
+ g_assert (g_variant_is_object_path (my_object_path.c_str()));
+ g_assert (my_owner_id == 0);
+
+ my_skeleton = G_DBUS_INTERFACE_SKELETON (g_object_ref (skeleton));
+
+ GError * err = NULL;
+ g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON(my_skeleton),
+ my_bus_connection,
+ my_object_path.c_str(),
+ &err);
+ g_assert_no_error (err);
+
+ my_owner_id = g_bus_own_name_on_connection (my_bus_connection,
+ my_object_name.c_str(),
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ on_name_acquired,
+ on_name_lost,
+ my_loop,
+ NULL);
+
+ // wait for the name to be acquired or timeout, whichever comes first
+ const guint timeout_id = g_timeout_add_seconds (TIMEOUT_SECONDS,
+ on_timeout_reached,
+ my_loop);
+ g_main_loop_run (my_loop);
+ g_assert (g_main_context_find_source_by_id (NULL, timeout_id) != NULL);
+ g_source_remove (timeout_id);
+}
+
+/***
+****
+***/
+
+MockObject :: MockObject (GMainLoop * loop,
+ GDBusConnection * bus_connection,
+ const std::string & object_name,
+ const std::string & object_path):
+ my_owner_id (0),
+ my_loop (g_main_loop_ref (loop)),
+ my_bus_connection (G_DBUS_CONNECTION (g_object_ref (bus_connection))),
+ my_object_name (object_name),
+ my_object_path (object_path),
+ my_skeleton (0)
+{
+}
+
+MockObject :: ~MockObject ()
+{
+ g_main_loop_unref (my_loop);
+
+ if (my_owner_id != 0)
+ {
+ g_bus_unown_name (my_owner_id);
+
+ my_owner_id = 0;
+ }
+
+ if (my_skeleton)
+ {
+ g_dbus_interface_skeleton_unexport (my_skeleton);
+
+ g_clear_object (&my_skeleton);
+ }
+
+ g_clear_object (&my_bus_connection);
+}
diff --git a/tests/backend-dbus/mock-object.h b/tests/backend-dbus/mock-object.h
new file mode 100644
index 0000000..8dc7070
--- /dev/null
+++ b/tests/backend-dbus/mock-object.h
@@ -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/>.
+ */
+
+#ifndef MOCK_OBJECT_H
+#define MOCK_OBJECT_H
+
+#include <string>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+class MockObject
+{
+ public:
+
+ MockObject (GMainLoop * loop,
+ GDBusConnection * bus_connection,
+ const std::string & object_name,
+ const std::string & object_path);
+
+ virtual ~MockObject ();
+
+ const char * name() const { return my_object_name.c_str(); }
+ const char * path() const { return my_object_path.c_str(); }
+
+ GDBusInterfaceSkeleton * skeleton() { return my_skeleton; }
+
+ protected:
+
+ guint my_owner_id;
+ GMainLoop * my_loop;
+ GDBusConnection * my_bus_connection;
+ const std::string my_object_name;
+ const std::string my_object_path;
+ GDBusInterfaceSkeleton * my_skeleton;
+
+ void set_skeleton (GDBusInterfaceSkeleton * skeleton);
+
+ private:
+ // safeguard to make sure we don't copy-by-value...
+ // this object's holding a handful of pointers
+ MockObject (const MockObject& rhs);
+ MockObject& operator= (const MockObject& rhs);
+};
+
+#endif
diff --git a/tests/backend-dbus/mock-screen-saver.cc b/tests/backend-dbus/mock-screen-saver.cc
new file mode 100644
index 0000000..1d3bb11
--- /dev/null
+++ b/tests/backend-dbus/mock-screen-saver.cc
@@ -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/>.
+ */
+
+#include "mock-screen-saver.h"
+
+
+gboolean
+MockScreenSaver :: handle_lock (GnomeScreenSaver * ss,
+ GDBusMethodInvocation * inv,
+ gpointer gself)
+{
+ static_cast<MockScreenSaver*>(gself)->my_last_action = Lock;
+ gnome_screen_saver_complete_lock (ss, inv);
+ return true;
+}
+
+gboolean
+MockScreenSaver :: handle_simulate_user_activity (GnomeScreenSaver * ss,
+ GDBusMethodInvocation * inv,
+ gpointer gself)
+{
+ static_cast<MockScreenSaver*>(gself)->my_last_action = UserActivity;
+ gnome_screen_saver_complete_simulate_user_activity (ss, inv);
+ return true;
+}
+
+/***
+****
+***/
+
+namespace
+{
+ const char * const SCREENSAVER_NAME = "org.gnome.ScreenSaver";
+ const char * const SCREENSAVER_PATH = "/org/gnome/ScreenSaver";
+
+}
+
+MockScreenSaver :: MockScreenSaver (GMainLoop * loop,
+ GDBusConnection * bus_connection):
+ MockObject (loop, bus_connection, SCREENSAVER_NAME, SCREENSAVER_PATH),
+ my_skeleton (gnome_screen_saver_skeleton_new ()),
+ my_last_action (None)
+{
+ g_signal_connect (my_skeleton, "handle-lock",
+ G_CALLBACK(handle_lock), this);
+ g_signal_connect (my_skeleton, "handle-simulate-user-activity",
+ G_CALLBACK(handle_simulate_user_activity), this);
+
+ set_skeleton (G_DBUS_INTERFACE_SKELETON(my_skeleton));
+}
+
+MockScreenSaver :: ~MockScreenSaver ()
+{
+ g_clear_object (&my_skeleton);
+}
diff --git a/tests/backend-dbus/mock-screen-saver.h b/tests/backend-dbus/mock-screen-saver.h
new file mode 100644
index 0000000..c57a4c6
--- /dev/null
+++ b/tests/backend-dbus/mock-screen-saver.h
@@ -0,0 +1,53 @@
+/*
+ * 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 MOCK_SCREENSAVER_H
+#define MOCK_SCREENSAVER_H
+
+#include "mock-object.h" // parent class
+#include "backend-dbus/gnome-screen-saver.h" // GnomeScreenSaver
+
+class MockScreenSaver: public MockObject
+{
+ public:
+
+ MockScreenSaver (GMainLoop * loop,
+ GDBusConnection * bus_connection);
+ virtual ~MockScreenSaver ();
+
+ public:
+
+ enum Action { None, Lock, UserActivity };
+ Action last_action () { return my_last_action; }
+
+ private:
+
+ GnomeScreenSaver * my_skeleton;
+ Action my_last_action;
+
+ static gboolean handle_lock (GnomeScreenSaver *,
+ GDBusMethodInvocation *,
+ gpointer);
+ static gboolean handle_simulate_user_activity (GnomeScreenSaver *,
+ GDBusMethodInvocation *,
+ gpointer);
+
+};
+
+#endif
diff --git a/tests/backend-dbus/mock-session-manager.cc b/tests/backend-dbus/mock-session-manager.cc
new file mode 100644
index 0000000..7a4ce87
--- /dev/null
+++ b/tests/backend-dbus/mock-session-manager.cc
@@ -0,0 +1,66 @@
+/*
+ * 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 "mock-session-manager.h"
+
+gboolean
+MockSessionManager :: handle_logout (GnomeSessionManager * gsm,
+ GDBusMethodInvocation * inv,
+ guint arg,
+ gpointer gself)
+{
+ Action action;
+ switch (arg) {
+ case 0: action = LogoutNormal; break;
+ case 1: action = LogoutQuiet; break;
+ case 2: action = LogoutForce; break;
+ default: action = None; break;
+ }
+ static_cast<MockSessionManager*>(gself)->my_last_action = action;
+ gnome_session_manager_complete_logout (gsm, inv);
+ return true;
+}
+
+ /***
+****
+***/
+
+namespace
+{
+ const char * const SESSION_MANAGER_NAME = "org.gnome.SessionManager";
+ const char * const SESSION_MANAGER_PATH = "/org/gnome/SessionManager";
+
+}
+
+MockSessionManager :: MockSessionManager (GMainLoop * loop,
+ GDBusConnection * bus_connection):
+ MockObject (loop, bus_connection, SESSION_MANAGER_NAME, SESSION_MANAGER_PATH),
+ my_skeleton (gnome_session_manager_skeleton_new ()),
+ my_last_action (None)
+{
+ g_signal_connect (my_skeleton, "handle-logout",
+ G_CALLBACK(handle_logout), this);
+
+ set_skeleton (G_DBUS_INTERFACE_SKELETON(my_skeleton));
+}
+
+MockSessionManager :: ~MockSessionManager ()
+{
+ g_clear_object (&my_skeleton);
+}
diff --git a/tests/backend-dbus/mock-session-manager.h b/tests/backend-dbus/mock-session-manager.h
new file mode 100644
index 0000000..6a21277
--- /dev/null
+++ b/tests/backend-dbus/mock-session-manager.h
@@ -0,0 +1,50 @@
+/*
+ * 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 MOCK_SESSION_MANAGER_H
+#define MOCK_SESSION_MANAGER_H
+
+#include "mock-object.h" // parent class
+#include "backend-dbus/gnome-session-manager.h" // GnomeSessionManager
+
+class MockSessionManager: public MockObject
+{
+ public:
+
+ MockSessionManager (GMainLoop * loop,
+ GDBusConnection * bus_connection);
+ virtual ~MockSessionManager ();
+
+ public:
+
+ enum Action { None, LogoutNormal, LogoutQuiet, LogoutForce };
+ Action last_action () { return my_last_action; }
+
+ private:
+
+ GnomeSessionManager * my_skeleton;
+ Action my_last_action;
+
+ static gboolean handle_logout (GnomeSessionManager *,
+ GDBusMethodInvocation *,
+ guint,
+ gpointer);
+};
+
+#endif
diff --git a/tests/backend-dbus/mock-user.cc b/tests/backend-dbus/mock-user.cc
new file mode 100644
index 0000000..f74b5a4
--- /dev/null
+++ b/tests/backend-dbus/mock-user.cc
@@ -0,0 +1,123 @@
+/*
+ * 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 "mock-user.h"
+
+/***
+****
+***/
+
+const char *
+MockUser :: username () const
+{
+ return accounts_user_get_user_name (my_skeleton);
+}
+
+const char *
+MockUser :: realname () const
+{
+ return accounts_user_get_real_name (my_skeleton);
+}
+
+void
+MockUser :: set_realname (const char * realname)
+{
+ accounts_user_set_real_name (my_skeleton, realname);
+ accounts_user_emit_changed (my_skeleton);
+}
+
+guint
+MockUser :: uid () const
+{
+ return accounts_user_get_uid (my_skeleton);
+}
+
+guint64
+MockUser :: login_frequency () const
+{
+ return accounts_user_get_login_frequency (my_skeleton);
+}
+
+void
+MockUser :: set_system_account (gboolean b)
+{
+ accounts_user_set_system_account (my_skeleton, b);
+}
+
+bool
+MockUser :: is_guest () const
+{
+ // a guest will look like this:
+ // username:[guest-jjbEVV] realname:[Guest] system:[1]
+ return accounts_user_get_system_account (my_skeleton)
+ && !g_ascii_strcasecmp (accounts_user_get_real_name(my_skeleton), "Guest");
+}
+
+/***
+****
+***/
+
+namespace
+{
+ const char * const DBUS_ACCOUNTS_NAME = "org.freedesktop.Accounts";
+
+ static guint next_uid = 1000;
+
+ std::string path_for_uid (guint uid)
+ {
+ char * tmp;
+ std::string ret;
+ const char * const DBUS_ACCOUNTS_PATH = "/org/freedesktop/Accounts";
+ tmp = g_strdup_printf ("%s/User%u", DBUS_ACCOUNTS_PATH, uid);
+ ret = tmp;
+ g_free (tmp);
+ return ret;
+ }
+}
+
+guint
+MockUser :: get_next_uid ()
+{
+ return next_uid++;
+}
+
+
+MockUser :: MockUser (GMainLoop * loop,
+ GDBusConnection * bus_connection,
+ const char * userName,
+ const char * realName,
+ guint64 login_frequency,
+ guint uid_):
+ MockObject (loop, bus_connection, DBUS_ACCOUNTS_NAME, path_for_uid(uid_)),
+ my_skeleton (accounts_user_skeleton_new ())
+{
+ accounts_user_set_uid (my_skeleton, uid_);
+ accounts_user_set_user_name (my_skeleton, userName);
+ accounts_user_set_real_name (my_skeleton, realName);
+ accounts_user_set_login_frequency (my_skeleton, login_frequency);
+ accounts_user_set_system_account (my_skeleton, false);
+
+ set_skeleton (G_DBUS_INTERFACE_SKELETON(my_skeleton));
+}
+
+MockUser :: ~MockUser ()
+{
+ g_signal_handlers_disconnect_by_data (my_skeleton, this);
+ g_clear_object (&my_skeleton);
+}
diff --git a/tests/backend-dbus/mock-user.h b/tests/backend-dbus/mock-user.h
new file mode 100644
index 0000000..c1d3d0f
--- /dev/null
+++ b/tests/backend-dbus/mock-user.h
@@ -0,0 +1,57 @@
+/*
+ * 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 MOCK_USER_H
+#define MOCK_USER_H
+
+#include "mock-object.h" // parent class
+#include "backend-dbus/dbus-user.h" // AccountsUser
+
+class MockUser: public MockObject
+{
+ protected:
+
+ static guint get_next_uid ();
+
+ public:
+
+ MockUser (GMainLoop * loop,
+ GDBusConnection * bus_connection,
+ const char * userName,
+ const char * realName,
+ guint64 login_frequency,
+ guint uid = get_next_uid());
+ virtual ~MockUser ();
+
+ const char * username () const;
+ const char * realname () const;
+ void set_realname (const char *);
+ guint uid () const;
+ guint64 login_frequency () const;
+ //bool system_account() const;
+
+ bool is_guest() const;
+ void set_system_account (gboolean b);
+
+ private:
+
+ AccountsUser * my_skeleton;
+};
+
+#endif
diff --git a/tests/backend-dbus/mock-webcredentials.cc b/tests/backend-dbus/mock-webcredentials.cc
new file mode 100644
index 0000000..22e10b7
--- /dev/null
+++ b/tests/backend-dbus/mock-webcredentials.cc
@@ -0,0 +1,39 @@
+/*
+ * 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 "mock-webcredentials.h"
+
+namespace
+{
+ const char * const MY_NAME = "com.canonical.indicators.webcredentials";
+ const char * const MY_PATH = "/com/canonical/indicators/webcredentials";
+}
+
+MockWebcredentials :: MockWebcredentials (GMainLoop * loop,
+ GDBusConnection * bus_connection):
+ MockObject (loop, bus_connection, MY_NAME, MY_PATH),
+ my_skeleton (webcredentials_skeleton_new ())
+{
+ set_skeleton (G_DBUS_INTERFACE_SKELETON(my_skeleton));
+}
+
+MockWebcredentials :: ~MockWebcredentials ()
+{
+ g_clear_object (&my_skeleton);
+}
diff --git a/tests/backend-dbus/mock-webcredentials.h b/tests/backend-dbus/mock-webcredentials.h
new file mode 100644
index 0000000..fd1b10c
--- /dev/null
+++ b/tests/backend-dbus/mock-webcredentials.h
@@ -0,0 +1,42 @@
+/*
+ * 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 MOCK_WEBCREDENTIALS_H
+#define MOCK_WEBCREDENTIALS_H
+
+#include "mock-object.h" // parent class
+#include "backend-dbus/dbus-webcredentials.h" // Webcredentials
+
+class MockWebcredentials: public MockObject
+{
+ public:
+
+ MockWebcredentials (GMainLoop * loop,
+ GDBusConnection * bus_connection);
+ virtual ~MockWebcredentials ();
+
+ bool has_error () const { return webcredentials_get_error_status (my_skeleton); }
+ void set_error (bool b) const { webcredentials_set_error_status (my_skeleton, b); }
+
+ private:
+
+ Webcredentials * my_skeleton;
+};
+
+#endif
diff --git a/tests/backend-dbus/test-actions.cc b/tests/backend-dbus/test-actions.cc
new file mode 100644
index 0000000..c0f8517
--- /dev/null
+++ b/tests/backend-dbus/test-actions.cc
@@ -0,0 +1,434 @@
+/*
+ * 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 "gtest-mock-dbus-fixture.h"
+
+#include "backend.h"
+#include "backend-dbus/backend-dbus.h"
+
+#define SUPPRESS_KEY "suppress-logout-restart-shutdown"
+
+/***
+****
+***/
+
+class Actions: public GTestMockDBusFixture
+{
+ private:
+
+ typedef GTestMockDBusFixture super;
+
+ protected:
+
+ GCancellable * cancellable;
+ IndicatorSessionActions * actions;
+ GSettings * indicator_settings;
+
+ virtual void SetUp ()
+ {
+ super :: SetUp ();
+
+ // init 'actions'
+ indicator_settings = g_settings_new ("com.canonical.indicator.session");
+ cancellable = g_cancellable_new ();
+ actions = 0;
+ backend_get (cancellable, &actions, NULL, NULL);
+ g_assert (actions != 0);
+ wait_msec (300);
+ }
+
+ virtual void TearDown ()
+ {
+ g_cancellable_cancel (cancellable);
+ g_clear_object (&indicator_settings);
+ g_clear_object (&cancellable);
+ g_clear_object (&actions);
+
+ super :: TearDown ();
+ }
+};
+
+/***
+****
+***/
+
+TEST_F (Actions, HelloWorld)
+{
+ ASSERT_TRUE (true);
+}
+
+namespace
+{
+ static gboolean toggle_can_switch (gpointer settings)
+ {
+ const char * key = "disable-user-switching";
+ gboolean b = g_settings_get_boolean (G_SETTINGS(settings), key);
+ g_settings_set_boolean (G_SETTINGS(settings), key, !b);
+ return G_SOURCE_REMOVE;
+ }
+}
+
+TEST_F (Actions, CanSwitch)
+{
+ const char * schema_id = "org.gnome.desktop.lockdown";
+ const char * settings_key = "disable-user-switching";
+ GSettings * s = g_settings_new (schema_id);
+
+ for (int i=0; i<3; ++i)
+ {
+ bool b;
+ gboolean b2;
+
+ b = login1_seat->can_activate_sessions() && !g_settings_get_boolean (s, settings_key);
+ ASSERT_EQ (b, indicator_session_actions_can_switch (actions));
+ g_object_get (actions, INDICATOR_SESSION_ACTIONS_PROP_CAN_SWITCH, &b2, NULL);
+ ASSERT_EQ (b, b2);
+
+ g_idle_add (toggle_can_switch, s);
+ wait_for_signal (actions, "notify::" INDICATOR_SESSION_ACTIONS_PROP_CAN_SWITCH);
+ }
+
+ g_object_unref (s);
+}
+
+namespace
+{
+ static gboolean toggle_can_lock (gpointer settings)
+ {
+ const char * key = "disable-lock-screen";
+ gboolean b = g_settings_get_boolean (G_SETTINGS(settings), key);
+ g_settings_set_boolean (G_SETTINGS(settings), key, !b);
+ return G_SOURCE_REMOVE;
+ }
+}
+
+TEST_F (Actions, CanLock)
+{
+ const char * schema_id = "org.gnome.desktop.lockdown";
+ const char * settings_key = "disable-lock-screen";
+ GSettings * s = g_settings_new (schema_id);
+
+ for (int i=0; i<3; ++i)
+ {
+ bool b;
+ gboolean b2;
+
+ b = g_settings_get_boolean (s, settings_key);
+ ASSERT_EQ (b, !indicator_session_actions_can_lock (actions));
+ g_object_get (actions, INDICATOR_SESSION_ACTIONS_PROP_CAN_LOCK, &b2, NULL);
+ ASSERT_EQ (b, !b2);
+
+ g_idle_add (toggle_can_lock, s);
+ wait_for_signal (actions, "notify::" INDICATOR_SESSION_ACTIONS_PROP_CAN_LOCK);
+ }
+
+ g_object_unref (s);
+}
+
+namespace
+{
+ static gboolean toggle_can_logout (gpointer settings)
+ {
+ const char * key = "disable-log-out";
+ gboolean b = g_settings_get_boolean (G_SETTINGS(settings), key);
+ g_settings_set_boolean (G_SETTINGS(settings), key, !b);
+ return G_SOURCE_REMOVE;
+ }
+}
+
+TEST_F (Actions, CanLogout)
+{
+ const char * schema_id = "org.gnome.desktop.lockdown";
+ const char * settings_key = "disable-log-out";
+ GSettings * s = g_settings_new (schema_id);
+
+ for (int i=0; i<3; ++i)
+ {
+ bool b;
+ gboolean b2;
+
+ b = g_settings_get_boolean (s, settings_key);
+ ASSERT_EQ (b, !indicator_session_actions_can_logout (actions));
+ g_object_get (actions, INDICATOR_SESSION_ACTIONS_PROP_CAN_LOGOUT, &b2, NULL);
+ ASSERT_EQ (b, !b2);
+
+ g_idle_add (toggle_can_logout, s);
+ wait_for_signal (actions, "notify::" INDICATOR_SESSION_ACTIONS_PROP_CAN_LOGOUT);
+ }
+
+ g_object_unref (s);
+}
+
+TEST_F (Actions, CanSuspend)
+{
+ const std::string can_suspend = login1_manager->can_suspend ();
+ gboolean b = indicator_session_actions_can_suspend (actions);
+ ASSERT_EQ (b, can_suspend=="yes" || can_suspend=="challenge");
+}
+
+TEST_F (Actions, CanHibernate)
+{
+ const std::string can_hibernate = login1_manager->can_hibernate ();
+ gboolean b = indicator_session_actions_can_hibernate (actions);
+ ASSERT_EQ (b, can_hibernate=="yes" || can_hibernate=="challenge");
+}
+
+TEST_F (Actions, Reboot)
+{
+ ASSERT_TRUE (login1_manager->last_action().empty());
+ ASSERT_FALSE (g_settings_get_boolean (indicator_settings, SUPPRESS_KEY));
+
+ // confirm that user is prompted
+ // and that no action is taken when the user cancels the dialog
+ indicator_session_actions_reboot (actions);
+ wait_msec (50);
+ ASSERT_TRUE (end_session_dialog->is_open());
+ end_session_dialog->cancel();
+ wait_msec (50);
+ ASSERT_TRUE (login1_manager->last_action().empty());
+
+ // confirm that user is prompted
+ // and that no action is taken when the user cancels the dialog
+ indicator_session_actions_reboot (actions);
+ wait_msec (50);
+ ASSERT_TRUE (end_session_dialog->is_open ());
+ end_session_dialog->confirm_reboot ();
+ wait_msec (100);
+ ASSERT_EQ (login1_manager->last_action(), "reboot");
+
+ // confirm that we try to reboot w/o prompting
+ // if prompting is disabled
+ login1_manager->clear_last_action ();
+ ASSERT_EQ ("", login1_manager->last_action());
+ g_settings_set_boolean (indicator_settings, SUPPRESS_KEY, TRUE);
+ wait_msec (50);
+ ASSERT_TRUE (login1_manager->last_action().empty());
+ wait_msec (50);
+ indicator_session_actions_reboot (actions);
+ wait_msec (50);
+ ASSERT_EQ ("reboot", login1_manager->last_action());
+
+ g_settings_reset (indicator_settings, SUPPRESS_KEY);
+}
+
+TEST_F (Actions, PowerOff)
+{
+ ASSERT_TRUE (login1_manager->last_action().empty());
+
+ // confirm that user is prompted
+ // and that no action is taken when the user cancels the dialog
+ indicator_session_actions_power_off (actions);
+ wait_msec (50);
+ ASSERT_TRUE (end_session_dialog->is_open());
+ end_session_dialog->cancel();
+ wait_msec (50);
+ ASSERT_TRUE (login1_manager->last_action().empty());
+
+ // confirm that user is prompted
+ // and that no action is taken when the user cancels the dialog
+ indicator_session_actions_power_off (actions);
+ wait_msec (50);
+ ASSERT_TRUE (end_session_dialog->is_open ());
+ end_session_dialog->confirm_shutdown ();
+ wait_msec (100);
+ ASSERT_EQ (login1_manager->last_action(), "power-off");
+
+ // confirm that we try to shutdown w/o prompting
+ // if the EndSessionDialog isn't available
+ // if prompting is disabled
+ login1_manager->clear_last_action ();
+ ASSERT_EQ ("", login1_manager->last_action());
+ g_settings_set_boolean (indicator_settings, SUPPRESS_KEY, TRUE);
+ wait_msec (50);
+ indicator_session_actions_power_off (actions);
+ wait_msec (50);
+ ASSERT_EQ (login1_manager->last_action(), "power-off");
+
+ g_settings_reset (indicator_settings, SUPPRESS_KEY);
+}
+
+TEST_F (Actions, Logout)
+{
+ ASSERT_EQ (MockSessionManager::None, session_manager->last_action ());
+
+ // confirm that user is prompted
+ // and that no action is taken when the user cancels the dialog
+ indicator_session_actions_logout (actions);
+ wait_msec (50);
+ ASSERT_TRUE (end_session_dialog->is_open());
+ end_session_dialog->cancel();
+ wait_msec (50);
+ ASSERT_EQ (MockSessionManager::None, session_manager->last_action ());
+
+ // confirm that user is prompted
+ // and that no action is taken when the user cancels the dialog
+ indicator_session_actions_logout (actions);
+ wait_msec (50);
+ ASSERT_TRUE (end_session_dialog->is_open ());
+ end_session_dialog->confirm_logout ();
+ wait_msec (100);
+ ASSERT_EQ (MockSessionManager::LogoutQuiet, session_manager->last_action ());
+
+ // confirm that we try to call SessionManager::LogoutQuet
+ // when prompts are disabled
+ login1_manager->clear_last_action ();
+ ASSERT_EQ ("", login1_manager->last_action());
+ g_settings_set_boolean (indicator_settings, SUPPRESS_KEY, TRUE);
+ wait_msec (50);
+ indicator_session_actions_logout (actions);
+ wait_msec (50);
+ ASSERT_EQ (MockSessionManager::LogoutQuiet, session_manager->last_action ());
+
+ g_settings_reset (indicator_settings, SUPPRESS_KEY);
+}
+
+TEST_F (Actions, Suspend)
+{
+ ASSERT_TRUE (login1_manager->last_action().empty());
+ indicator_session_actions_suspend (actions);
+ wait_msec (50);
+ ASSERT_EQ (login1_manager->last_action(), "suspend");
+}
+
+TEST_F (Actions, Hibernate)
+{
+ ASSERT_TRUE (login1_manager->last_action().empty());
+ indicator_session_actions_hibernate (actions);
+ wait_msec (50);
+ ASSERT_EQ (login1_manager->last_action(), "hibernate");
+}
+
+TEST_F (Actions, SwitchToScreensaver)
+{
+ ASSERT_EQ (MockScreenSaver::None, screen_saver->last_action());
+ indicator_session_actions_switch_to_screensaver (actions);
+ wait_msec (50);
+ ASSERT_EQ (MockScreenSaver::Lock, screen_saver->last_action());
+}
+
+TEST_F (Actions, SwitchToGreeter)
+{
+ ASSERT_NE (MockDisplayManagerSeat::GREETER, dm_seat->last_action());
+ indicator_session_actions_switch_to_greeter (actions);
+ wait_msec (50);
+ ASSERT_EQ (MockDisplayManagerSeat::GREETER, dm_seat->last_action());
+}
+
+TEST_F (Actions, SwitchToGuest)
+{
+ // allow guests
+ dm_seat->set_guest_allowed (true);
+
+ // set up a guest
+ MockUser * guest_user = new MockUser (loop, conn, "guest-zzbEVV", "Guest", 10);
+ guest_user->set_system_account (true);
+ accounts->add_user (guest_user);
+ int guest_session_tag = login1_manager->add_session (login1_seat, guest_user);
+
+ // try to switch to guest
+ indicator_session_actions_switch_to_guest (actions);
+ wait_for_signal (login1_seat->skeleton(), "notify::active-session");
+ ASSERT_EQ (guest_session_tag, login1_seat->active_session());
+ wait_msec (50);
+}
+
+TEST_F (Actions, SwitchToUsername)
+{
+ const char * const dr1_username = "whartnell";
+ const char * const dr2_username = "ptroughton";
+ MockUser * dr1_user;
+ MockUser * dr2_user;
+ int dr1_session;
+ int dr2_session;
+
+ dr1_user = accounts->find_by_username (dr1_username);
+ dr1_session = login1_manager->add_session (login1_seat, dr1_user);
+
+ dr2_user = accounts->find_by_username (dr2_username);
+ dr2_session = login1_manager->add_session (login1_seat, dr2_user);
+
+ indicator_session_actions_switch_to_username (actions, dr1_username);
+ wait_for_signal (login1_seat->skeleton(), "notify::active-session");
+ ASSERT_EQ (dr1_session, login1_seat->active_session());
+ wait_msec (50);
+
+ indicator_session_actions_switch_to_username (actions, dr2_username);
+ wait_for_signal (login1_seat->skeleton(), "notify::active-session");
+ ASSERT_EQ (dr2_session, login1_seat->active_session());
+ wait_msec (50);
+
+ indicator_session_actions_switch_to_username (actions, dr1_username);
+ wait_for_signal (login1_seat->skeleton(), "notify::active-session");
+ ASSERT_EQ (dr1_session, login1_seat->active_session());
+ wait_msec (50);
+}
+
+TEST_F (Actions, HasOnlineAccountError)
+{
+ bool b;
+ gboolean gb;
+
+ b = webcredentials->has_error ();
+ ASSERT_EQ (b, indicator_session_actions_has_online_account_error (actions));
+ g_object_get (actions, INDICATOR_SESSION_ACTIONS_PROP_HAS_ONLINE_ACCOUNT_ERROR, &gb, NULL);
+ ASSERT_EQ (b, gb);
+
+ b = !b;
+ webcredentials->set_error (b);
+ wait_msec (50);
+ ASSERT_EQ (b, indicator_session_actions_has_online_account_error (actions));
+ g_object_get (actions, INDICATOR_SESSION_ACTIONS_PROP_HAS_ONLINE_ACCOUNT_ERROR, &gb, NULL);
+ ASSERT_EQ (b, gb);
+
+ b = !b;
+ webcredentials->set_error (b);
+ wait_msec (50);
+ ASSERT_EQ (b, indicator_session_actions_has_online_account_error (actions));
+ g_object_get (actions, INDICATOR_SESSION_ACTIONS_PROP_HAS_ONLINE_ACCOUNT_ERROR, &gb, NULL);
+ ASSERT_EQ (b, gb);
+}
+
+namespace
+{
+ static gboolean toggle_suppress (gpointer settings)
+ {
+ const char * key = SUPPRESS_KEY;
+ gboolean b = g_settings_get_boolean (G_SETTINGS(settings), key);
+ g_settings_set_boolean (G_SETTINGS(settings), key, !b);
+ return G_SOURCE_REMOVE;
+ }
+}
+
+TEST_F (Actions, SuppressPrompts)
+{
+ for (int i=0; i<3; ++i)
+ {
+ bool b;
+ gboolean b2;
+
+ b = indicator_session_actions_can_prompt (actions);
+ b2 = !g_settings_get_boolean (indicator_settings, SUPPRESS_KEY);
+ ASSERT_EQ (b, b2);
+
+ g_idle_add (toggle_suppress, indicator_settings);
+ wait_for_signal (actions, "notify::" INDICATOR_SESSION_ACTIONS_PROP_CAN_PROMPT);
+ }
+
+ g_settings_reset (indicator_settings, SUPPRESS_KEY);
+}
diff --git a/tests/backend-dbus/test-guest.cc b/tests/backend-dbus/test-guest.cc
new file mode 100644
index 0000000..f71d445
--- /dev/null
+++ b/tests/backend-dbus/test-guest.cc
@@ -0,0 +1,193 @@
+/*
+ * 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 "gtest-mock-dbus-fixture.h"
+
+#include "backend.h"
+#include "backend-dbus/backend-dbus.h"
+
+/***
+****
+***/
+
+class Guest: public GTestMockDBusFixture
+{
+ private:
+
+ typedef GTestMockDBusFixture super;
+
+ protected:
+
+ GCancellable * cancellable;
+ IndicatorSessionGuest * guest;
+
+ virtual void SetUp ()
+ {
+ super :: SetUp ();
+
+ // get the guest-dbus
+ cancellable = g_cancellable_new ();
+ guest = 0;
+ backend_get (cancellable, NULL, NULL, &guest);
+ wait_msec (100);
+
+ // test the default state
+ ASSERT_TRUE (guest != 0);
+ ASSERT_FALSE (indicator_session_guest_is_allowed (guest));
+ ASSERT_FALSE (indicator_session_guest_is_logged_in (guest));
+ ASSERT_FALSE (indicator_session_guest_is_active (guest));
+ }
+
+ virtual void TearDown ()
+ {
+ g_cancellable_cancel (cancellable);
+ g_clear_object (&cancellable);
+ g_clear_object (&guest);
+
+ super :: TearDown ();
+ }
+
+ protected:
+
+ void add_mock_guest (MockUser *& guest_user,
+ int & guest_session_tag)
+ {
+ guest_user = new MockUser (loop, conn, "guest-jjbEVV", "Guest", 10, 100);
+ accounts->add_user (guest_user);
+ guest_user->set_system_account (true);
+ guest_session_tag = login1_manager->add_session (login1_seat, guest_user);
+ }
+};
+
+/**
+ * Confirms that the Fixture's SetUp() and TearDown() work
+ */
+TEST_F (Guest, HelloWorld)
+{
+ ASSERT_TRUE (true);
+}
+
+/**
+ * Toggle in the DM whether or not guests are allowed.
+ * Confirm that "guest" reflects the changes.
+ */
+TEST_F (Guest, Allowed)
+{
+ dm_seat->set_guest_allowed (true);
+ wait_for_signal (guest, "notify::guest-is-allowed");
+ ASSERT_TRUE (indicator_session_guest_is_allowed (guest));
+ ASSERT_FALSE (indicator_session_guest_is_logged_in (guest));
+ ASSERT_FALSE (indicator_session_guest_is_active (guest));
+
+ dm_seat->set_guest_allowed (false);
+ wait_for_signal (guest, "notify::guest-is-allowed");
+ ASSERT_FALSE (indicator_session_guest_is_allowed (guest));
+ ASSERT_FALSE (indicator_session_guest_is_logged_in (guest));
+ ASSERT_FALSE (indicator_session_guest_is_active (guest));
+}
+
+/**
+ * Have a guest user log in & out.
+ * Confirm that "guest" reflects the changes.
+ */
+TEST_F (Guest, Login)
+{
+ gboolean b;
+
+ dm_seat->set_guest_allowed (true);
+
+ // Log a Guest in
+ // And confirm that guest's is_login changes to true
+ MockUser * guest_user;
+ int session_tag;
+ add_mock_guest (guest_user, session_tag);
+ wait_for_signal (guest, "notify::guest-is-logged-in");
+ ASSERT_TRUE (indicator_session_guest_is_allowed (guest));
+ ASSERT_TRUE (indicator_session_guest_is_logged_in (guest));
+ g_object_get (guest, INDICATOR_SESSION_GUEST_PROPERTY_LOGGED_IN, &b,NULL);
+ ASSERT_TRUE (b);
+ ASSERT_FALSE (indicator_session_guest_is_active (guest));
+
+ // Log the Guest User out
+ // and confirm that guest's is_login changes to false
+ login1_manager->remove_session (login1_seat, session_tag);
+ accounts->remove_user (guest_user);
+ delete guest_user;
+ wait_for_signal (guest, "notify::guest-is-logged-in");
+ ASSERT_TRUE (indicator_session_guest_is_allowed (guest));
+ ASSERT_FALSE (indicator_session_guest_is_logged_in (guest));
+ g_object_get (guest, INDICATOR_SESSION_GUEST_PROPERTY_LOGGED_IN, &b,NULL);
+ ASSERT_FALSE (b);
+ ASSERT_FALSE (indicator_session_guest_is_active (guest));
+}
+
+/**
+ * Activate a Guest session, then activate a different session.
+ * Confirm that "guest" reflects the changes.
+ */
+TEST_F (Guest, Active)
+{
+ gboolean b;
+ const int user_session_tag = login1_seat->active_session();
+
+ dm_seat->set_guest_allowed (true);
+ MockUser * guest_user;
+ int guest_session_tag;
+ add_mock_guest (guest_user, guest_session_tag);
+
+ // Activate the guest session
+ // and confirm that guest's is_active changes to true
+ login1_seat->activate_session (guest_session_tag);
+ wait_for_signal (guest, "notify::guest-is-active-session");
+ ASSERT_TRUE (indicator_session_guest_is_allowed (guest));
+ ASSERT_TRUE (indicator_session_guest_is_logged_in (guest));
+ ASSERT_TRUE (indicator_session_guest_is_active (guest));
+ g_object_get (guest, INDICATOR_SESSION_GUEST_PROPERTY_ACTIVE, &b,NULL);
+ ASSERT_TRUE (b);
+
+ // Activate a non-guest session
+ // and confirm that guest's is_active changes to false
+ login1_seat->activate_session (user_session_tag);
+ wait_for_signal (guest, "notify::guest-is-active-session");
+ ASSERT_TRUE (indicator_session_guest_is_allowed (guest));
+ ASSERT_TRUE (indicator_session_guest_is_logged_in (guest));
+ ASSERT_FALSE (indicator_session_guest_is_active (guest));
+ g_object_get (guest, INDICATOR_SESSION_GUEST_PROPERTY_ACTIVE, &b,NULL);
+ ASSERT_FALSE (b);
+}
+
+/**
+ * Activate a guest session using the "guest" API.
+ * Confirm that the guest session gets activated on the bus.
+ */
+TEST_F (Guest, Activate)
+{
+ dm_seat->set_guest_allowed (true);
+ wait_for_signal (guest, "notify::guest-is-allowed");
+
+ MockUser * guest_user;
+ int guest_session_tag;
+ add_mock_guest (guest_user, guest_session_tag);
+
+ indicator_session_guest_switch_to_guest (guest);
+
+ wait_for_signal (login1_seat->skeleton(), "notify::active-session");
+ ASSERT_EQ (guest_session_tag, login1_seat->active_session());
+ wait_msec (50);
+}
diff --git a/tests/backend-dbus/test-users.cc b/tests/backend-dbus/test-users.cc
new file mode 100644
index 0000000..dccacbb
--- /dev/null
+++ b/tests/backend-dbus/test-users.cc
@@ -0,0 +1,381 @@
+/*
+ * 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 "gtest-mock-dbus-fixture.h"
+
+#include "backend.h"
+#include "backend-dbus/backend-dbus.h"
+
+/***
+****
+***/
+
+class Users: public GTestMockDBusFixture
+{
+ private:
+
+ typedef GTestMockDBusFixture super;
+
+ protected:
+
+ GCancellable * cancellable;
+ IndicatorSessionUsers * users;
+
+ virtual void SetUp ()
+ {
+ super :: SetUp ();
+
+ init_event_keys (0);
+
+ // init 'users'
+ cancellable = g_cancellable_new ();
+ users = 0;
+ backend_get (cancellable, NULL, &users, NULL);
+ g_assert (users != 0);
+
+ // wait for the users added by GTestMockDBusFixture::SetUp() to show up
+ wait_for_signals (users, INDICATOR_SESSION_USERS_SIGNAL_USER_ADDED, 12);
+ init_event_keys (0);
+ }
+
+ virtual void TearDown ()
+ {
+ g_cancellable_cancel (cancellable);
+ g_clear_object (&cancellable);
+ g_clear_object (&users);
+
+ super :: TearDown ();
+ }
+
+ protected:
+
+ void compare_user (const MockUser * mu, const IndicatorSessionUser * isu, const std::string& user_state)
+ {
+ ASSERT_EQ (user_state, login1_seat->user_state (mu->uid()));
+ ASSERT_EQ (mu->uid(), isu->uid);
+ ASSERT_EQ (mu->login_frequency(), isu->login_frequency);
+ ASSERT_STREQ (mu->username(), isu->user_name);
+ ASSERT_STREQ (mu->realname(), isu->real_name);
+ ASSERT_EQ (mu->uid(), isu->uid);
+ ASSERT_EQ (user_state!="offline", isu->is_logged_in);
+ ASSERT_EQ (user_state=="active", isu->is_current_user);
+ }
+
+ void compare_user (const MockUser * mu, guint uid, const std::string& user_state)
+ {
+ IndicatorSessionUser * isu;
+ isu = indicator_session_users_get_user (users, uid);
+ compare_user (mu, isu, user_state);
+ indicator_session_user_free (isu);
+ }
+
+ void compare_user (guint uid, const std::string& user_state)
+ {
+ IndicatorSessionUser * isu = indicator_session_users_get_user (users, uid);
+ MockUser * mu = accounts->find_by_uid (uid);
+ compare_user (mu, isu, user_state);
+ indicator_session_user_free (isu);
+ }
+
+ private:
+
+ void init_event_keys (size_t n)
+ {
+ expected_event_count = n;
+ event_keys.clear();
+ }
+
+ static gboolean
+ wait_for_signals__timeout (gpointer name)
+ {
+ g_error ("%s: timed out waiting for signal '%s'", G_STRLOC, (char*)name);
+ return G_SOURCE_REMOVE;
+ }
+
+ static void
+ wait_for_signals__event (IndicatorSessionUser * u G_GNUC_UNUSED,
+ guint uid,
+ gpointer gself)
+ {
+ Users * self = static_cast<Users*>(gself);
+
+ self->event_keys.push_back (uid);
+
+ if (self->event_keys.size() == self->expected_event_count)
+ g_main_loop_quit (self->loop);
+ }
+
+ protected:
+
+ std::vector<guint> event_keys;
+ size_t expected_event_count;
+
+ void wait_for_signals (gpointer o, const gchar * name, size_t n)
+ {
+ const int timeout_seconds = 5; // arbitrary
+
+ init_event_keys (n);
+
+ guint handler_id = g_signal_connect (o, name,
+ G_CALLBACK(wait_for_signals__event),
+ this);
+ gulong timeout_id = g_timeout_add_seconds (timeout_seconds,
+ wait_for_signals__timeout,
+ (gpointer)name);
+ g_main_loop_run (loop);
+ g_source_remove (timeout_id);
+ g_signal_handler_disconnect (o, handler_id);
+ }
+};
+
+/***
+****
+***/
+
+/**
+ * Confirm that the fixture's SetUp() and TearDown() work
+ */
+TEST_F (Users, HelloWorld)
+{
+ ASSERT_TRUE (true);
+}
+
+/**
+ * Confirm that 'users' can get the cached users from our Mock Accounts
+ */
+TEST_F (Users, InitialUsers)
+{
+ GList * l;
+ GList * uids = indicator_session_users_get_uids (users);
+
+ ASSERT_EQ (12, g_list_length (uids));
+
+ for (l=uids; l!=NULL; l=l->next)
+ {
+ const guint uid = GPOINTER_TO_UINT (l->data);
+ compare_user (uid, login1_seat->user_state (uid));
+ }
+
+ g_list_free (uids);
+}
+
+/**
+ * Confirm that 'users' can tell when a new user is added
+ */
+TEST_F (Users, UserAdded)
+{
+ MockUser * mu;
+
+ mu = new MockUser (loop, conn, "pcushing", "Peter Cushing", 2);
+ accounts->add_user (mu);
+ ASSERT_EQ (0, event_keys.size());
+ wait_for_signals (users, INDICATOR_SESSION_USERS_SIGNAL_USER_ADDED, 1);
+ ASSERT_EQ (1, event_keys.size());
+ compare_user (mu, event_keys[0], "offline");
+}
+
+/**
+ * Confirm that 'users' can tell when a user is removed
+ */
+TEST_F (Users, UserRemoved)
+{
+ MockUser * mu = accounts->find_by_username ("pdavison");
+
+ /* confirm that users knows about pdavison */
+ IndicatorSessionUser * isu = indicator_session_users_get_user (users, mu->uid());
+ ASSERT_TRUE (isu != NULL);
+ compare_user (mu, isu, "offline");
+ g_clear_pointer (&isu, indicator_session_user_free);
+
+ /* on the bus, remove pdavison. */
+ accounts->remove_user (mu);
+
+ /* now, users should emit a 'user removed' signal... */
+ ASSERT_EQ (0, event_keys.size());
+ wait_for_signals (users, INDICATOR_SESSION_USERS_SIGNAL_USER_REMOVED, 1);
+ ASSERT_EQ (1, event_keys.size());
+
+ /* confirm that users won't give us pdavison's info */
+ isu = indicator_session_users_get_user (users, mu->uid());
+ ASSERT_TRUE (isu == NULL);
+
+ /* confirm that users won't give us pdavison's uid */
+ GList * uids = indicator_session_users_get_uids (users);
+ ASSERT_TRUE (g_list_find (uids, GUINT_TO_POINTER(mu->uid())) == NULL);
+ g_list_free (uids);
+
+ delete mu;
+}
+
+/**
+ * Confirm that 'users' notices when a user's real name changes
+ */
+TEST_F (Users, RealnameChanged)
+{
+ MockUser * mu;
+
+ mu = accounts->find_by_username ("pdavison");
+ const char * const realname = "Peter M. G. Moffett";
+ mu->set_realname (realname);
+ ASSERT_NE (mu->realname(), realname);
+ ASSERT_STREQ (mu->realname(), realname);
+ wait_for_signals (users, INDICATOR_SESSION_USERS_SIGNAL_USER_CHANGED, 1);
+ ASSERT_EQ (1, event_keys.size());
+ compare_user (mu, event_keys[0], "offline");
+}
+
+/**
+ * Confirm that 'users' notices when users log in and out
+ */
+TEST_F (Users, LogInLogOut)
+{
+ // The fist doctor logs in.
+ // Confirm that 'users' notices.
+ MockUser * mu = accounts->find_by_username ("whartnell");
+ ASSERT_EQ (login1_seat->user_state (mu->uid()), "offline");
+ const int session_tag = login1_manager->add_session (login1_seat, mu);
+ wait_for_signals (users, INDICATOR_SESSION_USERS_SIGNAL_USER_CHANGED, 1);
+ ASSERT_EQ (1, event_keys.size());
+ compare_user (mu, event_keys[0], "online");
+
+ // The first doctor logs out.
+ // Confirm that 'users' notices.
+ login1_manager->remove_session (login1_seat, session_tag);
+ wait_for_signals (users, INDICATOR_SESSION_USERS_SIGNAL_USER_CHANGED, 1);
+ ASSERT_EQ (1, event_keys.size());
+ compare_user (mu, event_keys[0], "offline");
+}
+
+/**
+ * Confirm that 'users' notices when the active session changes
+ */
+TEST_F (Users, ActivateSession)
+{
+ // confirm preconditions: msmith is active, msmith is offline
+ MockUser * const whartnell = accounts->find_by_username ("whartnell");
+ ASSERT_EQ (login1_seat->user_state (whartnell->uid()), "offline");
+ MockUser * const msmith = accounts->find_by_username ("msmith");
+ ASSERT_EQ (login1_seat->user_state (msmith->uid()), "active");
+
+ // whartnell logs in... confirm that 'users' notices
+ login1_manager->add_session (login1_seat, whartnell);
+ wait_for_signals (users, INDICATOR_SESSION_USERS_SIGNAL_USER_CHANGED, 1);
+ ASSERT_EQ (1, event_keys.size());
+ compare_user (whartnell, event_keys[0], "online");
+
+ // activate whartnell's session... confirm that 'users' sees:
+ // 1. msmith changes from 'active' to 'online'
+ // 2. whartnell changes from 'online' to 'active'
+ login1_seat->switch_to_user (whartnell->username());
+ wait_for_signals (users, INDICATOR_SESSION_USERS_SIGNAL_USER_CHANGED, 2);
+ ASSERT_EQ (2, event_keys.size());
+ compare_user (msmith, event_keys[0], "online");
+ compare_user (whartnell, event_keys[1], "active");
+
+ // reverse the test
+ login1_seat->switch_to_user (msmith->username());
+ wait_for_signals (users, INDICATOR_SESSION_USERS_SIGNAL_USER_CHANGED, 2);
+ ASSERT_EQ (2, event_keys.size());
+ compare_user (whartnell, event_keys[0], "online");
+ compare_user (msmith, event_keys[1], "active");
+}
+
+/**
+ * Confirm that we can change the active session via users' API.
+ * This is nearly the same as ActivateSession but uses users' API
+ */
+TEST_F (Users, ActivateUser)
+{
+ // confirm preconditions: msmith is active, msmith is offline
+ MockUser * const whartnell = accounts->find_by_username ("whartnell");
+ ASSERT_EQ (login1_seat->user_state (whartnell->uid()), "offline");
+ MockUser * const msmith = accounts->find_by_username ("msmith");
+ ASSERT_EQ (login1_seat->user_state (msmith->uid()), "active");
+
+ // whartnell logs in... confirm that 'users' notices
+ login1_manager->add_session (login1_seat, whartnell);
+ wait_for_signals (users, INDICATOR_SESSION_USERS_SIGNAL_USER_CHANGED, 1);
+ ASSERT_EQ (1, event_keys.size());
+ compare_user (whartnell, event_keys[0], "online");
+
+ // activate whartnell's session... confirm that 'users' sees:
+ // 1. msmith changes from 'active' to 'online'
+ // 2. whartnell changes from 'online' to 'active'
+ indicator_session_users_activate_user (users, whartnell->uid());
+ wait_for_signals (users, INDICATOR_SESSION_USERS_SIGNAL_USER_CHANGED, 2);
+ ASSERT_EQ (2, event_keys.size());
+ compare_user (msmith, event_keys[0], "online");
+ compare_user (whartnell, event_keys[1], "active");
+
+ // reverse the test
+ indicator_session_users_activate_user (users, msmith->uid());
+ wait_for_signals (users, INDICATOR_SESSION_USERS_SIGNAL_USER_CHANGED, 2);
+ ASSERT_EQ (2, event_keys.size());
+ compare_user (whartnell, event_keys[0], "online");
+ compare_user (msmith, event_keys[1], "active");
+}
+
+/**
+ * Confirm that adding a Guest doesn't show up in the users list
+ */
+TEST_F (Users, UnwantedGuest)
+{
+ GList * uids;
+
+ uids = indicator_session_users_get_uids (users);
+ const size_t n = g_list_length (uids);
+ g_list_free (uids);
+
+ MockUser * mu = new MockUser (loop, conn, "guest-jjbEVV", "Guest", 1);
+ mu->set_system_account (true);
+ accounts->add_user (mu);
+ wait_msec (50);
+
+ uids = indicator_session_users_get_uids (users);
+ ASSERT_EQ (n, g_list_length (uids));
+ g_list_free (uids);
+}
+
+
+/**
+ * Confirm that we can detect live sessions
+ */
+TEST_F (Users, LiveSession)
+{
+ gboolean b;
+
+ // not initially a live session
+ ASSERT_FALSE (indicator_session_users_is_live_session (users));
+ g_object_get (users, INDICATOR_SESSION_USERS_PROP_IS_LIVE_SESSION, &b, NULL);
+ ASSERT_FALSE (b);
+
+ // now add the criteria for a live session
+ MockUser * live_user = new MockUser (loop, conn, "ubuntu", "Ubuntu", 1, 999);
+ live_user->set_system_account (true);
+ accounts->add_user (live_user);
+ const int session_tag = login1_manager->add_session (login1_seat, live_user);
+ wait_msec (100);
+ login1_seat->activate_session (session_tag);
+ wait_for_signal (users, "notify::" INDICATOR_SESSION_USERS_PROP_IS_LIVE_SESSION);
+
+ // confirm the backend thinks it's a live session
+ ASSERT_TRUE (indicator_session_users_is_live_session (users));
+ g_object_get (users, INDICATOR_SESSION_USERS_PROP_IS_LIVE_SESSION, &b, NULL);
+ ASSERT_TRUE (b);
+}
diff --git a/tests/backend-mock-actions.c b/tests/backend-mock-actions.c
new file mode 100644
index 0000000..25a606f
--- /dev/null
+++ b/tests/backend-mock-actions.c
@@ -0,0 +1,243 @@
+/*
+ * 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 <gio/gio.h>
+
+#include "backend-mock.h"
+#include "backend-mock-actions.h"
+
+G_DEFINE_TYPE (IndicatorSessionActionsMock,
+ indicator_session_actions_mock,
+ INDICATOR_TYPE_SESSION_ACTIONS)
+
+/***
+**** Virtual Functions
+***/
+
+static gboolean
+my_can_lock (IndicatorSessionActions * self G_GNUC_UNUSED)
+{
+ return g_settings_get_boolean (mock_settings, "can-lock");
+}
+
+static gboolean
+my_can_logout (IndicatorSessionActions * self G_GNUC_UNUSED)
+{
+ return g_settings_get_boolean (mock_settings, "can-logout");
+}
+
+static gboolean
+my_can_reboot (IndicatorSessionActions * self G_GNUC_UNUSED)
+{
+ return g_settings_get_boolean (mock_settings, "can-reboot");
+}
+
+static gboolean
+my_can_switch (IndicatorSessionActions * self G_GNUC_UNUSED)
+{
+ return g_settings_get_boolean (mock_settings, "can-switch-sessions");
+}
+
+static gboolean
+my_can_suspend (IndicatorSessionActions * self G_GNUC_UNUSED)
+{
+ return g_settings_get_boolean (mock_settings, "can-suspend");
+}
+
+static gboolean
+my_can_hibernate (IndicatorSessionActions * self G_GNUC_UNUSED)
+{
+ return g_settings_get_boolean (mock_settings, "can-hibernate");
+}
+
+static void
+my_logout (IndicatorSessionActions * self G_GNUC_UNUSED)
+{
+ g_settings_set_string (mock_settings, "last-command", "logout");
+}
+
+static void
+my_suspend (IndicatorSessionActions * self G_GNUC_UNUSED)
+{
+ g_settings_set_string (mock_settings, "last-command", "suspend");
+}
+
+static void
+my_hibernate (IndicatorSessionActions * self G_GNUC_UNUSED)
+{
+ g_settings_set_string (mock_settings, "last-command", "hibernate");
+}
+
+static void
+my_reboot (IndicatorSessionActions * self G_GNUC_UNUSED)
+{
+ g_settings_set_string (mock_settings, "last-command", "reboot");
+}
+
+static void
+my_power_off (IndicatorSessionActions * self G_GNUC_UNUSED)
+{
+ g_settings_set_string (mock_settings, "last-command", "power-off");
+}
+
+static void
+my_switch_to_screensaver (IndicatorSessionActions * self G_GNUC_UNUSED)
+{
+ g_settings_set_string (mock_settings, "last-command", "switch-to-screensaver");
+}
+
+static void
+my_switch_to_greeter (IndicatorSessionActions * self G_GNUC_UNUSED)
+{
+ g_settings_set_string (mock_settings, "last-command", "switch-to-greeter");
+}
+
+static void
+my_switch_to_guest (IndicatorSessionActions * self G_GNUC_UNUSED)
+{
+ g_settings_set_string (mock_settings, "last-command", "switch-to-guest");
+}
+
+static void
+my_switch_to_username (IndicatorSessionActions * self G_GNUC_UNUSED,
+ const char * username)
+{
+ gchar * str = g_strdup_printf ("switch-to-user::%s", username);
+ g_settings_set_string (mock_settings, "last-command", str);
+}
+
+static void
+my_help (IndicatorSessionActions * self G_GNUC_UNUSED)
+{
+ g_settings_set_string (mock_settings, "last-command", "help");
+}
+
+static void
+my_about (IndicatorSessionActions * self G_GNUC_UNUSED)
+{
+ g_settings_set_string (mock_settings, "last-command", "about");
+}
+
+static void
+my_settings (IndicatorSessionActions * self G_GNUC_UNUSED)
+{
+ g_settings_set_string (mock_settings, "last-command", "settings");
+}
+
+static void
+my_online_accounts (IndicatorSessionActions * self G_GNUC_UNUSED)
+{
+ g_settings_set_string (mock_settings, "last-command", "online-accounts");
+}
+
+static gboolean
+my_can_prompt (IndicatorSessionActions * self G_GNUC_UNUSED)
+{
+ return g_settings_get_boolean (mock_settings, "can-prompt");
+}
+
+static gboolean
+my_has_online_account_error (IndicatorSessionActions * self G_GNUC_UNUSED)
+{
+ return g_settings_get_boolean (mock_settings, "has-online-account-error");
+}
+
+static void
+my_dispose (GObject * o)
+{
+ G_OBJECT_CLASS (indicator_session_actions_mock_parent_class)->dispose (o);
+}
+
+static void
+my_finalize (GObject * o)
+{
+ G_OBJECT_CLASS (indicator_session_actions_mock_parent_class)->finalize (o);
+}
+
+/***
+**** GObject Boilerplate
+***/
+
+static void
+/* cppcheck-suppress unusedFunction */
+indicator_session_actions_mock_class_init (IndicatorSessionActionsMockClass * 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;
+}
+
+static void
+/* cppcheck-suppress unusedFunction */
+indicator_session_actions_mock_init (IndicatorSessionActionsMock * self)
+{
+ g_signal_connect_swapped (mock_settings, "changed::can-lock",
+ G_CALLBACK(indicator_session_actions_notify_can_lock), self);
+ g_signal_connect_swapped (mock_settings, "changed::can-logout",
+ G_CALLBACK(indicator_session_actions_notify_can_logout), self);
+ g_signal_connect_swapped (mock_settings, "changed::can-switch-sessions",
+ G_CALLBACK(indicator_session_actions_notify_can_switch), self);
+ g_signal_connect_swapped (mock_settings, "changed::can-suspend",
+ G_CALLBACK(indicator_session_actions_notify_can_suspend), self);
+ g_signal_connect_swapped (mock_settings, "changed::can-hibernate",
+ G_CALLBACK(indicator_session_actions_notify_can_hibernate), self);
+ g_signal_connect_swapped (mock_settings, "changed::can-prompt",
+ G_CALLBACK(indicator_session_actions_notify_can_prompt), self);
+ g_signal_connect_swapped (mock_settings, "changed::has-online-account-error",
+ G_CALLBACK(indicator_session_actions_notify_has_online_account_error), self);
+}
+
+/***
+**** Public
+***/
+
+IndicatorSessionActions *
+indicator_session_actions_mock_new (void)
+{
+ gpointer o = g_object_new (INDICATOR_TYPE_SESSION_ACTIONS_MOCK, NULL);
+
+ return INDICATOR_SESSION_ACTIONS (o);
+}
diff --git a/tests/backend-mock-actions.h b/tests/backend-mock-actions.h
new file mode 100644
index 0000000..96c51be
--- /dev/null
+++ b/tests/backend-mock-actions.h
@@ -0,0 +1,60 @@
+/*
+ * 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_MOCK_H__
+#define __INDICATOR_SESSION_ACTIONS_MOCK_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "actions.h" /* parent class */
+
+G_BEGIN_DECLS
+
+#define INDICATOR_TYPE_SESSION_ACTIONS_MOCK (indicator_session_actions_mock_get_type())
+#define INDICATOR_SESSION_ACTIONS_MOCK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_SESSION_ACTIONS_MOCK, IndicatorSessionActionsMock))
+#define INDICATOR_SESSION_ACTIONS_MOCK_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_SESSION_ACTIONS_MOCK, IndicatorSessionActionsMockClass))
+#define INDICATOR_IS_SESSION_ACTIONS_MOCK(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_SESSION_ACTIONS_MOCK))
+
+typedef struct _IndicatorSessionActionsMock IndicatorSessionActionsMock;
+typedef struct _IndicatorSessionActionsMockPriv IndicatorSessionActionsMockPriv;
+typedef struct _IndicatorSessionActionsMockClass IndicatorSessionActionsMockClass;
+
+/**
+ * An implementation of IndicatorSessionActions that lies about everything.
+ */
+struct _IndicatorSessionActionsMock
+{
+ /*< private >*/
+ IndicatorSessionActions parent;
+ IndicatorSessionActionsMockPriv * priv;
+};
+
+struct _IndicatorSessionActionsMockClass
+{
+ IndicatorSessionActionsClass parent_class;
+};
+
+GType indicator_session_actions_mock_get_type (void);
+
+IndicatorSessionActions * indicator_session_actions_mock_new (void);
+
+G_END_DECLS
+
+#endif /* __INDICATOR_SESSION_ACTIONS_MOCK_H__ */
diff --git a/tests/backend-mock-guest.c b/tests/backend-mock-guest.c
new file mode 100644
index 0000000..8bc188f
--- /dev/null
+++ b/tests/backend-mock-guest.c
@@ -0,0 +1,133 @@
+/*
+ * 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 "backend-mock-guest.h"
+
+struct _IndicatorSessionGuestMockPriv
+{
+ gboolean guest_is_active;
+ gboolean guest_is_logged_in;
+ gboolean guest_is_allowed;
+};
+
+typedef IndicatorSessionGuestMockPriv priv_t;
+
+G_DEFINE_TYPE (IndicatorSessionGuestMock,
+ indicator_session_guest_mock,
+ INDICATOR_TYPE_SESSION_GUEST)
+
+/**
+*** IndicatorSessionGuest virtual functions
+**/
+
+static gboolean
+my_is_allowed (IndicatorSessionGuest * self)
+{
+ return INDICATOR_SESSION_GUEST_MOCK(self)->priv->guest_is_allowed;
+}
+
+static gboolean
+my_is_logged_in (IndicatorSessionGuest * self)
+{
+ g_return_val_if_fail (INDICATOR_IS_SESSION_GUEST_MOCK(self), FALSE);
+
+ return INDICATOR_SESSION_GUEST_MOCK(self)->priv->guest_is_logged_in;
+}
+
+static gboolean
+my_is_active (IndicatorSessionGuest * self)
+{
+ return INDICATOR_SESSION_GUEST_MOCK(self)->priv->guest_is_active;
+}
+
+static void
+my_switch_to_guest (IndicatorSessionGuest * self G_GNUC_UNUSED)
+{
+ g_message ("%s %s FIXME", G_STRLOC, G_STRFUNC);
+}
+
+/***
+**** GObject virtual Functions
+***/
+
+static void
+my_dispose (GObject * o)
+{
+ G_OBJECT_CLASS (indicator_session_guest_mock_parent_class)->dispose (o);
+}
+
+static void
+my_finalize (GObject * o)
+{
+ G_OBJECT_CLASS (indicator_session_guest_mock_parent_class)->finalize (o);
+}
+
+/***
+**** GObject Boilerplate
+***/
+
+static void
+/* cppcheck-suppress unusedFunction */
+indicator_session_guest_mock_class_init (IndicatorSessionGuestMockClass * klass)
+{
+ GObjectClass * object_class;
+ IndicatorSessionGuestClass * guest_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = my_dispose;
+ object_class->finalize = my_finalize;
+
+ 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 (IndicatorSessionGuestMockPriv));
+}
+
+static void
+/* cppcheck-suppress unusedFunction */
+indicator_session_guest_mock_init (IndicatorSessionGuestMock * self)
+{
+ priv_t * p;
+
+ p = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ INDICATOR_TYPE_SESSION_GUEST_MOCK,
+ IndicatorSessionGuestMockPriv);
+ self->priv = p;
+
+ p->guest_is_allowed = TRUE;
+ p->guest_is_active = FALSE;
+ p->guest_is_logged_in = FALSE;
+}
+
+/***
+**** Public
+***/
+
+IndicatorSessionGuest *
+indicator_session_guest_mock_new (void)
+{
+ gpointer o = g_object_new (INDICATOR_TYPE_SESSION_GUEST_MOCK, NULL);
+
+ return INDICATOR_SESSION_GUEST (o);
+}
diff --git a/tests/backend-mock-guest.h b/tests/backend-mock-guest.h
new file mode 100644
index 0000000..4a15c70
--- /dev/null
+++ b/tests/backend-mock-guest.h
@@ -0,0 +1,60 @@
+/*
+ * 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_MOCK_H__
+#define __GUEST_MOCK_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "guest.h" /* parent class */
+
+G_BEGIN_DECLS
+
+#define INDICATOR_TYPE_SESSION_GUEST_MOCK (indicator_session_guest_mock_get_type())
+#define INDICATOR_SESSION_GUEST_MOCK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_SESSION_GUEST_MOCK, IndicatorSessionGuestMock))
+#define INDICATOR_SESSION_GUEST_MOCK_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_SESSION_GUEST_MOCK, IndicatorSessionGuestMockClass))
+#define INDICATOR_IS_SESSION_GUEST_MOCK(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_SESSION_GUEST_MOCK))
+
+typedef struct _IndicatorSessionGuestMock IndicatorSessionGuestMock;
+typedef struct _IndicatorSessionGuestMockPriv IndicatorSessionGuestMockPriv;
+typedef struct _IndicatorSessionGuestMockClass IndicatorSessionGuestMockClass;
+
+/**
+ * An implementation of IndicatorSessionGuest that lies about everything.
+ */
+struct _IndicatorSessionGuestMock
+{
+ /*< private >*/
+ IndicatorSessionGuest parent;
+ IndicatorSessionGuestMockPriv * priv;
+};
+
+struct _IndicatorSessionGuestMockClass
+{
+ IndicatorSessionGuestClass parent_class;
+};
+
+GType indicator_session_guest_mock_get_type (void);
+
+IndicatorSessionGuest * indicator_session_guest_mock_new (void);
+
+G_END_DECLS
+
+#endif
diff --git a/tests/backend-mock-users.c b/tests/backend-mock-users.c
new file mode 100644
index 0000000..f2291cd
--- /dev/null
+++ b/tests/backend-mock-users.c
@@ -0,0 +1,183 @@
+/*
+ * 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 "backend-mock.h"
+#include "backend-mock-users.h"
+
+struct _IndicatorSessionUsersMockPriv
+{
+ GHashTable * users;
+};
+
+typedef IndicatorSessionUsersMockPriv priv_t;
+
+G_DEFINE_TYPE (IndicatorSessionUsersMock,
+ indicator_session_users_mock,
+ INDICATOR_TYPE_SESSION_USERS)
+
+/***
+**** IndicatorSessionUsers virtual functions
+***/
+
+static gboolean
+my_is_live_session (IndicatorSessionUsers * users G_GNUC_UNUSED)
+{
+ return g_settings_get_boolean (mock_settings, "is-live-session");
+}
+
+static void
+my_activate_user (IndicatorSessionUsers * users, guint uid)
+{
+ g_message ("%s %s users %p uid %u FIXME", G_STRLOC, G_STRFUNC, (void*)users, uid);
+}
+
+static GList *
+my_get_uids (IndicatorSessionUsers * users)
+{
+ g_return_val_if_fail (INDICATOR_IS_SESSION_USERS_MOCK(users), NULL);
+
+ return g_hash_table_get_keys (INDICATOR_SESSION_USERS_MOCK(users)->priv->users);
+}
+
+static IndicatorSessionUser *
+my_get_user (IndicatorSessionUsers * self, guint uid)
+{
+ priv_t * p;
+ const IndicatorSessionUser * src;
+ IndicatorSessionUser * ret = NULL;
+
+ g_return_val_if_fail (INDICATOR_IS_SESSION_USERS_MOCK(self), NULL);
+ p = INDICATOR_SESSION_USERS_MOCK (self)->priv;
+
+ if ((src = g_hash_table_lookup (p->users, GUINT_TO_POINTER(uid))))
+ {
+ ret = g_new0 (IndicatorSessionUser, 1);
+ ret->is_current_user = src->is_current_user;
+ ret->is_logged_in = src->is_logged_in;
+ ret->uid = src->uid;
+ ret->login_frequency = src->login_frequency;
+ ret->user_name = g_strdup (src->user_name);
+ ret->real_name = g_strdup (src->real_name);
+ ret->icon_file = g_strdup (src->icon_file);
+ }
+
+ return ret;
+}
+
+/***
+**** GObject virtual functions
+***/
+
+static void
+my_dispose (GObject * o)
+{
+ G_OBJECT_CLASS (indicator_session_users_mock_parent_class)->dispose (o);
+}
+
+static void
+my_finalize (GObject * o)
+{
+ priv_t * p = INDICATOR_SESSION_USERS_MOCK (o)->priv;
+
+ g_hash_table_destroy (p->users);
+
+ G_OBJECT_CLASS (indicator_session_users_mock_parent_class)->finalize (o);
+}
+
+/***
+**** GObject boilerplate
+***/
+
+static void
+/* cppcheck-suppress unusedFunction */
+indicator_session_users_mock_class_init (IndicatorSessionUsersMockClass * 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 (IndicatorSessionUsersMockPriv));
+}
+
+static void
+/* cppcheck-suppress unusedFunction */
+indicator_session_users_mock_init (IndicatorSessionUsersMock * self)
+{
+ priv_t * p;
+
+ p = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ INDICATOR_TYPE_SESSION_USERS_MOCK,
+ IndicatorSessionUsersMockPriv);
+ self->priv = p;
+
+ p->users = g_hash_table_new_full (g_direct_hash,
+ g_direct_equal,
+ NULL,
+ (GDestroyNotify)indicator_session_user_free);
+
+ g_signal_connect_swapped (mock_settings, "changed::is-live-session",
+ G_CALLBACK(indicator_session_users_notify_is_live_session), self);
+}
+
+/***
+**** Public
+***/
+
+IndicatorSessionUsers *
+indicator_session_users_mock_new (void)
+{
+ gpointer o = g_object_new (INDICATOR_TYPE_SESSION_USERS_MOCK, NULL);
+
+ return INDICATOR_SESSION_USERS (o);
+}
+
+
+void
+indicator_session_users_mock_add_user (IndicatorSessionUsersMock * self,
+ IndicatorSessionUser * user)
+{
+ g_return_if_fail (INDICATOR_IS_SESSION_USERS_MOCK (self));
+ g_return_if_fail (user != NULL);
+ g_return_if_fail (user->uid > 0);
+ g_return_if_fail (!g_hash_table_contains (self->priv->users, GUINT_TO_POINTER(user->uid)));
+
+ g_hash_table_insert (self->priv->users, GUINT_TO_POINTER(user->uid), user);
+ indicator_session_users_added (INDICATOR_SESSION_USERS (self), user->uid);
+}
+
+void
+indicator_session_users_mock_remove_user (IndicatorSessionUsersMock * self,
+ guint uid)
+{
+ g_return_if_fail (INDICATOR_IS_SESSION_USERS_MOCK (self));
+ g_return_if_fail (uid > 0);
+
+ g_hash_table_remove (self->priv->users, GUINT_TO_POINTER(uid));
+ indicator_session_users_removed (INDICATOR_SESSION_USERS (self), uid);
+}
+
diff --git a/tests/backend-mock-users.h b/tests/backend-mock-users.h
new file mode 100644
index 0000000..513e799
--- /dev/null
+++ b/tests/backend-mock-users.h
@@ -0,0 +1,68 @@
+/*
+ * 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_MOCK_H__
+#define __USERS_MOCK_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "users.h" /* parent class */
+
+G_BEGIN_DECLS
+
+#define INDICATOR_TYPE_SESSION_USERS_MOCK (indicator_session_users_mock_get_type())
+#define INDICATOR_SESSION_USERS_MOCK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_SESSION_USERS_MOCK, IndicatorSessionUsersMock))
+#define INDICATOR_SESSION_USERS_MOCK_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_SESSION_USERS_MOCK, IndicatorSessionUsersMockClass))
+#define INDICATOR_IS_SESSION_USERS_MOCK(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_SESSION_USERS_MOCK))
+
+typedef struct _IndicatorSessionUsersMock IndicatorSessionUsersMock;
+typedef struct _IndicatorSessionUsersMockPriv IndicatorSessionUsersMockPriv;
+typedef struct _IndicatorSessionUsersMockClass IndicatorSessionUsersMockClass;
+
+/**
+ * An implementation of IndicatorSessionUsers that lies about everything.
+ */
+struct _IndicatorSessionUsersMock
+{
+ /*< private >*/
+ IndicatorSessionUsers parent;
+ IndicatorSessionUsersMockPriv * priv;
+};
+
+struct _IndicatorSessionUsersMockClass
+{
+ IndicatorSessionUsersClass parent_class;
+};
+
+GType indicator_session_users_mock_get_type (void);
+
+IndicatorSessionUsers * indicator_session_users_mock_new (void);
+
+void indicator_session_users_mock_add_user (IndicatorSessionUsersMock * self,
+ IndicatorSessionUser * user);
+
+void indicator_session_users_mock_remove_user (IndicatorSessionUsersMock * self,
+ guint uid);
+
+
+
+G_END_DECLS
+
+#endif
diff --git a/tests/backend-mock.c b/tests/backend-mock.c
new file mode 100644
index 0000000..48d7de4
--- /dev/null
+++ b/tests/backend-mock.c
@@ -0,0 +1,44 @@
+/*
+ * 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 "backend-mock.h"
+#include "backend-mock-actions.h"
+#include "backend-mock-guest.h"
+#include "backend-mock-users.h"
+
+GSettings * mock_settings = NULL;
+IndicatorSessionActions * mock_actions = NULL;
+IndicatorSessionUsers * mock_users = NULL;
+IndicatorSessionGuest * mock_guest = NULL;
+
+void
+backend_get (GCancellable * cancellable G_GNUC_UNUSED,
+ IndicatorSessionActions ** setme_actions,
+ IndicatorSessionUsers ** setme_users,
+ IndicatorSessionGuest ** setme_guest)
+{
+ if (setme_actions != NULL)
+ *setme_actions = g_object_ref (mock_actions);
+
+ if (setme_users != NULL)
+ *setme_users = g_object_ref (mock_users);
+
+ if (setme_guest != NULL)
+ *setme_guest = g_object_ref (mock_guest);
+}
diff --git a/tests/backend-mock.h b/tests/backend-mock.h
new file mode 100644
index 0000000..d80a185
--- /dev/null
+++ b/tests/backend-mock.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 __BACKEND_MOCK_H__
+#define __BACKEND_MOCK_H__
+
+#include <gio/gio.h> /* GCancellable */
+
+#include "actions.h"
+#include "guest.h"
+#include "users.h"
+
+G_BEGIN_DECLS
+
+extern GSettings * mock_settings;
+extern IndicatorSessionActions * mock_actions;
+extern IndicatorSessionUsers * mock_users;
+extern IndicatorSessionGuest * mock_guest;
+
+G_END_DECLS
+
+#endif
diff --git a/tests/com.canonical.indicator.session.backendmock.gschema.xml b/tests/com.canonical.indicator.session.backendmock.gschema.xml
new file mode 100644
index 0000000..79d0c02
--- /dev/null
+++ b/tests/com.canonical.indicator.session.backendmock.gschema.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<schemalist gettext-domain="gsettings-desktop-schemas">
+ <schema path="/com/canonical/indicator/session/backendmock/" id="com.canonical.indicator.session.backendmock">
+ <key type="s" name="last-command">
+ <default>''</default>
+ <summary>The last command activated</summary>
+ </key>
+ <key type="b" name="has-online-account-error">
+ <default>false</default>
+ <summary>Has online account error</summary>
+ </key>
+ <key type="b" name="can-hibernate">
+ <default>true</default>
+ <summary>Is hibernation allowed?</summary>
+ </key>
+ <key type="b" name="can-suspend">
+ <default>true</default>
+ <summary>Is suspending allowed?</summary>
+ </key>
+ <key type="b" name="can-logout">
+ <default>true</default>
+ <summary>Is logging out allowed?</summary>
+ </key>
+ <key type="b" name="can-reboot">
+ <default>true</default>
+ <summary>Is rebooting allowed?</summary>
+ </key>
+ <key type="b" name="can-lock">
+ <default>true</default>
+ <summary>Is locking the session allowed?</summary>
+ </key>
+ <key type="b" name="can-switch-sessions">
+ <default>true</default>
+ <summary>Is switching sessions allowed?</summary>
+ </key>
+ <key type="b" name="can-prompt">
+ <default>true</default>
+ <summary>Do we have a way of prompting for confirmation?</summary>
+ </key>
+ <key type="b" name="is-live-session">
+ <default>false</default>
+ <summary>Is this a session running on a live CD?</summary>
+ </key>
+ </schema>
+</schemalist>
diff --git a/tests/com.canonical.indicator.session.gschema.xml b/tests/com.canonical.indicator.session.gschema.xml
new file mode 100644
index 0000000..76b2be3
--- /dev/null
+++ b/tests/com.canonical.indicator.session.gschema.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<schemalist>
+ <schema path="/apps/indicator-session/" id="com.canonical.indicator.session">
+ <key type="b" name="suppress-logout-restart-shutdown">
+ <default>false</default>
+ <summary>Suppress the dialog to confirm logout, restart and shutdown action</summary>
+ <description>Whether or not to show confirmation dialogs for logout, restart and shutdown actions.</description>
+ </key>
+ <key type="b" name="suppress-logout-menuitem">
+ <default>false</default>
+ <summary>Remove the Log Out item from the session menu</summary>
+ <description>Makes it so that the logout button doesn’t show in the session menu.</description>
+ </key>
+ <key type="b" name="suppress-restart-menuitem">
+ <default>false</default>
+ <summary>Remove the Restart item from the session menu</summary>
+ <description>Makes it so that the restart button doesn’t show in the session menu.</description>
+ </key>
+ <key type="b" name="suppress-shutdown-menuitem">
+ <default>false</default>
+ <summary>Remove the shutdown item from the session menu</summary>
+ <description>Makes it so that the shutdown button doesn’t show in the session menu.</description>
+ </key>
+ <key type="b" name="show-real-name-on-panel">
+ <default>false</default>
+ <summary>Determine the visibility of the User's real name on the panel</summary>
+ <description>Allow for the Removal of the users name from the panel</description>
+ </key>
+
+ </schema>
+
+</schemalist> \ No newline at end of file
diff --git a/tests/gtest-dbus-fixture.h b/tests/gtest-dbus-fixture.h
new file mode 100644
index 0000000..e6cd9c7
--- /dev/null
+++ b/tests/gtest-dbus-fixture.h
@@ -0,0 +1,134 @@
+/*
+ * 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 <gio/gio.h>
+
+#include <gtest/gtest.h>
+
+/***
+****
+***/
+
+class GTestDBusFixture : public ::testing::Test
+{
+ private:
+
+ static void
+ on_bus_opened (GObject * o G_GNUC_UNUSED, GAsyncResult * res, gpointer gself)
+ {
+ GTestDBusFixture * self = static_cast<GTestDBusFixture*>(gself);
+
+ GError * err = 0;
+ self->conn = g_bus_get_finish (res, &err);
+ g_assert_no_error (err);
+
+ g_main_loop_quit (self->loop);
+ }
+
+ static void
+ on_bus_closed (GObject * o G_GNUC_UNUSED, GAsyncResult * res, gpointer gself)
+ {
+ GTestDBusFixture * self = static_cast<GTestDBusFixture*>(gself);
+
+ GError * err = 0;
+ g_dbus_connection_close_finish (self->conn, res, &err);
+ g_assert_no_error (err);
+
+ g_main_loop_quit (self->loop);
+ }
+
+ static gboolean
+ wait_for_signal__timeout (gpointer name)
+ {
+ g_error ("%s: timed out waiting for signal '%s'", G_STRLOC, (char*)name);
+ return G_SOURCE_REMOVE;
+ }
+
+ protected:
+
+ virtual void SetUp ()
+ {
+ conn = 0;
+ test_dbus = 0;
+ loop = 0;
+
+ g_setenv ("GSETTINGS_SCHEMA_DIR", SCHEMA_DIR, TRUE);
+ g_setenv ("GSETTINGS_BACKEND", "memory", TRUE);
+ g_debug ("SCHEMA_DIR is %s", SCHEMA_DIR);
+
+ // pull up a test dbus
+ loop = g_main_loop_new (NULL, FALSE);
+ test_dbus = g_test_dbus_new (G_TEST_DBUS_NONE);
+ g_test_dbus_add_service_dir (test_dbus, INDICATOR_SERVICE_DIR);
+ g_debug ("INDICATOR_SERVICE_DIR is %s", INDICATOR_SERVICE_DIR);
+ g_test_dbus_up (test_dbus);
+ const char * address;
+ address = g_test_dbus_get_bus_address (test_dbus);
+ g_setenv ("DBUS_SYSTEM_BUS_ADDRESS", address, TRUE);
+ g_debug ("test_dbus's address is %s", address);
+
+ // wait for the GDBusConnection before returning
+ g_bus_get (G_BUS_TYPE_SYSTEM, NULL, on_bus_opened, this);
+ g_main_loop_run (loop);
+ }
+
+ virtual void TearDown()
+ {
+ // close the bus connection
+ g_dbus_connection_close (conn, NULL, on_bus_closed, this);
+ g_main_loop_run (loop);
+ g_clear_object (&conn);
+
+ // tear down the test dbus
+ g_test_dbus_down (test_dbus);
+ g_clear_object (&test_dbus);
+
+ g_clear_pointer (&loop, g_main_loop_unref);
+ }
+
+ /* convenience func to loop while waiting for a GObject's signal */
+ void wait_for_signal (gpointer o, const gchar * signal)
+ {
+ const int timeout_seconds = 5; // arbitrary
+
+ // wait for the signal or for timeout, whichever comes first
+ guint handler_id = g_signal_connect_swapped (o, signal,
+ G_CALLBACK(g_main_loop_quit),
+ loop);
+ gulong timeout_id = g_timeout_add_seconds (timeout_seconds,
+ wait_for_signal__timeout,
+ loop);
+ g_main_loop_run (loop);
+ g_source_remove (timeout_id);
+ g_signal_handler_disconnect (o, handler_id);
+ }
+
+ /* convenience func to loop for N msec */
+ void wait_msec (int msec)
+ {
+ guint id = g_timeout_add (msec, (GSourceFunc)g_main_loop_quit, loop);
+ g_main_loop_run (loop);
+ g_source_remove (id);
+ }
+
+ GMainLoop * loop;
+ GTestDBus * test_dbus;
+ GDBusConnection * conn;
+};
diff --git a/tests/indicator-session.service.in b/tests/indicator-session.service.in
index 80aab0d..fb3798a 100644
--- a/tests/indicator-session.service.in
+++ b/tests/indicator-session.service.in
@@ -1,3 +1,3 @@
[D-BUS Service]
-Name=com.canonical.indicator.session
-Exec=@abs_top_builddir@/src/indicator-session-service
+Name=com.canonical.indicator.session-test
+Exec=${CMAKE_BINARY_DIR}/src/indicator-session-service --mock
diff --git a/tests/test-service.cc b/tests/test-service.cc
index b1ca5bc..b199175 100644
--- a/tests/test-service.cc
+++ b/tests/test-service.cc
@@ -17,70 +17,353 @@ 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-object.h>
-#include <gio/gio.h>
-#include <glib.h>
-
-#include <gtest/gtest.h>
-
-#include "shared-names.h"
+#include "gtest-dbus-fixture.h"
+#include "service.h"
+#include "backend-mock.h"
+#include "backend-mock-users.h"
+#include "backend-mock-guest.h"
+#include "backend-mock-actions.h"
/***
****
***/
-#define INDICATOR_SERVICE_OBJECT_PATH "/org/ayatana/indicator/service"
-#define INDICATOR_SERVICE_INTERFACE_NAME "org.ayatana.indicator.service"
+#if 0
+namespace
+{
+ void
+ dump_menu_model (GMenuModel * model, int depth)
+ {
+ GString * indent = g_string_new_len (" ", (depth*4));
+ const int n = g_menu_model_get_n_items (model);
+ g_message ("%s depth[%d] menu model[%p] has %d items", indent->str, depth, (void*)model, n);
+
+ for (int i=0; i<n; ++i)
+ {
+ const char * name;
+ GMenuModel * link_value;
+ GVariant * attribute_value;
+
+ GMenuAttributeIter * attribute_iter = g_menu_model_iterate_item_attributes (model, i);
+ while (g_menu_attribute_iter_get_next (attribute_iter, &name, &attribute_value))
+ {
+ char * str = g_variant_print (attribute_value, TRUE);
+ g_message ("%s depth[%d] menu model[%p] item[%d] attribute key[%s] value[%s]", indent->str, depth, (void*)model, i, name, str);
+ g_free (str);
+ g_variant_unref (attribute_value);
+ }
+ g_clear_object (&attribute_iter);
+
+ GMenuLinkIter * link_iter = g_menu_model_iterate_item_links (model, i);
+ while (g_menu_link_iter_get_next (link_iter, &name, &link_value))
+ {
+ g_message ("%s depth[%d] menu model[%p] item[%d] attribute key[%s] model[%p]", indent->str, depth, (void*)model, i, name, (void*)link_value);
+ dump_menu_model (link_value, depth+1);
+ g_object_unref (link_value);
+ }
+ g_clear_object (&link_iter);
+ }
+ g_string_free (indent, TRUE);
+ }
+}
+#endif
-class ClientTest : public ::testing::Test
+
+/* cppcheck-suppress noConstructor */
+class ServiceTest: public GTestDBusFixture
{
+ typedef GTestDBusFixture super;
+
+ enum { TIME_LIMIT_SEC = 10 };
+
+ private:
+
+ static void on_name_appeared (GDBusConnection * connection G_GNUC_UNUSED,
+ const gchar * name G_GNUC_UNUSED,
+ const gchar * name_owner G_GNUC_UNUSED,
+ gpointer gself)
+ {
+ g_main_loop_quit (static_cast<ServiceTest*>(gself)->loop);
+ }
+
+ GSList * menu_references;
+
+ gboolean any_item_changed;
+
+ static void on_items_changed (GMenuModel * model G_GNUC_UNUSED,
+ gint position G_GNUC_UNUSED,
+ gint removed G_GNUC_UNUSED,
+ gint added G_GNUC_UNUSED,
+ gpointer any_item_changed)
+ {
+ *((gboolean*)any_item_changed) = true;
+ }
+
+ protected:
+
+ void activate_subtree (GMenuModel * model)
+ {
+ // query the GDBusMenuModel for information to activate it
+ int n = g_menu_model_get_n_items (model);
+ if (!n)
+ {
+ // give the model a moment to populate its info
+ wait_msec (100);
+ n = g_menu_model_get_n_items (model);
+ }
+
+ // keep a ref so that it stays activated
+ menu_references = g_slist_prepend (menu_references, g_object_ref(model));
+
+ g_signal_connect (model, "items-changed", G_CALLBACK(on_items_changed), &any_item_changed);
+
+ // recurse
+ for (int i=0; i<n; ++i)
+ {
+ GMenuModel * link;
+ GMenuLinkIter * iter = g_menu_model_iterate_item_links (model, i);
+ while (g_menu_link_iter_get_next (iter, NULL, &link))
+ {
+ activate_subtree (link);
+ g_object_unref (link);
+ }
+ g_clear_object (&iter);
+ }
+ }
+
+ void sync_menu (void)
+ {
+ g_slist_free_full (menu_references, (GDestroyNotify)g_object_unref);
+ menu_references = NULL;
+ activate_subtree (G_MENU_MODEL (menu_model));
+ }
+
+ GDBusMenuModel * menu_model;
+ GDBusActionGroup * action_group;
+ IndicatorSessionService * service;
+ GTimer * timer;
+ GSettings * indicator_settings;
+
+ virtual void SetUp ()
+ {
+ super :: SetUp ();
+
+ menu_references = NULL;
+ any_item_changed = FALSE;
+
+ timer = g_timer_new ();
+ mock_settings = g_settings_new ("com.canonical.indicator.session.backendmock");
+ mock_actions = indicator_session_actions_mock_new ();
+ mock_users = indicator_session_users_mock_new ();
+ mock_guest = indicator_session_guest_mock_new ();
+ indicator_settings = g_settings_new ("com.canonical.indicator.session");
+
+ // Start an IndicatorSessionService and wait for it to appear on the bus.
+ // This way our calls to g_dbus_*_get() in the next paragraph won't activate
+ // a second copy of the service...
+ service = indicator_session_service_new ();
+
+ // wait for the service to show up on the bus
+ const guint watch_id = g_bus_watch_name_on_connection (conn,
+ "com.canonical.indicator.session",
+ G_BUS_NAME_WATCHER_FLAGS_NONE,
+ on_name_appeared, // quits the loop
+ NULL, this, NULL);
+ const guint timer_id = g_timeout_add_seconds (TIME_LIMIT_SEC, (GSourceFunc)g_main_loop_quit, loop);
+ g_main_loop_run (loop);
+ g_source_remove (timer_id);
+ g_bus_unwatch_name (watch_id);
+ ASSERT_FALSE (times_up());
+
+ // get the actions & menus that the service exported.
+ action_group = g_dbus_action_group_get (conn,
+ "com.canonical.indicator.session",
+ "/com/canonical/indicator/session");
+ menu_model = g_dbus_menu_model_get (conn,
+ "com.canonical.indicator.session",
+ "/com/canonical/indicator/session/desktop");
+ // the actions are added asynchronously, so wait for the actions
+ if (!g_action_group_has_action (G_ACTION_GROUP(action_group), "about"))
+ wait_for_signal (action_group, "action-added");
+ // activate all the groups in the menu so it'll be ready when we need it
+ g_signal_connect (menu_model, "items-changed", G_CALLBACK(on_items_changed), &any_item_changed);
+ sync_menu ();
+ }
+
+ virtual void TearDown ()
+ {
+ g_clear_pointer (&timer, g_timer_destroy);
+
+ g_slist_free_full (menu_references, (GDestroyNotify)g_object_unref);
+ menu_references = NULL;
+ g_clear_object (&menu_model);
+
+ g_clear_object (&action_group);
+ g_clear_object (&mock_settings);
+ g_clear_object (&indicator_settings);
+ g_clear_object (&service);
+ g_clear_object (&mock_actions);
+ g_clear_object (&mock_users);
+ g_clear_object (&mock_guest);
+ wait_msec (100);
+
+ super :: TearDown ();
+ }
+
+ protected:
+
+ bool times_up () const
+ {
+ return g_timer_elapsed (timer, NULL) >= TIME_LIMIT_SEC;
+ }
+
+ void wait_for_has_action (const char * name)
+ {
+ while (!g_action_group_has_action (G_ACTION_GROUP(action_group), name) && !times_up())
+ wait_msec (50);
+
+ ASSERT_FALSE (times_up());
+ ASSERT_TRUE (g_action_group_has_action (G_ACTION_GROUP(action_group), name));
+ }
+
+ void wait_for_menu_resync (void)
+ {
+ any_item_changed = false;
+ while (!times_up() && !any_item_changed)
+ wait_msec (50);
+ g_warn_if_fail (any_item_changed);
+ sync_menu ();
+ }
+
+ protected:
+
+ void check_last_command_is (const char * expected)
+ {
+ char * str = g_settings_get_string (mock_settings, "last-command");
+ ASSERT_STREQ (expected, str);
+ g_free (str);
+ }
+
+ void test_simple_action (const char * action_name)
+ {
+ wait_for_has_action (action_name);
+
+ g_action_group_activate_action (G_ACTION_GROUP (action_group), action_name, NULL);
+ wait_for_signal (mock_settings, "changed::last-command");
+ check_last_command_is (action_name);
+ }
+
protected:
- GTestDBus * test_dbus;
- GDBusConnection * session_bus;
- GMainLoop * main_loop;
+ bool find_menu_item_for_action (const char * action_key, GMenuModel ** setme, int * item_index)
+ {
+ bool success = false;
+
+ for (GSList * l=menu_references; !success && (l!=NULL); l=l->next)
+ {
+ GMenuModel * mm = G_MENU_MODEL (l->data);
+ const int n = g_menu_model_get_n_items (mm);
+
+ for (int i=0; !success && i<n; ++i)
+ {
+ char * action = NULL;
+ if (!g_menu_model_get_item_attribute (mm, i, G_MENU_ATTRIBUTE_ACTION, "s", &action))
+ continue;
+
+ if ((success = !g_strcmp0 (action, action_key)))
+ {
+ if (setme != NULL)
+ *setme = G_MENU_MODEL (g_object_ref (G_OBJECT(mm)));
+
+ if (item_index != NULL)
+ *item_index = i;
+ }
+
+ g_free (action);
+ }
+ }
+
+ return success;
+ }
+
+ bool action_menuitem_exists (const char * action_name)
+ {
+ bool found;
+ GMenuModel * model = 0;
+ int pos = -1;
+
+ if ((found = find_menu_item_for_action (action_name, &model, &pos)))
+ g_object_unref (G_OBJECT(model));
+
+ return found;
+ }
+
+ bool action_menuitem_label_is_ellipsized (const char * action_name)
+ {
+ int pos = -1;
+ GMenuModel * model = 0;
+ bool ellipsized = false;
+ char * label = NULL;
+
+ if (find_menu_item_for_action (action_name, &model, &pos))
+ {
+ g_menu_model_get_item_attribute (model, pos, G_MENU_ATTRIBUTE_LABEL, "s", &label);
+ g_object_unref (G_OBJECT(model));
+ }
+
+ ellipsized = (label != NULL) && g_str_has_suffix (label, "\342\200\246");
+ g_free (label);
+ return ellipsized;
+ }
- virtual void SetUp()
+ void check_header (const char * expected_label, const char * expected_icon, const char * expected_a11y)
{
- test_dbus = NULL;
- session_bus = NULL;
- main_loop = NULL;
+ GVariant * state = g_action_group_get_action_state (G_ACTION_GROUP(action_group), "_header");
+ ASSERT_TRUE (state != NULL);
+ ASSERT_TRUE (g_variant_is_of_type (state, G_VARIANT_TYPE ("a{sv}")));
- static bool first_run = true;
- if (first_run)
+ if (expected_label != NULL)
{
- g_setenv ("INDICATOR_SERVICE_SHUTDOWN_TIMEOUT", "1000", TRUE);
- g_unsetenv ("INDICATOR_ALLOW_NO_WATCHERS");
- g_unsetenv ("INDICATOR_SERVICE_REPLACE_MODE");
- g_setenv ("GSETTINGS_SCHEMA_DIR", SCHEMA_DIR, TRUE);
- g_setenv ("GSETTINGS_BACKEND", "memory", TRUE);
- first_run = false;
+ GVariant * v = g_variant_lookup_value (state, "label", G_VARIANT_TYPE_STRING);
+ if (!v) // if no label in the state, expected_label must be an empty string
+ ASSERT_FALSE (*expected_label);
+ else
+ ASSERT_STREQ (expected_label, g_variant_get_string (v, NULL));
}
- main_loop = g_main_loop_new (NULL, FALSE);
- // pull up a test dbus that's pointed at our test .service file
- test_dbus = g_test_dbus_new (G_TEST_DBUS_NONE);
- g_debug (G_STRLOC" service dir path is \"%s\"", INDICATOR_SERVICE_DIR);
- g_test_dbus_add_service_dir (test_dbus, INDICATOR_SERVICE_DIR);
+ if (expected_a11y != NULL)
+ {
+ GVariant * v = g_variant_lookup_value (state, "accessible-desc", G_VARIANT_TYPE_STRING);
+ ASSERT_TRUE (v != NULL);
+ ASSERT_STREQ (expected_a11y, g_variant_get_string (v, NULL));
+ g_variant_unref (v);
+ }
- // allow the service to exist w/o a sync indicator
- g_setenv ("INDICATOR_ALLOW_NO_WATCHERS", "1", TRUE);
+ if (expected_icon != NULL)
+ {
+ GVariant * v = g_variant_lookup_value (state, "icon", NULL);
+ GIcon * expected = g_themed_icon_new (expected_icon);
+ GIcon * actual = g_icon_deserialize (v);
+ ASSERT_TRUE (g_icon_equal (expected, actual));
+ g_object_unref (actual);
+ g_object_unref (expected);
+ g_variant_unref (v);
+ }
+
+ // the session menu is always visible...
+ gboolean visible = false;
+ g_variant_lookup (state, "visible", "b", &visible);
+ ASSERT_TRUE (visible);
- g_test_dbus_up (test_dbus);
- g_debug (G_STRLOC" this test bus' address is \"%s\"", g_test_dbus_get_bus_address(test_dbus));
- session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
- g_debug (G_STRLOC" the dbus connection %p unique name is \"%s\"", session_bus, g_dbus_connection_get_unique_name(session_bus));
- g_debug (G_STRLOC" the dbus connection %p refcount is %d", session_bus, G_OBJECT(session_bus)->ref_count);
+ g_variant_unref (state);
}
- // undo SetUp
- virtual void TearDown()
+ void check_label (const char * expected_label, GMenuModel * model, int pos)
{
- g_clear_object (&session_bus);
- g_debug (G_STRLOC" tearing down the bus");
- g_test_dbus_down (test_dbus);
- g_clear_object (&test_dbus);
- g_clear_pointer (&main_loop, g_main_loop_unref);
+ char * label = NULL;
+ ASSERT_TRUE (g_menu_model_get_item_attribute (model, pos, G_MENU_ATTRIBUTE_LABEL, "s", &label));
+ ASSERT_STREQ (expected_label, label);
+ g_free (label);
}
};
@@ -88,58 +371,451 @@ class ClientTest : public ::testing::Test
****
***/
+#if 0
+TEST_F (ServiceTest, HelloWorld)
+{
+ ASSERT_TRUE (true);
+}
+#endif
+
+TEST_F (ServiceTest, About)
+{
+ test_simple_action ("about");
+}
+
+TEST_F (ServiceTest, Help)
+{
+ test_simple_action ("help");
+}
+
+TEST_F (ServiceTest, Hibernate)
+{
+ test_simple_action ("hibernate");
+}
+
+TEST_F (ServiceTest, Settings)
+{
+ test_simple_action ("settings");
+}
+
+TEST_F (ServiceTest, Logout)
+{
+ test_simple_action ("logout");
+}
+
+TEST_F (ServiceTest, PowerOff)
+{
+ test_simple_action ("power-off");
+}
+
+TEST_F (ServiceTest, Reboot)
+{
+ test_simple_action ("reboot");
+}
+
+TEST_F (ServiceTest, SwitchToScreensaver)
+{
+ test_simple_action ("switch-to-screensaver");
+}
+
+TEST_F (ServiceTest, SwitchToGuest)
+{
+ test_simple_action ("switch-to-guest");
+}
+
+TEST_F (ServiceTest, SwitchToGreeter)
+{
+ test_simple_action ("switch-to-greeter");
+}
+
+TEST_F (ServiceTest, Suspend)
+{
+ test_simple_action ("suspend");
+}
+
+#if 0
+namespace
+{
+ gboolean
+ find_menu_item_for_action (GMenuModel * top, const char * action_key, GMenuModel ** setme, int * item_index)
+ {
+ gboolean success = FALSE;
+ const int n = g_menu_model_get_n_items (top);
+
+ for (int i=0; !success && i<n; ++i)
+ {
+ char * action = NULL;
+ if (g_menu_model_get_item_attribute (top, i, G_MENU_ATTRIBUTE_ACTION, "s", &action))
+ {
+ if ((success = !g_strcmp0 (action, action_key)))
+ {
+ *setme = G_MENU_MODEL (g_object_ref (G_OBJECT(top)));
+ *item_index = i;
+ }
+
+ g_free (action);
+ }
+
+ const char * name = NULL;
+ GMenuModel * link_value = NULL;
+ GMenuLinkIter * link_iter = g_menu_model_iterate_item_links (top, i);
+ while (!success && g_menu_link_iter_get_next (link_iter, &name, &link_value))
+ {
+ success = find_menu_item_for_action (link_value, action_key, setme, item_index);
+ g_object_unref (link_value);
+ }
+ g_clear_object (&link_iter);
+ }
+
+ return success;
+ }
+
+ gchar *
+ get_menu_label_for_action (GMenuModel * top, const char * action)
+ {
+ int pos;
+ GMenuModel * model;
+ gchar * label = NULL;
+
+ if (find_menu_item_for_action (top, action, &model, &pos))
+ {
+ g_menu_model_get_item_attribute (model, pos, G_MENU_ATTRIBUTE_LABEL, "s", &label);
+ g_object_unref (G_OBJECT(model));
+ }
+
+ return label;
+ }
+
+ gboolean str_is_ellipsized (const char * str)
+ {
+ g_assert (str != NULL);
+ return g_str_has_suffix (str, "\342\200\246");
+ }
+}
+#endif
+
+TEST_F (ServiceTest, ConfirmationDisabledByBackend)
+{
+ const char * const confirm_supported_key = "can-prompt";
+ const char * const confirm_disabled_key = "suppress-logout-restart-shutdown";
+
+ bool confirm_supported = g_settings_get_boolean (mock_settings, confirm_supported_key);
+ bool confirm_disabled = g_settings_get_boolean (indicator_settings, confirm_disabled_key);
+ bool confirm = confirm_supported && !confirm_disabled;
+
+ // confirm that the ellipsis are correct
+ ASSERT_EQ (confirm, action_menuitem_label_is_ellipsized ("indicator.switch-to-greeter"));
+ ASSERT_EQ (confirm, action_menuitem_label_is_ellipsized ("indicator.logout"));
+ if (action_menuitem_exists ("indicator.reboot"))
+ ASSERT_EQ (confirm, action_menuitem_label_is_ellipsized ("indicator.reboot"));
+ ASSERT_EQ (confirm, action_menuitem_label_is_ellipsized ("indicator.power-off"));
+
+ // now toggle the can-prompt flag
+ confirm_supported = !confirm_supported;
+ g_settings_set_boolean (mock_settings, confirm_supported_key, confirm_supported);
+ confirm = confirm_supported && !confirm_disabled;
+
+ wait_for_menu_resync ();
+
+ // confirm that the ellipsis are correct
+ ASSERT_EQ (confirm, action_menuitem_label_is_ellipsized ("indicator.switch-to-greeter"));
+ ASSERT_EQ (confirm, action_menuitem_label_is_ellipsized ("indicator.logout"));
+ if (action_menuitem_exists ("indicator.reboot"))
+ ASSERT_EQ (confirm, action_menuitem_label_is_ellipsized ("indicator.reboot"));
+ ASSERT_EQ (confirm, action_menuitem_label_is_ellipsized ("indicator.power-off"));
+
+ // cleanup
+ g_settings_reset (mock_settings, confirm_supported_key);
+}
+
+TEST_F (ServiceTest, ConfirmationDisabledByUser)
+{
+ const char * const confirm_supported_key = "can-prompt";
+ const char * const confirm_disabled_key = "suppress-logout-restart-shutdown";
+
+ bool confirm_supported = g_settings_get_boolean (mock_settings, confirm_supported_key);
+ bool confirm_disabled = g_settings_get_boolean (indicator_settings, confirm_disabled_key);
+ bool confirm = confirm_supported && !confirm_disabled;
+
+ // confirm that the ellipsis are correct
+ ASSERT_EQ (confirm, action_menuitem_label_is_ellipsized ("indicator.switch-to-greeter"));
+ ASSERT_EQ (confirm, action_menuitem_label_is_ellipsized ("indicator.logout"));
+ if (action_menuitem_exists ("indicator.reboot"))
+ ASSERT_EQ (confirm, action_menuitem_label_is_ellipsized ("indicator.reboot"));
+ ASSERT_EQ (confirm, action_menuitem_label_is_ellipsized ("indicator.power-off"));
+
+ // now toggle the can-prompt flag
+ confirm_disabled = !confirm_disabled;
+ g_settings_set_boolean (indicator_settings, confirm_disabled_key, confirm_disabled);
+ confirm = confirm_supported && !confirm_disabled;
+
+ wait_for_menu_resync ();
+
+ // confirm that the ellipsis are correct
+ ASSERT_EQ (confirm, action_menuitem_label_is_ellipsized ("indicator.switch-to-greeter"));
+ ASSERT_EQ (confirm, action_menuitem_label_is_ellipsized ("indicator.logout"));
+ if (action_menuitem_exists ("indicator.reboot"))
+ ASSERT_EQ (confirm, action_menuitem_label_is_ellipsized ("indicator.reboot"));
+ ASSERT_EQ (confirm, action_menuitem_label_is_ellipsized ("indicator.power-off"));
+
+ // cleanup
+ g_settings_reset (indicator_settings, confirm_disabled_key);
+}
+
/**
- * This is a basic test to see if we can launch indicator-session-service
- * and call its "GetUserRealName" method. The test succeeds if we can launch
- * the service, call the method, and get response that equals g_get_real_name().
- *
- * (You may be wondering why GetUserRealName() exists at all, instead of clients
- * using g_get_real_name(). It's because the former updates itslef when the user
- * edits his real name, while the latter returns its cached copy of the old name.)
+ * Check that the default menu has items for each of these actions
*/
-TEST_F (ClientTest, TestCanStartService)
-{
- GError * error;
- GVariant * result;
- const gchar * name;
-
- // call GetUserRealName(), which as a side effect should activate
- // indicator-session-service via the .service file in the tests/ directory
- error = NULL;
- result = g_dbus_connection_call_sync (session_bus,
- INDICATOR_SESSION_DBUS_NAME,
- INDICATOR_SESSION_SERVICE_DBUS_OBJECT,
- INDICATOR_SESSION_SERVICE_DBUS_IFACE,
- "GetUserRealName",
- NULL,
- G_VARIANT_TYPE("(s)"),
- G_DBUS_CALL_FLAGS_NONE,
- -1,
- NULL,
- &error);
-
- EXPECT_TRUE (error == NULL);
- ASSERT_TRUE (result != NULL);
-
- if (error != NULL)
- {
- g_warning ("GetUserRealName failed: %s", error->message);
- g_clear_error (&error);
- }
-
- name = NULL;
- g_variant_get (result, "(&s)", &name);
- ASSERT_STREQ (g_get_real_name(), name);
- g_clear_pointer (&result, g_variant_unref);
-
- // call IndicatorService's Shutdown() method for a clean exit
- result = g_dbus_connection_call_sync (session_bus,
- INDICATOR_SESSION_DBUS_NAME,
- "/org/ayatana/indicator/service",
- "org.ayatana.indicator.service",
- "Shutdown", NULL,
- NULL,
- G_DBUS_CALL_FLAGS_NONE,
- -1, NULL, NULL);
- g_clear_pointer (&result, g_variant_unref);
+TEST_F (ServiceTest, DefaultMenuItems)
+{
+ ASSERT_TRUE (find_menu_item_for_action ("indicator.about", NULL, NULL));
+ ASSERT_TRUE (find_menu_item_for_action ("indicator.help", NULL, NULL));
+ ASSERT_TRUE (find_menu_item_for_action ("indicator.settings", NULL, NULL));
+ ASSERT_TRUE (find_menu_item_for_action ("indicator.switch-to-greeter", NULL, NULL));
+ ASSERT_TRUE (find_menu_item_for_action ("indicator.switch-to-guest", NULL, NULL));
+ ASSERT_TRUE (find_menu_item_for_action ("indicator.logout", NULL, NULL));
+ ASSERT_TRUE (find_menu_item_for_action ("indicator.suspend", NULL, NULL));
+ ASSERT_TRUE (find_menu_item_for_action ("indicator.hibernate", NULL, NULL));
+ ASSERT_TRUE (find_menu_item_for_action ("indicator.power-off", NULL, NULL));
+}
+
+TEST_F (ServiceTest, OnlineAccountError)
+{
+ bool err;
+ int pos = -1;
+ GMenuModel * model = 0;
+ const char * const error_key = "has-online-account-error";
+
+ // check the initial default header state
+ check_header ("", "system-devices-panel", "System");
+
+ // check that the menuitems' existence matches the error flag
+ err = g_settings_get_boolean (mock_settings, error_key);
+ ASSERT_FALSE (err);
+ ASSERT_EQ (err, find_menu_item_for_action ("indicator.online-accounts", &model, &pos));
+ g_clear_object (&model);
+
+ // now toggle the error flag
+ err = !err;
+ g_settings_set_boolean (mock_settings, error_key, err);
+
+ // wait for the _header action and error menuitem to update
+ wait_for_menu_resync ();
+
+ // check that the menuitems' existence matches the error flag
+ ASSERT_TRUE (g_settings_get_boolean (mock_settings, error_key));
+ ASSERT_TRUE (find_menu_item_for_action ("indicator.online-accounts", &model, &pos));
+ g_clear_object (&model);
+
+ // check that the service has a corresponding action
+ ASSERT_TRUE (g_action_group_has_action (G_ACTION_GROUP(action_group), "online-accounts"));
+ ASSERT_TRUE (g_action_group_get_action_enabled (G_ACTION_GROUP(action_group), "online-accounts"));
+
+ // confirm that activating the action is handled by the service
+ g_action_group_activate_action (G_ACTION_GROUP(action_group), "online-accounts", NULL);
+ wait_for_signal (mock_settings, "changed::last-command");
+ check_last_command_is ("online-accounts");
+
+ // check that the header's icon and a11y adjusted to the error state
+ check_header ("", "system-devices-panel-alert", "System (Attention Required)");
+
+ // cleanup
+ g_settings_reset (mock_settings, error_key);
+}
+
+namespace
+{
+ gboolean set_live_session_to_true (gpointer unused G_GNUC_UNUSED)
+ {
+ const char * const live_session_key = "is-live-session";
+ g_settings_set_boolean (mock_settings, live_session_key, true);
+ return G_SOURCE_REMOVE;
+ }
+}
+
+TEST_F (ServiceTest, LiveSession)
+{
+ gboolean b;
+ const char * const live_session_key = "is-live-session";
+
+ // default BackendMock is not a live session
+ ASSERT_FALSE (g_settings_get_boolean (mock_settings, live_session_key));
+ g_object_get (mock_users, INDICATOR_SESSION_USERS_PROP_IS_LIVE_SESSION, &b, NULL);
+ ASSERT_FALSE (b);
+
+ // confirm that we can see live sessions
+ g_idle_add (set_live_session_to_true, NULL);
+ wait_for_signal (mock_users, "notify::" INDICATOR_SESSION_USERS_PROP_IS_LIVE_SESSION);
+ ASSERT_TRUE (g_settings_get_boolean (mock_settings, live_session_key));
+ wait_msec (50);
+ g_object_get (mock_users, INDICATOR_SESSION_USERS_PROP_IS_LIVE_SESSION, &b, NULL);
+ ASSERT_TRUE (b);
+
+ // cleanup
+ g_settings_reset (mock_settings, live_session_key);
+}
+
+
+TEST_F (ServiceTest, User)
+{
+ const char * const error_key = "has-online-account-error";
+ const char * const show_name_key = "show-real-name-on-panel";
+
+ struct {
+ guint uid;
+ guint64 login_frequency;
+ const gchar * user_name;
+ const gchar * real_name;
+ } account_info[] = {
+ { 101, 134, "whartnell", "First Doctor" },
+ { 102, 119, "ptroughton", "Second Doctor" },
+ { 103, 128, "jpertwee", "Third Doctor" },
+ { 104, 172, "tbaker", "Fourth Doctor" },
+ { 105, 69, "pdavison", "Fifth Doctor" },
+ { 106, 31, "cbaker", "Sixth Doctor" },
+ { 107, 42, "smccoy", "Seventh Doctor" },
+ { 108, 1, "pmcgann", "Eigth Doctor" },
+ { 109, 13, "ceccleston", "Ninth Doctor" },
+ { 110, 47, "dtennant", "Tenth Doctor" },
+ { 111, 34, "msmith", "Eleventh Doctor" },
+ { 201, 1, "rhurndall", "First Doctor" }
+ };
+
+ // Find the switcher menu model.
+ // In BackendMock's default setup, it will only two menuitems: greeter & guest
+ int pos = 0;
+ GMenuModel * switch_menu = 0;
+ ASSERT_TRUE (find_menu_item_for_action ("indicator.switch-to-greeter", &switch_menu, &pos));
+ ASSERT_EQ (0, pos);
+ ASSERT_EQ (2, g_menu_model_get_n_items (switch_menu));
+ g_clear_object (&switch_menu);
+
+ // now add some users
+ IndicatorSessionUser * users[12];
+ for (int i=0; i<12; ++i)
+ users[i] = 0;
+ for (int i=0; i<5; ++i)
+ {
+ IndicatorSessionUser * u = g_new0 (IndicatorSessionUser, 1);
+ u->is_current_user = false;
+ u->is_logged_in = false;
+ u->uid = account_info[i].uid;
+ u->login_frequency = account_info[i].login_frequency;
+ u->user_name = g_strdup (account_info[i].user_name);
+ u->real_name = g_strdup (account_info[i].real_name);
+ indicator_session_users_mock_add_user (INDICATOR_SESSION_USERS_MOCK(mock_users), u);
+ users[i] = u;
+ }
+
+ wait_for_menu_resync ();
+
+ // now there should be 7 menuitems: greeter + guest + the five doctors
+ ASSERT_TRUE (find_menu_item_for_action ("indicator.switch-to-greeter", &switch_menu, &pos));
+ ASSERT_EQ (0, pos);
+ ASSERT_EQ (7, g_menu_model_get_n_items (switch_menu));
+ // confirm that the doctor names are sorted
+ check_label ("Fifth Doctor", switch_menu, 2);
+ check_label ("First Doctor", switch_menu, 3);
+ check_label ("Fourth Doctor", switch_menu, 4);
+ check_label ("Second Doctor", switch_menu, 5);
+ check_label ("Third Doctor", switch_menu, 6);
+ g_clear_object (&switch_menu);
+
+ // now remove a couple of 'em
+ indicator_session_users_mock_remove_user (INDICATOR_SESSION_USERS_MOCK(mock_users), account_info[3].uid);
+ indicator_session_users_mock_remove_user (INDICATOR_SESSION_USERS_MOCK(mock_users), account_info[4].uid);
+
+ wait_for_menu_resync ();
+
+ // now there should be 5 menuitems: greeter + guest + the three doctors
+ ASSERT_TRUE (find_menu_item_for_action ("indicator.switch-to-greeter", &switch_menu, &pos));
+ ASSERT_EQ (0, pos);
+ ASSERT_EQ (5, g_menu_model_get_n_items (switch_menu));
+ // confirm that the doctor names are sorted
+ check_label ("First Doctor", switch_menu, 2);
+ check_label ("Second Doctor", switch_menu, 3);
+ check_label ("Third Doctor", switch_menu, 4);
+ g_clear_object (&switch_menu);
+
+ // now let's have the third one be the current user
+ users[2]->is_current_user = true;
+ users[2]->is_logged_in = true;
+ indicator_session_users_changed (mock_users, users[2]->uid);
+
+ wait_for_menu_resync ();
+
+ // now there should be 5 menuitems: greeter + guest + the three doctors
+ ASSERT_TRUE (find_menu_item_for_action ("indicator.switch-to-greeter", &switch_menu, &pos));
+ ASSERT_EQ (0, pos);
+ ASSERT_EQ (5, g_menu_model_get_n_items (switch_menu));
+ g_clear_object (&switch_menu);
+
+ // oh hey, while we've got an active user let's check the header
+ ASSERT_FALSE (g_settings_get_boolean (indicator_settings, show_name_key));
+ ASSERT_FALSE (g_settings_get_boolean (mock_settings, error_key));
+ check_header ("", "system-devices-panel", "System");
+ g_settings_set_boolean (indicator_settings, show_name_key, true);
+ wait_for_signal (action_group, "action-state-changed");
+ check_header ("Third Doctor", "system-devices-panel", "System, Third Doctor");
+ g_settings_set_boolean (mock_settings, error_key, true);
+ wait_for_signal (action_group, "action-state-changed");
+ check_header ("Third Doctor", "system-devices-panel-alert", "System, Third Doctor (Attention Required)");
+ g_settings_reset (mock_settings, error_key);
+ g_settings_reset (indicator_settings, show_name_key);
+ wait_for_menu_resync ();
+
+ // try setting the max user count to 2...
+ // since troughton has the fewest logins, he should get culled
+ g_object_set (service, "max-users", 2, NULL);
+ guint max_users;
+ g_object_get (service, "max-users", &max_users, NULL);
+ ASSERT_EQ (2, max_users);
+ wait_for_menu_resync ();
+ ASSERT_TRUE (find_menu_item_for_action ("indicator.switch-to-greeter", &switch_menu, &pos));
+ ASSERT_EQ (0, pos);
+ ASSERT_EQ (4, g_menu_model_get_n_items (switch_menu));
+ check_label ("First Doctor", switch_menu, 2);
+ check_label ("Third Doctor", switch_menu, 3);
+ g_clear_object (&switch_menu);
+
+ // add some more, test sorting and culling.
+ // add in all the doctors, but only show 7, and make msmith the current session
+ g_object_set (service, "max-users", 7, NULL);
+ g_object_get (service, "max-users", &max_users, NULL);
+ ASSERT_EQ (7, max_users);
+ for (int i=3; i<12; ++i)
+ {
+ IndicatorSessionUser * u = g_new0 (IndicatorSessionUser, 1);
+ u->is_current_user = false;
+ u->is_logged_in = false;
+ u->uid = 101 + i;
+ u->login_frequency = account_info[i].login_frequency;
+ u->user_name = g_strdup (account_info[i].user_name);
+ u->real_name = g_strdup (account_info[i].real_name);
+ indicator_session_users_mock_add_user (INDICATOR_SESSION_USERS_MOCK(mock_users), u);
+ users[i] = u;
+ }
+ users[2]->is_current_user = false;
+ indicator_session_users_changed (mock_users, users[2]->uid);
+ users[10]->is_current_user = true;
+ users[10]->is_logged_in = true;
+ indicator_session_users_changed (mock_users, users[10]->uid);
+ wait_for_menu_resync ();
+ ASSERT_TRUE (find_menu_item_for_action ("indicator.switch-to-greeter", &switch_menu, &pos));
+ ASSERT_EQ (0, pos);
+ ASSERT_EQ (9, g_menu_model_get_n_items (switch_menu));
+ check_label ("Eleventh Doctor", switch_menu, 2);
+ check_label ("Fifth Doctor", switch_menu, 3);
+ check_label ("First Doctor", switch_menu, 4);
+ check_label ("Fourth Doctor", switch_menu, 5);
+ check_label ("Second Doctor", switch_menu, 6);
+ check_label ("Tenth Doctor", switch_menu, 7);
+ check_label ("Third Doctor", switch_menu, 8);
+ g_clear_object (&switch_menu);
+
+ // now switch to one of the doctors
+ g_action_group_activate_action (G_ACTION_GROUP(action_group),
+ "switch-to-user",
+ g_variant_new_string("tbaker"));
+ wait_for_signal (mock_settings, "changed::last-command");
+ check_last_command_is ("switch-to-user::tbaker");
}
diff --git a/trim-lcov.py b/trim-lcov.py
new file mode 100755
index 0000000..78613d3
--- /dev/null
+++ b/trim-lcov.py
@@ -0,0 +1,53 @@
+#!/usr/bin/python
+
+# This script removes branch and/or line coverage data for lines that
+# contain a particular substring.
+#
+# In the interest of "fairness" it removes all branch or coverage data
+# when a match is found -- not just negative data. It is therefore
+# likely that running this script will actually reduce the total number
+# of lines and branches that are marked as covered (in absolute terms).
+#
+# This script intentionally avoids checking for errors. Any exceptions
+# will trigger make to fail.
+#
+# Author: Ryan Lortie <desrt@desrt.ca>
+
+import sys
+
+line_suppress = ['g_assert_not_reached']
+branch_suppress = ['g_assert', 'g_return_if_fail', 'g_clear_object', 'g_clear_pointer', 'g_return_val_if_fail', 'G_DEFINE_TYPE']
+
+def check_suppress(suppressions, source, data):
+ line, _, rest = data.partition(',')
+ line = int(line) - 1
+
+ assert line < len(source)
+
+ for suppression in suppressions:
+ if suppression in source[line]:
+ return True
+
+ return False
+
+source = []
+for line in sys.stdin:
+ line = line[:-1]
+
+ keyword, _, rest = line.partition(':')
+
+ # Source file
+ if keyword == 'SF':
+ source = file(rest).readlines()
+
+ # Branch coverage data
+ elif keyword == 'BRDA':
+ if check_suppress(branch_suppress, source, rest):
+ continue
+
+ # Line coverage data
+ elif keyword == 'DA':
+ if check_suppress(line_suppress, source, rest):
+ continue
+
+ print line