src/power/linux/SDL_syspower.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 02 Feb 2014 00:53:27 -0800
changeset 8149 681eb46b8ac4
parent 8093 b43765095a6f
child 9619 b94b6d0bff0f
permissions -rw-r--r--
Fixed bug 2374 - Update copyright for 2014...

Is it that time already??
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2014 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 
    36 static const char *proc_apm_path = "/proc/apm";
    37 static const char *proc_acpi_battery_path = "/proc/acpi/battery";
    38 static const char *proc_acpi_ac_adapter_path = "/proc/acpi/ac_adapter";
    39 
    40 static int open_acpi_file(const char *base, const char *node, const char *key)
    41 {
    42     const size_t pathlen = strlen(base) + strlen(node) + strlen(key) + 3;
    43     char *path = (char *) alloca(pathlen);
    44     if (path == NULL) {
    45         return -1;  /* oh well. */
    46     }
    47 
    48     snprintf(path, pathlen, "%s/%s/%s", base, node, key);
    49     return open(path, O_RDONLY);
    50 }
    51 
    52 
    53 static SDL_bool
    54 load_acpi_file(const char *base, const char *node, const char *key,
    55                char *buf, size_t buflen)
    56 {
    57     ssize_t br = 0;
    58     const int fd = open_acpi_file(base, node, key);
    59     if (fd == -1) {
    60         return SDL_FALSE;
    61     }
    62     br = read(fd, buf, buflen-1);
    63     close(fd);
    64     if (br < 0) {
    65         return SDL_FALSE;
    66     }
    67     buf[br] = '\0';             /* null-terminate the string. */
    68     return SDL_TRUE;
    69 }
    70 
    71 
    72 static SDL_bool
    73 make_proc_acpi_key_val(char **_ptr, char **_key, char **_val)
    74 {
    75     char *ptr = *_ptr;
    76 
    77     while (*ptr == ' ') {
    78         ptr++;  /* skip whitespace. */
    79     }
    80 
    81     if (*ptr == '\0') {
    82         return SDL_FALSE;  /* EOF. */
    83     }
    84 
    85     *_key = ptr;
    86 
    87     while ((*ptr != ':') && (*ptr != '\0')) {
    88         ptr++;
    89     }
    90 
    91     if (*ptr == '\0') {
    92         return SDL_FALSE;  /* (unexpected) EOF. */
    93     }
    94 
    95     *(ptr++) = '\0';  /* terminate the key. */
    96 
    97     while ((*ptr == ' ') && (*ptr != '\0')) {
    98         ptr++;  /* skip whitespace. */
    99     }
   100 
   101     if (*ptr == '\0') {
   102         return SDL_FALSE;  /* (unexpected) EOF. */
   103     }
   104 
   105     *_val = ptr;
   106 
   107     while ((*ptr != '\n') && (*ptr != '\0')) {
   108         ptr++;
   109     }
   110 
   111     if (*ptr != '\0') {
   112         *(ptr++) = '\0';  /* terminate the value. */
   113     }
   114 
   115     *_ptr = ptr;  /* store for next time. */
   116     return SDL_TRUE;
   117 }
   118 
   119 static void
   120 check_proc_acpi_battery(const char * node, SDL_bool * have_battery,
   121                         SDL_bool * charging, int *seconds, int *percent)
   122 {
   123     const char *base = proc_acpi_battery_path;
   124     char info[1024];
   125     char state[1024];
   126     char *ptr = NULL;
   127     char *key = NULL;
   128     char *val = NULL;
   129     SDL_bool charge = SDL_FALSE;
   130     SDL_bool choose = SDL_FALSE;
   131     int maximum = -1;
   132     int remaining = -1;
   133     int secs = -1;
   134     int pct = -1;
   135 
   136     if (!load_acpi_file(base, node, "state", state, sizeof (state))) {
   137         return;
   138     } else if (!load_acpi_file(base, node, "info", info, sizeof (info))) {
   139         return;
   140     }
   141 
   142     ptr = &state[0];
   143     while (make_proc_acpi_key_val(&ptr, &key, &val)) {
   144         if (strcmp(key, "present") == 0) {
   145             if (strcmp(val, "yes") == 0) {
   146                 *have_battery = SDL_TRUE;
   147             }
   148         } else if (strcmp(key, "charging state") == 0) {
   149             /* !!! FIXME: what exactly _does_ charging/discharging mean? */
   150             if (strcmp(val, "charging/discharging") == 0) {
   151                 charge = SDL_TRUE;
   152             } else if (strcmp(val, "charging") == 0) {
   153                 charge = SDL_TRUE;
   154             }
   155         } else if (strcmp(key, "remaining capacity") == 0) {
   156             char *endptr = NULL;
   157             const int cvt = (int) strtol(val, &endptr, 10);
   158             if (*endptr == ' ') {
   159                 remaining = cvt;
   160             }
   161         }
   162     }
   163 
   164     ptr = &info[0];
   165     while (make_proc_acpi_key_val(&ptr, &key, &val)) {
   166         if (strcmp(key, "design capacity") == 0) {
   167             char *endptr = NULL;
   168             const int cvt = (int) strtol(val, &endptr, 10);
   169             if (*endptr == ' ') {
   170                 maximum = cvt;
   171             }
   172         }
   173     }
   174 
   175     if ((maximum >= 0) && (remaining >= 0)) {
   176         pct = (int) ((((float) remaining) / ((float) maximum)) * 100.0f);
   177         if (pct < 0) {
   178             pct = 0;
   179         } else if (pct > 100) {
   180             pct = 100;
   181         }
   182     }
   183 
   184     /* !!! FIXME: calculate (secs). */
   185 
   186     /*
   187      * We pick the battery that claims to have the most minutes left.
   188      *  (failing a report of minutes, we'll take the highest percent.)
   189      */
   190     if ((secs < 0) && (*seconds < 0)) {
   191         if ((pct < 0) && (*percent < 0)) {
   192             choose = SDL_TRUE;  /* at least we know there's a battery. */
   193         }
   194         if (pct > *percent) {
   195             choose = SDL_TRUE;
   196         }
   197     } else if (secs > *seconds) {
   198         choose = SDL_TRUE;
   199     }
   200 
   201     if (choose) {
   202         *seconds = secs;
   203         *percent = pct;
   204         *charging = charge;
   205     }
   206 }
   207 
   208 static void
   209 check_proc_acpi_ac_adapter(const char * node, SDL_bool * have_ac)
   210 {
   211     const char *base = proc_acpi_ac_adapter_path;
   212     char state[256];
   213     char *ptr = NULL;
   214     char *key = NULL;
   215     char *val = NULL;
   216 
   217     if (!load_acpi_file(base, node, "state", state, sizeof (state))) {
   218         return;
   219     }
   220 
   221     ptr = &state[0];
   222     while (make_proc_acpi_key_val(&ptr, &key, &val)) {
   223         if (strcmp(key, "state") == 0) {
   224             if (strcmp(val, "on-line") == 0) {
   225                 *have_ac = SDL_TRUE;
   226             }
   227         }
   228     }
   229 }
   230 
   231 
   232 SDL_bool
   233 SDL_GetPowerInfo_Linux_proc_acpi(SDL_PowerState * state,
   234                                  int *seconds, int *percent)
   235 {
   236     struct dirent *dent = NULL;
   237     DIR *dirp = NULL;
   238     SDL_bool have_battery = SDL_FALSE;
   239     SDL_bool have_ac = SDL_FALSE;
   240     SDL_bool charging = SDL_FALSE;
   241 
   242     *seconds = -1;
   243     *percent = -1;
   244     *state = SDL_POWERSTATE_UNKNOWN;
   245 
   246     dirp = opendir(proc_acpi_battery_path);
   247     if (dirp == NULL) {
   248         return SDL_FALSE;  /* can't use this interface. */
   249     } else {
   250         while ((dent = readdir(dirp)) != NULL) {
   251             const char *node = dent->d_name;
   252             check_proc_acpi_battery(node, &have_battery, &charging,
   253                                     seconds, percent);
   254         }
   255         closedir(dirp);
   256     }
   257 
   258     dirp = opendir(proc_acpi_ac_adapter_path);
   259     if (dirp == NULL) {
   260         return SDL_FALSE;  /* can't use this interface. */
   261     } else {
   262         while ((dent = readdir(dirp)) != NULL) {
   263             const char *node = dent->d_name;
   264             check_proc_acpi_ac_adapter(node, &have_ac);
   265         }
   266         closedir(dirp);
   267     }
   268 
   269     if (!have_battery) {
   270         *state = SDL_POWERSTATE_NO_BATTERY;
   271     } else if (charging) {
   272         *state = SDL_POWERSTATE_CHARGING;
   273     } else if (have_ac) {
   274         *state = SDL_POWERSTATE_CHARGED;
   275     } else {
   276         *state = SDL_POWERSTATE_ON_BATTERY;
   277     }
   278 
   279     return SDL_TRUE;   /* definitive answer. */
   280 }
   281 
   282 
   283 static SDL_bool
   284 next_string(char **_ptr, char **_str)
   285 {
   286     char *ptr = *_ptr;
   287     char *str = *_str;
   288 
   289     while (*ptr == ' ') {       /* skip any spaces... */
   290         ptr++;
   291     }
   292 
   293     if (*ptr == '\0') {
   294         return SDL_FALSE;
   295     }
   296 
   297     str = ptr;
   298     while ((*ptr != ' ') && (*ptr != '\n') && (*ptr != '\0'))
   299         ptr++;
   300 
   301     if (*ptr != '\0')
   302         *(ptr++) = '\0';
   303 
   304     *_str = str;
   305     *_ptr = ptr;
   306     return SDL_TRUE;
   307 }
   308 
   309 static SDL_bool
   310 int_string(char *str, int *val)
   311 {
   312     char *endptr = NULL;
   313     *val = (int) strtol(str, &endptr, 0);
   314     return ((*str != '\0') && (*endptr == '\0'));
   315 }
   316 
   317 /* http://lxr.linux.no/linux+v2.6.29/drivers/char/apm-emulation.c */
   318 SDL_bool
   319 SDL_GetPowerInfo_Linux_proc_apm(SDL_PowerState * state,
   320                                 int *seconds, int *percent)
   321 {
   322     SDL_bool need_details = SDL_FALSE;
   323     int ac_status = 0;
   324     int battery_status = 0;
   325     int battery_flag = 0;
   326     int battery_percent = 0;
   327     int battery_time = 0;
   328     const int fd = open(proc_apm_path, O_RDONLY);
   329     char buf[128];
   330     char *ptr = &buf[0];
   331     char *str = NULL;
   332     ssize_t br;
   333 
   334     if (fd == -1) {
   335         return SDL_FALSE;       /* can't use this interface. */
   336     }
   337 
   338     br = read(fd, buf, sizeof (buf) - 1);
   339     close(fd);
   340 
   341     if (br < 0) {
   342         return SDL_FALSE;
   343     }
   344 
   345     buf[br] = '\0';             /* null-terminate the string. */
   346     if (!next_string(&ptr, &str)) {     /* driver version */
   347         return SDL_FALSE;
   348     }
   349     if (!next_string(&ptr, &str)) {     /* BIOS version */
   350         return SDL_FALSE;
   351     }
   352     if (!next_string(&ptr, &str)) {     /* APM flags */
   353         return SDL_FALSE;
   354     }
   355 
   356     if (!next_string(&ptr, &str)) {     /* AC line status */
   357         return SDL_FALSE;
   358     } else if (!int_string(str, &ac_status)) {
   359         return SDL_FALSE;
   360     }
   361 
   362     if (!next_string(&ptr, &str)) {     /* battery status */
   363         return SDL_FALSE;
   364     } else if (!int_string(str, &battery_status)) {
   365         return SDL_FALSE;
   366     }
   367     if (!next_string(&ptr, &str)) {     /* battery flag */
   368         return SDL_FALSE;
   369     } else if (!int_string(str, &battery_flag)) {
   370         return SDL_FALSE;
   371     }
   372     if (!next_string(&ptr, &str)) {     /* remaining battery life percent */
   373         return SDL_FALSE;
   374     }
   375     if (str[strlen(str) - 1] == '%') {
   376         str[strlen(str) - 1] = '\0';
   377     }
   378     if (!int_string(str, &battery_percent)) {
   379         return SDL_FALSE;
   380     }
   381 
   382     if (!next_string(&ptr, &str)) {     /* remaining battery life time */
   383         return SDL_FALSE;
   384     } else if (!int_string(str, &battery_time)) {
   385         return SDL_FALSE;
   386     }
   387 
   388     if (!next_string(&ptr, &str)) {     /* remaining battery life time units */
   389         return SDL_FALSE;
   390     } else if (strcmp(str, "min") == 0) {
   391         battery_time *= 60;
   392     }
   393 
   394     if (battery_flag == 0xFF) { /* unknown state */
   395         *state = SDL_POWERSTATE_UNKNOWN;
   396     } else if (battery_flag & (1 << 7)) {       /* no battery */
   397         *state = SDL_POWERSTATE_NO_BATTERY;
   398     } else if (battery_flag & (1 << 3)) {       /* charging */
   399         *state = SDL_POWERSTATE_CHARGING;
   400         need_details = SDL_TRUE;
   401     } else if (ac_status == 1) {
   402         *state = SDL_POWERSTATE_CHARGED;        /* on AC, not charging. */
   403         need_details = SDL_TRUE;
   404     } else {
   405         *state = SDL_POWERSTATE_ON_BATTERY;
   406         need_details = SDL_TRUE;
   407     }
   408 
   409     *percent = -1;
   410     *seconds = -1;
   411     if (need_details) {
   412         const int pct = battery_percent;
   413         const int secs = battery_time;
   414 
   415         if (pct >= 0) {         /* -1 == unknown */
   416             *percent = (pct > 100) ? 100 : pct; /* clamp between 0%, 100% */
   417         }
   418         if (secs >= 0) {        /* -1 == unknown */
   419             *seconds = secs;
   420         }
   421     }
   422 
   423     return SDL_TRUE;
   424 }
   425 
   426 #endif /* SDL_POWER_LINUX */
   427 #endif /* SDL_POWER_DISABLED */
   428 
   429 /* vi: set ts=4 sw=4 expandtab: */