Linux: Implemented sysfs-based version of SDL_GetPowerInfo().

Fixes Bugzilla #2938.
This commit is contained in:
Ryan C. Gordon 2015-06-03 13:11:28 -07:00
parent 36ecb766c8
commit e8b376c9d5
2 changed files with 99 additions and 7 deletions

View File

@ -29,6 +29,7 @@ typedef SDL_bool
(*SDL_GetPowerInfo_Impl) (SDL_PowerState * state, int *seconds,
int *percent);
SDL_bool SDL_GetPowerInfo_Linux_sys_class_power_supply(SDL_PowerState *, int *, int *);
SDL_bool SDL_GetPowerInfo_Linux_proc_acpi(SDL_PowerState *, int *, int *);
SDL_bool SDL_GetPowerInfo_Linux_proc_apm(SDL_PowerState *, int *, int *);
SDL_bool SDL_GetPowerInfo_Windows(SDL_PowerState *, int *, int *);
@ -58,6 +59,7 @@ SDL_GetPowerInfo_Hardwired(SDL_PowerState * state, int *seconds, int *percent)
static SDL_GetPowerInfo_Impl implementations[] = {
#ifndef SDL_POWER_DISABLED
#ifdef SDL_POWER_LINUX /* in order of preference. More than could work. */
SDL_GetPowerInfo_Linux_sys_class_power_supply,
SDL_GetPowerInfo_Linux_proc_acpi,
SDL_GetPowerInfo_Linux_proc_apm,
#endif

View File

@ -36,8 +36,10 @@
static const char *proc_apm_path = "/proc/apm";
static const char *proc_acpi_battery_path = "/proc/acpi/battery";
static const char *proc_acpi_ac_adapter_path = "/proc/acpi/ac_adapter";
static const char *sys_class_power_supply_path = "/sys/class/power_supply";
static int open_acpi_file(const char *base, const char *node, const char *key)
static int
open_power_file(const char *base, const char *node, const char *key)
{
const size_t pathlen = strlen(base) + strlen(node) + strlen(key) + 3;
char *path = (char *) alloca(pathlen);
@ -51,11 +53,11 @@ static int open_acpi_file(const char *base, const char *node, const char *key)
static SDL_bool
load_acpi_file(const char *base, const char *node, const char *key,
char *buf, size_t buflen)
read_power_file(const char *base, const char *node, const char *key,
char *buf, size_t buflen)
{
ssize_t br = 0;
const int fd = open_acpi_file(base, node, key);
const int fd = open_power_file(base, node, key);
if (fd == -1) {
return SDL_FALSE;
}
@ -133,9 +135,9 @@ check_proc_acpi_battery(const char * node, SDL_bool * have_battery,
int secs = -1;
int pct = -1;
if (!load_acpi_file(base, node, "state", state, sizeof (state))) {
if (!read_power_file(base, node, "state", state, sizeof (state))) {
return;
} else if (!load_acpi_file(base, node, "info", info, sizeof (info))) {
} else if (!read_power_file(base, node, "info", info, sizeof (info))) {
return;
}
@ -214,7 +216,7 @@ check_proc_acpi_ac_adapter(const char * node, SDL_bool * have_ac)
char *key = NULL;
char *val = NULL;
if (!load_acpi_file(base, node, "state", state, sizeof (state))) {
if (!read_power_file(base, node, "state", state, sizeof (state))) {
return;
}
@ -423,6 +425,94 @@ SDL_GetPowerInfo_Linux_proc_apm(SDL_PowerState * state,
return SDL_TRUE;
}
/* !!! FIXME: implement d-bus queries to org.freedesktop.UPower. */
SDL_bool
SDL_GetPowerInfo_Linux_sys_class_power_supply(SDL_PowerState *state, int *seconds, int *percent)
{
const char *base = sys_class_power_supply_path;
struct dirent *dent;
DIR *dirp;
dirp = opendir(base);
if (!dirp) {
return SDL_FALSE;
}
*state = SDL_POWERSTATE_NO_BATTERY; /* assume we're just plugged in. */
*seconds = -1;
*percent = -1;
while ((dent = readdir(dirp)) != NULL) {
const char *name = dent->d_name;
SDL_bool choose = SDL_FALSE;
char str[64];
SDL_PowerState st;
int secs;
int pct;
if ((SDL_strcmp(name, ".") == 0) || (SDL_strcmp(name, "..") == 0)) {
continue; /* skip these, of course. */
} else if (!read_power_file(base, name, "type", str, sizeof (str))) {
continue; /* Don't know _what_ we're looking at. Give up on it. */
} else if (SDL_strcmp(str, "Battery\n") != 0) {
continue; /* we don't care about UPS and such. */
}
/* some drivers don't offer this, so if it's not explicitly reported assume it's present. */
if (read_power_file(base, name, "present", str, sizeof (str)) && (SDL_strcmp(str, "0\n") == 0)) {
st = SDL_POWERSTATE_NO_BATTERY;
} else if (!read_power_file(base, name, "status", str, sizeof (str))) {
st = SDL_POWERSTATE_UNKNOWN; /* uh oh */
} else if (SDL_strcmp(str, "Charging\n") == 0) {
st = SDL_POWERSTATE_CHARGING;
} else if (SDL_strcmp(str, "Discharging\n") == 0) {
st = SDL_POWERSTATE_ON_BATTERY;
} else if ((SDL_strcmp(str, "Full\n") == 0) || (SDL_strcmp(str, "Not charging\n") == 0)) {
st = SDL_POWERSTATE_CHARGED;
} else {
st = SDL_POWERSTATE_UNKNOWN; /* uh oh */
}
if (!read_power_file(base, name, "capacity", str, sizeof (str))) {
pct = -1;
} else {
pct = SDL_atoi(str);
pct = (pct > 100) ? 100 : pct; /* clamp between 0%, 100% */
}
if (!read_power_file(base, name, "time_to_empty_now", str, sizeof (str))) {
secs = -1;
} else {
secs = SDL_atoi(str);
secs = (secs <= 0) ? -1 : secs; /* 0 == unknown */
}
/*
* We pick the battery that claims to have the most minutes left.
* (failing a report of minutes, we'll take the highest percent.)
*/
if ((secs < 0) && (*seconds < 0)) {
if ((pct < 0) && (*percent < 0)) {
choose = SDL_TRUE; /* at least we know there's a battery. */
} else if (pct > *percent) {
choose = SDL_TRUE;
}
} else if (secs > *seconds) {
choose = SDL_TRUE;
}
if (choose) {
*seconds = secs;
*percent = pct;
*state = st;
}
}
closedir(dirp);
return SDL_TRUE; /* don't look any further. */
}
#endif /* SDL_POWER_LINUX */
#endif /* SDL_POWER_DISABLED */