Linux: Implemented sysfs-based version of SDL_GetPowerInfo(). Fixes Bugzilla #2938.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
diff --git a/src/power/SDL_power.c b/src/power/SDL_power.c
index 7b8dc15..8f919f2 100644
--- a/src/power/SDL_power.c
+++ b/src/power/SDL_power.c
@@ -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
diff --git a/src/power/linux/SDL_syspower.c b/src/power/linux/SDL_syspower.c
index e8f1f36..3986489 100644
--- a/src/power/linux/SDL_syspower.c
+++ b/src/power/linux/SDL_syspower.c
@@ -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 */