diff --git a/CMakeLists.txt b/CMakeLists.txt index a8c3dcb..dad7d9d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,7 +78,7 @@ set( SOURCES src/config.c src/util/window.c ) if( ENABLE_BATTERY ) - set( SOURCES ${SOURCES} src/battery/battery.c ) + set( SOURCES ${SOURCES} src/battery/battery.c src/battery/linux.c) add_definitions( -DENABLE_BATTERY ) endif( ENABLE_BATTERY ) diff --git a/src/battery/battery.c b/src/battery/battery.c index 98dcd82..ccd10fb 100644 --- a/src/battery/battery.c +++ b/src/battery/battery.c @@ -61,10 +61,6 @@ char *battery_mclick_command; char *battery_rclick_command; char *battery_uwheel_command; char *battery_dwheel_command; -gchar *path_energy_now; -gchar *path_energy_full; -gchar *path_current_now; -gchar *path_status; int battery_found; #if defined(__OpenBSD__) || defined(__NetBSD__) @@ -151,10 +147,6 @@ void default_battery() battery_rclick_command = NULL; battery_uwheel_command = NULL; battery_dwheel_command = NULL; - path_energy_now = NULL; - path_energy_full = NULL; - path_current_now = NULL; - path_status = NULL; battery_state.percentage = 0; battery_state.time.hours = 0; battery_state.time.minutes = 0; @@ -171,14 +163,6 @@ void cleanup_battery() bat1_font_desc = NULL; pango_font_description_free(bat2_font_desc); bat2_font_desc = NULL; - g_free(path_energy_now); - path_energy_now = NULL; - g_free(path_energy_full); - path_energy_full = NULL; - g_free(path_current_now); - path_current_now = NULL; - g_free(path_status); - path_status = NULL; free(battery_low_cmd); battery_low_cmd = NULL; free(battery_lclick_command); @@ -199,6 +183,8 @@ void cleanup_battery() if ((apm_fd != -1) && (close(apm_fd) == -1)) warn("cannot close /dev/apm"); apm_fd = -1; +#elif defined(__linux) + free_linux_batteries(); #endif } @@ -224,89 +210,8 @@ void init_battery() battery_found = (sysctlbyname("hw.acpi.battery.state", &sysctl_out, &len, NULL, 0) == 0) || (sysctlbyname("hw.acpi.battery.time", &sysctl_out, &len, NULL, 0) == 0) || (sysctlbyname("hw.acpi.battery.life", &sysctl_out, &len, NULL, 0) == 0); -#else // Linux - GDir *directory = 0; - GError *error = NULL; - const char *entryname; - gchar *battery_dir = 0; - - directory = g_dir_open("/sys/class/power_supply", 0, &error); - if (error) { - g_error_free(error); - } else { - while ((entryname = g_dir_read_name(directory))) { - if (strncmp(entryname, "AC", 2) == 0) - continue; - - gchar *path1 = g_build_filename("/sys/class/power_supply", entryname, "present", NULL); - if (g_file_test(path1, G_FILE_TEST_EXISTS)) { - g_free(path1); - battery_dir = g_build_filename("/sys/class/power_supply", entryname, NULL); - break; - } - g_free(path1); - } - } - if (directory) - g_dir_close(directory); - if (!battery_dir) { - fprintf(stderr, "ERROR: battery applet cannot find any battery\n"); - battery_found = 0; - } else { - battery_found = 1; - - g_free(path_energy_now); - path_energy_now = g_build_filename(battery_dir, "energy_now", NULL); - if (!g_file_test(path_energy_now, G_FILE_TEST_EXISTS)) { - g_free(path_energy_now); - path_energy_now = g_build_filename(battery_dir, "charge_now", NULL); - } - if (!g_file_test(path_energy_now, G_FILE_TEST_EXISTS)) { - fprintf(stderr, "ERROR: battery applet cannot find energy_now nor charge_now\n"); - g_free(path_energy_now); - path_energy_now = NULL; - } - - g_free(path_energy_full); - path_energy_full = g_build_filename(battery_dir, "energy_full", NULL); - if (!g_file_test(path_energy_full, G_FILE_TEST_EXISTS)) { - g_free(path_energy_full); - path_energy_full = g_build_filename(battery_dir, "charge_full", NULL); - } - if (!g_file_test(path_energy_full, G_FILE_TEST_EXISTS)) { - fprintf(stderr, "ERROR: battery applet cannot find energy_now nor charge_now\n"); - g_free(path_energy_full); - path_energy_full = NULL; - } - - g_free(path_current_now); - path_current_now = g_build_filename(battery_dir, "power_now", NULL); - if (!g_file_test(path_current_now, G_FILE_TEST_EXISTS)) { - g_free(path_current_now); - path_current_now = g_build_filename(battery_dir, "current_now", NULL); - } - if (!g_file_test(path_current_now, G_FILE_TEST_EXISTS)) { - fprintf(stderr, "ERROR: battery applet cannot find power_now nor current_now\n"); - g_free(path_current_now); - path_current_now = NULL; - } - - g_free(path_status); - path_status = g_build_filename(battery_dir, "status", NULL); - if (!g_file_test(path_status, G_FILE_TEST_EXISTS)) { - fprintf(stderr, "ERROR: battery applet cannot find battery status\n"); - g_free(path_status); - path_status = NULL; - } - - g_free(battery_dir); - battery_dir = NULL; - } - - if (!path_status) { - battery_found = 0; - fprintf(stderr, "ERROR: battery applet cannot find any batteries\n"); - } +#elif defined(__linux) + battery_found = init_linux_batteries(); #endif if (!battery_timeout) @@ -420,82 +325,7 @@ int update_battery() { else new_percentage = sysctl_out; #else - FILE *fp = NULL; - char tmp[25] = ""; - int64_t current_now = 0; - if (path_status) { - fp = fopen(path_status, "r"); - if (fp != NULL) { - if (fgets(tmp, sizeof(tmp), fp)) { - if (strcasecmp(tmp, "Charging\n") == 0) - battery_state.state = BATTERY_CHARGING; - if (strcasecmp(tmp, "Discharging\n") == 0) - battery_state.state = BATTERY_DISCHARGING; - if (strcasecmp(tmp, "Full\n") == 0) - battery_state.state = BATTERY_FULL; - } - fclose(fp); - } else { - errors = 1; - } - } else { - errors = 1; - } - - if (path_energy_now) { - fp = fopen(path_energy_now, "r"); - if (fp != NULL) { - if (fgets(tmp, sizeof tmp, fp)) - energy_now = atoi(tmp); - fclose(fp); - } else { - errors = 1; - } - } else { - errors = 1; - } - - if (path_energy_full) { - fp = fopen(path_energy_full, "r"); - if (fp != NULL) { - if (fgets(tmp, sizeof tmp, fp)) - energy_full = atoi(tmp); - fclose(fp); - } else { - errors = 1; - } - } else { - errors = 1; - } - - if (path_current_now) { - fp = fopen(path_current_now, "r"); - if (fp != NULL) { - if (fgets(tmp, sizeof tmp, fp)) - current_now = atoi(tmp); - fclose(fp); - } else { - errors = 1; - } - } else { - errors = 1; - } - - if (current_now > 0) { - switch (battery_state.state) { - case BATTERY_CHARGING: - seconds = 3600 * (energy_full - energy_now) / current_now; - break; - case BATTERY_DISCHARGING: - seconds = 3600 * energy_now / current_now; - break; - default: - seconds = 0; - break; - } - } else { - seconds = 0; - } + update_linux_batteries(&battery_state.state, &energy_now, &energy_full, &seconds); #endif battery_state.time.hours = seconds / 3600; @@ -616,4 +446,4 @@ void battery_action(int button) break; } tint_exec(command); -} \ No newline at end of file +} diff --git a/src/battery/battery.h b/src/battery/battery.h index dd2aa14..e7729b3 100644 --- a/src/battery/battery.h +++ b/src/battery/battery.h @@ -79,4 +79,10 @@ int resize_battery(void *obj); void battery_action(int button); +#ifdef __linux +gboolean init_linux_batteries(); +void free_linux_batteries(); +void update_linux_batteries(enum chargestate *state, gint64 *energy_now, gint64 *energy_full, int *seconds); +#endif + #endif diff --git a/src/battery/linux.c b/src/battery/linux.c new file mode 100644 index 0000000..79f50eb --- /dev/null +++ b/src/battery/linux.c @@ -0,0 +1,287 @@ +/************************************************************************** +* +* Tint2 : Linux battery +* +* Copyright (C) 2015 Sebastian Reichel +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License version 2 +* or any later version as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +**************************************************************************/ + +#ifdef __linux + +#include + +#include "common.h" +#include "battery.h" + +struct psy_battery { + /* generic properties */ + gchar* name; + /* sysfs files */ + gchar* path_present; + gchar* path_energy_now; + gchar* path_energy_full; + gchar* path_power_now; + gchar* path_status; + /* sysfs hints */ + gboolean energy_in_uamp; + gboolean power_in_uamp; + /* values */ + gboolean present; + gint energy_now; + gint energy_full; + gint power_now; + enum chargestate status; +}; + +#define RETURN_ON_ERROR(err) if(error) { g_error_free(err); return FALSE; } + +static GList *batteries = NULL; + +static gboolean power_supply_is_battery(const gchar *entryname) { + gchar *path_type = g_build_filename("/sys/class/power_supply", entryname, "type", NULL); + GError *error = NULL; + gchar *type; + gsize typelen; + + g_file_get_contents(path_type, &type, &typelen, &error); + g_free(path_type); + RETURN_ON_ERROR(error); + + if(g_strcmp0(type, "Battery\n")) { + g_free(type); + return FALSE; + } + + g_free(type); + + return TRUE; +} + +static gboolean init_linux_battery(struct psy_battery *bat) { + const gchar *entryname = bat->name; + + bat->energy_in_uamp = FALSE; + bat->power_in_uamp = FALSE; + + bat->path_present = g_build_filename("/sys/class/power_supply", entryname, "present", NULL); + if (!g_file_test(bat->path_present, G_FILE_TEST_EXISTS)) { + goto err0; + } + + bat->path_energy_now = g_build_filename("/sys/class/power_supply", entryname, "energy_now", NULL); + if (!g_file_test(bat->path_energy_now, G_FILE_TEST_EXISTS)) { + g_free(bat->path_energy_now); + bat->path_energy_now = g_build_filename("/sys/class/power_supply", entryname, "charge_now", NULL); + bat->energy_in_uamp = TRUE; + } + if (!g_file_test(bat->path_energy_now, G_FILE_TEST_EXISTS)) { + goto err1; + } + + if(!bat->energy_in_uamp) { + bat->path_energy_full = g_build_filename("/sys/class/power_supply", entryname, "energy_full", NULL); + if (!g_file_test(bat->path_energy_full, G_FILE_TEST_EXISTS)) + goto err2; + } else { + bat->path_energy_full = g_build_filename("/sys/class/power_supply", entryname, "charge_full", NULL); + if (!g_file_test(bat->path_energy_full, G_FILE_TEST_EXISTS)) + goto err2; + } + + bat->path_power_now = g_build_filename("/sys/class/power_supply", entryname, "power_now", NULL); + if (!g_file_test(bat->path_power_now, G_FILE_TEST_EXISTS)) { + g_free(bat->path_power_now); + bat->path_power_now = g_build_filename("/sys/class/power_supply", entryname, "current_now", NULL); + bat->power_in_uamp = TRUE; + } + if (!g_file_test(bat->path_power_now, G_FILE_TEST_EXISTS)) { + goto err3; + } + + bat->path_status = g_build_filename("/sys/class/power_supply", entryname, "status", NULL); + if (!g_file_test(bat->path_status, G_FILE_TEST_EXISTS)) { + goto err4; + } + + return TRUE; + +err4: + g_free(bat->path_status); +err3: + g_free(bat->path_power_now); +err2: + g_free(bat->path_energy_full); +err1: + g_free(bat->path_energy_now); +err0: + g_free(bat->path_present); + + return FALSE; +} + +void free_linux_batteries() { + GList *l = batteries; + + while (l != NULL) { + GList *next = l->next; + struct psy_battery *bat = l->data; + + g_free(bat->name); + g_free(bat->path_status); + g_free(bat->path_power_now); + g_free(bat->path_energy_full); + g_free(bat->path_energy_now); + g_free(bat->path_present); + + batteries = g_list_delete_link(batteries, l); + l = next; + } + + batteries = NULL; +} + +gboolean init_linux_batteries() { + GDir *directory = 0; + GError *error = NULL; + const char *entryname; + + free_linux_batteries(); + + 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; + + 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); + } + } + + g_dir_close(directory); + + return batteries != NULL; +} + +static gboolean update_linux_battery(struct psy_battery *bat) { + GError *error = NULL; + gchar *data; + gsize datalen; + + /* reset values */ + bat->present = 0; + bat->status = BATTERY_UNKNOWN; + bat->energy_now = 0; + bat->energy_full = 0; + bat->power_now = 0; + + /* present */ + g_file_get_contents(bat->path_present, &data, &datalen, &error); + RETURN_ON_ERROR(error); + bat->present = (atoi(data) == 1); + g_free(data); + + /* we are done, if battery is not present */ + if(!bat->present) + return TRUE; + + /* status */ + bat->status = BATTERY_UNKNOWN; + g_file_get_contents(bat->path_status, &data, &datalen, &error); + RETURN_ON_ERROR(error); + if (!g_strcmp0(data, "Charging\n")) { + bat->status = BATTERY_CHARGING; + } else if (!g_strcmp0(data, "Discharging\n")) { + bat->status = BATTERY_DISCHARGING; + } else if (!g_strcmp0(data, "Full\n")) { + bat->status = BATTERY_FULL; + } + g_free(data); + + /* energy now */ + g_file_get_contents(bat->path_energy_now, &data, &datalen, &error); + RETURN_ON_ERROR(error); + bat->energy_now = atoi(data); + g_free(data); + + /* energy full */ + g_file_get_contents(bat->path_energy_full, &data, &datalen, &error); + RETURN_ON_ERROR(error); + bat->energy_full = atoi(data); + g_free(data); + + /* power now */ + g_file_get_contents(bat->path_power_now, &data, &datalen, &error); + RETURN_ON_ERROR(error); + bat->power_now = atoi(data); + g_free(data); + + return TRUE; +} + +void update_linux_batteries(enum chargestate *state, gint64 *energy_now, gint64 *energy_full, int *seconds) { + GList *l; + + gint64 total_energy_now = 0; + gint64 total_energy_full = 0; + gint64 total_power_now = 0; + + gboolean charging = FALSE; + gboolean discharging = FALSE; + gboolean full = FALSE; + + for (l = batteries; l != NULL; l = l->next) { + struct psy_battery *bat = l->data; + update_linux_battery(bat); + + total_energy_now += bat->energy_now; + total_energy_full += bat->energy_full; + total_power_now += bat->power_now; + + charging |= (bat->status == BATTERY_CHARGING); + discharging |= (bat->status == BATTERY_DISCHARGING); + full |= (bat->status == BATTERY_FULL); + } + + /* global energy stats */ + *energy_now = total_energy_now; + *energy_full = total_energy_full; + + /* build global state */ + *state = BATTERY_UNKNOWN; + if (charging && !discharging) + *state = BATTERY_CHARGING; + else if (!charging && discharging) + *state = BATTERY_DISCHARGING; + else if (!charging && !discharging && full) + *state = BATTERY_FULL; + + /* calculate seconds */ + *seconds = 0; + if (total_power_now > 0) { + if(*state == BATTERY_CHARGING) + *seconds = 3600 * (total_energy_full - total_energy_now) / total_power_now; + else if(*state == BATTERY_DISCHARGING) + *seconds = 3600 * total_energy_now / total_power_now; + } +} + +#endif