[gnome-applets] upower support



commit d810bb5b9f529b91729519856011d0f6901a3243
Author: Joachim Breitner <mail joachim-breitner de>
Date:   Mon Jul 5 12:20:04 2010 +0200

    upower support
    
    As HAL is being deprecated, the battstat applet is adjusted to talk to
    upower, using libupower-glib
    
    https://bugzilla.gnome.org/show_bug.cgi?id=607254

 battstat/Makefile.am         |    7 +-
 battstat/battstat-upower.c   |  301 ++++++++++++++++++++++++++++++++++++++++++
 battstat/battstat-upower.h   |   33 +++++
 battstat/battstat_applet.c   |    7 +-
 battstat/docs/C/battstat.xml |   19 ++-
 battstat/power-management.c  |   66 ++++++++--
 configure.in                 |   26 ++++
 7 files changed, 435 insertions(+), 24 deletions(-)
---
diff --git a/battstat/Makefile.am b/battstat/Makefile.am
index 01affb5..26edd63 100644
--- a/battstat/Makefile.am
+++ b/battstat/Makefile.am
@@ -27,6 +27,7 @@ INCLUDES =                                    \
        $(GNOME_APPLETS_CFLAGS)                 \
        $(LIBNOTIFY_CFLAGS)                     \
        $(HAL_CFLAGS)                           \
+       $(UPOWER_CFLAGS)                        \
        $(APMINC)                               \
        $(ACPIINC)                              \
        $(WARN_CFLAGS)                          \
@@ -48,13 +49,17 @@ battstat_applet_2_SOURCES = \
        acpi-freebsd.c \
        acpi-freebsd.h \
        battstat-hal.c \
-       battstat-hal.h
+       battstat-hal.h \
+       battstat-upower.c \
+       battstat-upower.h
 
 
 battstat_applet_2_LDADD = \
                     $(GNOME_APPLETS_LIBS) \
                     $(LIBNOTIFY_LIBS) \
                     $(HAL_LIBS) \
+                    $(UPOWER_LIBS) \
+                    $(LIBM) \
                     $(APMLIB)
 
 schemasdir   = @GCONF_SCHEMA_FILE_DIR@
diff --git a/battstat/battstat-upower.c b/battstat/battstat-upower.c
new file mode 100644
index 0000000..7370bca
--- /dev/null
+++ b/battstat/battstat-upower.c
@@ -0,0 +1,301 @@
+/*
+ *  Copyright (C) 2010 by Joachim Breitner <mail joachim-breitner de>
+ *
+ * Based on battstat-hal.c:
+ * Copyright (C) 2005 by Ryan Lortie <desrt desrt ca>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ * $Id$
+ */
+
+#include <config.h>
+
+#ifdef HAVE_UPOWER
+
+#include <upower.h>
+#include <math.h>
+
+#include "battstat-upower.h"
+
+static UpClient *upc;
+static void (*status_updated_callback) (void);
+
+
+/* status_updated_callback() can not be called directly because at the time of
+ * the device-remove signal, the device is not actually removed from the list
+ * of devices known to the up_client object (see libupower-glib/up-client.c in
+ * upower). Waiting for the next idle timer works around this issue and has has
+ * the additionaly benefit of possibly running status_updated_callback only
+ * once when several events happen very soon after each other.
+ */
+static gboolean status_update_scheduled;
+
+static gboolean
+update_status_idle (gpointer junk)
+{
+  if (status_updated_callback)
+    status_updated_callback ();
+
+  return status_update_scheduled = FALSE;
+}
+
+static void
+schedule_status_callback (void)
+{
+  if (status_update_scheduled)
+    return;
+
+  status_update_scheduled = TRUE;
+  g_idle_add (update_status_idle, NULL);
+}
+
+static void
+device_cb (UpClient *client, UpDevice *device, gpointer user_data) {
+  schedule_status_callback();
+}
+
+/* ---- public functions ---- */
+
+char *
+battstat_upower_initialise (void (*callback) (void))
+{
+  status_updated_callback = callback;
+
+  if( upc != NULL )
+    return g_strdup( "Already initialised!" );
+
+  if( (upc = up_client_new() ) == NULL )
+    goto error_out;
+
+  if (! up_client_enumerate_devices_sync( upc, NULL, NULL ) ) {
+    goto error_shutdownclient;
+  }
+
+  g_signal_connect_after( upc, "device-changed", device_cb, NULL );
+  g_signal_connect_after( upc, "device-added", device_cb, NULL );
+  g_signal_connect_after( upc, "device-removed", device_cb, NULL );
+
+  return NULL;
+
+error_shutdownclient:
+  g_object_unref( upc );
+  upc = NULL;
+
+error_out:
+  return "Can not initialize upower";
+}
+
+void
+battstat_upower_cleanup( void )
+{
+  if( upc == NULL )
+    return;
+  
+  g_object_unref( upc );
+  upc = NULL;
+}
+
+#include "battstat.h"
+
+/* This function currently exists to allow the multiple batteries supported
+ * by the upower backend to appear as a single composite battery device (since
+ * at the current time this is all that battstat supports).
+ *
+ * This entire function is filled with logic to make multiple batteries
+ * appear as one "composite" battery.  Comments included as appropriate.
+ *
+ * For more information about some of the assumptions made in the following
+ * code please see the following mailing list post and the resulting thread:
+ *
+ *   http://lists.freedesktop.org/archives/hal/2005-July/002841.html
+ */
+void
+battstat_upower_get_battery_info( BatteryStatus *status )
+{
+
+  GPtrArray *devices = up_client_get_devices( upc );
+
+  /* The calculation to get overall percentage power remaining is as follows:
+   *
+   *    Sum( Current charges ) / Sum( Full Capacities )
+   *
+   * We can't just take an average of all of the percentages since this
+   * doesn't deal with the case that one battery might have a larger
+   * capacity than the other.
+   *
+   * In order to do this calculation, we need to keep a running total of
+   * current charge and full capacities.
+   */
+  double current_charge_total = 0, full_capacity_total = 0;
+
+  /* Record the time remaining as reported by upower.  This is used in the event
+   * that the system has exactly one battery (since, then, upower is capable
+   * of providing an accurate time remaining report and we should trust it.)
+   */
+  gint64 remaining_time = 0;
+
+  /* The total (dis)charge rate of the system is the sum of the rates of
+   * the individual batteries.
+   */
+  double rate_total = 0;
+
+  /* We need to know if we should report the composite battery as present
+   * at all.  The logic is that if at least one actual battery is installed
+   * then the composite battery will be reported to exist.
+   */
+  int present = 0;
+
+  /* We need to know if we are on AC power or not.  Eventually, we can look
+   * at the AC adaptor upower devices to determine that.  For now, we assume that
+   * if any battery is discharging then we must not be on AC power.  Else, by
+   * default, we must be on AC.
+   */
+  int on_ac_power = 1;
+
+  /* Finally, we consider the composite battery to be "charging" if at least
+   * one of the actual batteries in the system is charging.
+   */
+  int charging = 0;
+
+  /* For each physical battery bay... */
+  int i;
+  for( i = 0; i < devices->len; i++ )
+  {
+    UpDevice *upd = g_ptr_array_index( devices, i );
+
+    int type, state;
+    double current_charge, full_capacity, rate;
+    gint64 time_to_full, time_to_empty;
+    
+    g_object_get( upd,
+      "kind", &type,
+      "state", &state,
+      "energy", &current_charge,
+      "energy-full", &full_capacity,
+      "energy-rate", &rate,
+      "time-to-full", &time_to_full,
+      "time-to-empty", &time_to_empty,
+      NULL );
+
+    /* Only count batteries here */
+
+    if (type != UP_DEVICE_KIND_BATTERY)
+      continue;
+
+    /* At least one battery present -> composite battery is present. */
+    present++;
+
+    /* At least one battery charging -> composite battery is charging. */
+    if( state == UP_DEVICE_STATE_CHARGING )
+      charging = 1;
+
+    /* At least one battery is discharging -> we're not on AC. */
+    if( state == UP_DEVICE_STATE_DISCHARGING )
+      on_ac_power = 0;
+
+    /* Sum the totals for current charge, design capacity, (dis)charge rate. */
+    current_charge_total += current_charge;
+    full_capacity_total += full_capacity;
+    rate_total += rate;
+
+    /* Record remaining time too, incase this is the only battery. */
+    remaining_time = (state == UP_DEVICE_STATE_DISCHARGING ? time_to_empty : time_to_full);
+  }
+
+  if( !present || full_capacity_total <= 0 || (charging && !on_ac_power) )
+  {
+    /* Either no battery is present or something has gone horribly wrong.
+     * In either case we must return that the composite battery is not
+     * present.
+     */
+    status->present = FALSE;
+    status->percent = 0;
+    status->minutes = -1;
+    status->on_ac_power = TRUE;
+    status->charging = FALSE;
+
+    g_ptr_array_unref( devices );
+    return;
+  }
+
+  /* Else, our composite battery is present. */
+  status->present = TRUE;
+
+  /* As per above, overall charge is:
+   *
+   *    Sum( Current charges ) / Sum( Full Capacities )
+   */
+  status->percent = ( current_charge_total / full_capacity_total ) * 100.0 + 0.5;
+
+  if( present == 1 )
+  {
+    /* In the case of exactly one battery, report the time remaining figure
+     * from upower directly since it might have come from an authorative source
+     * (ie: the PMU or APM subsystem).
+     *
+     * upower gives remaining time in seconds with a 0 to mean that the
+     * remaining time is unknown.  Battstat uses minutes and -1 for 
+     * unknown time remaining.
+     */
+
+    if( remaining_time == 0 )
+      status->minutes = -1;
+    else
+      status->minutes = (remaining_time + 30) / 60;
+  }
+  /* Rest of cases to deal with multiple battery systems... */
+  else if( !on_ac_power && rate_total != 0 )
+  {
+    /* Then we're discharging.  Calculate time remaining until at zero. */
+
+    double remaining;
+
+    remaining = current_charge_total;
+    remaining /= rate_total;
+    status->minutes = (int) floor( remaining * 60.0 + 0.5 );
+  }
+  else if( charging && rate_total != 0 )
+  {
+    /* Calculate time remaining until charged.  For systems with more than
+     * one battery, this code is very approximate.  The assumption is that if
+     * one battery reaches full charge before the other that the other will
+     * start charging faster due to the increase in available power (similar
+     * to how a laptop will charge faster if you're not using it).
+     */
+
+    double remaining;
+
+    remaining = full_capacity_total - current_charge_total;
+    if( remaining < 0 )
+      remaining = 0;
+    remaining /= rate_total;
+
+    status->minutes = (int) floor( remaining * 60.0 + 0.5 );
+  }
+  else
+  {
+    /* On AC power and not charging -or- rate is unknown. */
+    status->minutes = -1;
+  }
+
+  /* These are simple and well-explained above. */
+  status->charging = charging;
+  status->on_ac_power = on_ac_power;
+  
+  g_ptr_array_unref( devices );
+}
+
+#endif /* HAVE_UPOWER */
diff --git a/battstat/battstat-upower.h b/battstat/battstat-upower.h
new file mode 100644
index 0000000..ad7e035
--- /dev/null
+++ b/battstat/battstat-upower.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2010 by Joachim Breitner <mail joachim-breitner de>
+ *
+ * Based on battstat-hal.h:
+ * Copyright (C) 2005 by Ryan Lortie <desrt desrt ca>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ * $Id$
+ */
+
+#ifndef _battstat_upower_h_
+#define _battstat_upower_h_
+
+char *battstat_upower_initialise (void (*) (void));
+void battstat_upower_cleanup (void);
+
+#include "battstat.h"
+void battstat_upower_get_battery_info (BatteryStatus *status);
+
+#endif /* _battstat_upower_h_ */
diff --git a/battstat/battstat_applet.c b/battstat/battstat_applet.c
index 393daaa..986afa3 100644
--- a/battstat/battstat_applet.c
+++ b/battstat/battstat_applet.c
@@ -1188,9 +1188,12 @@ about_cb( GtkAction *action, ProgressData *battstat )
 
   char *comments = g_strdup_printf ("%s\n\n%s",
                  _("This utility shows the status of your laptop battery."),
-                 power_management_using_hal () ?
+                 power_management_using_upower () ?
+                       /* true */ _("upower backend enabled.") :
+                 (power_management_using_hal () ?
                        /* true */ _("HAL backend enabled.") :
-                       /* false */ _("Legacy (non-HAL) backend enabled."));
+                       /* false */ _("Legacy (non-HAL) backend enabled.")
+                 ));
 
   gtk_show_about_dialog( NULL,
     "version",             VERSION,
diff --git a/battstat/docs/C/battstat.xml b/battstat/docs/C/battstat.xml
index e7b9e6e..2d53926 100644
--- a/battstat/docs/C/battstat.xml
+++ b/battstat/docs/C/battstat.xml
@@ -226,10 +226,13 @@
      <title>Power Management Backends</title>
 
      <para>
-      The battery monitor supports a number of power management backends. If it
-      is available, the monitor will attempt to use the freedesktop.org
-      <ulink url="http://freedesktop.org/Software/hal";>HAL (Hardware Abstraction
-      Layer)</ulink>. If it is unavailable or unsupported on your platform, the
+      The battery monitor supports a number of power management backends.
+      If it is available, the monitor will attempt to use the freedesktop.org
+      <ulink url="http://upower.freedesktop.org/";>upower</ulink> interface.
+      If it is unavailable or unsupported on your platform, it will fall back
+      to the freedesktop.org
+      <ulink url="http://freedesktop.org/Software/hal";>HAL (Hardware
+      Abstraction Layer)</ulink>. If that is also not availble, the
       battery monitor will attempt direct access to the power management system.
      </para>
      <para>
@@ -410,10 +413,10 @@
     <sect2 id="battstat-troubleshooting-backends">
      <title>Determining the backend</title>
      <para>
-       If you are using the Hardware Abstraction Layer
-       (see <xref linkend="battstat-power-backends"/>) then that will be
-       indicated in the about dialog by placing a star next to the author of the
-       HAL backend.
+       If you are using the upower interface, or the the Hardware
+       Abstraction Layer (see <xref linkend="battstat-power-backends"/>) then
+       that will be indicated in the about dialog by placing a star next to the
+       author of the HAL backend.
     <figure id="battstat-credits-hal"> 
       <title>Check you're using the HAL backend</title> 
       <screenshot> 
diff --git a/battstat/power-management.c b/battstat/power-management.c
index 9eb9a78..c2c477b 100644
--- a/battstat/power-management.c
+++ b/battstat/power-management.c
@@ -41,6 +41,7 @@
 
 #include "battstat.h"
 #include "battstat-hal.h"
+#include "battstat-upower.h"
 
 #define ERR_ACPID _("Can't access ACPI events in /var/run/acpid.socket! "    \
                     "Make sure the ACPI subsystem is working and "           \
@@ -66,6 +67,9 @@ static int pm_initialised;
 #ifdef HAVE_HAL
 static int using_hal;
 #endif
+#ifdef HAVE_UPOWER
+static int using_upower;
+#endif
 
 /*
  * What follows is a series of platform-specific apm_readinfo functions
@@ -390,6 +394,14 @@ power_management_getinfo( BatteryStatus *status )
     return NULL;
   }
 
+#ifdef HAVE_UPOWER
+  if( using_upower )
+  {
+    battstat_upower_get_battery_info( status );
+    return NULL;
+  }
+#endif
+
 #ifdef HAVE_HAL
   if( using_hal )
   {
@@ -430,27 +442,36 @@ power_management_getinfo( BatteryStatus *status )
 const char *
 power_management_initialise (int no_hal, void (*callback) (void))
 {
+  char *err;
+  err = g_strdup( ":(" );
 #ifdef __linux__
   struct stat statbuf;
 #endif
-#ifdef HAVE_HAL
-  char *err;
+#ifdef HAVE_UPOWER
+  err = battstat_upower_initialise (callback);
 
-  if( no_hal )
-    err = g_strdup( ":(" );
-  else
-    err = battstat_hal_initialise (callback);
-
-
-  if( err == NULL ) /* HAL is up */
+  if( err == NULL ) /* UPOWER is up */
   {
     pm_initialised = 1;
-    using_hal = TRUE;
+    using_upower = TRUE;
     return NULL;
+  } 
+#endif
+
+#ifdef HAVE_HAL
+  if(! no_hal ) {
+    err = battstat_hal_initialise (callback);
+
+    if( err == NULL ) /* HAL is up */
+    {
+      pm_initialised = 1;
+      using_hal = TRUE;
+      return NULL;
+    }
   }
-  else
-    /* fallback to legacy methods */
-    g_free( err );
+
+  /* fallback to legacy methods */
+  g_free( err );
 #endif
     
 #ifdef __linux__
@@ -498,6 +519,15 @@ power_management_initialise (int no_hal, void (*callback) (void))
 void
 power_management_cleanup( void )
 {
+#ifdef HAVE_UPOWER
+  if( using_upower )
+  {
+    battstat_upower_cleanup();
+    pm_initialised = 1;
+    return;
+  }
+#endif
+
 #ifdef HAVE_HAL
   if( using_hal )
   {
@@ -525,6 +555,16 @@ power_management_cleanup( void )
 }
 
 int
+power_management_using_upower( void )
+{
+#ifdef HAVE_UPOWER
+  return using_upower;
+#else
+  return 0;
+#endif
+}
+
+int
 power_management_using_hal( void )
 {
 #ifdef HAVE_HAL
diff --git a/configure.in b/configure.in
index 1664c30..c026718 100644
--- a/configure.in
+++ b/configure.in
@@ -23,6 +23,7 @@ LIBXKLAVIER_REQUIRED=4.0
 LIBWNCK_REQUIRED=2.91.0
 LIBNOTIFY_REQUIRED=0.7
 HAL_REQUIRED=0.5.3
+UPOWER_REQUIRED=0.9.4
 DBUS_REQUIRED=1.1.2
 DBUS_GLIB_REQUIRED=0.74
 PYGOBJECT_REQUIRED=2.26
@@ -223,6 +224,30 @@ fi
 AC_SUBST(HAL_CFLAGS)
 AC_SUBST(HAL_LIBS)
 
+dnl -- check for libupower-glib (optional) --------------------------------------------
+UPOWER_CFLAGS=
+UPOWER_LIBS=
+AC_ARG_WITH(upower,[  --without-upower           build without upower support])
+
+if test "x$with_upower" != xno; then
+       PKG_CHECK_MODULES(UPOWER, upower-glib >= $UPOWER_REQUIRED,
+                 HAVE_UPOWER="yes",
+                 HAVE_UPOWER="no")
+
+       LT_LIB_M
+       if test "x$HAVE_UPOWER" = "xyes"; then
+               AC_DEFINE(HAVE_UPOWER, 1, [UPOWER available])
+       fi
+else
+       AC_MSG_WARN(["upower support disabled"])
+fi
+
+AC_SUBST(UPOWER_CFLAGS)
+AC_SUBST(UPOWER_LIBS)
+AC_SUBST(LIBM)
+
+
+
 dnl -- check for gucharmap (optional) -----------------------------------------
 
 PKG_CHECK_MODULES([GUCHARMAP],[gucharmap-2.90 >= $GUCHARMAP3_REQUIRED],
@@ -743,5 +768,6 @@ gnome-applets-$VERSION configure summary:
        Using DBUS:                     $HAVE_DBUS
        Using NetworkManager:           $HAVE_NETWORKMANAGER
        Using HAL:                      $HAVE_HAL
+       Using UPOWER:                   $HAVE_UPOWER
        Enabling IPv6:                  $have_ipv6
 " >&2


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