From edad9bb7f59c500aac4fd748647993fa31ba8f2c Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Fri, 7 Aug 2015 02:03:06 +0200 Subject: [PATCH 1/7] Fix memory leak for battery tooltips Instead of returning a const string, that is fed into strdup, tooltip functions are now supposed to return allocated strings. This fixes a memory leak in the battery tooltip. This is used instead of simply freeing the memory in the battery tooltip function, since it also avoids a uselesss strdup(). --- src/battery/battery.c | 2 +- src/battery/battery.h | 2 +- src/battery/linux.c | 2 +- src/clock/clock.c | 4 ++-- src/launcher/launcher.c | 4 ++-- src/taskbar/task.c | 4 ++-- src/tooltip/tooltip.c | 2 +- src/util/area.h | 3 ++- 8 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/battery/battery.c b/src/battery/battery.c index add8327..8e65e1a 100644 --- a/src/battery/battery.c +++ b/src/battery/battery.c @@ -220,7 +220,7 @@ void init_battery() battery_timeout = add_timeout(10, 30000, update_battery_tick, 0, &battery_timeout); } -const char* battery_get_tooltip(void* obj) { +char* battery_get_tooltip(void* obj) { #if defined(__linux) return linux_batteries_get_tooltip(); #else diff --git a/src/battery/battery.h b/src/battery/battery.h index 390661e..c016637 100644 --- a/src/battery/battery.h +++ b/src/battery/battery.h @@ -97,7 +97,7 @@ void battery_action(int button); gboolean init_linux_batteries(); void free_linux_batteries(); void update_linux_batteries(enum chargestate *state, gint64 *energy_now, gint64 *energy_full, int *seconds); -const char* linux_batteries_get_tooltip(); +char* linux_batteries_get_tooltip(); #endif #endif diff --git a/src/battery/linux.c b/src/battery/linux.c index 7054827..8dcabb2 100644 --- a/src/battery/linux.c +++ b/src/battery/linux.c @@ -319,7 +319,7 @@ static gchar* power_human_readable(struct psy_battery *bat) { } } -const char* linux_batteries_get_tooltip() { +char* linux_batteries_get_tooltip() { GList *l; GString *tooltip = g_string_new(""); gchar *result; diff --git a/src/clock/clock.c b/src/clock/clock.c index f5dab27..91dda59 100644 --- a/src/clock/clock.c +++ b/src/clock/clock.c @@ -144,10 +144,10 @@ struct tm* clock_gettime_for_tz(const char* timezone) { else return localtime(&time_clock.tv_sec); } -const char* clock_get_tooltip(void* obj) +char* clock_get_tooltip(void* obj) { strftime(buf_tooltip, sizeof(buf_tooltip), time_tooltip_format, clock_gettime_for_tz(time_tooltip_timezone)); - return buf_tooltip; + return strdup(buf_tooltip); } int time_format_needs_sec_ticks(char *time_format) diff --git a/src/launcher/launcher.c b/src/launcher/launcher.c index f0aaed0..2a637f1 100644 --- a/src/launcher/launcher.c +++ b/src/launcher/launcher.c @@ -347,10 +347,10 @@ void launcher_icon_on_change_layout(void *obj) launcherIcon->area.height = launcherIcon->icon_size; } -const char* launcher_icon_get_tooltip_text(void *obj) +char* launcher_icon_get_tooltip_text(void *obj) { LauncherIcon *launcherIcon = (LauncherIcon*)obj; - return launcherIcon->icon_tooltip; + return strdup(launcherIcon->icon_tooltip); } void draw_launcher_icon(void *obj, cairo_t *c) diff --git a/src/taskbar/task.c b/src/taskbar/task.c index aa9804a..a216d90 100644 --- a/src/taskbar/task.c +++ b/src/taskbar/task.c @@ -38,10 +38,10 @@ timeout* urgent_timeout; GSList* urgent_list; -const char* task_get_tooltip(void* obj) +char* task_get_tooltip(void* obj) { Task* t = obj; - return t->title; + return strdup(t->title); } diff --git a/src/tooltip/tooltip.c b/src/tooltip/tooltip.c index f438162..ee5c30a 100644 --- a/src/tooltip/tooltip.c +++ b/src/tooltip/tooltip.c @@ -304,7 +304,7 @@ void tooltip_copy_text(Area* area) { free(g_tooltip.tooltip_text); if (area && area->_get_tooltip_text) - g_tooltip.tooltip_text = strdup(area->_get_tooltip_text(area)); + g_tooltip.tooltip_text = area->_get_tooltip_text(area); else g_tooltip.tooltip_text = NULL; g_tooltip.area = area; diff --git a/src/util/area.h b/src/util/area.h index 1907586..898a7d3 100644 --- a/src/util/area.h +++ b/src/util/area.h @@ -91,7 +91,8 @@ typedef struct { // after pos/size changed, the rendering engine will call _on_change_layout(Area*) int on_changed; void (*_on_change_layout)(void *obj); - const char* (*_get_tooltip_text)(void *obj); + // returns allocated string, that must be free'd after usage + char* (*_get_tooltip_text)(void *obj); } Area; // on startup, initialize fixed pos/size From 441c42077323c084542698367e73645fa11d76f4 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Fri, 7 Aug 2015 02:32:18 +0200 Subject: [PATCH 2/7] Remove static clock tooltip buffer Use GLib's GDateTime to generate the tooltip, so that a correctly sized string is automatically created. --- src/clock/clock.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/clock/clock.c b/src/clock/clock.c index 91dda59..e6805fe 100644 --- a/src/clock/clock.c +++ b/src/clock/clock.c @@ -48,7 +48,6 @@ PangoFontDescription *time1_font_desc; PangoFontDescription *time2_font_desc; static char buf_time[256]; static char buf_date[256]; -static char buf_tooltip[512]; int clock_enabled; static timeout* clock_timeout; @@ -146,8 +145,13 @@ struct tm* clock_gettime_for_tz(const char* timezone) { char* clock_get_tooltip(void* obj) { - strftime(buf_tooltip, sizeof(buf_tooltip), time_tooltip_format, clock_gettime_for_tz(time_tooltip_timezone)); - return strdup(buf_tooltip); + GTimeZone *tz = g_time_zone_new(time_tooltip_timezone); + GDateTime *now = g_date_time_new_now(tz); + char *result = g_date_time_format(now, time_tooltip_format); + g_date_time_unref(now); + g_time_zone_unref(tz); + + return result; } int time_format_needs_sec_ticks(char *time_format) @@ -197,7 +201,6 @@ void init_clock_panel(void *p) if (time_tooltip_format) { clock->area._get_tooltip_text = clock_get_tooltip; - strftime(buf_tooltip, sizeof(buf_tooltip), time_tooltip_format, clock_gettime_for_tz(time_tooltip_timezone)); } } From d1c22762c40b601f092925799b191ff54d559962 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Fri, 7 Aug 2015 02:53:52 +0200 Subject: [PATCH 3/7] battery: move percentage calculation to linux code Only Linux provides detailed energy statistics. The BSDs directly provide percentage values. This change makes it easier to split out the BSD code and reduces code overhead on BSD systems. --- src/battery/battery.c | 7 +------ src/battery/battery.h | 2 +- src/battery/linux.c | 15 +++++++++------ 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/battery/battery.c b/src/battery/battery.c index 8e65e1a..babedf1 100644 --- a/src/battery/battery.c +++ b/src/battery/battery.c @@ -258,8 +258,6 @@ void init_battery_panel(void *p) int update_battery() { - int64_t energy_now = 0, - energy_full = 0; int seconds = 0; int8_t new_percentage = 0; int errors = 0; @@ -337,7 +335,7 @@ int update_battery() { else new_percentage = sysctl_out; #else - update_linux_batteries(&battery_state.state, &energy_now, &energy_full, &seconds); + update_linux_batteries(&battery_state.state, &new_percentage, &seconds); #endif battery_state.time.hours = seconds / 3600; @@ -346,9 +344,6 @@ int update_battery() { seconds -= 60 * battery_state.time.minutes; battery_state.time.seconds = seconds; - if (energy_full > 0) - new_percentage = 0.5 + ((energy_now <= energy_full ? energy_now : energy_full) * 100.0) / energy_full; - battery_state.percentage = new_percentage; // clamp percentage to 100 in case battery is misreporting that its current charge is more than its max diff --git a/src/battery/battery.h b/src/battery/battery.h index c016637..f9441da 100644 --- a/src/battery/battery.h +++ b/src/battery/battery.h @@ -96,7 +96,7 @@ 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); +void update_linux_batteries(enum chargestate *state, int8_t *percentage, int *seconds); char* linux_batteries_get_tooltip(); #endif diff --git a/src/battery/linux.c b/src/battery/linux.c index 8dcabb2..6234c82 100644 --- a/src/battery/linux.c +++ b/src/battery/linux.c @@ -48,6 +48,10 @@ struct psy_battery { static GList *batteries = 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) { gchar *path_type = g_build_filename("/sys/class/power_supply", entryname, "type", NULL); GError *error = NULL; @@ -237,7 +241,7 @@ static gboolean update_linux_battery(struct psy_battery *bat) { return TRUE; } -void update_linux_batteries(enum chargestate *state, gint64 *energy_now, gint64 *energy_full, int *seconds) { +void update_linux_batteries(enum chargestate *state, int8_t *percentage, int *seconds) { GList *l; gint64 total_energy_now = 0; @@ -261,10 +265,6 @@ void update_linux_batteries(enum chargestate *state, gint64 *energy_now, gint64 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) @@ -282,6 +282,9 @@ void update_linux_batteries(enum chargestate *state, gint64 *energy_now, gint64 else if(*state == BATTERY_DISCHARGING) *seconds = 3600 * total_energy_now / total_power_now; } + + /* calculate percentage */ + *percentage = energy_to_percent(total_energy_now, total_energy_full); } static gchar* energy_human_readable(struct psy_battery *bat) { @@ -341,7 +344,7 @@ char* linux_batteries_get_tooltip() { gchar *energy = energy_human_readable(bat); gchar *state = (bat->status == BATTERY_UNKNOWN) ? "Level" : chargestate2str(bat->status); - guint percentage = 0.5 + ((bat->energy_now <= bat->energy_full ? bat->energy_now : bat->energy_full) * 100.0) / bat->energy_full; + guint8 percentage = energy_to_percent(bat->energy_now, bat->energy_full); g_string_append_printf(tooltip, "\t%s: %s (%u %%)\n\tPower: %s", state, energy, percentage, power); From 66acd8ed3890ee237d106d99a85d2a3b9c86f5d0 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Fri, 7 Aug 2015 03:21:03 +0200 Subject: [PATCH 4/7] 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. --- src/battery/linux.c | 136 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 121 insertions(+), 15 deletions(-) diff --git a/src/battery/linux.c b/src/battery/linux.c index 6234c82..bae3d1a 100644 --- a/src/battery/linux.c +++ b/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 @@ 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); From 3c45cf29c7bfb05242dde4f55d12cc1972ff1b7d Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Fri, 7 Aug 2015 04:31:31 +0200 Subject: [PATCH 5/7] Battery: Split operating system specific code This removes all operating system specific code from the main battery file into their own source files. CMake will add the correct implementation automatically. --- CMakeLists.txt | 15 ++++- src/battery/battery.c | 147 +++--------------------------------------- src/battery/battery.h | 19 ++++-- src/battery/dummy.c | 40 ++++++++++++ src/battery/freebsd.c | 79 +++++++++++++++++++++++ src/battery/linux.c | 32 ++++----- src/battery/openbsd.c | 84 ++++++++++++++++++++++++ 7 files changed, 257 insertions(+), 159 deletions(-) create mode 100644 src/battery/dummy.c create mode 100644 src/battery/freebsd.c create mode 100644 src/battery/openbsd.c diff --git a/CMakeLists.txt b/CMakeLists.txt index dad7d9d..e2d1574 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,7 +78,20 @@ set( SOURCES src/config.c src/util/window.c ) if( ENABLE_BATTERY ) - set( SOURCES ${SOURCES} src/battery/battery.c src/battery/linux.c) + set( SOURCES ${SOURCES} src/battery/battery.c) + + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set( SOURCES ${SOURCES} src/battery/linux.c) + elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") + set( SOURCES ${SOURCES} src/battery/freebsd.c) + elseif(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") + set( SOURCES ${SOURCES} src/battery/openbsd.c) + elseif(CMAKE_SYSTEM_NAME STREQUAL "NetBSD") + set( SOURCES ${SOURCES} src/battery/openbsd.c) + else(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set( SOURCES ${SOURCES} src/battery/dummy.c) + endif(CMAKE_SYSTEM_NAME STREQUAL "Linux") + add_definitions( -DENABLE_BATTERY ) endif( ENABLE_BATTERY ) diff --git a/src/battery/battery.c b/src/battery/battery.c index babedf1..56dcf27 100644 --- a/src/battery/battery.c +++ b/src/battery/battery.c @@ -1,6 +1,6 @@ /************************************************************************** * -* Tint2 : battery +* Tint2 : Generic battery * * Copyright (C) 2009-2015 Sebastian Reichel * @@ -24,18 +24,6 @@ #include #include -#if defined(__OpenBSD__) || defined(__NetBSD__) -#include -#include -#include -#include -#endif - -#if defined(__FreeBSD__) -#include -#include -#endif - #include "window.h" #include "server.h" #include "panel.h" @@ -64,10 +52,6 @@ char *battery_uwheel_command; char *battery_dwheel_command; int battery_found; -#if defined(__OpenBSD__) || defined(__NetBSD__) -int apm_fd; -#endif - void update_battery_tick(void* arg) { if (!battery_enabled) @@ -154,9 +138,6 @@ void default_battery() battery_state.time.minutes = 0; battery_state.time.seconds = 0; battery_state.state = BATTERY_UNKNOWN; -#if defined(__OpenBSD__) || defined(__NetBSD__) - apm_fd = -1; -#endif } void cleanup_battery() @@ -181,51 +162,22 @@ void cleanup_battery() battery_timeout = NULL; battery_found = 0; -#if defined(__OpenBSD__) || defined(__NetBSD__) - if ((apm_fd != -1) && (close(apm_fd) == -1)) - warn("cannot close /dev/apm"); - apm_fd = -1; -#elif defined(__linux) - free_linux_batteries(); -#endif + battery_os_free(); } void init_battery() { if (!battery_enabled) return; - battery_found = 0; -#if defined(__OpenBSD__) || defined(__NetBSD__) - if (apm_fd > 0) - close(apm_fd); - apm_fd = open("/dev/apm", O_RDONLY); - if (apm_fd < 0) { - warn("ERROR: battery applet cannot open /dev/apm."); - battery_found = 0; - } else { - battery_found = 1; - } -#elif defined(__FreeBSD__) - int sysctl_out = 0; - size_t len = sizeof(sysctl_out); - 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); -#elif defined(__linux) - battery_found = init_linux_batteries(); -#endif + battery_found = battery_os_init(); if (!battery_timeout) battery_timeout = add_timeout(10, 30000, update_battery_tick, 0, &battery_timeout); } char* battery_get_tooltip(void* obj) { -#if defined(__linux) - return linux_batteries_get_tooltip(); -#else - return g_strdup("No tooltip support for this OS!"); -#endif + return battery_os_tooltip(); } void init_battery_panel(void *p) @@ -258,100 +210,21 @@ void init_battery_panel(void *p) int update_battery() { - int seconds = 0; - int8_t new_percentage = 0; - int errors = 0; + int err; + /* reset */ battery_state.state = BATTERY_UNKNOWN; + battery_state.percentage = 0; + batstate_set_time(&battery_state, 0); -#if defined(__OpenBSD__) || defined(__NetBSD__) - struct apm_power_info info; - if (apm_fd > 0 && ioctl(apm_fd, APM_IOC_GETPOWER, &(info)) == 0) { - // best attempt at mapping to Linux battery states - switch (info.battery_state) { - case APM_BATT_CHARGING: - battery_state.state = BATTERY_CHARGING; - break; - default: - battery_state.state = BATTERY_DISCHARGING; - break; - } - - if (info.battery_life == 100) - battery_state.state = BATTERY_FULL; - - // no mapping for openbsd really - energy_full = 0; - energy_now = 0; - - if (info.minutes_left != -1) - seconds = info.minutes_left * 60; - else - seconds = -1; - - new_percentage = info.battery_life; - } else { - warn("power update: APM_IOC_GETPOWER"); - errors = 1; - } -#elif defined(__FreeBSD__) - int sysctl_out = 0; - size_t len = sizeof(sysctl_out); - - if (sysctlbyname("hw.acpi.battery.state", &sysctl_out, &len, NULL, 0) == 0) { - // attemp to map the battery state to Linux - battery_state.state = BATTERY_UNKNOWN; - switch(sysctl_out) { - case 1: - battery_state.state = BATTERY_DISCHARGING; - break; - case 2: - battery_state.state = BATTERY_CHARGING; - break; - default: - battery_state.state = BATTERY_FULL; - break; - } - } else { - fprintf(stderr, "power update: no such sysctl"); - errors = 1; - } - - // no mapping for freebsd - energy_full = 0; - energy_now = 0; - - if (sysctlbyname("hw.acpi.battery.time", &sysctl_out, &len, NULL, 0) != 0) - seconds = -1; - else - seconds = sysctl_out * 60; - - // charging or error - if (seconds < 0) - seconds = 0; - - if (sysctlbyname("hw.acpi.battery.life", &sysctl_out, &len, NULL, 0) != 0) - new_percentage = -1; - else - new_percentage = sysctl_out; -#else - update_linux_batteries(&battery_state.state, &new_percentage, &seconds); -#endif - - battery_state.time.hours = seconds / 3600; - seconds -= 3600 * battery_state.time.hours; - battery_state.time.minutes = seconds / 60; - seconds -= 60 * battery_state.time.minutes; - battery_state.time.seconds = seconds; - - battery_state.percentage = new_percentage; + err = battery_os_update(&battery_state); // clamp percentage to 100 in case battery is misreporting that its current charge is more than its max if (battery_state.percentage > 100) { battery_state.percentage = 100; } - return errors; + return err; } diff --git a/src/battery/battery.h b/src/battery/battery.h index f9441da..3148d38 100644 --- a/src/battery/battery.h +++ b/src/battery/battery.h @@ -76,6 +76,14 @@ static inline gchar* chargestate2str(enum chargestate state) { }; } +static inline void batstate_set_time(struct batstate *state, int seconds) { + state->time.hours = seconds / 3600; + seconds -= 3600 * state->time.hours; + state->time.minutes = seconds / 60; + seconds -= 60 * state->time.minutes; + state->time.seconds = seconds; +} + // default global data void default_battery(); @@ -93,11 +101,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, int8_t *percentage, int *seconds); -char* linux_batteries_get_tooltip(); -#endif +/* operating system specific functions */ +gboolean battery_os_init(); +void battery_os_free(); +int battery_os_update(struct batstate *state); +char* battery_os_tooltip(); #endif diff --git a/src/battery/dummy.c b/src/battery/dummy.c new file mode 100644 index 0000000..1639692 --- /dev/null +++ b/src/battery/dummy.c @@ -0,0 +1,40 @@ +/************************************************************************** +* +* Tint2 : Dummy battery (non-functional) +* +* 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. +**************************************************************************/ + +#include +#include "common.h" +#include "battery.h" + +#warning tint2 has no battery support for this operating system! + +gboolean battery_os_init() { + return FALSE; +} + +void battery_os_free() { + return; +} + +int battery_os_update(struct batstate *state) { + return -1; +} + +char* battery_os_tooltip() { + return strdup("Operating System not supported"); +} diff --git a/src/battery/freebsd.c b/src/battery/freebsd.c new file mode 100644 index 0000000..0bdf665 --- /dev/null +++ b/src/battery/freebsd.c @@ -0,0 +1,79 @@ +/************************************************************************** +* +* Tint2 : FreeBSD battery +* +* 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. +**************************************************************************/ + +#if defined(__FreeBSD__) + +#include +#include +#include + +#include "common.h" +#include "battery.h" + +gboolean battery_os_init() { + int sysctl_out = 0; + size_t len = sizeof(sysctl_out); + + return (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); +} + +void battery_os_free() { + return; +} + +int battery_os_update(struct batstate *state) { + int sysctl_out = 0; + size_t len = sizeof(sysctl_out); + gboolean err = 0; + + if (sysctlbyname("hw.acpi.battery.state", &sysctl_out, &len, NULL, 0) == 0) { + switch(sysctl_out) { + case 1: + state->state = BATTERY_DISCHARGING; + break; + case 2: + state->state = BATTERY_CHARGING; + break; + default: + state->state = BATTERY_FULL; + break; + } + } else { + fprintf(stderr, "power update: no such sysctl"); + err = -1; + } + + if (sysctlbyname("hw.acpi.battery.time", &sysctl_out, &len, NULL, 0) == 0) + batstate_set_time(state, sysctl_out * 60); + else + err = -1; + + if (sysctlbyname("hw.acpi.battery.life", &sysctl_out, &len, NULL, 0) == 0) + state->percentage = sysctl_out; + else + err = -1; + + return err; +} + +char* battery_os_tooltip() { + return strdup("Operating System not supported"); +} + +#endif diff --git a/src/battery/linux.c b/src/battery/linux.c index bae3d1a..7ce9deb 100644 --- a/src/battery/linux.c +++ b/src/battery/linux.c @@ -170,7 +170,7 @@ static gboolean init_linux_mains(struct psy_mains *ac) { return TRUE; } -void free_linux_batteries() { +void battery_os_free() { GList *l = batteries; while (l != NULL) { @@ -230,12 +230,12 @@ static void add_mains(const char *entryname) { } } -gboolean init_linux_batteries() { +gboolean battery_os_init() { GDir *directory = 0; GError *error = NULL; const char *entryname; - free_linux_batteries(); + battery_os_free(); directory = g_dir_open("/sys/class/power_supply", 0, &error); RETURN_ON_ERROR(error); @@ -332,12 +332,13 @@ static gboolean update_linux_mains(struct psy_mains *ac) { return TRUE; } -void update_linux_batteries(enum chargestate *state, int8_t *percentage, int *seconds) { +int battery_os_update(struct batstate *state) { GList *l; gint64 total_energy_now = 0; gint64 total_energy_full = 0; gint64 total_power_now = 0; + gint seconds = 0; gboolean charging = FALSE; gboolean discharging = FALSE; @@ -362,25 +363,26 @@ void update_linux_batteries(enum chargestate *state, int8_t *percentage, int *se } /* build global state */ - *state = BATTERY_UNKNOWN; if (charging && !discharging) - *state = BATTERY_CHARGING; + state->state = BATTERY_CHARGING; else if (!charging && discharging) - *state = BATTERY_DISCHARGING; + state->state = BATTERY_DISCHARGING; else if (!charging && !discharging && full) - *state = BATTERY_FULL; + state->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; + if(state->state == BATTERY_CHARGING) + seconds = 3600 * (total_energy_full - total_energy_now) / total_power_now; + else if(state->state == BATTERY_DISCHARGING) + seconds = 3600 * total_energy_now / total_power_now; } + batstate_set_time(state, seconds); /* calculate percentage */ - *percentage = energy_to_percent(total_energy_now, total_energy_full); + state->percentage = energy_to_percent(total_energy_now, total_energy_full); + + return 0; } static gchar* energy_human_readable(struct psy_battery *bat) { @@ -418,7 +420,7 @@ static gchar* power_human_readable(struct psy_battery *bat) { } } -char* linux_batteries_get_tooltip() { +char* battery_os_tooltip() { GList *l; GString *tooltip = g_string_new(""); gchar *result; diff --git a/src/battery/openbsd.c b/src/battery/openbsd.c new file mode 100644 index 0000000..75d2381 --- /dev/null +++ b/src/battery/openbsd.c @@ -0,0 +1,84 @@ +/************************************************************************** +* +* Tint2 : OpenBSD & NetBSD battery +* +* 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. +**************************************************************************/ + +#if defined(__OpenBSD__) || defined(__NetBSD__) + +//#include +#include +#include +#include +#include + +#include "common.h" +#include "battery.h" + +int apm_fd = -1; + +gboolean battery_os_init() { + if (apm_fd > 0) + close(apm_fd); + + apm_fd = open("/dev/apm", O_RDONLY); + + if (apm_fd < 0) { + warn("ERROR: battery applet cannot open /dev/apm."); + return FALSE; + } else { + return TRUE; + } +} + +void battery_os_free() { + if ((apm_fd != -1) && (close(apm_fd) == -1)) + warn("cannot close /dev/apm"); + apm_fd = -1; +} + +int battery_os_update(struct batstate *state) { + struct apm_power_info info; + + if (apm_fd > 0 && ioctl(apm_fd, APM_IOC_GETPOWER, &(info)) == 0) { + // best attempt at mapping to Linux battery states + switch (info.battery_state) { + case APM_BATT_CHARGING: + state->state = BATTERY_CHARGING; + break; + default: + state->state = BATTERY_DISCHARGING; + break; + } + + if (info.battery_life == 100) + state->state = BATTERY_FULL; + + if (info.minutes_left != -1) + batstate_set_time(state, info.minutes_left * 60); + + state->percentage = info.battery_life; + } else { + warn("power update: APM_IOC_GETPOWER"); + return -1; + } + + return 0; +} + +char* battery_os_tooltip() { + return strdup("Operating System not supported"); +} + +#endif From db490247e07974a711bd9b3b52d4f917ee6a8f48 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Fri, 7 Aug 2015 05:20:24 +0200 Subject: [PATCH 6/7] Battery: support for ac connection event cmd This add two new config options "ac_connected_cmd" and "ac_disconnected_cmd". These commands are executed when AC (mains supply) is connected or disconnected. --- src/battery/battery.c | 12 ++++++++++++ src/battery/battery.h | 4 ++++ src/battery/linux.c | 5 +++++ src/config.c | 12 ++++++++++++ 4 files changed, 33 insertions(+) diff --git a/src/battery/battery.c b/src/battery/battery.c index 56dcf27..058eab2 100644 --- a/src/battery/battery.c +++ b/src/battery/battery.c @@ -44,6 +44,8 @@ static char buf_bat_time[20]; int8_t battery_low_status; unsigned char battery_low_cmd_sent; +char *ac_connected_cmd; +char *ac_disconnected_cmd; char *battery_low_cmd; char *battery_lclick_command; char *battery_mclick_command; @@ -59,6 +61,7 @@ void update_battery_tick(void* arg) int old_found = battery_found; int old_percentage = battery_state.percentage; + int old_ac_connected = battery_state.ac_connected; int16_t old_hours = battery_state.time.hours; int8_t old_minutes = battery_state.time.minutes; @@ -71,6 +74,14 @@ void update_battery_tick(void* arg) // Try again update_battery(); } + + if (old_ac_connected != battery_state.ac_connected) { + if(battery_state.ac_connected) + tint_exec(ac_connected_cmd); + else + tint_exec(ac_disconnected_cmd); + } + if (old_found == battery_found && old_percentage == battery_state.percentage && old_hours == battery_state.time.hours && @@ -215,6 +226,7 @@ int update_battery() { /* reset */ battery_state.state = BATTERY_UNKNOWN; battery_state.percentage = 0; + battery_state.ac_connected = FALSE; batstate_set_time(&battery_state, 0); err = battery_os_update(&battery_state); diff --git a/src/battery/battery.h b/src/battery/battery.h index 3148d38..8142760 100644 --- a/src/battery/battery.h +++ b/src/battery/battery.h @@ -44,6 +44,7 @@ typedef struct batstate { int percentage; struct battime time; enum chargestate state; + gboolean ac_connected; } batstate; extern struct batstate battery_state; @@ -56,6 +57,9 @@ extern int percentage_hide; extern int8_t battery_low_status; extern char *battery_low_cmd; +extern char *ac_connected_cmd; +extern char *ac_disconnected_cmd; + extern char *battery_lclick_command; extern char *battery_mclick_command; extern char *battery_rclick_command; diff --git a/src/battery/linux.c b/src/battery/linux.c index 7ce9deb..f2ba53e 100644 --- a/src/battery/linux.c +++ b/src/battery/linux.c @@ -343,6 +343,7 @@ int battery_os_update(struct batstate *state) { gboolean charging = FALSE; gboolean discharging = FALSE; gboolean full = FALSE; + gboolean ac_connected = FALSE; for (l = batteries; l != NULL; l = l->next) { struct psy_battery *bat = l->data; @@ -360,6 +361,7 @@ int battery_os_update(struct batstate *state) { for (l = mains; l != NULL; l = l->next) { struct psy_mains *ac = l->data; update_linux_mains(ac); + ac_connected |= (ac->online); } /* build global state */ @@ -382,6 +384,9 @@ int battery_os_update(struct batstate *state) { /* calculate percentage */ state->percentage = energy_to_percent(total_energy_now, total_energy_full); + /* AC state */ + state->ac_connected = ac_connected; + return 0; } diff --git a/src/config.c b/src/config.c index 378e2b6..9a8c625 100644 --- a/src/config.c +++ b/src/config.c @@ -392,6 +392,18 @@ void add_entry (char *key, char *value) #ifdef ENABLE_BATTERY if (strlen(value) > 0) battery_low_cmd = strdup (value); +#endif + } + else if (strcmp (key, "ac_connected_cmd") == 0) { +#ifdef ENABLE_BATTERY + if (strlen(value) > 0) + ac_connected_cmd = strdup (value); +#endif + } + else if (strcmp (key, "ac_disconnected_cmd") == 0) { +#ifdef ENABLE_BATTERY + if (strlen(value) > 0) + ac_disconnected_cmd = strdup (value); #endif } else if (strcmp (key, "bat1_font") == 0) { From 169278c9d8b33e1e06e26007df8dc223ab56bf23 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Fri, 7 Aug 2015 05:33:33 +0200 Subject: [PATCH 7/7] tint2conf: add support for ac_connected_cmd and ac_disconnected_cmd --- src/tint2conf/properties.c | 45 +++++++++++++++++++++++++++++++++++ src/tint2conf/properties.h | 1 + src/tint2conf/properties_rw.c | 9 +++++++ 3 files changed, 55 insertions(+) diff --git a/src/tint2conf/properties.c b/src/tint2conf/properties.c index a4eec73..019a333 100644 --- a/src/tint2conf/properties.c +++ b/src/tint2conf/properties.c @@ -94,6 +94,7 @@ GtkWidget *battery_padding_x, *battery_padding_y, *battery_font_line1, *battery_ GtkWidget *battery_background; GtkWidget *battery_tooltip; GtkWidget *battery_left_command, *battery_mclick_command, *battery_right_command, *battery_uwheel_command, *battery_dwheel_command; +GtkWidget *ac_connected_cmd, *ac_disconnected_cmd; // systray GtkWidget *systray_icon_order, *systray_padding_x, *systray_padding_y, *systray_spacing; @@ -3817,6 +3818,50 @@ void create_battery(GtkWidget *parent) change_paragraph(parent); + label = gtk_label_new(_("AC connection events")); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_label_set_use_markup(GTK_LABEL(label), TRUE); + gtk_widget_show(label); + gtk_box_pack_start(GTK_BOX(parent), label, FALSE, FALSE, 0); + + table = gtk_table_new(2, 10, FALSE); + gtk_widget_show(table); + gtk_box_pack_start(GTK_BOX(parent), table, FALSE, FALSE, 0); + gtk_table_set_row_spacings(GTK_TABLE(table), ROW_SPACING); + gtk_table_set_col_spacings(GTK_TABLE(table), COL_SPACING); + + row = 0, col = 2; + label = gtk_label_new(_("AC connected command")); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_widget_show(label); + gtk_table_attach(GTK_TABLE(table), label, col, col+1, row, row+1, GTK_FILL, 0, 0, 0); + col++; + + ac_connected_cmd = gtk_entry_new(); + gtk_widget_show(ac_connected_cmd); + gtk_entry_set_width_chars(GTK_ENTRY(ac_connected_cmd), 50); + gtk_table_attach(GTK_TABLE(table), ac_connected_cmd, col, col+1, row, row+1, GTK_FILL, 0, 0, 0); + col++; + gtk_tooltips_set_tip(tooltips, ac_connected_cmd, + _("Specifies a command that will be executed when AC is connected to the system."), NULL); + + row++, col = 2; + label = gtk_label_new(_("AC disconnected command")); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_widget_show(label); + gtk_table_attach(GTK_TABLE(table), label, col, col+1, row, row+1, GTK_FILL, 0, 0, 0); + col++; + + ac_disconnected_cmd = gtk_entry_new(); + gtk_widget_show(ac_disconnected_cmd); + gtk_entry_set_width_chars(GTK_ENTRY(ac_disconnected_cmd), 50); + gtk_table_attach(GTK_TABLE(table), ac_disconnected_cmd, col, col+1, row, row+1, GTK_FILL, 0, 0, 0); + col++; + gtk_tooltips_set_tip(tooltips, ac_disconnected_cmd, + _("Specifies a command that will be executed when AC is disconnected to the system."), NULL); + + change_paragraph(parent); + label = gtk_label_new(_("Mouse events")); gtk_misc_set_alignment(GTK_MISC(label), 0, 0); gtk_label_set_use_markup(GTK_LABEL(label), TRUE); diff --git a/src/tint2conf/properties.h b/src/tint2conf/properties.h index 00a94b5..c61821a 100644 --- a/src/tint2conf/properties.h +++ b/src/tint2conf/properties.h @@ -97,6 +97,7 @@ extern GtkWidget *battery_padding_x, *battery_padding_y, *battery_font_line1, *b extern GtkWidget *battery_background; extern GtkWidget *battery_tooltip; extern GtkWidget *battery_left_command, *battery_mclick_command, *battery_right_command, *battery_uwheel_command, *battery_dwheel_command; +extern GtkWidget *ac_connected_cmd, *ac_disconnected_cmd; // systray extern GtkWidget *systray_icon_order, *systray_padding_x, *systray_padding_y, *systray_spacing; diff --git a/src/tint2conf/properties_rw.c b/src/tint2conf/properties_rw.c index 7f3affd..996297a 100644 --- a/src/tint2conf/properties_rw.c +++ b/src/tint2conf/properties_rw.c @@ -583,6 +583,9 @@ void config_write_battery(FILE *fp) fprintf(fp, "battery_uwheel_command = %s\n", gtk_entry_get_text(GTK_ENTRY(battery_uwheel_command))); fprintf(fp, "battery_dwheel_command = %s\n", gtk_entry_get_text(GTK_ENTRY(battery_dwheel_command))); + fprintf(fp, "ac_connected_cmd = %s\n", gtk_entry_get_text(GTK_ENTRY(ac_connected_cmd))); + fprintf(fp, "ac_disconnected_cmd = %s\n", gtk_entry_get_text(GTK_ENTRY(ac_disconnected_cmd))); + fprintf(fp, "\n"); } @@ -958,6 +961,12 @@ void add_entry(char *key, char *value) else if (strcmp(key, "battery_dwheel_command") == 0) { gtk_entry_set_text(GTK_ENTRY(battery_dwheel_command), value); } + else if (strcmp(key, "ac_connected_cmd") == 0) { + gtk_entry_set_text(GTK_ENTRY(ac_connected_cmd), value); + } + else if (strcmp(key, "ac_disconnected_cmd") == 0) { + gtk_entry_set_text(GTK_ENTRY(ac_disconnected_cmd), value); + } /* Clock */ else if (strcmp(key, "time1_format") == 0) {