power: Add Linux org.freedesktop.UPower D-Bus implementation.
authorRyan C. Gordon <icculus@icculus.org>
Sun, 28 May 2017 07:14:11 -0400
changeset 110435be3faa49e54
parent 11042 303c875e47f0
child 11044 57e3891f1ef8
power: Add Linux org.freedesktop.UPower D-Bus implementation.

Fixes Bugzilla #3485.

(I think.)
src/core/linux/SDL_dbus.c
src/core/linux/SDL_dbus.h
src/power/SDL_power.c
src/power/SDL_syspower.h
src/power/linux/SDL_syspower.c
     1.1 --- a/src/core/linux/SDL_dbus.c	Sun May 28 07:11:52 2017 -0400
     1.2 +++ b/src/core/linux/SDL_dbus.c	Sun May 28 07:14:11 2017 -0400
     1.3 @@ -70,6 +70,7 @@
     1.4      SDL_DBUS_SYM(error_free);
     1.5      SDL_DBUS_SYM(get_local_machine_id);
     1.6      SDL_DBUS_SYM(free);
     1.7 +    SDL_DBUS_SYM(free_string_array);
     1.8      SDL_DBUS_SYM(shutdown);
     1.9  
    1.10      #undef SDL_DBUS_SYM
     2.1 --- a/src/core/linux/SDL_dbus.h	Sun May 28 07:11:52 2017 -0400
     2.2 +++ b/src/core/linux/SDL_dbus.h	Sun May 28 07:14:11 2017 -0400
     2.3 @@ -68,6 +68,7 @@
     2.4      void (*error_free)(DBusError *);
     2.5      char *(*get_local_machine_id)(void);
     2.6      void (*free)(void *);
     2.7 +    void (*free_string_array)(char **);
     2.8      void (*shutdown)(void);
     2.9  
    2.10  } SDL_DBusContext;
     3.1 --- a/src/power/SDL_power.c	Sun May 28 07:11:52 2017 -0400
     3.2 +++ b/src/power/SDL_power.c	Sun May 28 07:14:11 2017 -0400
     3.3 @@ -48,6 +48,7 @@
     3.4  static SDL_GetPowerInfo_Impl implementations[] = {
     3.5  #ifndef SDL_POWER_DISABLED
     3.6  #ifdef SDL_POWER_LINUX          /* in order of preference. More than could work. */
     3.7 +    SDL_GetPowerInfo_Linux_org_freedesktop_upower,
     3.8      SDL_GetPowerInfo_Linux_sys_class_power_supply,
     3.9      SDL_GetPowerInfo_Linux_proc_acpi,
    3.10      SDL_GetPowerInfo_Linux_proc_apm,
     4.1 --- a/src/power/SDL_syspower.h	Sun May 28 07:11:52 2017 -0400
     4.2 +++ b/src/power/SDL_syspower.h	Sun May 28 07:14:11 2017 -0400
     4.3 @@ -28,6 +28,7 @@
     4.4  #include "SDL_power.h"
     4.5  
     4.6  /* Not all of these are available in a given build. Use #ifdefs, etc. */
     4.7 +SDL_bool SDL_GetPowerInfo_Linux_org_freedesktop_upower(SDL_PowerState *, int *, int *);
     4.8  SDL_bool SDL_GetPowerInfo_Linux_sys_class_power_supply(SDL_PowerState *, int *, int *);
     4.9  SDL_bool SDL_GetPowerInfo_Linux_proc_acpi(SDL_PowerState *, int *, int *);
    4.10  SDL_bool SDL_GetPowerInfo_Linux_proc_apm(SDL_PowerState *, int *, int *);
     5.1 --- a/src/power/linux/SDL_syspower.c	Sun May 28 07:11:52 2017 -0400
     5.2 +++ b/src/power/linux/SDL_syspower.c	Sun May 28 07:14:11 2017 -0400
     5.3 @@ -34,6 +34,8 @@
     5.4  #include "SDL_power.h"
     5.5  #include "../SDL_syspower.h"
     5.6  
     5.7 +#include "../../core/linux/SDL_dbus.h"
     5.8 +
     5.9  static const char *proc_apm_path = "/proc/apm";
    5.10  static const char *proc_acpi_battery_path = "/proc/acpi/battery";
    5.11  static const char *proc_acpi_ac_adapter_path = "/proc/acpi/ac_adapter";
    5.12 @@ -426,8 +428,6 @@
    5.13      return SDL_TRUE;
    5.14  }
    5.15  
    5.16 -/* !!! FIXME: implement d-bus queries to org.freedesktop.UPower. */
    5.17 -
    5.18  SDL_bool
    5.19  SDL_GetPowerInfo_Linux_sys_class_power_supply(SDL_PowerState *state, int *seconds, int *percent)
    5.20  {
    5.21 @@ -514,6 +514,118 @@
    5.22      return SDL_TRUE;  /* don't look any further. */
    5.23  }
    5.24  
    5.25 +
    5.26 +/* d-bus queries to org.freedesktop.UPower. */
    5.27 +#if SDL_USE_LIBDBUS
    5.28 +#define UPOWER_DBUS_NODE "org.freedesktop.UPower"
    5.29 +#define UPOWER_DBUS_PATH "/org/freedesktop/UPower"
    5.30 +#define UPOWER_DBUS_INTERFACE "org.freedesktop.UPower"
    5.31 +#define UPOWER_DEVICE_DBUS_INTERFACE "org.freedesktop.UPower.Device"
    5.32 +
    5.33 +static void
    5.34 +check_upower_device(DBusConnection *conn, const char *path, SDL_PowerState *state, int *seconds, int *percent)
    5.35 +{
    5.36 +    SDL_bool choose = SDL_FALSE;
    5.37 +    SDL_PowerState st;
    5.38 +    int secs;
    5.39 +    int pct;
    5.40 +    Uint32 ui32 = 0;
    5.41 +    Sint64 si64 = 0;
    5.42 +    double d = 0.0;
    5.43 +
    5.44 +    if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "Type", DBUS_TYPE_UINT32, &ui32)) {
    5.45 +        return; /* Don't know _what_ we're looking at. Give up on it. */
    5.46 +    } else if (ui32 != 2) {  /* 2==Battery*/
    5.47 +        return;  /* we don't care about UPS and such. */
    5.48 +    } else if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "PowerSupply", DBUS_TYPE_BOOLEAN, &ui32)) {
    5.49 +        return;
    5.50 +    } else if (!ui32) {
    5.51 +        return;  /* we don't care about random devices with batteries, like wireless controllers, etc */
    5.52 +    } else if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "IsPresent", DBUS_TYPE_BOOLEAN, &ui32)) {
    5.53 +        return;
    5.54 +    } else if (!ui32) {
    5.55 +        st = SDL_POWERSTATE_NO_BATTERY;
    5.56 +    } else if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "State", DBUS_TYPE_UINT32, &ui32)) {
    5.57 +        st = SDL_POWERSTATE_UNKNOWN;  /* uh oh */
    5.58 +    } else if (ui32 == 1) {  /* 1 == charging */
    5.59 +        st = SDL_POWERSTATE_CHARGING;
    5.60 +    } else if ((ui32 == 2) || (ui32 == 3)) {  /* 2 == discharging, 3 == empty. */
    5.61 +        st = SDL_POWERSTATE_ON_BATTERY;
    5.62 +    } else if (ui32 == 4) {   /* 4 == full */
    5.63 +        st = SDL_POWERSTATE_CHARGED;
    5.64 +    } else {
    5.65 +        st = SDL_POWERSTATE_UNKNOWN;  /* uh oh */
    5.66 +    }
    5.67 +
    5.68 +    if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "Percentage", DBUS_TYPE_DOUBLE, &d)) {
    5.69 +        pct = -1;  /* some old/cheap batteries don't set this property. */
    5.70 +    } else {
    5.71 +        pct = (int) d;
    5.72 +        pct = (pct > 100) ? 100 : pct; /* clamp between 0%, 100% */
    5.73 +    }
    5.74 +
    5.75 +    if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "TimeToEmpty", DBUS_TYPE_INT64, &si64)) {
    5.76 +        secs = -1;
    5.77 +    } else {
    5.78 +        secs = (int) si64;
    5.79 +        secs = (secs <= 0) ? -1 : secs;  /* 0 == unknown */
    5.80 +    }
    5.81 +
    5.82 +    /*
    5.83 +     * We pick the battery that claims to have the most minutes left.
    5.84 +     *  (failing a report of minutes, we'll take the highest percent.)
    5.85 +     */
    5.86 +    if ((secs < 0) && (*seconds < 0)) {
    5.87 +        if ((pct < 0) && (*percent < 0)) {
    5.88 +            choose = SDL_TRUE;  /* at least we know there's a battery. */
    5.89 +        } else if (pct > *percent) {
    5.90 +            choose = SDL_TRUE;
    5.91 +        }
    5.92 +    } else if (secs > *seconds) {
    5.93 +        choose = SDL_TRUE;
    5.94 +    }
    5.95 +
    5.96 +    if (choose) {
    5.97 +        *seconds = secs;
    5.98 +        *percent = pct;
    5.99 +        *state = st;
   5.100 +    }
   5.101 +}
   5.102 +#endif
   5.103 +
   5.104 +SDL_bool
   5.105 +SDL_GetPowerInfo_Linux_org_freedesktop_upower(SDL_PowerState *state, int *seconds, int *percent)
   5.106 +{
   5.107 +    SDL_bool retval = SDL_FALSE;
   5.108 +
   5.109 +    #if SDL_USE_LIBDBUS
   5.110 +    SDL_DBusContext *dbus = SDL_DBus_GetContext();
   5.111 +    char **paths = NULL;
   5.112 +    int i, numpaths = 0;
   5.113 +
   5.114 +    if (!SDL_DBus_CallMethodOnConnection(dbus->system_conn, UPOWER_DBUS_NODE, UPOWER_DBUS_PATH, UPOWER_DBUS_INTERFACE, "EnumerateDevices",
   5.115 +            DBUS_TYPE_INVALID,
   5.116 +            DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &paths, &numpaths, DBUS_TYPE_INVALID)) {
   5.117 +        return SDL_FALSE;  /* try a different approach than UPower. */
   5.118 +    }
   5.119 +
   5.120 +    retval = SDL_TRUE;  /* Clearly we can use this interface. */
   5.121 +    *state = SDL_POWERSTATE_NO_BATTERY;  /* assume we're just plugged in. */
   5.122 +    *seconds = -1;
   5.123 +    *percent = -1;
   5.124 +
   5.125 +    for (i = 0; i < numpaths; i++) {
   5.126 +        check_upower_device(dbus->system_conn, paths[i], state, seconds, percent);
   5.127 +    }
   5.128 +
   5.129 +    if (dbus) {
   5.130 +        dbus->free_string_array(paths);
   5.131 +    }
   5.132 +    #endif  /* SDL_USE_LIBDBUS */
   5.133 +
   5.134 +    return retval;
   5.135 +}
   5.136 +
   5.137  #endif /* SDL_POWER_LINUX */
   5.138  #endif /* SDL_POWER_DISABLED */
   5.139