Add battery Mains/AC support for Linux Add initial support for mains/ac adapters. This adds the state only to the tooltip, but the code has been prepared to update AC state together with normal battery updates, so that it's possible to forward the state to the normal panel area or trigger something on AC (dis)connection.
Sebastian Reichel sre@ring0.de
1 files changed,
121 insertions(+),
15 deletions(-)
jump to
M
src/battery/linux.c
→
src/battery/linux.c
@@ -24,6 +24,12 @@
#include "common.h" #include "battery.h" +enum psy_type { + PSY_UNKNOWN, + PSY_BATTERY, + PSY_MAINS, +}; + struct psy_battery { /* generic properties */ gchar* name;@@ -44,15 +50,25 @@ gint power_now;
enum chargestate status; }; +struct psy_mains { + /* generic properties */ + gchar* name; + /* sysfs files */ + gchar* path_online; + /* values */ + gboolean online; +}; + #define RETURN_ON_ERROR(err) if(error) { g_error_free(err); return FALSE; } static GList *batteries = NULL; +static GList *mains = NULL; static guint8 energy_to_percent(gint energy_now, gint energy_full) { return 0.5 + ((energy_now <= energy_full ? energy_now : energy_full) * 100.0) / energy_full; } -static gboolean power_supply_is_battery(const gchar *entryname) { +static enum psy_type power_supply_get_type(const gchar *entryname) { gchar *path_type = g_build_filename("/sys/class/power_supply", entryname, "type", NULL); GError *error = NULL; gchar *type;@@ -60,16 +76,24 @@ gsize typelen;
g_file_get_contents(path_type, &type, &typelen, &error); g_free(path_type); - RETURN_ON_ERROR(error); + if (error) { + g_error_free(error); + return PSY_UNKNOWN; + } + + if(!g_strcmp0(type, "Battery\n")) { + g_free(type); + return PSY_BATTERY; + } - if(g_strcmp0(type, "Battery\n")) { + if(!g_strcmp0(type, "Mains\n")) { g_free(type); - return FALSE; + return PSY_MAINS; } g_free(type); - return TRUE; + return PSY_UNKNOWN; } static gboolean init_linux_battery(struct psy_battery *bat) {@@ -134,6 +158,18 @@
return FALSE; } +static gboolean init_linux_mains(struct psy_mains *ac) { + const gchar *entryname = ac->name; + + ac->path_online = g_build_filename("/sys/class/power_supply", entryname, "online", NULL); + if (!g_file_test(ac->path_online, G_FILE_TEST_EXISTS)) { + g_free(ac->path_online); + return FALSE; + } + + return TRUE; +} + void free_linux_batteries() { GList *l = batteries;@@ -152,7 +188,46 @@ batteries = g_list_delete_link(batteries, l);
l = next; } + l = mains; + while (l != NULL) { + GList *next = l->next; + struct psy_mains *ac = l->data; + + g_free(ac->name); + g_free(ac->path_online); + + mains = g_list_delete_link(mains, l); + l = next; + } + batteries = NULL; + mains = NULL; +} + +static void add_battery(const char *entryname) { + struct psy_battery *bat = g_malloc0(sizeof(*bat)); + bat->name = g_strdup(entryname); + + if(init_linux_battery(bat)) { + batteries = g_list_append(batteries, bat); + fprintf(stdout, "found battery \"%s\"\n", bat->name); + } else { + g_free(bat); + fprintf(stderr, RED "failed to initialize battery \"%s\"\n" RESET, entryname); + } +} + +static void add_mains(const char *entryname) { + struct psy_mains *ac = g_malloc0(sizeof(*ac)); + ac->name = g_strdup(entryname); + + if(init_linux_mains(ac)) { + mains = g_list_append(mains, ac); + fprintf(stdout, "found mains \"%s\"\n", ac->name); + } else { + g_free(ac); + fprintf(stderr, RED "failed to initialize mains \"%s\"\n" RESET, entryname); + } } gboolean init_linux_batteries() {@@ -166,17 +241,17 @@ directory = g_dir_open("/sys/class/power_supply", 0, &error);
RETURN_ON_ERROR(error); while ((entryname = g_dir_read_name(directory))) { - if(!power_supply_is_battery(entryname)) - continue; + enum psy_type type = power_supply_get_type(entryname); - struct psy_battery *bat = g_malloc0(sizeof(*bat)); - bat->name = g_strdup(entryname); - if(init_linux_battery(bat)) { - batteries = g_list_append(batteries, bat); - fprintf(stdout, "found battery \"%s\"\n", bat->name); - } else { - g_free(bat); - fprintf(stderr, RED "failed to initialize battery \"%s\"\n" RESET, entryname); + switch(type) { + case PSY_BATTERY: + add_battery(entryname); + break; + case PSY_MAINS: + add_mains(entryname); + break; + default: + break; } }@@ -241,6 +316,22 @@
return TRUE; } + +static gboolean update_linux_mains(struct psy_mains *ac) { + GError *error = NULL; + gchar *data; + gsize datalen; + ac->online = FALSE; + + /* online */ + g_file_get_contents(ac->path_online, &data, &datalen, &error); + RETURN_ON_ERROR(error); + ac->online = (atoi(data) == 1); + g_free(data); + + return TRUE; +} + void update_linux_batteries(enum chargestate *state, int8_t *percentage, int *seconds) { GList *l;@@ -263,6 +354,11 @@
charging |= (bat->status == BATTERY_CHARGING); discharging |= (bat->status == BATTERY_DISCHARGING); full |= (bat->status == BATTERY_FULL); + } + + for (l = mains; l != NULL; l = l->next) { + struct psy_mains *ac = l->data; + update_linux_mains(ac); } /* build global state */@@ -351,6 +447,16 @@ state, energy, percentage, power);
g_free(power); g_free(energy); + } + + for (l = mains; l != NULL; l = l->next) { + struct psy_mains *ac = l->data; + + if (tooltip->len) + g_string_append_c(tooltip, '\n'); + + g_string_append_printf(tooltip, "%s\n", ac->name); + g_string_append_printf(tooltip, ac->online ? "\tconnected" : "\tdisconnected"); } result = tooltip->str;