src/power/linux/SDL_syspower.c
author Ryan C. Gordon <icculus@icculus.org>
Tue, 30 Jun 2009 05:27:11 +0000
changeset 3204 f77f50add24f
parent 3203 790cbbda6429
child 3205 f9a4aa1e1b34
permissions -rw-r--r--
Removed stub for Linux /sys/power processing.

Apparently /sys/power doesn't expose battery information at this time.
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2009 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Lesser General Public
     7     License as published by the Free Software Foundation; either
     8     version 2.1 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Lesser General Public License for more details.
    14 
    15     You should have received a copy of the GNU Lesser General Public
    16     License along with this library; if not, write to the Free Software
    17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 #include "SDL_config.h"
    23 
    24 #ifndef SDL_POWER_DISABLED
    25 #ifdef SDL_POWER_LINUX
    26 
    27 #include <stdio.h>
    28 #include <unistd.h>
    29 
    30 #include <sys/types.h>
    31 #include <sys/stat.h>
    32 #include <dirent.h>
    33 #include <fcntl.h>
    34 
    35 #include "SDL_power.h"
    36 
    37 static const char *proc_acpi_path = "/proc/acpi/battery";
    38 
    39 static int open_acpi_file(const char *node, const char *key)
    40 {
    41     const size_t pathlen = strlen(proc_acpi_path)+strlen(node)+strlen(key)+3;
    42     char *path = (char *) alloca(pathlen);
    43     if (path == NULL) {
    44         return -1;  /* oh well. */
    45     }
    46 
    47     snprintf(path, pathlen, "%s/%s/%s", proc_acpi_path, node, key);
    48     return open(path, O_RDONLY);
    49 }
    50 
    51 
    52 static SDL_bool
    53 load_acpi_file(const char *node, const char *key, char *buf, size_t buflen)
    54 {
    55     ssize_t br = 0;
    56     const int fd = open_acpi_file(node, key);
    57     if (fd == -1) {
    58         return SDL_FALSE;
    59     }
    60     br = read(fd, buf, buflen-1);
    61     close(fd);
    62     if (br < 0) {
    63         return SDL_FALSE;
    64     }
    65     buf[br] = '\0';             // null-terminate the string.
    66     return SDL_TRUE;
    67 }
    68 
    69 static SDL_bool
    70 make_acpi_key_val(char **_ptr, char **_key, char **_val)
    71 {
    72     char *ptr = *_ptr;
    73 
    74     while (*ptr == ' ') {
    75         ptr++;  /* skip whitespace. */
    76     }
    77 
    78     if (*ptr == '\0') {
    79         return SDL_FALSE;  /* EOF. */
    80     }
    81 
    82     *_key = ptr;
    83 
    84     while ((*ptr != ':') && (*ptr != '\0')) {
    85         ptr++;
    86     }
    87 
    88     if (*ptr == '\0') {
    89         return SDL_FALSE;  /* (unexpected) EOF. */
    90     }
    91 
    92     *(ptr++) = '\0';  /* terminate the key. */
    93 
    94     while ((*ptr == ' ') && (*ptr != '\0')) {
    95         ptr++;  /* skip whitespace. */
    96     }
    97 
    98     if (*ptr == '\0') {
    99         return SDL_FALSE;  /* (unexpected) EOF. */
   100     }
   101 
   102     *_val = ptr;
   103 
   104     while ((*ptr != '\n') && (*ptr != '\0')) {
   105         ptr++;
   106     }
   107 
   108     if (*ptr != '\0') {
   109         *(ptr++) = '\0';  /* terminate the value. */
   110     }
   111 
   112     *_ptr = ptr;  /* store for next time. */
   113     return SDL_TRUE;
   114 }
   115 
   116 static void
   117 check_acpi(const char * fname, SDL_bool * have_ac, SDL_bool * have_battery,
   118            SDL_bool * charging, int *seconds, int *percent)
   119 {
   120     int fd = -1;
   121     char info[1024];
   122     char state[1024];
   123     ssize_t br = 0;
   124     char *ptr = NULL;
   125     char *key = NULL;
   126     char *val = NULL;
   127     SDL_bool charge = SDL_FALSE;
   128     SDL_bool choose = SDL_FALSE;
   129     SDL_bool is_ac = SDL_FALSE;
   130     int maximum = -1;
   131     int remaining = -1;
   132     int secs = -1;
   133     int pct = -1;
   134 
   135     if (!load_acpi_file(fname, "state", state, sizeof (state))) {
   136         return;
   137     } else if (!load_acpi_file(fname, "info", info, sizeof (info))) {
   138         return;
   139     }
   140 
   141     ptr = &state[0];
   142     while (make_acpi_key_val(&ptr, &key, &val)) {
   143         if (strcmp(key, "present") == 0) {
   144             if (strcmp(val, "yes") == 0) {
   145                 *have_battery = SDL_TRUE;
   146             }
   147         } else if (strcmp(key, "charging state") == 0) {
   148             /* !!! FIXME: what exactly _does_ charging/discharging mean? */
   149             if (strcmp(val, "charging/discharging") == 0) {
   150                 *have_ac = is_ac = SDL_TRUE;
   151                 charge = SDL_TRUE;
   152             } else if (strcmp(val, "charging") == 0) {
   153                 *have_ac = is_ac = SDL_TRUE;
   154                 charge = SDL_TRUE;
   155             } else if (strcmp(val, "charged") == 0) {
   156                 /* !!! FIXME: maybe another battery is discharging,
   157                    !!! FIXME:   instead of AC connection. */
   158                 *have_ac = is_ac = SDL_TRUE;
   159                 charge = SDL_TRUE;
   160             }
   161         } else if (strcmp(key, "remaining capacity") == 0) {
   162             char *endptr = NULL;
   163             const int cvt = (int) strtol(val, &endptr, 10);
   164             if (*endptr == ' ') {
   165                 remaining = cvt;
   166             }
   167         }
   168     }
   169     
   170     ptr = &info[0];
   171     while (make_acpi_key_val(&ptr, &key, &val)) {
   172         if (strcmp(key, "design capacity") == 0) {
   173             char *endptr = NULL;
   174             const int cvt = (int) strtol(val, &endptr, 10);
   175             if (*endptr == ' ') {
   176                 maximum = cvt;
   177             }
   178         }
   179     }
   180 
   181     if ((maximum >= 0) && (remaining >= 0)) {
   182         pct = (int) ((((float) remaining) / ((float) maximum)) * 100.0f);
   183         if (pct < 0) {
   184             pct = 0;
   185         } else if (pct > 100) {
   186             pct = 100;
   187         }
   188     }
   189 
   190     /* !!! FIXME: calculate (secs). */
   191 
   192     /*
   193      * We pick the battery that claims to have the most minutes left.
   194      *  (failing a report of minutes, we'll take the highest percent.)
   195      */
   196     if ((secs < 0) && (*seconds < 0)) {
   197         if ((pct < 0) && (*percent < 0)) {
   198             choose = SDL_TRUE;  /* at least we know there's a battery. */
   199         }
   200         if (pct > *percent) {
   201             choose = SDL_TRUE;
   202         }
   203     } else if (secs > *seconds) {
   204         choose = SDL_TRUE;
   205     }
   206 
   207     if (choose) {
   208         *seconds = secs;
   209         *percent = pct;
   210         *charging = charge;
   211     }
   212 
   213 }
   214 
   215 SDL_bool
   216 SDL_GetPowerInfo_Linux_proc_acpi(SDL_PowerState * state,
   217                                  int *seconds, int *percent)
   218 {
   219     struct dirent *dent = NULL;
   220     DIR *dirp = NULL;
   221     SDL_bool have_ac = SDL_FALSE;
   222     SDL_bool have_battery = SDL_FALSE;
   223     SDL_bool charging = SDL_FALSE;
   224 
   225     *seconds = -1;
   226     *percent = -1;
   227     *state = SDL_POWERSTATE_UNKNOWN;
   228 
   229     dirp = opendir(proc_acpi_path);
   230     if (dirp == NULL) {
   231         return SDL_FALSE;  /* can't use this interface. */
   232     }
   233 
   234     while ((dent = readdir(dirp)) != NULL) {
   235         const char *name = dent->d_name;
   236         check_acpi(name, &have_ac, &have_battery, &charging, seconds, percent);
   237     }
   238 
   239     if (!have_battery) {
   240         *state = SDL_POWERSTATE_NO_BATTERY;
   241     } else if (charging) {
   242         *state = SDL_POWERSTATE_CHARGING;
   243     } else if (have_ac) {
   244         *state = SDL_POWERSTATE_CHARGED;
   245     } else {
   246         *state = SDL_POWERSTATE_ON_BATTERY;
   247     }
   248 
   249     return SDL_TRUE;   /* definitive answer. */
   250 }
   251 
   252 
   253 static SDL_bool
   254 next_string(char **_ptr, char **_str)
   255 {
   256     char *ptr = *_ptr;
   257     char *str = *_str;
   258 
   259     while (*ptr == ' ') {       /* skip any spaces... */
   260         ptr++;
   261     }
   262 
   263     if (*ptr == '\0') {
   264         return SDL_FALSE;
   265     }
   266 
   267     str = ptr;
   268     while ((*ptr != ' ') && (*ptr != '\n') && (*ptr != '\0'))
   269         ptr++;
   270 
   271     if (*ptr != '\0')
   272         *(ptr++) = '\0';
   273 
   274     *_str = str;
   275     *_ptr = ptr;
   276     return SDL_TRUE;
   277 }
   278 
   279 static SDL_bool
   280 int_string(char *str, int *val)
   281 {
   282     char *endptr = NULL;
   283     *val = (int) strtol(str + 2, &endptr, 16);
   284     return ((*str != '\0') && (*endptr == '\0'));
   285 }
   286 
   287 /* http://lxr.linux.no/linux+v2.6.29/drivers/char/apm-emulation.c */
   288 SDL_bool
   289 SDL_GetPowerInfo_Linux_proc_apm(SDL_PowerState * state,
   290                                 int *seconds, int *percent)
   291 {
   292     SDL_bool need_details = SDL_FALSE;
   293     int ac_status = 0;
   294     int battery_status = 0;
   295     int battery_flag = 0;
   296     int battery_percent = 0;
   297     int battery_time = 0;
   298     const int fd = open("/proc/apm", O_RDONLY);
   299     char buf[128];
   300     char *ptr = &buf[0];
   301     char *str = NULL;
   302     ssize_t br;
   303 
   304     if (fd == -1) {
   305         return SDL_FALSE;       /* can't use this interface. */
   306     }
   307 
   308     br = read(fd, buf, sizeof (buf) - 1);
   309     close(fd);
   310 
   311     if (br < 0) {
   312         return SDL_FALSE;
   313     }
   314 
   315     buf[br] = '\0';             // null-terminate the string.
   316     if (!next_string(&ptr, &str)) {     /* driver version */
   317         return SDL_FALSE;
   318     }
   319     if (!next_string(&ptr, &str)) {     /* BIOS version */
   320         return SDL_FALSE;
   321     }
   322     if (!next_string(&ptr, &str)) {     /* APM flags */
   323         return SDL_FALSE;
   324     }
   325 
   326     if (!next_string(&ptr, &str)) {     /* AC line status */
   327         return SDL_FALSE;
   328     } else if (!int_string(str, &ac_status)) {
   329         return SDL_FALSE;
   330     }
   331 
   332     if (!next_string(&ptr, &str)) {     /* battery status */
   333         return SDL_FALSE;
   334     } else if (!int_string(str, &battery_status)) {
   335         return SDL_FALSE;
   336     }
   337     if (!next_string(&ptr, &str)) {     /* battery flag */
   338         return SDL_FALSE;
   339     } else if (!int_string(str, &battery_flag)) {
   340         return SDL_FALSE;
   341     }
   342     if (!next_string(&ptr, &str)) {     /* remaining battery life percent */
   343         return SDL_FALSE;
   344     }
   345     if (str[strlen(str) - 1] == '%') {
   346         str[strlen(str) - 1] = '\0';
   347     }
   348     if (!int_string(str, &battery_percent)) {
   349         return SDL_FALSE;
   350     }
   351 
   352     if (!next_string(&ptr, &str)) {     /* remaining battery life time */
   353         return SDL_FALSE;
   354     } else if (!int_string(str, &battery_time)) {
   355         return SDL_FALSE;
   356     }
   357 
   358     if (!next_string(&ptr, &str)) {     /* remaining battery life time units */
   359         return SDL_FALSE;
   360     } else if (strcmp(str, "min") == 0) {
   361         battery_time *= 60;
   362     }
   363 
   364     if (battery_flag == 0xFF) { /* unknown state */
   365         *state = SDL_POWERSTATE_UNKNOWN;
   366     } else if (battery_flag & (1 << 7)) {       /* no battery */
   367         *state = SDL_POWERSTATE_NO_BATTERY;
   368     } else if (battery_flag & (1 << 3)) {       /* charging */
   369         *state = SDL_POWERSTATE_CHARGING;
   370         need_details = SDL_TRUE;
   371     } else if (ac_status == 1) {
   372         *state = SDL_POWERSTATE_CHARGED;        /* on AC, not charging. */
   373         need_details = SDL_TRUE;
   374     } else {
   375         *state = SDL_POWERSTATE_ON_BATTERY;
   376         need_details = SDL_TRUE;
   377     }
   378 
   379     *percent = -1;
   380     *seconds = -1;
   381     if (need_details) {
   382         const int pct = battery_percent;
   383         const int secs = battery_time;
   384 
   385         if (pct >= 0) {         /* -1 == unknown */
   386             *percent = (pct > 100) ? 100 : pct; /* clamp between 0%, 100% */
   387         }
   388         if (secs >= 0) {        /* -1 == unknown */
   389             *seconds = secs;
   390         }
   391     }
   392 
   393     return SDL_TRUE;
   394 }
   395 
   396 #endif /* SDL_POWER_LINUX */
   397 #endif /* SDL_POWER_DISABLED */
   398 
   399 /* vi: set ts=4 sw=4 expandtab: */