aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt50
-rw-r--r--src/Makefile.am81
-rw-r--r--src/dbus-shared.h28
-rw-r--r--src/device-provider-upower.c10
-rw-r--r--src/device-provider-upower.h2
-rw-r--r--src/device-provider.c2
-rw-r--r--src/device.c492
-rw-r--r--src/device.h33
-rw-r--r--src/ib-brightness-control.c6
-rw-r--r--src/ib-brightness-uscreen-control.c202
-rw-r--r--src/ib-brightness-uscreen-control.h43
-rw-r--r--src/main.c10
-rw-r--r--src/notifier.c445
-rw-r--r--src/notifier.h74
-rw-r--r--src/service.c351
15 files changed, 1438 insertions, 391 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..7a4a297
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,50 @@
+set (SERVICE_LIB "indicatorpowerservice")
+set (SERVICE_EXEC "indicator-power-service")
+
+add_definitions(-DG_LOG_DOMAIN="Indicator-Power")
+
+# handwritten sources
+set(SERVICE_MANUAL_SOURCES
+ device-provider-upower.c
+ ib-brightness-control.c
+ ib-brightness-uscreen-control.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-upower
+ org.freedesktop
+ Dbus
+ ${CMAKE_CURRENT_SOURCE_DIR}/org.freedesktop.UPower.xml)
+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-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/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..400a060 100644
--- a/src/device-provider-upower.c
+++ b/src/device-provider-upower.c
@@ -17,8 +17,6 @@
* 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"
@@ -60,7 +58,7 @@ G_DEFINE_TYPE_WITH_CODE (
indicator_power_device_provider_upower,
G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (INDICATOR_TYPE_POWER_DEVICE_PROVIDER,
- indicator_power_device_provider_interface_init));
+ indicator_power_device_provider_interface_init))
/***
**** UPOWER DBUS
@@ -102,7 +100,7 @@ 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;
GVariant * dict = g_variant_get_child_value (response, 0);
@@ -120,7 +118,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 +127,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),
diff --git a/src/device-provider-upower.h b/src/device-provider-upower.h
index 7bdd5d5..7bfecd9 100644
--- a/src/device-provider-upower.h
+++ b/src/device-provider-upower.h
@@ -65,6 +65,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 7f1b14f..eff76d1 100644
--- a/src/device.c
+++ b/src/device.c
@@ -37,9 +37,12 @@ struct _IndicatorPowerDevicePrivate
gchar * object_path;
gdouble percentage;
time_t time;
-};
-#define INDICATOR_POWER_DEVICE_GET_PRIVATE(o) (INDICATOR_POWER_DEVICE(o)->priv)
+ /* Timestamp of when when we first noticed that upower couldn't estimate
+ the time-remaining field for this device, or 0 if not applicable.
+ This is used when generating the time-remaining string. */
+ GTimer * inestimable;
+};
/* Properties */
/* Enum for the properties so that they can be quickly found and looked up. */
@@ -64,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
@@ -136,6 +139,11 @@ indicator_power_device_init (IndicatorPowerDevice *self)
static void
indicator_power_device_dispose (GObject *object)
{
+ IndicatorPowerDevice * self = INDICATOR_POWER_DEVICE(object);
+ IndicatorPowerDevicePrivate * p = self->priv;
+
+ g_clear_pointer (&p->inestimable, g_timer_destroy);
+
G_OBJECT_CLASS (indicator_power_device_parent_class)->dispose (object);
}
@@ -179,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:
@@ -192,35 +200,54 @@ static void
set_property (GObject * o, guint prop_id, const GValue * value, GParamSpec * pspec)
{
IndicatorPowerDevice * self = INDICATOR_POWER_DEVICE(o);
- IndicatorPowerDevicePrivate * priv = self->priv;
+ IndicatorPowerDevicePrivate * p = self->priv;
switch (prop_id)
{
case PROP_KIND:
- priv->kind = g_value_get_int (value);
+ p->kind = (UpDeviceKind) g_value_get_int (value);
break;
case PROP_STATE:
- priv->state = g_value_get_int (value);
+ p->state = (UpDeviceState) g_value_get_int (value);
break;
case PROP_OBJECT_PATH:
- g_free (priv->object_path);
- priv->object_path = g_value_dup_string (value);
+ g_free (p->object_path);
+ p->object_path = g_value_dup_string (value);
break;
case PROP_PERCENTAGE:
- priv->percentage = g_value_get_double (value);
+ p->percentage = g_value_get_double (value);
break;
case PROP_TIME:
- priv->time = g_value_get_uint64(value);
+ p->time = (time_t) g_value_get_uint64(value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(o, prop_id, pspec);
break;
}
+
+ /**
+ * Check to see if the time-remaining value is estimable.
+ * When it first becomes inestimable, kick off a timer because
+ * we need to track that to generate the appropriate title text.
+ */
+
+ const gboolean is_inestimable = (p->time == 0)
+ && (p->state != UP_DEVICE_STATE_FULLY_CHARGED)
+ && (p->percentage > 0);
+
+ if (!is_inestimable)
+ {
+ g_clear_pointer (&p->inestimable, g_timer_destroy);
+ }
+ else if (p->inestimable == NULL)
+ {
+ p->inestimable = g_timer_new ();
+ }
}
/***
@@ -326,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):
@@ -387,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));
@@ -442,57 +456,6 @@ indicator_power_device_get_gicon (const IndicatorPowerDevice * device)
****
***/
-/* Format time remaining for reading ("H:MM") and speech ("H hours, MM minutes") */
-static void
-get_timestring (guint64 time_secs,
- gchar **readable_timestring,
- gchar **accessible_timestring)
-{
- gint hours;
- gint minutes;
-
- /* Add 0.5 to do rounding */
- minutes = (int) ( ( time_secs / 60.0 ) + 0.5 );
-
- if (minutes == 0)
- {
- *readable_timestring = g_strdup (_("Unknown time"));
- *accessible_timestring = g_strdup (_("Unknown time"));
-
- return;
- }
-
- if (minutes < 60)
- {
- *readable_timestring = g_strdup_printf ("0:%.2i", minutes);
- *accessible_timestring = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "%i minute",
- "%i minutes",
- minutes), minutes);
- return;
- }
-
- hours = minutes / 60;
- minutes = minutes % 60;
-
- *readable_timestring = g_strdup_printf ("%i:%.2i", hours, minutes);
-
- if (minutes == 0)
- {
- *accessible_timestring = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE,
- "%i hour",
- "%i hours",
- hours), hours);
- }
- else
- {
- /* TRANSLATOR: "%i %s %i %s" are "%i hours %i minutes"
- * Swap order with "%2$s %2$i %1$s %1$i if needed */
- *accessible_timestring = g_strdup_printf (_("%i %s %i %s"),
- hours, g_dngettext (GETTEXT_PACKAGE, "hour", "hours", hours),
- minutes, g_dngettext (GETTEXT_PACKAGE, "minute", "minutes", minutes));
- }
-}
-
static const gchar *
device_kind_to_localised_string (UpDeviceKind kind)
{
@@ -555,149 +518,314 @@ device_kind_to_localised_string (UpDeviceKind kind)
return text;
}
+/**
+ * The '''brief time-remaining string''' for a component should be:
+ * * the time remaining for it to empty or fully charge,
+ * if estimable, in H:MM format; otherwise
+ * * “estimating…” if the time remaining has been inestimable for
+ * less than 30 seconds; otherwise
+ * * “unknown” if the time remaining has been inestimable for
+ * between 30 seconds and one minute; otherwise
+ * * the empty string.
+ */
static char *
-join_strings (const char * name, const char * time, const char * percent)
+get_brief_time_remaining (const IndicatorPowerDevice * device)
{
- char * str;
- const gboolean have_name = name && *name;
- const gboolean have_time = time && *time;
- const gboolean have_percent = percent && *percent;
-
- if (have_name && have_time && have_percent)
- str = g_strdup_printf (_("%s (%s, %s)"), name, time, percent);
- else if (have_name && have_time)
- str = g_strdup_printf (_("%s (%s)"), name, time);
- else if (have_name && have_percent)
- str = g_strdup_printf (_("%s (%s)"), name, percent);
- else if (have_name)
- str = g_strdup (name);
- else if (have_time && have_percent)
- str = g_strdup_printf (_("(%s, %s)"), time, percent);
- else if (have_time)
- str = g_strdup_printf (_("(%s)"), time);
- else if (have_percent)
- str = g_strdup_printf (_("(%s)"), percent);
- else
- str = g_strdup ("");
+ gchar * str = NULL;
+ const IndicatorPowerDevicePrivate * p = device->priv;
+
+ if (p->time > 0)
+ {
+ int minutes = p->time / 60;
+ const int hours = minutes / 60;
+ minutes %= 60;
+
+ str = g_strdup_printf("%0d:%02d", hours, minutes);
+ }
+ else if (p->inestimable != NULL)
+ {
+ const double elapsed = g_timer_elapsed (p->inestimable, NULL);
+
+ if (elapsed < 30)
+ {
+ str = g_strdup_printf (_("estimating…"));
+ }
+ else if (elapsed < 60)
+ {
+ str = g_strdup_printf (_("unknown"));
+ }
+ }
return str;
}
-static void
-indicator_power_device_get_text (const IndicatorPowerDevice * device,
- gboolean show_time_in_header,
- gboolean show_percentage_in_header,
- gchar ** header,
- gchar ** label,
- gchar ** a11y)
+/**
+ * The '''expanded time-remaining string''' for a component should
+ * be the same as the brief time-remaining string, except that if
+ * the time is estimable:
+ * * if the component is charging, it should be “H:MM to charge”
+ * * if the component is discharging, it should be “H:MM left”.
+ */
+static char*
+get_expanded_time_remaining (const IndicatorPowerDevice * device)
{
- if (!INDICATOR_IS_POWER_DEVICE(device))
+ char * str = NULL;
+ const IndicatorPowerDevicePrivate * p = device->priv;
+
+ 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;
+ minutes %= 60;
+
+ 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);
+ }
+ }
+ else
{
- if (a11y != NULL) *a11y = NULL;
- if (label != NULL) *label = NULL;
- if (header != NULL) *header = NULL;
- g_warning ("%s: %p is not an IndicatorPowerDevice", G_STRFUNC, device);
- return;
+ str = get_brief_time_remaining (device);
}
- const time_t time = indicator_power_device_get_time (device);
- const UpDeviceState state = indicator_power_device_get_state (device);
- const UpDeviceKind kind = indicator_power_device_get_kind (device);
- const gchar * device_name = device_kind_to_localised_string (kind);
- const gdouble percentage = indicator_power_device_get_percentage (device);
- char pctstr[32] = { '\0' };
- g_snprintf (pctstr, sizeof(pctstr), "%.0lf%%", percentage);
+ return str;
+}
- GString * terse_time = g_string_new (NULL);
- GString * verbose_time = g_string_new (NULL);
- GString * accessible_time = g_string_new (NULL);
+/**
+ * The '''accessible time-remaining string''' for a component
+ * should be the same as the expanded time-remaining string,
+ * except the H:MM time should be rendered as “''H'' hours ''M'' minutes”,
+ * or just as “''M'' minutes” if the time is less than one hour.
+ */
+static char *
+get_accessible_time_remaining (const IndicatorPowerDevice * device)
+{
+ char * str = NULL;
+ const IndicatorPowerDevicePrivate * p = device->priv;
- if (time > 0)
+ if (p->time && ((p->state == UP_DEVICE_STATE_CHARGING) || (p->state == UP_DEVICE_STATE_DISCHARGING)))
{
- char * readable_timestr = NULL;
- char * accessible_timestr = NULL;
- get_timestring (time, &readable_timestr, &accessible_timestr);
+ guint minutes = (guint)p->time / 60u;
+ const guint hours = minutes / 60u;
+ minutes %= 60;
- if (state == UP_DEVICE_STATE_CHARGING)
- {
- g_string_assign (terse_time, readable_timestr);
- g_string_printf (verbose_time, _("%s to charge"), readable_timestr);
- g_string_printf (accessible_time, _("%s to charge"), accessible_timestr);
- }
- else if ((state == UP_DEVICE_STATE_DISCHARGING) && (time <= (60*60*24)))
+ if (p->state == UP_DEVICE_STATE_CHARGING)
{
- g_string_assign (terse_time, readable_timestr);
- g_string_printf (verbose_time, _("%s left"), readable_timestr);
- g_string_printf (accessible_time, _("%s left"), accessible_timestr);
+ if (hours > 0)
+ {
+ /* 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
+ {
+ /* 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
+ else // discharging
{
- /* if there's more than 24 hours remaining, we don't show it */
+ if (hours > 0)
+ {
+ /* 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));
+ }
}
-
- g_free (readable_timestr);
- g_free (accessible_timestr);
}
- else if (state == UP_DEVICE_STATE_FULLY_CHARGED)
+ else
{
- g_string_assign (verbose_time, _("charged"));
- g_string_assign (accessible_time, _("charged"));
+ str = get_brief_time_remaining (device);
}
- else if (percentage > 0)
+
+ return str;
+}
+
+/**
+ * The time is relevant for a device if either (a) the component is charging,
+ * or (b) the component is discharging and the estimated time is less than
+ * 24 hours. (A time greater than 24 hours is probably a mistaken calculation.)
+ */
+static gboolean
+time_is_relevant (const IndicatorPowerDevice * device)
+{
+ const IndicatorPowerDevicePrivate * p = device->priv;
+
+ if (p->state == UP_DEVICE_STATE_CHARGING)
+ return TRUE;
+
+ if ((p->state == UP_DEVICE_STATE_DISCHARGING) && (p->time<(24*60*60)))
+ return TRUE;
+
+ return FALSE;
+}
+
+/**
+ * The menu item for each chargeable component should consist of ...
+ * Text representing the name of the component (“Battery”, “Mouse”,
+ * “UPS”, “Alejandra’s iPod”, etc) and the charge status in brackets:
+ *
+ * * “X (charged)” if it is fully charged and not discharging;
+ * * “X (expanded time-remaining string)” if it is charging,
+ * or discharging with less than 24 hours left;
+ * * “X” if it is discharging with 24 hours or more left.
+ *
+ * The accessible label for the menu item should be the same as the
+ * visible label, except with the accessible time-remaining string
+ * instead of the expanded time-remaining string.
+ */
+static char *
+get_menuitem_text (const IndicatorPowerDevice * device,
+ gboolean accessible)
+{
+ char * str = NULL;
+ const IndicatorPowerDevicePrivate * p = device->priv;
+ const char * kind_str = device_kind_to_localised_string (p->kind);
+
+ if (p->state == UP_DEVICE_STATE_FULLY_CHARGED)
{
- g_string_assign (terse_time, _("estimating…"));
- g_string_assign (verbose_time, _("estimating…"));
- g_string_assign (accessible_time, _("estimating…"));
+ /* TRANSLATORS: example: "battery (charged)" */
+ str = g_strdup_printf (_("%s (charged)"), kind_str);
}
else
{
- *pctstr = '\0';
+ char * time_str = NULL;
+
+ if (time_is_relevant (device))
+ {
+ if (accessible)
+ time_str = get_accessible_time_remaining (device);
+ else
+ time_str = get_expanded_time_remaining (device);
+ }
- if (kind != UP_DEVICE_KIND_LINE_POWER)
+ if (time_str && *time_str)
{
- g_string_assign (verbose_time, _("not present"));
- g_string_assign (accessible_time, _("not present"));
+ /* TRANSLATORS: example: "battery (time remaining)" */
+ str = g_strdup_printf (_("%s (%s)"), kind_str, time_str);
}
+ else
+ {
+ str = g_strdup (kind_str);
+ }
+
+ g_free (time_str);
}
- if (header != NULL)
- *header = join_strings (NULL,
- show_time_in_header ? terse_time->str : "",
- show_percentage_in_header ? pctstr : "");
+ return str;
+}
+
+char *
+indicator_power_device_get_readable_text (const IndicatorPowerDevice * device)
+{
+ g_return_val_if_fail (INDICATOR_IS_POWER_DEVICE(device), NULL);
- if (label != NULL)
- *label = join_strings (device_name,
- verbose_time->str,
- NULL);
+ return get_menuitem_text (device, FALSE);
+}
- if (a11y != NULL)
- *a11y = join_strings (device_name,
- accessible_time->str,
- pctstr);
+char *
+indicator_power_device_get_accessible_text (const IndicatorPowerDevice * device)
+{
+ g_return_val_if_fail (INDICATOR_IS_POWER_DEVICE(device), NULL);
- g_string_free (terse_time, TRUE);
- g_string_free (verbose_time, TRUE);
- g_string_free (accessible_time, TRUE);
+ return get_menuitem_text (device, TRUE);
}
-gchar *
-indicator_power_device_get_label (const IndicatorPowerDevice * device)
+/**
+ * If the time is relevant and/or “Show Percentage in Menu Bar” is checked,
+ * the icon should be followed by brackets.
+ *
+ * If the time is relevant, the brackets should contain the time-remaining
+ * string for that component.
+ *
+ * If “Show Percentage in Menu Bar” is checked (as it should not be by default),
+ * the brackets should contain the percentage charge for that device.
+ *
+ * If both conditions are true, the time and percentage should be separated by a space.
+ */
+char*
+indicator_power_device_get_readable_title (const IndicatorPowerDevice * device,
+ gboolean want_time,
+ gboolean want_percent)
{
- gchar * label = NULL;
- indicator_power_device_get_text (device, FALSE, FALSE,
- NULL, &label, NULL);
- return label;
+ char * str = NULL;
+ char * time_str = NULL;
+ const IndicatorPowerDevicePrivate * p;
+
+ g_return_val_if_fail (INDICATOR_IS_POWER_DEVICE(device), NULL);
+
+ p = device->priv;
+
+ // if we can't provide time-remaining, turn off the time flag
+ if (want_time && !time_is_relevant (device))
+ want_time = FALSE;
+
+ // if we can't provide percent, turn off the percent flag
+ if (p->percentage < 0.01)
+ want_percent = FALSE;
+
+ // try to build the time-remaining string
+ if (want_time)
+ {
+ time_str = get_brief_time_remaining (device);
+ want_time = time_str && *time_str;
+ }
+
+ 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
+ {
+ str = NULL;
+ }
+
+ g_free (time_str);
+ return str;
}
-void
-indicator_power_device_get_header (const IndicatorPowerDevice * device,
- gboolean show_time,
- gboolean show_percentage,
- gchar ** header,
- gchar ** a11y)
+/**
+ * Regardless, the accessible name for the whole menu title should be the same
+ * as the accessible name for that thing’s component inside the menu itself.
+ */
+char *
+indicator_power_device_get_accessible_title (const IndicatorPowerDevice * device,
+ gboolean want_time G_GNUC_UNUSED,
+ gboolean want_percent G_GNUC_UNUSED)
{
- indicator_power_device_get_text (device, show_time, show_percentage,
- header, NULL, a11y);
+ g_return_val_if_fail (INDICATOR_IS_POWER_DEVICE(device), NULL);
+
+ return indicator_power_device_get_accessible_text (device);
}
/***
@@ -745,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 1f395a1..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
@@ -116,22 +116,27 @@ IndicatorPowerDevice* indicator_power_device_new (const gchar * object_path,
IndicatorPowerDevice* indicator_power_device_new_from_variant (GVariant * variant);
-UpDeviceKind indicator_power_device_get_kind (const IndicatorPowerDevice * device);
-UpDeviceState indicator_power_device_get_state (const IndicatorPowerDevice * device);
-const gchar * indicator_power_device_get_object_path (const IndicatorPowerDevice * device);
-gdouble indicator_power_device_get_percentage (const IndicatorPowerDevice * device);
-time_t indicator_power_device_get_time (const IndicatorPowerDevice * device);
+UpDeviceKind indicator_power_device_get_kind (const IndicatorPowerDevice * device);
+UpDeviceState indicator_power_device_get_state (const IndicatorPowerDevice * device);
+const gchar * indicator_power_device_get_object_path (const IndicatorPowerDevice * device);
+gdouble indicator_power_device_get_percentage (const IndicatorPowerDevice * device);
+time_t indicator_power_device_get_time (const IndicatorPowerDevice * device);
-GStrv indicator_power_device_get_icon_names (const IndicatorPowerDevice * device);
-GIcon * indicator_power_device_get_gicon (const IndicatorPowerDevice * device);
+GStrv indicator_power_device_get_icon_names (const IndicatorPowerDevice * device);
+GIcon * indicator_power_device_get_gicon (const IndicatorPowerDevice * device);
-gchar * indicator_power_device_get_label (const IndicatorPowerDevice * device);
-void indicator_power_device_get_header (const IndicatorPowerDevice * device,
- gboolean show_time,
- gboolean show_percentage,
- gchar ** header,
- gchar ** a11y);
+char * indicator_power_device_get_readable_text (const IndicatorPowerDevice * device);
+
+char * indicator_power_device_get_accessible_text (const IndicatorPowerDevice * device);
+
+char * indicator_power_device_get_readable_title (const IndicatorPowerDevice * device,
+ gboolean want_time,
+ gboolean want_percent);
+
+char * indicator_power_device_get_accessible_title (const IndicatorPowerDevice * device,
+ gboolean want_time,
+ gboolean want_percent);
G_END_DECLS
diff --git a/src/ib-brightness-control.c b/src/ib-brightness-control.c
index 4fb6bc5..67da10c 100644
--- a/src/ib-brightness-control.c
+++ b/src/ib-brightness-control.c
@@ -76,7 +76,7 @@ ib_brightness_control_set_value (IbBrightnessControl* self, gint value)
gint fd;
gchar *filename;
gchar *svalue;
- gint length;
+ size_t length;
gint err;
if (self->path == NULL)
@@ -95,7 +95,7 @@ ib_brightness_control_set_value (IbBrightnessControl* self, gint value)
err = errno;
errno = 0;
- if (write (fd, svalue, length) != length) {
+ if (write (fd, svalue, length) != (ssize_t)length) {
g_warning ("Fail to write brightness information: %s", g_strerror(errno));
}
errno = err;
@@ -105,7 +105,7 @@ ib_brightness_control_set_value (IbBrightnessControl* self, gint value)
g_free (filename);
}
-gint
+static gint
ib_brightness_control_get_value_from_file (IbBrightnessControl *self, const gchar *file)
{
GError *error;
diff --git a/src/ib-brightness-uscreen-control.c b/src/ib-brightness-uscreen-control.c
new file mode 100644
index 0000000..ad2c155
--- /dev/null
+++ b/src/ib-brightness-uscreen-control.c
@@ -0,0 +1,202 @@
+/*
+ * 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:
+ * Yuan-Chen Cheng <yc.cheng@canonical.com>
+ */
+
+#include "ib-brightness-uscreen-control.h"
+
+static gboolean getBrightnessParams(GDBusProxy* powerd_proxy, int *dim, int *min,
+ int *max, int *dflt, gboolean *ab_supported);
+
+GDBusProxy*
+uscreen_get_proxy(brightness_params_t *params)
+{
+ GError *error = NULL;
+ gboolean ret;
+
+ g_return_val_if_fail (params != NULL, NULL);
+
+ /* For now we still need to obtain the brigthness params from powerd */
+ GDBusProxy* powerd_proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ "com.canonical.powerd",
+ "/com/canonical/powerd",
+ "com.canonical.powerd",
+ NULL,
+ &error);
+
+ if (error != NULL)
+ {
+ g_debug ("could not connect to powerd: %s", error->message);
+ g_error_free (error);
+ return NULL;
+ }
+
+ ret = getBrightnessParams(powerd_proxy, &(params->dim), &(params->min),
+ &(params->max), &(params->dflt), &(params->ab_supported));
+
+ if (! ret)
+ {
+ g_debug ("can't get brightness parameters from powerd");
+ g_object_unref (powerd_proxy);
+ return NULL;
+ }
+
+ g_clear_object (&powerd_proxy);
+
+ GDBusProxy* uscreen_proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ "com.canonical.Unity.Screen",
+ "/com/canonical/Unity/Screen",
+ "com.canonical.Unity.Screen",
+ NULL,
+ &error);
+
+ if (error != NULL)
+ {
+ g_debug ("could not connect to unity screen: %s", error->message);
+ g_error_free (error);
+ return NULL;
+ }
+
+ return uscreen_proxy;
+}
+
+
+static gboolean
+getBrightnessParams(GDBusProxy* powerd_proxy, int *dim, int *min, int *max, int *dflt, gboolean *ab_supported)
+{
+ GVariant *ret = NULL;
+ GError *error = NULL;
+
+ ret = g_dbus_proxy_call_sync(powerd_proxy,
+ "getBrightnessParams",
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ 400, NULL, &error); // timeout: 400 ms
+ if (!ret)
+ {
+ if (error != NULL)
+ {
+ if (!g_error_matches(error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN))
+ {
+ g_warning("getBrightnessParams from powerd failed: %s", error->message);
+ }
+ g_error_free(error);
+ }
+ return FALSE;
+ }
+
+ g_variant_get(ret, "((iiiib))", dim, min, max, dflt, ab_supported);
+ g_variant_unref(ret);
+ return TRUE;
+}
+
+static gboolean setUserBrightness(GDBusProxy* uscreen_proxy, GCancellable *gcancel, int brightness)
+{
+ GVariant *ret = NULL;
+ GError *error = NULL;
+
+ ret = g_dbus_proxy_call_sync(uscreen_proxy,
+ "setUserBrightness",
+ g_variant_new("(i)", brightness),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, gcancel, &error);
+ if (!ret) {
+ g_warning("setUserBrightness via unity.screen failed: %s", error->message);
+ g_error_free(error);
+ return FALSE;
+ } else {
+ g_variant_unref(ret);
+ return TRUE;
+ }
+}
+
+struct _IbBrightnessUScreenControl
+{
+ GDBusProxy *uscreen_proxy;
+ GCancellable *gcancel;
+
+ int dim;
+ int min;
+ int max;
+ int dflt; // defalut value
+ gboolean ab_supported;
+
+ int current;
+};
+
+IbBrightnessUscreenControl*
+ib_brightness_uscreen_control_new (GDBusProxy* uscreen_proxy, brightness_params_t params)
+{
+ IbBrightnessUscreenControl *control;
+
+ control = g_new0 (IbBrightnessUscreenControl, 1);
+ control->uscreen_proxy = uscreen_proxy;
+ control->gcancel = g_cancellable_new();
+
+ control->dim = params.dim;
+ control->min = params.min;
+ control->max = params.max;
+ control->dflt = params.dflt;
+ control->ab_supported = params.ab_supported;
+
+ // XXX: set the brightness value is the only way to sync the brightness value with
+ // unity.screen, and we should set the user prefered / last set brightness value upon startup.
+ // Before we have code to store last set brightness value or other mechanism, we set
+ // it to default brightness that powerd proposed.
+ ib_brightness_uscreen_control_set_value(control, control->dflt);
+
+ return control;
+}
+
+void
+ib_brightness_uscreen_control_set_value (IbBrightnessUscreenControl* self, gint value)
+{
+ gboolean ret;
+
+ value = CLAMP(value, self->min, self->max);
+ ret = setUserBrightness(self->uscreen_proxy, self->gcancel, value);
+ if (ret)
+ {
+ self->current = value;
+ }
+}
+
+gint
+ib_brightness_uscreen_control_get_value (IbBrightnessUscreenControl* self)
+{
+ return self->current;
+}
+
+gint
+ib_brightness_uscreen_control_get_max_value (IbBrightnessUscreenControl* self)
+{
+ return self->max;
+}
+
+void
+ib_brightness_uscreen_control_free (IbBrightnessUscreenControl *self)
+{
+ g_cancellable_cancel (self->gcancel);
+ g_object_unref (self->gcancel);
+ g_object_unref (self->uscreen_proxy);
+ g_free (self);
+}
+
diff --git a/src/ib-brightness-uscreen-control.h b/src/ib-brightness-uscreen-control.h
new file mode 100644
index 0000000..3d026a9
--- /dev/null
+++ b/src/ib-brightness-uscreen-control.h
@@ -0,0 +1,43 @@
+/*
+ * 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:
+ * Y.C Cheng <yc.cheng@canonical.com>
+ */
+
+#ifndef __IB_BRIGHTNESS_USCREEN_CONTROL_H__
+#define __IB_BRIGHTNESS_USCREEN_CONTROL_H__
+
+#include <gio/gio.h>
+
+typedef struct {
+ int dim;
+ int min;
+ int max;
+ int dflt;
+ gboolean ab_supported;
+} brightness_params_t;
+
+GDBusProxy* uscreen_get_proxy(brightness_params_t *);
+
+typedef struct _IbBrightnessUScreenControl IbBrightnessUscreenControl;
+
+IbBrightnessUscreenControl* ib_brightness_uscreen_control_new (GDBusProxy* uscreen_proxy, brightness_params_t params);
+void ib_brightness_uscreen_control_set_value (IbBrightnessUscreenControl* self, gint value);
+gint ib_brightness_uscreen_control_get_value (IbBrightnessUscreenControl* self);
+gint ib_brightness_uscreen_control_get_max_value (IbBrightnessUscreenControl* self);
+void ib_brightness_uscreen_control_free (IbBrightnessUscreenControl *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..81cd6f1
--- /dev/null
+++ b/src/notifier.c
@@ -0,0 +1,445 @@
+/*
+ * 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 <libnotify/notify.h>
+
+#include <glib/gi18n.h>
+
+#define HINT_INTERACTIVE "x-canonical-switch-to-application"
+
+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;
+
+/**
+***
+**/
+
+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;
+ }
+}
+
+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
+notification_show(IndicatorPowerNotifier * self)
+{
+ priv_t * const p = get_priv(self);
+ gdouble pct;
+ char * body;
+ NotifyNotification * nn;
+ GError * error;
+
+ notification_clear(self);
+
+ /* create the notification */
+ pct = indicator_power_device_get_percentage(p->battery);
+ body = g_strdup_printf(_("%.0f%% charge remaining"), pct);
+ nn = notify_notification_new(_("Battery Low"), body, NULL);
+ g_free (body);
+ /*notify_notification_set_hint(nn, HINT_INTERACTIVE, g_variant_new_boolean(TRUE));*/
+
+ /* 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++)
+ {
+ if (!notify_init("indicator-power-service"))
+ {
+ g_critical("Unable to initialize libnotify! Notifications might not be shown.");
+ }
+ }
+}
+
+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/service.c b/src/service.c
index 982a24e..0cd448b 100644
--- a/src/service.c
+++ b/src/service.c
@@ -18,15 +18,16 @@
* 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 "dbus-shared.h"
#include "device.h"
#include "device-provider.h"
+#include "notifier.h"
#include "ib-brightness-control.h"
+#include "ib-brightness-uscreen-control.h"
#include "service.h"
#define BUS_NAME "com.canonical.indicator.power"
@@ -104,6 +105,7 @@ struct _IndicatorPowerServicePrivate
GSettings * settings;
IbBrightnessControl * brightness_control;
+ IbBrightnessUscreenControl * brightness_uscreen_control;
guint own_id;
guint actions_export_id;
@@ -120,6 +122,7 @@ struct _IndicatorPowerServicePrivate
GList * devices; /* IndicatorPowerDevice */
IndicatorPowerDeviceProvider * device_provider;
+ IndicatorPowerNotifier * notifier;
};
typedef IndicatorPowerServicePrivate priv_t;
@@ -312,21 +315,7 @@ static GVariant *
create_header_state (IndicatorPowerService * self)
{
GVariantBuilder b;
- gchar * label = NULL;
- gchar * a11y = NULL;
- GIcon * icon = NULL;
- priv_t * p = self->priv;
-
- if (p->primary_device != NULL)
- {
- indicator_power_device_get_header (p->primary_device,
- g_settings_get_boolean (p->settings, SETTINGS_SHOW_TIME_S),
- g_settings_get_boolean (p->settings, SETTINGS_SHOW_PERCENTAGE_S),
- &label,
- &a11y);
-
- icon = indicator_power_device_get_gicon (p->primary_device);
- }
+ const priv_t * const p = self->priv;
g_variant_builder_init (&b, G_VARIANT_TYPE("a{sv}"));
@@ -335,24 +324,48 @@ create_header_state (IndicatorPowerService * self)
g_variant_builder_add (&b, "{sv}", "visible",
g_variant_new_boolean (should_be_visible (self)));
- if (label != NULL)
- g_variant_builder_add (&b, "{sv}", "label", g_variant_new_take_string (label));
-
- if (icon != NULL)
+ if (p->primary_device != NULL)
{
- GVariant * v;
+ char * title;
+ GIcon * icon;
+ const gboolean want_time = g_settings_get_boolean (p->settings, SETTINGS_SHOW_TIME_S);
+ const gboolean want_percent = g_settings_get_boolean (p->settings, SETTINGS_SHOW_PERCENTAGE_S);
+
+ title = indicator_power_device_get_readable_title (p->primary_device,
+ want_time,
+ want_percent);
+ if (title)
+ {
+ if (*title)
+ g_variant_builder_add (&b, "{sv}", "label", g_variant_new_take_string (title));
+ else
+ g_free (title);
+ }
- if ((v = g_icon_serialize (icon)))
+ title = indicator_power_device_get_accessible_title (p->primary_device,
+ want_time,
+ want_percent);
+ if (title)
{
- g_variant_builder_add (&b, "{sv}", "icon", v);
- g_variant_unref (v);
+ if (*title)
+ g_variant_builder_add (&b, "{sv}", "accessible-desc", g_variant_new_take_string (title));
+ else
+ g_free (title);
}
- g_object_unref (icon);
- }
+ if ((icon = indicator_power_device_get_gicon (p->primary_device)))
+ {
+ GVariant * serialized_icon = g_icon_serialize (icon);
- if (a11y != NULL)
- g_variant_builder_add (&b, "{sv}", "accessible-desc", g_variant_new_take_string (a11y));
+ if (serialized_icon != NULL)
+ {
+ g_variant_builder_add (&b, "{sv}", "icon", serialized_icon);
+ g_variant_unref (serialized_icon);
+ }
+
+ g_object_unref (icon);
+ }
+ }
return g_variant_builder_end (&b);
}
@@ -365,7 +378,7 @@ create_header_state (IndicatorPowerService * self)
***/
static void
-append_device_to_menu (GMenu * menu, const IndicatorPowerDevice * device)
+append_device_to_menu (GMenu * menu, const IndicatorPowerDevice * device, int profile)
{
const UpDeviceKind kind = indicator_power_device_get_kind (device);
@@ -375,24 +388,33 @@ append_device_to_menu (GMenu * menu, const IndicatorPowerDevice * device)
GMenuItem * item;
GIcon * icon;
- label = indicator_power_device_get_label (device);
- item = g_menu_item_new (label, "indicator.activate-statistics");
+ label = indicator_power_device_get_readable_text (device);
+ item = g_menu_item_new (label, NULL);
g_free (label);
- g_menu_item_set_action_and_target(item, "indicator.activate-statistics", "s",
- indicator_power_device_get_object_path (device));
+
+ g_menu_item_set_attribute (item, "x-canonical-type", "s", "com.canonical.indicator.basic");
if ((icon = indicator_power_device_get_gicon (device)))
{
- GVariant * v;
- if ((v = g_icon_serialize (icon)))
+ GVariant * serialized_icon = g_icon_serialize (icon);
+
+ if (serialized_icon != NULL)
{
- g_menu_item_set_attribute_value (item, G_MENU_ATTRIBUTE_ICON, v);
- g_variant_unref (v);
+ g_menu_item_set_attribute_value (item,
+ G_MENU_ATTRIBUTE_ICON,
+ serialized_icon);
+ g_variant_unref (serialized_icon);
}
g_object_unref (icon);
}
+ if (profile == PROFILE_DESKTOP)
+ {
+ g_menu_item_set_action_and_target(item, "indicator.activate-statistics", "s",
+ indicator_power_device_get_object_path (device));
+ }
+
g_menu_append_item (menu, item);
g_object_unref (item);
}
@@ -400,13 +422,13 @@ append_device_to_menu (GMenu * menu, const IndicatorPowerDevice * device)
static GMenuModel *
-create_desktop_devices_section (IndicatorPowerService * self)
+create_desktop_devices_section (IndicatorPowerService * self, int profile)
{
GList * l;
GMenu * menu = g_menu_new ();
for (l=self->priv->devices; l!=NULL; l=l->next)
- append_device_to_menu (menu, l->data);
+ append_device_to_menu (menu, l->data, profile);
return G_MENU_MODEL (menu);
}
@@ -440,8 +462,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 */
+ priv_t * p = self->priv;
+ int max = 0;
+ if (p->brightness_control)
+ {
+ max = ib_brightness_control_get_max_value (self->priv->brightness_control);
+ }
+ else if (p->brightness_uscreen_control)
+ {
+ max = ib_brightness_uscreen_control_get_max_value (self->priv->brightness_uscreen_control);
+ }
+ *low = (gint)(max * 0.05); /* 5% minimum -- don't let the screen go completely dark */
*high = max;
}
@@ -461,29 +492,19 @@ percentage_to_brightness (IndicatorPowerService * self, double percentage)
return (int)(lo + (percentage*(hi-lo)));
}
-static GMenuItem *
-create_brightness_menuitem (IndicatorPowerService * self)
-{
- 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" );
-
- return item;
-}
-
static GVariant *
action_state_for_brightness (IndicatorPowerService * self)
{
priv_t * p = self->priv;
- const gint brightness = ib_brightness_control_get_value (p->brightness_control);
+ gint brightness = 0;
+ if (p->brightness_control)
+ {
+ brightness = ib_brightness_control_get_value (p->brightness_control);
+ }
+ else if (p->brightness_uscreen_control)
+ {
+ brightness = ib_brightness_uscreen_control_get_value (p->brightness_uscreen_control);
+ }
return g_variant_new_double (brightness_to_percentage (self, brightness));
}
@@ -502,7 +523,16 @@ on_brightness_change_requested (GSimpleAction * action G_GNUC_UNUSED,
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);
+
+ if (self->priv->brightness_control)
+ {
+ ib_brightness_control_set_value (self->priv->brightness_control, brightness);
+ }
+ else if (self->priv->brightness_uscreen_control)
+ {
+ ib_brightness_uscreen_control_set_value (self->priv->brightness_uscreen_control, brightness);
+ }
+
update_brightness_action_state (self);
}
@@ -527,18 +557,12 @@ 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;
section = g_menu_new ();
-
- item = create_brightness_menuitem (self);
- g_menu_append_item (section, item);
update_brightness_action_state (self);
- g_object_unref (item);
-
g_menu_append (section, _("Battery settings…"), "indicator.activate-phone-settings");
return G_MENU_MODEL (section);
@@ -571,18 +595,18 @@ 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));
- rebuild_section (greeter->submenu, 0, create_desktop_devices_section (self));
+ rebuild_section (desktop->submenu, 0, create_desktop_devices_section (self, PROFILE_DESKTOP));
+ rebuild_section (greeter->submenu, 0, create_desktop_devices_section (self, PROFILE_DESKTOP_GREETER));
}
if (sections & SECTION_SETTINGS)
@@ -598,18 +622,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)
{
@@ -633,12 +645,12 @@ create_menu (IndicatorPowerService * self, int profile)
break;
case PROFILE_DESKTOP:
- sections[n++] = create_desktop_devices_section (self);
+ sections[n++] = create_desktop_devices_section (self, PROFILE_DESKTOP);
sections[n++] = create_desktop_settings_section (self);
break;
case PROFILE_DESKTOP_GREETER:
- sections[n++] = create_desktop_devices_section (self);
+ sections[n++] = create_desktop_devices_section (self, PROFILE_DESKTOP_GREETER);
break;
}
@@ -812,6 +824,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,
@@ -911,11 +926,17 @@ 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);
@@ -992,6 +1013,7 @@ my_dispose (GObject * o)
g_clear_object (&p->settings);
}
+ g_clear_object (&p->notifier);
g_clear_object (&p->brightness_action);
g_clear_object (&p->battery_level_action);
g_clear_object (&p->header_action);
@@ -999,7 +1021,9 @@ my_dispose (GObject * o)
g_clear_object (&p->conn);
+ // g_clear_pointer has NULL check inside.
g_clear_pointer (&p->brightness_control, ib_brightness_control_free);
+ g_clear_pointer (&p->brightness_uscreen_control, ib_brightness_uscreen_control_free);
indicator_power_service_set_device_provider (self, NULL);
@@ -1013,6 +1037,8 @@ my_dispose (GObject * o)
static void
indicator_power_service_init (IndicatorPowerService * self)
{
+ GDBusProxy *uscreen_proxy;
+ brightness_params_t brightness_params;
priv_t * p = G_TYPE_INSTANCE_GET_PRIVATE (self,
INDICATOR_TYPE_POWER_SERVICE,
IndicatorPowerServicePrivate);
@@ -1022,7 +1048,17 @@ indicator_power_service_init (IndicatorPowerService * self)
p->settings = g_settings_new ("com.canonical.indicator.power");
- p->brightness_control = ib_brightness_control_new ();
+ p->notifier = indicator_power_notifier_new ();
+
+ uscreen_proxy = uscreen_get_proxy(&brightness_params);
+ if (uscreen_proxy != NULL)
+ {
+ p->brightness_uscreen_control = ib_brightness_uscreen_control_new(uscreen_proxy, brightness_params);
+ }
+ else
+ {
+ p->brightness_control = ib_brightness_control_new ();
+ }
init_gactions (self);
@@ -1096,9 +1132,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);
@@ -1118,6 +1153,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)
{
@@ -1125,13 +1283,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;