aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCharles Kerr <charles.kerr@canonical.com>2013-06-19 21:13:09 +0000
committerTarmac <Unknown>2013-06-19 21:13:09 +0000
commit0ce78de0eae6f44a0afe6e5b4bdc6da83f6dbdf8 (patch)
treece626b15bcfb92beec4487a6b7fe50ef4be91dac /src
parent0b0585581a8da994362ef4cfe3a2168917a47ff4 (diff)
parent258f3ad689ae4ba49e2813a5051b4968d568f999 (diff)
downloadayatana-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.am120
-rw-r--r--src/dbus-listener.c247
-rw-r--r--src/dbus-listener.h82
-rw-r--r--src/device-provider-upower.c424
-rw-r--r--src/device-provider-upower.h72
-rw-r--r--src/device-provider.c90
-rw-r--r--src/device-provider.h80
-rw-r--r--src/device.c25
-rw-r--r--src/device.h33
-rw-r--r--src/indicator-power.c663
-rw-r--r--src/indicator-power.h60
-rw-r--r--src/main.c68
-rw-r--r--src/org.freedesktop.UPower.Device.xml705
-rw-r--r--src/org.freedesktop.UPower.xml43
-rw-r--r--src/service.c1015
-rw-r--r--src/service.h78
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__ */