diff --git a/configure.ac b/configure.ac index 5f25f59..c03fd24 100644 --- a/configure.ac +++ b/configure.ac @@ -12,6 +12,7 @@ AC_CONFIG_HEADERS([config.h.in]) pkg_modules="glib-2.0 >= 2.6.0 \ gtk+-2.0 >= 2.6.0 \ gthread-2.0 \ + dbus-glib-1 \ lxpanel" PKG_CHECK_MODULES(PACKAGE, [$pkg_modules]) diff --git a/src/Makefile.am b/src/Makefile.am index 539a63d..3c28d2d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,7 +10,8 @@ module_LTLIBRARIES = cutiepi_battery.la moduledir = $(libdir) cutiepi_battery_la_SOURCES = \ - main.c + main.c \ + dbus.c cutiepi_battery_la_LIBADD = \ $(PACKAGE_LIBS) diff --git a/src/dbus.c b/src/dbus.c new file mode 100644 index 0000000..fc3d6c5 --- /dev/null +++ b/src/dbus.c @@ -0,0 +1,147 @@ +/** + * + * Copyright (c) 2022 Fabio Manganiello, see the file AUTHORS for details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include + +#include "dbus.h" + +static DBusConnection* conn; + +static DBusConnection *dbus_connect() +{ + DBusError err; + + dbus_error_init(&err); + conn = dbus_bus_get(DBUS_BUS_SESSION, &err); + + if (dbus_error_is_set(&err)) + { + fprintf(stderr, "Connection Error (%s)\n", err.message); + dbus_error_free(&err); + } + + if (!conn) + { + fprintf(stderr, "Cannot initialize the DBus connection"); + exit(1); + } + + return conn; +} + +static void add_signal_listener() +{ + DBusError err; + dbus_bus_add_match( + conn, + "type='signal',interface='io.cutiepi.interface',path='/mcu'", + &err); + + dbus_connection_flush(conn); + if (dbus_error_is_set(&err)) + { + fprintf(stderr, "Match Error (%s)\n", err.message); + exit(1); + } +} + +static void reset_battery_info(BatteryInfo* info) +{ + info->_is_parsed[0] = info->_is_parsed[1] = false; +} + +static bool is_battery_info_parsed(BatteryInfo* info) +{ + return info->_is_parsed[0] && info->_is_parsed[1]; +} + +static void process_signal(DBusMessage* msg, DBusMessageIter* iter, BatteryInfo* info) +{ + char current_type; + char* str_val = NO_STRING_VAL; + int int_val = NO_INT_VAL; + + while ((current_type = dbus_message_iter_get_arg_type(iter)) != DBUS_TYPE_INVALID) + { + if (current_type == DBUS_TYPE_STRING) + dbus_message_iter_get_basic(iter, &str_val); + else if (current_type == DBUS_TYPE_INT32) + dbus_message_iter_get_basic(iter, &int_val); + + if (strlen(str_val) && int_val != NO_INT_VAL) + { + if (!strncmp(str_val, BATTERY_LEVEL_VARNAME, strlen(str_val)-1)) + { + info->level = (int) MIN(MAX(((int_val * 100) / MAX_BATTERY_VOLTAGE), 0), 100); + info->_is_parsed[0] = true; + } + else if (!strncmp(str_val, BATTERY_STATE_VARNAME, strlen(str_val)-1)) + { + info->is_charging = int_val == BATTERY_STATE_CHARGING; + info->_is_parsed[1] = true; + } + + str_val = NO_STRING_VAL; + int_val = NO_INT_VAL; + } + + dbus_message_iter_next(iter); + } +} + +void dbus_init() +{ + dbus_connect(); + add_signal_listener(); +} + +void get_battery_info(BatteryInfo* info) +{ + DBusMessage* msg; + DBusMessageIter iter; + reset_battery_info(info); + + while (!is_battery_info_parsed(info)) + { + dbus_connection_read_write(conn, 0); + msg = dbus_connection_pop_message(conn); + + if (!msg) + { + sleep(1); + continue; + } + + if (dbus_message_is_signal(msg, "io.cutiepi.interface", "updateEvent")) + { + if (!dbus_message_iter_init(msg, &iter)) + fprintf(stderr, "Message has no arguments!\n"); + else + process_signal(msg, &iter, info); + } + + dbus_message_unref(msg); + } +} + diff --git a/src/dbus.h b/src/dbus.h new file mode 100644 index 0000000..a5ea9e9 --- /dev/null +++ b/src/dbus.h @@ -0,0 +1,47 @@ +/** + * + * Copyright (c) 2022 Fabio Manganiello, see the file AUTHORS for details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _DBUS_BATTERY_H +#define _DBUS_BATTERY_H + +#include +#include +#include +#include + +#define BATTERY_STATE_CHARGING (4) +#define BATTERY_STATE_DISCHARGING (5) +#define NO_STRING_VAL ("") +#define NO_INT_VAL (-1) +#define BATTERY_LEVEL_VARNAME ("battery") +#define BATTERY_STATE_VARNAME ("charge") +/* Expressed in mV (I guess). Value based on empirical evidence */ +#define MAX_BATTERY_VOLTAGE (4000) + +typedef struct { + uint8_t level; /* Battery level in percentage between 0-100 */ + bool is_charging; + bool _is_parsed[2]; +} BatteryInfo; + +extern void dbus_init(); +extern void get_battery_info(BatteryInfo* info); + +#endif /* _DBUS_BATTERY_H */ diff --git a/src/main.c b/src/main.c index 7bb3786..8218cb6 100644 --- a/src/main.c +++ b/src/main.c @@ -19,59 +19,62 @@ */ #include +#include #include -#define LabelSize 32 +#include "dbus.h" + +#define POLL_INTERVAL_MS 1000 + +static gboolean refresh_state(void* data) +{ + GtkWidget* label = (GtkWidget *) data; + BatteryInfo info; + char label_str[32] = {0}; + + get_battery_info(&info); + snprintf( + label_str, + sizeof(label_str)-1, + "%s %d%%", + info.is_charging ? "⚡" : "🔋", + info.level + ); + + gtk_label_set_text(GTK_LABEL(label), label_str); + return TRUE; +} GtkWidget *constructor(LXPanel *panel, config_setting_t *settings) { /* panel is a pointer to the panel and - settings is a pointer to the configuration data - since we don't use it, we'll make sure it doesn't - give us an error at compilation time */ + settings is a pointer to the configuration data + since we don't use it, we'll make sure it doesn't + give us an error at compilation time */ (void)panel; (void)settings; - // make a label out of the hostname - char cIdBuf[LabelSize + 1] = {'\0'}; - FILE *fp; - fp = fopen("/etc/hostname", "r"); - fgets(cIdBuf, LabelSize, fp); - fclose(fp); - - // create a label widget instance - GtkWidget *pLabel = gtk_label_new(cIdBuf); - - // set the label to be visible - gtk_widget_show(pLabel); - - // need to create a container to be able to set a border + dbus_init(); GtkWidget *p = gtk_event_box_new(); - - // our widget doesn't have a window... - // it is usually illegal to call gtk_widget_set_has_window() from application but for GtkEventBox it doesn't hurt gtk_widget_set_has_window(p, FALSE); - - // set border width gtk_container_set_border_width(GTK_CONTAINER(p), 1); - // add the label to the container - gtk_container_add(GTK_CONTAINER(p), pLabel); + GtkWidget *label = gtk_label_new(""); + gtk_container_add(GTK_CONTAINER(p), label); + gtk_widget_show(label); + + g_timeout_add(POLL_INTERVAL_MS, refresh_state, label); // set the size we want // gtk_widget_set_size_request(p, 100, 25); - // success!!! return p; } FM_DEFINE_MODULE(lxpanel_gtk, test) -/* Plugin descriptor. */ LXPanelPluginInit fm_module_init_lxpanel_gtk = { - .name = "CutiePi battery", - .description = "Displays the current state of the CutiePi battery", - .one_per_system = 1, - - // assigning our functions to provided pointers. - .new_instance = constructor}; + .name = "CutiePi battery", + .description = "Displays the current state of the CutiePi battery", + .new_instance = constructor +};