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.
This commit is contained in:
Sebastian Reichel 2015-08-07 03:21:03 +02:00
parent d1c22762c4
commit 66acd8ed38

View file

@ -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 @@ struct psy_battery {
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 @@ static gboolean power_supply_is_battery(const gchar *entryname) {
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")) {
if(!g_strcmp0(type, "Battery\n")) {
g_free(type);
return FALSE;
return PSY_BATTERY;
}
if(!g_strcmp0(type, "Mains\n")) {
g_free(type);
return PSY_MAINS;
}
g_free(type);
return TRUE;
return PSY_UNKNOWN;
}
static gboolean init_linux_battery(struct psy_battery *bat) {
@ -134,6 +158,18 @@ err0:
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 @@ void free_linux_batteries() {
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 @@ gboolean init_linux_batteries() {
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 @@ static gboolean update_linux_battery(struct psy_battery *bat) {
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;
@ -265,6 +356,11 @@ void update_linux_batteries(enum chargestate *state, int8_t *percentage, int *se
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 */
*state = BATTERY_UNKNOWN;
if (charging && !discharging)
@ -353,6 +449,16 @@ char* linux_batteries_get_tooltip() {
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;
g_string_free(tooltip, FALSE);