SDL  2.0
SDL_syspower.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2016 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 static const char *sys_class_power_supply_path = "/sys/class/power_supply";
40 
41 static int
42 open_power_file(const char *base, const char *node, const char *key)
43 {
44  const size_t pathlen = strlen(base) + strlen(node) + strlen(key) + 3;
45  char *path = (char *) alloca(pathlen);
46  if (path == NULL) {
47  return -1; /* oh well. */
48  }
49 
50  snprintf(path, pathlen, "%s/%s/%s", base, node, key);
51  return open(path, O_RDONLY);
52 }
53 
54 
55 static SDL_bool
56 read_power_file(const char *base, const char *node, const char *key,
57  char *buf, size_t buflen)
58 {
59  ssize_t br = 0;
60  const int fd = open_power_file(base, node, key);
61  if (fd == -1) {
62  return SDL_FALSE;
63  }
64  br = read(fd, buf, buflen-1);
65  close(fd);
66  if (br < 0) {
67  return SDL_FALSE;
68  }
69  buf[br] = '\0'; /* null-terminate the string. */
70  return SDL_TRUE;
71 }
72 
73 
74 static SDL_bool
75 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 SDL_FALSE; /* EOF. */
85  }
86 
87  *_key = ptr;
88 
89  while ((*ptr != ':') && (*ptr != '\0')) {
90  ptr++;
91  }
92 
93  if (*ptr == '\0') {
94  return SDL_FALSE; /* (unexpected) EOF. */
95  }
96 
97  *(ptr++) = '\0'; /* terminate the key. */
98 
99  while ((*ptr == ' ') && (*ptr != '\0')) {
100  ptr++; /* skip whitespace. */
101  }
102 
103  if (*ptr == '\0') {
104  return SDL_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 SDL_TRUE;
119 }
120 
121 static void
122 check_proc_acpi_battery(const char * node, SDL_bool * have_battery,
123  SDL_bool * charging, int *seconds, int *percent)
124 {
125  const char *base = proc_acpi_battery_path;
126  char info[1024];
127  char state[1024];
128  char *ptr = NULL;
129  char *key = NULL;
130  char *val = NULL;
131  SDL_bool charge = SDL_FALSE;
132  SDL_bool choose = SDL_FALSE;
133  int maximum = -1;
134  int remaining = -1;
135  int secs = -1;
136  int pct = -1;
137 
138  if (!read_power_file(base, node, "state", state, sizeof (state))) {
139  return;
140  } else if (!read_power_file(base, node, "info", info, sizeof (info))) {
141  return;
142  }
143 
144  ptr = &state[0];
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;
149  }
150  } else if (strcmp(key, "charging state") == 0) {
151  /* !!! FIXME: what exactly _does_ charging/discharging mean? */
152  if (strcmp(val, "charging/discharging") == 0) {
153  charge = SDL_TRUE;
154  } else if (strcmp(val, "charging") == 0) {
155  charge = SDL_TRUE;
156  }
157  } else if (strcmp(key, "remaining capacity") == 0) {
158  char *endptr = NULL;
159  const int cvt = (int) strtol(val, &endptr, 10);
160  if (*endptr == ' ') {
161  remaining = cvt;
162  }
163  }
164  }
165 
166  ptr = &info[0];
167  while (make_proc_acpi_key_val(&ptr, &key, &val)) {
168  if (strcmp(key, "design capacity") == 0) {
169  char *endptr = NULL;
170  const int cvt = (int) strtol(val, &endptr, 10);
171  if (*endptr == ' ') {
172  maximum = cvt;
173  }
174  }
175  }
176 
177  if ((maximum >= 0) && (remaining >= 0)) {
178  pct = (int) ((((float) remaining) / ((float) maximum)) * 100.0f);
179  if (pct < 0) {
180  pct = 0;
181  } else if (pct > 100) {
182  pct = 100;
183  }
184  }
185 
186  /* !!! FIXME: calculate (secs). */
187 
188  /*
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.)
191  */
192  if ((secs < 0) && (*seconds < 0)) {
193  if ((pct < 0) && (*percent < 0)) {
194  choose = SDL_TRUE; /* at least we know there's a battery. */
195  }
196  if (pct > *percent) {
197  choose = SDL_TRUE;
198  }
199  } else if (secs > *seconds) {
200  choose = SDL_TRUE;
201  }
202 
203  if (choose) {
204  *seconds = secs;
205  *percent = pct;
206  *charging = charge;
207  }
208 }
209 
210 static void
211 check_proc_acpi_ac_adapter(const char * node, SDL_bool * have_ac)
212 {
213  const char *base = proc_acpi_ac_adapter_path;
214  char state[256];
215  char *ptr = NULL;
216  char *key = NULL;
217  char *val = NULL;
218 
219  if (!read_power_file(base, node, "state", state, sizeof (state))) {
220  return;
221  }
222 
223  ptr = &state[0];
224  while (make_proc_acpi_key_val(&ptr, &key, &val)) {
225  if (strcmp(key, "state") == 0) {
226  if (strcmp(val, "on-line") == 0) {
227  *have_ac = SDL_TRUE;
228  }
229  }
230  }
231 }
232 
233 
234 SDL_bool
236  int *seconds, int *percent)
237 {
238  struct dirent *dent = NULL;
239  DIR *dirp = NULL;
240  SDL_bool have_battery = SDL_FALSE;
241  SDL_bool have_ac = SDL_FALSE;
242  SDL_bool charging = SDL_FALSE;
243 
244  *seconds = -1;
245  *percent = -1;
246  *state = SDL_POWERSTATE_UNKNOWN;
247 
248  dirp = opendir(proc_acpi_battery_path);
249  if (dirp == NULL) {
250  return SDL_FALSE; /* can't use this interface. */
251  } else {
252  while ((dent = readdir(dirp)) != NULL) {
253  const char *node = dent->d_name;
254  check_proc_acpi_battery(node, &have_battery, &charging,
255  seconds, percent);
256  }
257  closedir(dirp);
258  }
259 
260  dirp = opendir(proc_acpi_ac_adapter_path);
261  if (dirp == NULL) {
262  return SDL_FALSE; /* can't use this interface. */
263  } else {
264  while ((dent = readdir(dirp)) != NULL) {
265  const char *node = dent->d_name;
266  check_proc_acpi_ac_adapter(node, &have_ac);
267  }
268  closedir(dirp);
269  }
270 
271  if (!have_battery) {
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;
277  } else {
278  *state = SDL_POWERSTATE_ON_BATTERY;
279  }
280 
281  return SDL_TRUE; /* definitive answer. */
282 }
283 
284 
285 static SDL_bool
286 next_string(char **_ptr, char **_str)
287 {
288  char *ptr = *_ptr;
289  char *str = *_str;
290 
291  while (*ptr == ' ') { /* skip any spaces... */
292  ptr++;
293  }
294 
295  if (*ptr == '\0') {
296  return SDL_FALSE;
297  }
298 
299  str = ptr;
300  while ((*ptr != ' ') && (*ptr != '\n') && (*ptr != '\0'))
301  ptr++;
302 
303  if (*ptr != '\0')
304  *(ptr++) = '\0';
305 
306  *_str = str;
307  *_ptr = ptr;
308  return SDL_TRUE;
309 }
310 
311 static SDL_bool
312 int_string(char *str, int *val)
313 {
314  char *endptr = NULL;
315  *val = (int) strtol(str, &endptr, 0);
316  return ((*str != '\0') && (*endptr == '\0'));
317 }
318 
319 /* http://lxr.linux.no/linux+v2.6.29/drivers/char/apm-emulation.c */
320 SDL_bool
322  int *seconds, int *percent)
323 {
324  SDL_bool need_details = SDL_FALSE;
325  int ac_status = 0;
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);
331  char buf[128];
332  char *ptr = &buf[0];
333  char *str = NULL;
334  ssize_t br;
335 
336  if (fd == -1) {
337  return SDL_FALSE; /* can't use this interface. */
338  }
339 
340  br = read(fd, buf, sizeof (buf) - 1);
341  close(fd);
342 
343  if (br < 0) {
344  return SDL_FALSE;
345  }
346 
347  buf[br] = '\0'; /* null-terminate the string. */
348  if (!next_string(&ptr, &str)) { /* driver version */
349  return SDL_FALSE;
350  }
351  if (!next_string(&ptr, &str)) { /* BIOS version */
352  return SDL_FALSE;
353  }
354  if (!next_string(&ptr, &str)) { /* APM flags */
355  return SDL_FALSE;
356  }
357 
358  if (!next_string(&ptr, &str)) { /* AC line status */
359  return SDL_FALSE;
360  } else if (!int_string(str, &ac_status)) {
361  return SDL_FALSE;
362  }
363 
364  if (!next_string(&ptr, &str)) { /* battery status */
365  return SDL_FALSE;
366  } else if (!int_string(str, &battery_status)) {
367  return SDL_FALSE;
368  }
369  if (!next_string(&ptr, &str)) { /* battery flag */
370  return SDL_FALSE;
371  } else if (!int_string(str, &battery_flag)) {
372  return SDL_FALSE;
373  }
374  if (!next_string(&ptr, &str)) { /* remaining battery life percent */
375  return SDL_FALSE;
376  }
377  if (str[strlen(str) - 1] == '%') {
378  str[strlen(str) - 1] = '\0';
379  }
380  if (!int_string(str, &battery_percent)) {
381  return SDL_FALSE;
382  }
383 
384  if (!next_string(&ptr, &str)) { /* remaining battery life time */
385  return SDL_FALSE;
386  } else if (!int_string(str, &battery_time)) {
387  return SDL_FALSE;
388  }
389 
390  if (!next_string(&ptr, &str)) { /* remaining battery life time units */
391  return SDL_FALSE;
392  } else if (strcmp(str, "min") == 0) {
393  battery_time *= 60;
394  }
395 
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;
406  } else {
407  *state = SDL_POWERSTATE_ON_BATTERY;
408  need_details = SDL_TRUE;
409  }
410 
411  *percent = -1;
412  *seconds = -1;
413  if (need_details) {
414  const int pct = battery_percent;
415  const int secs = battery_time;
416 
417  if (pct >= 0) { /* -1 == unknown */
418  *percent = (pct > 100) ? 100 : pct; /* clamp between 0%, 100% */
419  }
420  if (secs >= 0) { /* -1 == unknown */
421  *seconds = secs;
422  }
423  }
424 
425  return SDL_TRUE;
426 }
427 
428 /* !!! FIXME: implement d-bus queries to org.freedesktop.UPower. */
429 
430 SDL_bool
431 SDL_GetPowerInfo_Linux_sys_class_power_supply(SDL_PowerState *state, int *seconds, int *percent)
432 {
433  const char *base = sys_class_power_supply_path;
434  struct dirent *dent;
435  DIR *dirp;
436 
437  dirp = opendir(base);
438  if (!dirp) {
439  return SDL_FALSE;
440  }
441 
442  *state = SDL_POWERSTATE_NO_BATTERY; /* assume we're just plugged in. */
443  *seconds = -1;
444  *percent = -1;
445 
446  while ((dent = readdir(dirp)) != NULL) {
447  const char *name = dent->d_name;
448  SDL_bool choose = SDL_FALSE;
449  char str[64];
450  SDL_PowerState st;
451  int secs;
452  int pct;
453 
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. */
460  }
461 
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)) {
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) {
469  } else if (SDL_strcmp(str, "Discharging\n") == 0) {
471  } else if ((SDL_strcmp(str, "Full\n") == 0) || (SDL_strcmp(str, "Not charging\n") == 0)) {
473  } else {
474  st = SDL_POWERSTATE_UNKNOWN; /* uh oh */
475  }
476 
477  if (!read_power_file(base, name, "capacity", str, sizeof (str))) {
478  pct = -1;
479  } else {
480  pct = SDL_atoi(str);
481  pct = (pct > 100) ? 100 : pct; /* clamp between 0%, 100% */
482  }
483 
484  if (!read_power_file(base, name, "time_to_empty_now", str, sizeof (str))) {
485  secs = -1;
486  } else {
487  secs = SDL_atoi(str);
488  secs = (secs <= 0) ? -1 : secs; /* 0 == unknown */
489  }
490 
491  /*
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.)
494  */
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) {
499  choose = SDL_TRUE;
500  }
501  } else if (secs > *seconds) {
502  choose = SDL_TRUE;
503  }
504 
505  if (choose) {
506  *seconds = secs;
507  *percent = pct;
508  *state = st;
509  }
510  }
511 
512  closedir(dirp);
513  return SDL_TRUE; /* don't look any further. */
514 }
515 
516 #endif /* SDL_POWER_LINUX */
517 #endif /* SDL_POWER_DISABLED */
518 
519 /* vi: set ts=4 sw=4 expandtab: */
struct xkb_state * state
SDL_bool SDL_GetPowerInfo_Linux_proc_apm(SDL_PowerState *, int *, int *)
SDL_bool SDL_GetPowerInfo_Linux_sys_class_power_supply(SDL_PowerState *, int *, int *)
GLuint const GLchar * name
GLuint GLfloat * val
GLenum GLuint GLenum GLsizei const GLchar * buf
#define SDL_atoi
#define NULL
Definition: begin_code.h:143
SDL_bool
Definition: SDL_stdinc.h:130
SDL_PowerState
The basic state for the system&#39;s power supply.
Definition: SDL_power.h:42
GLsizei const GLchar *const * path
#define SDL_strcmp
SDL_bool SDL_GetPowerInfo_Linux_proc_acpi(SDL_PowerState *, int *, int *)