Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Linux: Implemented sysfs-based version of SDL_GetPowerInfo().
Fixes Bugzilla #2938.
  • Loading branch information
icculus committed Jun 3, 2015
1 parent 36ecb76 commit e8b376c
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 7 deletions.
2 changes: 2 additions & 0 deletions src/power/SDL_power.c
Expand Up @@ -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 *);
Expand Down Expand Up @@ -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
Expand Down
104 changes: 97 additions & 7 deletions src/power/linux/SDL_syspower.c
Expand Up @@ -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);
Expand All @@ -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;
}
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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 */

Expand Down

0 comments on commit e8b376c

Please sign in to comment.