sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_syspower.c (19719B)


      1 /*
      2   Simple DirectMedia Layer
      3   Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
      4 
      5   This software is provided 'as-is', without any express or implied
      6   warranty.  In no event will the authors be held liable for any damages
      7   arising from the use of this software.
      8 
      9   Permission is granted to anyone to use this software for any purpose,
     10   including commercial applications, and to alter it and redistribute it
     11   freely, subject to the following restrictions:
     12 
     13   1. The origin of this software must not be misrepresented; you must not
     14      claim that you wrote the original software. If you use this software
     15      in a product, an acknowledgment in the product documentation would be
     16      appreciated but is not required.
     17   2. Altered source versions must be plainly marked as such, and must not be
     18      misrepresented as being the original software.
     19   3. This notice may not be removed or altered from any source distribution.
     20 */
     21 #include "../../SDL_internal.h"
     22 
     23 #ifndef SDL_POWER_DISABLED
     24 #if SDL_POWER_LINUX
     25 
     26 #include <stdio.h>
     27 #include <unistd.h>
     28 
     29 #include <sys/types.h>
     30 #include <sys/stat.h>
     31 #include <dirent.h>
     32 #include <fcntl.h>
     33 
     34 #include "SDL_power.h"
     35 #include "../SDL_syspower.h"
     36 
     37 #include "../../core/linux/SDL_dbus.h"
     38 
     39 static const char *proc_apm_path = "/proc/apm";
     40 static const char *proc_acpi_battery_path = "/proc/acpi/battery";
     41 static const char *proc_acpi_ac_adapter_path = "/proc/acpi/ac_adapter";
     42 static const char *sys_class_power_supply_path = "/sys/class/power_supply";
     43 
     44 static int
     45 open_power_file(const char *base, const char *node, const char *key)
     46 {
     47     const size_t pathlen = strlen(base) + strlen(node) + strlen(key) + 3;
     48     char *path = (char *) alloca(pathlen);
     49     if (path == NULL) {
     50         return -1;  /* oh well. */
     51     }
     52 
     53     snprintf(path, pathlen, "%s/%s/%s", base, node, key);
     54     return open(path, O_RDONLY);
     55 }
     56 
     57 
     58 static SDL_bool
     59 read_power_file(const char *base, const char *node, const char *key,
     60                 char *buf, size_t buflen)
     61 {
     62     ssize_t br = 0;
     63     const int fd = open_power_file(base, node, key);
     64     if (fd == -1) {
     65         return SDL_FALSE;
     66     }
     67     br = read(fd, buf, buflen-1);
     68     close(fd);
     69     if (br < 0) {
     70         return SDL_FALSE;
     71     }
     72     buf[br] = '\0';             /* null-terminate the string. */
     73     return SDL_TRUE;
     74 }
     75 
     76 
     77 static SDL_bool
     78 make_proc_acpi_key_val(char **_ptr, char **_key, char **_val)
     79 {
     80     char *ptr = *_ptr;
     81 
     82     while (*ptr == ' ') {
     83         ptr++;  /* skip whitespace. */
     84     }
     85 
     86     if (*ptr == '\0') {
     87         return SDL_FALSE;  /* EOF. */
     88     }
     89 
     90     *_key = ptr;
     91 
     92     while ((*ptr != ':') && (*ptr != '\0')) {
     93         ptr++;
     94     }
     95 
     96     if (*ptr == '\0') {
     97         return SDL_FALSE;  /* (unexpected) EOF. */
     98     }
     99 
    100     *(ptr++) = '\0';  /* terminate the key. */
    101 
    102     while (*ptr == ' ') {
    103         ptr++;  /* skip whitespace. */
    104     }
    105 
    106     if (*ptr == '\0') {
    107         return SDL_FALSE;  /* (unexpected) EOF. */
    108     }
    109 
    110     *_val = ptr;
    111 
    112     while ((*ptr != '\n') && (*ptr != '\0')) {
    113         ptr++;
    114     }
    115 
    116     if (*ptr != '\0') {
    117         *(ptr++) = '\0';  /* terminate the value. */
    118     }
    119 
    120     *_ptr = ptr;  /* store for next time. */
    121     return SDL_TRUE;
    122 }
    123 
    124 static void
    125 check_proc_acpi_battery(const char * node, SDL_bool * have_battery,
    126                         SDL_bool * charging, int *seconds, int *percent)
    127 {
    128     const char *base = proc_acpi_battery_path;
    129     char info[1024];
    130     char state[1024];
    131     char *ptr = NULL;
    132     char *key = NULL;
    133     char *val = NULL;
    134     SDL_bool charge = SDL_FALSE;
    135     SDL_bool choose = SDL_FALSE;
    136     int maximum = -1;
    137     int remaining = -1;
    138     int secs = -1;
    139     int pct = -1;
    140 
    141     if (!read_power_file(base, node, "state", state, sizeof (state))) {
    142         return;
    143     } else if (!read_power_file(base, node, "info", info, sizeof (info))) {
    144         return;
    145     }
    146 
    147     ptr = &state[0];
    148     while (make_proc_acpi_key_val(&ptr, &key, &val)) {
    149         if (strcmp(key, "present") == 0) {
    150             if (strcmp(val, "yes") == 0) {
    151                 *have_battery = SDL_TRUE;
    152             }
    153         } else if (strcmp(key, "charging state") == 0) {
    154             /* !!! FIXME: what exactly _does_ charging/discharging mean? */
    155             if (strcmp(val, "charging/discharging") == 0) {
    156                 charge = SDL_TRUE;
    157             } else if (strcmp(val, "charging") == 0) {
    158                 charge = SDL_TRUE;
    159             }
    160         } else if (strcmp(key, "remaining capacity") == 0) {
    161             char *endptr = NULL;
    162             const int cvt = (int) strtol(val, &endptr, 10);
    163             if (*endptr == ' ') {
    164                 remaining = cvt;
    165             }
    166         }
    167     }
    168 
    169     ptr = &info[0];
    170     while (make_proc_acpi_key_val(&ptr, &key, &val)) {
    171         if (strcmp(key, "design capacity") == 0) {
    172             char *endptr = NULL;
    173             const int cvt = (int) strtol(val, &endptr, 10);
    174             if (*endptr == ' ') {
    175                 maximum = cvt;
    176             }
    177         }
    178     }
    179 
    180     if ((maximum >= 0) && (remaining >= 0)) {
    181         pct = (int) ((((float) remaining) / ((float) maximum)) * 100.0f);
    182         if (pct < 0) {
    183             pct = 0;
    184         } else if (pct > 100) {
    185             pct = 100;
    186         }
    187     }
    188 
    189     /* !!! FIXME: calculate (secs). */
    190 
    191     /*
    192      * We pick the battery that claims to have the most minutes left.
    193      *  (failing a report of minutes, we'll take the highest percent.)
    194      */
    195     if ((secs < 0) && (*seconds < 0)) {
    196         if ((pct < 0) && (*percent < 0)) {
    197             choose = SDL_TRUE;  /* at least we know there's a battery. */
    198         }
    199         if (pct > *percent) {
    200             choose = SDL_TRUE;
    201         }
    202     } else if (secs > *seconds) {
    203         choose = SDL_TRUE;
    204     }
    205 
    206     if (choose) {
    207         *seconds = secs;
    208         *percent = pct;
    209         *charging = charge;
    210     }
    211 }
    212 
    213 static void
    214 check_proc_acpi_ac_adapter(const char * node, SDL_bool * have_ac)
    215 {
    216     const char *base = proc_acpi_ac_adapter_path;
    217     char state[256];
    218     char *ptr = NULL;
    219     char *key = NULL;
    220     char *val = NULL;
    221 
    222     if (!read_power_file(base, node, "state", state, sizeof (state))) {
    223         return;
    224     }
    225 
    226     ptr = &state[0];
    227     while (make_proc_acpi_key_val(&ptr, &key, &val)) {
    228         if (strcmp(key, "state") == 0) {
    229             if (strcmp(val, "on-line") == 0) {
    230                 *have_ac = SDL_TRUE;
    231             }
    232         }
    233     }
    234 }
    235 
    236 
    237 SDL_bool
    238 SDL_GetPowerInfo_Linux_proc_acpi(SDL_PowerState * state,
    239                                  int *seconds, int *percent)
    240 {
    241     struct dirent *dent = NULL;
    242     DIR *dirp = NULL;
    243     SDL_bool have_battery = SDL_FALSE;
    244     SDL_bool have_ac = SDL_FALSE;
    245     SDL_bool charging = SDL_FALSE;
    246 
    247     *seconds = -1;
    248     *percent = -1;
    249     *state = SDL_POWERSTATE_UNKNOWN;
    250 
    251     dirp = opendir(proc_acpi_battery_path);
    252     if (dirp == NULL) {
    253         return SDL_FALSE;  /* can't use this interface. */
    254     } else {
    255         while ((dent = readdir(dirp)) != NULL) {
    256             const char *node = dent->d_name;
    257             check_proc_acpi_battery(node, &have_battery, &charging,
    258                                     seconds, percent);
    259         }
    260         closedir(dirp);
    261     }
    262 
    263     dirp = opendir(proc_acpi_ac_adapter_path);
    264     if (dirp == NULL) {
    265         return SDL_FALSE;  /* can't use this interface. */
    266     } else {
    267         while ((dent = readdir(dirp)) != NULL) {
    268             const char *node = dent->d_name;
    269             check_proc_acpi_ac_adapter(node, &have_ac);
    270         }
    271         closedir(dirp);
    272     }
    273 
    274     if (!have_battery) {
    275         *state = SDL_POWERSTATE_NO_BATTERY;
    276     } else if (charging) {
    277         *state = SDL_POWERSTATE_CHARGING;
    278     } else if (have_ac) {
    279         *state = SDL_POWERSTATE_CHARGED;
    280     } else {
    281         *state = SDL_POWERSTATE_ON_BATTERY;
    282     }
    283 
    284     return SDL_TRUE;   /* definitive answer. */
    285 }
    286 
    287 
    288 static SDL_bool
    289 next_string(char **_ptr, char **_str)
    290 {
    291     char *ptr = *_ptr;
    292     char *str;
    293 
    294     while (*ptr == ' ') {       /* skip any spaces... */
    295         ptr++;
    296     }
    297 
    298     if (*ptr == '\0') {
    299         return SDL_FALSE;
    300     }
    301 
    302     str = ptr;
    303     while ((*ptr != ' ') && (*ptr != '\n') && (*ptr != '\0'))
    304         ptr++;
    305 
    306     if (*ptr != '\0')
    307         *(ptr++) = '\0';
    308 
    309     *_str = str;
    310     *_ptr = ptr;
    311     return SDL_TRUE;
    312 }
    313 
    314 static SDL_bool
    315 int_string(char *str, int *val)
    316 {
    317     char *endptr = NULL;
    318     *val = (int) strtol(str, &endptr, 0);
    319     return ((*str != '\0') && (*endptr == '\0'));
    320 }
    321 
    322 /* http://lxr.linux.no/linux+v2.6.29/drivers/char/apm-emulation.c */
    323 SDL_bool
    324 SDL_GetPowerInfo_Linux_proc_apm(SDL_PowerState * state,
    325                                 int *seconds, int *percent)
    326 {
    327     SDL_bool need_details = SDL_FALSE;
    328     int ac_status = 0;
    329     int battery_status = 0;
    330     int battery_flag = 0;
    331     int battery_percent = 0;
    332     int battery_time = 0;
    333     const int fd = open(proc_apm_path, O_RDONLY);
    334     char buf[128];
    335     char *ptr = &buf[0];
    336     char *str = NULL;
    337     ssize_t br;
    338 
    339     if (fd == -1) {
    340         return SDL_FALSE;       /* can't use this interface. */
    341     }
    342 
    343     br = read(fd, buf, sizeof (buf) - 1);
    344     close(fd);
    345 
    346     if (br < 0) {
    347         return SDL_FALSE;
    348     }
    349 
    350     buf[br] = '\0';             /* null-terminate the string. */
    351     if (!next_string(&ptr, &str)) {     /* driver version */
    352         return SDL_FALSE;
    353     }
    354     if (!next_string(&ptr, &str)) {     /* BIOS version */
    355         return SDL_FALSE;
    356     }
    357     if (!next_string(&ptr, &str)) {     /* APM flags */
    358         return SDL_FALSE;
    359     }
    360 
    361     if (!next_string(&ptr, &str)) {     /* AC line status */
    362         return SDL_FALSE;
    363     } else if (!int_string(str, &ac_status)) {
    364         return SDL_FALSE;
    365     }
    366 
    367     if (!next_string(&ptr, &str)) {     /* battery status */
    368         return SDL_FALSE;
    369     } else if (!int_string(str, &battery_status)) {
    370         return SDL_FALSE;
    371     }
    372     if (!next_string(&ptr, &str)) {     /* battery flag */
    373         return SDL_FALSE;
    374     } else if (!int_string(str, &battery_flag)) {
    375         return SDL_FALSE;
    376     }
    377     if (!next_string(&ptr, &str)) {     /* remaining battery life percent */
    378         return SDL_FALSE;
    379     }
    380     if (str[strlen(str) - 1] == '%') {
    381         str[strlen(str) - 1] = '\0';
    382     }
    383     if (!int_string(str, &battery_percent)) {
    384         return SDL_FALSE;
    385     }
    386 
    387     if (!next_string(&ptr, &str)) {     /* remaining battery life time */
    388         return SDL_FALSE;
    389     } else if (!int_string(str, &battery_time)) {
    390         return SDL_FALSE;
    391     }
    392 
    393     if (!next_string(&ptr, &str)) {     /* remaining battery life time units */
    394         return SDL_FALSE;
    395     } else if (strcmp(str, "min") == 0) {
    396         battery_time *= 60;
    397     }
    398 
    399     if (battery_flag == 0xFF) { /* unknown state */
    400         *state = SDL_POWERSTATE_UNKNOWN;
    401     } else if (battery_flag & (1 << 7)) {       /* no battery */
    402         *state = SDL_POWERSTATE_NO_BATTERY;
    403     } else if (battery_flag & (1 << 3)) {       /* charging */
    404         *state = SDL_POWERSTATE_CHARGING;
    405         need_details = SDL_TRUE;
    406     } else if (ac_status == 1) {
    407         *state = SDL_POWERSTATE_CHARGED;        /* on AC, not charging. */
    408         need_details = SDL_TRUE;
    409     } else {
    410         *state = SDL_POWERSTATE_ON_BATTERY;
    411         need_details = SDL_TRUE;
    412     }
    413 
    414     *percent = -1;
    415     *seconds = -1;
    416     if (need_details) {
    417         const int pct = battery_percent;
    418         const int secs = battery_time;
    419 
    420         if (pct >= 0) {         /* -1 == unknown */
    421             *percent = (pct > 100) ? 100 : pct; /* clamp between 0%, 100% */
    422         }
    423         if (secs >= 0) {        /* -1 == unknown */
    424             *seconds = secs;
    425         }
    426     }
    427 
    428     return SDL_TRUE;
    429 }
    430 
    431 SDL_bool
    432 SDL_GetPowerInfo_Linux_sys_class_power_supply(SDL_PowerState *state, int *seconds, int *percent)
    433 {
    434     const char *base = sys_class_power_supply_path;
    435     struct dirent *dent;
    436     DIR *dirp;
    437 
    438     dirp = opendir(base);
    439     if (!dirp) {
    440         return SDL_FALSE;
    441     }
    442 
    443     *state = SDL_POWERSTATE_NO_BATTERY;  /* assume we're just plugged in. */
    444     *seconds = -1;
    445     *percent = -1;
    446 
    447     while ((dent = readdir(dirp)) != NULL) {
    448         const char *name = dent->d_name;
    449         SDL_bool choose = SDL_FALSE;
    450         char str[64];
    451         SDL_PowerState st;
    452         int secs;
    453         int pct;
    454         int energy;
    455         int power;
    456 
    457         if ((SDL_strcmp(name, ".") == 0) || (SDL_strcmp(name, "..") == 0)) {
    458             continue;  /* skip these, of course. */
    459         } else if (!read_power_file(base, name, "type", str, sizeof (str))) {
    460             continue;  /* Don't know _what_ we're looking at. Give up on it. */
    461         } else if (SDL_strcmp(str, "Battery\n") != 0) {
    462             continue;  /* we don't care about UPS and such. */
    463         }
    464 
    465         /* if the scope is "device," it might be something like a PS4
    466            controller reporting its own battery, and not something that powers
    467            the system. Most system batteries don't list a scope at all; we
    468            assume it's a system battery if not specified. */
    469         if (read_power_file(base, name, "scope", str, sizeof (str))) {
    470             if (SDL_strcmp(str, "device\n") == 0) {
    471                 continue;  /* skip external devices with their own batteries. */
    472             }
    473         }
    474 
    475         /* some drivers don't offer this, so if it's not explicitly reported assume it's present. */
    476         if (read_power_file(base, name, "present", str, sizeof (str)) && (SDL_strcmp(str, "0\n") == 0)) {
    477             st = SDL_POWERSTATE_NO_BATTERY;
    478         } else if (!read_power_file(base, name, "status", str, sizeof (str))) {
    479             st = SDL_POWERSTATE_UNKNOWN;  /* uh oh */
    480         } else if (SDL_strcmp(str, "Charging\n") == 0) {
    481             st = SDL_POWERSTATE_CHARGING;
    482         } else if (SDL_strcmp(str, "Discharging\n") == 0) {
    483             st = SDL_POWERSTATE_ON_BATTERY;
    484         } else if ((SDL_strcmp(str, "Full\n") == 0) || (SDL_strcmp(str, "Not charging\n") == 0)) {
    485             st = SDL_POWERSTATE_CHARGED;
    486         } else {
    487             st = SDL_POWERSTATE_UNKNOWN;  /* uh oh */
    488         }
    489 
    490         if (!read_power_file(base, name, "capacity", str, sizeof (str))) {
    491             pct = -1;
    492         } else {
    493             pct = SDL_atoi(str);
    494             pct = (pct > 100) ? 100 : pct; /* clamp between 0%, 100% */
    495         }
    496 
    497         if (read_power_file(base, name, "time_to_empty_now", str, sizeof (str))) {
    498             secs = SDL_atoi(str);
    499             secs = (secs <= 0) ? -1 : secs;  /* 0 == unknown */
    500         } else if (st == SDL_POWERSTATE_ON_BATTERY) {
    501             /* energy is Watt*hours and power is Watts */
    502             energy = (read_power_file(base, name, "energy_now", str, sizeof (str))) ? SDL_atoi(str) : -1;
    503             power = (read_power_file(base, name, "power_now", str, sizeof (str))) ? SDL_atoi(str) : -1;
    504             secs = (energy >= 0 && power > 0) ? (3600LL * energy) / power : -1;
    505         } else {
    506             secs = -1;
    507         }
    508 
    509         /*
    510          * We pick the battery that claims to have the most minutes left.
    511          *  (failing a report of minutes, we'll take the highest percent.)
    512          */
    513         if ((secs < 0) && (*seconds < 0)) {
    514             if ((pct < 0) && (*percent < 0)) {
    515                 choose = SDL_TRUE;  /* at least we know there's a battery. */
    516             } else if (pct > *percent) {
    517                 choose = SDL_TRUE;
    518             }
    519         } else if (secs > *seconds) {
    520             choose = SDL_TRUE;
    521         }
    522 
    523         if (choose) {
    524             *seconds = secs;
    525             *percent = pct;
    526             *state = st;
    527         }
    528     }
    529 
    530     closedir(dirp);
    531     return SDL_TRUE;  /* don't look any further. */
    532 }
    533 
    534 
    535 /* d-bus queries to org.freedesktop.UPower. */
    536 #if SDL_USE_LIBDBUS
    537 #define UPOWER_DBUS_NODE "org.freedesktop.UPower"
    538 #define UPOWER_DBUS_PATH "/org/freedesktop/UPower"
    539 #define UPOWER_DBUS_INTERFACE "org.freedesktop.UPower"
    540 #define UPOWER_DEVICE_DBUS_INTERFACE "org.freedesktop.UPower.Device"
    541 
    542 static void
    543 check_upower_device(DBusConnection *conn, const char *path, SDL_PowerState *state, int *seconds, int *percent)
    544 {
    545     SDL_bool choose = SDL_FALSE;
    546     SDL_PowerState st;
    547     int secs;
    548     int pct;
    549     Uint32 ui32 = 0;
    550     Sint64 si64 = 0;
    551     double d = 0.0;
    552 
    553     if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "Type", DBUS_TYPE_UINT32, &ui32)) {
    554         return; /* Don't know _what_ we're looking at. Give up on it. */
    555     } else if (ui32 != 2) {  /* 2==Battery*/
    556         return;  /* we don't care about UPS and such. */
    557     } else if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "PowerSupply", DBUS_TYPE_BOOLEAN, &ui32)) {
    558         return;
    559     } else if (!ui32) {
    560         return;  /* we don't care about random devices with batteries, like wireless controllers, etc */
    561     } else if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "IsPresent", DBUS_TYPE_BOOLEAN, &ui32)) {
    562         return;
    563     } else if (!ui32) {
    564         st = SDL_POWERSTATE_NO_BATTERY;
    565     } else if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "State", DBUS_TYPE_UINT32, &ui32)) {
    566         st = SDL_POWERSTATE_UNKNOWN;  /* uh oh */
    567     } else if (ui32 == 1) {  /* 1 == charging */
    568         st = SDL_POWERSTATE_CHARGING;
    569     } else if ((ui32 == 2) || (ui32 == 3)) {  /* 2 == discharging, 3 == empty. */
    570         st = SDL_POWERSTATE_ON_BATTERY;
    571     } else if (ui32 == 4) {   /* 4 == full */
    572         st = SDL_POWERSTATE_CHARGED;
    573     } else {
    574         st = SDL_POWERSTATE_UNKNOWN;  /* uh oh */
    575     }
    576 
    577     if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "Percentage", DBUS_TYPE_DOUBLE, &d)) {
    578         pct = -1;  /* some old/cheap batteries don't set this property. */
    579     } else {
    580         pct = (int) d;
    581         pct = (pct > 100) ? 100 : pct; /* clamp between 0%, 100% */
    582     }
    583 
    584     if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "TimeToEmpty", DBUS_TYPE_INT64, &si64)) {
    585         secs = -1;
    586     } else {
    587         secs = (int) si64;
    588         secs = (secs <= 0) ? -1 : secs;  /* 0 == unknown */
    589     }
    590 
    591     /*
    592      * We pick the battery that claims to have the most minutes left.
    593      *  (failing a report of minutes, we'll take the highest percent.)
    594      */
    595     if ((secs < 0) && (*seconds < 0)) {
    596         if ((pct < 0) && (*percent < 0)) {
    597             choose = SDL_TRUE;  /* at least we know there's a battery. */
    598         } else if (pct > *percent) {
    599             choose = SDL_TRUE;
    600         }
    601     } else if (secs > *seconds) {
    602         choose = SDL_TRUE;
    603     }
    604 
    605     if (choose) {
    606         *seconds = secs;
    607         *percent = pct;
    608         *state = st;
    609     }
    610 }
    611 #endif
    612 
    613 SDL_bool
    614 SDL_GetPowerInfo_Linux_org_freedesktop_upower(SDL_PowerState *state, int *seconds, int *percent)
    615 {
    616     SDL_bool retval = SDL_FALSE;
    617 
    618 #if SDL_USE_LIBDBUS
    619     SDL_DBusContext *dbus = SDL_DBus_GetContext();
    620     char **paths = NULL;
    621     int i, numpaths = 0;
    622 
    623     if (!dbus || !SDL_DBus_CallMethodOnConnection(dbus->system_conn, UPOWER_DBUS_NODE, UPOWER_DBUS_PATH, UPOWER_DBUS_INTERFACE, "EnumerateDevices",
    624             DBUS_TYPE_INVALID,
    625             DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &paths, &numpaths, DBUS_TYPE_INVALID)) {
    626         return SDL_FALSE;  /* try a different approach than UPower. */
    627     }
    628 
    629     retval = SDL_TRUE;  /* Clearly we can use this interface. */
    630     *state = SDL_POWERSTATE_NO_BATTERY;  /* assume we're just plugged in. */
    631     *seconds = -1;
    632     *percent = -1;
    633 
    634     for (i = 0; i < numpaths; i++) {
    635         check_upower_device(dbus->system_conn, paths[i], state, seconds, percent);
    636     }
    637 
    638     if (dbus) {
    639         dbus->free_string_array(paths);
    640     }
    641 #endif  /* SDL_USE_LIBDBUS */
    642 
    643     return retval;
    644 }
    645 
    646 #endif /* SDL_POWER_LINUX */
    647 #endif /* SDL_POWER_DISABLED */
    648 
    649 /* vi: set ts=4 sw=4 expandtab: */