From b08f9b096f956b528ad974a30828a809b827efef Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 21 Jul 2014 00:50:24 -0500 Subject: second draft of low battery power notifications, still a work in progress --- data/com.canonical.indicator.power.Battery.xml | 23 +++ src/CMakeLists.txt | 4 + src/dbus-shared.h | 28 +++ src/main.c | 4 - src/notifier.c | 270 +++++++++++++++++++------ src/notifier.h | 4 + src/service.c | 11 + 7 files changed, 275 insertions(+), 69 deletions(-) create mode 100644 data/com.canonical.indicator.power.Battery.xml create mode 100644 src/dbus-shared.h diff --git a/data/com.canonical.indicator.power.Battery.xml b/data/com.canonical.indicator.power.Battery.xml new file mode 100644 index 0000000..d2c8a2d --- /dev/null +++ b/data/com.canonical.indicator.power.Battery.xml @@ -0,0 +1,23 @@ + + + + + + + + + The battery's power level. 0==No Low Battery, 1==Low, 2==Very Low, 3==Critical + + + + + + + + Whether or not indicator-power-service is warning the user about low battery power. + + + + + + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4747b12..7a4a297 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,6 +20,10 @@ 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}) 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 . + * + * Authors: + * Charles Kerr + * Ted Gould + */ + +#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/main.c b/src/main.c index 70b7f2a..d7953e6 100644 --- a/src/main.c +++ b/src/main.c @@ -25,7 +25,6 @@ #include "device.h" #include "device-provider-upower.h" -#include "notifier.h" #include "service.h" /*** @@ -43,7 +42,6 @@ int main (int argc G_GNUC_UNUSED, char ** argv G_GNUC_UNUSED) { IndicatorPowerDeviceProvider * device_provider; - IndicatorPowerNotifier * notifier; IndicatorPowerService * service; GMainLoop * loop; @@ -54,7 +52,6 @@ main (int argc G_GNUC_UNUSED, char ** argv G_GNUC_UNUSED) /* run */ device_provider = indicator_power_device_provider_upower_new (); - notifier = indicator_power_notifier_new (device_provider); service = indicator_power_service_new (device_provider); loop = g_main_loop_new (NULL, FALSE); g_signal_connect (service, INDICATOR_POWER_SERVICE_SIGNAL_NAME_LOST, @@ -64,7 +61,6 @@ main (int argc G_GNUC_UNUSED, char ** argv G_GNUC_UNUSED) /* cleanup */ g_main_loop_unref (loop); g_clear_object (&service); - g_clear_object (¬ifier); g_clear_object (&device_provider); return 0; } diff --git a/src/notifier.c b/src/notifier.c index 79ea4cc..897154a 100644 --- a/src/notifier.c +++ b/src/notifier.c @@ -17,6 +17,8 @@ * Charles Kerr */ +#include "dbus-battery-info.h" +#include "dbus-shared.h" #include "device.h" #include "device-provider.h" #include "notifier.h" @@ -35,20 +37,41 @@ enum { PROP_0, PROP_DEVICE_PROVIDER, + PROP_IS_WARNING, + PROP_POWER_LEVEL, LAST_PROP }; +#define DEVICE_PROVIDER_NAME "device-provider" +#define IS_WARNING_NAME "is-warning" +#define POWER_LEVEL_NAME "power-level" + static GParamSpec * properties[LAST_PROP]; static int n_notifiers = 0; +typedef enum +{ + POWER_LEVEL_OK, + POWER_LEVEL_LOW, + POWER_LEVEL_VERY_LOW, + POWER_LEVEL_CRITICAL +} +PowerLevel; + struct _IndicatorPowerNotifierPrivate { IndicatorPowerDeviceProvider * device_provider; - IndicatorPowerDevice * primary_device; - gdouble battery_level; - time_t time_remaining; + + IndicatorPowerDevice * battery; NotifyNotification* notify_notification; + gboolean is_warning; + PowerLevel power_level; + + DbusBattery * dbus_battery; + GBinding * is_warning_binding; + GBinding * power_level_binding; + GDBusConnection * bus; }; typedef IndicatorPowerNotifierPrivate priv_t; @@ -57,35 +80,53 @@ typedef IndicatorPowerNotifierPrivate priv_t; **** ***/ +/* implemented here rather than my_set_property() to guard from public use + because this is a read-only property */ static void -emit_critical_signal(IndicatorPowerNotifier * self G_GNUC_UNUSED) +set_is_warning_property (IndicatorPowerNotifier * self, gboolean is_warning) { - g_message("FIXME %s %s", G_STRFUNC, G_STRLOC); -} + priv_t * p = self->priv; -static void -emit_hide_signal(IndicatorPowerNotifier * self G_GNUC_UNUSED) -{ - g_message("FIXME %s %s", G_STRFUNC, G_STRLOC); + if (p->is_warning != is_warning) + { + p->is_warning = is_warning; + + g_object_notify_by_pspec (G_OBJECT(self), properties[PROP_IS_WARNING]); + } } +/* implemented here rather than my_set_property() to guard from public use + because this is a read-only property */ static void -emit_show_signal(IndicatorPowerNotifier * self G_GNUC_UNUSED) +set_power_level_property (IndicatorPowerNotifier * self, PowerLevel power_level) { - g_message("FIXME %s %s", G_STRFUNC, G_STRLOC); + priv_t * p = self->priv; + + if (p->power_level != power_level) + { + p->power_level = power_level; + + g_object_notify_by_pspec (G_OBJECT(self), properties[PROP_POWER_LEVEL]); + } } +/*** +**** +***/ + static void -notification_clear(IndicatorPowerNotifier * self) +notification_clear (IndicatorPowerNotifier * self) { priv_t * p = self->priv; if (p->notify_notification != NULL) { + set_is_warning_property (self, FALSE); + notify_notification_clear_actions(p->notify_notification); g_signal_handlers_disconnect_by_data(p->notify_notification, self); g_clear_object(&p->notify_notification); - emit_hide_signal(self); + } } @@ -107,10 +148,7 @@ notification_show(IndicatorPowerNotifier * self, NotifyNotification * nn; // if there's already a notification, tear it down - if (p->notify_notification != NULL) - { - notification_clear (self); - } + notification_clear (self); // create the notification body = g_strdup_printf(_("%d%% charge remaining"), (int)indicator_power_device_get_percentage(device)); @@ -130,63 +168,75 @@ notification_show(IndicatorPowerNotifier * self, } else { - emit_show_signal(self); + set_is_warning_property (self, TRUE); } g_free (body); } -static void -on_battery_level_changed(IndicatorPowerNotifier * self G_GNUC_UNUSED, - IndicatorPowerDevice * device, - gdouble old_value, - gdouble new_value) +static PowerLevel +get_power_level (const IndicatorPowerDevice * device) { - static const double critical_level = 2.0; - static const double very_low_level = 5.0; - static const double low_level = 48.0; - - if (indicator_power_device_get_state(device) != UP_DEVICE_STATE_DISCHARGING) - return; - -g_message ("%s - %s - %f - %f", G_STRFUNC, indicator_power_device_get_object_path(device), old_value, new_value); + static const double percent_critical = 2.0; + static const double percent_very_low = 5.0; + static const double percent_low = 48.0; + const gdouble p = indicator_power_device_get_percentage(device); + PowerLevel ret; + + 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; - if ((old_value > critical_level) && (new_value <= critical_level)) - { - emit_critical_signal(self); - } - else if ((old_value > very_low_level) && (new_value <= very_low_level)) - { - notification_show(self, device); - } - else if ((old_value > low_level) && (new_value <= low_level)) - { - notification_show(self, device); - } + return ret; } - + static void on_devices_changed(IndicatorPowerNotifier * self) { priv_t * p = self->priv; GList * devices; - - // find the primary device - devices = indicator_power_device_provider_get_devices(p->device_provider); - g_clear_object(&p->primary_device); - p->primary_device = indicator_power_service_choose_primary_device (devices); + IndicatorPowerDevice * primary; + + /* find the primary battery */ + devices = indicator_power_device_provider_get_devices (p->device_provider); + primary = indicator_power_service_choose_primary_device (devices); + g_clear_object (&p->battery); + if ((primary != NULL) && (indicator_power_device_get_kind (primary) == UP_DEVICE_KIND_BATTERY)) + p->battery = g_object_ref (primary); + g_clear_object(&primary); g_list_free_full (devices, (GDestroyNotify)g_object_unref); - if (p->primary_device != NULL) + /* update our state based on the new primary device */ + if (p->battery == NULL) { - // test for battery level change - const gdouble old_level = (int)(p->battery_level*1000) ? p->battery_level : 100.0; - const gdouble new_level = indicator_power_device_get_percentage(p->primary_device); - if ((int)(old_level*1000) != (int)(new_level*1000)) - on_battery_level_changed (self, p->primary_device, old_level, new_level); - - p->battery_level = new_level; - p->time_remaining = indicator_power_device_get_time (p->primary_device); + /* if there's no primary battery, put everything in standby mode */ + set_power_level_property (self, POWER_LEVEL_OK); + notification_clear(self); + } + else + { + const PowerLevel power_level = get_power_level (p->battery); + + if (p->power_level != power_level) + { + set_power_level_property (self, power_level); + + /* maybe update the notifications */ + if ((power_level == POWER_LEVEL_OK) || + (indicator_power_device_get_state(p->battery) != UP_DEVICE_STATE_DISCHARGING)) + { + notification_clear (self); + } + else + { + notification_show (self, p->battery); + } + } } } @@ -209,6 +259,14 @@ my_get_property (GObject * o, g_value_set_object (value, p->device_provider); break; + case PROP_POWER_LEVEL: + g_value_set_int (value, p->power_level); + break; + + case PROP_IS_WARNING: + g_value_set_boolean (value, p->is_warning); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec); } @@ -237,16 +295,24 @@ static void my_dispose (GObject * o) { IndicatorPowerNotifier * self = INDICATOR_POWER_NOTIFIER(o); + priv_t * p = self->priv; +g_message ("%s %s dispose %p", G_STRLOC, G_STRFUNC, (void*)o); - indicator_power_notifier_set_device_provider(self, NULL); + indicator_power_notifier_set_bus(self, NULL); notification_clear(self); + indicator_power_notifier_set_device_provider (self, NULL); + + g_clear_pointer (&p->power_level_binding, g_binding_unbind); + g_clear_pointer (&p->is_warning_binding, g_binding_unbind); + 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) +my_finalize (GObject * o) { +g_message ("%s %s finalize %p", G_STRLOC, G_STRFUNC, (void*)o); if (!--n_notifiers) notify_uninit(); } @@ -262,8 +328,23 @@ indicator_power_notifier_init (IndicatorPowerNotifier * self) INDICATOR_TYPE_POWER_NOTIFIER, IndicatorPowerNotifierPrivate); self->priv = p; +g_message ("%s %s init %p", G_STRLOC, G_STRFUNC, (void*)self); + + p->dbus_battery = dbus_battery_skeleton_new (); + + /* FIXME: own the busname and export the skeleton */ - //p->battery_levels = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_variant_unref); + p->is_warning_binding = g_object_bind_property (self, + IS_WARNING_NAME, + p->dbus_battery, + IS_WARNING_NAME, + G_BINDING_SYNC_CREATE); + + p->power_level_binding = g_object_bind_property (self, + POWER_LEVEL_NAME, + p->dbus_battery, + POWER_LEVEL_NAME, + G_BINDING_SYNC_CREATE); if (!n_notifiers++ && !notify_init("indicator-power-service")) g_critical("Unable to initialize libnotify! Notifications might not be shown."); @@ -284,12 +365,28 @@ indicator_power_notifier_class_init (IndicatorPowerNotifierClass * klass) properties[PROP_0] = NULL; properties[PROP_DEVICE_PROVIDER] = g_param_spec_object ( - "device-provider", + DEVICE_PROVIDER_NAME, "Device Provider", "Source for power devices", G_TYPE_OBJECT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + properties[PROP_POWER_LEVEL] = g_param_spec_int ( + POWER_LEVEL_NAME, + "Power Level", + "Power Level of the batteries", + POWER_LEVEL_OK, + POWER_LEVEL_CRITICAL, + POWER_LEVEL_OK, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + properties[PROP_IS_WARNING] = g_param_spec_boolean ( + IS_WARNING_NAME, + "Is Warning", + "Whether or not we're currently warning the user about a low battery", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (object_class, LAST_PROP, properties); } @@ -301,7 +398,7 @@ IndicatorPowerNotifier * indicator_power_notifier_new (IndicatorPowerDeviceProvider * device_provider) { GObject * o = g_object_new (INDICATOR_TYPE_POWER_NOTIFIER, - "device-provider", device_provider, + DEVICE_PROVIDER_NAME, device_provider, NULL); return INDICATOR_POWER_NOTIFIER (o); @@ -321,7 +418,7 @@ indicator_power_notifier_set_device_provider(IndicatorPowerNotifier * self, { g_signal_handlers_disconnect_by_data(p->device_provider, self); g_clear_object(&p->device_provider); - g_clear_object(&p->primary_device); + g_clear_object(&p->battery); } if (dp != NULL) @@ -334,3 +431,46 @@ indicator_power_notifier_set_device_provider(IndicatorPowerNotifier * self, on_devices_changed(self); } } + +void +indicator_power_notifier_set_bus (IndicatorPowerNotifier * self, + GDBusConnection * bus) +{ + priv_t * p; + + g_return_if_fail(INDICATOR_IS_POWER_NOTIFIER(self)); + p = self->priv; + + if (p->bus != NULL) + { + if (p->dbus_battery != NULL) + { + g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON(p->dbus_battery)); + } + + g_clear_object (&p->bus); + } + + if (bus != NULL) + { + GError * error; + + p->bus = g_object_ref (bus); + + error = NULL; + g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(p->dbus_battery), + bus, + BUS_PATH"/Battery", + &error); + if (error != NULL) + { + g_warning ("Unable to export LowBattery properties: %s", error->message); + g_error_free (error); + } + } +} + + + + + diff --git a/src/notifier.h b/src/notifier.h index e8dfaab..c224b29 100644 --- a/src/notifier.h +++ b/src/notifier.h @@ -22,6 +22,7 @@ #include #include +#include /* GDBusConnection */ #include "device-provider.h" @@ -59,6 +60,9 @@ GType indicator_power_notifier_get_type (void); IndicatorPowerNotifier * indicator_power_notifier_new (IndicatorPowerDeviceProvider * provider); +void indicator_power_notifier_set_bus (IndicatorPowerNotifier * self, + GDBusConnection * connection); + void indicator_power_notifier_set_device_provider (IndicatorPowerNotifier * self, IndicatorPowerDeviceProvider * provider); diff --git a/src/service.c b/src/service.c index 7478d0f..d8f1371 100644 --- a/src/service.c +++ b/src/service.c @@ -22,8 +22,10 @@ #include #include +#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" @@ -120,6 +122,7 @@ struct _IndicatorPowerServicePrivate GList * devices; /* IndicatorPowerDevice */ IndicatorPowerDeviceProvider * device_provider; + IndicatorPowerNotifier * notifier; }; typedef IndicatorPowerServicePrivate priv_t; @@ -821,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, @@ -1001,6 +1007,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); @@ -1035,6 +1042,8 @@ indicator_power_service_init (IndicatorPowerService * self) p->settings = g_settings_new ("com.canonical.indicator.power"); + p->notifier = indicator_power_notifier_new (NULL); + uscreen_proxy = uscreen_get_proxy(&brightness_params); if (uscreen_proxy != NULL) { @@ -1136,6 +1145,8 @@ indicator_power_service_set_device_provider (IndicatorPowerService * self, on_devices_changed (self); } + + indicator_power_notifier_set_device_provider (p->notifier, dp); } /* If a device has multiple batteries and uses only one of them at a time, -- cgit v1.2.3