[gnome-settings-daemon] power: Add the engine functionality so that batteries are monitored



commit 9147c8df3eed321f2f3cf29f6c077d70c5c4b323
Author: Richard Hughes <richard hughsie com>
Date:   Fri Jul 1 12:32:37 2011 +0100

    power: Add the engine functionality so that batteries are monitored
    
    This also creates the composite device if required.

 ...settings-daemon.plugins.power.gschema.xml.in.in |   35 +
 plugins/power/gsd-power-manager.c                  |  949 +++++++++++++++++++-
 2 files changed, 974 insertions(+), 10 deletions(-)
---
diff --git a/data/org.gnome.settings-daemon.plugins.power.gschema.xml.in.in b/data/org.gnome.settings-daemon.plugins.power.gschema.xml.in.in
index ba1dca7..52c1b0c 100644
--- a/data/org.gnome.settings-daemon.plugins.power.gschema.xml.in.in
+++ b/data/org.gnome.settings-daemon.plugins.power.gschema.xml.in.in
@@ -106,5 +106,40 @@
       <summary>Battery critical low action</summary>
       <description>The action to take when the battery is critically low.</description>
     </key>
+    <key name="percentage-low" type="i">
+      <default>10</default>
+      <_summary>Percentage considered low</_summary>
+      <_description>The percentage of the battery when it is considered low. Only valid when use-time-for-policy is false.</_description>
+    </key>
+    <key name="percentage-critical" type="i">
+      <default>3</default>
+      <_summary>Percentage considered critical</_summary>
+      <_description>The percentage of the battery when it is considered critical. Only valid when use-time-for-policy is false.</_description>
+    </key>
+    <key name="percentage-action" type="i">
+      <default>2</default>
+      <_summary>Percentage action is taken</_summary>
+      <_description>The percentage of the battery when the critical action is performed. Only valid when use-time-for-policy is false.</_description>
+    </key>
+    <key name="time-low" type="i">
+      <default>1200</default>
+      <_summary>The time remaining when low</_summary>
+      <_description>The time remaining in seconds of the battery when it is considered low. Only valid when use-time-for-policy is true.</_description>
+    </key>
+    <key name="time-critical" type="i">
+      <default>300</default>
+      <_summary>The time remaining when critical</_summary>
+      <_description>The time remaining in seconds of the battery when it is considered critical. Only valid when use-time-for-policy is true.</_description>
+    </key>
+    <key name="time-action" type="i">
+      <default>120</default>
+      <_summary>The time remaining when action is taken</_summary>
+      <_description>The time remaining in seconds of the battery when critical action is taken. Only valid when use-time-for-policy is true.</_description>
+    </key>
+    <key name="use-time-for-policy" type="b">
+      <default>true</default>
+      <_summary>Whether to use time-based notifications</_summary>
+      <_description>If time based notifications should be used. If set to false, then the percentage change is used instead, which may fix a broken ACPI BIOS.</_description>
+    </key>
   </schema>
 </schemalist>
diff --git a/plugins/power/gsd-power-manager.c b/plugins/power/gsd-power-manager.c
index 90af0da..4cd5adc 100644
--- a/plugins/power/gsd-power-manager.c
+++ b/plugins/power/gsd-power-manager.c
@@ -22,6 +22,7 @@
 #include "config.h"
 
 #include <stdlib.h>
+#include <string.h>
 #include <stdio.h>
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
@@ -32,6 +33,7 @@
 #include <libgnome-desktop/gnome-rr.h>
 
 #include "gpm-common.h"
+#include "gpm-phone.h"
 #include "gnome-settings-profile.h"
 #include "gsd-enums.h"
 #include "gsd-power-manager.h"
@@ -50,6 +52,7 @@
 
 #define GSD_POWER_SETTINGS_SCHEMA               "org.gnome.settings-daemon.plugins.power"
 
+#define GSD_DBUS_SERVICE                        "org.gnome.SettingsDaemon"
 #define GSD_DBUS_PATH                           "/org/gnome/SettingsDaemon"
 #define GSD_POWER_DBUS_PATH                     GSD_DBUS_PATH "/Power"
 #define GSD_POWER_DBUS_INTERFACE                "org.gnome.SettingsDaemon.Power"
@@ -121,6 +124,18 @@ struct GsdPowerManagerPrivate
         gint                     kbd_brightness_max;
         gint                     kbd_brightness_old;
         GnomeRRScreen           *x11_screen;
+        gboolean                 use_time_primary;
+        gchar                   *previous_summary;
+        GIcon                   *previous_icon;
+        GpmPhone                *phone;
+        GPtrArray               *devices_array;
+        guint                    action_percentage;
+        guint                    action_time;
+        guint                    critical_percentage;
+        guint                    critical_time;
+        guint                    low_percentage;
+        guint                    low_time;
+        UpDevice                *device_composite;
 };
 
 enum {
@@ -131,6 +146,9 @@ static void     gsd_power_manager_class_init  (GsdPowerManagerClass *klass);
 static void     gsd_power_manager_init        (GsdPowerManager      *power_manager);
 static void     gsd_power_manager_finalize    (GObject              *object);
 
+static UpDevice *engine_get_composite_device (GsdPowerManager *manager, UpDevice *original_device);
+static UpDevice *engine_update_composite_device (GsdPowerManager *manager, UpDevice *original_device);
+
 G_DEFINE_TYPE (GsdPowerManager, gsd_power_manager, G_TYPE_OBJECT)
 
 static gpointer manager_object = NULL;
@@ -144,6 +162,848 @@ gsd_power_manager_error_quark (void)
         return quark;
 }
 
+typedef enum {
+        WARNING_NONE            = 0,
+        WARNING_DISCHARGING     = 1,
+        WARNING_LOW             = 2,
+        WARNING_CRITICAL        = 3,
+        WARNING_ACTION          = 4
+} GsdPowerManagerWarning;
+
+static void
+engine_emit_changed (GsdPowerManager *manager)
+{
+        gboolean ret;
+        GError *error = NULL;
+
+        /* not yet connected to the bus */
+        if (manager->priv->connection == NULL)
+                return;
+        ret = g_dbus_connection_emit_signal (manager->priv->connection,
+                                             GSD_DBUS_SERVICE,
+                                             GSD_POWER_DBUS_PATH,
+                                             GSD_POWER_DBUS_INTERFACE,
+                                             "Changed",
+                                             NULL,
+                                             &error);
+        if (!ret) {
+                g_warning ("failed to emit Changed: %s", error->message);
+                g_error_free (error);
+        }
+}
+
+static GsdPowerManagerWarning
+engine_get_warning_csr (GsdPowerManager *manager, UpDevice *device)
+{
+        gdouble percentage;
+
+        /* get device properties */
+        g_object_get (device, "percentage", &percentage, NULL);
+
+        if (percentage < 26.0f)
+                return WARNING_LOW;
+        else if (percentage < 13.0f)
+                return WARNING_CRITICAL;
+        return WARNING_NONE;
+}
+
+static GsdPowerManagerWarning
+engine_get_warning_percentage (GsdPowerManager *manager, UpDevice *device)
+{
+        gdouble percentage;
+
+        /* get device properties */
+        g_object_get (device, "percentage", &percentage, NULL);
+
+        if (percentage <= manager->priv->action_percentage)
+                return WARNING_ACTION;
+        if (percentage <= manager->priv->critical_percentage)
+                return WARNING_CRITICAL;
+        if (percentage <= manager->priv->low_percentage)
+                return WARNING_LOW;
+        return WARNING_NONE;
+}
+
+static GsdPowerManagerWarning
+engine_get_warning_time (GsdPowerManager *manager, UpDevice *device)
+{
+        UpDeviceKind kind;
+        gint64 time_to_empty;
+
+        /* get device properties */
+        g_object_get (device,
+                      "kind", &kind,
+                      "time-to-empty", &time_to_empty,
+                      NULL);
+
+        /* this is probably an error condition */
+        if (time_to_empty == 0) {
+                g_debug ("time zero, falling back to percentage for %s",
+                         up_device_kind_to_string (kind));
+                return engine_get_warning_percentage (manager, device);
+        }
+
+        if (time_to_empty <= manager->priv->action_time)
+                return WARNING_ACTION;
+        if (time_to_empty <= manager->priv->critical_time)
+                return WARNING_CRITICAL;
+        if (time_to_empty <= manager->priv->low_time)
+                return WARNING_LOW;
+        return WARNING_NONE;
+}
+
+/**
+ * This gets the possible engine state for the device according to the
+ * policy, which could be per-percent, or per-time.
+ **/
+static GsdPowerManagerWarning
+engine_get_warning (GsdPowerManager *manager, UpDevice *device)
+{
+        UpDeviceKind kind;
+        UpDeviceState state;
+        GsdPowerManagerWarning warning_type;
+
+        /* get device properties */
+        g_object_get (device,
+                      "kind", &kind,
+                      "state", &state,
+                      NULL);
+
+        /* default to no engine */
+        warning_type = WARNING_NONE;
+
+        /* if the device in question is on ac, don't give a warning */
+        if (state == UP_DEVICE_STATE_CHARGING)
+                goto out;
+
+        if (kind == UP_DEVICE_KIND_MOUSE ||
+            kind == UP_DEVICE_KIND_KEYBOARD) {
+
+                warning_type = engine_get_warning_csr (manager, device);
+
+        } else if (kind == UP_DEVICE_KIND_UPS ||
+#if UP_CHECK_VERSION(0,9,5)
+                   kind == UP_DEVICE_KIND_MEDIA_PLAYER ||
+                   kind == UP_DEVICE_KIND_TABLET ||
+                   kind == UP_DEVICE_KIND_COMPUTER ||
+#endif
+                   kind == UP_DEVICE_KIND_PDA) {
+
+                warning_type = engine_get_warning_percentage (manager, device);
+
+        } else if (kind == UP_DEVICE_KIND_PHONE) {
+
+                warning_type = engine_get_warning_percentage (manager, device);
+
+        } else if (kind == UP_DEVICE_KIND_BATTERY) {
+                /* only use the time when it is accurate, and settings is not disabled */
+                if (manager->priv->use_time_primary)
+                        warning_type = engine_get_warning_time (manager, device);
+                else
+                        warning_type = engine_get_warning_percentage (manager, device);
+        }
+
+        /* If we have no important engines, we should test for discharging */
+        if (warning_type == WARNING_NONE) {
+                if (state == UP_DEVICE_STATE_DISCHARGING)
+                        warning_type = WARNING_DISCHARGING;
+        }
+
+ out:
+        return warning_type;
+}
+
+static gchar *
+engine_get_summary (GsdPowerManager *manager)
+{
+        guint i;
+        GPtrArray *array;
+        UpDevice *device;
+        UpDeviceState state;
+        GString *tooltip = NULL;
+        gchar *part;
+        gboolean is_present;
+
+
+        /* need to get AC state */
+        tooltip = g_string_new ("");
+
+        /* do we have specific device types? */
+        array = manager->priv->devices_array;
+        for (i=0;i<array->len;i++) {
+                device = g_ptr_array_index (array, i);
+                g_object_get (device,
+                              "is-present", &is_present,
+                              "state", &state,
+                              NULL);
+                if (!is_present)
+                        continue;
+                if (state == UP_DEVICE_STATE_EMPTY)
+                        continue;
+                part = gpm_upower_get_device_summary (device);
+                if (part != NULL)
+                        g_string_append_printf (tooltip, "%s\n", part);
+                g_free (part);
+        }
+
+        /* remove the last \n */
+        g_string_truncate (tooltip, tooltip->len-1);
+
+        g_debug ("tooltip: %s", tooltip->str);
+
+        return g_string_free (tooltip, FALSE);
+}
+
+static GIcon *
+engine_get_icon_priv (GsdPowerManager *manager,
+                      UpDeviceKind device_kind,
+                      GsdPowerManagerWarning warning,
+                      gboolean use_state)
+{
+        guint i;
+        GPtrArray *array;
+        UpDevice *device;
+        GsdPowerManagerWarning warning_temp;
+        UpDeviceKind kind;
+        UpDeviceState state;
+        gboolean is_present;
+
+        /* do we have specific device types? */
+        array = manager->priv->devices_array;
+        for (i=0;i<array->len;i++) {
+                device = g_ptr_array_index (array, i);
+
+                /* get device properties */
+                g_object_get (device,
+                              "kind", &kind,
+                              "state", &state,
+                              "is-present", &is_present,
+                              NULL);
+
+                /* if battery then use composite device to cope with multiple batteries */
+                if (kind == UP_DEVICE_KIND_BATTERY)
+                        device = engine_get_composite_device (manager, device);
+
+                warning_temp = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(device),
+                                                                  "engine-warning-old"));
+                if (kind == device_kind && is_present) {
+                        if (warning != WARNING_NONE) {
+                                if (warning_temp == warning)
+                                        return gpm_upower_get_device_icon (device, TRUE);
+                                continue;
+                        }
+                        if (use_state) {
+                                if (state == UP_DEVICE_STATE_CHARGING ||
+                                    state == UP_DEVICE_STATE_DISCHARGING)
+                                        return gpm_upower_get_device_icon (device, TRUE);
+                                continue;
+                        }
+                        return gpm_upower_get_device_icon (device, TRUE);
+                }
+        }
+        return NULL;
+}
+
+static GIcon *
+engine_get_icon (GsdPowerManager *manager)
+{
+        GIcon *icon = NULL;
+
+
+        /* we try CRITICAL: BATTERY, UPS, MOUSE, KEYBOARD */
+        icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_BATTERY, WARNING_CRITICAL, FALSE);
+        if (icon != NULL)
+                return icon;
+        icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_UPS, WARNING_CRITICAL, FALSE);
+        if (icon != NULL)
+                return icon;
+        icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_MOUSE, WARNING_CRITICAL, FALSE);
+        if (icon != NULL)
+                return icon;
+        icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_KEYBOARD, WARNING_CRITICAL, FALSE);
+        if (icon != NULL)
+                return icon;
+
+        /* we try CRITICAL: BATTERY, UPS, MOUSE, KEYBOARD */
+        icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_BATTERY, WARNING_LOW, FALSE);
+        if (icon != NULL)
+                return icon;
+        icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_UPS, WARNING_LOW, FALSE);
+        if (icon != NULL)
+                return icon;
+        icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_MOUSE, WARNING_LOW, FALSE);
+        if (icon != NULL)
+                return icon;
+        icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_KEYBOARD, WARNING_LOW, FALSE);
+        if (icon != NULL)
+                return icon;
+
+        /* we try (DIS)CHARGING: BATTERY, UPS */
+        icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_BATTERY, WARNING_NONE, TRUE);
+        if (icon != NULL)
+                return icon;
+        icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_UPS, WARNING_NONE, TRUE);
+        if (icon != NULL)
+                return icon;
+
+        /* we try PRESENT: BATTERY, UPS */
+        icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_BATTERY, WARNING_NONE, FALSE);
+        if (icon != NULL)
+                return icon;
+        icon = engine_get_icon_priv (manager, UP_DEVICE_KIND_UPS, WARNING_NONE, FALSE);
+        if (icon != NULL)
+                return icon;
+
+        /* do not show an icon */
+        return NULL;
+}
+
+static gboolean
+engine_recalculate_state_icon (GsdPowerManager *manager)
+{
+        GIcon *icon;
+
+        /* show a different icon if we are disconnected */
+        icon = engine_get_icon (manager);
+        if (icon == NULL) {
+                /* none before, now none */
+                if (manager->priv->previous_icon == NULL)
+                        return FALSE;
+
+                g_object_unref (manager->priv->previous_icon);
+                manager->priv->previous_icon = NULL;
+
+                /* icon before, now none */
+                engine_emit_changed (manager);
+
+                return TRUE;
+        }
+
+        /* no icon before, now icon */
+        if (manager->priv->previous_icon == NULL) {
+                engine_emit_changed (manager);
+                manager->priv->previous_icon = g_object_ref (icon);
+                return TRUE;
+        }
+
+        /* icon before, now different */
+        if (!g_icon_equal (manager->priv->previous_icon, icon)) {
+                g_object_unref (manager->priv->previous_icon);
+                manager->priv->previous_icon = g_object_ref (icon);
+                engine_emit_changed (manager);
+                return TRUE;
+        }
+
+        g_debug ("no change");
+        /* nothing to do */
+        g_object_unref (icon);
+        return FALSE;
+}
+
+static gboolean
+engine_recalculate_state_summary (GsdPowerManager *manager)
+{
+        gchar *summary;
+
+        summary = engine_get_summary (manager);
+        if (manager->priv->previous_summary == NULL) {
+                manager->priv->previous_summary = summary;
+                engine_emit_changed (manager);
+                return TRUE;
+        }
+
+        if (strcmp (manager->priv->previous_summary, summary) != 0) {
+                g_free (manager->priv->previous_summary);
+                manager->priv->previous_summary = summary;
+                engine_emit_changed (manager);
+                return TRUE;
+        }
+        g_debug ("no change");
+        /* nothing to do */
+        g_free (summary);
+        return FALSE;
+}
+
+static void
+engine_recalculate_state (GsdPowerManager *manager)
+{
+        engine_recalculate_state_icon (manager);
+        engine_recalculate_state_summary (manager);
+
+        engine_emit_changed (manager);
+}
+
+static void
+engine_settings_key_changed_cb (GSettings *settings,
+                                const gchar *key,
+                                GsdPowerManager *manager)
+{
+        if (g_strcmp0 (key, "use-time-for-policy") == 0) {
+                manager->priv->use_time_primary = g_settings_get_boolean (settings, key);
+        }
+}
+
+static UpDevice *
+engine_get_composite_device (GsdPowerManager *manager,
+                             UpDevice *original_device)
+{
+        guint battery_devices = 0;
+        GPtrArray *array;
+        UpDevice *device;
+        UpDeviceKind kind;
+        UpDeviceKind original_kind;
+        guint i;
+
+        /* get the type of the original device */
+        g_object_get (original_device,
+                      "kind", &original_kind,
+                      NULL);
+
+        /* find out how many batteries in the system */
+        array = manager->priv->devices_array;
+        for (i=0;i<array->len;i++) {
+                device = g_ptr_array_index (array, i);
+                g_object_get (device,
+                              "kind", &kind,
+                              NULL);
+                if (kind == original_kind)
+                        battery_devices++;
+        }
+
+        /* just use the original device if only one primary battery */
+        if (battery_devices <= 1) {
+                g_debug ("using original device as only one primary battery");
+                device = original_device;
+                goto out;
+        }
+
+        /* use the composite device */
+        device = manager->priv->device_composite;
+out:
+        /* return composite device or original device */
+        return device;
+}
+
+static UpDevice *
+engine_update_composite_device (GsdPowerManager *manager,
+                                UpDevice *original_device)
+{
+        guint i;
+        gdouble percentage = 0.0;
+        gdouble energy = 0.0;
+        gdouble energy_full = 0.0;
+        gdouble energy_rate = 0.0;
+        gdouble energy_total = 0.0;
+        gdouble energy_full_total = 0.0;
+        gdouble energy_rate_total = 0.0;
+        gint64 time_to_empty = 0;
+        gint64 time_to_full = 0;
+        guint battery_devices = 0;
+        gboolean is_charging = FALSE;
+        gboolean is_discharging = FALSE;
+        gboolean is_fully_charged = TRUE;
+        GPtrArray *array;
+        UpDevice *device;
+        UpDeviceState state;
+        UpDeviceKind kind;
+        UpDeviceKind original_kind;
+
+        /* get the type of the original device */
+        g_object_get (original_device,
+                      "kind", &original_kind,
+                      NULL);
+
+        /* update the composite device */
+        array = manager->priv->devices_array;
+        for (i=0;i<array->len;i++) {
+                device = g_ptr_array_index (array, i);
+                g_object_get (device,
+                              "kind", &kind,
+                              "state", &state,
+                              "energy", &energy,
+                              "energy-full", &energy_full,
+                              "energy-rate", &energy_rate,
+                              NULL);
+                if (kind != original_kind)
+                        continue;
+
+                /* one of these will be charging or discharging */
+                if (state == UP_DEVICE_STATE_CHARGING)
+                        is_charging = TRUE;
+                if (state == UP_DEVICE_STATE_DISCHARGING)
+                        is_discharging = TRUE;
+                if (state != UP_DEVICE_STATE_FULLY_CHARGED)
+                        is_fully_charged = FALSE;
+
+                /* sum up composite */
+                energy_total += energy;
+                energy_full_total += energy_full;
+                energy_rate_total += energy_rate;
+                battery_devices++;
+        }
+
+        /* just use the original device if only one primary battery */
+        if (battery_devices == 1) {
+                g_debug ("using original device as only one primary battery");
+                device = original_device;
+                goto out;
+        }
+
+        /* use percentage weighted for each battery capacity */
+        percentage = 100.0 * energy_total / energy_full_total;
+
+        /* set composite state */
+        if (is_charging)
+                state = UP_DEVICE_STATE_CHARGING;
+        else if (is_discharging)
+                state = UP_DEVICE_STATE_DISCHARGING;
+        else if (is_fully_charged)
+                state = UP_DEVICE_STATE_FULLY_CHARGED;
+        else
+                state = UP_DEVICE_STATE_UNKNOWN;
+
+        /* calculate a quick and dirty time remaining value */
+        if (energy_rate_total > 0) {
+                if (state == UP_DEVICE_STATE_DISCHARGING)
+                        time_to_empty = 3600 * (energy_total / energy_rate_total);
+                else if (state == UP_DEVICE_STATE_CHARGING)
+                        time_to_full = 3600 * ((energy_full_total - energy_total) / energy_rate_total);
+        }
+
+        /* okay, we can use the composite device */
+        device = manager->priv->device_composite;
+
+        g_debug ("printing composite device");
+        g_object_set (device,
+                      "energy", energy,
+                      "energy-full", energy_full,
+                      "energy-rate", energy_rate,
+                      "time-to-empty", time_to_empty,
+                      "time-to-full", time_to_full,
+                      "percentage", percentage,
+                      "state", state,
+                      NULL);
+
+        /* force update of icon */
+        engine_recalculate_state_icon (manager);
+out:
+        /* return composite device or original device */
+        return device;
+}
+
+static void
+engine_device_add (GsdPowerManager *manager, UpDevice *device)
+{
+        GsdPowerManagerWarning warning;
+        UpDeviceState state;
+        UpDeviceKind kind;
+        UpDevice *composite;
+
+        /* assign warning */
+        warning = engine_get_warning (manager, device);
+        g_object_set_data (G_OBJECT(device),
+                           "engine-warning-old",
+                           GUINT_TO_POINTER(warning));
+
+        /* get device properties */
+        g_object_get (device,
+                      "kind", &kind,
+                      "state", &state,
+                      NULL);
+
+        /* add old state for transitions */
+        g_debug ("adding %s with state %s",
+                 up_device_get_object_path (device), up_device_state_to_string (state));
+        g_object_set_data (G_OBJECT(device),
+                           "engine-state-old",
+                           GUINT_TO_POINTER(state));
+
+        if (kind == UP_DEVICE_KIND_BATTERY) {
+                g_debug ("updating because we added a device");
+                composite = engine_update_composite_device (manager, device);
+
+                /* get the same values for the composite device */
+                warning = engine_get_warning (manager, composite);
+                g_object_set_data (G_OBJECT(composite),
+                                   "engine-warning-old",
+                                   GUINT_TO_POINTER(warning));
+                g_object_get (composite, "state", &state, NULL);
+                g_object_set_data (G_OBJECT(composite),
+                                   "engine-state-old",
+                                   GUINT_TO_POINTER(state));
+        }
+}
+
+static gboolean
+engine_check_recall (GsdPowerManager *manager, UpDevice *device)
+{
+        UpDeviceKind kind;
+        gboolean recall_notice = FALSE;
+        gchar *recall_vendor = NULL;
+        gchar *recall_url = NULL;
+
+        /* get device properties */
+        g_object_get (device,
+                      "kind", &kind,
+                      "recall-notice", &recall_notice,
+                      "recall-vendor", &recall_vendor,
+                      "recall-url", &recall_url,
+                      NULL);
+
+        /* not battery */
+        if (kind != UP_DEVICE_KIND_BATTERY)
+                goto out;
+
+        /* no recall data */
+        if (!recall_notice)
+                goto out;
+
+        /* emit signal for manager */
+        g_debug ("** EMIT: perhaps-recall");
+        g_debug ("%s-%s", recall_vendor, recall_url);
+out:
+        g_free (recall_vendor);
+        g_free (recall_url);
+        return recall_notice;
+}
+
+static gboolean
+engine_coldplug (GsdPowerManager *manager)
+{
+        guint i;
+        GPtrArray *array = NULL;
+        UpDevice *device;
+        gboolean ret;
+        GError *error = NULL;
+
+        /* get devices from UPower */
+        ret = up_client_enumerate_devices_sync (manager->priv->up_client, NULL, &error);
+        if (!ret) {
+                g_error ("failed to get device list: %s", error->message);
+                g_error_free (error);
+                goto out;
+        }
+
+        /* connected mobile phones */
+        gpm_phone_coldplug (manager->priv->phone);
+
+        engine_recalculate_state (manager);
+
+        /* add to database */
+        array = up_client_get_devices (manager->priv->up_client);
+        for (i=0;i<array->len;i++) {
+                device = g_ptr_array_index (array, i);
+                engine_device_add (manager, device);
+                engine_check_recall (manager, device);
+        }
+out:
+        if (array != NULL)
+                g_ptr_array_unref (array);
+        /* never repeat */
+        return FALSE;
+}
+
+static void
+engine_device_added_cb (UpClient *client, UpDevice *device, GsdPowerManager *manager)
+{
+        /* add to list */
+        g_ptr_array_add (manager->priv->devices_array, g_object_ref (device));
+        engine_check_recall (manager, device);
+
+        engine_recalculate_state (manager);
+}
+
+static void
+engine_device_removed_cb (UpClient *client, UpDevice *device, GsdPowerManager *manager)
+{
+        gboolean ret;
+        ret = g_ptr_array_remove (manager->priv->devices_array, device);
+        if (!ret)
+                return;
+        engine_recalculate_state (manager);
+}
+
+static void
+engine_device_changed_cb (UpClient *client, UpDevice *device, GsdPowerManager *manager)
+{
+        UpDeviceKind kind;
+        UpDeviceState state;
+        UpDeviceState state_old;
+        GsdPowerManagerWarning warning_old;
+        GsdPowerManagerWarning warning;
+
+        /* get device properties */
+        g_object_get (device,
+                      "kind", &kind,
+                      NULL);
+
+        /* if battery then use composite device to cope with multiple batteries */
+        if (kind == UP_DEVICE_KIND_BATTERY) {
+                g_debug ("updating because %s changed", up_device_get_object_path (device));
+                device = engine_update_composite_device (manager, device);
+        }
+
+        /* get device properties (may be composite) */
+        g_object_get (device,
+                      "state", &state,
+                      NULL);
+
+        g_debug ("%s state is now %s", up_device_get_object_path (device), up_device_state_to_string (state));
+
+        /* see if any interesting state changes have happened */
+        state_old = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(device), "engine-state-old"));
+        if (state_old != state) {
+                if (state == UP_DEVICE_STATE_DISCHARGING) {
+                        g_debug ("** EMIT: discharging");
+                } else if (state == UP_DEVICE_STATE_FULLY_CHARGED) {
+                        g_debug ("** EMIT: fully charged");
+                }
+
+                /* save new state */
+                g_object_set_data (G_OBJECT(device), "engine-state-old", GUINT_TO_POINTER(state));
+        }
+
+        /* check the warning state has not changed */
+        warning_old = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(device), "engine-warning-old"));
+        warning = engine_get_warning (manager, device);
+        if (warning != warning_old) {
+                if (warning == WARNING_LOW) {
+                        g_debug ("** EMIT: charge-low");
+                } else if (warning == WARNING_CRITICAL) {
+                        g_debug ("** EMIT: charge-critical");
+                } else if (warning == WARNING_ACTION) {
+                        g_debug ("** EMIT: charge-action");
+                }
+                /* save new state */
+                g_object_set_data (G_OBJECT(device), "engine-warning-old", GUINT_TO_POINTER(warning));
+        }
+
+        engine_recalculate_state (manager);
+}
+
+static UpDevice *
+engine_get_primary_device (GsdPowerManager *manager)
+{
+        guint i;
+        UpDevice *device = NULL;
+        UpDevice *device_tmp;
+        UpDeviceKind kind;
+        UpDeviceState state;
+        gboolean is_present;
+
+        for (i=0; i<manager->priv->devices_array->len; i++) {
+                device_tmp = g_ptr_array_index (manager->priv->devices_array, i);
+
+                /* get device properties */
+                g_object_get (device_tmp,
+                              "kind", &kind,
+                              "state", &state,
+                              "is-present", &is_present,
+                              NULL);
+
+                /* not present */
+                if (!is_present)
+                        continue;
+
+                /* not discharging */
+                if (state != UP_DEVICE_STATE_DISCHARGING)
+                        continue;
+
+                /* not battery */
+                if (kind != UP_DEVICE_KIND_BATTERY)
+                        continue;
+
+                /* use composite device to cope with multiple batteries */
+                device = g_object_ref (engine_get_composite_device (manager, device_tmp));
+                break;
+        }
+        return device;
+}
+
+static void
+phone_device_added_cb (GpmPhone *phone, guint idx, GsdPowerManager *manager)
+{
+        UpDevice *device;
+        device = up_device_new ();
+
+        g_debug ("phone added %i", idx);
+
+        /* get device properties */
+        g_object_set (device,
+                      "kind", UP_DEVICE_KIND_PHONE,
+                      "is-rechargeable", TRUE,
+                      "native-path", g_strdup_printf ("dummy:phone_%i", idx),
+                      "is-present", TRUE,
+                      NULL);
+
+        /* state changed */
+        engine_device_add (manager, device);
+        g_ptr_array_add (manager->priv->devices_array, g_object_ref (device));
+        engine_recalculate_state (manager);
+}
+
+static void
+phone_device_removed_cb (GpmPhone *phone, guint idx, GsdPowerManager *manager)
+{
+        guint i;
+        UpDevice *device;
+        UpDeviceKind kind;
+
+        g_debug ("phone removed %i", idx);
+
+        for (i=0; i<manager->priv->devices_array->len; i++) {
+                device = g_ptr_array_index (manager->priv->devices_array, i);
+
+                /* get device properties */
+                g_object_get (device,
+                              "kind", &kind,
+                              NULL);
+
+                if (kind == UP_DEVICE_KIND_PHONE) {
+                        g_ptr_array_remove_index (manager->priv->devices_array, i);
+                        break;
+                }
+        }
+
+        /* state changed */
+        engine_recalculate_state (manager);
+}
+
+static void
+phone_device_refresh_cb (GpmPhone *phone, guint idx, GsdPowerManager *manager)
+{
+        guint i;
+        UpDevice *device;
+        UpDeviceKind kind;
+        UpDeviceState state;
+        gboolean is_present;
+        gdouble percentage;
+
+        g_debug ("phone refresh %i", idx);
+
+        for (i=0; i<manager->priv->devices_array->len; i++) {
+                device = g_ptr_array_index (manager->priv->devices_array, i);
+
+                /* get device properties */
+                g_object_get (device,
+                              "kind", &kind,
+                              "state", &state,
+                              "percentage", &percentage,
+                              "is-present", &is_present,
+                              NULL);
+
+                if (kind == UP_DEVICE_KIND_PHONE) {
+                        is_present = gpm_phone_get_present (phone, idx);
+                        state = gpm_phone_get_on_ac (phone, idx) ? UP_DEVICE_STATE_CHARGING : UP_DEVICE_STATE_DISCHARGING;
+                        percentage = gpm_phone_get_percentage (phone, idx);
+                        break;
+                }
+        }
+
+        /* state changed */
+        engine_recalculate_state (manager);
+}
+
 static void
 gnome_session_shutdown_cb (GObject *source_object,
                            GAsyncResult *res,
@@ -376,6 +1236,9 @@ gsd_power_manager_start (GsdPowerManager *manager,
         if (manager->priv->x11_screen == NULL)
                 return FALSE;
 
+        /* coldplug the engine */
+        engine_coldplug (manager);
+
         gnome_settings_profile_end (NULL);
         return TRUE;
 }
@@ -466,8 +1329,16 @@ gsd_power_manager_init (GsdPowerManager *manager)
 
         manager->priv->kbd_brightness_old = -1;
         manager->priv->settings = g_settings_new (GSD_POWER_SETTINGS_SCHEMA);
+        g_signal_connect (manager->priv->settings, "changed",
+                          G_CALLBACK (engine_settings_key_changed_cb), manager);
         manager->priv->up_client = up_client_new ();
         manager->priv->lid_is_closed = up_client_get_lid_is_closed (manager->priv->up_client);
+        g_signal_connect (manager->priv->up_client, "device-added",
+                          G_CALLBACK (engine_device_added_cb), manager);
+        g_signal_connect (manager->priv->up_client, "device-removed",
+                          G_CALLBACK (engine_device_removed_cb), manager);
+        g_signal_connect (manager->priv->up_client, "device-changed",
+                          G_CALLBACK (engine_device_changed_cb), manager);
         g_signal_connect (manager->priv->up_client, "changed",
                           G_CALLBACK (up_client_changed_cb), manager);
 
@@ -481,6 +1352,46 @@ gsd_power_manager_init (GsdPowerManager *manager)
                                   NULL,
                                   power_keyboard_proxy_ready_cb,
                                   manager);
+
+        manager->priv->devices_array = g_ptr_array_new_with_free_func (g_object_unref);
+
+        manager->priv->phone = gpm_phone_new ();
+        g_signal_connect (manager->priv->phone, "device-added",
+                          G_CALLBACK (phone_device_added_cb), manager);
+        g_signal_connect (manager->priv->phone, "device-removed",
+                          G_CALLBACK (phone_device_removed_cb), manager);
+        g_signal_connect (manager->priv->phone, "device-refresh",
+                          G_CALLBACK (phone_device_refresh_cb), manager);
+
+        /* create a fake virtual composite battery */
+        manager->priv->device_composite = up_device_new ();
+        g_object_set (manager->priv->device_composite,
+                      "kind", UP_DEVICE_KIND_BATTERY,
+                      "is-rechargeable", TRUE,
+                      "native-path", "dummy:composite_battery",
+                      "power-supply", TRUE,
+                      "is-present", TRUE,
+                      NULL);
+
+        /* get percentage policy */
+        manager->priv->low_percentage = g_settings_get_int (manager->priv->settings,
+                                                            "percentage-low");
+        manager->priv->critical_percentage = g_settings_get_int (manager->priv->settings,
+                                                                 "percentage-critical");
+        manager->priv->action_percentage = g_settings_get_int (manager->priv->settings,
+                                                               "percentage-action");
+
+        /* get time policy */
+        manager->priv->low_time = g_settings_get_int (manager->priv->settings,
+                                                      "time-low");
+        manager->priv->critical_time = g_settings_get_int (manager->priv->settings,
+                                                           "time-critical");
+        manager->priv->action_time = g_settings_get_int (manager->priv->settings,
+                                                         "time-action");
+
+        /* we can disable this if the time remaining is inaccurate or just plain wrong */
+        manager->priv->use_time_primary = g_settings_get_boolean (manager->priv->settings,
+                                                                  "use-time-for-policy");
 }
 
 static void
@@ -500,6 +1411,14 @@ gsd_power_manager_finalize (GObject *object)
         if (manager->priv->x11_screen != NULL);
                 g_object_unref (manager->priv->x11_screen);
 
+        g_ptr_array_unref (manager->priv->devices_array);
+        g_object_unref (manager->priv->phone);
+        g_object_unref (manager->priv->device_composite);
+
+        if (manager->priv->previous_icon != NULL)
+                g_object_unref (manager->priv->previous_icon);
+        g_free (manager->priv->previous_summary);
+
         G_OBJECT_CLASS (gsd_power_manager_parent_class)->finalize (object);
 }
 
@@ -742,7 +1661,7 @@ handle_method_call_main (GsdPowerManager *manager,
                          GVariant *parameters,
                          GDBusMethodInvocation *invocation)
 {
-        GPtrArray *array = NULL;
+        GPtrArray *array;
         guint i;
         GVariantBuilder *builder;
         GVariant *tuple = NULL;
@@ -753,19 +1672,19 @@ handle_method_call_main (GsdPowerManager *manager,
         if (g_strcmp0 (method_name, "GetPrimaryDevice") == 0) {
 
                 /* get the virtual device */
-                device = NULL;
+                device = engine_get_primary_device (manager);
                 if (device == NULL) {
                         g_dbus_method_invocation_return_dbus_error (invocation,
                                                                     "org.gnome.SettingsDaemon.Power.Failed",
                                                                     "There is no primary device.");
-                        goto out;
+                        return;
                 }
 
                 /* return the value */
                 value = device_to_variant_blob (device);
                 tuple = g_variant_new_tuple (&value, 1);
                 g_dbus_method_invocation_return_value (invocation, tuple);
-                goto out;
+                return;
         }
 
         /* return array */
@@ -775,7 +1694,7 @@ handle_method_call_main (GsdPowerManager *manager,
                 builder = g_variant_builder_new (G_VARIANT_TYPE("a(susdut)"));
 
                 /* add each tuple to the array */
-                array = g_ptr_array_new ();
+                array = g_ptr_array_ref (manager->priv->devices_array);
                 for (i=0; i<array->len; i++) {
                         device = g_ptr_array_index (array, i);
                         value = device_to_variant_blob (device);
@@ -787,10 +1706,10 @@ handle_method_call_main (GsdPowerManager *manager,
                 tuple = g_variant_new_tuple (&value, 1);
                 g_dbus_method_invocation_return_value (invocation, tuple);
                 g_variant_builder_unref (builder);
+                return;
         }
-out:
-        if (array != NULL)
-                g_ptr_array_unref (array);
+
+        g_assert_not_reached ();
 }
 
 static void
@@ -836,17 +1755,27 @@ handle_get_property (GDBusConnection *connection,
                      const gchar *property_name,
                      GError **error, gpointer user_data)
 {
+        GsdPowerManager *manager = GSD_POWER_MANAGER (user_data);
+        gchar *tooltip = NULL;
+        GIcon *icon = NULL;
         GVariant *retval = NULL;
 
         if (g_strcmp0 (property_name, "Icon") == 0) {
-                retval = g_variant_new_string ("");
+                icon = engine_get_icon (manager);
+                if (icon != NULL)
+                        tooltip = g_icon_to_string (icon);
+                retval = g_variant_new_string (tooltip != NULL ? tooltip : "");
                 goto out;
         }
         if (g_strcmp0 (property_name, "Tooltip") == 0) {
-                retval = g_variant_new_string ("");
+                tooltip = engine_get_summary (manager);
+                retval = g_variant_new_string (tooltip != NULL ? tooltip : "");
                 goto out;
         }
 out:
+        if (icon != NULL)
+                g_object_unref (icon);
+        g_free (tooltip);
         return retval;
 }
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]