src/power/linux/SDL_syspower.c
author Ryan C. Gordon <icculus@icculus.org>
Mon, 29 Oct 2018 20:18:50 -0400
changeset 12370 86e097182d16
parent 11949 2db064e4a284
child 12503 806492103856
permissions -rw-r--r--
winmain: Patched to compile on C89 compilers.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2018 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 == ' ') && (*ptr != '\0')) {
   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 = *_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 
   455         if ((SDL_strcmp(name, ".") == 0) || (SDL_strcmp(name, "..") == 0)) {
   456             continue;  /* skip these, of course. */
   457         } else if (!read_power_file(base, name, "type", str, sizeof (str))) {
   458             continue;  /* Don't know _what_ we're looking at. Give up on it. */
   459         } else if (SDL_strcmp(str, "Battery\n") != 0) {
   460             continue;  /* we don't care about UPS and such. */
   461         }
   462 
   463         /* if the scope is "device," it might be something like a PS4
   464            controller reporting its own battery, and not something that powers
   465            the system. Most system batteries don't list a scope at all; we
   466            assume it's a system battery if not specified. */
   467         if (read_power_file(base, name, "scope", str, sizeof (str))) {
   468             if (SDL_strcmp(str, "device\n") == 0) {
   469                 continue;  /* skip external devices with their own batteries. */
   470             }
   471         }
   472 
   473         /* some drivers don't offer this, so if it's not explicitly reported assume it's present. */
   474         if (read_power_file(base, name, "present", str, sizeof (str)) && (SDL_strcmp(str, "0\n") == 0)) {
   475             st = SDL_POWERSTATE_NO_BATTERY;
   476         } else if (!read_power_file(base, name, "status", str, sizeof (str))) {
   477             st = SDL_POWERSTATE_UNKNOWN;  /* uh oh */
   478         } else if (SDL_strcmp(str, "Charging\n") == 0) {
   479             st = SDL_POWERSTATE_CHARGING;
   480         } else if (SDL_strcmp(str, "Discharging\n") == 0) {
   481             st = SDL_POWERSTATE_ON_BATTERY;
   482         } else if ((SDL_strcmp(str, "Full\n") == 0) || (SDL_strcmp(str, "Not charging\n") == 0)) {
   483             st = SDL_POWERSTATE_CHARGED;
   484         } else {
   485             st = SDL_POWERSTATE_UNKNOWN;  /* uh oh */
   486         }
   487 
   488         if (!read_power_file(base, name, "capacity", str, sizeof (str))) {
   489             pct = -1;
   490         } else {
   491             pct = SDL_atoi(str);
   492             pct = (pct > 100) ? 100 : pct; /* clamp between 0%, 100% */
   493         }
   494 
   495         if (!read_power_file(base, name, "time_to_empty_now", str, sizeof (str))) {
   496             secs = -1;
   497         } else {
   498             secs = SDL_atoi(str);
   499             secs = (secs <= 0) ? -1 : secs;  /* 0 == unknown */
   500         }
   501 
   502         /*
   503          * We pick the battery that claims to have the most minutes left.
   504          *  (failing a report of minutes, we'll take the highest percent.)
   505          */
   506         if ((secs < 0) && (*seconds < 0)) {
   507             if ((pct < 0) && (*percent < 0)) {
   508                 choose = SDL_TRUE;  /* at least we know there's a battery. */
   509             } else if (pct > *percent) {
   510                 choose = SDL_TRUE;
   511             }
   512         } else if (secs > *seconds) {
   513             choose = SDL_TRUE;
   514         }
   515 
   516         if (choose) {
   517             *seconds = secs;
   518             *percent = pct;
   519             *state = st;
   520         }
   521     }
   522 
   523     closedir(dirp);
   524     return SDL_TRUE;  /* don't look any further. */
   525 }
   526 
   527 
   528 /* d-bus queries to org.freedesktop.UPower. */
   529 #if SDL_USE_LIBDBUS
   530 #define UPOWER_DBUS_NODE "org.freedesktop.UPower"
   531 #define UPOWER_DBUS_PATH "/org/freedesktop/UPower"
   532 #define UPOWER_DBUS_INTERFACE "org.freedesktop.UPower"
   533 #define UPOWER_DEVICE_DBUS_INTERFACE "org.freedesktop.UPower.Device"
   534 
   535 static void
   536 check_upower_device(DBusConnection *conn, const char *path, SDL_PowerState *state, int *seconds, int *percent)
   537 {
   538     SDL_bool choose = SDL_FALSE;
   539     SDL_PowerState st;
   540     int secs;
   541     int pct;
   542     Uint32 ui32 = 0;
   543     Sint64 si64 = 0;
   544     double d = 0.0;
   545 
   546     if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "Type", DBUS_TYPE_UINT32, &ui32)) {
   547         return; /* Don't know _what_ we're looking at. Give up on it. */
   548     } else if (ui32 != 2) {  /* 2==Battery*/
   549         return;  /* we don't care about UPS and such. */
   550     } else if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "PowerSupply", DBUS_TYPE_BOOLEAN, &ui32)) {
   551         return;
   552     } else if (!ui32) {
   553         return;  /* we don't care about random devices with batteries, like wireless controllers, etc */
   554     } else if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "IsPresent", DBUS_TYPE_BOOLEAN, &ui32)) {
   555         return;
   556     } else if (!ui32) {
   557         st = SDL_POWERSTATE_NO_BATTERY;
   558     } else if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "State", DBUS_TYPE_UINT32, &ui32)) {
   559         st = SDL_POWERSTATE_UNKNOWN;  /* uh oh */
   560     } else if (ui32 == 1) {  /* 1 == charging */
   561         st = SDL_POWERSTATE_CHARGING;
   562     } else if ((ui32 == 2) || (ui32 == 3)) {  /* 2 == discharging, 3 == empty. */
   563         st = SDL_POWERSTATE_ON_BATTERY;
   564     } else if (ui32 == 4) {   /* 4 == full */
   565         st = SDL_POWERSTATE_CHARGED;
   566     } else {
   567         st = SDL_POWERSTATE_UNKNOWN;  /* uh oh */
   568     }
   569 
   570     if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "Percentage", DBUS_TYPE_DOUBLE, &d)) {
   571         pct = -1;  /* some old/cheap batteries don't set this property. */
   572     } else {
   573         pct = (int) d;
   574         pct = (pct > 100) ? 100 : pct; /* clamp between 0%, 100% */
   575     }
   576 
   577     if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "TimeToEmpty", DBUS_TYPE_INT64, &si64)) {
   578         secs = -1;
   579     } else {
   580         secs = (int) si64;
   581         secs = (secs <= 0) ? -1 : secs;  /* 0 == unknown */
   582     }
   583 
   584     /*
   585      * We pick the battery that claims to have the most minutes left.
   586      *  (failing a report of minutes, we'll take the highest percent.)
   587      */
   588     if ((secs < 0) && (*seconds < 0)) {
   589         if ((pct < 0) && (*percent < 0)) {
   590             choose = SDL_TRUE;  /* at least we know there's a battery. */
   591         } else if (pct > *percent) {
   592             choose = SDL_TRUE;
   593         }
   594     } else if (secs > *seconds) {
   595         choose = SDL_TRUE;
   596     }
   597 
   598     if (choose) {
   599         *seconds = secs;
   600         *percent = pct;
   601         *state = st;
   602     }
   603 }
   604 #endif
   605 
   606 SDL_bool
   607 SDL_GetPowerInfo_Linux_org_freedesktop_upower(SDL_PowerState *state, int *seconds, int *percent)
   608 {
   609     SDL_bool retval = SDL_FALSE;
   610 
   611 #if SDL_USE_LIBDBUS
   612     SDL_DBusContext *dbus = SDL_DBus_GetContext();
   613     char **paths = NULL;
   614     int i, numpaths = 0;
   615 
   616     if (!dbus || !SDL_DBus_CallMethodOnConnection(dbus->system_conn, UPOWER_DBUS_NODE, UPOWER_DBUS_PATH, UPOWER_DBUS_INTERFACE, "EnumerateDevices",
   617             DBUS_TYPE_INVALID,
   618             DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &paths, &numpaths, DBUS_TYPE_INVALID)) {
   619         return SDL_FALSE;  /* try a different approach than UPower. */
   620     }
   621 
   622     retval = SDL_TRUE;  /* Clearly we can use this interface. */
   623     *state = SDL_POWERSTATE_NO_BATTERY;  /* assume we're just plugged in. */
   624     *seconds = -1;
   625     *percent = -1;
   626 
   627     for (i = 0; i < numpaths; i++) {
   628         check_upower_device(dbus->system_conn, paths[i], state, seconds, percent);
   629     }
   630 
   631     if (dbus) {
   632         dbus->free_string_array(paths);
   633     }
   634 #endif  /* SDL_USE_LIBDBUS */
   635 
   636     return retval;
   637 }
   638 
   639 #endif /* SDL_POWER_LINUX */
   640 #endif /* SDL_POWER_DISABLED */
   641 
   642 /* vi: set ts=4 sw=4 expandtab: */