SDL_syspower.c (11469B)
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2014 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 36static const char *proc_apm_path = "/proc/apm"; 37static const char *proc_acpi_battery_path = "/proc/acpi/battery"; 38static const char *proc_acpi_ac_adapter_path = "/proc/acpi/ac_adapter"; 39 40static int open_acpi_file(const char *base, const char *node, const char *key) 41{ 42 const size_t pathlen = strlen(base) + strlen(node) + strlen(key) + 3; 43 char *path = (char *) alloca(pathlen); 44 if (path == NULL) { 45 return -1; /* oh well. */ 46 } 47 48 snprintf(path, pathlen, "%s/%s/%s", base, node, key); 49 return open(path, O_RDONLY); 50} 51 52 53static SDL_bool 54load_acpi_file(const char *base, const char *node, const char *key, 55 char *buf, size_t buflen) 56{ 57 ssize_t br = 0; 58 const int fd = open_acpi_file(base, node, key); 59 if (fd == -1) { 60 return SDL_FALSE; 61 } 62 br = read(fd, buf, buflen-1); 63 close(fd); 64 if (br < 0) { 65 return SDL_FALSE; 66 } 67 buf[br] = '\0'; /* null-terminate the string. */ 68 return SDL_TRUE; 69} 70 71 72static SDL_bool 73make_proc_acpi_key_val(char **_ptr, char **_key, char **_val) 74{ 75 char *ptr = *_ptr; 76 77 while (*ptr == ' ') { 78 ptr++; /* skip whitespace. */ 79 } 80 81 if (*ptr == '\0') { 82 return SDL_FALSE; /* EOF. */ 83 } 84 85 *_key = ptr; 86 87 while ((*ptr != ':') && (*ptr != '\0')) { 88 ptr++; 89 } 90 91 if (*ptr == '\0') { 92 return SDL_FALSE; /* (unexpected) EOF. */ 93 } 94 95 *(ptr++) = '\0'; /* terminate the key. */ 96 97 while ((*ptr == ' ') && (*ptr != '\0')) { 98 ptr++; /* skip whitespace. */ 99 } 100 101 if (*ptr == '\0') { 102 return SDL_FALSE; /* (unexpected) EOF. */ 103 } 104 105 *_val = ptr; 106 107 while ((*ptr != '\n') && (*ptr != '\0')) { 108 ptr++; 109 } 110 111 if (*ptr != '\0') { 112 *(ptr++) = '\0'; /* terminate the value. */ 113 } 114 115 *_ptr = ptr; /* store for next time. */ 116 return SDL_TRUE; 117} 118 119static void 120check_proc_acpi_battery(const char * node, SDL_bool * have_battery, 121 SDL_bool * charging, int *seconds, int *percent) 122{ 123 const char *base = proc_acpi_battery_path; 124 char info[1024]; 125 char state[1024]; 126 char *ptr = NULL; 127 char *key = NULL; 128 char *val = NULL; 129 SDL_bool charge = SDL_FALSE; 130 SDL_bool choose = SDL_FALSE; 131 int maximum = -1; 132 int remaining = -1; 133 int secs = -1; 134 int pct = -1; 135 136 if (!load_acpi_file(base, node, "state", state, sizeof (state))) { 137 return; 138 } else if (!load_acpi_file(base, node, "info", info, sizeof (info))) { 139 return; 140 } 141 142 ptr = &state[0]; 143 while (make_proc_acpi_key_val(&ptr, &key, &val)) { 144 if (strcmp(key, "present") == 0) { 145 if (strcmp(val, "yes") == 0) { 146 *have_battery = SDL_TRUE; 147 } 148 } else if (strcmp(key, "charging state") == 0) { 149 /* !!! FIXME: what exactly _does_ charging/discharging mean? */ 150 if (strcmp(val, "charging/discharging") == 0) { 151 charge = SDL_TRUE; 152 } else if (strcmp(val, "charging") == 0) { 153 charge = SDL_TRUE; 154 } 155 } else if (strcmp(key, "remaining capacity") == 0) { 156 char *endptr = NULL; 157 const int cvt = (int) strtol(val, &endptr, 10); 158 if (*endptr == ' ') { 159 remaining = cvt; 160 } 161 } 162 } 163 164 ptr = &info[0]; 165 while (make_proc_acpi_key_val(&ptr, &key, &val)) { 166 if (strcmp(key, "design capacity") == 0) { 167 char *endptr = NULL; 168 const int cvt = (int) strtol(val, &endptr, 10); 169 if (*endptr == ' ') { 170 maximum = cvt; 171 } 172 } 173 } 174 175 if ((maximum >= 0) && (remaining >= 0)) { 176 pct = (int) ((((float) remaining) / ((float) maximum)) * 100.0f); 177 if (pct < 0) { 178 pct = 0; 179 } else if (pct > 100) { 180 pct = 100; 181 } 182 } 183 184 /* !!! FIXME: calculate (secs). */ 185 186 /* 187 * We pick the battery that claims to have the most minutes left. 188 * (failing a report of minutes, we'll take the highest percent.) 189 */ 190 if ((secs < 0) && (*seconds < 0)) { 191 if ((pct < 0) && (*percent < 0)) { 192 choose = SDL_TRUE; /* at least we know there's a battery. */ 193 } 194 if (pct > *percent) { 195 choose = SDL_TRUE; 196 } 197 } else if (secs > *seconds) { 198 choose = SDL_TRUE; 199 } 200 201 if (choose) { 202 *seconds = secs; 203 *percent = pct; 204 *charging = charge; 205 } 206} 207 208static void 209check_proc_acpi_ac_adapter(const char * node, SDL_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 (!load_acpi_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 (strcmp(key, "state") == 0) { 224 if (strcmp(val, "on-line") == 0) { 225 *have_ac = SDL_TRUE; 226 } 227 } 228 } 229} 230 231 232SDL_bool 233SDL_GetPowerInfo_Linux_proc_acpi(SDL_PowerState * state, 234 int *seconds, int *percent) 235{ 236 struct dirent *dent = NULL; 237 DIR *dirp = NULL; 238 SDL_bool have_battery = SDL_FALSE; 239 SDL_bool have_ac = SDL_FALSE; 240 SDL_bool charging = SDL_FALSE; 241 242 *seconds = -1; 243 *percent = -1; 244 *state = SDL_POWERSTATE_UNKNOWN; 245 246 dirp = opendir(proc_acpi_battery_path); 247 if (dirp == NULL) { 248 return SDL_FALSE; /* can't use this interface. */ 249 } else { 250 while ((dent = readdir(dirp)) != NULL) { 251 const char *node = dent->d_name; 252 check_proc_acpi_battery(node, &have_battery, &charging, 253 seconds, percent); 254 } 255 closedir(dirp); 256 } 257 258 dirp = opendir(proc_acpi_ac_adapter_path); 259 if (dirp == NULL) { 260 return SDL_FALSE; /* can't use this interface. */ 261 } else { 262 while ((dent = readdir(dirp)) != NULL) { 263 const char *node = dent->d_name; 264 check_proc_acpi_ac_adapter(node, &have_ac); 265 } 266 closedir(dirp); 267 } 268 269 if (!have_battery) { 270 *state = SDL_POWERSTATE_NO_BATTERY; 271 } else if (charging) { 272 *state = SDL_POWERSTATE_CHARGING; 273 } else if (have_ac) { 274 *state = SDL_POWERSTATE_CHARGED; 275 } else { 276 *state = SDL_POWERSTATE_ON_BATTERY; 277 } 278 279 return SDL_TRUE; /* definitive answer. */ 280} 281 282 283static SDL_bool 284next_string(char **_ptr, char **_str) 285{ 286 char *ptr = *_ptr; 287 char *str = *_str; 288 289 while (*ptr == ' ') { /* skip any spaces... */ 290 ptr++; 291 } 292 293 if (*ptr == '\0') { 294 return SDL_FALSE; 295 } 296 297 str = ptr; 298 while ((*ptr != ' ') && (*ptr != '\n') && (*ptr != '\0')) 299 ptr++; 300 301 if (*ptr != '\0') 302 *(ptr++) = '\0'; 303 304 *_str = str; 305 *_ptr = ptr; 306 return SDL_TRUE; 307} 308 309static SDL_bool 310int_string(char *str, int *val) 311{ 312 char *endptr = NULL; 313 *val = (int) strtol(str, &endptr, 0); 314 return ((*str != '\0') && (*endptr == '\0')); 315} 316 317/* http://lxr.linux.no/linux+v2.6.29/drivers/char/apm-emulation.c */ 318SDL_bool 319SDL_GetPowerInfo_Linux_proc_apm(SDL_PowerState * state, 320 int *seconds, int *percent) 321{ 322 SDL_bool need_details = SDL_FALSE; 323 int ac_status = 0; 324 int battery_status = 0; 325 int battery_flag = 0; 326 int battery_percent = 0; 327 int battery_time = 0; 328 const int fd = open(proc_apm_path, O_RDONLY); 329 char buf[128]; 330 char *ptr = &buf[0]; 331 char *str = NULL; 332 ssize_t br; 333 334 if (fd == -1) { 335 return SDL_FALSE; /* can't use this interface. */ 336 } 337 338 br = read(fd, buf, sizeof (buf) - 1); 339 close(fd); 340 341 if (br < 0) { 342 return SDL_FALSE; 343 } 344 345 buf[br] = '\0'; /* null-terminate the string. */ 346 if (!next_string(&ptr, &str)) { /* driver version */ 347 return SDL_FALSE; 348 } 349 if (!next_string(&ptr, &str)) { /* BIOS version */ 350 return SDL_FALSE; 351 } 352 if (!next_string(&ptr, &str)) { /* APM flags */ 353 return SDL_FALSE; 354 } 355 356 if (!next_string(&ptr, &str)) { /* AC line status */ 357 return SDL_FALSE; 358 } else if (!int_string(str, &ac_status)) { 359 return SDL_FALSE; 360 } 361 362 if (!next_string(&ptr, &str)) { /* battery status */ 363 return SDL_FALSE; 364 } else if (!int_string(str, &battery_status)) { 365 return SDL_FALSE; 366 } 367 if (!next_string(&ptr, &str)) { /* battery flag */ 368 return SDL_FALSE; 369 } else if (!int_string(str, &battery_flag)) { 370 return SDL_FALSE; 371 } 372 if (!next_string(&ptr, &str)) { /* remaining battery life percent */ 373 return SDL_FALSE; 374 } 375 if (str[strlen(str) - 1] == '%') { 376 str[strlen(str) - 1] = '\0'; 377 } 378 if (!int_string(str, &battery_percent)) { 379 return SDL_FALSE; 380 } 381 382 if (!next_string(&ptr, &str)) { /* remaining battery life time */ 383 return SDL_FALSE; 384 } else if (!int_string(str, &battery_time)) { 385 return SDL_FALSE; 386 } 387 388 if (!next_string(&ptr, &str)) { /* remaining battery life time units */ 389 return SDL_FALSE; 390 } else if (strcmp(str, "min") == 0) { 391 battery_time *= 60; 392 } 393 394 if (battery_flag == 0xFF) { /* unknown state */ 395 *state = SDL_POWERSTATE_UNKNOWN; 396 } else if (battery_flag & (1 << 7)) { /* no battery */ 397 *state = SDL_POWERSTATE_NO_BATTERY; 398 } else if (battery_flag & (1 << 3)) { /* charging */ 399 *state = SDL_POWERSTATE_CHARGING; 400 need_details = SDL_TRUE; 401 } else if (ac_status == 1) { 402 *state = SDL_POWERSTATE_CHARGED; /* on AC, not charging. */ 403 need_details = SDL_TRUE; 404 } else { 405 *state = SDL_POWERSTATE_ON_BATTERY; 406 need_details = SDL_TRUE; 407 } 408 409 *percent = -1; 410 *seconds = -1; 411 if (need_details) { 412 const int pct = battery_percent; 413 const int secs = battery_time; 414 415 if (pct >= 0) { /* -1 == unknown */ 416 *percent = (pct > 100) ? 100 : pct; /* clamp between 0%, 100% */ 417 } 418 if (secs >= 0) { /* -1 == unknown */ 419 *seconds = secs; 420 } 421 } 422 423 return SDL_TRUE; 424} 425 426#endif /* SDL_POWER_LINUX */ 427#endif /* SDL_POWER_DISABLED */ 428 429/* vi: set ts=4 sw=4 expandtab: */