2 Simple DirectMedia Layer
3 Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
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.
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:
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.
21 #include "../../SDL_internal.h"
23 #ifndef SDL_POWER_DISABLED
29 #include <sys/types.h>
34 #include "SDL_power.h"
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 static const char *sys_class_power_supply_path = "/sys/class/power_supply";
42 open_power_file(const char *base, const char *node, const char *key)
44 const size_t pathlen = strlen(base) + strlen(node) + strlen(key) + 3;
45 char *path = (char *) alloca(pathlen);
47 return -1; /* oh well. */
50 snprintf(path, pathlen, "%s/%s/%s", base, node, key);
51 return open(path, O_RDONLY);
56 read_power_file(const char *base, const char *node, const char *key,
57 char *buf, size_t buflen)
60 const int fd = open_power_file(base, node, key);
64 br = read(fd, buf, buflen-1);
69 buf[br] = '\0'; /* null-terminate the string. */
75 make_proc_acpi_key_val(char **_ptr, char **_key, char **_val)
80 ptr++; /* skip whitespace. */
84 return SDL_FALSE; /* EOF. */
89 while ((*ptr != ':') && (*ptr != '\0')) {
94 return SDL_FALSE; /* (unexpected) EOF. */
97 *(ptr++) = '\0'; /* terminate the key. */
99 while ((*ptr == ' ') && (*ptr != '\0')) {
100 ptr++; /* skip whitespace. */
104 return SDL_FALSE; /* (unexpected) EOF. */
109 while ((*ptr != '\n') && (*ptr != '\0')) {
114 *(ptr++) = '\0'; /* terminate the value. */
117 *_ptr = ptr; /* store for next time. */
122 check_proc_acpi_battery(const char * node, SDL_bool * have_battery,
123 SDL_bool * charging, int *seconds, int *percent)
125 const char *base = proc_acpi_battery_path;
131 SDL_bool charge = SDL_FALSE;
132 SDL_bool choose = SDL_FALSE;
138 if (!read_power_file(base, node, "state", state, sizeof (state))) {
140 } else if (!read_power_file(base, node, "info", info, sizeof (info))) {
145 while (make_proc_acpi_key_val(&ptr, &key, &val)) {
146 if (strcmp(key, "present") == 0) {
147 if (strcmp(val, "yes") == 0) {
148 *have_battery = SDL_TRUE;
150 } else if (strcmp(key, "charging state") == 0) {
151 /* !!! FIXME: what exactly _does_ charging/discharging mean? */
152 if (strcmp(val, "charging/discharging") == 0) {
154 } else if (strcmp(val, "charging") == 0) {
157 } else if (strcmp(key, "remaining capacity") == 0) {
159 const int cvt = (int) strtol(val, &endptr, 10);
160 if (*endptr == ' ') {
167 while (make_proc_acpi_key_val(&ptr, &key, &val)) {
168 if (strcmp(key, "design capacity") == 0) {
170 const int cvt = (int) strtol(val, &endptr, 10);
171 if (*endptr == ' ') {
177 if ((maximum >= 0) && (remaining >= 0)) {
178 pct = (int) ((((float) remaining) / ((float) maximum)) * 100.0f);
181 } else if (pct > 100) {
186 /* !!! FIXME: calculate (secs). */
189 * We pick the battery that claims to have the most minutes left.
190 * (failing a report of minutes, we'll take the highest percent.)
192 if ((secs < 0) && (*seconds < 0)) {
193 if ((pct < 0) && (*percent < 0)) {
194 choose = SDL_TRUE; /* at least we know there's a battery. */
196 if (pct > *percent) {
199 } else if (secs > *seconds) {
211 check_proc_acpi_ac_adapter(const char * node, SDL_bool * have_ac)
213 const char *base = proc_acpi_ac_adapter_path;
219 if (!read_power_file(base, node, "state", state, sizeof (state))) {
224 while (make_proc_acpi_key_val(&ptr, &key, &val)) {
225 if (strcmp(key, "state") == 0) {
226 if (strcmp(val, "on-line") == 0) {
235 SDL_GetPowerInfo_Linux_proc_acpi(SDL_PowerState * state,
236 int *seconds, int *percent)
238 struct dirent *dent = NULL;
240 SDL_bool have_battery = SDL_FALSE;
241 SDL_bool have_ac = SDL_FALSE;
242 SDL_bool charging = SDL_FALSE;
246 *state = SDL_POWERSTATE_UNKNOWN;
248 dirp = opendir(proc_acpi_battery_path);
250 return SDL_FALSE; /* can't use this interface. */
252 while ((dent = readdir(dirp)) != NULL) {
253 const char *node = dent->d_name;
254 check_proc_acpi_battery(node, &have_battery, &charging,
260 dirp = opendir(proc_acpi_ac_adapter_path);
262 return SDL_FALSE; /* can't use this interface. */
264 while ((dent = readdir(dirp)) != NULL) {
265 const char *node = dent->d_name;
266 check_proc_acpi_ac_adapter(node, &have_ac);
272 *state = SDL_POWERSTATE_NO_BATTERY;
273 } else if (charging) {
274 *state = SDL_POWERSTATE_CHARGING;
275 } else if (have_ac) {
276 *state = SDL_POWERSTATE_CHARGED;
278 *state = SDL_POWERSTATE_ON_BATTERY;
281 return SDL_TRUE; /* definitive answer. */
286 next_string(char **_ptr, char **_str)
291 while (*ptr == ' ') { /* skip any spaces... */
300 while ((*ptr != ' ') && (*ptr != '\n') && (*ptr != '\0'))
312 int_string(char *str, int *val)
315 *val = (int) strtol(str, &endptr, 0);
316 return ((*str != '\0') && (*endptr == '\0'));
319 /* http://lxr.linux.no/linux+v2.6.29/drivers/char/apm-emulation.c */
321 SDL_GetPowerInfo_Linux_proc_apm(SDL_PowerState * state,
322 int *seconds, int *percent)
324 SDL_bool need_details = SDL_FALSE;
326 int battery_status = 0;
327 int battery_flag = 0;
328 int battery_percent = 0;
329 int battery_time = 0;
330 const int fd = open(proc_apm_path, O_RDONLY);
337 return SDL_FALSE; /* can't use this interface. */
340 br = read(fd, buf, sizeof (buf) - 1);
347 buf[br] = '\0'; /* null-terminate the string. */
348 if (!next_string(&ptr, &str)) { /* driver version */
351 if (!next_string(&ptr, &str)) { /* BIOS version */
354 if (!next_string(&ptr, &str)) { /* APM flags */
358 if (!next_string(&ptr, &str)) { /* AC line status */
360 } else if (!int_string(str, &ac_status)) {
364 if (!next_string(&ptr, &str)) { /* battery status */
366 } else if (!int_string(str, &battery_status)) {
369 if (!next_string(&ptr, &str)) { /* battery flag */
371 } else if (!int_string(str, &battery_flag)) {
374 if (!next_string(&ptr, &str)) { /* remaining battery life percent */
377 if (str[strlen(str) - 1] == '%') {
378 str[strlen(str) - 1] = '\0';
380 if (!int_string(str, &battery_percent)) {
384 if (!next_string(&ptr, &str)) { /* remaining battery life time */
386 } else if (!int_string(str, &battery_time)) {
390 if (!next_string(&ptr, &str)) { /* remaining battery life time units */
392 } else if (strcmp(str, "min") == 0) {
396 if (battery_flag == 0xFF) { /* unknown state */
397 *state = SDL_POWERSTATE_UNKNOWN;
398 } else if (battery_flag & (1 << 7)) { /* no battery */
399 *state = SDL_POWERSTATE_NO_BATTERY;
400 } else if (battery_flag & (1 << 3)) { /* charging */
401 *state = SDL_POWERSTATE_CHARGING;
402 need_details = SDL_TRUE;
403 } else if (ac_status == 1) {
404 *state = SDL_POWERSTATE_CHARGED; /* on AC, not charging. */
405 need_details = SDL_TRUE;
407 *state = SDL_POWERSTATE_ON_BATTERY;
408 need_details = SDL_TRUE;
414 const int pct = battery_percent;
415 const int secs = battery_time;
417 if (pct >= 0) { /* -1 == unknown */
418 *percent = (pct > 100) ? 100 : pct; /* clamp between 0%, 100% */
420 if (secs >= 0) { /* -1 == unknown */
428 /* !!! FIXME: implement d-bus queries to org.freedesktop.UPower. */
431 SDL_GetPowerInfo_Linux_sys_class_power_supply(SDL_PowerState *state, int *seconds, int *percent)
433 const char *base = sys_class_power_supply_path;
437 dirp = opendir(base);
442 *state = SDL_POWERSTATE_NO_BATTERY; /* assume we're just plugged in. */
446 while ((dent = readdir(dirp)) != NULL) {
447 const char *name = dent->d_name;
448 SDL_bool choose = SDL_FALSE;
454 if ((SDL_strcmp(name, ".") == 0) || (SDL_strcmp(name, "..") == 0)) {
455 continue; /* skip these, of course. */
456 } else if (!read_power_file(base, name, "type", str, sizeof (str))) {
457 continue; /* Don't know _what_ we're looking at. Give up on it. */
458 } else if (SDL_strcmp(str, "Battery\n") != 0) {
459 continue; /* we don't care about UPS and such. */
462 /* some drivers don't offer this, so if it's not explicitly reported assume it's present. */
463 if (read_power_file(base, name, "present", str, sizeof (str)) && (SDL_strcmp(str, "0\n") == 0)) {
464 st = SDL_POWERSTATE_NO_BATTERY;
465 } else if (!read_power_file(base, name, "status", str, sizeof (str))) {
466 st = SDL_POWERSTATE_UNKNOWN; /* uh oh */
467 } else if (SDL_strcmp(str, "Charging\n") == 0) {
468 st = SDL_POWERSTATE_CHARGING;
469 } else if (SDL_strcmp(str, "Discharging\n") == 0) {
470 st = SDL_POWERSTATE_ON_BATTERY;
471 } else if ((SDL_strcmp(str, "Full\n") == 0) || (SDL_strcmp(str, "Not charging\n") == 0)) {
472 st = SDL_POWERSTATE_CHARGED;
474 st = SDL_POWERSTATE_UNKNOWN; /* uh oh */
477 if (!read_power_file(base, name, "capacity", str, sizeof (str))) {
481 pct = (pct > 100) ? 100 : pct; /* clamp between 0%, 100% */
484 if (!read_power_file(base, name, "time_to_empty_now", str, sizeof (str))) {
487 secs = SDL_atoi(str);
488 secs = (secs <= 0) ? -1 : secs; /* 0 == unknown */
492 * We pick the battery that claims to have the most minutes left.
493 * (failing a report of minutes, we'll take the highest percent.)
495 if ((secs < 0) && (*seconds < 0)) {
496 if ((pct < 0) && (*percent < 0)) {
497 choose = SDL_TRUE; /* at least we know there's a battery. */
498 } else if (pct > *percent) {
501 } else if (secs > *seconds) {
513 return SDL_TRUE; /* don't look any further. */
516 #endif /* SDL_POWER_LINUX */
517 #endif /* SDL_POWER_DISABLED */
519 /* vi: set ts=4 sw=4 expandtab: */