SDL 2.0
SDL_syspower.c
Go to the documentation of this file.
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2019 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
39static const char *proc_apm_path = "/proc/apm";
40static const char *proc_acpi_battery_path = "/proc/acpi/battery";
41static const char *proc_acpi_ac_adapter_path = "/proc/acpi/ac_adapter";
42static const char *sys_class_power_supply_path = "/sys/class/power_supply";
43
44static int
45open_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
58static SDL_bool
59read_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
77static SDL_bool
78make_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
124static void
125check_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
213static void
214check_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
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;
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) {
276 } else if (charging) {
278 } else if (have_ac) {
280 } else {
282 }
283
284 return SDL_TRUE; /* definitive answer. */
285}
286
287
288static SDL_bool
289next_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
314static SDL_bool
315int_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 */
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 */
401 } else if (battery_flag & (1 << 7)) { /* no battery */
403 } else if (battery_flag & (1 << 3)) { /* 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 {
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
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];
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)) {
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) {
480 } else if (SDL_strcmp(str, "Discharging\n") == 0) {
482 } else if ((SDL_strcmp(str, "Full\n") == 0) || (SDL_strcmp(str, "Not charging\n") == 0)) {
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
535static void
536check_upower_device(DBusConnection *conn, const char *path, SDL_PowerState *state, int *seconds, int *percent)
537{
538 SDL_bool choose = SDL_FALSE;
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) {
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 */
562 } else if ((ui32 == 2) || (ui32 == 3)) { /* 2 == discharging, 3 == empty. */
564 } else if (ui32 == 4) { /* 4 == full */
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
608{
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: */
#define SDL_strcmp
#define SDL_atoi
SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char const char SDL_SCANF_FORMAT_STRING const char return SDL_ThreadFunction const char void return Uint32 return Uint32 SDL_AssertionHandler void SDL_SpinLock SDL_atomic_t int int return SDL_atomic_t return void void void return void return int return SDL_AudioSpec SDL_AudioSpec return int int return return int SDL_RWops int SDL_AudioSpec Uint8 ** d
GLuint GLfloat * val
GLsizei const GLuint * paths
GLuint const GLchar * name
GLenum GLuint GLenum GLsizei const GLchar * buf
GLsizei const GLchar *const * path
SDL_PowerState
The basic state for the system's power supply.
Definition: SDL_power.h:43
@ SDL_POWERSTATE_NO_BATTERY
Definition: SDL_power.h:46
@ SDL_POWERSTATE_CHARGING
Definition: SDL_power.h:47
@ SDL_POWERSTATE_CHARGED
Definition: SDL_power.h:48
@ SDL_POWERSTATE_UNKNOWN
Definition: SDL_power.h:44
@ SDL_POWERSTATE_ON_BATTERY
Definition: SDL_power.h:45
SDL_bool
Definition: SDL_stdinc.h:162
@ SDL_TRUE
Definition: SDL_stdinc.h:164
@ SDL_FALSE
Definition: SDL_stdinc.h:163
uint32_t Uint32
Definition: SDL_stdinc.h:203
int64_t Sint64
Definition: SDL_stdinc.h:210
SDL_bool SDL_GetPowerInfo_Linux_sys_class_power_supply(SDL_PowerState *, int *, int *)
SDL_bool SDL_GetPowerInfo_Linux_org_freedesktop_upower(SDL_PowerState *, int *, int *)
SDL_bool SDL_GetPowerInfo_Linux_proc_apm(SDL_PowerState *, int *, int *)
SDL_bool SDL_GetPowerInfo_Linux_proc_acpi(SDL_PowerState *, int *, int *)
struct xkb_state * state
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
#define NULL
Definition: begin_code.h:167
GLuint64 key
Definition: gl2ext.h:2192
GLuint64 GLenum GLint fd
Definition: gl2ext.h:1508
SDL_bool retval