aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt96
-rw-r--r--Makefile.am43
-rw-r--r--Makefile.am.coverage48
-rwxr-xr-xautogen.sh27
-rw-r--r--cmake/GCov.cmake51
-rw-r--r--cmake/GdbusCodegen.cmake36
-rw-r--r--cmake/Translations.cmake37
-rw-r--r--cmake/UseGSettings.cmake23
-rw-r--r--configure.ac138
-rw-r--r--data/CMakeLists.txt90
-rw-r--r--data/Makefile.am58
-rw-r--r--data/com.canonical.indicator.power5
-rw-r--r--data/com.canonical.indicator.power.Battery.xml23
-rw-r--r--data/indicator-power.conf.in8
-rw-r--r--data/indicator-power.desktop.in4
-rw-r--r--data/indicator-power.upstart.desktop.in10
-rw-r--r--debian/changelog117
-rw-r--r--debian/control22
-rwxr-xr-xdebian/rules5
-rw-r--r--m4/gcov.m486
-rw-r--r--m4/gtest.m463
-rw-r--r--po/CMakeLists.txt3
-rw-r--r--src/CMakeLists.txt46
-rw-r--r--src/Makefile.am81
-rw-r--r--src/brightness.c509
-rw-r--r--src/brightness.h67
-rw-r--r--src/dbus-shared.h28
-rw-r--r--src/device-provider-upower.c509
-rw-r--r--src/device-provider-upower.h6
-rw-r--r--src/device-provider.c2
-rw-r--r--src/device.c88
-rw-r--r--src/device.h2
-rw-r--r--src/ib-brightness-control.c156
-rw-r--r--src/ib-brightness-control.h33
-rw-r--r--src/main.c10
-rw-r--r--src/notifier.c504
-rw-r--r--src/notifier.h74
-rw-r--r--src/org.freedesktop.UPower.xml43
-rw-r--r--src/service.c341
-rw-r--r--tests/CMakeLists.txt61
-rw-r--r--tests/Makefile.am64
-rw-r--r--tests/device-provider-mock.c107
-rw-r--r--tests/device-provider-mock.h79
-rw-r--r--tests/glib-fixture.h141
-rw-r--r--tests/indicator-power-service-cmdline-battery.cc127
-rw-r--r--tests/manual87
-rw-r--r--tests/test-device.cc243
-rw-r--r--tests/test-notify.cc410
48 files changed, 3493 insertions, 1318 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..569100d
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,96 @@
+project(indicator-power C CXX)
+cmake_minimum_required(VERSION 2.8.9)
+
+list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
+
+set(PROJECT_VERSION "14.10.0")
+set(PACKAGE ${CMAKE_PROJECT_NAME})
+set(GETTEXT_PACKAGE "indicator-power")
+add_definitions (-DGETTEXT_PACKAGE="${GETTEXT_PACKAGE}"
+ -DGNOMELOCALEDIR="${CMAKE_INSTALL_FULL_LOCALEDIR}")
+
+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 (CheckIncludeFile)
+include (FindPkgConfig)
+
+pkg_check_modules(SERVICE_DEPS REQUIRED
+ glib-2.0>=2.36
+ gio-2.0>=2.36
+ gio-unix-2.0>=2.36
+ gudev-1.0>=204
+ libnotify>=0.7.6
+ url-dispatcher-1>=1)
+
+include_directories (SYSTEM ${SERVICE_DEPS_INCLUDE_DIRS})
+
+##
+## custom targets
+##
+
+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)
+
+##
+## Actual building
+##
+
+if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
+ set(C_WARNING_ARGS "${C_WARNING_ARGS} -Weverything")
+ set(C_WARNING_ARGS "${C_WARNING_ARGS} -Wno-c++98-compat -Wno-padded") # these are annoying
+ set(C_WARNING_ARGS "${C_WARNING_ARGS} -Wno-documentation") # gtk-doc != doxygen
+else()
+ set(C_WARNING_ARGS "${C_WARNING_ARGS} -Wall -Wextra -Wpedantic -Wformat=2")
+endif()
+set(C_WARNING_ARGS "${C_WARNING_ARGS} -Wno-missing-field-initializers") # GActionEntry
+
+
+include_directories (${CMAKE_CURRENT_SOURCE_DIR}/include)
+include_directories (${CMAKE_CURRENT_BINARY_DIR}/include)
+
+# 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 ()
+
+# actually build things
+add_subdirectory(src)
+add_subdirectory(data)
+add_subdirectory(po)
+if (${enable_tests})
+ add_subdirectory(tests)
+endif ()
+
diff --git a/Makefile.am b/Makefile.am
deleted file mode 100644
index fbf05ca..0000000
--- a/Makefile.am
+++ /dev/null
@@ -1,43 +0,0 @@
-SUBDIRS = po data src
-
-if BUILD_TESTS
-SUBDIRS += tests
-# build src first
-tests: src
-endif
-
-
-############################################################
-
-dist_noinst_SCRIPTS = \
- autogen.sh
-
-############################################################
-
-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)/build-aux/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)/build-aux/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/autogen.sh b/autogen.sh
deleted file mode 100755
index 1912c87..0000000
--- a/autogen.sh
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/sh
-
-test -n "$srcdir" || srcdir=`dirname "$0"`
-test -n "$srcdir" || srcdir=.
-
-olddir=`pwd`
-cd $srcdir
-
-AUTORECONF=`which autoreconf`
-if test -z $AUTORECONF; then
- echo "*** No autoreconf found, please intall it ***"
- exit 1
-fi
-
-INTLTOOLIZE=`which intltoolize`
-if test -z $INTLTOOLIZE; then
- echo "*** No intltoolize found, please install the intltool package ***"
- exit 1
-fi
-
-mkdir -p build-aux
-
-autopoint --force
-AUTOPOINT='intltoolize --automake --copy' autoreconf --force --install --verbose
-
-cd $olddir
-test -n "$NOCONFIGURE" || "$srcdir/configure" "$@"
diff --git a/cmake/GCov.cmake b/cmake/GCov.cmake
new file mode 100644
index 0000000..81c0c40
--- /dev/null
+++ b/cmake/GCov.cmake
@@ -0,0 +1,51 @@
+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 "${LCOV_EXECUTABLE}" -r dconf-lcov.info /usr/include/\\* -o nosys-lcov.info
+ COMMAND LANG=C "${GENHTML_EXECUTABLE}" --prefix ${CMAKE_BINARY_DIR} --output-directory lcov-html --legend --show-details nosys-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..b51c39d
--- /dev/null
+++ b/cmake/Translations.cmake
@@ -0,0 +1,37 @@
+# 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}/${PO_INPUT_BASE}/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)
+
+ # init this list, which will hold all the sources across all dirs
+ set(SOURCES "")
+
+ # add each directory's sources to the overall sources list
+ foreach(FILES_INPUT ${ARGN})
+ set (DIR ${CMAKE_CURRENT_SOURCE_DIR}/${FILES_INPUT})
+ file (GLOB_RECURSE DIR_SOURCES ${DIR}/*.c ${DIR}/*.cc ${DIR}/*.cpp ${DIR}/*.cxx ${DIR}/*.vala)
+ set (SOURCES ${SOURCES} ${DIR_SOURCES})
+ endforeach()
+
+ add_custom_command (TARGET pot COMMAND
+ ${XGETTEXT_EXECUTABLE} -d ${NLS_PACKAGE} -o ${CMAKE_CURRENT_SOURCE_DIR}/${NLS_PACKAGE}.pot
+ ${SOURCES} --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 ca7e793..0000000
--- a/configure.ac
+++ /dev/null
@@ -1,138 +0,0 @@
-AC_INIT([indicator-power],
- [13.10.0],
- [http://bugs.launchpad.net/indicator-power],
- [indicator-power],
- [http://launchpad.net/indicator-power])
-AC_COPYRIGHT([Copyright 2011-2013 Canonical])
-
-AC_PREREQ([2.64])
-
-AC_CONFIG_HEADERS([config.h])
-AC_CONFIG_SRCDIR([src/device.c])
-AC_CONFIG_MACRO_DIR([m4])
-AC_CONFIG_AUX_DIR([build-aux])
-
-AM_INIT_AUTOMAKE([1.11 -Wall foreign dist-xz check-news])
-AM_MAINTAINER_MODE([enable])
-
-AM_SILENT_RULES([yes])
-
-# Check for programs
-AC_PROG_CC
-AM_PROG_CC_C_O
-AC_PROG_CXX
-AM_PROG_AR
-
-# Initialize libtool
-LT_PREREQ([2.2.6])
-LT_INIT
-
-
-###########################
-# Dependencies
-###########################
-
-GLIB_REQUIRED_VERSION=2.35.4
-GIO_REQUIRED_VERSION=2.26
-GIO_UNIX_REQUIRED_VERSION=2.26
-GUDEV_REQUIRED_VERSION=204
-
-PKG_CHECK_MODULES([SERVICE_DEPS],[glib-2.0 >= $GLIB_REQUIRED_VERSION
- gio-2.0 >= $GIO_REQUIRED_VERSION
- gio-unix-2.0 >= $GIO_UNIX_REQUIRED_VERSION
- gudev-1.0 >= $GUDEV_REQUIRED_VERSION
- url-dispatcher-1])
-
-###########################
-# GSETTINGS
-###########################
-
-GLIB_GSETTINGS
-
-###########################
-# 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
- 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
-fi
-AM_CONDITIONAL([BUILD_TESTS],[test "x$enable_tests" = "xyes"])
-
-###########################
-# 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_CXXFLAGS)
-AC_SUBST(COVERAGE_LDFLAGS)
-
-##############################
-# 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
-###########################
-
-IT_PROG_INTLTOOL([0.50.0])
-
-AM_GNU_GETTEXT([external])
-AM_GNU_GETTEXT_VERSION([0.17])
-
-AC_SUBST([GETTEXT_PACKAGE],[${PACKAGE_TARNAME}])
-AC_DEFINE([GETTEXT_PACKAGE],[PACKAGE_TARNAME],[Define to the gettext package name.])
-AC_DEFINE_PATH([GNOMELOCALEDIR],"${datadir}/locale",[locale directory])
-
-###########################
-# Files
-###########################
-
-AC_CONFIG_FILES([
-Makefile
-po/Makefile.in
-data/Makefile
-src/Makefile
-tests/Makefile
-])
-AC_OUTPUT
-
-###########################
-# Results
-###########################
-
-AC_MSG_NOTICE([
-
-Power Indicator Configuration:
-
- Prefix: $prefix
- Unit Tests: $enable_tests
- gcov: $use_gcov
-
-])
diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt
new file mode 100644
index 0000000..0750128
--- /dev/null
+++ b/data/CMakeLists.txt
@@ -0,0 +1,90 @@
+##
+## GSettings schema
+##
+
+include (UseGSettings)
+set (SCHEMA_NAME "com.canonical.indicator.power.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 Job File
+##
+
+# where to install
+set (UPSTART_JOB_DIR "${CMAKE_INSTALL_FULL_DATADIR}/upstart/sessions")
+message (STATUS "${UPSTART_JOB_DIR} is the Upstart Job File 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}")
+
+##
+## XDG Autostart File
+##
+
+# where to install
+set (XDG_AUTOSTART_DIR "/etc/xdg/autostart")
+message (STATUS "${XDG_AUTOSTART_DIR} is the DBus Service File install dir")
+
+set (XDG_AUTOSTART_NAME "${CMAKE_PROJECT_NAME}.desktop")
+set (XDG_AUTOSTART_FILE "${CMAKE_CURRENT_BINARY_DIR}/${XDG_AUTOSTART_NAME}")
+set (XDG_AUTOSTART_FILE_IN "${CMAKE_CURRENT_SOURCE_DIR}/${XDG_AUTOSTART_NAME}.in")
+
+# build it
+set (pkglibexecdir "${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}")
+configure_file ("${XDG_AUTOSTART_FILE_IN}" "${XDG_AUTOSTART_FILE}")
+
+# install it
+install (FILES "${XDG_AUTOSTART_FILE}"
+ DESTINATION "${XDG_AUTOSTART_DIR}")
+
+##
+## Upstart XDG Autostart Override
+##
+
+# where to install
+set (UPSTART_XDG_AUTOSTART_DIR "${CMAKE_INSTALL_FULL_DATAROOTDIR}/upstart/xdg/autostart")
+message (STATUS "${UPSTART_XDG_AUTOSTART_DIR} is the Upstart XDG autostart override dir")
+
+set (UPSTART_XDG_AUTOSTART_NAME "${CMAKE_PROJECT_NAME}.upstart.desktop")
+set (UPSTART_XDG_AUTOSTART_FILE "${CMAKE_CURRENT_BINARY_DIR}/${UPSTART_XDG_AUTOSTART_NAME}")
+set (UPSTART_XDG_AUTOSTART_FILE_IN "${CMAKE_CURRENT_SOURCE_DIR}/${UPSTART_XDG_AUTOSTART_NAME}.in")
+
+# build it
+set (pkglibexecdir "${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}")
+configure_file ("${UPSTART_XDG_AUTOSTART_FILE_IN}" "${UPSTART_XDG_AUTOSTART_FILE}")
+
+# install it
+install (FILES "${UPSTART_XDG_AUTOSTART_FILE}"
+ DESTINATION "${UPSTART_XDG_AUTOSTART_DIR}"
+ RENAME "${XDG_AUTOSTART_NAME}")
+
+##
+## 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.power")
+set (UNITY_INDICATOR_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${UNITY_INDICATOR_NAME}")
+
+install (FILES "${UNITY_INDICATOR_FILE}"
+ DESTINATION "${UNITY_INDICATOR_DIR}")
diff --git a/data/Makefile.am b/data/Makefile.am
deleted file mode 100644
index 9a4caca..0000000
--- a/data/Makefile.am
+++ /dev/null
@@ -1,58 +0,0 @@
-BUILT_SOURCES=
-CLEANFILES=
-EXTRA_DIST=
-
-#
-# the indicator bus file
-#
-
-indicatorsdir = $(prefix)/share/unity/indicators
-dist_indicators_DATA = com.canonical.indicator.power
-
-#
-# the upstart job file
-#
-
-upstart_jobsdir = $(datadir)/upstart/sessions
-upstart_jobs_DATA = indicator-power.conf
-upstart_jobs_in = $(upstart_jobs_DATA:.conf=.conf.in)
-$(upstart_jobs_DATA): $(upstart_jobs_in)
- $(AM_V_GEN) $(SED) -e "s|\@pkglibexecdir\@|$(pkglibexecdir)|" $< > $@
-BUILT_SOURCES += $(upstart_jobs_DATA)
-CLEANFILES += $(upstart_jobs_DATA)
-EXTRA_DIST += $(upstart_jobs_in)
-
-#
-# the xdg autostart job file
-#
-
-xdg_autostartdir = /etc/xdg/autostart
-xdg_autostart_DATA = indicator-power.desktop
-xdg_autostart_in = $(xdg_autostart_DATA:.desktop=.desktop.in)
-$(xdg_autostart_DATA): $(xdg_autostart_in)
- $(AM_V_GEN) $(SED) -e "s|\@pkglibexecdir\@|$(pkglibexecdir)|" $< > $@
-BUILT_SOURCES += $(xdg_autostart_DATA)
-CLEANFILES += $(xdg_autostart_DATA)
-EXTRA_DIST += $(xdg_autostart_in)
-
-#
-# the gettings
-#
-
-gsettings_in_file = com.canonical.indicator.power.gschema.xml.in
-gsettings_SCHEMAS = $(gsettings_in_file:.xml.in=.xml)
-CLEANFILES += $(gsettings_SCHEMAS)
-
-@INTLTOOL_XML_NOMERGE_RULE@
-
-@GSETTINGS_RULES@
-
-dist_noinst_DATA = \
- com.canonical.indicator.power.gschema.xml \
- $(gsettings_in_file)
-
-CLEANFILES += \
- $(gsettings_SCHEMAS)
-
-MAINTAINERCLEANFILES = \
- $(gsettings_SCHEMAS:.xml=.valid)
diff --git a/data/com.canonical.indicator.power b/data/com.canonical.indicator.power
index b0b8fb7..8b862a2 100644
--- a/data/com.canonical.indicator.power
+++ b/data/com.canonical.indicator.power
@@ -5,6 +5,7 @@ Position=40
[phone]
ObjectPath=/com/canonical/indicator/power/phone
+Position=25
[desktop]
ObjectPath=/com/canonical/indicator/power/desktop
@@ -17,3 +18,7 @@ ObjectPath=/com/canonical/indicator/power/desktop_greeter
[phone_greeter]
ObjectPath=/com/canonical/indicator/power/desktop_greeter
+Position=25
+
+[ubiquity]
+ObjectPath=/com/canonical/indicator/power/desktop_greeter
diff --git a/data/com.canonical.indicator.power.Battery.xml b/data/com.canonical.indicator.power.Battery.xml
new file mode 100644
index 0000000..eca4524
--- /dev/null
+++ b/data/com.canonical.indicator.power.Battery.xml
@@ -0,0 +1,23 @@
+<?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="com.canonical.indicator.power.Battery">
+
+ <property name="PowerLevel" type="s" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>The battery's power level. Possible values: 'ok', 'low', 'very_low', 'critical'</doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="IsWarning" type="b" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>Whether or not indicator-power-service is warning the user about low battery power.</doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ </interface>
+</node>
diff --git a/data/indicator-power.conf.in b/data/indicator-power.conf.in
index a2bc7aa..665fb35 100644
--- a/data/indicator-power.conf.in
+++ b/data/indicator-power.conf.in
@@ -1,11 +1,9 @@
-description "Indicator Power Backend"
+description "Indicator Power Service"
-# Want to move to indicator-services-[start|end], but that's not all
-# there yet. Use the signals that exist today for now.
-
-start on indicators-loaded or indicator-services-start
+start on indicator-services-start
stop on desktop-end or indicator-services-end
respawn
+respawn limit 2 10
exec @pkglibexecdir@/indicator-power-service
diff --git a/data/indicator-power.desktop.in b/data/indicator-power.desktop.in
index 28025a2..c2fd54c 100644
--- a/data/indicator-power.desktop.in
+++ b/data/indicator-power.desktop.in
@@ -2,8 +2,8 @@
Type=Application
Name=Indicator Power
Exec=@pkglibexecdir@/indicator-power-service
-NotShowIn=Unity;
+OnlyShowIn=Unity;GNOME;
NoDisplay=true
StartupNotify=false
Terminal=false
-
+AutostartCondition=GNOME3 unless-session gnome
diff --git a/data/indicator-power.upstart.desktop.in b/data/indicator-power.upstart.desktop.in
new file mode 100644
index 0000000..5f95f8e
--- /dev/null
+++ b/data/indicator-power.upstart.desktop.in
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Type=Application
+Name=Indicator Power
+Exec=@pkglibexecdir@/indicator-power-service
+OnlyShowIn=Unity;GNOME;
+NoDisplay=true
+StartupNotify=false
+Terminal=false
+AutostartCondition=GNOME3 unless-session gnome
+Hidden=true
diff --git a/debian/changelog b/debian/changelog
index dbb6ea0..499aac0 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,120 @@
+indicator-power (12.10.6+14.10.20141006-0ubuntu1) utopic; urgency=low
+
+ [ Charles Kerr ]
+ * Move the position of this indicator on the panel. (LP: #1377294)
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 06 Oct 2014 17:31:07 +0000
+
+indicator-power (12.10.6+14.10.20140912-0ubuntu1) utopic; urgency=low
+
+ [ Charles Kerr ]
+ * Restore the the brightness slider and have the brightness setting
+ persist between reboots. (LP: #1289470)
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Fri, 12 Sep 2014 18:49:45 +0000
+
+indicator-power (12.10.6+14.10.20140909-0ubuntu1) utopic; urgency=low
+
+ [ Charles Kerr ]
+ * Add support for UPower 0.99. (LP: #1330037)
+ * When the phone's battery goes down past a certain level, pop up a
+ snap decision to warn the user. (LP: #1296431)
+
+ [ Ted Gould ]
+ * Synchronize process management across indicators
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 09 Sep 2014 04:22:52 +0000
+
+indicator-power (12.10.6+14.10.20140822-0ubuntu1) utopic; urgency=low
+
+ [ Charles Kerr ]
+ * Choose the icon that's closest to the current battery charge
+ percentage (LP: #1186181)
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Fri, 22 Aug 2014 16:15:51 +0000
+
+indicator-power (12.10.6+14.10.20140814-0ubuntu1) utopic; urgency=low
+
+ [ Charles Kerr ]
+ * Re-use the same Translations.cmake file across indicators (LP:
+ #1354058)
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 14 Aug 2014 14:51:02 +0000
+
+indicator-power (12.10.6+14.10.20140730-0ubuntu1) utopic; urgency=low
+
+ [ Charles Kerr ]
+ * Add low-battery notifications. (LP: #1317858)
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 30 Jul 2014 10:52:39 +0000
+
+indicator-power (12.10.6+14.10.20140718-0ubuntu1) utopic; urgency=low
+
+ [ Charles Kerr ]
+ * Switch from automake/autoconf to CMake.
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Fri, 18 Jul 2014 21:33:30 +0000
+
+indicator-power (12.10.6+14.10.20140624-0ubuntu1) utopic; urgency=low
+
+ [ Alberto Aguirre ]
+ * Changes to address setBrightness interface moving from powerd to
+ unity-system-compositor
+
+ [ Iain Lane ]
+ * Drop powerd and u-s-c to suggests, since the code handles them not
+ being present and we don't need them on desktop (and they are in
+ universe).
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 24 Jun 2014 09:45:56 +0000
+
+indicator-power (12.10.6+14.10.20140611-0ubuntu1) utopic; urgency=low
+
+ [ Charles Kerr ]
+ * Prefer the 'battery-XXX-charging' (eg, 'battery-020-charging') icons
+ over the 'battery-low-charging' ones because the former are more
+ precise and likely closer to the actual battery level. (LP:
+ #1186181)
+
+ [ Iain Lane ]
+ * Remove the brightness slider from the phone menu. (LP: #1289470)
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 11 Jun 2014 17:21:30 +0000
+
+indicator-power (12.10.6+14.10.20140428-0ubuntu1) utopic; urgency=low
+
+ [ Ricardo Salveti de Araujo ]
+ * Updating code to reflect latest powerd dbus API changes
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 28 Apr 2014 23:25:53 +0000
+
+indicator-power (12.10.6+14.04.20140411-0ubuntu1) trusty; urgency=low
+
+ [ Sebastien Bacher ]
+ * export an ubiquity profile, reusing the desktop_greeter object (LP:
+ #1306604)
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Fri, 11 Apr 2014 14:01:36 +0000
+
+indicator-power (12.10.6+14.04.20140328-0ubuntu1) trusty; urgency=low
+
+ [ Charles Kerr ]
+ * If there are two batteries detected, combine their percentages and
+ their time-remainings as per the revised spec. (LP: #880881)
+
+ [ Lars Uebernickel ]
+ * Use com.canonical.indicator.basic menu item for device items That
+ menu item can handle non-square icons. (LP: #1263228)
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Fri, 28 Mar 2014 16:10:45 +0000
+
+indicator-power (12.10.6+14.04.20140326-0ubuntu1) trusty; urgency=low
+
+ [ Y.C cheng ]
+ * Set brightness via powerd if it exist (using dbus) (LP: #1287599)
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 26 Mar 2014 18:50:46 +0000
+
indicator-power (12.10.6+14.04.20140314-0ubuntu1) trusty; urgency=low
[ Charles Kerr ]
diff --git a/debian/control b/debian/control
index fa86f1d..ef641b4 100644
--- a/debian/control
+++ b/debian/control
@@ -2,16 +2,22 @@ Source: indicator-power
Section: gnome
Priority: optional
Maintainer: Ubuntu Core Developers <ubuntu-devel-discuss@lists.ubuntu.com>
-Build-Depends: debhelper (>= 9),
- dh-autoreconf,
- autopoint,
- intltool,
- libgtest-dev,
+Build-Depends: cmake,
+ libnotify-dev (>= 0.7.6),
libglib2.0-dev (>= 2.36),
libgudev-1.0-dev,
liburl-dispatcher1-dev,
- python,
-Standards-Version: 3.9.2
+ python:any,
+# for packaging
+ debhelper (>= 9),
+ dh-translations,
+ intltool,
+# for tests
+ libgtest-dev,
+ python3-dbusmock,
+ dbus-test-runner,
+ libdbustest1-dev,
+Standards-Version: 3.9.5
Homepage: https://launchpad.net/indicator-power
# If you aren't a member of ~indicator-applet-developers but need to upload
# packaging changes, just go ahead. ~indicator-applet-developers will notice
@@ -26,6 +32,8 @@ Depends: ${shlibs:Depends},
upower,
Recommends: unity-control-center | gnome-control-center (>= 3.1) | ubuntu-system-settings | switchboard-plug-power | xfce4-power-manager,
indicator-applet (>= 0.2) | indicator-renderer,
+Suggests: powerd,
+ unity-system-compositor (>= 0.0.4),
Description: Indicator showing power state.
This indicator displays current power management information and gives
the user a way to access power management preferences.
diff --git a/debian/rules b/debian/rules
index 83edd94..885b94c 100755
--- a/debian/rules
+++ b/debian/rules
@@ -7,10 +7,7 @@
export DPKG_GENSYMBOLS_CHECK_LEVEL=4
%:
- dh $@ --with autoreconf
-
-override_dh_autoreconf:
- NOCONFIGURE=1 dh_autoreconf ./autogen.sh
+ dh $@ --with translations
override_dh_install:
find debian/indicator-power -name \*.la -delete
diff --git a/m4/gcov.m4 b/m4/gcov.m4
deleted file mode 100644
index 2d0b4bb..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 1.10"
- 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..8325f6e
--- /dev/null
+++ b/po/CMakeLists.txt
@@ -0,0 +1,3 @@
+include (Translations)
+add_translations_directory ("${GETTEXT_PACKAGE}")
+add_translations_catalog ("${GETTEXT_PACKAGE}" ../src/)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..f7efb80
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,46 @@
+set (SERVICE_LIB "indicatorpowerservice")
+set (SERVICE_EXEC "indicator-power-service")
+
+add_definitions(-DG_LOG_DOMAIN="Indicator-Power")
+
+# handwritten sources
+set(SERVICE_MANUAL_SOURCES
+ brightness.c
+ device-provider-upower.c
+ device-provider.c
+ device.c
+ notifier.c
+ service.c)
+
+# generated sources
+include(GdbusCodegen)
+set(SERVICE_GENERATED_SOURCES)
+add_gdbus_codegen_with_namespace(SERVICE_GENERATED_SOURCES dbus-battery
+ com.canonical.indicator.power
+ Dbus
+ ${CMAKE_SOURCE_DIR}/data/com.canonical.indicator.power.Battery.xml)
+# add the bin dir to our include path so the code can find the generated header files
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+
+# add warnings/coverage info on handwritten files
+# but not the autogenerated ones...
+set(C_WARNING_ARGS "${C_WARNING_ARGS} -Wno-bad-function-cast") # g_clear_object()
+set(C_WARNING_ARGS "${C_WARNING_ARGS} -Wno-used-but-marked-unused") # G_ADD_PRIVATE
+set(C_WARNING_ARGS "${C_WARNING_ARGS} -Wno-disabled-macro-expansion") # G_DEFINE_TYPE
+set(C_WARNING_ARGS "${C_WARNING_ARGS} -Wno-assign-enum") # GParamFlags
+set(C_WARNING_ARGS "${C_WARNING_ARGS} -Wno-switch-enum")
+set_source_files_properties(${SERVICE_MANUAL_SOURCES}
+ PROPERTIES COMPILE_FLAGS "${C_WARNING_ARGS} ${GCOV_FLAGS} -g -std=c99")
+
+# the service library for tests to link against (basically, everything except main())
+add_library(${SERVICE_LIB} STATIC ${SERVICE_MANUAL_SOURCES} ${SERVICE_GENERATED_SOURCES})
+include_directories(${CMAKE_SOURCE_DIR})
+link_directories(${SERVICE_DEPS_LIBRARY_DIRS})
+
+# the executable: lib + main()
+add_executable (${SERVICE_EXEC} main.c)
+set_source_files_properties(${SERVICE_SOURCES} main.c PROPERTIES COMPILE_FLAGS "${C_WARNING_ARGS} -g -std=c99")
+target_link_libraries (${SERVICE_EXEC} ${SERVICE_LIB} ${SERVICE_DEPS_LIBRARIES} ${GCOV_LIBS})
+install (TARGETS ${SERVICE_EXEC} RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR})
+
diff --git a/src/Makefile.am b/src/Makefile.am
deleted file mode 100644
index be746db..0000000
--- a/src/Makefile.am
+++ /dev/null
@@ -1,81 +0,0 @@
-BUILT_SOURCES =
-EXTRA_DIST =
-CLEANFILES =
-
-SHARED_CFLAGS = \
- -Wall -Wextra -Werror \
- $(SERVICE_DEPS_CFLAGS) \
- -DG_LOG_DOMAIN=\"Indicator-Power\"
-
-###
-###
-
-upower_dbus_sources = \
- dbus-upower.c \
- dbus-upower.h
-
-$(upower_dbus_sources): org.freedesktop.UPower.xml
- $(AM_V_GEN) gdbus-codegen \
- --c-namespace Dbus \
- --interface-prefix org.freedesktop \
- --generate-c-code dbus-upower \
- $^
-
-BUILT_SOURCES += $(upower_dbus_sources)
-CLEANFILES += $(upower_dbus_sources)
-EXTRA_DIST += org.freedesktop.UPower.xml
-
-###
-###
-###
-
-noinst_LIBRARIES = libindicatorpower-upower.a libindicatorpower-service.a
-
-libindicatorpower_upower_a_SOURCES = \
- $(upower_dbus_sources) \
- device-provider-upower.c \
- device-provider-upower.h
-
-libindicatorpower_upower_a_CFLAGS = \
- $(SHARED_CFLAGS) \
- -Wno-unused-parameter \
- $(COVERAGE_CFLAGS)
-
-libindciatorpower_upower_a_LDFLAGS = $(COVERAGE_LDFLAGS)
-
-libindicatorpower_service_a_SOURCES = \
- ib-brightness-control.c \
- ib-brightness-control.h \
- device-provider.c \
- device-provider.h \
- device.c \
- device.h \
- service.c \
- service.h
-
-libindicatorpower_service_a_CFLAGS = \
- $(SHARED_CFLAGS) \
- -Wno-missing-field-initializers \
- $(COVERAGE_CFLAGS)
-
-libindicatorpower_service_a_LDFLAGS = $(COVERAGE_LDFLAGS)
-
-###
-###
-###
-
-pkglibexec_PROGRAMS = indicator-power-service
-
-indicator_power_service_SOURCES = main.c
-
-indicator_power_service_CFLAGS = \
- $(SHARED_CFLAGS) \
- $(COVERAGE_CFLAGS)
-
-indicator_power_service_LDADD = \
- libindicatorpower-upower.a \
- libindicatorpower-service.a \
- $(SERVICE_DEPS_LIBS)
-
-indicator_power_service_LDFLAGS = \
- $(COVERAGE_LDFLAGS)
diff --git a/src/brightness.c b/src/brightness.c
new file mode 100644
index 0000000..5e7c5e5
--- /dev/null
+++ b/src/brightness.c
@@ -0,0 +1,509 @@
+/*
+ * Copyright 2014 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 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/>.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ */
+
+#include "brightness.h"
+
+#include <gio/gio.h>
+
+#define SCHEMA_NAME "com.ubuntu.touch.system"
+#define KEY_AUTO "auto-brightness"
+#define KEY_AUTO_SUPPORTED "auto-brightness-supported"
+#define KEY_BRIGHTNESS "brightness"
+#define KEY_NEED_DEFAULT "brightness-needs-hardware-default"
+
+enum
+{
+ PROP_0,
+ PROP_PERCENTAGE,
+ PROP_AUTO,
+ PROP_AUTO_SUPPORTED,
+ LAST_PROP
+};
+
+static GParamSpec* properties[LAST_PROP];
+
+typedef struct
+{
+ GDBusConnection * system_bus;
+ GCancellable * cancellable;
+
+ GSettings * settings;
+
+ guint powerd_name_tag;
+
+ double percentage;
+
+ /* powerd brightness params */
+ gint powerd_dim;
+ gint powerd_min;
+ gint powerd_max;
+ gint powerd_default_value;
+ gboolean powerd_ab_supported;
+ gboolean have_powerd_params;
+}
+IndicatorPowerBrightnessPrivate;
+
+typedef IndicatorPowerBrightnessPrivate priv_t;
+
+G_DEFINE_TYPE_WITH_PRIVATE(IndicatorPowerBrightness,
+ indicator_power_brightness,
+ G_TYPE_OBJECT)
+
+#define get_priv(o) ((priv_t*)indicator_power_brightness_get_instance_private(o))
+
+/***
+**** GObject virtual functions
+***/
+
+static void
+my_get_property(GObject * o,
+ guint property_id,
+ GValue * value,
+ GParamSpec * pspec)
+{
+ IndicatorPowerBrightness * self = INDICATOR_POWER_BRIGHTNESS(o);
+ priv_t * p = get_priv(self);
+
+ switch (property_id)
+ {
+ case PROP_PERCENTAGE:
+ g_value_set_double(value, indicator_power_brightness_get_percentage(self));
+ break;
+
+ case PROP_AUTO:
+ g_value_set_boolean(value, p->settings ? g_settings_get_boolean(p->settings, KEY_AUTO) : FALSE);
+ break;
+
+ case PROP_AUTO_SUPPORTED:
+ g_value_set_boolean(value, p->powerd_ab_supported);
+ 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)
+{
+ IndicatorPowerBrightness * self = INDICATOR_POWER_BRIGHTNESS(o);
+ priv_t * p = get_priv(self);
+
+ switch (property_id)
+ {
+ case PROP_PERCENTAGE:
+ indicator_power_brightness_set_percentage(self, g_value_get_double(value));
+ break;
+
+ case PROP_AUTO:
+ if (p->settings != NULL)
+ g_settings_set_boolean (p->settings, KEY_AUTO, g_value_get_boolean(value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(o, property_id, pspec);
+ }
+}
+
+static void
+my_dispose(GObject * o)
+{
+ IndicatorPowerBrightness * self = INDICATOR_POWER_BRIGHTNESS(o);
+ priv_t * p = get_priv(self);
+
+ if (p->cancellable != NULL)
+ {
+ g_cancellable_cancel(p->cancellable);
+ g_clear_object(&p->cancellable);
+ }
+
+ if (p->powerd_name_tag)
+ {
+ g_bus_unwatch_name(p->powerd_name_tag);
+ p->powerd_name_tag = 0;
+ }
+
+ g_clear_object(&p->settings);
+ g_clear_object(&p->system_bus);
+
+ G_OBJECT_CLASS(indicator_power_brightness_parent_class)->dispose(o);
+}
+
+/***
+**** Percentage <-> Brightness Int conversion helpers
+***/
+
+static gdouble
+brightness_to_percentage(IndicatorPowerBrightness * self, int brightness)
+{
+ const priv_t * p;
+ gdouble percentage;
+
+ p = get_priv(self);
+ if (p->have_powerd_params)
+ {
+ const int lo = p->powerd_min;
+ const int hi = p->powerd_max;
+ percentage = (brightness-lo) / (double)(hi-lo);
+ }
+ else
+ {
+ percentage = 0;
+ }
+
+ return percentage;
+}
+
+static int
+percentage_to_brightness(IndicatorPowerBrightness * self, double percentage)
+{
+ const priv_t * p;
+ int brightness;
+
+ p = get_priv(self);
+ if (p->have_powerd_params)
+ {
+ const int lo = p->powerd_min;
+ const int hi = p->powerd_max;
+ brightness = (int)(lo + (percentage*(hi-lo)));
+ }
+ else
+ {
+ brightness = 0;
+ }
+
+ return brightness;
+}
+
+/**
+ * DBus Chatter: com.canonical.powerd
+ *
+ * This is used to get default value, and upper and lower bounds,
+ * of the brightness setting
+ */
+
+static void set_brightness_global(IndicatorPowerBrightness*, int);
+
+static void
+on_powerd_brightness_params_ready(GObject * source,
+ GAsyncResult * res,
+ gpointer gself)
+{
+ GError * error;
+ GVariant * v;
+
+ error = NULL;
+ v = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), res, &error);
+ if (v == NULL)
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning("Unable to get system bus: %s", error->message);
+
+ g_error_free(error);
+ }
+ else
+ {
+ IndicatorPowerBrightness * self = INDICATOR_POWER_BRIGHTNESS(gself);
+ priv_t * p = get_priv(self);
+ const gboolean old_ab_supported = p->powerd_ab_supported;
+
+ p->have_powerd_params = TRUE;
+ g_variant_get(v, "((iiiib))", &p->powerd_dim,
+ &p->powerd_min,
+ &p->powerd_max,
+ &p->powerd_default_value,
+ &p->powerd_ab_supported);
+ g_debug("powerd brightness settings: dim=%d, min=%d, max=%d, default=%d, ab_supported=%d",
+ p->powerd_dim,
+ p->powerd_min,
+ p->powerd_max,
+ p->powerd_default_value,
+ (int)p->powerd_ab_supported);
+
+ if (old_ab_supported != p->powerd_ab_supported)
+ g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_AUTO_SUPPORTED]);
+
+ if (p->settings != NULL)
+ {
+ if (g_settings_get_boolean(p->settings, KEY_NEED_DEFAULT))
+ {
+ /* user's first session, so init the schema's default
+ brightness from powerd's hardware-specific params */
+ g_debug("%s is true, so initializing brightness to powerd default '%d'", KEY_NEED_DEFAULT, p->powerd_default_value);
+ set_brightness_global(self, p->powerd_default_value);
+ g_settings_set_boolean(p->settings, KEY_NEED_DEFAULT, FALSE);
+ }
+ else
+ {
+ /* not the first time, so restore the previous session's brightness */
+ set_brightness_global(self, g_settings_get_int(p->settings, KEY_BRIGHTNESS));
+ }
+ }
+
+ /* cleanup */
+ g_variant_unref(v);
+ }
+}
+
+static void
+call_powerd_get_brightness_params(IndicatorPowerBrightness * self)
+{
+ priv_t * p = get_priv(self);
+
+ g_dbus_connection_call(p->system_bus,
+ "com.canonical.powerd",
+ "/com/canonical/powerd",
+ "com.canonical.powerd",
+ "getBrightnessParams",
+ NULL,
+ G_VARIANT_TYPE("((iiiib))"),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, /* default timeout */
+ p->cancellable,
+ on_powerd_brightness_params_ready,
+ self);
+}
+
+static void
+on_powerd_appeared(GDBusConnection * connection,
+ const gchar * bus_name G_GNUC_UNUSED,
+ const gchar * name_owner G_GNUC_UNUSED,
+ gpointer gself)
+{
+ IndicatorPowerBrightness * self = INDICATOR_POWER_BRIGHTNESS(gself);
+ priv_t * p = get_priv(self);
+
+ /* keep a handle to the system bus */
+ g_clear_object(&p->system_bus);
+ p->system_bus = g_object_ref(connection);
+
+ /* update our cache of powerd's brightness params */
+ call_powerd_get_brightness_params(self);
+}
+
+static void
+on_powerd_vanished(GDBusConnection * connection G_GNUC_UNUSED,
+ const gchar * bus_name G_GNUC_UNUSED,
+ gpointer gself)
+{
+ priv_t * p = get_priv(INDICATOR_POWER_BRIGHTNESS(gself));
+
+ p->have_powerd_params = FALSE;
+}
+
+/**
+ * DBus Chatter: com.canonical.Unity.Screen
+ *
+ * Used to set the backlight brightness via setUserBrightness
+ */
+
+/* setUserBrightness doesn't return anything,
+ so this function is just to check for bus error messages */
+static void
+on_set_uscreen_user_brightness_result(GObject * system_bus,
+ GAsyncResult * res,
+ gpointer gself G_GNUC_UNUSED)
+{
+ GError * error;
+ GVariant * v;
+
+ error = NULL;
+ v = g_dbus_connection_call_finish(G_DBUS_CONNECTION(system_bus), res, &error);
+ if (error != NULL)
+ {
+ if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning("Unable to call uscreen.setBrightness: %s", error->message);
+
+ g_error_free(error);
+ }
+
+ g_clear_pointer(&v, g_variant_unref);
+}
+
+static void
+set_uscreen_user_brightness(IndicatorPowerBrightness * self,
+ int value)
+{
+ priv_t * p = get_priv(self);
+
+ g_dbus_connection_call(p->system_bus,
+ "com.canonical.Unity.Screen",
+ "/com/canonical/Unity/Screen",
+ "com.canonical.Unity.Screen",
+ "setUserBrightness",
+ g_variant_new("(i)", value),
+ NULL, /* no return args */
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, /* default timeout */
+ p->cancellable,
+ on_set_uscreen_user_brightness_result,
+ self);
+}
+
+/***
+****
+***/
+
+static void
+set_brightness_local(IndicatorPowerBrightness * self, int brightness)
+{
+ priv_t * p = get_priv(self);
+ p->percentage = brightness_to_percentage(self, brightness);
+ g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_PERCENTAGE]);
+}
+
+static void
+on_brightness_changed_in_schema(GSettings * settings,
+ gchar * key,
+ gpointer gself)
+{
+ set_brightness_local(INDICATOR_POWER_BRIGHTNESS(gself),
+ g_settings_get_int(settings, key));
+}
+
+static void
+set_brightness_global(IndicatorPowerBrightness * self, int brightness)
+{
+ priv_t * p = get_priv(self);
+
+ set_uscreen_user_brightness(self, brightness);
+
+ if (p->settings != NULL)
+ g_settings_set_int(p->settings, KEY_BRIGHTNESS, brightness);
+ else
+ set_brightness_local(self, brightness);
+}
+
+static void
+on_auto_changed_in_schema(IndicatorPowerBrightness * self)
+{
+ g_object_notify_by_pspec(G_OBJECT(self), properties[PROP_AUTO]);
+}
+
+
+/***
+**** Instantiation
+***/
+
+static void
+indicator_power_brightness_init(IndicatorPowerBrightness * self)
+{
+ priv_t * p;
+ GSettingsSchema * schema;
+
+ p = get_priv(self);
+ p->cancellable = g_cancellable_new();
+
+ schema = g_settings_schema_source_lookup(g_settings_schema_source_get_default(),
+ SCHEMA_NAME,
+ TRUE);
+
+ /* "brightness" is only spec'ed for the phone profile,
+ so fail gracefully & silently if we don't have the
+ schema for it. */
+ if (schema != NULL)
+ {
+ if (g_settings_schema_has_key(schema, KEY_BRIGHTNESS))
+ {
+ p->settings = g_settings_new(SCHEMA_NAME);
+ g_signal_connect(p->settings, "changed::" KEY_BRIGHTNESS,
+ G_CALLBACK(on_brightness_changed_in_schema), self);
+ g_signal_connect_swapped(p->settings, "changed::" KEY_AUTO,
+ G_CALLBACK(on_auto_changed_in_schema), self);
+ }
+ g_settings_schema_unref(schema);
+ }
+
+ p->powerd_name_tag = g_bus_watch_name(G_BUS_TYPE_SYSTEM,
+ "com.canonical.powerd",
+ G_BUS_NAME_WATCHER_FLAGS_NONE,
+ on_powerd_appeared,
+ on_powerd_vanished,
+ self,
+ NULL);
+}
+
+static void
+indicator_power_brightness_class_init(IndicatorPowerBrightnessClass * 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;
+
+ properties[PROP_0] = NULL;
+
+ properties[PROP_PERCENTAGE] = g_param_spec_double(
+ "percentage",
+ "Percentage",
+ "Brightness percentage",
+ 0.0, /* minimum */
+ 1.0, /* maximum */
+ 0.8,
+ G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_AUTO] = g_param_spec_boolean(
+ "auto-brightness",
+ "Auto-Brightness",
+ "Automatically adjust brightness level",
+ FALSE,
+ G_PARAM_READWRITE|G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_AUTO_SUPPORTED] = g_param_spec_boolean(
+ "auto-brightness-supported",
+ "Auto-Brightness Supported",
+ "True if the device can automatically adjust brightness",
+ FALSE,
+ G_PARAM_READABLE|G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(object_class, LAST_PROP, properties);
+}
+
+/***
+**** Public API
+***/
+
+IndicatorPowerBrightness *
+indicator_power_brightness_new(void)
+{
+ gpointer o = g_object_new(INDICATOR_TYPE_POWER_BRIGHTNESS, NULL);
+
+ return INDICATOR_POWER_BRIGHTNESS(o);
+}
+
+void
+indicator_power_brightness_set_percentage(IndicatorPowerBrightness * self,
+ double percentage)
+{
+ g_return_if_fail(INDICATOR_IS_POWER_BRIGHTNESS(self));
+
+ set_brightness_global(self, percentage_to_brightness(self, percentage));
+}
+
+double
+indicator_power_brightness_get_percentage(IndicatorPowerBrightness * self)
+{
+ g_return_val_if_fail(INDICATOR_IS_POWER_BRIGHTNESS(self), 0.0);
+
+ return get_priv(self)->percentage;
+}
diff --git a/src/brightness.h b/src/brightness.h
new file mode 100644
index 0000000..d2fcc61
--- /dev/null
+++ b/src/brightness.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2014 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 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/>.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ */
+
+#ifndef INDICATOR_POWER_BRIGHTNESS__H
+#define INDICATOR_POWER_BRIGHTNESS__H
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/* standard GObject macros */
+#define INDICATOR_POWER_BRIGHTNESS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_POWER_BRIGHTNESS, IndicatorPowerBrightness))
+#define INDICATOR_TYPE_POWER_BRIGHTNESS (indicator_power_brightness_get_type())
+#define INDICATOR_IS_POWER_BRIGHTNESS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_POWER_BRIGHTNESS))
+
+typedef struct _IndicatorPowerBrightness IndicatorPowerBrightness;
+typedef struct _IndicatorPowerBrightnessClass IndicatorPowerBrightnessClass;
+
+/* property keys */
+#define INDICATOR_POWER_BRIGHTNESS_PROP_PERCENTAGE "percentage"
+
+/**
+ * The Indicator Power Brightness.
+ */
+struct _IndicatorPowerBrightness
+{
+ /*< private >*/
+ GObject parent;
+};
+
+struct _IndicatorPowerBrightnessClass
+{
+ GObjectClass parent_class;
+};
+
+/***
+****
+***/
+
+GType indicator_power_brightness_get_type(void);
+
+IndicatorPowerBrightness * indicator_power_brightness_new(void);
+
+void indicator_power_brightness_set_percentage(IndicatorPowerBrightness * self, double percentage);
+
+double indicator_power_brightness_get_percentage(IndicatorPowerBrightness * self);
+
+G_END_DECLS
+
+#endif /* INDICATOR_POWER_BRIGHTNESS__H */
diff --git a/src/dbus-shared.h b/src/dbus-shared.h
new file mode 100644
index 0000000..bf54034
--- /dev/null
+++ b/src/dbus-shared.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 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 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/>.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ * Ted Gould <ted@canonical.com>
+ */
+
+#ifndef DBUS_SHARED_H
+#define DBUS_SHARED_H
+
+#define BUS_NAME "com.canonical.indicator.power"
+#define BUS_PATH "/com/canonical/indicator/power"
+
+#endif /* DBUS_SHARED_H */
+
diff --git a/src/device-provider-upower.c b/src/device-provider-upower.c
index 7c12beb..63f78ad 100644
--- a/src/device-provider-upower.c
+++ b/src/device-provider-upower.c
@@ -17,36 +17,45 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "config.h"
-
-#include "dbus-upower.h"
#include "device.h"
#include "device-provider.h"
#include "device-provider-upower.h"
#define BUS_NAME "org.freedesktop.UPower"
-#define BUS_PATH "/org/freedesktop/UPower"
+
+#define MGR_IFACE "org.freedesktop.UPower"
+#define MGR_PATH "/org/freedesktop/UPower"
+
+#define DISPLAY_DEVICE_PATH "/org/freedesktop/UPower/devices/DisplayDevice"
/***
**** private struct
***/
-struct _IndicatorPowerDeviceProviderUPowerPriv
+typedef struct
{
GDBusConnection * bus;
-
- DbusUPower * upower_proxy;
- GHashTable * devices; /* dbus object path --> IndicatorPowerDevice */
GCancellable * cancellable;
+ /* dbus object path --> IndicatorPowerDevice */
+ GHashTable * devices;
+
/* a hashset of paths whose devices need to be refreshed */
GHashTable * queued_paths;
/* when this timer fires, the queued_paths will be refreshed */
guint queued_paths_timer;
-};
-typedef IndicatorPowerDeviceProviderUPowerPriv priv_t;
+ GSList* subscriptions;
+
+ guint name_tag;
+}
+IndicatorPowerDeviceProviderUPowerPrivate;
+
+typedef IndicatorPowerDeviceProviderUPowerPrivate priv_t;
+
+#define get_priv(o) ((priv_t*)indicator_power_device_provider_upower_get_instance_private(o))
+
/***
**** GObject boilerplate
@@ -59,8 +68,9 @@ G_DEFINE_TYPE_WITH_CODE (
IndicatorPowerDeviceProviderUPower,
indicator_power_device_provider_upower,
G_TYPE_OBJECT,
+ G_ADD_PRIVATE(IndicatorPowerDeviceProviderUPower)
G_IMPLEMENT_INTERFACE (INDICATOR_TYPE_POWER_DEVICE_PROVIDER,
- indicator_power_device_provider_interface_init));
+ indicator_power_device_provider_interface_init))
/***
**** UPOWER DBUS
@@ -79,11 +89,11 @@ emit_devices_changed (IndicatorPowerDeviceProviderUPower * self)
}
static void
-on_device_properties_ready (GObject * o, GAsyncResult * res, gpointer gdata)
+on_get_all_response (GObject * o, GAsyncResult * res, gpointer gdata)
{
+ struct device_get_all_data * data = gdata;
GError * error;
GVariant * response;
- struct device_get_all_data * data = gdata;
error = NULL;
response = g_dbus_connection_call_finish (G_DBUS_CONNECTION(o), res, &error);
@@ -102,9 +112,9 @@ on_device_properties_ready (GObject * o, GAsyncResult * res, gpointer gdata)
gdouble percentage = 0;
gint64 time_to_empty = 0;
gint64 time_to_full = 0;
- time_t time;
+ gint64 time;
IndicatorPowerDevice * device;
- IndicatorPowerDeviceProviderUPowerPriv * p = data->self->priv;
+ priv_t * p = get_priv(data->self);
GVariant * dict = g_variant_get_child_value (response, 0);
g_variant_lookup (dict, "Type", "u", &kind);
@@ -120,7 +130,7 @@ on_device_properties_ready (GObject * o, GAsyncResult * res, gpointer gdata)
INDICATOR_POWER_DEVICE_STATE, (gint)state,
INDICATOR_POWER_DEVICE_OBJECT_PATH, data->path,
INDICATOR_POWER_DEVICE_PERCENTAGE, percentage,
- INDICATOR_POWER_DEVICE_TIME, (guint64)time,
+ INDICATOR_POWER_DEVICE_TIME, time,
NULL);
}
else
@@ -129,7 +139,7 @@ on_device_properties_ready (GObject * o, GAsyncResult * res, gpointer gdata)
kind,
percentage,
state,
- time);
+ (time_t)time);
g_hash_table_insert (p->devices,
g_strdup (data->path),
@@ -151,55 +161,55 @@ static void
update_device_from_object_path (IndicatorPowerDeviceProviderUPower * self,
const char * path)
{
- priv_t * p = self->priv;
+ priv_t * p = get_priv(self);
struct device_get_all_data * data;
+ /* Symbolic composite item. Nice idea! But its composite rules
+ differ from Design's so (for now) don't use it.
+ https://wiki.ubuntu.com/Power#Handling_multiple_batteries */
+ if (!g_strcmp0(path, DISPLAY_DEVICE_PATH))
+ return;
+
data = g_slice_new (struct device_get_all_data);
data->path = g_strdup (path);
data->self = self;
- g_dbus_connection_call (p->bus,
- BUS_NAME,
- path,
- "org.freedesktop.DBus.Properties",
- "GetAll",
- g_variant_new ("(s)", "org.freedesktop.UPower.Device"),
- G_VARIANT_TYPE("(a{sv})"),
- G_DBUS_CALL_FLAGS_NO_AUTO_START,
- -1, /* default timeout */
- p->cancellable,
- on_device_properties_ready,
- data);
+ g_dbus_connection_call(p->bus,
+ BUS_NAME,
+ path,
+ "org.freedesktop.DBus.Properties",
+ "GetAll",
+ g_variant_new ("(s)", "org.freedesktop.UPower.Device"),
+ G_VARIANT_TYPE("(a{sv})"),
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ -1, /* default timeout */
+ p->cancellable,
+ on_get_all_response,
+ data);
}
/*
- * UPower doesn't seem to be sending PropertyChanged signals.
- *
- * Instead, it's got a DIY mechanism for notification: a DeviceChanged signal
- * that doesn't tell us which property changed, so to refresh we need to
- * rebuild all the properties with a GetAll() call.
+ * UPower 0.99 added proper PropertyChanged signals, but before that
+ * it MGR_IFACE emitted a DeviceChanged signal which didn't tell which
+ * property changed, so all properties had to get refreshed w/GetAll().
*
- * To make things worse, these DeviceChanged signals come fast and furious
- * in common situations like disconnecting a power cable.
- *
- * This code tries to reduce bus traffic by adding a timer to wait a small bit
- * before rebuilding our proxy's properties. This helps to fold multiple
- * DeviceChanged events into a single rebuild.
+ * Changes often come in bursts, so this timer tries to fold them together
+ * by waiting a small bit before making calling GetAll().
*/
-/* rebuild all the proxies listed in our queued_paths hashset */
+/* rebuild all the devices listed in our queued_paths hashset */
static gboolean
-on_queued_paths_timer (gpointer gself)
+on_queued_paths_timer(gpointer gself)
{
- gpointer path;
- GHashTableIter iter;
IndicatorPowerDeviceProviderUPower * self;
priv_t * p;
+ GHashTableIter iter;
+ gpointer path;
self = INDICATOR_POWER_DEVICE_PROVIDER_UPOWER (gself);
- p = self->priv;
+ p = get_priv(self);
- /* create new proxies for all the queued paths */
+ /* create new devices for all the queued paths */
g_hash_table_iter_init (&iter, p->queued_paths);
while (g_hash_table_iter_next (&iter, &path, NULL))
update_device_from_object_path (self, path);
@@ -215,7 +225,7 @@ static void
refresh_device_soon (IndicatorPowerDeviceProviderUPower * self,
const char * object_path)
{
- priv_t * p = self->priv;
+ priv_t * p = get_priv(self);
g_hash_table_add (p->queued_paths, g_strdup (object_path));
@@ -228,155 +238,261 @@ refresh_device_soon (IndicatorPowerDeviceProviderUPower * self,
***/
static void
-on_upower_device_enumerations_ready (GObject * proxy,
- GAsyncResult * res,
- gpointer gself)
+on_enumerate_devices_response(GObject * bus,
+ GAsyncResult * res,
+ gpointer gself)
{
- GError * err;
- char ** object_paths;
-
- err = NULL;
- dbus_upower_call_enumerate_devices_finish (DBUS_UPOWER(proxy),
- &object_paths,
- res,
- &err);
+ GError* error;
+ GVariant* v;
- if (err != NULL)
+ error = NULL;
+ v = g_dbus_connection_call_finish(G_DBUS_CONNECTION(bus), res, &error);
+ if (v == NULL)
{
- g_warning ("Unable to get UPower devices: %s", err->message);
- g_error_free (err);
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("Unable to enumerate UPower devices: %s", error->message);
+ g_error_free (error);
}
- else
+ else if (g_variant_is_of_type(v, G_VARIANT_TYPE("(ao)")))
{
- guint i;
+ GVariant * ao;
+ GVariantIter iter;
+ const gchar * path;
- for (i=0; object_paths && object_paths[i]; i++)
- refresh_device_soon (gself, object_paths[i]);
+ ao = g_variant_get_child_value(v, 0);
+ g_variant_iter_init(&iter, ao);
+ path = NULL;
+ while(g_variant_iter_loop(&iter, "o", &path))
+ refresh_device_soon (gself, path);
- g_strfreev (object_paths);
+ g_variant_unref(ao);
}
-}
-static void
-on_upower_device_changed (DbusUPower * unused G_GNUC_UNUSED,
- const char * object_path,
- gpointer gself)
-{
- refresh_device_soon (gself, object_path);
+ g_clear_pointer(&v, g_variant_unref);
}
static void
-on_upower_device_added (DbusUPower * unused G_GNUC_UNUSED,
- const char * object_path,
- gpointer gself)
+on_device_properties_changed(GDBusConnection * connection G_GNUC_UNUSED,
+ const gchar * sender_name G_GNUC_UNUSED,
+ const gchar * object_path,
+ const gchar * interface_name G_GNUC_UNUSED,
+ const gchar * signal_name G_GNUC_UNUSED,
+ GVariant * parameters,
+ gpointer gself)
{
- refresh_device_soon (gself, object_path);
-}
+ IndicatorPowerDeviceProviderUPower* self;
+ priv_t* p;
+ IndicatorPowerDevice* device;
-static void
-on_upower_device_removed (DbusUPower * unused G_GNUC_UNUSED,
- const char * object_path,
- gpointer gself)
-{
- IndicatorPowerDeviceProviderUPower * self;
+ self = INDICATOR_POWER_DEVICE_PROVIDER_UPOWER(gself);
+ p = get_priv(self);
- self = INDICATOR_POWER_DEVICE_PROVIDER_UPOWER (gself);
- g_hash_table_remove (self->priv->devices, object_path);
- g_hash_table_remove (self->priv->queued_paths, object_path);
+ device = g_hash_table_lookup(p->devices, object_path);
+ if (device == NULL) /* unlikely, but let's handle it */
+ {
+ refresh_device_soon (self, object_path);
+ }
+ else if ((parameters != NULL) && g_variant_n_children(parameters)>=2)
+ {
+ gboolean changed = FALSE;
+ GVariant* dict;
+ GVariantIter iter;
+ gchar* key;
+ GVariant* value;
+
+ dict = g_variant_get_child_value(parameters, 1);
+ g_variant_iter_init(&iter, dict);
+ while (g_variant_iter_next(&iter, "{sv}", &key, &value))
+ {
+ if (!g_strcmp0(key, "TimeToFull") || !g_strcmp0(key, "TimeToEmpty"))
+ {
+ const gint64 i = g_variant_get_int64(value);
+ if (i != 0)
+ {
+ g_object_set(device,
+ INDICATOR_POWER_DEVICE_TIME, (guint64)i,
+ NULL);
+ changed = TRUE;
+ }
+ }
+ else if (!g_strcmp0(key, "Percentage"))
+ {
+ const gdouble d = g_variant_get_double(value);
+ g_object_set(device,
+ INDICATOR_POWER_DEVICE_PERCENTAGE, d,
+ NULL);
+ changed = TRUE;
+ }
+ else if (!g_strcmp0(key, "Type"))
+ {
+ const guint32 u = g_variant_get_uint32(value);
+ g_object_set(device,
+ INDICATOR_POWER_DEVICE_KIND, (gint)u,
+ NULL);
+ changed = TRUE;
+ }
+ else if (!g_strcmp0(key, "State"))
+ {
+ const guint32 u = g_variant_get_uint32(value);
+ g_object_set(device,
+ INDICATOR_POWER_DEVICE_STATE, (gint)u,
+ NULL);
+ changed = TRUE;
+ }
+ }
+ g_variant_unref(dict);
- emit_devices_changed (self);
+ if (changed)
+ emit_devices_changed(self);
+ }
}
-static void
-on_upower_resuming (DbusUPower * unused G_GNUC_UNUSED,
- gpointer gself)
+static const gchar*
+get_path_from_nth_child(GVariant* parameters, gsize i)
{
- IndicatorPowerDeviceProviderUPower * self;
- GHashTableIter iter;
- gpointer object_path;
+ const gchar* path = NULL;
- self = INDICATOR_POWER_DEVICE_PROVIDER_UPOWER (gself);
+ if ((parameters != NULL) && g_variant_n_children(parameters)>i)
+ {
+ GVariant* v = g_variant_get_child_value(parameters, i);
+ if (g_variant_is_of_type(v, G_VARIANT_TYPE_STRING) || /* UPower < 0.99 */
+ g_variant_is_of_type(v, G_VARIANT_TYPE_OBJECT_PATH)) /* and >= 0.99 */
+ {
+ path = g_variant_get_string(v, NULL);
+ }
+ g_variant_unref(v);
+ }
- g_debug ("Resumed from hibernate/sleep; queueing all devices for a refresh");
- g_hash_table_iter_init (&iter, self->priv->devices);
- while (g_hash_table_iter_next (&iter, &object_path, NULL))
- refresh_device_soon (self, object_path);
+ return path;
}
static void
-on_upower_proxy_ready (GObject * source G_GNUC_UNUSED,
- GAsyncResult * res,
- gpointer gself)
+on_upower_signal(GDBusConnection * connection G_GNUC_UNUSED,
+ const gchar * sender_name G_GNUC_UNUSED,
+ const gchar * object_path G_GNUC_UNUSED,
+ const gchar * interface_name G_GNUC_UNUSED,
+ const gchar * signal_name,
+ GVariant * parameters,
+ gpointer gself)
{
- GError * err;
- DbusUPower * proxy;
+ IndicatorPowerDeviceProviderUPower * self;
+ priv_t * p;
+
+ self = INDICATOR_POWER_DEVICE_PROVIDER_UPOWER(gself);
+ p = get_priv(self);
- err = NULL;
- proxy = dbus_upower_proxy_new_finish (res, &err);
- if (err != NULL)
+ if (!g_strcmp0(signal_name, "DeviceAdded"))
{
- g_warning ("Unable to get UPower proxy: %s", err->message);
- g_error_free (err);
+ refresh_device_soon (self, get_path_from_nth_child(parameters, 0));
}
- else
+ else if (!g_strcmp0(signal_name, "DeviceRemoved"))
{
- IndicatorPowerDeviceProviderUPower * self;
- priv_t * p;
-
- self = INDICATOR_POWER_DEVICE_PROVIDER_UPOWER (gself);
- p = self->priv;
-
- p->upower_proxy = proxy;
- g_signal_connect (proxy, "resuming",
- G_CALLBACK (on_upower_resuming), self);
- g_signal_connect (proxy, "device-changed",
- G_CALLBACK (on_upower_device_changed), self);
- g_signal_connect (proxy, "device-added",
- G_CALLBACK (on_upower_device_added), self);
- g_signal_connect (proxy, "device-removed",
- G_CALLBACK (on_upower_device_removed), self);
-
- dbus_upower_call_enumerate_devices (p->upower_proxy,
- p->cancellable,
- on_upower_device_enumerations_ready,
- self);
+ const char* device_path = get_path_from_nth_child(parameters, 0);
+ g_hash_table_remove(p->devices, device_path);
+ g_hash_table_remove(p->queued_paths, device_path);
+ emit_devices_changed(self);
+ }
+ else if (!g_strcmp0(signal_name, "DeviceChanged")) /* UPower < 0.99 */
+ {
+ refresh_device_soon (self, get_path_from_nth_child(parameters, 0));
+ }
+ else if (!g_strcmp0(signal_name, "Resuming")) /* UPower < 0.99 */
+ {
+ GHashTableIter iter;
+ gpointer device_path = NULL;
+ g_debug("Resumed from hibernate/sleep; queueing all devices for a refresh");
+ g_hash_table_iter_init (&iter, p->devices);
+ while (g_hash_table_iter_next (&iter, &device_path, NULL))
+ refresh_device_soon (self, device_path);
}
}
+/* start listening for UPower events on the bus */
static void
-on_bus_ready (GObject * source_object G_GNUC_UNUSED,
- GAsyncResult * res,
- gpointer gself)
+on_bus_name_appeared(GDBusConnection * bus,
+ const gchar * name G_GNUC_UNUSED,
+ const gchar * name_owner,
+ gpointer gself)
{
- GError * error;
- GDBusConnection * tmp;
+ IndicatorPowerDeviceProviderUPower * self;
+ priv_t * p;
+ guint tag;
+
+ self = INDICATOR_POWER_DEVICE_PROVIDER_UPOWER(gself);
+ p = get_priv(self);
+ p->bus = G_DBUS_CONNECTION(g_object_ref(bus));
+
+ /* listen for signals from the boss */
+ tag = g_dbus_connection_signal_subscribe(p->bus,
+ name_owner,
+ MGR_IFACE,
+ NULL /*signal_name*/,
+ MGR_PATH,
+ NULL /*arg0*/,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ on_upower_signal,
+ self,
+ NULL);
+ p->subscriptions = g_slist_prepend(p->subscriptions, GUINT_TO_POINTER(tag));
+
+ /* listen for change events from the devices */
+ tag = g_dbus_connection_signal_subscribe(p->bus,
+ name_owner,
+ "org.freedesktop.DBus.Properties",
+ "PropertiesChanged",
+ NULL /*object_path*/,
+ "org.freedesktop.UPower.Device", /*arg0*/
+ G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE,
+ on_device_properties_changed,
+ self,
+ NULL);
+ p->subscriptions = g_slist_prepend(p->subscriptions, GUINT_TO_POINTER(tag));
+
+ /* rebuild our devices list */
+ g_dbus_connection_call(p->bus,
+ BUS_NAME,
+ MGR_PATH,
+ MGR_IFACE,
+ "EnumerateDevices",
+ NULL,
+ G_VARIANT_TYPE("(ao)"),
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ -1, /* default timeout */
+ p->cancellable,
+ on_enumerate_devices_response,
+ self);
+}
- error = NULL;
- tmp = g_bus_get_finish (res, &error);
- if (error != NULL)
+static void
+on_bus_name_vanished(GDBusConnection * connection G_GNUC_UNUSED,
+ const gchar * name G_GNUC_UNUSED,
+ gpointer gself)
+{
+ IndicatorPowerDeviceProviderUPower * self;
+ priv_t * p;
+ GSList * l;
+
+ self = INDICATOR_POWER_DEVICE_PROVIDER_UPOWER(gself);
+ p = get_priv(self);
+
+ /* clear the devices */
+ g_hash_table_remove_all(p->devices);
+ g_hash_table_remove_all(p->queued_paths);
+ if (p->queued_paths_timer != 0)
{
- if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
- g_warning ("Error acquiring bus: %s", error->message);
- g_error_free (error);
+ g_source_remove(p->queued_paths_timer);
+ p->queued_paths_timer = 0;
}
- else
- {
- IndicatorPowerDeviceProviderUPower * self;
- priv_t * p;
-
- self = INDICATOR_POWER_DEVICE_PROVIDER_UPOWER (gself);
- p = self->priv;
+ emit_devices_changed (self);
- p->bus = tmp;
+ /* clear the bus subscriptions */
+ for (l=p->subscriptions; l!=NULL; l=l->next)
+ g_dbus_connection_signal_unsubscribe(p->bus, GPOINTER_TO_UINT(l->data));
+ g_slist_free(p->subscriptions);
+ p->subscriptions = NULL;
- dbus_upower_proxy_new (p->bus,
- G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES,
- BUS_NAME,
- BUS_PATH,
- p->cancellable,
- on_upower_proxy_ready,
- self);
- }
+ /* clear the bus */
+ g_clear_object(&p->bus);
}
/***
@@ -384,14 +500,16 @@ on_bus_ready (GObject * source_object G_GNUC_UNUSED,
***/
static GList *
-my_get_devices (IndicatorPowerDeviceProvider * provider)
+my_get_devices(IndicatorPowerDeviceProvider * provider)
{
- GList * devices;
IndicatorPowerDeviceProviderUPower * self;
+ priv_t * p;
+ GList * devices;
self = INDICATOR_POWER_DEVICE_PROVIDER_UPOWER(provider);
+ p = get_priv(self);
- devices = g_hash_table_get_values (self->priv->devices);
+ devices = g_hash_table_get_values (p->devices);
g_list_foreach (devices, (GFunc)g_object_ref, NULL);
return devices;
}
@@ -407,7 +525,7 @@ my_dispose (GObject * o)
priv_t * p;
self = INDICATOR_POWER_DEVICE_PROVIDER_UPOWER(o);
- p = self->priv;
+ p = get_priv(self);
if (p->cancellable != NULL)
{
@@ -423,18 +541,15 @@ my_dispose (GObject * o)
p->queued_paths_timer = 0;
}
- if (p->upower_proxy != NULL)
+ if (p->name_tag != 0)
{
- g_signal_handlers_disconnect_by_data (p->upower_proxy, self);
+ g_bus_unwatch_name(p->name_tag);
+ on_bus_name_vanished(NULL, NULL, self);
- g_clear_object (&p->upower_proxy);
+ p->name_tag = 0;
}
- g_hash_table_remove_all (p->devices);
-
- g_clear_object (&p->bus);
-
- G_OBJECT_CLASS (indicator_power_device_provider_upower_parent_class)->dispose (o);
+ G_OBJECT_CLASS (indicator_power_device_provider_upower_parent_class)->dispose(o);
}
static void
@@ -444,12 +559,12 @@ my_finalize (GObject * o)
priv_t * p;
self = INDICATOR_POWER_DEVICE_PROVIDER_UPOWER(o);
- p = self->priv;
+ p = get_priv(self);
g_hash_table_destroy (p->devices);
g_hash_table_destroy (p->queued_paths);
- G_OBJECT_CLASS (indicator_power_device_provider_upower_parent_class)->dispose (o);
+ G_OBJECT_CLASS (indicator_power_device_provider_upower_parent_class)->finalize (o);
}
/***
@@ -463,9 +578,6 @@ indicator_power_device_provider_upower_class_init (IndicatorPowerDeviceProviderU
object_class->dispose = my_dispose;
object_class->finalize = my_finalize;
-
- g_type_class_add_private (klass,
- sizeof (IndicatorPowerDeviceProviderUPowerPriv));
}
static void
@@ -477,30 +589,27 @@ indicator_power_device_provider_interface_init (IndicatorPowerDeviceProviderInte
static void
indicator_power_device_provider_upower_init (IndicatorPowerDeviceProviderUPower * self)
{
- IndicatorPowerDeviceProviderUPowerPriv * p;
-
- p = G_TYPE_INSTANCE_GET_PRIVATE (self,
- INDICATOR_TYPE_POWER_DEVICE_PROVIDER_UPOWER,
- IndicatorPowerDeviceProviderUPowerPriv);
-
- self->priv = p;
-
- p->cancellable = g_cancellable_new ();
-
- p->devices = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- g_object_unref);
-
- p->queued_paths = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- NULL);
-
- g_bus_get (G_BUS_TYPE_SYSTEM,
- p->cancellable,
- on_bus_ready,
- self);
+ priv_t * p = get_priv(self);
+
+ p->cancellable = g_cancellable_new();
+
+ p->devices = g_hash_table_new_full(g_str_hash,
+ g_str_equal,
+ g_free,
+ g_object_unref);
+
+ p->queued_paths = g_hash_table_new_full(g_str_hash,
+ g_str_equal,
+ g_free,
+ NULL);
+
+ p->name_tag = g_bus_watch_name(G_BUS_TYPE_SYSTEM,
+ BUS_NAME,
+ G_BUS_NAME_WATCHER_FLAGS_NONE,
+ on_bus_name_appeared,
+ on_bus_name_vanished,
+ self,
+ NULL);
}
/***
@@ -508,7 +617,7 @@ indicator_power_device_provider_upower_init (IndicatorPowerDeviceProviderUPower
***/
IndicatorPowerDeviceProvider *
-indicator_power_device_provider_upower_new (void)
+indicator_power_device_provider_upower_new(void)
{
gpointer o = g_object_new (INDICATOR_TYPE_POWER_DEVICE_PROVIDER_UPOWER, NULL);
diff --git a/src/device-provider-upower.h b/src/device-provider-upower.h
index 7bdd5d5..f385479 100644
--- a/src/device-provider-upower.h
+++ b/src/device-provider-upower.h
@@ -45,8 +45,6 @@ G_BEGIN_DECLS
typedef struct _IndicatorPowerDeviceProviderUPower
IndicatorPowerDeviceProviderUPower;
-typedef struct _IndicatorPowerDeviceProviderUPowerPriv
- IndicatorPowerDeviceProviderUPowerPriv;
typedef struct _IndicatorPowerDeviceProviderUPowerClass
IndicatorPowerDeviceProviderUPowerClass;
@@ -56,8 +54,6 @@ typedef struct _IndicatorPowerDeviceProviderUPowerClass
struct _IndicatorPowerDeviceProviderUPower
{
GObject parent_instance;
-
- IndicatorPowerDeviceProviderUPowerPriv * priv;
};
struct _IndicatorPowerDeviceProviderUPowerClass
@@ -65,6 +61,8 @@ struct _IndicatorPowerDeviceProviderUPowerClass
GObjectClass parent_class;
};
+GType indicator_power_device_provider_upower_get_type (void);
+
IndicatorPowerDeviceProvider * indicator_power_device_provider_upower_new (void);
G_END_DECLS
diff --git a/src/device-provider.c b/src/device-provider.c
index 81a8eec..46fcfad 100644
--- a/src/device-provider.c
+++ b/src/device-provider.c
@@ -29,7 +29,7 @@ static guint signals[SIGNAL_LAST] = { 0 };
G_DEFINE_INTERFACE (IndicatorPowerDeviceProvider,
indicator_power_device_provider,
- 0);
+ 0)
static void
indicator_power_device_provider_default_init (IndicatorPowerDeviceProviderInterface * klass)
diff --git a/src/device.c b/src/device.c
index ed3c399..eff76d1 100644
--- a/src/device.c
+++ b/src/device.c
@@ -44,8 +44,6 @@ struct _IndicatorPowerDevicePrivate
GTimer * inestimable;
};
-#define INDICATOR_POWER_DEVICE_GET_PRIVATE(o) (INDICATOR_POWER_DEVICE(o)->priv)
-
/* Properties */
/* Enum for the properties so that they can be quickly found and looked up. */
enum {
@@ -69,7 +67,7 @@ static void set_property (GObject*, guint prop_id, const GValue*, GParamSpec* );
static void get_property (GObject*, guint prop_id, GValue*, GParamSpec* );
/* LCOV_EXCL_START */
-G_DEFINE_TYPE (IndicatorPowerDevice, indicator_power_device, G_TYPE_OBJECT);
+G_DEFINE_TYPE (IndicatorPowerDevice, indicator_power_device, G_TYPE_OBJECT)
/* LCOV_EXCL_STOP */
static void
@@ -189,7 +187,7 @@ get_property (GObject * o, guint prop_id, GValue * value, GParamSpec * pspec)
break;
case PROP_TIME:
- g_value_set_uint64 (value, priv->time);
+ g_value_set_uint64 (value, (guint64)priv->time);
break;
default:
@@ -207,11 +205,11 @@ set_property (GObject * o, guint prop_id, const GValue * value, GParamSpec * psp
switch (prop_id)
{
case PROP_KIND:
- p->kind = g_value_get_int (value);
+ p->kind = (UpDeviceKind) g_value_get_int (value);
break;
case PROP_STATE:
- p->state = g_value_get_int (value);
+ p->state = (UpDeviceState) g_value_get_int (value);
break;
case PROP_OBJECT_PATH:
@@ -224,7 +222,7 @@ set_property (GObject * o, guint prop_id, const GValue * value, GParamSpec * psp
break;
case PROP_TIME:
- p->time = g_value_get_uint64(value);
+ p->time = (time_t) g_value_get_uint64(value);
break;
default:
@@ -355,13 +353,6 @@ device_kind_to_string (UpDeviceKind kind)
indicator_power_device_get_icon_names:
@device: #IndicatorPowerDevice from which to generate the icon names
- This function's logic differs from GSD's power plugin in some ways:
-
- 1. For discharging batteries, we decide whether or not to use the 'caution'
- icon based on whether or not we have <= 30 minutes remaining, rather than
- looking at the battery's percentage left.
- <https://bugs.launchpad.net/indicator-power/+bug/743823>
-
See also indicator_power_device_get_gicon().
Return value: (array zero-terminated=1) (transfer full):
@@ -416,21 +407,15 @@ indicator_power_device_get_icon_names (const IndicatorPowerDevice * device)
case UP_DEVICE_STATE_CHARGING:
suffix_str = get_device_icon_suffix (percentage);
index_str = get_device_icon_index (percentage);
- g_ptr_array_add (names, g_strdup_printf ("%s-%s-charging-symbolic", kind_str, suffix_str));
+ g_ptr_array_add (names, g_strdup_printf ("%s-%s-charging", kind_str, index_str));
g_ptr_array_add (names, g_strdup_printf ("gpm-%s-%s-charging", kind_str, index_str));
+ g_ptr_array_add (names, g_strdup_printf ("%s-%s-charging-symbolic", kind_str, suffix_str));
g_ptr_array_add (names, g_strdup_printf ("%s-%s-charging", kind_str, suffix_str));
break;
case UP_DEVICE_STATE_PENDING_CHARGE:
case UP_DEVICE_STATE_DISCHARGING:
case UP_DEVICE_STATE_PENDING_DISCHARGE:
- /* Don't show the caution/red icons unless we have <=30 min left.
- <https://bugs.launchpad.net/indicator-power/+bug/743823>
- Themes use the caution color when the percentage is 0% or 20%,
- so if we have >30 min left, use 30% as the icon's percentage floor */
- if (indicator_power_device_get_time (device) > (30*60))
- percentage = MAX(percentage, 30);
-
suffix_str = get_device_icon_suffix (percentage);
index_str = get_device_icon_index (percentage);
g_ptr_array_add (names, g_strdup_printf ("%s-%s", kind_str, index_str));
@@ -595,10 +580,12 @@ get_expanded_time_remaining (const IndicatorPowerDevice * device)
if (p->state == UP_DEVICE_STATE_CHARGING)
{
+ /* TRANSLATORS: H:MM (hours, minutes) to charge the battery. Example: "1:30 to charge" */
str = g_strdup_printf (_("%0d:%02d to charge"), hours, minutes);
}
else // discharging
{
+ /* TRANSLATORS: H:MM (hours, minutes) to discharge the battery. Example: "1:30 left"*/
str = g_strdup_printf (_("%0d:%02d left"), hours, minutes);
}
}
@@ -624,29 +611,45 @@ get_accessible_time_remaining (const IndicatorPowerDevice * device)
if (p->time && ((p->state == UP_DEVICE_STATE_CHARGING) || (p->state == UP_DEVICE_STATE_DISCHARGING)))
{
- int minutes = p->time / 60;
- const int hours = minutes / 60;
+ guint minutes = (guint)p->time / 60u;
+ const guint hours = minutes / 60u;
minutes %= 60;
if (p->state == UP_DEVICE_STATE_CHARGING)
{
if (hours > 0)
- str = g_strdup_printf (_("%d %s %d %s to charge"),
- hours, g_dngettext (NULL, "hour", "hours", hours),
- minutes, g_dngettext (NULL, "minute", "minutes", minutes));
+ {
+ /* TRANSLATORS: "X (hour,hours) Y (minute,minutes) to charge" the battery.
+ Example: "1 hour 10 minutes to charge" */
+ str = g_strdup_printf (_("%d %s %d %s to charge"),
+ hours, g_dngettext (NULL, "hour", "hours", hours),
+ minutes, g_dngettext (NULL, "minute", "minutes", minutes));
+ }
else
- str = g_strdup_printf (_("%d %s to charge"),
- minutes, g_dngettext (NULL, "minute", "minutes", minutes));
+ {
+ /* TRANSLATORS: "Y (minute,minutes) to charge" the battery.
+ Example: "59 minutes to charge" */
+ str = g_strdup_printf (_("%d %s to charge"),
+ minutes, g_dngettext (NULL, "minute", "minutes", minutes));
+ }
}
else // discharging
{
if (hours > 0)
- str = g_strdup_printf (_("%d %s %d %s left"),
- hours, g_dngettext (NULL, "hour", "hours", hours),
- minutes, g_dngettext (NULL, "minute", "minutes", minutes));
- else
- str = g_strdup_printf (_("%d %s left"),
- minutes, g_dngettext (NULL, "minute", "minutes", minutes));
+ {
+ /* TRANSLATORS: "X (hour,hours) Y (minute,minutes) left" until the battery's empty.
+ Example: "1 hour 10 minutes left" */
+ str = g_strdup_printf (_("%d %s %d %s left"),
+ hours, g_dngettext (NULL, "hour", "hours", hours),
+ minutes, g_dngettext (NULL, "minute", "minutes", minutes));
+ }
+ else
+ {
+ /* TRANSLATORS: "Y (minute,minutes) left" until the battery's empty.
+ Example: "59 minutes left" */
+ str = g_strdup_printf (_("%d %s left"),
+ minutes, g_dngettext (NULL, "minute", "minutes", minutes));
+ }
}
}
else
@@ -700,6 +703,7 @@ get_menuitem_text (const IndicatorPowerDevice * device,
if (p->state == UP_DEVICE_STATE_FULLY_CHARGED)
{
+ /* TRANSLATORS: example: "battery (charged)" */
str = g_strdup_printf (_("%s (charged)"), kind_str);
}
else
@@ -715,9 +719,14 @@ get_menuitem_text (const IndicatorPowerDevice * device,
}
if (time_str && *time_str)
- str = g_strdup_printf (_("%s (%s)"), kind_str, time_str);
+ {
+ /* TRANSLATORS: example: "battery (time remaining)" */
+ str = g_strdup_printf (_("%s (%s)"), kind_str, time_str);
+ }
else
- str = g_strdup (kind_str);
+ {
+ str = g_strdup (kind_str);
+ }
g_free (time_str);
}
@@ -783,14 +792,17 @@ indicator_power_device_get_readable_title (const IndicatorPowerDevice * device,
if (want_time && want_percent)
{
+ /* TRANSLATORS: after the icon, a time-remaining string + battery %. Example: "(0:59, 33%)" */
str = g_strdup_printf (_("(%s, %.0lf%%)"), time_str, p->percentage);
}
else if (want_time)
{
+ /* TRANSLATORS: after the icon, a time-remaining string Example: "(0:59)" */
str = g_strdup_printf (_("(%s)"), time_str);
}
else if (want_percent)
{
+ /* TRANSLATORS: after the icon, a battery %. Example: "(33%)" */
str = g_strdup_printf (_("(%.0lf%%)"), p->percentage);
}
else
@@ -861,5 +873,5 @@ indicator_power_device_new_from_variant (GVariant * v)
kind,
percentage,
state,
- time);
+ (time_t)time);
}
diff --git a/src/device.h b/src/device.h
index 3a10f89..d867707 100644
--- a/src/device.h
+++ b/src/device.h
@@ -24,7 +24,7 @@ License along with this library. If not, see
#ifndef __INDICATOR_POWER_DEVICE_H__
#define __INDICATOR_POWER_DEVICE_H__
-#include <glib-object.h>
+#include <gio/gio.h> /* GIcon */
G_BEGIN_DECLS
diff --git a/src/ib-brightness-control.c b/src/ib-brightness-control.c
deleted file mode 100644
index 4fb6bc5..0000000
--- a/src/ib-brightness-control.c
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright 2012 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 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/>.
- *
- * Authors:
- * Renato Araujo Oliveira Filho <renato@canonical.com>
- */
-
-#include <gudev/gudev.h>
-
-#include <errno.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <string.h>
-
-#include "ib-brightness-control.h"
-
-struct _IbBrightnessControl
-{
- gchar *path;
-};
-
-IbBrightnessControl*
-ib_brightness_control_new (void)
-{
- IbBrightnessControl *control;
- GUdevClient *client;
- gchar *path = NULL;
- GList *devices;
-
- // detect device
- client = g_udev_client_new (NULL);
- devices = g_udev_client_query_by_subsystem (client, "backlight");
- if (devices != NULL) {
- GList *device;
- const gchar *device_type;
-
- for (device = devices; device != NULL; device = device->next) {
- device_type = g_udev_device_get_sysfs_attr (device->data, "type");
- if ((g_strcmp0 (device_type, "firmware") == 0) ||
- (g_strcmp0 (device_type, "platform") == 0) ||
- (g_strcmp0 (device_type, "raw") == 0)) {
- path = g_strdup (g_udev_device_get_sysfs_path (device->data));
- g_debug ("found: %s", path);
- break;
- }
- }
-
- g_list_free_full (devices, g_object_unref);
- }
- else {
- g_warning ("Fail to query backlight devices.");
- }
-
- control = g_new0 (IbBrightnessControl, 1);
- control->path = path;
-
- g_object_unref (client);
- return control;
-}
-
-void
-ib_brightness_control_set_value (IbBrightnessControl* self, gint value)
-{
- gint fd;
- gchar *filename;
- gchar *svalue;
- gint length;
- gint err;
-
- if (self->path == NULL)
- return;
-
- filename = g_build_filename (self->path, "brightness", NULL);
- fd = open(filename, O_WRONLY);
- if (fd < 0) {
- g_warning ("Fail to set brightness.");
- g_free (filename);
- return;
- }
-
- svalue = g_strdup_printf ("%i", value);
- length = strlen (svalue);
-
- err = errno;
- errno = 0;
- if (write (fd, svalue, length) != length) {
- g_warning ("Fail to write brightness information: %s", g_strerror(errno));
- }
- errno = err;
-
- close (fd);
- g_free (svalue);
- g_free (filename);
-}
-
-gint
-ib_brightness_control_get_value_from_file (IbBrightnessControl *self, const gchar *file)
-{
- GError *error;
- gchar *svalue;
- gint value;
- gchar *filename;
-
- if (self->path == NULL)
- return 0;
-
- svalue = NULL;
- error = NULL;
- filename = g_build_filename (self->path, file, NULL);
- g_file_get_contents (filename, &svalue, NULL, &error);
- if (error) {
- g_warning ("Fail to get brightness value: %s", error->message);
- value = -1;
- g_error_free (error);
- } else {
- value = atoi (svalue);
- g_free (svalue);
- }
-
- g_free (filename);
-
- return value;
-
-}
-
-gint
-ib_brightness_control_get_value (IbBrightnessControl* self)
-{
- return ib_brightness_control_get_value_from_file (self, "brightness");
-}
-
-gint
-ib_brightness_control_get_max_value (IbBrightnessControl* self)
-{
- return ib_brightness_control_get_value_from_file (self, "max_brightness");
-}
-
-void
-ib_brightness_control_free (IbBrightnessControl *self)
-{
- g_free (self->path);
- g_free (self);
-}
-
diff --git a/src/ib-brightness-control.h b/src/ib-brightness-control.h
deleted file mode 100644
index 87711e4..0000000
--- a/src/ib-brightness-control.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2012 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 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/>.
- *
- * Authors:
- * Renato Araujo Oliveira Filho <renato@canonical.com>
- */
-
-#ifndef __IB_BRIGHTNESS_CONTROL_H__
-#define __IB_BRIGHTNESS_CONTROL_H__
-
-#include <gio/gio.h>
-
-typedef struct _IbBrightnessControl IbBrightnessControl;
-
-IbBrightnessControl* ib_brightness_control_new (void);
-void ib_brightness_control_set_value (IbBrightnessControl* self, gint value);
-gint ib_brightness_control_get_value (IbBrightnessControl* self);
-gint ib_brightness_control_get_max_value (IbBrightnessControl* self);
-void ib_brightness_control_free (IbBrightnessControl *self);
-
-#endif
diff --git a/src/main.c b/src/main.c
index ef615dc..d7953e6 100644
--- a/src/main.c
+++ b/src/main.c
@@ -17,8 +17,6 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "config.h"
-
#include <locale.h>
#include <stdlib.h> /* exit() */
@@ -43,9 +41,9 @@ on_name_lost (gpointer instance G_GNUC_UNUSED, gpointer loop)
int
main (int argc G_GNUC_UNUSED, char ** argv G_GNUC_UNUSED)
{
- GMainLoop * loop;
- IndicatorPowerService * service;
IndicatorPowerDeviceProvider * device_provider;
+ IndicatorPowerService * service;
+ GMainLoop * loop;
/* boilerplate i18n */
setlocale (LC_ALL, "");
@@ -61,8 +59,8 @@ main (int argc G_GNUC_UNUSED, char ** argv G_GNUC_UNUSED)
g_main_loop_run (loop);
/* cleanup */
- g_clear_object (&device_provider);
- g_clear_object (&service);
g_main_loop_unref (loop);
+ g_clear_object (&service);
+ g_clear_object (&device_provider);
return 0;
}
diff --git a/src/notifier.c b/src/notifier.c
new file mode 100644
index 0000000..993e332
--- /dev/null
+++ b/src/notifier.c
@@ -0,0 +1,504 @@
+/*
+ * Copyright 2014 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 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/>.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ */
+
+#include "dbus-battery.h"
+#include "dbus-shared.h"
+#include "notifier.h"
+
+#include <url-dispatcher.h>
+
+#include <libnotify/notify.h>
+
+#include <glib/gi18n.h>
+
+#include <stdint.h> /* UINT32_MAX */
+
+typedef enum
+{
+ POWER_LEVEL_CRITICAL,
+ POWER_LEVEL_VERY_LOW,
+ POWER_LEVEL_LOW,
+ POWER_LEVEL_OK
+}
+PowerLevel;
+
+/**
+*** GObject Properties
+**/
+
+enum
+{
+ PROP_0,
+ PROP_BATTERY,
+ LAST_PROP
+};
+
+#define PROP_BATTERY_NAME "battery"
+
+static GParamSpec * properties[LAST_PROP];
+
+static int instance_count = 0;
+
+static gboolean actions_supported = FALSE;
+
+/**
+***
+**/
+
+typedef struct
+{
+ /* The battery we're currently watching.
+ This may be a physical battery or it may be an aggregated
+ battery from multiple batteries present on the device.
+ See indicator_power_service_choose_primary_device() and
+ bug #880881 */
+ IndicatorPowerDevice * battery;
+ PowerLevel power_level;
+ gboolean discharging;
+
+ NotifyNotification * notify_notification;
+
+ GDBusConnection * bus;
+ DbusBattery * dbus_battery; /* com.canonical.indicator.power.Battery skeleton */
+}
+IndicatorPowerNotifierPrivate;
+
+typedef IndicatorPowerNotifierPrivate priv_t;
+
+G_DEFINE_TYPE_WITH_PRIVATE(IndicatorPowerNotifier,
+ indicator_power_notifier,
+ G_TYPE_OBJECT)
+
+#define get_priv(o) ((priv_t*)indicator_power_notifier_get_instance_private(o))
+
+/***
+****
+***/
+
+static const char *
+power_level_to_dbus_string (const PowerLevel power_level)
+{
+ switch (power_level)
+ {
+ case POWER_LEVEL_LOW: return POWER_LEVEL_STR_LOW;
+ case POWER_LEVEL_VERY_LOW: return POWER_LEVEL_STR_VERY_LOW;
+ case POWER_LEVEL_CRITICAL: return POWER_LEVEL_STR_CRITICAL;
+ default: return POWER_LEVEL_STR_OK;
+ }
+}
+
+static PowerLevel
+get_battery_power_level (IndicatorPowerDevice * battery)
+{
+ static const double percent_critical = 2.0;
+ static const double percent_very_low = 5.0;
+ static const double percent_low = 10.0;
+ gdouble p;
+ PowerLevel ret;
+
+ g_return_val_if_fail(battery != NULL, POWER_LEVEL_OK);
+ g_return_val_if_fail(indicator_power_device_get_kind(battery) == UP_DEVICE_KIND_BATTERY, POWER_LEVEL_OK);
+
+ p = indicator_power_device_get_percentage(battery);
+
+ if (p <= percent_critical)
+ ret = POWER_LEVEL_CRITICAL;
+ else if (p <= percent_very_low)
+ ret = POWER_LEVEL_VERY_LOW;
+ else if (p <= percent_low)
+ ret = POWER_LEVEL_LOW;
+ else
+ ret = POWER_LEVEL_OK;
+
+ return ret;
+}
+
+/***
+**** Notifications
+***/
+
+static void
+on_notify_notification_finalized (gpointer gself, GObject * dead)
+{
+ IndicatorPowerNotifier * const self = INDICATOR_POWER_NOTIFIER(gself);
+ priv_t * const p = get_priv(self);
+ g_return_if_fail ((void*)(p->notify_notification) == (void*)dead);
+ p->notify_notification = NULL;
+ dbus_battery_set_is_warning (p->dbus_battery, FALSE);
+}
+
+static void
+notification_clear (IndicatorPowerNotifier * self)
+{
+ priv_t * const p = get_priv(self);
+ NotifyNotification * nn;
+
+ if ((nn = p->notify_notification))
+ {
+ GError * error = NULL;
+
+ g_object_weak_unref(G_OBJECT(nn), on_notify_notification_finalized, self);
+
+ if (!notify_notification_close(nn, &error))
+ {
+ g_warning("Unable to close notification: %s", error->message);
+ g_error_free(error);
+ }
+
+ p->notify_notification = NULL;
+ dbus_battery_set_is_warning (p->dbus_battery, FALSE);
+ }
+}
+
+static void
+on_battery_settings_clicked(NotifyNotification * nn G_GNUC_UNUSED,
+ char * action G_GNUC_UNUSED,
+ gpointer user_data G_GNUC_UNUSED)
+{
+ url_dispatch_send("settings:///system/battery", NULL, NULL);
+}
+
+static void
+on_dismiss_clicked(NotifyNotification * nn G_GNUC_UNUSED,
+ char * action G_GNUC_UNUSED,
+ gpointer user_data G_GNUC_UNUSED)
+{
+ /* no-op; libnotify warns if we have a NULL action callback */
+}
+
+static void
+notification_show(IndicatorPowerNotifier * self)
+{
+ priv_t * const p = get_priv(self);
+ gdouble pct;
+ const char * title;
+ char * body;
+ GStrv icon_names;
+ const char * icon_name;
+ NotifyNotification * nn;
+ GError * error;
+ const PowerLevel power_level = get_battery_power_level(p->battery);
+
+ notification_clear(self);
+
+ g_return_if_fail(power_level != POWER_LEVEL_OK);
+
+ /* create the notification */
+ title = power_level == POWER_LEVEL_LOW
+ ? _("Battery Low")
+ : _("Battery Critical");
+ pct = indicator_power_device_get_percentage(p->battery);
+ body = g_strdup_printf(_("%.0f%% charge remaining"), pct);
+ icon_names = indicator_power_device_get_icon_names(p->battery);
+ if (icon_names && *icon_names)
+ icon_name = icon_names[0];
+ else
+ icon_name = NULL;
+ nn = notify_notification_new(title, body, icon_name);
+ g_strfreev (icon_names);
+ g_free (body);
+
+ if (actions_supported)
+ {
+ notify_notification_set_hint(nn, "x-canonical-snap-decisions", g_variant_new_string("true"));
+ notify_notification_set_hint(nn, "x-canonical-non-shaped-icon", g_variant_new_string("true"));
+ notify_notification_set_hint(nn, "x-canonical-private-affirmative-tint", g_variant_new_string("true"));
+ notify_notification_set_hint(nn, "x-canonical-snap-decisions-timeout", g_variant_new_int32(INT32_MAX));
+ notify_notification_set_timeout(nn, NOTIFY_EXPIRES_NEVER);
+ notify_notification_add_action(nn, "dismiss", _("OK"), on_dismiss_clicked, NULL, NULL);
+ notify_notification_add_action(nn, "settings", _("Battery settings"), on_battery_settings_clicked, NULL, NULL);
+ }
+
+ /* if we can show it, keep it */
+ error = NULL;
+ if (notify_notification_show(nn, &error))
+ {
+ p->notify_notification = nn;
+ g_signal_connect(nn, "closed", G_CALLBACK(g_object_unref), NULL);
+ g_object_weak_ref(G_OBJECT(nn), on_notify_notification_finalized, self);
+ dbus_battery_set_is_warning (p->dbus_battery, TRUE);
+ }
+ else
+ {
+ g_critical("Unable to show snap decision for '%s': %s", body, error->message);
+ g_error_free(error);
+ g_object_unref(nn);
+ }
+}
+
+/***
+****
+***/
+
+static void
+on_battery_property_changed (IndicatorPowerNotifier * self)
+{
+ priv_t * p;
+ PowerLevel old_power_level;
+ PowerLevel new_power_level;
+ gboolean old_discharging;
+ gboolean new_discharging;
+
+ g_return_if_fail(INDICATOR_IS_POWER_NOTIFIER(self));
+ p = get_priv (self);
+ g_return_if_fail(INDICATOR_IS_POWER_DEVICE(p->battery));
+
+ old_power_level = p->power_level;
+ new_power_level = get_battery_power_level (p->battery);
+
+ old_discharging = p->discharging;
+ new_discharging = indicator_power_device_get_state(p->battery) == UP_DEVICE_STATE_DISCHARGING;
+
+ /* pop up a 'low battery' notification if either:
+ a) it's already discharging, and its PowerLevel worsens, OR
+ b) it's already got a bad PowerLevel and its state becomes 'discharging */
+ if ((new_discharging && (old_power_level > new_power_level)) ||
+ ((new_power_level != POWER_LEVEL_OK) && new_discharging && !old_discharging))
+ {
+ notification_show (self);
+ }
+ else if (!new_discharging || (new_power_level == POWER_LEVEL_OK))
+ {
+ notification_clear (self);
+ }
+
+ dbus_battery_set_power_level (p->dbus_battery, power_level_to_dbus_string (new_power_level));
+ p->power_level = new_power_level;
+ p->discharging = new_discharging;
+}
+
+/***
+**** GObject virtual functions
+***/
+
+static void
+my_get_property (GObject * o,
+ guint property_id,
+ GValue * value,
+ GParamSpec * pspec)
+{
+ IndicatorPowerNotifier * const self = INDICATOR_POWER_NOTIFIER (o);
+ priv_t * const p = get_priv (self);
+
+ switch (property_id)
+ {
+ case PROP_BATTERY:
+ g_value_set_object (value, p->battery);
+ 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)
+{
+ IndicatorPowerNotifier * const self = INDICATOR_POWER_NOTIFIER (o);
+
+ switch (property_id)
+ {
+ case PROP_BATTERY:
+ indicator_power_notifier_set_battery (self, g_value_get_object(value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec);
+ }
+}
+
+static void
+my_dispose (GObject * o)
+{
+ IndicatorPowerNotifier * const self = INDICATOR_POWER_NOTIFIER(o);
+ priv_t * const p = get_priv (self);
+
+ indicator_power_notifier_set_bus (self, NULL);
+ notification_clear (self);
+ indicator_power_notifier_set_battery (self, NULL);
+ g_clear_object (&p->dbus_battery);
+
+ G_OBJECT_CLASS (indicator_power_notifier_parent_class)->dispose (o);
+}
+
+static void
+my_finalize (GObject * o G_GNUC_UNUSED)
+{
+ /* FIXME: This is an awkward place to put this.
+ Ordinarily something like this would go in main(), but we need libnotify
+ to clean itself up before shutting down the bus in the unit tests as well. */
+ if (!--instance_count)
+ notify_uninit();
+}
+
+/***
+**** Instantiation
+***/
+
+static void
+indicator_power_notifier_init (IndicatorPowerNotifier * self)
+{
+ priv_t * const p = get_priv (self);
+
+ /* bind the read-only properties so they'll get pushed to the bus */
+
+ p->dbus_battery = dbus_battery_skeleton_new ();
+
+ p->power_level = POWER_LEVEL_OK;
+
+ if (!instance_count++)
+ {
+ actions_supported = FALSE;
+
+ if (!notify_init("indicator-power-service"))
+ {
+ g_critical("Unable to initialize libnotify! Notifications might not be shown.");
+ }
+ else
+ {
+ GList * caps;
+ GList * l;
+
+ /* see if actions are supported */
+ caps = notify_get_server_caps();
+ for (l=caps; l!=NULL && !actions_supported; l=l->next)
+ if (!g_strcmp0(l->data, "actions"))
+ actions_supported = TRUE;
+ g_list_free_full(caps, g_free);
+ }
+ }
+}
+
+static void
+indicator_power_notifier_class_init (IndicatorPowerNotifierClass * klass)
+{
+ GObjectClass * object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = my_dispose;
+ object_class->finalize = my_finalize;
+ object_class->get_property = my_get_property;
+ object_class->set_property = my_set_property;
+
+ properties[PROP_BATTERY] = g_param_spec_object (
+ PROP_BATTERY_NAME,
+ "Battery",
+ "The current battery",
+ G_TYPE_OBJECT,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, LAST_PROP, properties);
+}
+
+/***
+**** Public API
+***/
+
+IndicatorPowerNotifier *
+indicator_power_notifier_new (void)
+{
+ GObject * o = g_object_new (INDICATOR_TYPE_POWER_NOTIFIER, NULL);
+
+ return INDICATOR_POWER_NOTIFIER (o);
+}
+
+void
+indicator_power_notifier_set_battery (IndicatorPowerNotifier * self,
+ IndicatorPowerDevice * battery)
+{
+ priv_t * p;
+
+ g_return_if_fail(INDICATOR_IS_POWER_NOTIFIER(self));
+ g_return_if_fail((battery == NULL) || INDICATOR_IS_POWER_DEVICE(battery));
+ g_return_if_fail((battery == NULL) || (indicator_power_device_get_kind(battery) == UP_DEVICE_KIND_BATTERY));
+
+ p = get_priv (self);
+
+ if (p->battery == battery)
+ return;
+
+ if (p->battery != NULL)
+ {
+ g_signal_handlers_disconnect_by_data (p->battery, self);
+ g_clear_object (&p->battery);
+ dbus_battery_set_power_level (p->dbus_battery, power_level_to_dbus_string (POWER_LEVEL_OK));
+ notification_clear (self);
+ }
+
+ if (battery != NULL)
+ {
+ p->battery = g_object_ref (battery);
+ g_signal_connect_swapped (p->battery, "notify::"INDICATOR_POWER_DEVICE_PERCENTAGE,
+ G_CALLBACK(on_battery_property_changed), self);
+ g_signal_connect_swapped (p->battery, "notify::"INDICATOR_POWER_DEVICE_STATE,
+ G_CALLBACK(on_battery_property_changed), self);
+ on_battery_property_changed (self);
+ }
+}
+
+void
+indicator_power_notifier_set_bus (IndicatorPowerNotifier * self,
+ GDBusConnection * bus)
+{
+ priv_t * p;
+ GDBusInterfaceSkeleton * skel;
+
+ g_return_if_fail(INDICATOR_IS_POWER_NOTIFIER(self));
+ g_return_if_fail((bus == NULL) || G_IS_DBUS_CONNECTION(bus));
+
+ p = get_priv (self);
+
+ if (p->bus == bus)
+ return;
+
+ skel = G_DBUS_INTERFACE_SKELETON(p->dbus_battery);
+
+ if (p->bus != NULL)
+ {
+ if (skel != NULL)
+ g_dbus_interface_skeleton_unexport (skel);
+
+ g_clear_object (&p->bus);
+ }
+
+ if (bus != NULL)
+ {
+ GError * error;
+
+ p->bus = g_object_ref (bus);
+
+ error = NULL;
+ if (!g_dbus_interface_skeleton_export(skel,
+ bus,
+ BUS_PATH"/Battery",
+ &error))
+ {
+ g_warning ("Unable to export LowBattery properties: %s", error->message);
+ g_error_free (error);
+ }
+ }
+}
+
+const char *
+indicator_power_notifier_get_power_level (IndicatorPowerDevice * battery)
+{
+ return power_level_to_dbus_string (get_battery_power_level (battery));
+}
diff --git a/src/notifier.h b/src/notifier.h
new file mode 100644
index 0000000..18e25d7
--- /dev/null
+++ b/src/notifier.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2014 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 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/>.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ */
+
+#ifndef __INDICATOR_POWER_NOTIFIER_H__
+#define __INDICATOR_POWER_NOTIFIER_H__
+
+#include <gio/gio.h>
+
+#include "device.h"
+
+G_BEGIN_DECLS
+
+/* standard GObject macros */
+#define INDICATOR_POWER_NOTIFIER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_POWER_NOTIFIER, IndicatorPowerNotifier))
+#define INDICATOR_TYPE_POWER_NOTIFIER (indicator_power_notifier_get_type())
+#define INDICATOR_IS_POWER_NOTIFIER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_POWER_NOTIFIER))
+#define INDICATOR_POWER_NOTIFIER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_POWER_NOTIFIER, IndicatorPowerNotifierClass))
+
+typedef struct _IndicatorPowerNotifier IndicatorPowerNotifier;
+typedef struct _IndicatorPowerNotifierClass IndicatorPowerNotifierClass;
+
+/**
+ * The Indicator Power Notifier.
+ */
+struct _IndicatorPowerNotifier
+{
+ /*< private >*/
+ GObject parent;
+};
+
+struct _IndicatorPowerNotifierClass
+{
+ GObjectClass parent_class;
+};
+
+/***
+****
+***/
+
+GType indicator_power_notifier_get_type (void);
+
+IndicatorPowerNotifier * indicator_power_notifier_new (void);
+
+void indicator_power_notifier_set_bus (IndicatorPowerNotifier * self,
+ GDBusConnection * connection);
+
+void indicator_power_notifier_set_battery (IndicatorPowerNotifier * self,
+ IndicatorPowerDevice * battery);
+
+#define POWER_LEVEL_STR_OK "ok"
+#define POWER_LEVEL_STR_LOW "low"
+#define POWER_LEVEL_STR_VERY_LOW "very_low"
+#define POWER_LEVEL_STR_CRITICAL "critical"
+const char * indicator_power_notifier_get_power_level (IndicatorPowerDevice * battery);
+
+G_END_DECLS
+
+#endif /* __INDICATOR_POWER_NOTIFIER_H__ */
diff --git a/src/org.freedesktop.UPower.xml b/src/org.freedesktop.UPower.xml
deleted file mode 100644
index 7b73583..0000000
--- a/src/org.freedesktop.UPower.xml
+++ /dev/null
@@ -1,43 +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>
- <interface name="org.freedesktop.UPower">
- <method name="HibernateAllowed">
- <arg name="allowed" type="b" direction="out"/>
- </method>
- <method name="Hibernate">
- </method>
- <method name="SuspendAllowed">
- <arg name="allowed" type="b" direction="out"/>
- </method>
- <method name="Suspend">
- </method>
- <method name="AboutToSleep">
- </method>
- <method name="EnumerateDevices">
- <arg name="devices" type="ao" direction="out"/>
- </method>
- <signal name="Resuming">
- </signal>
- <signal name="Sleeping">
- </signal>
- <signal name="Changed">
- </signal>
- <signal name="DeviceChanged">
- <arg type="s"/>
- </signal>
- <signal name="DeviceRemoved">
- <arg type="s"/>
- </signal>
- <signal name="DeviceAdded">
- <arg type="s"/>
- </signal>
- <property name="LidIsPresent" type="b" access="read"/>
- <property name="LidIsClosed" type="b" access="read"/>
- <property name="OnLowBattery" type="b" access="read"/>
- <property name="OnBattery" type="b" access="read"/>
- <property name="CanHibernate" type="b" access="read"/>
- <property name="CanSuspend" type="b" access="read"/>
- <property name="DaemonVersion" type="s" access="read"/>
- </interface>
-</node> \ No newline at end of file
diff --git a/src/service.c b/src/service.c
index f22d33d..32cec38 100644
--- a/src/service.c
+++ b/src/service.c
@@ -18,15 +18,15 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "config.h"
-
#include <glib/gi18n.h>
#include <gio/gio.h>
#include <url-dispatcher.h>
+#include "brightness.h"
+#include "dbus-shared.h"
#include "device.h"
#include "device-provider.h"
-#include "ib-brightness-control.h"
+#include "notifier.h"
#include "service.h"
#define BUS_NAME "com.canonical.indicator.power"
@@ -103,7 +103,7 @@ struct _IndicatorPowerServicePrivate
GSettings * settings;
- IbBrightnessControl * brightness_control;
+ IndicatorPowerBrightness * brightness;
guint own_id;
guint actions_export_id;
@@ -120,6 +120,7 @@ struct _IndicatorPowerServicePrivate
GList * devices; /* IndicatorPowerDevice */
IndicatorPowerDeviceProvider * device_provider;
+ IndicatorPowerNotifier * notifier;
};
typedef IndicatorPowerServicePrivate priv_t;
@@ -389,6 +390,8 @@ append_device_to_menu (GMenu * menu, const IndicatorPowerDevice * device, int pr
item = g_menu_item_new (label, NULL);
g_free (label);
+ g_menu_item_set_attribute (item, "x-canonical-type", "s", "com.canonical.indicator.basic");
+
if ((icon = indicator_power_device_get_gicon (device)))
{
GVariant * serialized_icon = g_icon_serialize (icon);
@@ -454,44 +457,17 @@ create_phone_devices_section (IndicatorPowerService * self G_GNUC_UNUSED)
****
***/
-static void
-get_brightness_range (IndicatorPowerService * self, gint * low, gint * high)
-{
- const int max = ib_brightness_control_get_max_value (self->priv->brightness_control);
- *low = max * 0.05; /* 5% minimum -- don't let the screen go completely dark */
- *high = max;
-}
-
-static gdouble
-brightness_to_percentage (IndicatorPowerService * self, int brightness)
-{
- int lo, hi;
- get_brightness_range (self, &lo, &hi);
- return (brightness-lo) / (double)(hi-lo);
-}
-
-static int
-percentage_to_brightness (IndicatorPowerService * self, double percentage)
-{
- int lo, hi;
- get_brightness_range (self, &lo, &hi);
- return (int)(lo + (percentage*(hi-lo)));
-}
-
static GMenuItem *
-create_brightness_menuitem (IndicatorPowerService * self)
+create_brightness_menu_item(void)
{
- int lo, hi;
GMenuItem * item;
- get_brightness_range (self, &lo, &hi);
-
- item = g_menu_item_new (NULL, "indicator.brightness");
- g_menu_item_set_attribute (item, "x-canonical-type", "s", "com.canonical.unity.slider");
- g_menu_item_set_attribute (item, "min-value", "d", brightness_to_percentage (self, lo));
- g_menu_item_set_attribute (item, "max-value", "d", brightness_to_percentage (self, hi));
- g_menu_item_set_attribute (item, "min-icon", "s", "torch-off" );
- g_menu_item_set_attribute (item, "max-icon", "s", "torch-on" );
+ item = g_menu_item_new(NULL, "indicator.brightness");
+ g_menu_item_set_attribute(item, "x-canonical-type", "s", "com.canonical.unity.slider");
+ g_menu_item_set_attribute(item, "min-value", "d", 0.0);
+ g_menu_item_set_attribute(item, "max-value", "d", 1.0);
+ g_menu_item_set_attribute(item, "min-icon", "s", "torch-off" );
+ g_menu_item_set_attribute(item, "max-icon", "s", "torch-on" );
return item;
}
@@ -499,9 +475,8 @@ create_brightness_menuitem (IndicatorPowerService * self)
static GVariant *
action_state_for_brightness (IndicatorPowerService * self)
{
- priv_t * p = self->priv;
- const gint brightness = ib_brightness_control_get_value (p->brightness_control);
- return g_variant_new_double (brightness_to_percentage (self, brightness));
+ IndicatorPowerBrightness * b = self->priv->brightness;
+ return g_variant_new_double(indicator_power_brightness_get_percentage(b));
}
static void
@@ -517,10 +492,9 @@ on_brightness_change_requested (GSimpleAction * action G_GNUC_UNUSED,
gpointer gself)
{
IndicatorPowerService * self = INDICATOR_POWER_SERVICE (gself);
- const gdouble percentage = g_variant_get_double (parameter);
- const int brightness = percentage_to_brightness (self, percentage);
- ib_brightness_control_set_value (self->priv->brightness_control, brightness);
- update_brightness_action_state (self);
+
+ indicator_power_brightness_set_percentage(self->priv->brightness,
+ g_variant_get_double (parameter));
}
static GMenuModel *
@@ -544,21 +518,34 @@ create_desktop_settings_section (IndicatorPowerService * self G_GNUC_UNUSED)
}
static GMenuModel *
-create_phone_settings_section (IndicatorPowerService * self G_GNUC_UNUSED)
+create_phone_settings_section(IndicatorPowerService * self)
{
GMenu * section;
GMenuItem * item;
+ gboolean ab_supported;
- section = g_menu_new ();
+ section = g_menu_new();
- item = create_brightness_menuitem (self);
- g_menu_append_item (section, item);
- update_brightness_action_state (self);
- g_object_unref (item);
+ item = create_brightness_menu_item();
+ g_menu_append_item(section, item);
+ update_brightness_action_state(self);
+ g_object_unref(item);
+
+ g_object_get(self->priv->brightness,
+ "auto-brightness-supported", &ab_supported,
+ NULL);
+
+ if (ab_supported)
+ {
+ item = g_menu_item_new(_("Adjust brightness automatically"), "indicator.auto-brightness");
+ g_menu_item_set_attribute(item, "x-canonical-type", "s", "com.canonical.indicator.switch");
+ g_menu_append_item(section, item);
+ g_object_unref(item);
+ }
- g_menu_append (section, _("Battery settings…"), "indicator.activate-phone-settings");
+ g_menu_append(section, _("Battery settings…"), "indicator.activate-phone-settings");
- return G_MENU_MODEL (section);
+ return G_MENU_MODEL(section);
}
/***
@@ -588,14 +575,14 @@ rebuild_now (IndicatorPowerService * self, guint sections)
struct ProfileMenuInfo * desktop = &p->menus[PROFILE_DESKTOP];
struct ProfileMenuInfo * greeter = &p->menus[PROFILE_DESKTOP_GREETER];
- if (p->conn == NULL) /* we haven't built the menus yet */
- return;
-
if (sections & SECTION_HEADER)
{
g_simple_action_set_state (p->header_action, create_header_state (self));
}
+ if (p->conn == NULL) /* we haven't built the menus yet */
+ return;
+
if (sections & SECTION_DEVICES)
{
rebuild_section (desktop->submenu, 0, create_desktop_devices_section (self, PROFILE_DESKTOP));
@@ -605,7 +592,7 @@ rebuild_now (IndicatorPowerService * self, guint sections)
if (sections & SECTION_SETTINGS)
{
rebuild_section (desktop->submenu, 1, create_desktop_settings_section (self));
- rebuild_section (phone->submenu, 1, create_desktop_settings_section (self));
+ rebuild_section (phone->submenu, 1, create_phone_settings_section (self));
}
}
@@ -615,18 +602,6 @@ rebuild_header_now (IndicatorPowerService * self)
rebuild_now (self, SECTION_HEADER);
}
-static inline void
-rebuild_devices_section_now (IndicatorPowerService * self)
-{
- rebuild_now (self, SECTION_DEVICES);
-}
-
-static inline void
-rebuild_settings_section_now (IndicatorPowerService * self)
-{
- rebuild_now (self, SECTION_SETTINGS);
-}
-
static void
create_menu (IndicatorPowerService * self, int profile)
{
@@ -761,6 +736,28 @@ on_phone_settings_activated (GSimpleAction * a G_GNUC_UNUSED,
****
***/
+static gboolean
+convert_auto_prop_to_state(GBinding * binding G_GNUC_UNUSED,
+ const GValue * from_value,
+ GValue * to_value,
+ gpointer user_data G_GNUC_UNUSED)
+{
+ const gboolean b = g_value_get_boolean(from_value);
+ g_value_set_variant(to_value, g_variant_new_boolean(b));
+ return TRUE;
+}
+
+static gboolean
+convert_auto_state_to_prop(GBinding * binding G_GNUC_UNUSED,
+ const GValue * from_value,
+ GValue * to_value,
+ gpointer user_data G_GNUC_UNUSED)
+{
+ GVariant * v = g_value_get_variant(from_value);
+ g_value_set_boolean(to_value, g_variant_get_boolean(v));
+ return TRUE;
+}
+
static void
init_gactions (IndicatorPowerService * self)
{
@@ -793,6 +790,16 @@ init_gactions (IndicatorPowerService * self)
g_action_map_add_action (G_ACTION_MAP(p->actions), G_ACTION(a));
p->battery_level_action = a;
+ /* add the auto-brightness action */
+ a = g_simple_action_new_stateful("auto-brightness", NULL, g_variant_new_boolean(FALSE));
+ g_object_bind_property_full(p->brightness, "auto-brightness",
+ a, "state",
+ G_BINDING_SYNC_CREATE|G_BINDING_BIDIRECTIONAL,
+ convert_auto_prop_to_state,
+ convert_auto_state_to_prop,
+ NULL, NULL);
+ g_action_map_add_action(G_ACTION_MAP(p->actions), G_ACTION(a));
+
/* add the brightness action */
a = g_simple_action_new_stateful ("brightness", NULL, action_state_for_brightness (self));
g_action_map_add_action (G_ACTION_MAP(p->actions), G_ACTION(a));
@@ -833,6 +840,9 @@ on_bus_acquired (GDBusConnection * connection,
p->conn = g_object_ref (G_OBJECT (connection));
+ /* export the battery properties */
+ indicator_power_notifier_set_bus (p->notifier, connection);
+
/* export the actions */
if ((id = g_dbus_connection_export_action_group (connection,
BUS_PATH,
@@ -854,9 +864,6 @@ on_bus_acquired (GDBusConnection * connection,
g_string_printf (path, "%s/%s", BUS_PATH, menu_names[i]);
- if (menu->menu == NULL)
- create_menu (self, i);
-
if ((id = g_dbus_connection_export_menu_model (connection,
path->str,
G_MENU_MODEL (menu->menu),
@@ -932,16 +939,28 @@ on_devices_changed (IndicatorPowerService * self)
g_clear_object (&p->primary_device);
p->primary_device = indicator_power_service_choose_primary_device (p->devices);
+ /* update the notifier's battery */
+ if ((p->primary_device != NULL) && (indicator_power_device_get_kind(p->primary_device) == UP_DEVICE_KIND_BATTERY))
+ indicator_power_notifier_set_battery (p->notifier, p->primary_device);
+ else
+ indicator_power_notifier_set_battery (p->notifier, NULL);
+
/* update the battery-level action's state */
if (p->primary_device == NULL)
battery_level = 0;
else
- battery_level = (int)(indicator_power_device_get_percentage (p->primary_device) + 0.5);
+ battery_level = (guint32)(indicator_power_device_get_percentage (p->primary_device) + 0.5);
g_simple_action_set_state (p->battery_level_action, g_variant_new_uint32 (battery_level));
rebuild_now (self, SECTION_HEADER | SECTION_DEVICES);
}
+static void
+on_auto_brightness_supported_changed(IndicatorPowerService * self)
+{
+ rebuild_now(self, SECTION_SETTINGS);
+}
+
/***
**** GObject virtual functions
@@ -1013,15 +1032,15 @@ my_dispose (GObject * o)
g_clear_object (&p->settings);
}
+ g_clear_object (&p->notifier);
g_clear_object (&p->brightness_action);
+ g_clear_object (&p->brightness);
g_clear_object (&p->battery_level_action);
g_clear_object (&p->header_action);
g_clear_object (&p->actions);
g_clear_object (&p->conn);
- g_clear_pointer (&p->brightness_control, ib_brightness_control_free);
-
indicator_power_service_set_device_provider (self, NULL);
G_OBJECT_CLASS (indicator_power_service_parent_class)->dispose (o);
@@ -1034,29 +1053,42 @@ my_dispose (GObject * o)
static void
indicator_power_service_init (IndicatorPowerService * self)
{
- priv_t * p = G_TYPE_INSTANCE_GET_PRIVATE (self,
- INDICATOR_TYPE_POWER_SERVICE,
- IndicatorPowerServicePrivate);
+ priv_t * p;
+ int i;
+
+ p = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ INDICATOR_TYPE_POWER_SERVICE,
+ IndicatorPowerServicePrivate);
self->priv = p;
p->cancellable = g_cancellable_new ();
p->settings = g_settings_new ("com.canonical.indicator.power");
- p->brightness_control = ib_brightness_control_new ();
+ p->notifier = indicator_power_notifier_new ();
+
+ p->brightness = indicator_power_brightness_new();
+ g_signal_connect_swapped(p->brightness, "notify::percentage",
+ G_CALLBACK(update_brightness_action_state), self);
init_gactions (self);
g_signal_connect_swapped (p->settings, "changed", G_CALLBACK(rebuild_header_now), self);
- p->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);
+ for (i=0; i<N_PROFILES; ++i)
+ create_menu(self, i);
+
+ g_signal_connect_swapped(p->brightness, "notify::auto-brightness-supported",
+ G_CALLBACK(on_auto_brightness_supported_changed), self);
+
+ p->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);
}
static void
@@ -1117,9 +1149,8 @@ indicator_power_service_set_device_provider (IndicatorPowerService * self,
if (p->device_provider != NULL)
{
- g_signal_handlers_disconnect_by_func (p->device_provider,
- G_CALLBACK(on_devices_changed),
- self);
+ g_signal_handlers_disconnect_by_data (p->device_provider, self);
+
g_clear_object (&p->device_provider);
g_clear_object (&p->primary_device);
@@ -1139,6 +1170,129 @@ indicator_power_service_set_device_provider (IndicatorPowerService * self,
}
}
+/* If a device has multiple batteries and uses only one of them at a time,
+ they should be presented as separate items inside the battery menu,
+ but everywhere else they should be aggregated (bug 880881).
+ Their percentages should be averaged. If any are discharging,
+ the aggregated time remaining should be the maximum of the times
+ for all those that are discharging, plus the sum of the times
+ for all those that are idle. Otherwise, the aggregated time remaining
+ should be the the maximum of the times for all those that are charging. */
+static IndicatorPowerDevice *
+create_totalled_battery_device (const GList * devices)
+{
+ const GList * l;
+ guint n_charged = 0;
+ guint n_charging = 0;
+ guint n_discharging = 0;
+ guint n_batteries = 0;
+ double sum_percent = 0;
+ time_t max_discharge_time = 0;
+ time_t max_charge_time = 0;
+ time_t sum_charged_time = 0;
+ IndicatorPowerDevice * device = NULL;
+
+ for (l=devices; l!=NULL; l=l->next)
+ {
+ const IndicatorPowerDevice * walk = INDICATOR_POWER_DEVICE(l->data);
+
+ if (indicator_power_device_get_kind(walk) == UP_DEVICE_KIND_BATTERY)
+ {
+ const double percent = indicator_power_device_get_percentage (walk);
+ const time_t t = indicator_power_device_get_time (walk);
+ const UpDeviceState state = indicator_power_device_get_state (walk);
+
+ ++n_batteries;
+
+ if (percent > 0.01)
+ sum_percent += percent;
+
+ if (state == UP_DEVICE_STATE_CHARGING)
+ {
+ ++n_charging;
+ max_charge_time = MAX(max_charge_time, t);
+ }
+ else if (state == UP_DEVICE_STATE_DISCHARGING)
+ {
+ ++n_discharging;
+ max_discharge_time = MAX(max_discharge_time, t);
+ }
+ else if (state == UP_DEVICE_STATE_FULLY_CHARGED)
+ {
+ ++n_charged;
+ sum_charged_time += t;
+ }
+ }
+ }
+
+ if (n_batteries > 1)
+ {
+ const double percent = sum_percent / n_batteries;
+ UpDeviceState state;
+ time_t time_left;
+
+ if (n_discharging > 0)
+ {
+ state = UP_DEVICE_STATE_DISCHARGING;
+ time_left = max_discharge_time + sum_charged_time;
+ }
+ else if (n_charging > 0)
+ {
+ state = UP_DEVICE_STATE_CHARGING;
+ time_left = max_charge_time;
+ }
+ else if (n_charged > 0)
+ {
+ state = UP_DEVICE_STATE_FULLY_CHARGED;
+ time_left = 0;
+ }
+ else
+ {
+ state = UP_DEVICE_STATE_UNKNOWN;
+ time_left = 0;
+ }
+
+ device = indicator_power_device_new (NULL,
+ UP_DEVICE_KIND_BATTERY,
+ percent,
+ state,
+ time_left);
+ }
+
+ return device;
+}
+
+/**
+ * If there are multiple UP_DEVICE_KIND_BATTERY devices in the list,
+ * they're merged into a new 'totalled' device representing the sum of them.
+ *
+ * Returns: (element-type IndicatorPowerDevice)(transfer full): a list of devices
+ */
+static GList*
+merge_batteries_together (GList * devices)
+{
+ GList * ret;
+ IndicatorPowerDevice * merged_device;
+
+ if ((merged_device = create_totalled_battery_device (devices)))
+ {
+ GList * l;
+
+ ret = g_list_append (NULL, merged_device);
+
+ for (l=devices; l!=NULL; l=l->next)
+ if (indicator_power_device_get_kind(INDICATOR_POWER_DEVICE(l->data)) != UP_DEVICE_KIND_BATTERY)
+ ret = g_list_append (ret, g_object_ref(l->data));
+ }
+ else /* not enough batteries to merge */
+ {
+ ret = g_list_copy (devices);
+ g_list_foreach (ret, (GFunc)g_object_ref, NULL);
+ }
+
+ return ret;
+}
+
IndicatorPowerDevice *
indicator_power_service_choose_primary_device (GList * devices)
{
@@ -1146,13 +1300,10 @@ indicator_power_service_choose_primary_device (GList * devices)
if (devices != NULL)
{
- GList * tmp;
-
- tmp = g_list_copy (devices);
+ GList * tmp = merge_batteries_together (devices);
tmp = g_list_sort (tmp, device_compare_func);
primary = g_object_ref (tmp->data);
-
- g_list_free (tmp);
+ g_list_free_full (tmp, (GDestroyNotify)g_object_unref);
}
return primary;
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644
index 0000000..a0d24af
--- /dev/null
+++ b/tests/CMakeLists.txt
@@ -0,0 +1,61 @@
+# 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 ${INCLUDE_DIRECTORIES} ${GTEST_INCLUDE_DIR})
+set_target_properties (gtest PROPERTIES COMPILE_FLAGS ${COMPILE_FLAGS} -w)
+
+# dbustest
+pkg_check_modules(DBUSTEST REQUIRED
+ dbustest-1>=14.04.0)
+include_directories (SYSTEM ${DBUSTEST_INCLUDE_DIRS})
+
+# add warnings
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g ${C_WARNING_ARGS}")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-weak-vtables -Wno-global-constructors") # Google Test
+
+# build the necessary schemas
+set_directory_properties (PROPERTIES
+ ADDITIONAL_MAKE_CLEAN_FILES gschemas.compiled)
+set_source_files_properties (gschemas.compiled GENERATED)
+
+# GSettings:
+# compile the indicator-power schema 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.power.gschema.xml
+ COMMAND cp -f ${CMAKE_BINARY_DIR}/data/*gschema.xml ${SCHEMA_DIR}
+ COMMAND ${COMPILE_SCHEMA_EXECUTABLE} ${SCHEMA_DIR})
+
+# look for headers in our src dir, and also in the directories where we autogenerate files...
+include_directories (${CMAKE_SOURCE_DIR}/src)
+include_directories (${CMAKE_BINARY_DIR}/src)
+include_directories (${CMAKE_CURRENT_BINARY_DIR})
+
+###
+###
+
+function(add_test_by_name name)
+ set (TEST_NAME ${name})
+ add_executable (${TEST_NAME} ${TEST_NAME}.cc gschemas.compiled)
+ add_test (${TEST_NAME} ${TEST_NAME})
+ add_dependencies (${TEST_NAME} libindicatorpowerservice)
+ target_link_libraries (${TEST_NAME} indicatorpowerservice gtest ${DBUSTEST_LIBRARIES} ${SERVICE_DEPS_LIBRARIES} ${GTEST_LIBS})
+endfunction()
+add_test_by_name(test-notify)
+add_test(NAME dear-reader-the-next-test-takes-80-seconds COMMAND true)
+add_test_by_name(test-device)
+
+###
+###
+
+set (APP_NAME indicator-power-service-cmdline-battery)
+add_executable (${APP_NAME} ${APP_NAME}.cc device-provider-mock.c)
+add_dependencies (${APP_NAME} libindicatorpowerservice)
+target_link_libraries (${APP_NAME} indicatorpowerservice ${SERVICE_DEPS_LIBRARIES})
+
diff --git a/tests/Makefile.am b/tests/Makefile.am
deleted file mode 100644
index 47b3e84..0000000
--- a/tests/Makefile.am
+++ /dev/null
@@ -1,64 +0,0 @@
-TESTS =
-CLEANFILES =
-BUILT_SOURCES =
-check_PROGRAMS =
-
-###
-### tests: stock tests on user-visible strings
-###
-
-include $(srcdir)/Makefile.am.strings
-
-###
-### gtest library
-###
-
-check_LIBRARIES = libgtest.a
-nodist_libgtest_a_SOURCES = \
- $(GTEST_SOURCE)/src/gtest-all.cc \
- $(GTEST_SOURCE)/src/gtest_main.cc
-
-AM_CPPFLAGS = $(GTEST_CPPFLAGS) -I${top_srcdir}/src -Wall -Werror -std=c++11
-AM_CXXFLAGS = $(GTEST_CXXFLAGS)
-
-###
-### tests: indicator-power-device
-###
-
-TEST_LIBS = $(COVERAGE_LDFLAGS) libgtest.a -lpthread
-TEST_CPPFLAGS = $(SERVICE_DEPS_CFLAGS) $(AM_CPPFLAGS)
-
-TESTS += test-device
-check_PROGRAMS += test-device
-test_device_SOURCES = test-device.cc
-test_device_CPPFLAGS = $(TEST_CPPFLAGS) -Wall -Werror -Wextra
-test_device_LDADD = \
- $(top_builddir)/src/libindicatorpower-service.a \
- $(SERVICE_DEPS_LIBS) \
- $(TEST_LIBS)
-
-
-# (FIXME: incomplete)
-#
-###
-### tests: indicator-power-service
-###
-#
-# build a local copy of the GSettings schemas
-#BUILT_SOURCES += gschemas.compiled
-#CLEANFILES += gschemas.compiled
-#gschemas.compiled: Makefile
-# @glib-compile-schemas --targetdir=$(abs_builddir) $(top_builddir)/data
-#
-#TESTS += test-service
-#check_PROGRAMS += test-service
-#test_service_SOURCES = test-service.cc
-#test_service_LDADD = $(TEST_LIBS)
-#test_service_CPPFLAGS = $(TEST_CPPFLAGS) -DSCHEMA_DIR="\"$(top_builddir)/tests/\""
-#
-#TESTS += test-dbus-listener
-#check_PROGRAMS += test-dbus-listener
-#test_dbus_listener_SOURCES = test-dbus-listener.cc
-#test_dbus_listener_LDADD = $(TEST_LIBS)
-#test_dbus_listener_CPPFLAGS = $(TEST_CPPFLAGS)
-#
diff --git a/tests/device-provider-mock.c b/tests/device-provider-mock.c
new file mode 100644
index 0000000..afca178
--- /dev/null
+++ b/tests/device-provider-mock.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2014 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 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/>.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ */
+
+#include "device.h"
+#include "device-provider.h"
+#include "device-provider-mock.h"
+
+/***
+**** GObject boilerplate
+***/
+
+static void indicator_power_device_provider_interface_init (
+ IndicatorPowerDeviceProviderInterface * iface);
+
+G_DEFINE_TYPE_WITH_CODE (
+ IndicatorPowerDeviceProviderMock,
+ indicator_power_device_provider_mock,
+ G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (INDICATOR_TYPE_POWER_DEVICE_PROVIDER,
+ indicator_power_device_provider_interface_init))
+
+/***
+**** IndicatorPowerDeviceProvider virtual functions
+***/
+
+static GList *
+my_get_devices (IndicatorPowerDeviceProvider * provider)
+{
+ IndicatorPowerDeviceProviderMock * self = INDICATOR_POWER_DEVICE_PROVIDER_MOCK(provider);
+
+ return g_list_copy_deep (self->devices, (GCopyFunc)g_object_ref, NULL);
+}
+
+/***
+**** GObject virtual functions
+***/
+
+static void
+my_dispose (GObject * o)
+{
+ IndicatorPowerDeviceProviderMock * self = INDICATOR_POWER_DEVICE_PROVIDER_MOCK(o);
+
+ g_list_free_full (self->devices, g_object_unref);
+
+ G_OBJECT_CLASS (indicator_power_device_provider_mock_parent_class)->dispose (o);
+}
+
+/***
+**** Instantiation
+***/
+
+static void
+indicator_power_device_provider_mock_class_init (IndicatorPowerDeviceProviderMockClass * klass)
+{
+ GObjectClass * object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = my_dispose;
+}
+
+static void
+indicator_power_device_provider_interface_init (IndicatorPowerDeviceProviderInterface * iface)
+{
+ iface->get_devices = my_get_devices;
+}
+
+static void
+indicator_power_device_provider_mock_init (IndicatorPowerDeviceProviderMock * self)
+{
+}
+
+/***
+**** Public API
+***/
+
+IndicatorPowerDeviceProvider *
+indicator_power_device_provider_mock_new (void)
+{
+ gpointer o = g_object_new (INDICATOR_TYPE_POWER_DEVICE_PROVIDER_MOCK, NULL);
+
+ return INDICATOR_POWER_DEVICE_PROVIDER (o);
+}
+
+void
+indicator_power_device_provider_add_device (IndicatorPowerDeviceProviderMock * provider,
+ IndicatorPowerDevice * device)
+{
+ provider->devices = g_list_append (provider->devices, g_object_ref(device));
+
+ g_signal_connect_swapped (device, "notify", G_CALLBACK(indicator_power_device_provider_emit_devices_changed), provider);
+}
diff --git a/tests/device-provider-mock.h b/tests/device-provider-mock.h
new file mode 100644
index 0000000..4d06924
--- /dev/null
+++ b/tests/device-provider-mock.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2014 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 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/>.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ */
+
+#ifndef __INDICATOR_POWER_DEVICE_PROVIDER_MOCK__H__
+#define __INDICATOR_POWER_DEVICE_PROVIDER_MOCK__H__
+
+#include <glib-object.h> /* parent class */
+
+#include "device.h"
+#include "device-provider.h"
+
+G_BEGIN_DECLS
+
+#define INDICATOR_TYPE_POWER_DEVICE_PROVIDER_MOCK \
+ (indicator_power_device_provider_mock_get_type())
+
+#define INDICATOR_POWER_DEVICE_PROVIDER_MOCK(o) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((o), \
+ INDICATOR_TYPE_POWER_DEVICE_PROVIDER_MOCK, \
+ IndicatorPowerDeviceProviderMock))
+
+#define INDICATOR_POWER_DEVICE_PROVIDER_MOCK_GET_CLASS(o) \
+ (G_TYPE_INSTANCE_GET_CLASS ((o), \
+ INDICATOR_TYPE_POWER_DEVICE_PROVIDER_MOCK, \
+ IndicatorPowerDeviceProviderMockClass))
+
+#define INDICATOR_IS_POWER_DEVICE_PROVIDER_MOCK(o) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((o), \
+ INDICATOR_TYPE_POWER_DEVICE_PROVIDER_MOCK))
+
+typedef struct _IndicatorPowerDeviceProviderMock
+ IndicatorPowerDeviceProviderMock;
+typedef struct _IndicatorPowerDeviceProviderMockPriv
+ IndicatorPowerDeviceProviderMockPriv;
+typedef struct _IndicatorPowerDeviceProviderMockClass
+ IndicatorPowerDeviceProviderMockClass;
+
+/**
+ * An IndicatorPowerDeviceProvider which gets its devices from Mock.
+ */
+struct _IndicatorPowerDeviceProviderMock
+{
+ GObject parent_instance;
+
+ /*< private >*/
+ GList * devices;
+};
+
+struct _IndicatorPowerDeviceProviderMockClass
+{
+ GObjectClass parent_class;
+};
+
+GType indicator_power_device_provider_mock_get_type (void);
+
+IndicatorPowerDeviceProvider * indicator_power_device_provider_mock_new (void);
+
+void indicator_power_device_provider_add_device (IndicatorPowerDeviceProviderMock * provider,
+ IndicatorPowerDevice * device);
+
+G_END_DECLS
+
+#endif /* __INDICATOR_POWER_DEVICE_PROVIDER_MOCK__H__ */
diff --git a/tests/glib-fixture.h b/tests/glib-fixture.h
new file mode 100644
index 0000000..ce834a0
--- /dev/null
+++ b/tests/glib-fixture.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2014 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 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/>.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ */
+
+#include <map>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <gio/gio.h>
+
+#include <gtest/gtest.h>
+
+#include <locale.h> // setlocale()
+
+class GlibFixture : public ::testing::Test
+{
+ private:
+
+ GLogFunc realLogHandler;
+
+ std::map<GLogLevelFlags,size_t> expected_log;
+ std::map<GLogLevelFlags,std::vector<std::string>> log;
+
+ void test_log_counts()
+ {
+ const GLogLevelFlags levels_to_test[] = { G_LOG_LEVEL_ERROR,
+ G_LOG_LEVEL_CRITICAL,
+ G_LOG_LEVEL_MESSAGE,
+ G_LOG_LEVEL_WARNING };
+
+ for(const auto& level : levels_to_test)
+ {
+ const auto& v = log[level];
+ const auto n = v.size();
+
+ EXPECT_EQ(expected_log[level], n);
+
+ if (expected_log[level] != n)
+ for (size_t i=0; i<n; ++i)
+ g_print("%d %s\n", (n+1), v[i].c_str());
+ }
+
+ expected_log.clear();
+ log.clear();
+ }
+
+ static void default_log_handler(const gchar * log_domain,
+ GLogLevelFlags log_level,
+ const gchar * message,
+ gpointer self)
+ {
+ auto tmp = g_strdup_printf ("%s:%d \"%s\"", log_domain, int(log_level), message);
+ static_cast<GlibFixture*>(self)->log[log_level].push_back(tmp);
+ g_free(tmp);
+ }
+
+ protected:
+
+ void increment_expected_errors(GLogLevelFlags level, size_t n=1)
+ {
+ expected_log[level] += n;
+ }
+
+ virtual void SetUp()
+ {
+ setlocale(LC_ALL, "C.UTF-8");
+
+ loop = g_main_loop_new(nullptr, false);
+
+ g_log_set_default_handler(default_log_handler, this);
+
+ g_unsetenv("DISPLAY");
+ }
+
+ virtual void TearDown()
+ {
+ test_log_counts();
+
+ g_log_set_default_handler(realLogHandler, this);
+
+ g_clear_pointer(&loop, g_main_loop_unref);
+ }
+
+ private:
+
+ static gboolean
+ wait_for_signal__timeout(gpointer name)
+ {
+ g_error("%s: timed out waiting for signal '%s'", G_STRLOC, static_cast<char*>(name));
+ return G_SOURCE_REMOVE;
+ }
+
+ static gboolean
+ wait_msec__timeout(gpointer loop)
+ {
+ g_main_loop_quit(static_cast<GMainLoop*>(loop));
+ return G_SOURCE_CONTINUE;
+ }
+
+ protected:
+
+ /* convenience func to loop while waiting for a GObject's signal */
+ void wait_for_signal(gpointer o, const gchar * signal, const guint timeout_seconds=5)
+ {
+ // wait for the signal or for timeout, whichever comes first
+ const auto handler_id = g_signal_connect_swapped(o, signal,
+ G_CALLBACK(g_main_loop_quit),
+ loop);
+ const auto 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(guint msec=50)
+ {
+ const auto id = g_timeout_add(msec, wait_msec__timeout, loop);
+ g_main_loop_run(loop);
+ g_source_remove(id);
+ }
+
+ GMainLoop * loop;
+};
diff --git a/tests/indicator-power-service-cmdline-battery.cc b/tests/indicator-power-service-cmdline-battery.cc
new file mode 100644
index 0000000..50ed2bb
--- /dev/null
+++ b/tests/indicator-power-service-cmdline-battery.cc
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2014 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 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/>.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ */
+
+#include <cstdlib>
+
+#include <locale.h> // setlocale()
+#include <libintl.h> // bindtextdomain()
+#include <unistd.h> // STDIN_FILENO
+
+#include <gio/gio.h>
+
+#include "device-provider-mock.h"
+
+#include "service.h"
+
+/***
+****
+***/
+
+static void
+on_name_lost (gpointer instance G_GNUC_UNUSED, gpointer loop)
+{
+ g_message ("exiting: service couldn't acquire or lost ownership of busname");
+ g_main_loop_quit (static_cast<GMainLoop*>(loop));
+}
+
+static IndicatorPowerDevice * battery = nullptr;
+
+static GMainLoop * loop = nullptr;
+
+static gboolean on_command_stream_available (GIOChannel *source,
+ GIOCondition /*condition*/,
+ gpointer /*user_data*/)
+{
+ gchar * str = nullptr;
+ GError * error = nullptr;
+ auto status = g_io_channel_read_line (source, &str, nullptr, nullptr, &error);
+ g_assert_no_error (error);
+
+ if (status == G_IO_STATUS_NORMAL)
+ {
+ g_strstrip (str);
+
+ if (!g_strcmp0 (str, "charging"))
+ {
+ g_object_set (battery, INDICATOR_POWER_DEVICE_STATE, UP_DEVICE_STATE_CHARGING, nullptr);
+ }
+ else if (!g_strcmp0 (str, "discharging"))
+ {
+ g_object_set (battery, INDICATOR_POWER_DEVICE_STATE, UP_DEVICE_STATE_DISCHARGING, nullptr);
+ }
+ else
+ {
+ g_object_set (battery, INDICATOR_POWER_DEVICE_PERCENTAGE, atof(str), nullptr);
+ }
+ }
+ else if (status == G_IO_STATUS_EOF)
+ {
+ g_main_loop_quit (loop);
+ }
+
+ g_free (str);
+ return G_SOURCE_CONTINUE;
+}
+
+/* this is basically indicator-power-service with a custom provider */
+int
+main (int argc G_GNUC_UNUSED, char ** argv G_GNUC_UNUSED)
+{
+ g_print("This test app has the same code as indicator-power-service\n"
+ "except instead of listening to UPower, it has a fake battery\n"
+ "which you can edit with keyboard inputs. Supported commands:\n"
+ "1. A number in [0..100] to set battery level\n"
+ "2. 'charging'\n"
+ "3. 'discharging'\n"
+ "4. ctrl-c to exit\n");
+
+ IndicatorPowerDeviceProvider * device_provider;
+ IndicatorPowerService * service;
+
+ g_assert(g_setenv("GSETTINGS_SCHEMA_DIR", SCHEMA_DIR, true));
+ g_assert(g_setenv("GSETTINGS_BACKEND", "memory", true));
+
+ /* boilerplate i18n */
+ setlocale (LC_ALL, "");
+ bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
+ textdomain (GETTEXT_PACKAGE);
+
+ /* read lines from the command line */
+ auto channel = g_io_channel_unix_new (STDIN_FILENO);
+ auto watch_tag = g_io_add_watch (channel, G_IO_IN, on_command_stream_available, nullptr);
+
+ /* run */
+ battery = indicator_power_device_new ("/some/path", UP_DEVICE_KIND_BATTERY, 50.0, UP_DEVICE_STATE_DISCHARGING, 30*60);
+ device_provider = indicator_power_device_provider_mock_new ();
+ indicator_power_device_provider_add_device (INDICATOR_POWER_DEVICE_PROVIDER_MOCK(device_provider), battery);
+ service = indicator_power_service_new (device_provider);
+ loop = g_main_loop_new (NULL, FALSE);
+ g_signal_connect (service, INDICATOR_POWER_SERVICE_SIGNAL_NAME_LOST,
+ G_CALLBACK(on_name_lost), loop);
+ g_main_loop_run (loop);
+
+ /* cleanup */
+ g_main_loop_unref (loop);
+ g_source_remove (watch_tag);
+ g_io_channel_unref (channel);
+ g_clear_object (&service);
+ g_clear_object (&device_provider);
+ g_clear_object (&battery);
+ return 0;
+}
diff --git a/tests/manual b/tests/manual
index d3a22e1..1ec1524 100644
--- a/tests/manual
+++ b/tests/manual
@@ -1,24 +1,89 @@
+Notes on Battery Testing
+
+When building from source, an executable 'indicator-power-service-cmdline-battery' will be built in the tests/ directory. This has the same code as indicator-power-service, except instead of listening to UPower it has a single fake battery that can be set from the command line to set its charge level and whether it's charging or discharging.
+
+You'll need to stop the current indicator-power-service before starting the test one. After that, you enter in a number, or 'charging', or 'discharging', to set the fake battery. ctrl-c exits.
+
+Example:
+
+$ stop indicator-power # stop the real indicator-power service
+$ build/tests/indicator-power-service-cmdline-battery # start the test service
+50 # sets the fake battery level to 50%
+30 # sets the fake battery level to 30%
+charging # sets the fake battery to charging
+discharging # sets the fake battery to discharging
+ctrl-c # exits the test service
+$ start indicator-power # restart the real service
+
+
+
Test-case indicator-power/unity7-items-check
<dl>
- <dt>Log in to a Unity 7 user session</dt>
- <dt>Go to the panel and click on the Power indicator</dt>
- <dd>Ensure there are items in the menu</dd>
+ <dt>Log in to a Unity 7 user session</dt>
+ <dt>Go to the panel and click on the Power indicator</dt>
+ <dd>Ensure there are items in the menu</dd>
</dl>
Test-case indicator-power/unity7-greeter-items-check
<dl>
- <dt>Start a system and wait for the greeter or logout of the current user session</dt>
- <dt>Go to the panel and click on the Power indicator</dt>
- <dd>Ensure there are items in the menu</dd>
+ <dt>Start a system and wait for the greeter or logout of the current user session</dt>
+ <dt>Go to the panel and click on the Power indicator</dt>
+ <dd>Ensure there are items in the menu</dd>
</dl>
Test-case indicator-power/unity8-items-check
<dl>
- <dt>Login to a user session running Unity 8</dt>
- <dt>Pull down the top panel until it sticks open</dt>
- <dt>Navigate through the tabs until "Battery" is shown</dt>
- <dd>Battery is at the top of the menu</dd>
- <dd>The menu is populated with items</dd>
+ <dt>Login to a user session running Unity 8</dt>
+ <dt>Pull down the top panel until it sticks open</dt>
+ <dt>Navigate through the tabs until "Battery" is shown</dt>
+ <dd>Battery is at the top of the menu</dd>
+ <dd>The menu is populated with items</dd>
</dl>
+Test-case indicator-power/detect-charging-or-discharging
+<dl>
+ <dt>Begin with a discharging device</dt>
+ <dd>The indicator's icon should denote a discharging battery; e.g. an icon without the '⚡' sign</dd>
+ <dt>Plug it in so that its battery is charging</dt>
+ <dd>The indicator's icon should change to show a charging battery</dd>
+ <dt>Unplug it again</dt>
+ <dd>The indicator's icon should revert back to the same one in step one</dd>
+</dl>
+
+Test-case indicator-power/low-power-notifications
+<dl>
+ <dt>Wait for the system's battery level to drop to 10% (or fake it, see 'Notes on Battery Testing' above)</dt>
+ <dd>A notification should appear</dd>
+ <dd>Its title should read "Battery Low"</dd>
+ <dd>Its text should read "10% charge remaining"</dd>
+ <dd>The icon should be a low power icon</dd>
+ <dd>It should have two actions, "Battery settings" and "OK". </dd>
+ <dt>Tap OK to dismiss the popup</dt>
+ <dt>Wait (or fake) the battery level to drop to 9%</dt>
+ <dd>No new notification should appear -- we're still at the "Low" level </dd>
+ <dt>Wait (or fake) the battery level to drop to 4%</dt>
+ <dd>A notification should appear</dd>
+ <dd>Its title should read "Battery Critical"</dd>
+ <dd>Its text should read "4% charge remaining"</dd>
+ <dd>The icon should be a critical power icon</dd>
+ <dd>It should have two actions, "Battery settings" and "OK". </dd>
+ <dt>Tap 'Battery Settings'</dt>
+ <dd>ubuntu-system-settings should be launched to the Battery page </dd>
+</dl>
+
+Test-case indicator-power/device-brightness-slider
+<dl>
+ <dt>On a device, pull down the power indicator's menu</dt>
+ <dd>The menu should include a brightness slider with icons</dd>
+ <dt>Slide the brightness slider back and forth</dt>
+ <dd>The screen should get brighter and darker in sync with the slider's position</dd>
+ <dt>Launch unity-system-settings' Brightness panel</dt>
+ <dt>Move both the indicator's and the settings panel's sliders</dt>
+ <dd>Both sliders' positions should stay in sync with each other</dd>
+ <dd>Both should have the same effect on the screen's brightness</dd>
+ <dt>Make a note of the current brightness level and slider position</dt>
+ <dt>Reboot the device</dt>
+ <dd>The screen brightness should be the same as it was before rebooting</dd>
+ <dd>The indicator's brightness slider should be in the same position as it was before rebooting</dd>
+</dl>
diff --git a/tests/test-device.cc b/tests/test-device.cc
index 4d4a89f..fbeb1c0 100644
--- a/tests/test-device.cc
+++ b/tests/test-device.cc
@@ -19,6 +19,7 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#include <gio/gio.h>
#include <gtest/gtest.h>
+#include <cmath> // ceil()
#include "device.h"
#include "service.h"
@@ -171,11 +172,11 @@ TEST_F(DeviceTest, Properties)
key = INDICATOR_POWER_DEVICE_PERCENTAGE;
g_object_set (o, key, 50.0, NULL);
g_object_get (o, key, &d, NULL);
- ASSERT_EQ((int)d, 50);
+ ASSERT_EQ(int(d), 50);
// TIME
key = INDICATOR_POWER_DEVICE_TIME;
- g_object_set (o, key, (guint64)30, NULL);
+ g_object_set (o, key, guint64(30), NULL);
g_object_get (o, key, &u64, NULL);
ASSERT_EQ(u64, 30);
@@ -192,11 +193,11 @@ TEST_F(DeviceTest, New)
30);
ASSERT_TRUE (device != NULL);
ASSERT_TRUE (INDICATOR_IS_POWER_DEVICE(device));
- ASSERT_EQ (indicator_power_device_get_kind(device), UP_DEVICE_KIND_BATTERY);
- ASSERT_EQ (indicator_power_device_get_state(device), UP_DEVICE_STATE_CHARGING);
- ASSERT_STREQ (indicator_power_device_get_object_path(device), "/object/path");
- ASSERT_EQ ((int)indicator_power_device_get_percentage(device), 50);
- ASSERT_EQ (indicator_power_device_get_time(device), 30);
+ ASSERT_EQ (UP_DEVICE_KIND_BATTERY, indicator_power_device_get_kind(device));
+ ASSERT_EQ (UP_DEVICE_STATE_CHARGING, indicator_power_device_get_state(device));
+ ASSERT_STREQ ("/object/path", indicator_power_device_get_object_path(device));
+ ASSERT_EQ (50, int(indicator_power_device_get_percentage(device)));
+ ASSERT_EQ (30, indicator_power_device_get_time(device));
// cleanup
g_object_unref (device);
@@ -204,22 +205,22 @@ TEST_F(DeviceTest, New)
TEST_F(DeviceTest, NewFromVariant)
{
- GVariant * variant = g_variant_new ("(susdut)",
- "/object/path",
- (guint32) UP_DEVICE_KIND_BATTERY,
- "icon",
- (gdouble) 50.0,
- (guint32) UP_DEVICE_STATE_CHARGING,
- (guint64) 30);
+ auto variant = g_variant_new("(susdut)",
+ "/object/path",
+ guint32(UP_DEVICE_KIND_BATTERY),
+ "icon",
+ 50.0,
+ guint32(UP_DEVICE_STATE_CHARGING),
+ guint64(30));
IndicatorPowerDevice * device = indicator_power_device_new_from_variant (variant);
ASSERT_TRUE (variant != NULL);
ASSERT_TRUE (device != NULL);
ASSERT_TRUE (INDICATOR_IS_POWER_DEVICE(device));
- ASSERT_EQ (indicator_power_device_get_kind(device), UP_DEVICE_KIND_BATTERY);
- ASSERT_EQ (indicator_power_device_get_state(device), UP_DEVICE_STATE_CHARGING);
- ASSERT_STREQ (indicator_power_device_get_object_path(device), "/object/path");
- ASSERT_EQ ((int)indicator_power_device_get_percentage(device), 50);
- ASSERT_EQ (indicator_power_device_get_time(device), 30);
+ ASSERT_EQ (UP_DEVICE_KIND_BATTERY, indicator_power_device_get_kind(device));
+ ASSERT_EQ (UP_DEVICE_STATE_CHARGING, indicator_power_device_get_state(device));
+ ASSERT_STREQ ("/object/path", indicator_power_device_get_object_path(device));
+ ASSERT_EQ (50, int(indicator_power_device_get_percentage(device)));
+ ASSERT_EQ (30, indicator_power_device_get_time(device));
// cleanup
g_object_unref (device);
@@ -323,8 +324,9 @@ TEST_F(DeviceTest, IconNames)
INDICATOR_POWER_DEVICE_PERCENTAGE, 95.0,
NULL);
- g_string_append_printf (expected, "%s-full-charging-symbolic;", kind_str);
+ g_string_append_printf (expected, "%s-100-charging;", kind_str);
g_string_append_printf (expected, "gpm-%s-100-charging;", kind_str);
+ g_string_append_printf (expected, "%s-full-charging-symbolic;", kind_str);
g_string_append_printf (expected, "%s-full-charging", kind_str);
check_icon_names (device, expected->str);
g_string_truncate (expected, 0);
@@ -334,8 +336,9 @@ TEST_F(DeviceTest, IconNames)
INDICATOR_POWER_DEVICE_STATE, UP_DEVICE_STATE_CHARGING,
INDICATOR_POWER_DEVICE_PERCENTAGE, 85.0,
NULL);
- g_string_append_printf (expected, "%s-full-charging-symbolic;", kind_str);
+ g_string_append_printf (expected, "%s-080-charging;", kind_str);
g_string_append_printf (expected, "gpm-%s-080-charging;", kind_str);
+ g_string_append_printf (expected, "%s-full-charging-symbolic;", kind_str);
g_string_append_printf (expected, "%s-full-charging", kind_str);
check_icon_names (device, expected->str);
g_string_truncate (expected, 0);
@@ -345,8 +348,9 @@ TEST_F(DeviceTest, IconNames)
INDICATOR_POWER_DEVICE_STATE, UP_DEVICE_STATE_CHARGING,
INDICATOR_POWER_DEVICE_PERCENTAGE, 50.0,
NULL);
- g_string_append_printf (expected, "%s-good-charging-symbolic;", kind_str);
+ g_string_append_printf (expected, "%s-060-charging;", kind_str);
g_string_append_printf (expected, "gpm-%s-060-charging;", kind_str);
+ g_string_append_printf (expected, "%s-good-charging-symbolic;", kind_str);
g_string_append_printf (expected, "%s-good-charging", kind_str);
check_icon_names (device, expected->str);
g_string_truncate (expected, 0);
@@ -356,8 +360,9 @@ TEST_F(DeviceTest, IconNames)
INDICATOR_POWER_DEVICE_STATE, UP_DEVICE_STATE_CHARGING,
INDICATOR_POWER_DEVICE_PERCENTAGE, 25.0,
NULL);
- g_string_append_printf (expected, "%s-low-charging-symbolic;", kind_str);
+ g_string_append_printf (expected, "%s-020-charging;", kind_str);
g_string_append_printf (expected, "gpm-%s-020-charging;", kind_str);
+ g_string_append_printf (expected, "%s-low-charging-symbolic;", kind_str);
g_string_append_printf (expected, "%s-low-charging", kind_str);
check_icon_names (device, expected->str);
g_string_truncate (expected, 0);
@@ -367,8 +372,9 @@ TEST_F(DeviceTest, IconNames)
INDICATOR_POWER_DEVICE_STATE, UP_DEVICE_STATE_CHARGING,
INDICATOR_POWER_DEVICE_PERCENTAGE, 5.0,
NULL);
- g_string_append_printf (expected, "%s-caution-charging-symbolic;", kind_str);
+ g_string_append_printf (expected, "%s-000-charging;", kind_str);
g_string_append_printf (expected, "gpm-%s-000-charging;", kind_str);
+ g_string_append_printf (expected, "%s-caution-charging-symbolic;", kind_str);
g_string_append_printf (expected, "%s-caution-charging", kind_str);
check_icon_names (device, expected->str);
g_string_truncate (expected, 0);
@@ -401,7 +407,7 @@ TEST_F(DeviceTest, IconNames)
g_object_set (o, INDICATOR_POWER_DEVICE_KIND, kind,
INDICATOR_POWER_DEVICE_STATE, UP_DEVICE_STATE_DISCHARGING,
INDICATOR_POWER_DEVICE_PERCENTAGE, 50.0,
- INDICATOR_POWER_DEVICE_TIME, (guint64)(60*60),
+ INDICATOR_POWER_DEVICE_TIME, guint64(60*60),
NULL);
g_string_append_printf (expected, "%s-060;", kind_str);
g_string_append_printf (expected, "gpm-%s-060;", kind_str);
@@ -414,12 +420,12 @@ TEST_F(DeviceTest, IconNames)
g_object_set (o, INDICATOR_POWER_DEVICE_KIND, kind,
INDICATOR_POWER_DEVICE_STATE, UP_DEVICE_STATE_DISCHARGING,
INDICATOR_POWER_DEVICE_PERCENTAGE, 25.0,
- INDICATOR_POWER_DEVICE_TIME, (guint64)(60*60),
+ INDICATOR_POWER_DEVICE_TIME, guint64(60*60),
NULL);
- g_string_append_printf (expected, "%s-040;", kind_str);
- g_string_append_printf (expected, "gpm-%s-040;", kind_str);
- g_string_append_printf (expected, "%s-good-symbolic;", kind_str);
- g_string_append_printf (expected, "%s-good", kind_str);
+ g_string_append_printf (expected, "%s-020;", kind_str);
+ g_string_append_printf (expected, "gpm-%s-020;", kind_str);
+ g_string_append_printf (expected, "%s-low-symbolic;", kind_str);
+ g_string_append_printf (expected, "%s-low", kind_str);
check_icon_names (device, expected->str);
g_string_truncate (expected, 0);
@@ -427,7 +433,7 @@ TEST_F(DeviceTest, IconNames)
g_object_set (o, INDICATOR_POWER_DEVICE_KIND, kind,
INDICATOR_POWER_DEVICE_STATE, UP_DEVICE_STATE_DISCHARGING,
INDICATOR_POWER_DEVICE_PERCENTAGE, 25.0,
- INDICATOR_POWER_DEVICE_TIME, (guint64)(60*15),
+ INDICATOR_POWER_DEVICE_TIME, guint64(60*15),
NULL);
g_string_append_printf (expected, "%s-020;", kind_str);
g_string_append_printf (expected, "gpm-%s-020;", kind_str);
@@ -440,12 +446,12 @@ TEST_F(DeviceTest, IconNames)
g_object_set (o, INDICATOR_POWER_DEVICE_KIND, kind,
INDICATOR_POWER_DEVICE_STATE, UP_DEVICE_STATE_DISCHARGING,
INDICATOR_POWER_DEVICE_PERCENTAGE, 5.0,
- INDICATOR_POWER_DEVICE_TIME, (guint64)(60*60),
+ INDICATOR_POWER_DEVICE_TIME, guint64(60*60),
NULL);
- g_string_append_printf (expected, "%s-040;", kind_str);
- g_string_append_printf (expected, "gpm-%s-040;", kind_str);
- g_string_append_printf (expected, "%s-good-symbolic;", kind_str);
- g_string_append_printf (expected, "%s-good", kind_str);
+ g_string_append_printf (expected, "%s-000;", kind_str);
+ g_string_append_printf (expected, "gpm-%s-000;", kind_str);
+ g_string_append_printf (expected, "%s-caution-symbolic;", kind_str);
+ g_string_append_printf (expected, "%s-caution", kind_str);
check_icon_names (device, expected->str);
g_string_truncate (expected, 0);
@@ -453,7 +459,7 @@ TEST_F(DeviceTest, IconNames)
g_object_set (o, INDICATOR_POWER_DEVICE_KIND, kind,
INDICATOR_POWER_DEVICE_STATE, UP_DEVICE_STATE_DISCHARGING,
INDICATOR_POWER_DEVICE_PERCENTAGE, 5.0,
- INDICATOR_POWER_DEVICE_TIME, (guint64)(60*15),
+ INDICATOR_POWER_DEVICE_TIME, guint64(60*15),
NULL);
g_string_append_printf (expected, "%s-000;", kind_str);
g_string_append_printf (expected, "gpm-%s-000;", kind_str);
@@ -489,12 +495,12 @@ TEST_F(DeviceTest, Labels)
log_count_ipower_expected++;
check_label (NULL, NULL);
log_count_ipower_expected += 5;
- check_header (NULL, NULL, NULL, NULL, NULL);
+ check_header (nullptr, nullptr, nullptr, nullptr, nullptr);
// bad args: a GObject that isn't a device
GObject * o = G_OBJECT(g_cancellable_new());
log_count_ipower_expected++;
- check_label ((IndicatorPowerDevice*)o, NULL);
+ check_label (INDICATOR_POWER_DEVICE(o), nullptr);
log_count_ipower_expected += 5;
check_header (NULL, NULL, NULL, NULL, NULL);
g_object_unref (o);
@@ -668,77 +674,106 @@ TEST_F(DeviceTest, Inestimable___this_takes_80_seconds)
g_free (real_lang);
}
-
-/* The menu title should tell you at a glance what you need to know most: what
- device will lose power soonest (and optionally when), or otherwise which
- device will take longest to charge (and optionally how long it will take). */
+/* If a device has multiple batteries and uses only one of them at a time,
+ they should be presented as separate items inside the battery menu,
+ but everywhere else they should be aggregated (bug 880881).
+ Their percentages should be averaged. If any are discharging,
+ the aggregated time remaining should be the maximum of the times
+ for all those that are discharging, plus the sum of the times
+ for all those that are idle. Otherwise, the aggregated time remaining
+ should be the the maximum of the times for all those that are charging. */
TEST_F(DeviceTest, ChoosePrimary)
{
- GList * device_list;
- IndicatorPowerDevice * a;
- IndicatorPowerDevice * b;
-
- a = indicator_power_device_new ("/org/freedesktop/UPower/devices/mouse",
- UP_DEVICE_KIND_MOUSE,
- 0.0,
- UP_DEVICE_STATE_DISCHARGING,
- 0);
- b = indicator_power_device_new ("/org/freedesktop/UPower/devices/battery",
- UP_DEVICE_KIND_BATTERY,
- 0.0,
- UP_DEVICE_STATE_DISCHARGING,
- 0);
-
- /* device states + time left to {discharge,charge} + % of charge left,
- sorted in order of preference wrt the spec's criteria.
- So tests[i] should be picked over any test with an index greater than i */
- struct {
- int kind;
- int state;
+ struct Description
+ {
+ const char * path;
+ UpDeviceKind kind;
+ UpDeviceState state;
guint64 time;
double percentage;
- } tests[] = {
- { UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_DISCHARGING, 49, 50.0 },
- { UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_DISCHARGING, 50, 50.0 },
- { UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_DISCHARGING, 50, 100.0 },
- { UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_DISCHARGING, 51, 50.0 },
- { UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_CHARGING, 50, 50.0 },
- { UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_CHARGING, 49, 50.0 },
- { UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_CHARGING, 49, 100.0 },
- { UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_CHARGING, 48, 50.0 },
- { UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_FULLY_CHARGED, 0, 50.0 },
- { UP_DEVICE_KIND_KEYBOARD, UP_DEVICE_STATE_FULLY_CHARGED, 0, 50.0 },
- { UP_DEVICE_KIND_LINE_POWER, UP_DEVICE_STATE_UNKNOWN, 0, 0.0 }
};
- device_list = NULL;
- device_list = g_list_append (device_list, a);
- device_list = g_list_append (device_list, b);
+ const Description descriptions[] = {
+ { "/some/path/d0", UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_DISCHARGING, 10, 60.0 }, // 0
+ { "/some/path/d1", UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_DISCHARGING, 20, 80.0 }, // 1
+ { "/some/path/d2", UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_DISCHARGING, 30, 100.0 }, // 2
- for (int i=0, n=G_N_ELEMENTS(tests); i<n; i++)
- {
- for (int j=i+1; j<n; j++)
- {
- g_object_set (a, INDICATOR_POWER_DEVICE_KIND, tests[i].kind,
- INDICATOR_POWER_DEVICE_STATE, tests[i].state,
- INDICATOR_POWER_DEVICE_TIME, guint64(tests[i].time),
- INDICATOR_POWER_DEVICE_PERCENTAGE, tests[i].percentage,
- NULL);
- g_object_set (b, INDICATOR_POWER_DEVICE_KIND, tests[j].kind,
- INDICATOR_POWER_DEVICE_STATE, tests[j].state,
- INDICATOR_POWER_DEVICE_TIME, guint64(tests[j].time),
- INDICATOR_POWER_DEVICE_PERCENTAGE, tests[j].percentage,
- NULL);
- ASSERT_EQ (a, indicator_power_service_choose_primary_device(device_list));
- g_object_unref (G_OBJECT(a));
-
- /* reverse the list to check that list order doesn't matter */
- device_list = g_list_reverse (device_list);
- ASSERT_EQ (a, indicator_power_service_choose_primary_device(device_list));
- g_object_unref (G_OBJECT(a));
- }
- }
+ { "/some/path/c0", UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_CHARGING, 10, 60.0 }, // 3
+ { "/some/path/c1", UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_CHARGING, 20, 80.0 }, // 4
+ { "/some/path/c2", UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_CHARGING, 30, 100.0 }, // 5
- // cleanup
- g_list_free_full (device_list, g_object_unref);
+ { "/some/path/f0", UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_FULLY_CHARGED, 0, 100.0 }, // 6
+ { "/some/path/m0", UP_DEVICE_KIND_MOUSE, UP_DEVICE_STATE_DISCHARGING, 20, 80.0 }, // 7
+ { "/some/path/m1", UP_DEVICE_KIND_MOUSE, UP_DEVICE_STATE_FULLY_CHARGED, 0, 100.0 }, // 8
+ { "/some/path/pw", UP_DEVICE_KIND_LINE_POWER, UP_DEVICE_STATE_UNKNOWN, 0, 0.0 } // 9
+ };
+
+ std::vector<IndicatorPowerDevice*> devices;
+ for(const auto& desc : descriptions)
+ devices.push_back(indicator_power_device_new(desc.path, desc.kind, desc.percentage, desc.state, time_t(desc.time)));
+
+ const struct {
+ std::vector<unsigned int> device_indices;
+ Description expected;
+ } tests[] = {
+
+ { { 0 }, descriptions[0] }, // 1 discharging
+ { { 0, 1 }, { nullptr, UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_DISCHARGING, 20, 70.0 } }, // 2 discharging
+ { { 1, 2 }, { nullptr, UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_DISCHARGING, 30, 90.0 } }, // 2 discharging
+ { { 0, 1, 2 }, { nullptr, UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_DISCHARGING, 30, 80.0 } }, // 3 discharging
+
+ { { 3 }, descriptions[3] }, // 1 charging
+ { { 3, 4 }, { nullptr, UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_CHARGING, 20, 70.0 } }, // 2 charging
+ { { 4, 5 }, { nullptr, UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_CHARGING, 30, 90.0 } }, // 2 charging
+ { { 3, 4, 5 }, { nullptr, UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_CHARGING, 30, 80.0 } }, // 3 charging
+
+ { { 6 }, descriptions[6] }, // 1 charged
+ { { 6, 0 }, { nullptr, UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_DISCHARGING, 10, 80.0 } }, // 1 charged, 1 discharging
+ { { 6, 3 }, { nullptr, UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_CHARGING, 10, 80.0 } }, // 1 charged, 1 charging
+ { { 6, 0, 3 }, { nullptr, UP_DEVICE_KIND_BATTERY, UP_DEVICE_STATE_DISCHARGING, 10, 73.3 } }, // 1 charged, 1 charging, 1 discharging
+
+ { { 0, 7 }, descriptions[0] }, // 1 discharging battery, 1 discharging mouse. pick the one with the least time left.
+ { { 2, 7 }, descriptions[7] }, // 1 discharging battery, 1 discharging mouse. pick the one with the least time left.
+
+ { { 0, 8 }, descriptions[0] }, // 1 discharging battery, 1 fully-charged mouse. pick the one that's discharging.
+ { { 6, 7 }, descriptions[7] }, // 1 discharging mouse, 1 fully-charged battery. pick the one that's discharging.
+
+ { { 0, 9 }, descriptions[0] }, // everything comes before power lines
+ { { 3, 9 }, descriptions[3] },
+ { { 7, 9 }, descriptions[7] }
+ };
+
+ for(const auto& test : tests)
+ {
+ const auto& x = test.expected;
+
+ GList * device_glist = nullptr;
+ for(const auto& i : test.device_indices)
+ device_glist = g_list_append(device_glist, devices[i]);
+
+ auto primary = indicator_power_service_choose_primary_device(device_glist);
+ EXPECT_STREQ(x.path, indicator_power_device_get_object_path(primary));
+ EXPECT_EQ(x.kind, indicator_power_device_get_kind(primary));
+ EXPECT_EQ(x.state, indicator_power_device_get_state(primary));
+ EXPECT_EQ(x.time, indicator_power_device_get_time(primary));
+ EXPECT_EQ(int(ceil(x.percentage)), int(ceil(indicator_power_device_get_percentage(primary))));
+ g_object_unref(primary);
+
+ // reverse the list and repeat the test
+ // to confirm that list order doesn't matter
+ device_glist = g_list_reverse (device_glist);
+ primary = indicator_power_service_choose_primary_device (device_glist);
+ EXPECT_STREQ(x.path, indicator_power_device_get_object_path(primary));
+ EXPECT_EQ(x.kind, indicator_power_device_get_kind(primary));
+ EXPECT_EQ(x.state, indicator_power_device_get_state(primary));
+ EXPECT_EQ(x.time, indicator_power_device_get_time(primary));
+ EXPECT_EQ(int(ceil(x.percentage)), int(ceil(indicator_power_device_get_percentage(primary))));
+ g_object_unref(primary);
+
+ // cleanup
+ g_list_free(device_glist);
+ }
+
+ for (auto& device : devices)
+ g_object_unref (device);
}
diff --git a/tests/test-notify.cc b/tests/test-notify.cc
new file mode 100644
index 0000000..d056f3f
--- /dev/null
+++ b/tests/test-notify.cc
@@ -0,0 +1,410 @@
+/*
+ * Copyright 2014 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 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/>.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ */
+
+
+#include "glib-fixture.h"
+
+#include "dbus-shared.h"
+#include "device.h"
+#include "notifier.h"
+
+#include <gtest/gtest.h>
+
+#include <libdbustest/dbus-test.h>
+
+#include <libnotify/notify.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+/***
+****
+***/
+
+class NotifyFixture: public GlibFixture
+{
+private:
+
+ typedef GlibFixture super;
+
+ static constexpr char const * NOTIFY_BUSNAME {"org.freedesktop.Notifications"};
+ static constexpr char const * NOTIFY_INTERFACE {"org.freedesktop.Notifications"};
+ static constexpr char const * NOTIFY_PATH {"/org/freedesktop/Notifications"};
+
+protected:
+
+ DbusTestService * service = nullptr;
+ DbusTestDbusMock * mock = nullptr;
+ DbusTestDbusMockObject * obj = nullptr;
+ GDBusConnection * bus = nullptr;
+
+ static constexpr int FIRST_NOTIFY_ID {1234};
+
+ static constexpr int NOTIFICATION_CLOSED_EXPIRED {1};
+ static constexpr int NOTIFICATION_CLOSED_DISMISSED {2};
+ static constexpr int NOTIFICATION_CLOSED_API {3};
+ static constexpr int NOTIFICATION_CLOSED_UNDEFINED {4};
+
+ static constexpr char const * APP_NAME {"indicator-power-service"};
+
+ static constexpr char const * METHOD_CLOSE {"CloseNotification"};
+ static constexpr char const * METHOD_NOTIFY {"Notify"};
+ static constexpr char const * METHOD_GET_CAPS {"GetCapabilities"};
+ static constexpr char const * METHOD_GET_INFO {"GetServerInformation"};
+ static constexpr char const * SIGNAL_CLOSED {"NotificationClosed"};
+
+ static constexpr char const * HINT_TIMEOUT {"x-canonical-snap-decisions-timeout"};
+
+protected:
+
+ void SetUp()
+ {
+ super::SetUp();
+
+ // init DBusMock / dbus-test-runner
+
+ service = dbus_test_service_new(nullptr);
+
+ GError * error = nullptr;
+ mock = dbus_test_dbus_mock_new(NOTIFY_BUSNAME);
+ obj = dbus_test_dbus_mock_get_object(mock,
+ NOTIFY_PATH,
+ NOTIFY_INTERFACE,
+ &error);
+ g_assert_no_error (error);
+
+ // METHOD_GET_INFO
+ dbus_test_dbus_mock_object_add_method(mock, obj, METHOD_GET_INFO,
+ nullptr,
+ G_VARIANT_TYPE("(ssss)"),
+ "ret = ('mock-notify', 'test vendor', '1.0', '1.1')",
+ &error);
+ g_assert_no_error (error);
+
+ // METHOD_NOTIFY
+ auto str = g_strdup_printf("try:\n"
+ " self.NextNotifyId\n"
+ "except AttributeError:\n"
+ " self.NextNotifyId = %d\n"
+ "ret = self.NextNotifyId\n"
+ "self.NextNotifyId += 1\n",
+ FIRST_NOTIFY_ID);
+ dbus_test_dbus_mock_object_add_method(mock, obj, METHOD_NOTIFY,
+ G_VARIANT_TYPE("(susssasa{sv}i)"),
+ G_VARIANT_TYPE_UINT32,
+ str,
+ &error);
+ g_assert_no_error (error);
+ g_free (str);
+
+ // METHOD_CLOSE
+ str = g_strdup_printf("self.EmitSignal('%s', '%s', 'uu', [ args[0], %d ])",
+ NOTIFY_INTERFACE,
+ SIGNAL_CLOSED,
+ NOTIFICATION_CLOSED_API);
+ dbus_test_dbus_mock_object_add_method(mock, obj, METHOD_CLOSE,
+ G_VARIANT_TYPE("(u)"),
+ nullptr,
+ str,
+ &error);
+ g_assert_no_error (error);
+ g_free (str);
+
+ dbus_test_service_add_task(service, DBUS_TEST_TASK(mock));
+ dbus_test_service_start_tasks(service);
+
+ bus = g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, nullptr);
+ g_dbus_connection_set_exit_on_close(bus, FALSE);
+ g_object_add_weak_pointer(G_OBJECT(bus), reinterpret_cast<gpointer*>(&bus));
+
+ notify_init(APP_NAME);
+ }
+
+ virtual void TearDown()
+ {
+ notify_uninit();
+
+ g_clear_object(&mock);
+ g_clear_object(&service);
+ g_object_unref(bus);
+
+ // wait a little while for the scaffolding to shut down,
+ // but don't block on it forever...
+ unsigned int cleartry = 0;
+ while ((bus != nullptr) && (cleartry < 50))
+ {
+ g_usleep(100000);
+ while (g_main_pending())
+ g_main_iteration(true);
+ cleartry++;
+ }
+
+ super::TearDown();
+ }
+};
+
+/***
+****
+***/
+
+// simple test to confirm the NotifyFixture plumbing all works
+TEST_F(NotifyFixture, HelloWorld)
+{
+}
+
+/***
+****
+***/
+
+
+namespace
+{
+ static constexpr double percent_critical {2.0};
+ static constexpr double percent_very_low {5.0};
+ static constexpr double percent_low {10.0};
+
+ void set_battery_percentage (IndicatorPowerDevice * battery, gdouble p)
+ {
+ g_object_set (battery, INDICATOR_POWER_DEVICE_PERCENTAGE, p, nullptr);
+ }
+}
+
+TEST_F(NotifyFixture, PercentageToLevel)
+{
+ auto battery = indicator_power_device_new ("/object/path",
+ UP_DEVICE_KIND_BATTERY,
+ 50.0,
+ UP_DEVICE_STATE_DISCHARGING,
+ 30);
+
+ // confirm that the power levels trigger at the right percentages
+ for (int i=100; i>=0; --i)
+ {
+ set_battery_percentage (battery, i);
+ const auto level = indicator_power_notifier_get_power_level(battery);
+
+ if (i <= percent_critical)
+ EXPECT_STREQ (POWER_LEVEL_STR_CRITICAL, level);
+ else if (i <= percent_very_low)
+ EXPECT_STREQ (POWER_LEVEL_STR_VERY_LOW, level);
+ else if (i <= percent_low)
+ EXPECT_STREQ (POWER_LEVEL_STR_LOW, level);
+ else
+ EXPECT_STREQ (POWER_LEVEL_STR_OK, level);
+ }
+
+ g_object_unref (battery);
+}
+
+/***
+****
+***/
+
+// scaffolding to monitor PropertyChanged signals
+namespace
+{
+ enum
+ {
+ FIELD_POWER_LEVEL = (1<<0),
+ FIELD_IS_WARNING = (1<<1)
+ };
+
+ struct ChangedParams
+ {
+ std::string power_level = POWER_LEVEL_STR_OK;
+ bool is_warning = false;
+ uint32_t fields = 0;
+ };
+
+ void on_battery_property_changed (GDBusConnection *connection G_GNUC_UNUSED,
+ const gchar *sender_name G_GNUC_UNUSED,
+ const gchar *object_path G_GNUC_UNUSED,
+ const gchar *interface_name G_GNUC_UNUSED,
+ const gchar *signal_name G_GNUC_UNUSED,
+ GVariant *parameters,
+ gpointer gchanged_params)
+ {
+ g_return_if_fail (g_variant_n_children (parameters) == 3);
+ auto dict = g_variant_get_child_value (parameters, 1);
+ g_return_if_fail (g_variant_is_of_type (dict, G_VARIANT_TYPE_DICTIONARY));
+ auto changed_params = static_cast<ChangedParams*>(gchanged_params);
+
+ const char * power_level;
+ if (g_variant_lookup (dict, "PowerLevel", "&s", &power_level, nullptr))
+ {
+ changed_params->power_level = power_level;
+ changed_params->fields |= FIELD_POWER_LEVEL;
+ }
+
+ gboolean is_warning;
+ if (g_variant_lookup (dict, "IsWarning", "b", &is_warning, nullptr))
+ {
+ changed_params->is_warning = is_warning;
+ changed_params->fields |= FIELD_IS_WARNING;
+ }
+
+ g_variant_unref (dict);
+ }
+}
+
+TEST_F(NotifyFixture, LevelsDuringBatteryDrain)
+{
+ auto battery = indicator_power_device_new ("/object/path",
+ UP_DEVICE_KIND_BATTERY,
+ 50.0,
+ UP_DEVICE_STATE_DISCHARGING,
+ 30);
+
+ // set up a notifier and give it the battery so changing the battery's
+ // charge should show up on the bus.
+ auto notifier = indicator_power_notifier_new ();
+ indicator_power_notifier_set_battery (notifier, battery);
+ indicator_power_notifier_set_bus (notifier, bus);
+ wait_msec();
+
+ ChangedParams changed_params;
+ auto sub_tag = g_dbus_connection_signal_subscribe (bus,
+ nullptr,
+ "org.freedesktop.DBus.Properties",
+ "PropertiesChanged",
+ BUS_PATH"/Battery",
+ nullptr,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ on_battery_property_changed,
+ &changed_params,
+ nullptr);
+
+ // confirm that draining the battery puts
+ // the power_level change through its paces
+ for (int i=100; i>=0; --i)
+ {
+ changed_params = ChangedParams();
+ EXPECT_TRUE (changed_params.fields == 0);
+
+ const auto old_level = indicator_power_notifier_get_power_level(battery);
+ set_battery_percentage (battery, i);
+ const auto new_level = indicator_power_notifier_get_power_level(battery);
+ wait_msec();
+
+ if (old_level == new_level)
+ {
+ EXPECT_EQ (0, (changed_params.fields & FIELD_POWER_LEVEL));
+ }
+ else
+ {
+ EXPECT_EQ (FIELD_POWER_LEVEL, (changed_params.fields & FIELD_POWER_LEVEL));
+ EXPECT_EQ (new_level, changed_params.power_level);
+ }
+ }
+
+ // cleanup
+ g_dbus_connection_signal_unsubscribe (bus, sub_tag);
+ g_object_unref (notifier);
+ g_object_unref (battery);
+}
+
+/***
+****
+***/
+
+TEST_F(NotifyFixture, EventsThatChangeNotifications)
+{
+ // GetCapabilities returns an array containing 'actions', so that we'll
+ // get snap decisions and the 'IsWarning' property
+ GError * error = nullptr;
+ dbus_test_dbus_mock_object_add_method (mock,
+ obj,
+ METHOD_GET_CAPS,
+ nullptr,
+ G_VARIANT_TYPE_STRING_ARRAY,
+ "ret = ['actions', 'body']",
+ &error);
+ g_assert_no_error (error);
+
+ auto battery = indicator_power_device_new ("/object/path",
+ UP_DEVICE_KIND_BATTERY,
+ percent_low + 1.0,
+ UP_DEVICE_STATE_DISCHARGING,
+ 30);
+
+ // set up a notifier and give it the battery so changing the battery's
+ // charge should show up on the bus.
+ auto notifier = indicator_power_notifier_new ();
+ indicator_power_notifier_set_battery (notifier, battery);
+ indicator_power_notifier_set_bus (notifier, bus);
+ ChangedParams changed_params;
+ auto sub_tag = g_dbus_connection_signal_subscribe (bus,
+ nullptr,
+ "org.freedesktop.DBus.Properties",
+ "PropertiesChanged",
+ BUS_PATH"/Battery",
+ nullptr,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ on_battery_property_changed,
+ &changed_params,
+ nullptr);
+
+ // test setup case
+ wait_msec();
+ EXPECT_STREQ (POWER_LEVEL_STR_OK, changed_params.power_level.c_str());
+
+ // change the percent past the 'low' threshold and confirm that
+ // a) the power level changes
+ // b) we get a notification
+ changed_params = ChangedParams();
+ set_battery_percentage (battery, percent_low);
+ wait_msec();
+ EXPECT_EQ (FIELD_POWER_LEVEL|FIELD_IS_WARNING, changed_params.fields);
+ EXPECT_EQ (indicator_power_notifier_get_power_level(battery), changed_params.power_level);
+ EXPECT_TRUE (changed_params.is_warning);
+
+ // now test that the warning changes if the level goes down even lower...
+ changed_params = ChangedParams();
+ set_battery_percentage (battery, percent_very_low);
+ wait_msec();
+ EXPECT_EQ (FIELD_POWER_LEVEL, changed_params.fields);
+ EXPECT_STREQ (POWER_LEVEL_STR_VERY_LOW, changed_params.power_level.c_str());
+
+ // ...and that the warning is taken down if the battery is plugged back in...
+ changed_params = ChangedParams();
+ g_object_set (battery, INDICATOR_POWER_DEVICE_STATE, UP_DEVICE_STATE_CHARGING, nullptr);
+ wait_msec();
+ EXPECT_EQ (FIELD_IS_WARNING, changed_params.fields);
+ EXPECT_FALSE (changed_params.is_warning);
+
+ // ...and that it comes back if we unplug again...
+ changed_params = ChangedParams();
+ g_object_set (battery, INDICATOR_POWER_DEVICE_STATE, UP_DEVICE_STATE_DISCHARGING, nullptr);
+ wait_msec();
+ EXPECT_EQ (FIELD_IS_WARNING, changed_params.fields);
+ EXPECT_TRUE (changed_params.is_warning);
+
+ // ...and that it's taken down if the power level is OK
+ changed_params = ChangedParams();
+ set_battery_percentage (battery, percent_low+1);
+ wait_msec();
+ EXPECT_EQ (FIELD_POWER_LEVEL|FIELD_IS_WARNING, changed_params.fields);
+ EXPECT_STREQ (POWER_LEVEL_STR_OK, changed_params.power_level.c_str());
+ EXPECT_FALSE (changed_params.is_warning);
+
+ // cleanup
+ g_dbus_connection_signal_unsubscribe (bus, sub_tag);
+ g_object_unref (notifier);
+ g_object_unref (battery);
+}