1 | /* |
2 | Simple DirectMedia Layer |
3 | Copyright (C) 1997-2021 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 | |
39 | static const char *proc_apm_path = "/proc/apm" ; |
40 | static const char *proc_acpi_battery_path = "/proc/acpi/battery" ; |
41 | static const char *proc_acpi_ac_adapter_path = "/proc/acpi/ac_adapter" ; |
42 | static const char *sys_class_power_supply_path = "/sys/class/power_supply" ; |
43 | |
44 | static int |
45 | open_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 | |
58 | static SDL_bool |
59 | read_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 | |
77 | static SDL_bool |
78 | make_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 == ' ') { |
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 | |
124 | static void |
125 | check_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 | |
213 | static void |
214 | check_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 | |
237 | SDL_bool |
238 | SDL_GetPowerInfo_Linux_proc_acpi(SDL_PowerState * state, |
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; |
249 | *state = SDL_POWERSTATE_UNKNOWN; |
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) { |
275 | *state = SDL_POWERSTATE_NO_BATTERY; |
276 | } else if (charging) { |
277 | *state = SDL_POWERSTATE_CHARGING; |
278 | } else if (have_ac) { |
279 | *state = SDL_POWERSTATE_CHARGED; |
280 | } else { |
281 | *state = SDL_POWERSTATE_ON_BATTERY; |
282 | } |
283 | |
284 | return SDL_TRUE; /* definitive answer. */ |
285 | } |
286 | |
287 | |
288 | static SDL_bool |
289 | next_string(char **_ptr, char **_str) |
290 | { |
291 | char *ptr = *_ptr; |
292 | char *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 | |
314 | static SDL_bool |
315 | int_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 */ |
323 | SDL_bool |
324 | SDL_GetPowerInfo_Linux_proc_apm(SDL_PowerState * state, |
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 */ |
400 | *state = SDL_POWERSTATE_UNKNOWN; |
401 | } else if (battery_flag & (1 << 7)) { /* no battery */ |
402 | *state = SDL_POWERSTATE_NO_BATTERY; |
403 | } else if (battery_flag & (1 << 3)) { /* charging */ |
404 | *state = SDL_POWERSTATE_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 { |
410 | *state = SDL_POWERSTATE_ON_BATTERY; |
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 | |
431 | SDL_bool |
432 | SDL_GetPowerInfo_Linux_sys_class_power_supply(SDL_PowerState *state, int *seconds, int *percent) |
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]; |
451 | SDL_PowerState st; |
452 | int secs; |
453 | int pct; |
454 | int energy; |
455 | int power; |
456 | |
457 | if ((SDL_strcmp(name, "." ) == 0) || (SDL_strcmp(name, ".." ) == 0)) { |
458 | continue; /* skip these, of course. */ |
459 | } else if (!read_power_file(base, name, "type" , str, sizeof (str))) { |
460 | continue; /* Don't know _what_ we're looking at. Give up on it. */ |
461 | } else if (SDL_strcmp(str, "Battery\n" ) != 0) { |
462 | continue; /* we don't care about UPS and such. */ |
463 | } |
464 | |
465 | /* if the scope is "device," it might be something like a PS4 |
466 | controller reporting its own battery, and not something that powers |
467 | the system. Most system batteries don't list a scope at all; we |
468 | assume it's a system battery if not specified. */ |
469 | if (read_power_file(base, name, "scope" , str, sizeof (str))) { |
470 | if (SDL_strcmp(str, "device\n" ) == 0) { |
471 | continue; /* skip external devices with their own batteries. */ |
472 | } |
473 | } |
474 | |
475 | /* some drivers don't offer this, so if it's not explicitly reported assume it's present. */ |
476 | if (read_power_file(base, name, "present" , str, sizeof (str)) && (SDL_strcmp(str, "0\n" ) == 0)) { |
477 | st = SDL_POWERSTATE_NO_BATTERY; |
478 | } else if (!read_power_file(base, name, "status" , str, sizeof (str))) { |
479 | st = SDL_POWERSTATE_UNKNOWN; /* uh oh */ |
480 | } else if (SDL_strcmp(str, "Charging\n" ) == 0) { |
481 | st = SDL_POWERSTATE_CHARGING; |
482 | } else if (SDL_strcmp(str, "Discharging\n" ) == 0) { |
483 | st = SDL_POWERSTATE_ON_BATTERY; |
484 | } else if ((SDL_strcmp(str, "Full\n" ) == 0) || (SDL_strcmp(str, "Not charging\n" ) == 0)) { |
485 | st = SDL_POWERSTATE_CHARGED; |
486 | } else { |
487 | st = SDL_POWERSTATE_UNKNOWN; /* uh oh */ |
488 | } |
489 | |
490 | if (!read_power_file(base, name, "capacity" , str, sizeof (str))) { |
491 | pct = -1; |
492 | } else { |
493 | pct = SDL_atoi(str); |
494 | pct = (pct > 100) ? 100 : pct; /* clamp between 0%, 100% */ |
495 | } |
496 | |
497 | if (read_power_file(base, name, "time_to_empty_now" , str, sizeof (str))) { |
498 | secs = SDL_atoi(str); |
499 | secs = (secs <= 0) ? -1 : secs; /* 0 == unknown */ |
500 | } else if (st == SDL_POWERSTATE_ON_BATTERY) { |
501 | /* energy is Watt*hours and power is Watts */ |
502 | energy = (read_power_file(base, name, "energy_now" , str, sizeof (str))) ? SDL_atoi(str) : -1; |
503 | power = (read_power_file(base, name, "power_now" , str, sizeof (str))) ? SDL_atoi(str) : -1; |
504 | secs = (energy >= 0 && power > 0) ? (3600LL * energy) / power : -1; |
505 | } else { |
506 | secs = -1; |
507 | } |
508 | |
509 | /* |
510 | * We pick the battery that claims to have the most minutes left. |
511 | * (failing a report of minutes, we'll take the highest percent.) |
512 | */ |
513 | if ((secs < 0) && (*seconds < 0)) { |
514 | if ((pct < 0) && (*percent < 0)) { |
515 | choose = SDL_TRUE; /* at least we know there's a battery. */ |
516 | } else if (pct > *percent) { |
517 | choose = SDL_TRUE; |
518 | } |
519 | } else if (secs > *seconds) { |
520 | choose = SDL_TRUE; |
521 | } |
522 | |
523 | if (choose) { |
524 | *seconds = secs; |
525 | *percent = pct; |
526 | *state = st; |
527 | } |
528 | } |
529 | |
530 | closedir(dirp); |
531 | return SDL_TRUE; /* don't look any further. */ |
532 | } |
533 | |
534 | |
535 | /* d-bus queries to org.freedesktop.UPower. */ |
536 | #if SDL_USE_LIBDBUS |
537 | #define UPOWER_DBUS_NODE "org.freedesktop.UPower" |
538 | #define UPOWER_DBUS_PATH "/org/freedesktop/UPower" |
539 | #define UPOWER_DBUS_INTERFACE "org.freedesktop.UPower" |
540 | #define UPOWER_DEVICE_DBUS_INTERFACE "org.freedesktop.UPower.Device" |
541 | |
542 | static void |
543 | check_upower_device(DBusConnection *conn, const char *path, SDL_PowerState *state, int *seconds, int *percent) |
544 | { |
545 | SDL_bool choose = SDL_FALSE; |
546 | SDL_PowerState st; |
547 | int secs; |
548 | int pct; |
549 | Uint32 ui32 = 0; |
550 | Sint64 si64 = 0; |
551 | double d = 0.0; |
552 | |
553 | if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "Type" , DBUS_TYPE_UINT32, &ui32)) { |
554 | return; /* Don't know _what_ we're looking at. Give up on it. */ |
555 | } else if (ui32 != 2) { /* 2==Battery*/ |
556 | return; /* we don't care about UPS and such. */ |
557 | } else if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "PowerSupply" , DBUS_TYPE_BOOLEAN, &ui32)) { |
558 | return; |
559 | } else if (!ui32) { |
560 | return; /* we don't care about random devices with batteries, like wireless controllers, etc */ |
561 | } else if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "IsPresent" , DBUS_TYPE_BOOLEAN, &ui32)) { |
562 | return; |
563 | } else if (!ui32) { |
564 | st = SDL_POWERSTATE_NO_BATTERY; |
565 | } else if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "State" , DBUS_TYPE_UINT32, &ui32)) { |
566 | st = SDL_POWERSTATE_UNKNOWN; /* uh oh */ |
567 | } else if (ui32 == 1) { /* 1 == charging */ |
568 | st = SDL_POWERSTATE_CHARGING; |
569 | } else if ((ui32 == 2) || (ui32 == 3)) { /* 2 == discharging, 3 == empty. */ |
570 | st = SDL_POWERSTATE_ON_BATTERY; |
571 | } else if (ui32 == 4) { /* 4 == full */ |
572 | st = SDL_POWERSTATE_CHARGED; |
573 | } else { |
574 | st = SDL_POWERSTATE_UNKNOWN; /* uh oh */ |
575 | } |
576 | |
577 | if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "Percentage" , DBUS_TYPE_DOUBLE, &d)) { |
578 | pct = -1; /* some old/cheap batteries don't set this property. */ |
579 | } else { |
580 | pct = (int) d; |
581 | pct = (pct > 100) ? 100 : pct; /* clamp between 0%, 100% */ |
582 | } |
583 | |
584 | if (!SDL_DBus_QueryPropertyOnConnection(conn, UPOWER_DBUS_NODE, path, UPOWER_DEVICE_DBUS_INTERFACE, "TimeToEmpty" , DBUS_TYPE_INT64, &si64)) { |
585 | secs = -1; |
586 | } else { |
587 | secs = (int) si64; |
588 | secs = (secs <= 0) ? -1 : secs; /* 0 == unknown */ |
589 | } |
590 | |
591 | /* |
592 | * We pick the battery that claims to have the most minutes left. |
593 | * (failing a report of minutes, we'll take the highest percent.) |
594 | */ |
595 | if ((secs < 0) && (*seconds < 0)) { |
596 | if ((pct < 0) && (*percent < 0)) { |
597 | choose = SDL_TRUE; /* at least we know there's a battery. */ |
598 | } else if (pct > *percent) { |
599 | choose = SDL_TRUE; |
600 | } |
601 | } else if (secs > *seconds) { |
602 | choose = SDL_TRUE; |
603 | } |
604 | |
605 | if (choose) { |
606 | *seconds = secs; |
607 | *percent = pct; |
608 | *state = st; |
609 | } |
610 | } |
611 | #endif |
612 | |
613 | SDL_bool |
614 | SDL_GetPowerInfo_Linux_org_freedesktop_upower(SDL_PowerState *state, int *seconds, int *percent) |
615 | { |
616 | SDL_bool retval = SDL_FALSE; |
617 | |
618 | #if SDL_USE_LIBDBUS |
619 | SDL_DBusContext *dbus = SDL_DBus_GetContext(); |
620 | char **paths = NULL; |
621 | int i, numpaths = 0; |
622 | |
623 | if (!dbus || !SDL_DBus_CallMethodOnConnection(dbus->system_conn, UPOWER_DBUS_NODE, UPOWER_DBUS_PATH, UPOWER_DBUS_INTERFACE, "EnumerateDevices" , |
624 | DBUS_TYPE_INVALID, |
625 | DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &paths, &numpaths, DBUS_TYPE_INVALID)) { |
626 | return SDL_FALSE; /* try a different approach than UPower. */ |
627 | } |
628 | |
629 | retval = SDL_TRUE; /* Clearly we can use this interface. */ |
630 | *state = SDL_POWERSTATE_NO_BATTERY; /* assume we're just plugged in. */ |
631 | *seconds = -1; |
632 | *percent = -1; |
633 | |
634 | for (i = 0; i < numpaths; i++) { |
635 | check_upower_device(dbus->system_conn, paths[i], state, seconds, percent); |
636 | } |
637 | |
638 | if (dbus) { |
639 | dbus->free_string_array(paths); |
640 | } |
641 | #endif /* SDL_USE_LIBDBUS */ |
642 | |
643 | return retval; |
644 | } |
645 | |
646 | #endif /* SDL_POWER_LINUX */ |
647 | #endif /* SDL_POWER_DISABLED */ |
648 | |
649 | /* vi: set ts=4 sw=4 expandtab: */ |
650 | |