1 | /* |
2 | Simple DirectMedia Layer |
3 | Copyright (C) 1997-2025 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 | #ifdef 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_syspower.h" |
35 | |
36 | #include "../../core/linux/SDL_dbus.h" |
37 | |
38 | static const char *proc_apm_path = "/proc/apm" ; |
39 | static const char *proc_acpi_battery_path = "/proc/acpi/battery" ; |
40 | static const char *proc_acpi_ac_adapter_path = "/proc/acpi/ac_adapter" ; |
41 | static const char *sys_class_power_supply_path = "/sys/class/power_supply" ; |
42 | |
43 | static int open_power_file(const char *base, const char *node, const char *key) |
44 | { |
45 | int fd; |
46 | const size_t pathlen = SDL_strlen(base) + SDL_strlen(node) + SDL_strlen(key) + 3; |
47 | char *path = SDL_stack_alloc(char, pathlen); |
48 | if (!path) { |
49 | return -1; // oh well. |
50 | } |
51 | |
52 | (void)SDL_snprintf(path, pathlen, "%s/%s/%s" , base, node, key); |
53 | fd = open(path, O_RDONLY | O_CLOEXEC); |
54 | SDL_stack_free(path); |
55 | return fd; |
56 | } |
57 | |
58 | static bool read_power_file(const char *base, const char *node, const char *key, |
59 | char *buf, size_t buflen) |
60 | { |
61 | ssize_t br = 0; |
62 | const int fd = open_power_file(base, node, key); |
63 | if (fd == -1) { |
64 | return false; |
65 | } |
66 | br = read(fd, buf, buflen - 1); |
67 | close(fd); |
68 | if (br < 0) { |
69 | return false; |
70 | } |
71 | buf[br] = '\0'; // null-terminate the string. |
72 | return true; |
73 | } |
74 | |
75 | static bool make_proc_acpi_key_val(char **_ptr, char **_key, char **_val) |
76 | { |
77 | char *ptr = *_ptr; |
78 | |
79 | while (*ptr == ' ') { |
80 | ptr++; // skip whitespace. |
81 | } |
82 | |
83 | if (*ptr == '\0') { |
84 | return false; // EOF. |
85 | } |
86 | |
87 | *_key = ptr; |
88 | |
89 | while ((*ptr != ':') && (*ptr != '\0')) { |
90 | ptr++; |
91 | } |
92 | |
93 | if (*ptr == '\0') { |
94 | return false; // (unexpected) EOF. |
95 | } |
96 | |
97 | *(ptr++) = '\0'; // terminate the key. |
98 | |
99 | while (*ptr == ' ') { |
100 | ptr++; // skip whitespace. |
101 | } |
102 | |
103 | if (*ptr == '\0') { |
104 | return false; // (unexpected) EOF. |
105 | } |
106 | |
107 | *_val = ptr; |
108 | |
109 | while ((*ptr != '\n') && (*ptr != '\0')) { |
110 | ptr++; |
111 | } |
112 | |
113 | if (*ptr != '\0') { |
114 | *(ptr++) = '\0'; // terminate the value. |
115 | } |
116 | |
117 | *_ptr = ptr; // store for next time. |
118 | return true; |
119 | } |
120 | |
121 | static void check_proc_acpi_battery(const char *node, bool *have_battery, |
122 | bool *charging, int *seconds, int *percent) |
123 | { |
124 | const char *base = proc_acpi_battery_path; |
125 | char info[1024]; |
126 | char state[1024]; |
127 | char *ptr = NULL; |
128 | char *key = NULL; |
129 | char *val = NULL; |
130 | bool charge = false; |
131 | bool choose = false; |
132 | int maximum = -1; |
133 | int remaining = -1; |
134 | int secs = -1; |
135 | int pct = -1; |
136 | |
137 | if (!read_power_file(base, node, "state" , state, sizeof(state))) { |
138 | return; |
139 | } else if (!read_power_file(base, node, "info" , info, sizeof(info))) { |
140 | return; |
141 | } |
142 | |
143 | ptr = &state[0]; |
144 | while (make_proc_acpi_key_val(&ptr, &key, &val)) { |
145 | if (SDL_strcasecmp(key, "present" ) == 0) { |
146 | if (SDL_strcasecmp(val, "yes" ) == 0) { |
147 | *have_battery = true; |
148 | } |
149 | } else if (SDL_strcasecmp(key, "charging state" ) == 0) { |
150 | // !!! FIXME: what exactly _does_ charging/discharging mean? |
151 | if (SDL_strcasecmp(val, "charging/discharging" ) == 0) { |
152 | charge = true; |
153 | } else if (SDL_strcasecmp(val, "charging" ) == 0) { |
154 | charge = true; |
155 | } |
156 | } else if (SDL_strcasecmp(key, "remaining capacity" ) == 0) { |
157 | char *endptr = NULL; |
158 | const int cvt = (int)SDL_strtol(val, &endptr, 10); |
159 | if (*endptr == ' ') { |
160 | remaining = cvt; |
161 | } |
162 | } |
163 | } |
164 | |
165 | ptr = &info[0]; |
166 | while (make_proc_acpi_key_val(&ptr, &key, &val)) { |
167 | if (SDL_strcasecmp(key, "design capacity" ) == 0) { |
168 | char *endptr = NULL; |
169 | const int cvt = (int)SDL_strtol(val, &endptr, 10); |
170 | if (*endptr == ' ') { |
171 | maximum = cvt; |
172 | } |
173 | } |
174 | } |
175 | |
176 | if ((maximum >= 0) && (remaining >= 0)) { |
177 | pct = (int)((((float)remaining) / ((float)maximum)) * 100.0f); |
178 | if (pct < 0) { |
179 | pct = 0; |
180 | } else if (pct > 100) { |
181 | pct = 100; |
182 | } |
183 | } |
184 | |
185 | // !!! FIXME: calculate (secs). |
186 | |
187 | /* |
188 | * We pick the battery that claims to have the most minutes left. |
189 | * (failing a report of minutes, we'll take the highest percent.) |
190 | */ |
191 | if ((secs < 0) && (*seconds < 0)) { |
192 | if ((pct < 0) && (*percent < 0)) { |
193 | choose = true; // at least we know there's a battery. |
194 | } |
195 | if (pct > *percent) { |
196 | choose = true; |
197 | } |
198 | } else if (secs > *seconds) { |
199 | choose = true; |
200 | } |
201 | |
202 | if (choose) { |
203 | *seconds = secs; |
204 | *percent = pct; |
205 | *charging = charge; |
206 | } |
207 | } |
208 | |
209 | static void check_proc_acpi_ac_adapter(const char *node, 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 (!read_power_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 (SDL_strcasecmp(key, "state" ) == 0) { |
224 | if (SDL_strcasecmp(val, "on-line" ) == 0) { |
225 | *have_ac = true; |
226 | } |
227 | } |
228 | } |
229 | } |
230 | |
231 | bool SDL_GetPowerInfo_Linux_proc_acpi(SDL_PowerState *state, int *seconds, int *percent) |
232 | { |
233 | struct dirent *dent = NULL; |
234 | DIR *dirp = NULL; |
235 | bool have_battery = false; |
236 | bool have_ac = false; |
237 | bool charging = false; |
238 | |
239 | *seconds = -1; |
240 | *percent = -1; |
241 | *state = SDL_POWERSTATE_UNKNOWN; |
242 | |
243 | dirp = opendir(proc_acpi_battery_path); |
244 | if (!dirp) { |
245 | return false; // can't use this interface. |
246 | } else { |
247 | while ((dent = readdir(dirp)) != NULL) { |
248 | const char *node = dent->d_name; |
249 | check_proc_acpi_battery(node, &have_battery, &charging, |
250 | seconds, percent); |
251 | } |
252 | closedir(dirp); |
253 | } |
254 | |
255 | dirp = opendir(proc_acpi_ac_adapter_path); |
256 | if (!dirp) { |
257 | return false; // can't use this interface. |
258 | } else { |
259 | while ((dent = readdir(dirp)) != NULL) { |
260 | const char *node = dent->d_name; |
261 | check_proc_acpi_ac_adapter(node, &have_ac); |
262 | } |
263 | closedir(dirp); |
264 | } |
265 | |
266 | if (!have_battery) { |
267 | *state = SDL_POWERSTATE_NO_BATTERY; |
268 | } else if (charging) { |
269 | *state = SDL_POWERSTATE_CHARGING; |
270 | } else if (have_ac) { |
271 | *state = SDL_POWERSTATE_CHARGED; |
272 | } else { |
273 | *state = SDL_POWERSTATE_ON_BATTERY; |
274 | } |
275 | |
276 | return true; // definitive answer. |
277 | } |
278 | |
279 | static bool next_string(char **_ptr, char **_str) |
280 | { |
281 | char *ptr = *_ptr; |
282 | char *str; |
283 | |
284 | while (*ptr == ' ') { // skip any spaces... |
285 | ptr++; |
286 | } |
287 | |
288 | if (*ptr == '\0') { |
289 | return false; |
290 | } |
291 | |
292 | str = ptr; |
293 | while ((*ptr != ' ') && (*ptr != '\n') && (*ptr != '\0')) { |
294 | ptr++; |
295 | } |
296 | |
297 | if (*ptr != '\0') { |
298 | *(ptr++) = '\0'; |
299 | } |
300 | |
301 | *_str = str; |
302 | *_ptr = ptr; |
303 | return true; |
304 | } |
305 | |
306 | static bool int_string(char *str, int *val) |
307 | { |
308 | char *endptr = NULL; |
309 | *val = (int)SDL_strtol(str, &endptr, 0); |
310 | return (*str != '\0') && (*endptr == '\0'); |
311 | } |
312 | |
313 | // http://lxr.linux.no/linux+v2.6.29/drivers/char/apm-emulation.c |
314 | bool SDL_GetPowerInfo_Linux_proc_apm(SDL_PowerState *state, int *seconds, int *percent) |
315 | { |
316 | bool need_details = false; |
317 | int ac_status = 0; |
318 | int battery_status = 0; |
319 | int battery_flag = 0; |
320 | int battery_percent = 0; |
321 | int battery_time = 0; |
322 | const int fd = open(proc_apm_path, O_RDONLY | O_CLOEXEC); |
323 | char buf[128]; |
324 | char *ptr = &buf[0]; |
325 | char *str = NULL; |
326 | ssize_t br; |
327 | |
328 | if (fd == -1) { |
329 | return false; // can't use this interface. |
330 | } |
331 | |
332 | br = read(fd, buf, sizeof(buf) - 1); |
333 | close(fd); |
334 | |
335 | if (br < 0) { |
336 | return false; |
337 | } |
338 | |
339 | buf[br] = '\0'; // null-terminate the string. |
340 | if (!next_string(&ptr, &str)) { // driver version |
341 | return false; |
342 | } |
343 | if (!next_string(&ptr, &str)) { // BIOS version |
344 | return false; |
345 | } |
346 | if (!next_string(&ptr, &str)) { // APM flags |
347 | return false; |
348 | } |
349 | |
350 | if (!next_string(&ptr, &str)) { // AC line status |
351 | return false; |
352 | } else if (!int_string(str, &ac_status)) { |
353 | return false; |
354 | } |
355 | |
356 | if (!next_string(&ptr, &str)) { // battery status |
357 | return false; |
358 | } else if (!int_string(str, &battery_status)) { |
359 | return false; |
360 | } |
361 | if (!next_string(&ptr, &str)) { // battery flag |
362 | return false; |
363 | } else if (!int_string(str, &battery_flag)) { |
364 | return false; |
365 | } |
366 | if (!next_string(&ptr, &str)) { // remaining battery life percent |
367 | return false; |
368 | } |
369 | if (str[SDL_strlen(str) - 1] == '%') { |
370 | str[SDL_strlen(str) - 1] = '\0'; |
371 | } |
372 | if (!int_string(str, &battery_percent)) { |
373 | return false; |
374 | } |
375 | |
376 | if (!next_string(&ptr, &str)) { // remaining battery life time |
377 | return false; |
378 | } else if (!int_string(str, &battery_time)) { |
379 | return false; |
380 | } |
381 | |
382 | if (!next_string(&ptr, &str)) { // remaining battery life time units |
383 | return false; |
384 | } else if (SDL_strcasecmp(str, "min" ) == 0) { |
385 | battery_time *= 60; |
386 | } |
387 | |
388 | if (battery_flag == 0xFF) { // unknown state |
389 | *state = SDL_POWERSTATE_UNKNOWN; |
390 | } else if (battery_flag & (1 << 7)) { // no battery |
391 | *state = SDL_POWERSTATE_NO_BATTERY; |
392 | } else if (battery_flag & (1 << 3)) { // charging |
393 | *state = SDL_POWERSTATE_CHARGING; |
394 | need_details = true; |
395 | } else if (ac_status == 1) { |
396 | *state = SDL_POWERSTATE_CHARGED; // on AC, not charging. |
397 | need_details = true; |
398 | } else { |
399 | *state = SDL_POWERSTATE_ON_BATTERY; |
400 | need_details = true; |
401 | } |
402 | |
403 | *percent = -1; |
404 | *seconds = -1; |
405 | if (need_details) { |
406 | const int pct = battery_percent; |
407 | const int secs = battery_time; |
408 | |
409 | if (pct >= 0) { // -1 == unknown |
410 | *percent = (pct > 100) ? 100 : pct; // clamp between 0%, 100% |
411 | } |
412 | if (secs >= 0) { // -1 == unknown |
413 | *seconds = secs; |
414 | } |
415 | } |
416 | |
417 | return true; |
418 | } |
419 | |
420 | bool SDL_GetPowerInfo_Linux_sys_class_power_supply(SDL_PowerState *state, int *seconds, int *percent) |
421 | { |
422 | const char *base = sys_class_power_supply_path; |
423 | struct dirent *dent; |
424 | DIR *dirp; |
425 | |
426 | dirp = opendir(base); |
427 | if (!dirp) { |
428 | return false; |
429 | } |
430 | |
431 | *state = SDL_POWERSTATE_NO_BATTERY; // assume we're just plugged in. |
432 | *seconds = -1; |
433 | *percent = -1; |
434 | |
435 | while ((dent = readdir(dirp)) != NULL) { |
436 | const char *name = dent->d_name; |
437 | bool choose = false; |
438 | char str[64]; |
439 | SDL_PowerState st; |
440 | int secs; |
441 | int pct; |
442 | int energy; |
443 | int power; |
444 | |
445 | if ((SDL_strcmp(name, "." ) == 0) || (SDL_strcmp(name, ".." ) == 0)) { |
446 | continue; // skip these, of course. |
447 | } else if (!read_power_file(base, name, "type" , str, sizeof(str))) { |
448 | continue; // Don't know _what_ we're looking at. Give up on it. |
449 | } else if (SDL_strcasecmp(str, "Battery\n" ) != 0) { |
450 | continue; // we don't care about UPS and such. |
451 | } |
452 | |
453 | /* if the scope is "device," it might be something like a PS4 |
454 | controller reporting its own battery, and not something that powers |
455 | the system. Most system batteries don't list a scope at all; we |
456 | assume it's a system battery if not specified. */ |
457 | if (read_power_file(base, name, "scope" , str, sizeof(str))) { |
458 | if (SDL_strcasecmp(str, "Device\n" ) == 0) { |
459 | continue; // skip external devices with their own batteries. |
460 | } |
461 | } |
462 | |
463 | // some drivers don't offer this, so if it's not explicitly reported assume it's present. |
464 | if (read_power_file(base, name, "present" , str, sizeof(str)) && (SDL_strcmp(str, "0\n" ) == 0)) { |
465 | st = SDL_POWERSTATE_NO_BATTERY; |
466 | } else if (!read_power_file(base, name, "status" , str, sizeof(str))) { |
467 | st = SDL_POWERSTATE_UNKNOWN; // uh oh |
468 | } else if (SDL_strcasecmp(str, "Charging\n" ) == 0) { |
469 | st = SDL_POWERSTATE_CHARGING; |
470 | } else if (SDL_strcasecmp(str, "Discharging\n" ) == 0) { |
471 | st = SDL_POWERSTATE_ON_BATTERY; |
472 | } else if ((SDL_strcasecmp(str, "Full\n" ) == 0) || (SDL_strcasecmp(str, "Not charging\n" ) == 0)) { |
473 | st = SDL_POWERSTATE_CHARGED; |
474 | } else { |
475 | st = SDL_POWERSTATE_UNKNOWN; // uh oh |
476 | } |
477 | |
478 | if (!read_power_file(base, name, "capacity" , str, sizeof(str))) { |
479 | pct = -1; |
480 | } else { |
481 | pct = SDL_atoi(str); |
482 | pct = (pct > 100) ? 100 : pct; // clamp between 0%, 100% |
483 | } |
484 | |
485 | if (read_power_file(base, name, "time_to_empty_now" , str, sizeof(str))) { |
486 | secs = SDL_atoi(str); |
487 | secs = (secs <= 0) ? -1 : secs; // 0 == unknown |
488 | } else if (st == SDL_POWERSTATE_ON_BATTERY) { |
489 | /* energy is Watt*hours and power is Watts */ |
490 | energy = (read_power_file(base, name, "energy_now" , str, sizeof(str))) ? SDL_atoi(str) : -1; |
491 | power = (read_power_file(base, name, "power_now" , str, sizeof(str))) ? SDL_atoi(str) : -1; |
492 | secs = (energy >= 0 && power > 0) ? (3600LL * energy) / power : -1; |
493 | } else { |
494 | secs = -1; |
495 | } |
496 | |
497 | /* |
498 | * We pick the battery that claims to have the most minutes left. |
499 | * (failing a report of minutes, we'll take the highest percent.) |
500 | */ |
501 | if ((secs < 0) && (*seconds < 0)) { |
502 | if ((pct < 0) && (*percent < 0)) { |
503 | choose = true; // at least we know there's a battery. |
504 | } else if (pct > *percent) { |
505 | choose = true; |
506 | } |
507 | } else if (secs > *seconds) { |
508 | choose = true; |
509 | } |
510 | |
511 | if (choose) { |
512 | *seconds = secs; |
513 | *percent = pct; |
514 | *state = st; |
515 | } |
516 | } |
517 | |
518 | closedir(dirp); |
519 | return true; // don't look any further. |
520 | } |
521 | |
522 | // d-bus queries to org.freedesktop.UPower. |
523 | #ifdef SDL_USE_LIBDBUS |
524 | #define UPOWER_DBUS_NODE "org.freedesktop.UPower" |
525 | #define UPOWER_DBUS_PATH "/org/freedesktop/UPower" |
526 | #define UPOWER_DBUS_INTERFACE "org.freedesktop.UPower" |
527 | #define UPOWER_DEVICE_DBUS_INTERFACE "org.freedesktop.UPower.Device" |
528 | |
529 | static void check_upower_device(DBusConnection *conn, const char *path, SDL_PowerState *state, int *seconds, int *percent) |
530 | { |
531 | bool choose = false; |
532 | SDL_PowerState st; |
533 | int secs; |
534 | int pct; |
535 | Uint32 ui32 = 0; |
536 | Sint64 si64 = 0; |
537 | double d = 0.0; |
538 | |
539 | if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "Type" , DBUS_TYPE_UINT32, &ui32)) { |
540 | return; // Don't know _what_ we're looking at. Give up on it. |
541 | } else if (ui32 != 2) { // 2==Battery |
542 | return; // we don't care about UPS and such. |
543 | } else if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "PowerSupply" , DBUS_TYPE_BOOLEAN, &ui32)) { |
544 | return; |
545 | } else if (!ui32) { |
546 | return; // we don't care about random devices with batteries, like wireless controllers, etc |
547 | } |
548 | |
549 | if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "IsPresent" , DBUS_TYPE_BOOLEAN, &ui32)) { |
550 | return; |
551 | } |
552 | if (!ui32) { |
553 | st = SDL_POWERSTATE_NO_BATTERY; |
554 | } else { |
555 | /* Get updated information on the battery status |
556 | * This can occasionally fail, and we'll just return slightly stale data in that case |
557 | */ |
558 | SDL_DBus_CallMethodOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "Refresh" , DBUS_TYPE_INVALID, DBUS_TYPE_INVALID); |
559 | |
560 | if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "State" , DBUS_TYPE_UINT32, &ui32)) { |
561 | st = SDL_POWERSTATE_UNKNOWN; // uh oh |
562 | } else if (ui32 == 1) { // 1 == charging |
563 | st = SDL_POWERSTATE_CHARGING; |
564 | } else if ((ui32 == 2) || (ui32 == 3) || (ui32 == 6)) { |
565 | /* 2 == discharging; |
566 | * 3 == empty; |
567 | * 6 == "pending discharge" which GNOME interprets as equivalent |
568 | * to discharging */ |
569 | st = SDL_POWERSTATE_ON_BATTERY; |
570 | } else if ((ui32 == 4) || (ui32 == 5)) { |
571 | /* 4 == full; |
572 | * 5 == "pending charge" which GNOME shows as "Not charging", |
573 | * used when a battery is configured to stop charging at a |
574 | * lower than 100% threshold */ |
575 | st = SDL_POWERSTATE_CHARGED; |
576 | } else { |
577 | st = SDL_POWERSTATE_UNKNOWN; // uh oh |
578 | } |
579 | } |
580 | |
581 | if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "Percentage" , DBUS_TYPE_DOUBLE, &d)) { |
582 | pct = -1; // some old/cheap batteries don't set this property. |
583 | } else { |
584 | pct = (int)d; |
585 | pct = (pct > 100) ? 100 : pct; // clamp between 0%, 100% |
586 | } |
587 | |
588 | if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "TimeToEmpty" , DBUS_TYPE_INT64, &si64)) { |
589 | secs = -1; |
590 | } else { |
591 | secs = (int)si64; |
592 | secs = (secs <= 0) ? -1 : secs; // 0 == unknown |
593 | } |
594 | |
595 | /* |
596 | * We pick the battery that claims to have the most minutes left. |
597 | * (failing a report of minutes, we'll take the highest percent.) |
598 | */ |
599 | if ((secs < 0) && (*seconds < 0)) { |
600 | if ((pct < 0) && (*percent < 0)) { |
601 | choose = true; // at least we know there's a battery. |
602 | } else if (pct > *percent) { |
603 | choose = true; |
604 | } |
605 | } else if (secs > *seconds) { |
606 | choose = true; |
607 | } |
608 | |
609 | if (choose) { |
610 | *seconds = secs; |
611 | *percent = pct; |
612 | *state = st; |
613 | } |
614 | } |
615 | #endif |
616 | |
617 | bool SDL_GetPowerInfo_Linux_org_freedesktop_upower(SDL_PowerState *state, int *seconds, int *percent) |
618 | { |
619 | bool result = false; |
620 | |
621 | #ifdef SDL_USE_LIBDBUS |
622 | SDL_DBusContext *dbus = SDL_DBus_GetContext(); |
623 | char **paths = NULL; |
624 | int i, numpaths = 0; |
625 | |
626 | if (!dbus || !SDL_DBus_CallMethodOnConnection(dbus->system_conn, UPOWER_DBUS_NODE, UPOWER_DBUS_PATH, UPOWER_DBUS_INTERFACE, "EnumerateDevices" , |
627 | DBUS_TYPE_INVALID, |
628 | DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &paths, &numpaths, DBUS_TYPE_INVALID)) { |
629 | return false; // try a different approach than UPower. |
630 | } |
631 | |
632 | result = true; // Clearly we can use this interface. |
633 | *state = SDL_POWERSTATE_NO_BATTERY; // assume we're just plugged in. |
634 | *seconds = -1; |
635 | *percent = -1; |
636 | |
637 | for (i = 0; i < numpaths; i++) { |
638 | check_upower_device(dbus->system_conn, paths[i], state, seconds, percent); |
639 | } |
640 | |
641 | dbus->free_string_array(paths); |
642 | #endif // SDL_USE_LIBDBUS |
643 | |
644 | return result; |
645 | } |
646 | |
647 | #endif // SDL_POWER_LINUX |
648 | #endif // SDL_POWER_DISABLED |
649 | |