Linux: Implemented sysfs-based version of SDL_GetPowerInfo().
authorRyan C. Gordon <icculus@icculus.org>
Wed, 03 Jun 2015 13:11:28 -0700
changeset 9700d8cc85e50561
parent 9699 8e0e18edb270
child 9701 1c0e96906974
Linux: Implemented sysfs-based version of SDL_GetPowerInfo().

Fixes Bugzilla #2938.
src/power/SDL_power.c
src/power/linux/SDL_syspower.c
     1.1 --- a/src/power/SDL_power.c	Mon Jun 01 01:25:22 2015 -0400
     1.2 +++ b/src/power/SDL_power.c	Wed Jun 03 13:11:28 2015 -0700
     1.3 @@ -29,6 +29,7 @@
     1.4      (*SDL_GetPowerInfo_Impl) (SDL_PowerState * state, int *seconds,
     1.5                                int *percent);
     1.6  
     1.7 +SDL_bool SDL_GetPowerInfo_Linux_sys_class_power_supply(SDL_PowerState *, int *, int *);
     1.8  SDL_bool SDL_GetPowerInfo_Linux_proc_acpi(SDL_PowerState *, int *, int *);
     1.9  SDL_bool SDL_GetPowerInfo_Linux_proc_apm(SDL_PowerState *, int *, int *);
    1.10  SDL_bool SDL_GetPowerInfo_Windows(SDL_PowerState *, int *, int *);
    1.11 @@ -58,6 +59,7 @@
    1.12  static SDL_GetPowerInfo_Impl implementations[] = {
    1.13  #ifndef SDL_POWER_DISABLED
    1.14  #ifdef SDL_POWER_LINUX          /* in order of preference. More than could work. */
    1.15 +    SDL_GetPowerInfo_Linux_sys_class_power_supply,
    1.16      SDL_GetPowerInfo_Linux_proc_acpi,
    1.17      SDL_GetPowerInfo_Linux_proc_apm,
    1.18  #endif
     2.1 --- a/src/power/linux/SDL_syspower.c	Mon Jun 01 01:25:22 2015 -0400
     2.2 +++ b/src/power/linux/SDL_syspower.c	Wed Jun 03 13:11:28 2015 -0700
     2.3 @@ -36,8 +36,10 @@
     2.4  static const char *proc_apm_path = "/proc/apm";
     2.5  static const char *proc_acpi_battery_path = "/proc/acpi/battery";
     2.6  static const char *proc_acpi_ac_adapter_path = "/proc/acpi/ac_adapter";
     2.7 +static const char *sys_class_power_supply_path = "/sys/class/power_supply";
     2.8  
     2.9 -static int open_acpi_file(const char *base, const char *node, const char *key)
    2.10 +static int
    2.11 +open_power_file(const char *base, const char *node, const char *key)
    2.12  {
    2.13      const size_t pathlen = strlen(base) + strlen(node) + strlen(key) + 3;
    2.14      char *path = (char *) alloca(pathlen);
    2.15 @@ -51,11 +53,11 @@
    2.16  
    2.17  
    2.18  static SDL_bool
    2.19 -load_acpi_file(const char *base, const char *node, const char *key,
    2.20 -               char *buf, size_t buflen)
    2.21 +read_power_file(const char *base, const char *node, const char *key,
    2.22 +                char *buf, size_t buflen)
    2.23  {
    2.24      ssize_t br = 0;
    2.25 -    const int fd = open_acpi_file(base, node, key);
    2.26 +    const int fd = open_power_file(base, node, key);
    2.27      if (fd == -1) {
    2.28          return SDL_FALSE;
    2.29      }
    2.30 @@ -133,9 +135,9 @@
    2.31      int secs = -1;
    2.32      int pct = -1;
    2.33  
    2.34 -    if (!load_acpi_file(base, node, "state", state, sizeof (state))) {
    2.35 +    if (!read_power_file(base, node, "state", state, sizeof (state))) {
    2.36          return;
    2.37 -    } else if (!load_acpi_file(base, node, "info", info, sizeof (info))) {
    2.38 +    } else if (!read_power_file(base, node, "info", info, sizeof (info))) {
    2.39          return;
    2.40      }
    2.41  
    2.42 @@ -214,7 +216,7 @@
    2.43      char *key = NULL;
    2.44      char *val = NULL;
    2.45  
    2.46 -    if (!load_acpi_file(base, node, "state", state, sizeof (state))) {
    2.47 +    if (!read_power_file(base, node, "state", state, sizeof (state))) {
    2.48          return;
    2.49      }
    2.50  
    2.51 @@ -423,6 +425,94 @@
    2.52      return SDL_TRUE;
    2.53  }
    2.54  
    2.55 +/* !!! FIXME: implement d-bus queries to org.freedesktop.UPower. */
    2.56 +
    2.57 +SDL_bool
    2.58 +SDL_GetPowerInfo_Linux_sys_class_power_supply(SDL_PowerState *state, int *seconds, int *percent)
    2.59 +{
    2.60 +    const char *base = sys_class_power_supply_path;
    2.61 +    struct dirent *dent;
    2.62 +    DIR *dirp;
    2.63 +
    2.64 +    dirp = opendir(base);
    2.65 +    if (!dirp) {
    2.66 +        return SDL_FALSE;
    2.67 +    }
    2.68 +
    2.69 +    *state = SDL_POWERSTATE_NO_BATTERY;  /* assume we're just plugged in. */
    2.70 +    *seconds = -1;
    2.71 +    *percent = -1;
    2.72 +
    2.73 +    while ((dent = readdir(dirp)) != NULL) {
    2.74 +        const char *name = dent->d_name;
    2.75 +        SDL_bool choose = SDL_FALSE;
    2.76 +        char str[64];
    2.77 +        SDL_PowerState st;
    2.78 +        int secs;
    2.79 +        int pct;
    2.80 +
    2.81 +        if ((SDL_strcmp(name, ".") == 0) || (SDL_strcmp(name, "..") == 0)) {
    2.82 +            continue;  /* skip these, of course. */
    2.83 +        } else if (!read_power_file(base, name, "type", str, sizeof (str))) {
    2.84 +            continue;  /* Don't know _what_ we're looking at. Give up on it. */
    2.85 +        } else if (SDL_strcmp(str, "Battery\n") != 0) {
    2.86 +            continue;  /* we don't care about UPS and such. */
    2.87 +        }
    2.88 +
    2.89 +        /* some drivers don't offer this, so if it's not explicitly reported assume it's present. */
    2.90 +        if (read_power_file(base, name, "present", str, sizeof (str)) && (SDL_strcmp(str, "0\n") == 0)) {
    2.91 +            st = SDL_POWERSTATE_NO_BATTERY;
    2.92 +        } else if (!read_power_file(base, name, "status", str, sizeof (str))) {
    2.93 +            st = SDL_POWERSTATE_UNKNOWN;  /* uh oh */
    2.94 +        } else if (SDL_strcmp(str, "Charging\n") == 0) {
    2.95 +            st = SDL_POWERSTATE_CHARGING;
    2.96 +        } else if (SDL_strcmp(str, "Discharging\n") == 0) {
    2.97 +            st = SDL_POWERSTATE_ON_BATTERY;
    2.98 +        } else if ((SDL_strcmp(str, "Full\n") == 0) || (SDL_strcmp(str, "Not charging\n") == 0)) {
    2.99 +            st = SDL_POWERSTATE_CHARGED;
   2.100 +        } else {
   2.101 +            st = SDL_POWERSTATE_UNKNOWN;  /* uh oh */
   2.102 +        }
   2.103 +
   2.104 +        if (!read_power_file(base, name, "capacity", str, sizeof (str))) {
   2.105 +            pct = -1;
   2.106 +        } else {
   2.107 +            pct = SDL_atoi(str);
   2.108 +            pct = (pct > 100) ? 100 : pct; /* clamp between 0%, 100% */
   2.109 +        }
   2.110 +
   2.111 +        if (!read_power_file(base, name, "time_to_empty_now", str, sizeof (str))) {
   2.112 +            secs = -1;
   2.113 +        } else {
   2.114 +            secs = SDL_atoi(str);
   2.115 +            secs = (secs <= 0) ? -1 : secs;  /* 0 == unknown */
   2.116 +        }
   2.117 +
   2.118 +        /*
   2.119 +         * We pick the battery that claims to have the most minutes left.
   2.120 +         *  (failing a report of minutes, we'll take the highest percent.)
   2.121 +         */
   2.122 +        if ((secs < 0) && (*seconds < 0)) {
   2.123 +            if ((pct < 0) && (*percent < 0)) {
   2.124 +                choose = SDL_TRUE;  /* at least we know there's a battery. */
   2.125 +            } else if (pct > *percent) {
   2.126 +                choose = SDL_TRUE;
   2.127 +            }
   2.128 +        } else if (secs > *seconds) {
   2.129 +            choose = SDL_TRUE;
   2.130 +        }
   2.131 +
   2.132 +        if (choose) {
   2.133 +            *seconds = secs;
   2.134 +            *percent = pct;
   2.135 +            *state = st;
   2.136 +        }
   2.137 +    }
   2.138 +
   2.139 +    closedir(dirp);
   2.140 +    return SDL_TRUE;  /* don't look any further. */
   2.141 +}
   2.142 +
   2.143  #endif /* SDL_POWER_LINUX */
   2.144  #endif /* SDL_POWER_DISABLED */
   2.145