diff options
author | Charles Kerr <charles.kerr@canonical.com> | 2013-06-19 21:13:09 +0000 |
---|---|---|
committer | Tarmac <Unknown> | 2013-06-19 21:13:09 +0000 |
commit | 0ce78de0eae6f44a0afe6e5b4bdc6da83f6dbdf8 (patch) | |
tree | ce626b15bcfb92beec4487a6b7fe50ef4be91dac /src | |
parent | 0b0585581a8da994362ef4cfe3a2168917a47ff4 (diff) | |
parent | 258f3ad689ae4ba49e2813a5051b4968d568f999 (diff) | |
download | ayatana-indicator-power-0ce78de0eae6f44a0afe6e5b4bdc6da83f6dbdf8.tar.gz ayatana-indicator-power-0ce78de0eae6f44a0afe6e5b4bdc6da83f6dbdf8.tar.bz2 ayatana-indicator-power-0ce78de0eae6f44a0afe6e5b4bdc6da83f6dbdf8.zip |
Convert the power indicator into a service that exports GMenus and GActions in accordance with our indicator-ng design.
Approved by PS Jenkins bot, Ted Gould.
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 120 | ||||
-rw-r--r-- | src/dbus-listener.c | 247 | ||||
-rw-r--r-- | src/dbus-listener.h | 82 | ||||
-rw-r--r-- | src/device-provider-upower.c | 424 | ||||
-rw-r--r-- | src/device-provider-upower.h | 72 | ||||
-rw-r--r-- | src/device-provider.c | 90 | ||||
-rw-r--r-- | src/device-provider.h | 80 | ||||
-rw-r--r-- | src/device.c | 25 | ||||
-rw-r--r-- | src/device.h | 33 | ||||
-rw-r--r-- | src/indicator-power.c | 663 | ||||
-rw-r--r-- | src/indicator-power.h | 60 | ||||
-rw-r--r-- | src/main.c | 68 | ||||
-rw-r--r-- | src/org.freedesktop.UPower.Device.xml | 705 | ||||
-rw-r--r-- | src/org.freedesktop.UPower.xml | 43 | ||||
-rw-r--r-- | src/service.c | 1015 | ||||
-rw-r--r-- | src/service.h | 78 |
16 files changed, 2716 insertions, 1089 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index b2cf3df..8495233 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,36 +1,88 @@ +BUILT_SOURCES = +EXTRA_DIST = CLEANFILES = -ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} - -################### -# Indicator Stuff -################### - -powerlibdir = $(INDICATORDIR) -powerlib_LTLIBRARIES = libpower.la - -libpower_la_SOURCES = \ - dbus-listener.c \ - dbus-listener.h \ - device.c \ - device.h \ - indicator-power.h \ - indicator-power.c - -CLEANFILES += .libs/*.gcda .libs/*.gcno *.gcda *.gcno - -libpower_la_CFLAGS = \ - $(UPOWER_CFLAGS) \ - $(INDICATOR_CFLAGS) \ - $(COVERAGE_CFLAGS) \ - -Wall -Werror \ - -DG_LOG_DOMAIN=\"Indicator-Power\" - -libpower_la_LIBADD = \ - $(UPOWER_LIBS) \ - $(INDICATOR_LIBS) - -libpower_la_LDFLAGS = \ - $(COVERAGE_LDFLAGS) \ - -module \ - -avoid-version + +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 + +### +### + +upower_device_dbus_sources = \ + dbus-upower-device.c \ + dbus-upower-device.h + +$(upower_device_dbus_sources): org.freedesktop.UPower.Device.xml + $(AM_V_GEN) gdbus-codegen \ + --c-namespace Dbus \ + --interface-prefix org.freedesktop.UPower \ + --generate-c-code dbus-upower-device \ + $^ + +BUILT_SOURCES += $(upower_device_dbus_sources) +CLEANFILES += $(upower_device_dbus_sources) +EXTRA_DIST += org.freedesktop.UPower.Device.xml + +### +### +### + +noinst_LIBRARIES = libindicatorpower-upower.a libindicatorpower-service.a + +libindicatorpower_upower_a_SOURCES = \ + $(upower_dbus_sources) \ + $(upower_device_dbus_sources) \ + device-provider-upower.c \ + device-provider-upower.h + +libindicatorpower_upower_a_CFLAGS = $(SHARED_CFLAGS) -Wno-unused-parameter + +libindicatorpower_service_a_SOURCES = \ + device-provider.c \ + device-provider.h \ + device.c \ + device.h \ + service.c \ + service.h + +libindicatorpower_service_a_CFLAGS = $(SHARED_CFLAGS) -Wno-missing-field-initializers + +### +### +### + +libexec_PROGRAMS = indicator-power-service + +indicator_power_service_SOURCES = main.c + +indicator_power_service_CFLAGS = $(SHARED_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-listener.c b/src/dbus-listener.c deleted file mode 100644 index b1f64a7..0000000 --- a/src/dbus-listener.c +++ /dev/null @@ -1,247 +0,0 @@ -/* - -Listens on DBus for Power changes and passes them to an IndicatorPower - -Copyright 2012 Canonical Ltd. - -Authors: - Charles Kerr <charles.kerr@canonical.com> - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -version 3.0 as published by the Free Software Foundation. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License version 3.0 for more details. - -You should have received a copy of the GNU General Public -License along with this library. If not, see -<http://www.gnu.org/licenses/>. -*/ - -#include "dbus-listener.h" -#include "device.h" - -struct _IndicatorPowerDbusListenerPrivate -{ - GCancellable * cancellable; - GDBusProxy * proxy; - guint watcher_id; -}; - -#define INDICATOR_POWER_DBUS_LISTENER_GET_PRIVATE(o) (INDICATOR_POWER_DBUS_LISTENER(o)->priv) - -/* Signals */ -enum { - SIGNAL_DEVICES, - SIGNAL_LAST -}; -static guint signals[SIGNAL_LAST] = { 0 }; - - -/* GObject stuff */ -static void indicator_power_dbus_listener_class_init (IndicatorPowerDbusListenerClass *klass); -static void indicator_power_dbus_listener_init (IndicatorPowerDbusListener *self); -static void indicator_power_dbus_listener_dispose (GObject *object); -static void indicator_power_dbus_listener_finalize (GObject *object); - -static void gsd_appeared_callback (GDBusConnection *connection, const gchar *name, const gchar *name_owner, gpointer user_data); - -/* LCOV_EXCL_START */ -G_DEFINE_TYPE (IndicatorPowerDbusListener, indicator_power_dbus_listener, G_TYPE_OBJECT); -/* LCOV_EXCL_STOP */ - -static void -indicator_power_dbus_listener_class_init (IndicatorPowerDbusListenerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - g_type_class_add_private (klass, sizeof (IndicatorPowerDbusListenerPrivate)); - - /* methods */ - object_class->dispose = indicator_power_dbus_listener_dispose; - object_class->finalize = indicator_power_dbus_listener_finalize; - - /** - * IndicatorPowerDbusListener::devices-enumerated: - * - * @listener: the IndicatorPowerDbusListener - * @devices: a GSList of #IndicatorPowerDevice objects. (transfer none) - * - * This is emitted each time a new set of devices is enumerated over the bus. - */ - signals[SIGNAL_DEVICES] = g_signal_new (INDICATOR_POWER_DBUS_LISTENER_DEVICES_ENUMERATED, - G_TYPE_FROM_CLASS(klass), 0, - G_STRUCT_OFFSET (IndicatorPowerDbusListenerClass, devices_enumerated), - NULL, NULL, - g_cclosure_marshal_VOID__POINTER, - G_TYPE_NONE, 1, G_TYPE_POINTER); -} - -/* Initialize an instance */ -static void -indicator_power_dbus_listener_init (IndicatorPowerDbusListener *self) -{ - IndicatorPowerDbusListenerPrivate * priv; - - priv = G_TYPE_INSTANCE_GET_PRIVATE (self, - INDICATOR_POWER_DBUS_LISTENER_TYPE, - IndicatorPowerDbusListenerPrivate); - - priv->cancellable = g_cancellable_new (); - - priv->watcher_id = g_bus_watch_name (G_BUS_TYPE_SESSION, - GSD_SERVICE, - G_BUS_NAME_WATCHER_FLAGS_NONE, - gsd_appeared_callback, - NULL, - self, - NULL); - - self->priv = priv; -} - -static void -indicator_power_dbus_listener_dispose (GObject *object) -{ - IndicatorPowerDbusListener * self = INDICATOR_POWER_DBUS_LISTENER(object); - IndicatorPowerDbusListenerPrivate * priv = self->priv; - - g_clear_object (&priv->proxy); - - if (priv->cancellable != NULL) - { - g_cancellable_cancel (priv->cancellable); - g_clear_object (&priv->cancellable); - } - - if (priv->watcher_id) - { - g_bus_unwatch_name (priv->watcher_id); - priv->watcher_id = 0; - } - - G_OBJECT_CLASS (indicator_power_dbus_listener_parent_class)->dispose (object); -} - -static void -indicator_power_dbus_listener_finalize (GObject *object) -{ - G_OBJECT_CLASS (indicator_power_dbus_listener_parent_class)->finalize (object); -} - -/*** -**** -***/ - -static void -get_devices_cb (GObject * source_object, - GAsyncResult * res, - gpointer user_data) -{ - GError *error; - GSList * devices = NULL; - GVariant * devices_container; - IndicatorPowerDbusListener * self = INDICATOR_POWER_DBUS_LISTENER (user_data); - - /* build an array of IndicatorPowerDevices from the DBus response */ - error = NULL; - devices_container = g_dbus_proxy_call_finish (G_DBUS_PROXY (source_object), res, &error); - if (devices_container == NULL) - { - g_message ("Couldn't get devices: %s\n", error->message); - g_error_free (error); - } - else - { - gsize i; - GVariant * devices_variant = g_variant_get_child_value (devices_container, 0); - const int device_count = devices_variant ? g_variant_n_children (devices_variant) : 0; - - for (i=0; i<device_count; i++) - { - GVariant * v = g_variant_get_child_value (devices_variant, i); - devices = g_slist_prepend (devices, indicator_power_device_new_from_variant (v)); - g_variant_unref (v); - } - - devices = g_slist_reverse (devices); - - g_variant_unref (devices_variant); - g_variant_unref (devices_container); - } - - g_signal_emit (self, signals[SIGNAL_DEVICES], (GQuark)0, devices); - g_slist_free_full (devices, g_object_unref); -} - -static void -request_device_list (IndicatorPowerDbusListener * self) -{ - g_dbus_proxy_call (self->priv->proxy, - "GetDevices", - NULL, - G_DBUS_CALL_FLAGS_NONE, - -1, - self->priv->cancellable, - get_devices_cb, - self); -} - -static void -receive_properties_changed (GDBusProxy *proxy G_GNUC_UNUSED, - GVariant *changed_properties G_GNUC_UNUSED, - GStrv invalidated_properties G_GNUC_UNUSED, - gpointer user_data) -{ - request_device_list (INDICATOR_POWER_DBUS_LISTENER(user_data)); -} - - -static void -service_proxy_cb (GObject *object, - GAsyncResult *res, - gpointer user_data) -{ - GError * error = NULL; - IndicatorPowerDbusListener * self = INDICATOR_POWER_DBUS_LISTENER(user_data); - IndicatorPowerDbusListenerPrivate * priv = self->priv; - - priv->proxy = g_dbus_proxy_new_for_bus_finish (res, &error); - - if (error != NULL) - { - g_error ("Error creating proxy: %s", error->message); - g_error_free (error); - return; - } - - /* we want to change the primary device changes */ - g_signal_connect (priv->proxy, "g-properties-changed", - G_CALLBACK (receive_properties_changed), user_data); - - /* get the initial state */ - request_device_list (self); -} - -static void -gsd_appeared_callback (GDBusConnection *connection, - const gchar *name, - const gchar *name_owner, - gpointer user_data) -{ - IndicatorPowerDbusListener * self = INDICATOR_POWER_DBUS_LISTENER(user_data); - IndicatorPowerDbusListenerPrivate * priv = self->priv; - - g_dbus_proxy_new (connection, - G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, - NULL, - name, - GSD_POWER_DBUS_PATH, - GSD_POWER_DBUS_INTERFACE, - priv->cancellable, - service_proxy_cb, - self); -} diff --git a/src/dbus-listener.h b/src/dbus-listener.h deleted file mode 100644 index e4d006b..0000000 --- a/src/dbus-listener.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - -Listens for Power changes from org.gnome.SettingsDaemon.Power on Dbus - -Copyright 2012 Canonical Ltd. - -Authors: - Javier Jardon <javier.jardon@codethink.co.uk> - Charles Kerr <charles.kerr@canonical.com> - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -version 3.0 as published by the Free Software Foundation. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License version 3.0 for more details. - -You should have received a copy of the GNU General Public -License along with this library. If not, see -<http://www.gnu.org/licenses/>. -*/ - -#ifndef __INDICATOR_POWER_DBUS_LISTENER_H__ -#define __INDICATOR_POWER_DBUS_LISTENER_H__ - -#include <glib-object.h> -#include <libupower-glib/upower.h> - -G_BEGIN_DECLS - -#define INDICATOR_POWER_DBUS_LISTENER_TYPE (indicator_power_dbus_listener_get_type ()) -#define INDICATOR_POWER_DBUS_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), INDICATOR_POWER_DBUS_LISTENER_TYPE, IndicatorPowerDbusListener)) -#define INDICATOR_POWER_DBUS_LISTENER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), INDICATOR_POWER_DBUS_LISTENER_TYPE, IndicatorPowerDbusListenerClass)) -#define INDICATOR_IS_POWER_DBUS_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), INDICATOR_POWER_DBUS_LISTENER_TYPE)) -#define INDICATOR_IS_POWER_DBUS_LISTENER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), INDICATOR_POWER_DBUS_LISTENER_TYPE)) -#define INDICATOR_POWER_DBUS_LISTENER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), INDICATOR_POWER_DBUS_LISTENER_TYPE, IndicatorPowerDbusListenerClass)) - -typedef struct _IndicatorPowerDbusListener IndicatorPowerDbusListener; -typedef struct _IndicatorPowerDbusListenerClass IndicatorPowerDbusListenerClass; -typedef struct _IndicatorPowerDbusListenerPrivate IndicatorPowerDbusListenerPrivate; - -#define GSD_SERVICE "org.gnome.SettingsDaemon" -#define GSD_PATH "/org/gnome/SettingsDaemon" -#define GSD_POWER_DBUS_INTERFACE GSD_SERVICE ".Power" -#define GSD_POWER_DBUS_PATH GSD_PATH "/Power" - -/* signals */ -#define INDICATOR_POWER_DBUS_LISTENER_DEVICES_ENUMERATED "devices-enumerated" - -/** - * IndicatorPowerDbusListenerClass: - * @parent_class: #GObjectClass - */ -struct _IndicatorPowerDbusListenerClass -{ - GObjectClass parent_class; - - void (* devices_enumerated) (IndicatorPowerDbusListener*, GSList * devices); -}; - -/** - * IndicatorPowerDbusListener: - * @parent: #GObject - * @priv: A cached reference to the private data for the instance. -*/ -struct _IndicatorPowerDbusListener -{ - GObject parent; - IndicatorPowerDbusListenerPrivate * priv; -}; - -/*** -**** -***/ - -GType indicator_power_dbus_listener_get_type (void); - -G_END_DECLS - -#endif diff --git a/src/device-provider-upower.c b/src/device-provider-upower.c new file mode 100644 index 0000000..05faeab --- /dev/null +++ b/src/device-provider-upower.c @@ -0,0 +1,424 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include "dbus-upower.h" +#include "dbus-upower-device.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" + +/*** +**** private struct +***/ + +struct _IndicatorPowerDeviceProviderUPowerPriv +{ + DbusUPower * upower_proxy; + GHashTable * devices; /* dbus object path --> IndicatorPowerDevice */ + GCancellable * cancellable; + + /* 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; + +/*** +**** GObject boilerplate +***/ + +static void indicator_power_device_provider_interface_init ( + IndicatorPowerDeviceProviderInterface * iface); + +G_DEFINE_TYPE_WITH_CODE ( + IndicatorPowerDeviceProviderUPower, + indicator_power_device_provider_upower, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (INDICATOR_TYPE_POWER_DEVICE_PROVIDER, + indicator_power_device_provider_interface_init)); + +/*** +**** UPOWER DBUS +***/ + +static void +emit_devices_changed (IndicatorPowerDeviceProviderUPower * self) +{ + indicator_power_device_provider_emit_devices_changed (INDICATOR_POWER_DEVICE_PROVIDER (self)); +} + +static void +on_upower_device_proxy_ready (GObject * o, GAsyncResult * res, gpointer gself) +{ + GError * err; + DbusDevice * tmp; + + err = NULL; + tmp = dbus_device_proxy_new_for_bus_finish (res, &err); + if (err != NULL) + { + g_warning ("Unable to get UPower Device Proxy: %s", err->message); + g_error_free (err); + } + else + { + /* use this proxy's properties to update our own IndicatorPowerDevice */ + + IndicatorPowerDevice * device; + IndicatorPowerDeviceProviderUPower * self; + priv_t * p; + + self = INDICATOR_POWER_DEVICE_PROVIDER_UPOWER (gself); + p = self->priv; + + const guint kind = dbus_device_get_type_ (tmp); + const gdouble percentage = dbus_device_get_percentage (tmp); + const guint state = dbus_device_get_state (tmp); + const gint64 time_to_empty = dbus_device_get_time_to_empty (tmp); + const gint64 time_to_full = dbus_device_get_time_to_full (tmp); + const time_t time = time_to_empty ? time_to_empty : time_to_full; + const char * path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (tmp)); + + device = indicator_power_device_new (path, + kind, + percentage, + state, + time); + + g_hash_table_insert (p->devices, + g_strdup (path), + g_object_ref (device)); + + emit_devices_changed (self); + + g_object_unref (device); + g_object_unref (tmp); + } +} + +static void +update_device_from_object_path (IndicatorPowerDeviceProviderUPower * self, + const char * path) +{ + dbus_device_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_NONE, + BUS_NAME, + path, + self->priv->cancellable, + on_upower_device_proxy_ready, + self); +} + +/* + * 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. + * + * 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. + */ + +/* rebuild all the proxies listed in our queued_paths hashset */ +static gboolean +on_queued_paths_timer (gpointer gself) +{ + gpointer path; + GHashTableIter iter; + IndicatorPowerDeviceProviderUPower * self; + priv_t * p; + + self = INDICATOR_POWER_DEVICE_PROVIDER_UPOWER (gself); + p = self->priv; + + /* create new proxies 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); + + /* cleanup */ + g_hash_table_remove_all (p->queued_paths); + p->queued_paths_timer = 0; + return G_SOURCE_REMOVE; +} + +/* add the path to our queued_paths hashset and ensure the timer's running */ +static void +refresh_device_soon (IndicatorPowerDeviceProviderUPower * self, + const char * object_path) +{ + priv_t * p = self->priv; + + g_hash_table_add (p->queued_paths, g_strdup (object_path)); + + if (p->queued_paths_timer == 0) + p->queued_paths_timer = g_timeout_add (500, on_queued_paths_timer, self); +} + +/*** +**** +***/ + +static void +on_upower_device_enumerations_ready (GObject * proxy, + GAsyncResult * res, + gpointer gself) +{ + GError * err; + char ** object_paths; + + err = NULL; + dbus_upower_call_enumerate_devices_finish (DBUS_UPOWER(proxy), + &object_paths, + res, + &err); + + if (err != NULL) + { + g_warning ("Unable to get UPower devices: %s", err->message); + g_error_free (err); + } + else + { + guint i; + + for (i=0; object_paths && object_paths[i]; i++) + refresh_device_soon (gself, object_paths[i]); + + g_strfreev (object_paths); + } +} + +static void +on_upower_device_changed (DbusUPower * unused G_GNUC_UNUSED, + const char * object_path, + gpointer gself) +{ + refresh_device_soon (gself, object_path); +} + +static void +on_upower_device_added (DbusUPower * unused G_GNUC_UNUSED, + const char * object_path, + gpointer gself) +{ + refresh_device_soon (gself, object_path); +} + +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); + g_hash_table_remove (self->priv->devices, object_path); + g_hash_table_remove (self->priv->queued_paths, object_path); + + emit_devices_changed (self); +} + +static void +on_upower_proxy_ready (GObject * source G_GNUC_UNUSED, + GAsyncResult * res, + gpointer gself) +{ + GError * err; + DbusUPower * proxy; + + err = NULL; + proxy = dbus_upower_proxy_new_for_bus_finish (res, &err); + if (err != NULL) + { + g_warning ("Unable to get UPower proxy: %s", err->message); + g_error_free (err); + } + else + { + IndicatorPowerDeviceProviderUPower * self; + priv_t * p; + + self = INDICATOR_POWER_DEVICE_PROVIDER_UPOWER (gself); + p = self->priv; + + p->upower_proxy = proxy; + 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); + } +} + +/*** +**** IndicatorPowerDeviceProvider virtual functions +***/ + +static GList * +my_get_devices (IndicatorPowerDeviceProvider * provider) +{ + GList * devices; + IndicatorPowerDeviceProviderUPower * self; + + self = INDICATOR_POWER_DEVICE_PROVIDER_UPOWER(provider); + + devices = g_hash_table_get_values (self->priv->devices); + g_list_foreach (devices, (GFunc)g_object_ref, NULL); + return devices; +} + +/*** +**** GObject virtual functions +***/ + +static void +my_dispose (GObject * o) +{ + IndicatorPowerDeviceProviderUPower * self; + priv_t * p; + + self = INDICATOR_POWER_DEVICE_PROVIDER_UPOWER(o); + p = self->priv; + + if (p->cancellable != NULL) + { + g_cancellable_cancel (p->cancellable); + + g_clear_object (&p->cancellable); + } + + if (p->queued_paths_timer != 0) + { + g_source_remove (p->queued_paths_timer); + + p->queued_paths_timer = 0; + } + + if (p->upower_proxy != NULL) + { + g_signal_handlers_disconnect_by_data (p->upower_proxy, self); + + g_clear_object (&p->upower_proxy); + } + + g_hash_table_remove_all (p->devices); + + G_OBJECT_CLASS (indicator_power_device_provider_upower_parent_class)->dispose (o); +} + +static void +my_finalize (GObject * o) +{ + IndicatorPowerDeviceProviderUPower * self; + priv_t * p; + + self = INDICATOR_POWER_DEVICE_PROVIDER_UPOWER(o); + p = self->priv; + + 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); +} + +/*** +**** Instantiation +***/ + +static void +indicator_power_device_provider_upower_class_init (IndicatorPowerDeviceProviderUPowerClass * klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = my_dispose; + object_class->finalize = my_finalize; + + g_type_class_add_private (klass, + sizeof (IndicatorPowerDeviceProviderUPowerPriv)); +} + +static void +indicator_power_device_provider_interface_init (IndicatorPowerDeviceProviderInterface * iface) +{ + iface->get_devices = my_get_devices; +} + +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); + + dbus_upower_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES, + BUS_NAME, + BUS_PATH, + p->cancellable, + on_upower_proxy_ready, + self); +} + +/*** +**** Public API +***/ + +IndicatorPowerDeviceProvider * +indicator_power_device_provider_upower_new (void) +{ + gpointer o = g_object_new (INDICATOR_TYPE_POWER_DEVICE_PROVIDER_UPOWER, NULL); + + return INDICATOR_POWER_DEVICE_PROVIDER (o); +} diff --git a/src/device-provider-upower.h b/src/device-provider-upower.h new file mode 100644 index 0000000..7bdd5d5 --- /dev/null +++ b/src/device-provider-upower.h @@ -0,0 +1,72 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __INDICATOR_POWER_DEVICE_PROVIDER_UPOWER__H__ +#define __INDICATOR_POWER_DEVICE_PROVIDER_UPOWER__H__ + +#include <glib-object.h> /* parent class */ + +#include "device-provider.h" + +G_BEGIN_DECLS + +#define INDICATOR_TYPE_POWER_DEVICE_PROVIDER_UPOWER \ + (indicator_power_device_provider_upower_get_type()) + +#define INDICATOR_POWER_DEVICE_PROVIDER_UPOWER(o) \ + (G_TYPE_CHECK_INSTANCE_CAST ((o), \ + INDICATOR_TYPE_POWER_DEVICE_PROVIDER_UPOWER, \ + IndicatorPowerDeviceProviderUPower)) + +#define INDICATOR_POWER_DEVICE_PROVIDER_UPOWER_GET_CLASS(o) \ + (G_TYPE_INSTANCE_GET_CLASS ((o), \ + INDICATOR_TYPE_POWER_DEVICE_PROVIDER_UPOWER, \ + IndicatorPowerDeviceProviderUPowerClass)) + +#define INDICATOR_IS_POWER_DEVICE_PROVIDER_UPOWER(o) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((o), \ + INDICATOR_TYPE_POWER_DEVICE_PROVIDER_UPOWER)) + +typedef struct _IndicatorPowerDeviceProviderUPower + IndicatorPowerDeviceProviderUPower; +typedef struct _IndicatorPowerDeviceProviderUPowerPriv + IndicatorPowerDeviceProviderUPowerPriv; +typedef struct _IndicatorPowerDeviceProviderUPowerClass + IndicatorPowerDeviceProviderUPowerClass; + +/** + * An IndicatorPowerDeviceProvider which gets its devices from UPower. + */ +struct _IndicatorPowerDeviceProviderUPower +{ + GObject parent_instance; + + IndicatorPowerDeviceProviderUPowerPriv * priv; +}; + +struct _IndicatorPowerDeviceProviderUPowerClass +{ + GObjectClass parent_class; +}; + +IndicatorPowerDeviceProvider * indicator_power_device_provider_upower_new (void); + +G_END_DECLS + +#endif /* __INDICATOR_POWER_DEVICE_PROVIDER_UPOWER__H__ */ diff --git a/src/device-provider.c b/src/device-provider.c new file mode 100644 index 0000000..5ccf588 --- /dev/null +++ b/src/device-provider.c @@ -0,0 +1,90 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "device-provider.h" + +enum +{ + SIGNAL_DEVICES_CHANGED, + SIGNAL_LAST +}; + +static guint signals[SIGNAL_LAST] = { 0 }; + +G_DEFINE_INTERFACE (IndicatorPowerDeviceProvider, + indicator_power_device_provider, + 0); + +static void +indicator_power_device_provider_default_init (IndicatorPowerDeviceProviderInterface * klass) +{ + signals[SIGNAL_DEVICES_CHANGED] = g_signal_new ( + "devices-changed", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (IndicatorPowerDeviceProviderInterface, devices_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +/*** +**** PUBLIC API +***/ + +/** + * Get a list of devices + * + * An easy way to free the list properly in one step is as follows: + * + * g_slist_free_full (list, (GDestroyNotify)g_object_unref); + * + * Return value: (element-type IndicatorPowerDevice) + * (transfer full): + * list of devices + */ +GList * +indicator_power_device_provider_get_devices (IndicatorPowerDeviceProvider * self) +{ + GList * devices; + IndicatorPowerDeviceProviderInterface * iface; + + g_return_val_if_fail (INDICATOR_IS_POWER_DEVICE_PROVIDER (self), NULL); + iface = INDICATOR_POWER_DEVICE_PROVIDER_GET_INTERFACE (self); + + if (iface->get_devices != NULL) + devices = iface->get_devices (self); + else + devices = NULL; + + return devices; +} + +/** + * Emits the "devices-changed" signal. + * + * This should only be called by subclasses. + */ +void +indicator_power_device_provider_emit_devices_changed (IndicatorPowerDeviceProvider * self) +{ + g_return_if_fail (INDICATOR_IS_POWER_DEVICE_PROVIDER (self)); + + g_signal_emit (self, signals[SIGNAL_DEVICES_CHANGED], 0, NULL); +} diff --git a/src/device-provider.h b/src/device-provider.h new file mode 100644 index 0000000..edef6f2 --- /dev/null +++ b/src/device-provider.h @@ -0,0 +1,80 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __INDICATOR_POWER_DEVICE_PROVIDER__H__ +#define __INDICATOR_POWER_DEVICE_PROVIDER__H__ + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define INDICATOR_TYPE_POWER_DEVICE_PROVIDER \ + (indicator_power_device_provider_get_type ()) + +#define INDICATOR_POWER_DEVICE_PROVIDER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + INDICATOR_TYPE_POWER_DEVICE_PROVIDER, \ + IndicatorPowerDeviceProvider)) + +#define INDICATOR_IS_POWER_DEVICE_PROVIDER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), INDICATOR_TYPE_POWER_DEVICE_PROVIDER)) + +#define INDICATOR_POWER_DEVICE_PROVIDER_GET_INTERFACE(inst) \ + (G_TYPE_INSTANCE_GET_INTERFACE ((inst), \ + INDICATOR_TYPE_POWER_DEVICE_PROVIDER, \ + IndicatorPowerDeviceProviderInterface)) + +typedef struct _IndicatorPowerDeviceProvider + IndicatorPowerDeviceProvider; + +typedef struct _IndicatorPowerDeviceProviderInterface + IndicatorPowerDeviceProviderInterface; + +/** + * An interface class for an object that provides IndicatorPowerDevices. + * + * Example uses: + * - in unit tests, a mock that feeds fake devices to the service + * - in production, an implementation that monitors upower + * - in the future, upower can be replaced by changing providers + */ +struct _IndicatorPowerDeviceProviderInterface +{ + GTypeInterface parent_iface; + + /* signals */ + void (*devices_changed) (IndicatorPowerDeviceProvider * self); + + /* virtual functions */ + GList* (*get_devices) (IndicatorPowerDeviceProvider * self); +}; + +GType indicator_power_device_provider_get_type (void); + +/*** +**** +***/ + +GList * indicator_power_device_provider_get_devices (IndicatorPowerDeviceProvider * self); + +void indicator_power_device_provider_emit_devices_changed (IndicatorPowerDeviceProvider * self); + +G_END_DECLS + +#endif /* __INDICATOR_POWER_DEVICE_PROVIDER__H__ */ diff --git a/src/device.c b/src/device.c index d028ab7..02c9e65 100644 --- a/src/device.c +++ b/src/device.c @@ -26,6 +26,7 @@ License along with this library. If not, see #endif #include <glib/gi18n-lib.h> +#include <gio/gio.h> #include "device.h" @@ -301,6 +302,26 @@ get_device_icon_index (gdouble percentage) return "000"; } +static const char * +device_kind_to_string (UpDeviceKind kind) +{ + switch (kind) + { + case UP_DEVICE_KIND_LINE_POWER: return "line-power"; + case UP_DEVICE_KIND_BATTERY: return "battery"; + case UP_DEVICE_KIND_UPS: return "ups"; + case UP_DEVICE_KIND_MONITOR: return "monitor"; + case UP_DEVICE_KIND_MOUSE: return "mouse"; + case UP_DEVICE_KIND_KEYBOARD: return "keyboard"; + case UP_DEVICE_KIND_PDA: return "pda"; + case UP_DEVICE_KIND_PHONE: return "phone"; + case UP_DEVICE_KIND_MEDIA_PLAYER: return "media-player"; + case UP_DEVICE_KIND_TABLET: return "tablet"; + case UP_DEVICE_KIND_COMPUTER: return "computer"; + default: return "unknown"; + } +} + /** indicator_power_device_get_icon_names: @device: #IndicatorPowerDevice from which to generate the icon names @@ -334,7 +355,7 @@ indicator_power_device_get_icon_names (const IndicatorPowerDevice * device) gdouble percentage = indicator_power_device_get_percentage (device); const UpDeviceKind kind = indicator_power_device_get_kind (device); const UpDeviceState state = indicator_power_device_get_state (device); - const gchar * kind_str = up_device_kind_to_string (kind); + const gchar * kind_str = device_kind_to_string (kind); GPtrArray * names = g_ptr_array_new (); @@ -534,7 +555,7 @@ device_kind_to_localised_string (UpDeviceKind kind) break; default: g_warning ("enum unrecognised: %i", kind); - text = up_device_kind_to_string (kind); + text = device_kind_to_string (kind); } return text; diff --git a/src/device.h b/src/device.h index 3f7bbee..ffbb5da 100644 --- a/src/device.h +++ b/src/device.h @@ -25,7 +25,6 @@ License along with this library. If not, see #define __INDICATOR_POWER_DEVICE_H__ #include <glib-object.h> -#include <libupower-glib/upower.h> G_BEGIN_DECLS @@ -46,6 +45,38 @@ typedef struct _IndicatorPowerDevicePrivate IndicatorPowerDevicePrivate; #define INDICATOR_POWER_DEVICE_PERCENTAGE "percentage" #define INDICATOR_POWER_DEVICE_TIME "time" +typedef enum +{ + UP_DEVICE_KIND_UNKNOWN, + UP_DEVICE_KIND_LINE_POWER, + UP_DEVICE_KIND_BATTERY, + UP_DEVICE_KIND_UPS, + UP_DEVICE_KIND_MONITOR, + UP_DEVICE_KIND_MOUSE, + UP_DEVICE_KIND_KEYBOARD, + UP_DEVICE_KIND_PDA, + UP_DEVICE_KIND_PHONE, + UP_DEVICE_KIND_MEDIA_PLAYER, + UP_DEVICE_KIND_TABLET, + UP_DEVICE_KIND_COMPUTER, + UP_DEVICE_KIND_LAST +} +UpDeviceKind; + +typedef enum +{ + UP_DEVICE_STATE_UNKNOWN, + UP_DEVICE_STATE_CHARGING, + UP_DEVICE_STATE_DISCHARGING, + UP_DEVICE_STATE_EMPTY, + UP_DEVICE_STATE_FULLY_CHARGED, + UP_DEVICE_STATE_PENDING_CHARGE, + UP_DEVICE_STATE_PENDING_DISCHARGE, + UP_DEVICE_STATE_LAST +} +UpDeviceState; + + /** * IndicatorPowerDeviceClass: * @parent_class: #GObjectClass diff --git a/src/indicator-power.c b/src/indicator-power.c deleted file mode 100644 index 729c7cf..0000000 --- a/src/indicator-power.c +++ /dev/null @@ -1,663 +0,0 @@ -/* -An indicator to power related information in the menubar. - -Copyright 2011 Canonical Ltd. - -Authors: - Javier Jardon <javier.jardon@codethink.co.uk> - -This program is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License version 3, as published -by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranties of -MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -#ifdef HAVE_CONFIG_H - #include "config.h" -#endif - -/* GStuff */ -#include <glib-object.h> -#include <glib/gi18n-lib.h> -#include <gio/gio.h> - -#include "dbus-listener.h" -#include "device.h" -#include "indicator-power.h" - -#define ICON_POLICY_KEY "icon-policy" - -#define DEFAULT_ICON "gpm-battery-missing" - -enum { - POWER_INDICATOR_ICON_POLICY_PRESENT, - POWER_INDICATOR_ICON_POLICY_CHARGE, - POWER_INDICATOR_ICON_POLICY_NEVER -}; - -struct _IndicatorPowerPrivate -{ - GtkMenu *menu; - - GtkLabel *label; - GtkImage *status_image; - gchar *accessible_desc; - - IndicatorPowerDbusListener * dbus_listener; - - GSList * devices; - IndicatorPowerDevice * device; - - GSettings *settings; -}; - - -/* LCOV_EXCL_START */ -INDICATOR_SET_VERSION -INDICATOR_SET_TYPE (INDICATOR_POWER_TYPE) -/* LCOV_EXCL_STOP */ - -/* Prototypes */ -static void indicator_power_dispose (GObject *object); -static void indicator_power_finalize (GObject *object); - -static GtkLabel* get_label (IndicatorObject * io); -static GtkImage* get_image (IndicatorObject * io); -static GtkMenu* get_menu (IndicatorObject * io); -static const gchar* get_accessible_desc (IndicatorObject * io); -static const gchar* get_name_hint (IndicatorObject * io); - -static void update_visibility (IndicatorPower * self); -static gboolean should_be_visible (IndicatorPower * self); - -static void on_entry_added (IndicatorObject * io, IndicatorObjectEntry * entry, gpointer user_data); - -/* -static void gsd_appeared_callback (GDBusConnection *connection, const gchar *name, const gchar *name_owner, gpointer user_data); -*/ - -/* LCOV_EXCL_START */ -G_DEFINE_TYPE (IndicatorPower, indicator_power, INDICATOR_OBJECT_TYPE); -/* LCOV_EXCL_STOP */ - -static void -indicator_power_class_init (IndicatorPowerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - IndicatorObjectClass *io_class = INDICATOR_OBJECT_CLASS (klass); - - g_type_class_add_private (klass, sizeof (IndicatorPowerPrivate)); - - object_class->dispose = indicator_power_dispose; - object_class->finalize = indicator_power_finalize; - - io_class->get_label = get_label; - io_class->get_image = get_image; - io_class->get_menu = get_menu; - io_class->get_accessible_desc = get_accessible_desc; - io_class->get_name_hint = get_name_hint; -} - -static void -indicator_power_init (IndicatorPower *self) -{ - IndicatorPowerPrivate * priv; - - priv = G_TYPE_INSTANCE_GET_PRIVATE (self, INDICATOR_POWER_TYPE, IndicatorPowerPrivate); - - priv->menu = GTK_MENU(gtk_menu_new()); - - priv->accessible_desc = NULL; - - priv->dbus_listener = g_object_new (INDICATOR_POWER_DBUS_LISTENER_TYPE, NULL); - g_signal_connect_swapped (priv->dbus_listener, INDICATOR_POWER_DBUS_LISTENER_DEVICES_ENUMERATED, - G_CALLBACK(indicator_power_set_devices), self); - - priv->settings = g_settings_new ("com.canonical.indicator.power"); - g_signal_connect_swapped (priv->settings, "changed::" ICON_POLICY_KEY, - G_CALLBACK(update_visibility), self); - g_object_set (G_OBJECT(self), - INDICATOR_OBJECT_DEFAULT_VISIBILITY, FALSE, - NULL); - - g_signal_connect (INDICATOR_OBJECT(self), INDICATOR_OBJECT_SIGNAL_ENTRY_ADDED, - G_CALLBACK(on_entry_added), NULL); - - self->priv = priv; -} - -static void -dispose_devices (IndicatorPower * self) -{ - IndicatorPowerPrivate * priv = self->priv; - - g_clear_object (&priv->device); - g_slist_free_full (priv->devices, g_object_unref); - priv->devices = NULL; -} -static void -indicator_power_dispose (GObject *object) -{ - IndicatorPower *self = INDICATOR_POWER(object); - IndicatorPowerPrivate * priv = self->priv; - - dispose_devices (self); - - g_clear_object (&priv->label); - g_clear_object (&priv->status_image); - g_clear_object (&priv->dbus_listener); - g_clear_object (&priv->settings); - - G_OBJECT_CLASS (indicator_power_parent_class)->dispose (object); -} - -static void -indicator_power_finalize (GObject *object) -{ - IndicatorPower *self = INDICATOR_POWER(object); - IndicatorPowerPrivate * priv = self->priv; - - g_free (priv->accessible_desc); - - G_OBJECT_CLASS (indicator_power_parent_class)->finalize (object); -} - -/*** -**** -***/ - -static void -spawn_command_line_async (const char * command) -{ - GError * err = NULL; - if (!g_spawn_command_line_async (command, &err)) - g_warning ("Couldn't execute command \"%s\": %s", command, err->message); - g_clear_error (&err); -} - -static void -option_toggled_cb (GtkCheckMenuItem *item, IndicatorPower * self) -{ - gtk_widget_set_visible (GTK_WIDGET (self->priv->label), - gtk_check_menu_item_get_active(item)); -} - -/* ensure that the entry is using self's accessible description */ -static void -refresh_entry_accessible_desc (IndicatorPower * self, IndicatorObjectEntry * entry) -{ - const char * newval = self->priv->accessible_desc; - - if (entry->accessible_desc != newval) - { - g_debug ("%s: setting entry %p accessible description to '%s'", G_STRFUNC, entry, newval); - entry->accessible_desc = newval; - g_signal_emit (self, INDICATOR_OBJECT_SIGNAL_ACCESSIBLE_DESC_UPDATE_ID, 0, entry); - } -} - -static void -on_entry_added (IndicatorObject * io, - IndicatorObjectEntry * entry, - gpointer user_data G_GNUC_UNUSED) -{ - refresh_entry_accessible_desc (INDICATOR_POWER(io), entry); -} - -static void -set_accessible_desc (IndicatorPower *self, const gchar *desc) -{ - g_debug ("%s: setting accessible description to '%s'", G_STRFUNC, desc); - - if (desc && *desc) - { - /* update our copy of the string */ - char * oldval = self->priv->accessible_desc; - self->priv->accessible_desc = g_strdup (desc); - - /* ensure that the entries are using self's accessible description */ - GList * l; - GList * entries = indicator_object_get_entries(INDICATOR_OBJECT(self)); - for (l=entries; l!=NULL; l=l->next) - refresh_entry_accessible_desc (self, l->data); - - /* cleanup */ - g_list_free (entries); - g_free (oldval); - } -} - -static gboolean -menu_add_device (GtkMenu * menu, const IndicatorPowerDevice * device) -{ - gboolean added = FALSE; - const UpDeviceKind kind = indicator_power_device_get_kind (device); - - if (kind != UP_DEVICE_KIND_LINE_POWER) - { - GtkWidget *icon; - GtkWidget *item; - GtkWidget *details_label; - GtkWidget *grid; - GIcon *device_gicon; - gchar *short_details = NULL; - gchar *details = NULL; - gchar *accessible_name = NULL; - AtkObject *atk_object; - - /* Process the data */ - device_gicon = indicator_power_device_get_gicon (device); - icon = gtk_image_new_from_gicon (device_gicon, GTK_ICON_SIZE_SMALL_TOOLBAR); - g_clear_object (&device_gicon); - - indicator_power_device_get_time_details (device, &short_details, &details, &accessible_name); - - /* Create menu item */ - item = gtk_image_menu_item_new (); - atk_object = gtk_widget_get_accessible(item); - if (atk_object != NULL) - atk_object_set_name (atk_object, accessible_name); - - grid = gtk_grid_new (); - gtk_grid_set_column_spacing (GTK_GRID (grid), 6); - gtk_grid_attach (GTK_GRID (grid), icon, 0, 0, 1, 1); - details_label = gtk_label_new (details); - gtk_grid_attach_next_to (GTK_GRID (grid), details_label, icon, GTK_POS_RIGHT, 1, 1); - gtk_container_add (GTK_CONTAINER (item), grid); - gtk_widget_show (grid); - - gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); - added = TRUE; - - g_signal_connect_swapped (G_OBJECT (item), "activate", - G_CALLBACK (spawn_command_line_async), "gnome-power-statistics"); - - g_free (short_details); - g_free (details); - g_free (accessible_name); - } - - return added; -} - -static gsize -menu_add_devices (GtkMenu * menu, GSList * devices) -{ - GSList * l; - gsize n_added = 0; - - for (l=devices; l!=NULL; l=l->next) - if (menu_add_device (menu, l->data)) - ++n_added; - - return n_added; -} - -static gboolean -get_greeter_mode (void) -{ - const gchar *var; - var = g_getenv("INDICATOR_GREETER_MODE"); - return (g_strcmp0(var, "1") == 0); -} - -static void -build_menu (IndicatorPower *self) -{ - GtkWidget *item; - GtkWidget *image; - GList *children; - gsize n_devices = 0; - IndicatorPowerPrivate * priv = self->priv; - - /* remove the existing menuitems */ - children = gtk_container_get_children (GTK_CONTAINER (priv->menu)); - g_list_foreach (children, (GFunc) gtk_widget_destroy, NULL); - g_list_free (children); - - /* devices */ - n_devices = menu_add_devices (priv->menu, priv->devices); - - if (!get_greeter_mode ()) { - /* only do the separator if we have at least one device */ - if (n_devices != 0) - { - item = gtk_separator_menu_item_new (); - gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), item); - } - - /* options */ - item = gtk_check_menu_item_new_with_label (_("Show Time in Menu Bar")); - g_signal_connect (item, "toggled", G_CALLBACK(option_toggled_cb), self); - g_settings_bind (priv->settings, "show-time", item, "active", G_SETTINGS_BIND_DEFAULT); - gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), item); - - /* preferences */ - item = gtk_image_menu_item_new_with_label (_("Power Settings…")); - image = gtk_image_new_from_icon_name (GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image); - g_signal_connect_swapped (G_OBJECT (item), "activate", - G_CALLBACK (spawn_command_line_async), "gnome-control-center power"); - gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), item); - } - - /* show the menu */ - gtk_widget_show_all (GTK_WIDGET (priv->menu)); -} - -/* the higher the weight, the more interesting the device */ -static int -get_device_kind_weight (const IndicatorPowerDevice * device) -{ - UpDeviceKind kind; - static gboolean initialized = FALSE; - static int weights[UP_DEVICE_KIND_LAST]; - - kind = indicator_power_device_get_kind (device); - g_return_val_if_fail (0<=kind && kind<UP_DEVICE_KIND_LAST, 0); - - if (G_UNLIKELY(!initialized)) - { - int i; - - initialized = TRUE; - - for (i=0; i<UP_DEVICE_KIND_LAST; i++) - weights[i] = 1; - weights[UP_DEVICE_KIND_BATTERY] = 2; - weights[UP_DEVICE_KIND_LINE_POWER] = 0; - } - - return weights[kind]; -} - -/* sort devices from most interesting to least interesting on this criteria: - 1. discharging items from least time remaining until most time remaining - 2. discharging items with an unknown time remaining - 3. charging items from most time left to charge to least time left to charge - 4. charging items with an unknown time remaining - 5. batteries, then non-line power, then line-power */ -static gint -device_compare_func (gconstpointer ga, gconstpointer gb) -{ - int ret; - int state; - const IndicatorPowerDevice * a = INDICATOR_POWER_DEVICE(ga); - const IndicatorPowerDevice * b = INDICATOR_POWER_DEVICE(gb); - const int a_state = indicator_power_device_get_state (a); - const int b_state = indicator_power_device_get_state (b); - const gdouble a_percentage = indicator_power_device_get_percentage (a); - const gdouble b_percentage = indicator_power_device_get_percentage (b); - const time_t a_time = indicator_power_device_get_time (a); - const time_t b_time = indicator_power_device_get_time (b); - - ret = 0; - - state = UP_DEVICE_STATE_DISCHARGING; - if (!ret && ((a_state == state) || (b_state == state))) - { - if (a_state != state) /* b is discharging */ - { - ret = 1; - } - else if (b_state != state) /* a is discharging */ - { - ret = -1; - } - else /* both are discharging; least-time-left goes first */ - { - if (!a_time || !b_time) /* known time always trumps unknown time */ - ret = a_time ? -1 : 1; - else if (a_time != b_time) - ret = a_time < b_time ? -1 : 1; - else - ret = a_percentage < b_percentage ? -1 : 1; - } - } - - state = UP_DEVICE_STATE_CHARGING; - if (!ret && (((a_state == state) && a_time) || ((b_state == state) && b_time))) - { - if (a_state != state) /* b is charging */ - { - ret = 1; - } - else if (b_state != state) /* a is charging */ - { - ret = -1; - } - else /* both are discharging; most-time-to-charge goes first */ - { - if (!a_time || !b_time) /* known time always trumps unknown time */ - ret = a_time ? -1 : 1; - else if (a_time != b_time) - ret = a_time > b_time ? -1 : 1; - else - ret = a_percentage < b_percentage ? -1 : 1; - } - } - - if (!ret) /* neither device is charging nor discharging... */ - { - const int weight_a = get_device_kind_weight (a); - const int weight_b = get_device_kind_weight (b); - - if (weight_a > weight_b) - { - ret = -1; - } - else if (weight_a < weight_b) - { - ret = 1; - } - } - - if (!ret) - ret = a_state - b_state; - - return ret; -} - -IndicatorPowerDevice * -indicator_power_choose_primary_device (GSList * devices) -{ - IndicatorPowerDevice * primary = NULL; - - if (devices != NULL) - { - GSList * tmp; - - tmp = g_slist_copy (devices); - tmp = g_slist_sort (tmp, device_compare_func); - primary = g_object_ref (tmp->data); - - g_slist_free (tmp); - } - - return primary; -} - -static void -put_primary_device (IndicatorPower *self, IndicatorPowerDevice *device) -{ - IndicatorPowerPrivate * priv = self->priv; - - /* set icon */ - GIcon * device_gicon = indicator_power_device_get_gicon (device); - gtk_image_set_from_gicon (priv->status_image, device_gicon, GTK_ICON_SIZE_LARGE_TOOLBAR); - g_clear_object (&device_gicon); - gtk_widget_show (GTK_WIDGET (priv->status_image)); - - /* get the description */ - gchar * short_details; - gchar * details; - gchar * accessible_name; - indicator_power_device_get_time_details (device, &short_details, &details, &accessible_name); - gtk_label_set_label (GTK_LABEL (priv->label), short_details); - set_accessible_desc (self, accessible_name); - g_free (accessible_name); - g_free (details); - g_free (short_details); -} - -void -indicator_power_set_devices (IndicatorPower * self, GSList * devices) -{ - /* LCOV_EXCL_START */ - g_return_if_fail (IS_INDICATOR_POWER(self)); - /* LCOV_EXCL_STOP */ - - IndicatorPowerPrivate * priv = self->priv; - - /* update our devices & primary device */ - g_slist_foreach (devices, (GFunc)g_object_ref, NULL); - dispose_devices (self); - priv->devices = g_slist_copy (devices); - priv->device = indicator_power_choose_primary_device (devices); - - /* and our menus/visibility from the new device list */ - if (priv->device != NULL) - put_primary_device (self, priv->device); - else - g_message ("Couldn't find primary device"); - build_menu (self); - update_visibility (self); -} - -static void -update_visibility (IndicatorPower * self) -{ - indicator_object_set_visible (INDICATOR_OBJECT (self), - should_be_visible (self)); -} - - -/* Grabs the label. Creates it if it doesn't - exist already */ -static GtkLabel * -get_label (IndicatorObject *io) -{ - IndicatorPower *self = INDICATOR_POWER (io); - IndicatorPowerPrivate * priv = self->priv; - - if (priv->label == NULL) - { - /* Create the label if it doesn't exist already */ - priv->label = GTK_LABEL (gtk_label_new ("")); - g_object_ref_sink (priv->label); - gtk_widget_set_visible (GTK_WIDGET (priv->label), FALSE); - } - - return priv->label; -} - -static GtkImage * -get_image (IndicatorObject *io) -{ - GIcon *gicon; - IndicatorPower *self = INDICATOR_POWER (io); - IndicatorPowerPrivate * priv = self->priv; - - if (priv->status_image == NULL) - { - /* Will create the status icon if it doesn't exist already */ - gicon = g_themed_icon_new (DEFAULT_ICON); - priv->status_image = GTK_IMAGE (gtk_image_new_from_gicon (gicon, - GTK_ICON_SIZE_LARGE_TOOLBAR)); - g_object_ref_sink (priv->status_image); - g_object_unref (gicon); - } - - return priv->status_image; -} - -static GtkMenu * -get_menu (IndicatorObject *io) -{ - IndicatorPower *self = INDICATOR_POWER (io); - - build_menu (self); - - return GTK_MENU (self->priv->menu); -} - -static const gchar * -get_accessible_desc (IndicatorObject *io) -{ - IndicatorPower *self = INDICATOR_POWER (io); - - return self->priv->accessible_desc; -} - -static const gchar * -get_name_hint (IndicatorObject *io) -{ - return PACKAGE_NAME; -} - -/*** -**** -***/ - -static void -count_batteries (GSList * devices, int *total, int *inuse) -{ - GSList * l; - - for (l=devices; l!=NULL; l=l->next) - { - const IndicatorPowerDevice * device = INDICATOR_POWER_DEVICE(l->data); - - if (indicator_power_device_get_kind(device) == UP_DEVICE_KIND_BATTERY || - indicator_power_device_get_kind(device) == UP_DEVICE_KIND_UPS) - { - ++*total; - - const UpDeviceState state = indicator_power_device_get_state (device); - if ((state == UP_DEVICE_STATE_CHARGING) || (state == UP_DEVICE_STATE_DISCHARGING)) - ++*inuse; - } - } - - g_debug("count_batteries found %d batteries (%d are charging/discharging)", *total, *inuse); -} - -static gboolean -should_be_visible (IndicatorPower * self) -{ - gboolean visible = TRUE; - IndicatorPowerPrivate * priv = self->priv; - - const int icon_policy = g_settings_get_enum (priv->settings, ICON_POLICY_KEY); - - g_debug ("icon_policy is: %d (present==0, charge==1, never==2)", icon_policy); - - if (icon_policy == POWER_INDICATOR_ICON_POLICY_NEVER) - { - visible = FALSE; - } - else - { - int batteries=0, inuse=0; - count_batteries (priv->devices, &batteries, &inuse); - - if (icon_policy == POWER_INDICATOR_ICON_POLICY_PRESENT) - { - visible = batteries > 0; - } - else if (icon_policy == POWER_INDICATOR_ICON_POLICY_CHARGE) - { - visible = inuse > 0; - } - } - - g_debug ("should_be_visible: %s", visible?"yes":"no"); - return visible; -} diff --git a/src/indicator-power.h b/src/indicator-power.h deleted file mode 100644 index 34d0d0e..0000000 --- a/src/indicator-power.h +++ /dev/null @@ -1,60 +0,0 @@ -/* -An indicator to power related information in the menubar. - -Copyright 2011 Canonical Ltd. - -Authors: - Javier Jardon <javier.jardon@codethink.co.uk> - -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/>. -*/ - -/* Gtk required */ -#include <gtk/gtk.h> - -/* parent class */ -#include <libindicator/indicator.h> -#include <libindicator/indicator-object.h> - -G_BEGIN_DECLS - -#define INDICATOR_POWER_TYPE (indicator_power_get_type ()) -#define INDICATOR_POWER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), INDICATOR_POWER_TYPE, IndicatorPower)) -#define INDICATOR_POWER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), INDICATOR_POWER_TYPE, IndicatorPowerClass)) -#define IS_INDICATOR_POWER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), INDICATOR_POWER_TYPE)) -#define IS_INDICATOR_POWER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), INDICATOR_POWER_TYPE)) -#define INDICATOR_POWER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), INDICATOR_POWER_TYPE, IndicatorPowerClass)) - -typedef struct _IndicatorPower IndicatorPower; -typedef struct _IndicatorPowerClass IndicatorPowerClass; -typedef struct _IndicatorPowerPrivate IndicatorPowerPrivate; - -struct _IndicatorPowerClass -{ - IndicatorObjectClass parent_class; -}; - -struct _IndicatorPower -{ - IndicatorObject parent_instance; - IndicatorPowerPrivate * priv; -}; - -GType indicator_power_get_type (void) G_GNUC_CONST; - -void indicator_power_set_devices (IndicatorPower * power, - GSList * devices); - -IndicatorPowerDevice* indicator_power_choose_primary_device (GSList * devices); - -G_END_DECLS diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..ef615dc --- /dev/null +++ b/src/main.c @@ -0,0 +1,68 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <locale.h> +#include <stdlib.h> /* exit() */ + +#include <glib/gi18n.h> +#include <gio/gio.h> + +#include "device.h" +#include "device-provider-upower.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 ((GMainLoop*)loop); +} + +int +main (int argc G_GNUC_UNUSED, char ** argv G_GNUC_UNUSED) +{ + GMainLoop * loop; + IndicatorPowerService * service; + IndicatorPowerDeviceProvider * device_provider; + + /* boilerplate i18n */ + setlocale (LC_ALL, ""); + bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); + textdomain (GETTEXT_PACKAGE); + + /* run */ + device_provider = indicator_power_device_provider_upower_new (); + 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_clear_object (&device_provider); + g_clear_object (&service); + g_main_loop_unref (loop); + return 0; +} diff --git a/src/org.freedesktop.UPower.Device.xml b/src/org.freedesktop.UPower.Device.xml new file mode 100644 index 0000000..7c9a65b --- /dev/null +++ b/src/org.freedesktop.UPower.Device.xml @@ -0,0 +1,705 @@ +<!DOCTYPE node PUBLIC +"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" +"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd" [ + <!ENTITY ERROR_GENERAL "org.freedesktop.UPower.Device.GeneralError"> +]> +<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd"> + <interface name="org.freedesktop.UPower.Device"> + <doc:doc> + <doc:description> + <doc:para> + Objects implementing this interface are usually discovered through + the <doc:tt>org.freedesktop.UPower</doc:tt> interface on + the <doc:tt>/org/freedesktop/UPower</doc:tt> object on + the D-Bus system bus service with the well-known + name <doc:tt>org.freedesktop.UPower</doc:tt> using + the + <doc:ref type="method" to="Power.EnumerateDevices">EnumerateDevices</doc:ref> + method. + </doc:para> + <doc:para> + <doc:example language="shell" title="simple example"> + <doc:code> +$ dbus-send --print-reply \ + --system \ + --dest=org.freedesktop.UPower \ + /org/freedesktop/UPower/devices/battery_BAT0 \ + org.freedesktop.DBus.Properties.GetAll \ + string:org.freedesktop.UPower.Device + +method return sender=:1.386 -> dest=:1.477 reply_serial=2 + array [ + dict entry( + string "native-path" + variant string "/sys/devices/LNXSYSTM:00/device:00/PNP0A08:00/device:01/PNP0C09:00/PNP0C0A:00/power_supply/BAT0" + ) + dict entry( + string "vendor" + variant string "SONY" + ) + dict entry( + string "model" + variant string "42T4568" + ) + dict entry( + string "serial" + variant string "4179" + ) + dict entry( + string "update-time" + variant uint64 1226417875 + ) + dict entry( + string "type" + variant uint 2 + ) + dict entry( + string "power-supply" + variant boolean true + ) + dict entry( + string "has-history" + variant boolean true + ) + dict entry( + string "has-statistics" + variant boolean true + ) + dict entry( + string "online" + variant boolean false + ) + dict entry( + string "energy" + variant double 72.85 + ) + dict entry( + string "energy-empty" + variant double 0 + ) + dict entry( + string "energy-full" + variant double 74.55 + ) + dict entry( + string "energy-full-design" + variant double 74.88 + ) + dict entry( + string "energy-rate" + variant double 0 + ) + dict entry( + string "voltage" + variant double 16.415 + ) + dict entry( + string "time-to-empty" + variant int64 0 + ) + dict entry( + string "time-to-full" + variant int64 0 + ) + dict entry( + string "percentage" + variant double 97.7197 + ) + dict entry( + string "is-present" + variant boolean true + ) + dict entry( + string "state" + variant uint 3 + ) + dict entry( + string "is-rechargeable" + variant boolean true + ) + dict entry( + string "capacity" + variant double 100 + ) + dict entry( + string "technology" + variant uint 1 + ) + ] + </doc:code> + </doc:example> + </doc:para> + <doc:para> + Unless otherwise noted, an empty string or the value 0 in a + property on this interface means not set. + </doc:para> + </doc:description> + </doc:doc> + + + <!-- ************************************************************ --> + <method name="Refresh"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <doc:doc> + <doc:description> + <doc:para> + Refreshes the data collected from the power source. + </doc:para> + </doc:description> + <doc:permission>Callers need the org.freedesktop.upower.refresh-power-source authorization</doc:permission> + <doc:errors> + <doc:error name="&ERROR_GENERAL;">if an error occured while refreshing</doc:error> + </doc:errors> + </doc:doc> + </method> + + <!-- ************************************************************ --> + <signal name="Changed"> + <doc:doc> + <doc:description> + <doc:para> + Some value on the power source changed. + </doc:para> + </doc:description> + </doc:doc> + </signal> + + <!-- ************************************************************ --> + <method name="GetHistory"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg name="type" direction="in" type="s"> + <doc:doc><doc:summary>The type of history. + Valid types are <doc:tt>rate</doc:tt> or <doc:tt>charge</doc:tt>.</doc:summary></doc:doc> + </arg> + <arg name="timespan" direction="in" type="u"> + <doc:doc><doc:summary>The amount of data to return in seconds, or 0 for all.</doc:summary></doc:doc> + </arg> + <arg name="resolution" direction="in" type="u"> + <doc:doc> + <doc:summary> + The approximate number of points to return. + A higher resolution is more accurate, at the expense of plotting speed. + </doc:summary> + </doc:doc> + </arg> + <arg name="data" direction="out" type="a(udu)"> + <doc:doc><doc:summary> + The history data for the power device, if the device supports history. + Data is ordered from the earliest in time, to the newest data point. + Each element contains the following members: + <doc:list> + <doc:item> + <doc:term>time</doc:term> + <doc:definition> + The time value in seconds from the <doc:tt>gettimeofday()</doc:tt> method. + </doc:definition> + </doc:item> + <doc:item> + <doc:term>value</doc:term> + <doc:definition> + The data value, for instance the rate in W or the charge in %. + </doc:definition> + </doc:item> + <doc:item> + <doc:term>state</doc:term> + <doc:definition> + The state of the device, for instance <doc:tt>charging</doc:tt> or + <doc:tt>discharging</doc:tt>. + </doc:definition> + </doc:item> + </doc:list> + </doc:summary></doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para> + Gets history for the power device that is persistent across reboots. + </doc:para> + </doc:description> + </doc:doc> + </method> + + <!-- ************************************************************ --> + <method name="GetStatistics"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg name="type" direction="in" type="s"> + <doc:doc><doc:summary>The mode for the statistics. + Valid types are <doc:tt>charging</doc:tt> or <doc:tt>discharging</doc:tt>.</doc:summary></doc:doc> + </arg> + <arg name="data" direction="out" type="a(dd)"> + <doc:doc><doc:summary> + The statistics data for the power device. + Each element contains the following members: + <doc:list> + <doc:item> + <doc:term>value</doc:term> + <doc:definition> + The value of the percentage point, usually in seconds + </doc:definition> + </doc:item> + <doc:item> + <doc:term>accuracy</doc:term> + <doc:definition> + The accuracy of the prediction in percent. + </doc:definition> + </doc:item> + </doc:list> + </doc:summary></doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para> + Gets statistics for the power device that may be interesting + to show on a graph in the session. + </doc:para> + </doc:description> + </doc:doc> + </method> + + <!-- ************************************************************ --> + <property name="NativePath" type="s" access="read"> + <doc:doc> + <doc:description> + <doc:para> + OS specific native path of the power source. On Linux this + is the sysfs path, for + example <doc:tt>/sys/devices/LNXSYSTM:00/device:00/PNP0C0A:00/power_supply/BAT0</doc:tt>. Is + blank if the device is being driven by a user space + driver. + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="Vendor" type="s" access="read"> + <doc:doc> + <doc:description> + <doc:para> + Name of the vendor of the battery. + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="Model" type="s" access="read"> + <doc:doc> + <doc:description> + <doc:para> + Name of the model of this battery. + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="Serial" type="s" access="read"> + <doc:doc> + <doc:description> + <doc:para> + Unique serial number of the battery. + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="UpdateTime" type="t" access="read"> + <doc:doc> + <doc:description> + <doc:para> + The point in time (seconds since the Epoch Jan 1, 1970 + 0:00 UTC) that data was read from the power source. + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="Type" type="u" access="read"> + <doc:doc> + <doc:description> + <doc:para> + Type of power source. + </doc:para> + <doc:list> + <doc:item> + <doc:term>0</doc:term><doc:definition>Unknown</doc:definition> + </doc:item> + <doc:item> + <doc:term>1</doc:term><doc:definition>Line Power</doc:definition> + </doc:item> + <doc:item> + <doc:term>2</doc:term><doc:definition>Battery</doc:definition> + </doc:item> + <doc:item> + <doc:term>3</doc:term><doc:definition>Ups</doc:definition> + </doc:item> + <doc:item> + <doc:term>4</doc:term><doc:definition>Monitor</doc:definition> + </doc:item> + <doc:item> + <doc:term>5</doc:term><doc:definition>Mouse</doc:definition> + </doc:item> + <doc:item> + <doc:term>6</doc:term><doc:definition>Keyboard</doc:definition> + </doc:item> + <doc:item> + <doc:term>7</doc:term><doc:definition>Pda</doc:definition> + </doc:item> + <doc:item> + <doc:term>8</doc:term><doc:definition>Phone</doc:definition> + </doc:item> + </doc:list> + </doc:description> + </doc:doc> + </property> + + <property name="PowerSupply" type="b" access="read"> + <doc:doc> + <doc:description> + <doc:para> + If the power device is used to supply the system. + This would be set TRUE for laptop batteries and UPS devices, + but set FALSE for wireless mice or PDAs. + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="HasHistory" type="b" access="read"> + <doc:doc> + <doc:description> + <doc:para> + If the power device has history. + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="HasStatistics" type="b" access="read"> + <doc:doc> + <doc:description> + <doc:para> + If the power device has statistics. + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="Online" type="b" access="read"> + <doc:doc> + <doc:description> + <doc:para> + Whether power is currently being provided through line power. + This property is only valid if the property + <doc:ref type="property" to="Source:Type">type</doc:ref> + has the value "line-power". + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="Energy" type="d" access="read"> + <doc:doc> + <doc:description> + <doc:para> + Amount of energy (measured in Wh) currently available in + the power source. + </doc:para><doc:para> + This property is only valid if the property + <doc:ref type="property" to="Source:Type">type</doc:ref> + has the value "battery". + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="EnergyEmpty" type="d" access="read"> + <doc:doc> + <doc:description> + <doc:para> + Amount of energy (measured in Wh) in the power source when + it's considered to be empty. + </doc:para><doc:para> + This property is only valid if the property + <doc:ref type="property" to="Source:Type">type</doc:ref> + has the value "battery". + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="EnergyFull" type="d" access="read"> + <doc:doc> + <doc:description> + <doc:para> + Amount of energy (measured in Wh) in the power source when + it's considered full. + </doc:para><doc:para> + This property is only valid if the property + <doc:ref type="property" to="Source:Type">type</doc:ref> + has the value "battery". + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="EnergyFullDesign" type="d" access="read"> + <doc:doc> + <doc:description> + <doc:para> + Amount of energy (measured in Wh) the power source is + designed to hold when it's considered full. + </doc:para><doc:para> + This property is only valid if the property + <doc:ref type="property" to="Source:Type">type</doc:ref> + has the value "battery". + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="EnergyRate" type="d" access="read"> + <doc:doc> + <doc:description> + <doc:para> + Amount of energy being drained from the source, measured + in W. If positive, the source is being discharged, if + negative it's being charged. + </doc:para><doc:para> + This property is only valid if the property + <doc:ref type="property" to="Source:Type">type</doc:ref> + has the value "battery". + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="Voltage" type="d" access="read"> + <doc:doc> + <doc:description> + <doc:para> + Voltage in the Cell or being recorded by the meter. + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="TimeToEmpty" type="x" access="read"> + <doc:doc> + <doc:description> + <doc:para> + Number of seconds until the power source is considered empty. + Is set to 0 if unknown. + </doc:para><doc:para> + This property is only valid if the property + <doc:ref type="property" to="Source:Type">type</doc:ref> + has the value "battery". + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="TimeToFull" type="x" access="read"> + <doc:doc> + <doc:description> + <doc:para> + Number of seconds until the power source is considered full. + Is set to 0 if unknown. + </doc:para><doc:para> + This property is only valid if the property + <doc:ref type="property" to="Source:Type">type</doc:ref> + has the value "battery". + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="Percentage" type="d" access="read"> + <doc:doc> + <doc:description> + <doc:para> + The amount of energy left in the power source expressed as + a percentage between 0 and 100. Typically this is the same as + (<doc:ref type="property" to="Source:Energy">energy</doc:ref> - + <doc:ref type="property" to="Source:EnergyEmpty">energy-empty</doc:ref>) / + (<doc:ref type="property" to="Source:EnergyFull">energy-full</doc:ref> - + <doc:ref type="property" to="Source:EnergyEmpty">energy-empty</doc:ref>). + However, some primitive power sources are capable of only + reporting percentages and in this case the energy-* + properties will be unset while this property is set. + </doc:para><doc:para> + This property is only valid if the property + <doc:ref type="property" to="Source:Type">type</doc:ref> + has the value "battery". + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="IsPresent" type="b" access="read"> + <doc:doc> + <doc:description> + <doc:para> + If the power source is present in the bay. + This field is required as some batteries are hot-removable, for example + expensive UPS and most laptop batteries. + </doc:para><doc:para> + This property is only valid if the property + <doc:ref type="property" to="Source:Type">type</doc:ref> + has the value "battery". + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="State" type="u" access="read"> + <doc:doc> + <doc:description> + <doc:para> + The battery power state. + </doc:para> + <doc:list> + <doc:item> + <doc:term>0</doc:term><doc:definition>Unknown</doc:definition> + </doc:item> + <doc:item> + <doc:term>1</doc:term><doc:definition>Charging</doc:definition> + </doc:item> + <doc:item> + <doc:term>2</doc:term><doc:definition>Discharging</doc:definition> + </doc:item> + <doc:item> + <doc:term>3</doc:term><doc:definition>Empty</doc:definition> + </doc:item> + <doc:item> + <doc:term>4</doc:term><doc:definition>Fully charged</doc:definition> + </doc:item> + <doc:item> + <doc:term>5</doc:term><doc:definition>Pending charge</doc:definition> + </doc:item> + <doc:item> + <doc:term>6</doc:term><doc:definition>Pending discharge</doc:definition> + </doc:item> + </doc:list> + <doc:para> + This property is only valid if the property + <doc:ref type="property" to="Source:Type">type</doc:ref> + has the value "battery". + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="IsRechargeable" type="b" access="read"> + <doc:doc> + <doc:description> + <doc:para> + If the power source is rechargeable. + </doc:para><doc:para> + This property is only valid if the property + <doc:ref type="property" to="Source:Type">type</doc:ref> + has the value "battery". + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="Capacity" type="d" access="read"> + <doc:doc> + <doc:description> + <doc:para> + The capacity of the power source expressed as a percentage between 0 and 100. + The capacity of the battery will reduce with age. + A capacity value less than 75% is usually a sign that you should renew your battery. + Typically this value is the same as + (<doc:ref type="property" to="Source:FullDesign">full-design</doc:ref> / + <doc:ref type="property" to="Source:Full">full</doc:ref>) * 100. + However, some primitive power sources are not capable reporting capacity + and in this case the capacity property will be unset. + </doc:para><doc:para> + This property is only valid if the property + <doc:ref type="property" to="Source:Type">type</doc:ref> + has the value "battery". + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="Technology" type="u" access="read"> + <doc:doc> + <doc:description> + <doc:para> + Technology used in the battery: + </doc:para> + <doc:list> + <doc:item> + <doc:term>0</doc:term><doc:definition>Unknown</doc:definition> + </doc:item> + <doc:item> + <doc:term>1</doc:term><doc:definition>Lithium ion</doc:definition> + </doc:item> + <doc:item> + <doc:term>2</doc:term><doc:definition>Lithium polymer</doc:definition> + </doc:item> + <doc:item> + <doc:term>3</doc:term><doc:definition>Lithium iron phosphate</doc:definition> + </doc:item> + <doc:item> + <doc:term>4</doc:term><doc:definition>Lead acid</doc:definition> + </doc:item> + <doc:item> + <doc:term>5</doc:term><doc:definition>Nickel cadmium</doc:definition> + </doc:item> + <doc:item> + <doc:term>6</doc:term><doc:definition>Nickel metal hydride</doc:definition> + </doc:item> + </doc:list> + <doc:para> + This property is only valid if the property + <doc:ref type="property" to="Source:Type">type</doc:ref> + has the value "battery". + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="RecallNotice" type="b" access="read"> + <doc:doc> + <doc:description> + <doc:para> + If the device may have been recalled by the vendor due to a suspected + fault. + This key does not imply the device is faulty, only that it approximatly + matches the description from the vendor of units that were recalled. + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="RecallVendor" type="s" access="read"> + <doc:doc> + <doc:description> + <doc:para> + The vendor that is handling the hardware recall. + </doc:para> + <doc:para> + This property is only valid if the property recall-notice is true. + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="RecallUrl" type="s" access="read"> + <doc:doc> + <doc:description> + <doc:para> + The URL to visit about the hardware recall. + </doc:para> + <doc:para> + This property is only valid if the property recall-notice is true. + </doc:para> + </doc:description> + </doc:doc> + </property> + + </interface> + +</node> diff --git a/src/org.freedesktop.UPower.xml b/src/org.freedesktop.UPower.xml new file mode 100644 index 0000000..7b73583 --- /dev/null +++ b/src/org.freedesktop.UPower.xml @@ -0,0 +1,43 @@ +<!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 new file mode 100644 index 0000000..012ef13 --- /dev/null +++ b/src/service.c @@ -0,0 +1,1015 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * Ted Gould <ted@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <glib/gi18n.h> +#include <gio/gio.h> + +#include "device.h" +#include "device-provider.h" +#include "service.h" + +#define BUS_NAME "com.canonical.indicator.power" +#define BUS_PATH "/com/canonical/indicator/power" + +#define SETTINGS_SHOW_TIME_S "show-time" +#define SETTINGS_ICON_POLICY_S "icon-policy" + +G_DEFINE_TYPE (IndicatorPowerService, + indicator_power_service, + G_TYPE_OBJECT) + +enum +{ + SIGNAL_NAME_LOST, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +enum +{ + PROP_0, + PROP_DEVICE_PROVIDER, + LAST_PROP +}; + +static GParamSpec * properties[LAST_PROP]; + +enum +{ + SECTION_HEADER = (1<<0), + SECTION_DEVICES = (1<<1), + SECTION_SETTINGS = (1<<2), +}; + +enum +{ + PROFILE_DESKTOP, + PROFILE_GREETER, + N_PROFILES +}; + +static const char * const menu_names[N_PROFILES] = +{ + "desktop", + "greeter" +}; + +enum +{ + POWER_INDICATOR_ICON_POLICY_PRESENT, + POWER_INDICATOR_ICON_POLICY_CHARGE, + POWER_INDICATOR_ICON_POLICY_NEVER +}; + +struct ProfileMenuInfo +{ + /* the root level -- the header is the only child of this */ + GMenu * menu; + + /* parent of the sections. This is the header's submenu */ + GMenu * submenu; + + guint export_id; +}; + +struct _IndicatorPowerServicePrivate +{ + GCancellable * cancellable; + + GSettings * settings; + + guint own_id; + guint actions_export_id; + GDBusConnection * conn; + + struct ProfileMenuInfo menus[N_PROFILES]; + + GSimpleActionGroup * actions; + GSimpleAction * header_action; + GSimpleAction * show_time_action; + + IndicatorPowerDevice * primary_device; + GList * devices; /* IndicatorPowerDevice */ + + IndicatorPowerDeviceProvider * device_provider; +}; + +typedef IndicatorPowerServicePrivate priv_t; + +/*** +**** +**** DEVICES +**** +***/ + +/* the higher the weight, the more interesting the device */ +static int +get_device_kind_weight (const IndicatorPowerDevice * device) +{ + UpDeviceKind kind; + static gboolean initialized = FALSE; + static int weights[UP_DEVICE_KIND_LAST]; + + kind = indicator_power_device_get_kind (device); + g_return_val_if_fail (0<=kind && kind<UP_DEVICE_KIND_LAST, 0); + + if (G_UNLIKELY(!initialized)) + { + int i; + + initialized = TRUE; + + for (i=0; i<UP_DEVICE_KIND_LAST; i++) + weights[i] = 1; + weights[UP_DEVICE_KIND_BATTERY] = 2; + weights[UP_DEVICE_KIND_LINE_POWER] = 0; + } + + return weights[kind]; +} + +/* sort devices from most interesting to least interesting on this criteria: + 1. discharging items from least time remaining until most time remaining + 2. discharging items with an unknown time remaining + 3. charging items from most time left to charge to least time left to charge + 4. charging items with an unknown time remaining + 5. batteries, then non-line power, then line-power */ +static gint +device_compare_func (gconstpointer ga, gconstpointer gb) +{ + int ret; + int state; + const IndicatorPowerDevice * a = ga; + const IndicatorPowerDevice * b = gb; + const int a_state = indicator_power_device_get_state (a); + const int b_state = indicator_power_device_get_state (b); + const gdouble a_percentage = indicator_power_device_get_percentage (a); + const gdouble b_percentage = indicator_power_device_get_percentage (b); + const time_t a_time = indicator_power_device_get_time (a); + const time_t b_time = indicator_power_device_get_time (b); + + ret = 0; + + state = UP_DEVICE_STATE_DISCHARGING; + if (!ret && ((a_state == state) || (b_state == state))) + { + if (a_state != state) /* b is discharging */ + { + ret = 1; + } + else if (b_state != state) /* a is discharging */ + { + ret = -1; + } + else /* both are discharging; least-time-left goes first */ + { + if (!a_time || !b_time) /* known time always trumps unknown time */ + ret = a_time ? -1 : 1; + else if (a_time != b_time) + ret = a_time < b_time ? -1 : 1; + else + ret = a_percentage < b_percentage ? -1 : 1; + } + } + + state = UP_DEVICE_STATE_CHARGING; + if (!ret && (((a_state == state) && a_time) || + ((b_state == state) && b_time))) + { + if (a_state != state) /* b is charging */ + { + ret = 1; + } + else if (b_state != state) /* a is charging */ + { + ret = -1; + } + else /* both are discharging; most-time-to-charge goes first */ + { + if (!a_time || !b_time) /* known time always trumps unknown time */ + ret = a_time ? -1 : 1; + else if (a_time != b_time) + ret = a_time > b_time ? -1 : 1; + else + ret = a_percentage < b_percentage ? -1 : 1; + } + } + + if (!ret) /* neither device is charging nor discharging... */ + { + const int weight_a = get_device_kind_weight (a); + const int weight_b = get_device_kind_weight (b); + + if (weight_a > weight_b) + { + ret = -1; + } + else if (weight_a < weight_b) + { + ret = 1; + } + } + + if (!ret) + ret = a_state - b_state; + + return ret; +} + +/*** +**** +**** HEADER SECTION +**** +***/ + +static void +count_batteries (GList * devices, int *total, int *inuse) +{ + GList * l; + + for (l=devices; l!=NULL; l=l->next) + { + const IndicatorPowerDevice * device = INDICATOR_POWER_DEVICE(l->data); + + if (indicator_power_device_get_kind(device) == UP_DEVICE_KIND_BATTERY || + indicator_power_device_get_kind(device) == UP_DEVICE_KIND_UPS) + { + ++*total; + + const UpDeviceState state = indicator_power_device_get_state (device); + if ((state == UP_DEVICE_STATE_CHARGING) || + (state == UP_DEVICE_STATE_DISCHARGING)) + ++*inuse; + } + } + + g_debug ("count_batteries found %d batteries (%d are charging/discharging)", + *total, *inuse); +} + +static gboolean +should_be_visible (IndicatorPowerService * self) +{ + gboolean visible = TRUE; + priv_t * p = self->priv; + + const int policy = g_settings_get_enum (p->settings, SETTINGS_ICON_POLICY_S); + g_debug ("policy is: %d (present==0, charge==1, never==2)", policy); + + if (policy == POWER_INDICATOR_ICON_POLICY_NEVER) + { + visible = FALSE; + } + else + { + int batteries=0, inuse=0; + + count_batteries (p->devices, &batteries, &inuse); + + if (policy == POWER_INDICATOR_ICON_POLICY_PRESENT) + { + visible = batteries > 0; + } + else if (policy == POWER_INDICATOR_ICON_POLICY_CHARGE) + { + visible = inuse > 0; + } + } + + g_debug ("should_be_visible: %s", visible?"yes":"no"); + return visible; +} + +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) + { + gchar * details; + + indicator_power_device_get_time_details (p->primary_device, + &label, + &details, + &a11y); + + icon = indicator_power_device_get_gicon (p->primary_device); + + g_free (details); + } + + g_variant_builder_init (&b, G_VARIANT_TYPE("a{sv}")); + + g_variant_builder_add (&b, "{sv}", "visible", + g_variant_new_boolean (should_be_visible (self))); + + if (label != NULL) + { + if (g_settings_get_boolean (p->settings, SETTINGS_SHOW_TIME_S)) + g_variant_builder_add (&b, "{sv}", "label", + g_variant_new_string (label)); + + g_free (label); + } + + if (icon != NULL) + { + g_variant_builder_add (&b, "{sv}", "icon", g_icon_serialize (icon)); + + g_object_unref (icon); + } + + if (a11y != NULL) + { + g_variant_builder_add (&b, "{sv}", "accessible-desc", + g_variant_new_string (a11y)); + + g_free (a11y); + } + + return g_variant_builder_end (&b); +} + + +/*** +**** +**** DEVICE SECTION +**** +***/ + +static void +append_device_to_menu (GMenu * menu, const IndicatorPowerDevice * device) +{ + const UpDeviceKind kind = indicator_power_device_get_kind (device); + + if (kind != UP_DEVICE_KIND_LINE_POWER) + { + char * brief; + char * label; + char * a11y; + GMenuItem * menu_item; + GIcon * icon = indicator_power_device_get_gicon (device); + + indicator_power_device_get_time_details (device, + &brief, + &label, + &a11y); + + menu_item = g_menu_item_new (label, "indicator.activate-statistics"); + + if (icon != NULL) + { + g_menu_item_set_attribute_value (menu_item, + G_MENU_ATTRIBUTE_ICON, + g_icon_serialize (icon)); + } + + g_menu_append_item (menu, menu_item); + g_object_unref (menu_item); + + g_clear_object (&icon); + g_free (brief); + g_free (label); + g_free (a11y); + } +} + + +static GMenuModel * +create_devices_section (IndicatorPowerService * self) +{ + GList * l; + GMenu * menu = g_menu_new (); + + for (l=self->priv->devices; l!=NULL; l=l->next) + append_device_to_menu (menu, l->data); + + return G_MENU_MODEL (menu); +} + + +/*** +**** +**** SETTINGS SECTION +**** +***/ + +static GMenuModel * +create_settings_section (IndicatorPowerService * self G_GNUC_UNUSED) +{ + GMenu * menu = g_menu_new (); + + g_menu_append (menu, + _("Show Time in Menu Bar"), + "indicator.show-time"); + + g_menu_append (menu, + _("Power Settings\342\200\246"), + "indicator.activate-settings"); + + return G_MENU_MODEL (menu); +} + +/*** +**** +**** SECTION REBUILDING +**** +***/ + +/** + * A small helper function for rebuild_now(). + * - removes the previous section + * - adds and unrefs the new section + */ +static void +rebuild_section (GMenu * parent, int pos, GMenuModel * new_section) +{ + g_menu_remove (parent, pos); + g_menu_insert_section (parent, pos, NULL, new_section); + g_object_unref (new_section); +} + +static void +rebuild_now (IndicatorPowerService * self, guint sections) +{ + priv_t * p = self->priv; + struct ProfileMenuInfo * desktop = &p->menus[PROFILE_DESKTOP]; + struct ProfileMenuInfo * greeter = &p->menus[PROFILE_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 (sections & SECTION_DEVICES) + { + rebuild_section (desktop->submenu, 0, create_devices_section (self)); + rebuild_section (greeter->submenu, 0, create_devices_section (self)); + } + + if (sections & SECTION_SETTINGS) + { + rebuild_section (desktop->submenu, 1, create_settings_section (self)); + } +} + +static inline void +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) +{ + GMenu * menu; + GMenu * submenu; + GMenuItem * header; + GMenuModel * sections[16]; + guint i; + guint n = 0; + + g_assert (0<=profile && profile<N_PROFILES); + g_assert (self->priv->menus[profile].menu == NULL); + + if (profile == PROFILE_DESKTOP) + { + sections[n++] = create_devices_section (self); + sections[n++] = create_settings_section (self); + } + else if (profile == PROFILE_GREETER) + { + sections[n++] = create_devices_section (self); + } + + /* add sections to the submenu */ + + submenu = g_menu_new (); + + for (i=0; i<n; ++i) + { + g_menu_append_section (submenu, NULL, sections[i]); + g_object_unref (sections[i]); + } + + /* add submenu to the header */ + header = g_menu_item_new (NULL, "indicator._header"); + g_menu_item_set_attribute (header, "x-canonical-type", + "s", "com.canonical.indicator.root"); + g_menu_item_set_submenu (header, G_MENU_MODEL (submenu)); + g_object_unref (submenu); + + /* add header to the menu */ + menu = g_menu_new (); + g_menu_append_item (menu, header); + g_object_unref (header); + + self->priv->menus[profile].menu = menu; + self->priv->menus[profile].submenu = submenu; +} + +/*** +**** GActions +***/ + +/* Run a particular program based on an activation */ +static void +execute_command (const gchar * cmd) +{ + GError * err = NULL; + + g_debug ("Issuing command '%s'", cmd); + + if (!g_spawn_command_line_async (cmd, &err)) + { + g_warning ("Unable to start %s: %s", cmd, err->message); + g_error_free (err); + } +} + +static void +on_settings_activated (GSimpleAction * a G_GNUC_UNUSED, + GVariant * param G_GNUC_UNUSED, + gpointer gself G_GNUC_UNUSED) +{ + execute_command ("gnome-control-center power"); +} + +static void +on_statistics_activated (GSimpleAction * a G_GNUC_UNUSED, + GVariant * param G_GNUC_UNUSED, + gpointer gself G_GNUC_UNUSED) +{ + execute_command ("gnome-power-statistics"); +} + +/* FIXME: use a GBinding to tie the gaction's state and the GSetting together? */ + +static void +set_show_time_flag (IndicatorPowerService * self, gboolean b) +{ + GVariant * v; + priv_t * p = self->priv; + + /* update the settings */ + if (b != g_settings_get_boolean (p->settings, SETTINGS_SHOW_TIME_S)) + g_settings_set_boolean (p->settings, SETTINGS_SHOW_TIME_S, b); + + /* update the action state */ + v = g_action_get_state (G_ACTION(p->show_time_action)); + if (b != g_variant_get_boolean (v)) + g_simple_action_set_state (p->show_time_action, g_variant_new_boolean (b)); + g_variant_unref (v); + + rebuild_header_now (self); +} +static void +on_show_time_setting_changed (GSettings * settings, gchar * key, gpointer gself) +{ + set_show_time_flag (INDICATOR_POWER_SERVICE(gself), + g_settings_get_boolean (settings, key)); +} + +static void +on_show_time_action_state_changed (GAction * action, + GParamSpec * pspec G_GNUC_UNUSED, + gpointer gself) +{ + GVariant * v = g_action_get_state (action); + set_show_time_flag (INDICATOR_POWER_SERVICE(gself), + g_variant_get_boolean (v)); + g_variant_unref (v); +} + +/* toggles the state */ +static void +on_show_time_action_activated (GSimpleAction * simple, + GVariant * parameter G_GNUC_UNUSED, + gpointer unused G_GNUC_UNUSED) +{ + GVariant * v = g_action_get_state (G_ACTION (simple)); + gboolean flag = g_variant_get_boolean (v); + g_simple_action_set_state (simple, g_variant_new_boolean (!flag)); + g_variant_unref (v); +} + +static void +init_gactions (IndicatorPowerService * self) +{ + GSimpleAction * a; + gboolean show_time; + priv_t * p = self->priv; + + GActionEntry entries[] = { + { "activate-settings", on_settings_activated }, + { "activate-statistics", on_statistics_activated } + }; + + p->actions = g_simple_action_group_new (); + + g_action_map_add_action_entries (G_ACTION_MAP(p->actions), + entries, + G_N_ELEMENTS(entries), + self); + + /* add the header action */ + a = g_simple_action_new_stateful ("_header", NULL, create_header_state (self)); + g_simple_action_group_insert (p->actions, G_ACTION(a)); + p->header_action = a; + + /* add the show-time action */ + show_time = g_settings_get_boolean (p->settings, SETTINGS_SHOW_TIME_S); + a = g_simple_action_new_stateful ("show-time", + NULL, + g_variant_new_boolean(show_time)); + g_signal_connect (a, "activate", + G_CALLBACK(on_show_time_action_activated), self); + g_signal_connect (a, "notify", + G_CALLBACK(on_show_time_action_state_changed), self); + g_simple_action_group_insert (p->actions, G_ACTION(a)); + p->show_time_action = a; + + rebuild_header_now (self); +} + +/*** +**** GDBus Name Ownership & Menu / Action Exporting +***/ + +static void +on_bus_acquired (GDBusConnection * connection, + const gchar * name, + gpointer gself) +{ + int i; + guint id; + GError * err = NULL; + IndicatorPowerService * self = INDICATOR_POWER_SERVICE(gself); + priv_t * p = self->priv; + GString * path = g_string_new (NULL); + + g_debug ("bus acquired: %s", name); + + p->conn = g_object_ref (G_OBJECT (connection)); + + /* export the actions */ + if ((id = g_dbus_connection_export_action_group (connection, + BUS_PATH, + G_ACTION_GROUP (p->actions), + &err))) + { + p->actions_export_id = id; + } + else + { + g_warning ("cannot export action group: %s", err->message); + g_clear_error (&err); + } + + /* export the menus */ + for (i=0; i<N_PROFILES; ++i) + { + struct ProfileMenuInfo * menu = &p->menus[i]; + + 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), + &err))) + { + menu->export_id = id; + } + else + { + g_warning ("cannot export %s menu: %s", path->str, err->message); + g_clear_error (&err); + } + } + + g_string_free (path, TRUE); +} + +static void +unexport (IndicatorPowerService * self) +{ + int i; + priv_t * p = self->priv; + + /* unexport the menus */ + for (i=0; i<N_PROFILES; ++i) + { + guint * id = &self->priv->menus[i].export_id; + + if (*id) + { + g_dbus_connection_unexport_menu_model (p->conn, *id); + *id = 0; + } + } + + /* unexport the actions */ + if (p->actions_export_id) + { + g_dbus_connection_unexport_action_group (p->conn, p->actions_export_id); + p->actions_export_id = 0; + } +} + +static void +on_name_lost (GDBusConnection * connection G_GNUC_UNUSED, + const gchar * name, + gpointer gself) +{ + IndicatorPowerService * self = INDICATOR_POWER_SERVICE (gself); + + g_debug ("%s %s name lost %s", G_STRLOC, G_STRFUNC, name); + + unexport (self); + + g_signal_emit (self, signals[SIGNAL_NAME_LOST], 0, NULL); +} + +/*** +**** Events +***/ + +static void +on_devices_changed (IndicatorPowerService * self) +{ + priv_t * p = self->priv; + + /* update the device list */ + g_list_free_full (p->devices, (GDestroyNotify)g_object_unref); + p->devices = indicator_power_device_provider_get_devices (p->device_provider); + + /* update the primary device */ + g_clear_object (&p->primary_device); + p->primary_device = indicator_power_service_choose_primary_device (p->devices); + + rebuild_now (self, SECTION_HEADER | SECTION_DEVICES); +} + + +/*** +**** GObject virtual functions +***/ + +static void +my_get_property (GObject * o, + guint property_id, + GValue * value, + GParamSpec * pspec) +{ + IndicatorPowerService * self = INDICATOR_POWER_SERVICE (o); + priv_t * p = self->priv; + + switch (property_id) + { + case PROP_DEVICE_PROVIDER: + g_value_set_object (value, p->device_provider); + 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) +{ + IndicatorPowerService * self = INDICATOR_POWER_SERVICE (o); + + switch (property_id) + { + case PROP_DEVICE_PROVIDER: + indicator_power_service_set_device_provider (self, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec); + } +} + +static void +my_dispose (GObject * o) +{ + IndicatorPowerService * self = INDICATOR_POWER_SERVICE(o); + priv_t * p = self->priv; + + if (p->own_id) + { + g_bus_unown_name (p->own_id); + p->own_id = 0; + } + + unexport (self); + + if (p->cancellable != NULL) + { + g_cancellable_cancel (p->cancellable); + g_clear_object (&p->cancellable); + } + + if (p->settings != NULL) + { + g_signal_handlers_disconnect_by_data (p->settings, self); + + g_clear_object (&p->settings); + } + + if (p->show_time_action != NULL) + { + g_signal_handlers_disconnect_by_data (p->show_time_action, self); + + g_clear_object (&p->show_time_action); + } + + g_clear_object (&p->header_action); + g_clear_object (&p->show_time_action); + g_clear_object (&p->actions); + + g_clear_object (&p->conn); + + indicator_power_service_set_device_provider (self, NULL); + + G_OBJECT_CLASS (indicator_power_service_parent_class)->dispose (o); +} + +/*** +**** Instantiation +***/ + +static void +indicator_power_service_init (IndicatorPowerService * self) +{ + priv_t * 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"); + + init_gactions (self); + + g_signal_connect_swapped (p->settings, "changed::" SETTINGS_ICON_POLICY_S, + G_CALLBACK(rebuild_header_now), self); + g_signal_connect (p->settings, "changed::" SETTINGS_SHOW_TIME_S, + G_CALLBACK(on_show_time_setting_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 +indicator_power_service_class_init (IndicatorPowerServiceClass * klass) +{ + GObjectClass * object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = my_dispose; + object_class->get_property = my_get_property; + object_class->set_property = my_set_property; + + g_type_class_add_private (klass, sizeof (IndicatorPowerServicePrivate)); + + signals[SIGNAL_NAME_LOST] = g_signal_new ( + INDICATOR_POWER_SERVICE_SIGNAL_NAME_LOST, + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (IndicatorPowerServiceClass, name_lost), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + properties[PROP_0] = NULL; + + properties[PROP_DEVICE_PROVIDER] = g_param_spec_object ( + "device-provider", + "Device Provider", + "Source for power devices", + G_TYPE_OBJECT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, LAST_PROP, properties); +} + +/*** +**** Public API +***/ + +IndicatorPowerService * +indicator_power_service_new (IndicatorPowerDeviceProvider * device_provider) +{ + GObject * o = g_object_new (INDICATOR_TYPE_POWER_SERVICE, + "device-provider", device_provider, + NULL); + + return INDICATOR_POWER_SERVICE (o); +} + +void +indicator_power_service_set_device_provider (IndicatorPowerService * self, + IndicatorPowerDeviceProvider * dp) +{ + priv_t * p; + + g_return_if_fail (INDICATOR_IS_POWER_SERVICE (self)); + g_return_if_fail (!dp || INDICATOR_IS_POWER_DEVICE_PROVIDER (dp)); + p = self->priv; + + if (p->device_provider != NULL) + { + g_signal_handlers_disconnect_by_func (p->device_provider, + G_CALLBACK(on_devices_changed), + self); + g_clear_object (&p->device_provider); + + g_clear_object (&p->primary_device); + + g_list_free_full (p->devices, g_object_unref); + p->devices = NULL; + } + + if (dp != NULL) + { + p->device_provider = g_object_ref (dp); + + g_signal_connect_swapped (p->device_provider, "devices-changed", + G_CALLBACK(on_devices_changed), self); + + on_devices_changed (self); + } +} + +IndicatorPowerDevice * +indicator_power_service_choose_primary_device (GList * devices) +{ + IndicatorPowerDevice * primary = NULL; + + if (devices != NULL) + { + GList * tmp; + + tmp = g_list_copy (devices); + tmp = g_list_sort (tmp, device_compare_func); + primary = g_object_ref (tmp->data); + + g_list_free (tmp); + } + + return primary; +} diff --git a/src/service.h b/src/service.h new file mode 100644 index 0000000..76ed10f --- /dev/null +++ b/src/service.h @@ -0,0 +1,78 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __INDICATOR_POWER_SERVICE_H__ +#define __INDICATOR_POWER_SERVICE_H__ + +#include <glib.h> +#include <glib-object.h> + +#include "device-provider.h" + +G_BEGIN_DECLS + +/* standard GObject macros */ +#define INDICATOR_POWER_SERVICE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_POWER_SERVICE, IndicatorPowerService)) +#define INDICATOR_TYPE_POWER_SERVICE (indicator_power_service_get_type()) +#define INDICATOR_IS_POWER_SERVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_POWER_SERVICE)) + +typedef struct _IndicatorPowerService IndicatorPowerService; +typedef struct _IndicatorPowerServiceClass IndicatorPowerServiceClass; +typedef struct _IndicatorPowerServicePrivate IndicatorPowerServicePrivate; + +/* signal keys */ +#define INDICATOR_POWER_SERVICE_SIGNAL_NAME_LOST "name-lost" + +/** + * The Indicator Power Service. + */ +struct _IndicatorPowerService +{ + /*< private >*/ + GObject parent; + IndicatorPowerServicePrivate * priv; +}; + +struct _IndicatorPowerServiceClass +{ + GObjectClass parent_class; + + /* signals */ + + void (* name_lost)(IndicatorPowerService * self); +}; + +/*** +**** +***/ + +GType indicator_power_service_get_type (void); + +IndicatorPowerService * indicator_power_service_new (IndicatorPowerDeviceProvider * provider); + +void indicator_power_service_set_device_provider (IndicatorPowerService * self, + IndicatorPowerDeviceProvider * provider); + +IndicatorPowerDevice * indicator_power_service_choose_primary_device (GList * devices); + + + +G_END_DECLS + +#endif /* __INDICATOR_POWER_SERVICE_H__ */ |