tmisu

Notification to stdout daemon
git clone https://git.inz.fi/tmisu/
Log | Files | Refs | README | LICENSE

commit 732d6b3d422839853a0d8f90d07f07886eca3480
parent 2fca804c5ebd83c043d890aedfa0f93e5002f3b9
Author: Sweets <Sweets@users.noreply.github.com>
Date:   Sun, 13 Sep 2020 23:10:05 -0700

Major refactor

Diffstat:
MMakefile | 2+-
Dcallbacks.c | 257-------------------------------------------------------------------------------
Dcallbacks.h | 24------------------------
Aoutput.c | 172+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aoutput.h | 25+++++++++++++++++++++++++
Mtiramisu.c | 97++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Mtiramisu.h | 33+++++++++++++++++++++++++++++++++
7 files changed, 292 insertions(+), 318 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,6 +1,6 @@ TARGET = tiramisu -SRC := tiramisu.c callbacks.c +SRC := tiramisu.c output.c PREFIX ?= /usr/local diff --git a/callbacks.c b/callbacks.c @@ -1,257 +0,0 @@ -#include <stdio.h> -#include <glib.h> - -#include "tiramisu.h" -#include "callbacks.h" - -unsigned int notification_id = 0; - -char *sanitize(const char *string) { - /* allocating double the size of the original string should always be enough */ - char *out = calloc(strlen(string) * 2 + 1, 1); - - while (*string) { - if (*string == '"') - strcat(out, "\\\""); - else if (*string == '\n') - strcat(out, "\\n"); - else - out[strlen(out)] = *string; - string++; - } - - return out; -} - -void method_handler(GDBusConnection *connection, const gchar *sender, - const gchar *object, const gchar *interface, const gchar *method, - GVariant *parameters, GDBusMethodInvocation *invocation, - gpointer user_data) { - - GVariantIter iterator; - gchar *app_name; - guint32 replaces_id; - gchar *app_icon; - gchar *summary; - gchar *body; - gchar **actions; - GVariant *hints; - gint32 timeout; - GVariant *return_value = NULL; - - if (!strcmp(method, "Notify")) - goto output; - - if (!strcmp(method, "GetServerInformation")) { - return_value = g_variant_new("(ssss)", - "tiramisu", "Sweets", "1.0", "1.2"); - goto flush; - } - - print("Unhandled: %s %s\n", method, sender); - -output: - notification_id++; - - g_variant_iter_init(&iterator, parameters); - g_variant_iter_next(&iterator, "s", &app_name); - g_variant_iter_next(&iterator, "u", &replaces_id); - g_variant_iter_next(&iterator, "s", &app_icon); - g_variant_iter_next(&iterator, "s", &summary); - g_variant_iter_next(&iterator, "s", &body); - g_variant_iter_next(&iterator, "^a&s", &actions); - g_variant_iter_next(&iterator, "@a{sv}", &hints); - g_variant_iter_next(&iterator, "i", &timeout); - - char *app_name_sanitized = sanitize(app_name); - char *app_icon_sanitized = sanitize(app_icon); - printf( -#ifdef PRINT_JSON - "{ \"app_name\": \"%s\", \"app_icon\": \"%s\", ", -#else - "app_name: %s\napp_icon: %s\n", -#endif - app_name_sanitized, - app_icon_sanitized); - - free(app_name_sanitized); - free(app_icon_sanitized); - free(app_name); - free(app_icon); - - printf( -#ifdef PRINT_JSON - "\"replaces_id\": \"%u\", \"timeout\": \"%d\", ", -#else - "replaces_id: %u\ntimeout: %d\n", -#endif - replaces_id, timeout); - -#ifdef PRINT_JSON - printf("\"hints\": { "); -#else - printf("hints: "); -#endif - - gchar *key; - GVariant *value; - -#ifdef PRINT_JSON - const char *int_format = "\"%s\": %d"; - const char *uint_format = "\"%s\": %u"; -#else - const char *int_format = "\t%s: %d\n"; - const char *uint_format = "\t%s: %u\n"; -#endif - - unsigned int index = 0; - g_variant_iter_init(&iterator, hints); - while (g_variant_iter_loop(&iterator, "{sv}", &key, NULL)) { - -#ifdef PRINT_JSON - if (index > 0) - printf(", "); -#endif - - /* There has to be a better way. glib, why? */ - - if ((value = g_variant_lookup_value(hints, key, GT_STRING))) { - char *value_sanitized = sanitize(g_variant_get_string(value, NULL)); - printf( -#ifdef PRINT_JSON - "\"%s\": \"%s\"", -#else - "\t%s: %s\n", -#endif - key, value_sanitized); - free(value_sanitized); - } else if ((value = g_variant_lookup_value(hints, key, GT_INT16))) - printf(int_format, key, g_variant_get_int16(value)); - else if ((value = g_variant_lookup_value(hints, key, GT_INT32))) - printf(int_format, key, g_variant_get_int32(value)); - else if ((value = g_variant_lookup_value(hints, key, GT_INT64))) - printf(int_format, key, g_variant_get_int64(value)); - else if ((value = g_variant_lookup_value(hints, key, GT_UINT16))) - printf(uint_format, key, g_variant_get_uint16(value)); - else if ((value = g_variant_lookup_value(hints, key, GT_UINT32))) - printf(uint_format, key, g_variant_get_uint32(value)); - else if ((value = g_variant_lookup_value(hints, key, GT_UINT64))) - printf(uint_format, key, g_variant_get_uint64(value)); - else if ((value = g_variant_lookup_value(hints, key, GT_DOUBLE))) - printf( -#ifdef PRINT_JSON - "\"%s\": %f", -#else - "\t%s: %f\n", -#endif - key, g_variant_get_double(value)); - else if ((value = g_variant_lookup_value(hints, key, GT_BYTE))) - printf( -#ifdef PRINT_JSON - "\"%s\": %x", -#else - "\t%s: %x\n", -#endif - key, g_variant_get_byte(value)); - else if ((value = g_variant_lookup_value(hints, key, GT_BOOL))) - printf( -#ifdef PRINT_JSON - "\"%s\": %d", -#else - "\t%s: %d\n", -#endif - key, g_variant_get_boolean(value)); - - index++; - g_variant_unref(value); - } - - g_variant_unref(hints); - - index = 0; -#ifdef PRINT_JSON - printf("}, \"actions\": {"); -#else - printf("actions: "); -#endif - - while (actions[index] && actions[index + 1]) { -#ifdef PRINT_JSON - if (index > 0) - printf(", "); - printf("\"%s\": \"%s\"", -#else - printf("\t%s: %s\n", -#endif - actions[index + 1], actions[index]); - index += 2; - } - free(actions); - - char *summary_sanitized = sanitize(summary); - char *body_sanitized = sanitize(body); - printf( -#ifdef PRINT_JSON - "}, \"summary\": \"%s\", \"body\": \"%s\" }\n", -#else - "summary: %s\nbody: %s\n", -#endif - summary_sanitized, - body_sanitized); - free(summary_sanitized); - free(body_sanitized); - free(summary); - free(body); - - return_value = g_variant_new("(u)", notification_id); - - fflush(stdout); - - goto flush; - -flush: - g_dbus_method_invocation_return_value(invocation, return_value); - g_dbus_connection_flush(connection, NULL, NULL, NULL); - return; - -} - -void bus_acquired(GDBusConnection *connection, const gchar *name, - gpointer user_data) { - print("%s\n", "Bus has been acquired."); - - guint registered_object; - registered_object = g_dbus_connection_register_object(connection, - "/org/freedesktop/Notifications", - introspection->interfaces[0], - &(const GDBusInterfaceVTable){ method_handler }, - NULL, - NULL, - NULL); - - if (!registered_object) { - print("%s\n", "Unable to register."); - stop_main_loop(NULL); - } -} - -void name_acquired(GDBusConnection *connection, const gchar *name, - gpointer user_data) { - dbus_connection = connection; - print("%s\n", "Name has been acquired."); -} - -void name_lost(GDBusConnection *connection, const gchar *name, - gpointer user_data) { - // we lost the Notifications daemon name or couldn't acquire it, shutdown - - if (!connection) { - printf("%s; %s\n", - "Unable to connect to acquire org.freedesktop.Notifications", - "could not connect to dbus."); - stop_main_loop(NULL); - } - else - print("%s\n", "Successfully acquired org.freedesktop.Notifications"); - -} diff --git a/callbacks.h b/callbacks.h @@ -1,24 +0,0 @@ -#pragma once - -#include <gio/gio.h> -#include <glib.h> - -#define GT_STRING G_VARIANT_TYPE_STRING -#define GT_INT16 G_VARIANT_TYPE_INT16 -#define GT_INT32 G_VARIANT_TYPE_INT32 -#define GT_INT64 G_VARIANT_TYPE_INT64 -#define GT_UINT16 G_VARIANT_TYPE_UINT16 -#define GT_UINT32 G_VARIANT_TYPE_UINT32 -#define GT_UINT64 G_VARIANT_TYPE_UINT64 -#define GT_DOUBLE G_VARIANT_TYPE_DOUBLE -#define GT_BOOL G_VARIANT_TYPE_BOOLEAN -#define GT_BYTE G_VARIANT_TYPE_BYTE - -extern unsigned int notification_id; - -void method_handler(GDBusConnection*, const gchar*, const gchar*, const gchar*, - const gchar*, GVariant*, GDBusMethodInvocation*, gpointer); - -void bus_acquired(GDBusConnection*, const gchar*, gpointer); -void name_acquired(GDBusConnection*, const gchar*, gpointer); -void name_lost(GDBusConnection*, const gchar*, gpointer); diff --git a/output.c b/output.c @@ -0,0 +1,172 @@ +#include <stdio.h> +#include <glib.h> + +#include "tiramisu.h" +#include "output.h" + +char *sanitize(const char *string) { + /* allocating double the size of the original string should be enough */ + char *out = calloc(strlen(string) * 2 + 1, 1); + + while (*string) { + if (*string == '"') + strcat(out, "\\\""); + else if (*string == '\n') + strcat(out, "\\n"); + else + out[strlen(out)] = *string; + string++; + } + + return out; +} + +void output_notification(GVariant *parameters) { + GVariantIter iterator; + gchar *app_name; + guint32 replaces_id; + gchar *app_icon; + gchar *summary; + gchar *body; + gchar **actions; + GVariant *hints; + gint32 timeout; + + g_variant_iter_init(&iterator, parameters); + g_variant_iter_next(&iterator, "s", &app_name); + g_variant_iter_next(&iterator, "u", &replaces_id); + g_variant_iter_next(&iterator, "s", &app_icon); + g_variant_iter_next(&iterator, "s", &summary); + g_variant_iter_next(&iterator, "s", &body); + g_variant_iter_next(&iterator, "^a&s", &actions); + g_variant_iter_next(&iterator, "@a{sv}", &hints); + g_variant_iter_next(&iterator, "i", &timeout); + + char *app_name_sanitized = sanitize(app_name); + char *app_icon_sanitized = sanitize(app_icon); + char *summary_sanitized = sanitize(summary); + char *body_sanitized = sanitize(body); + + json_output(app_name_sanitized, app_icon_sanitized, replaces_id, timeout, + hints, actions, summary_sanitized, body_sanitized); + + free(app_name_sanitized); + free(app_icon_sanitized); + free(app_name); + free(app_icon); + + free(actions); + + free(summary); + free(body); + free(summary_sanitized); + free(body_sanitized); + + fflush(stdout); +} + +void hints_output_iterator(GVariant *hints, const char *str_format, + const char *int_format, const char *uint_format, + const char *double_format, const char *boolean_format, + const char *byte_format) { + + GVariantIter iterator; + gchar *key; + GVariant *value; + + unsigned int index = 0; + char *value_sanitized; + + g_variant_iter_init(&iterator, hints); + while (g_variant_iter_loop(&iterator, "{sv}", &key, NULL)) { + if (index) + printf(", "); + + /* Strings */ + if ((value = g_variant_lookup_value(hints, key, GT_STRING))) { + value_sanitized = sanitize(g_variant_get_string(value, NULL)); + printf(str_format, key, value_sanitized); + free(value_sanitized); + } + /* Integers */ + else if ((value = g_variant_lookup_value(hints, key, GT_INT16))) + printf(int_format, key, g_variant_get_int16(value)); + else if ((value = g_variant_lookup_value(hints, key, GT_INT32))) + printf(int_format, key, g_variant_get_int32(value)); + else if ((value = g_variant_lookup_value(hints, key, GT_INT64))) + printf(int_format, key, g_variant_get_int64(value)); + /* Unsigned integers */ + else if ((value = g_variant_lookup_value(hints, key, GT_UINT16))) + printf(uint_format, key, g_variant_get_uint16(value)); + else if ((value = g_variant_lookup_value(hints, key, GT_UINT32))) + printf(uint_format, key, g_variant_get_uint32(value)); + else if ((value = g_variant_lookup_value(hints, key, GT_UINT64))) + printf(uint_format, key, g_variant_get_uint64(value)); + /* Doubles */ + else if ((value = g_variant_lookup_value(hints, key, GT_DOUBLE))) + printf(double_format, key, g_variant_get_double(value)); + /* Bytes */ + else if ((value = g_variant_lookup_value(hints, key, GT_BYTE))) + printf(byte_format, key, g_variant_get_byte(value)); + /* Booleans */ + else if ((value = g_variant_lookup_value(hints, key, GT_BOOL))) + printf(boolean_format, key, g_variant_get_boolean(value)); + + g_variant_unref(value); + index++; + } + + g_variant_unref(hints); +} + +void default_output(gchar *app_name, gchar *app_icon, guint32 replaces_id, + gint32 timeout, GVariant *hints, gchar **actions, gchar *summary, + gchar *body) { + + printf("app_name: %s\napp_icon: %s\nreplaces_id: %u\ntimeout: %d\n", + app_name, app_icon, replaces_id, timeout); + + printf("hints:\n"); + hints_output_iterator(hints, "\t%s: %s\n", "\t%s: %d\n", "\t%s: %u", + "\t%s: %f\n", "\t%s: %x\n", "\t%s: %d\n"); + printf("actions:\n"); + + unsigned int index = 0; + while (actions[index] && actions[index + 1]) { + printf("\t%s: %s\n", actions[index + 1], actions[index]); + index += 2; + } + + printf("summary: %s\nbody: %s", summary, body); + +} + +void json_output(gchar *app_name, gchar *app_icon, guint32 replaces_id, + gint32 timeout, GVariant *hints, gchar **actions, gchar *summary, + gchar *body) { + + printf("{" + "\"app_name\": \"%s\", " + "\"app_icon\": \"%s\", " + "\"replaces_id\": %u, " + "\"timeout\": %d, ", + app_name, app_icon, replaces_id, timeout); + + printf("\"hints\": {"); + hints_output_iterator(hints, "\"%s\": \"%s\"", "\"%s\": %d", "\"%s\": %u", + "\"%s\": %f", "\"%s\": %x", "\"%s\": %d"); + printf("}, \"actions\": {"); + + unsigned int index = 0; + while (actions[index] && actions[index + 1]) { + if (index) + printf(", "); + printf("\"%s\": \"%s\"", actions[index + 1], actions[index]); + index += 2; + } + + printf("}, "); + printf("\"summary\": \"%s\", " + "\"body\": \"%s\"}\n", summary, body); + +} diff --git a/output.h b/output.h @@ -0,0 +1,25 @@ +#pragma once + +#include <gio/gio.h> +#include <glib.h> + +#define GT_STRING G_VARIANT_TYPE_STRING +#define GT_INT16 G_VARIANT_TYPE_INT16 +#define GT_INT32 G_VARIANT_TYPE_INT32 +#define GT_INT64 G_VARIANT_TYPE_INT64 +#define GT_UINT16 G_VARIANT_TYPE_UINT16 +#define GT_UINT32 G_VARIANT_TYPE_UINT32 +#define GT_UINT64 G_VARIANT_TYPE_UINT64 +#define GT_DOUBLE G_VARIANT_TYPE_DOUBLE +#define GT_BOOL G_VARIANT_TYPE_BOOLEAN +#define GT_BYTE G_VARIANT_TYPE_BYTE + +char *sanitize(const char*); + +void output_notification(GVariant*); +void hints_output_iterator(GVariant*, const char*, const char*, const char*, + const char*, const char*, const char*); +void default_output(gchar*, gchar*, guint32, gint32, GVariant*, gchar**, gchar*, + gchar*); +void json_output(gchar*, gchar*, guint32, gint32, GVariant*, gchar**, gchar*, + gchar*); diff --git a/tiramisu.c b/tiramisu.c @@ -7,43 +7,13 @@ #include <glib-unix.h> #include "tiramisu.h" -#include "callbacks.h" +#include "output.h" #include "config.h" GDBusConnection *dbus_connection = NULL; GDBusNodeInfo *introspection = NULL; GMainLoop *main_loop = NULL; - -/* Build introspection XML based on configuration */ - -const char *xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" - "<node name=\"/org/freedesktop/Notifications\">\n" - " <interface name=\"org.freedesktop.Notifications\">\n" - - " <method name=\"Notify\">\n" - " <arg direction=\"in\" type=\"s\" name=\"app_name\"/>\n" - " <arg direction=\"in\" type=\"u\" name=\"replaces_id\"/>\n" - " <arg direction=\"in\" type=\"s\" name=\"app_icon\"/>\n" - " <arg direction=\"in\" type=\"s\" name=\"summary\"/>\n" - " <arg direction=\"in\" type=\"s\" name=\"body\"/>\n" - " <arg direction=\"in\" type=\"as\" name=\"actions\"/>\n" - " <arg direction=\"in\" type=\"a{sv}\" name=\"hints\"/>\n" - " <arg direction=\"in\" type=\"i\"" - " name=\"expire_timeout\"/>\n" - " <arg direction=\"out\" type=\"u\"" - " name=\"id\"/>\n" - " </method>\n" - - - " <method name=\"GetServerInformation\">\n" - " <arg direction=\"out\" type=\"s\" name=\"name\"/>\n" - " <arg direction=\"out\" type=\"s\" name=\"vendor\"/>\n" - " <arg direction=\"out\" type=\"s\" name=\"version\"/>\n" - " <arg direction=\"out\" type=\"s\" name=\"spec_version\"/>\n" - " </method>\n" - - " </interface>\n" - "</node>"; +unsigned int notification_id = 0; gboolean stop_main_loop(gpointer user_data) { g_main_loop_quit(main_loop); @@ -56,13 +26,13 @@ int main(int argc, char **argv) { /* Connect to DBUS */ - introspection = g_dbus_node_info_new_for_xml(xml, NULL); + introspection = g_dbus_node_info_new_for_xml(INTROSPECTION_XML, NULL); owned_name = g_bus_own_name(G_BUS_TYPE_SESSION, "org.freedesktop.Notifications", G_BUS_NAME_OWNER_FLAGS_NONE, - (GBusAcquiredCallback)bus_acquired, /* bus_acquired_handler */ - (GBusNameAcquiredCallback)name_acquired, /* name_acquired_handler */ - (GBusNameLostCallback)name_lost, /* name_lost_handler */ + (GBusAcquiredCallback)bus_acquired, + (GBusNameAcquiredCallback)name_acquired, + (GBusNameLostCallback)name_lost, NULL, /* user_data */ NULL); /* user_data_free_func */ @@ -83,3 +53,58 @@ int main(int argc, char **argv) { g_bus_unown_name(owned_name); } + +void bus_acquired(GDBusConnection *connection, const gchar *name, + gpointer user_data) { + print("%s\n", "Bus has been acquired."); + + guint registered_object = g_dbus_connection_register_object(connection, + "/org/freedesktop/Notifications", introspection->interfaces[0], + &(const GDBusInterfaceVTable){ method_handler }, NULL, NULL, NULL); + + if (!registered_object) { + print("%s\n", "Unable to register."); + stop_main_loop(NULL); + } +} + +void name_acquired(GDBusConnection *connection, const gchar *name, + gpointer user_data) { + dbus_connection = connection; + print("%s\n", "Name has been acquired."); +} + +void name_lost(GDBusConnection *connection, const gchar *name, + gpointer user_data) { + // we lost the Notifications daemon name or couldn't acquire it, shutdown + + if (!connection) { + printf("%s; %s\n", + "Unable to connect to acquire org.freedesktop.Notifications", + "could not connect to dbus."); + stop_main_loop(NULL); + } + else + print("%s\n", "Successfully acquired org.freedesktop.Notifications"); +} + +void method_handler(GDBusConnection *connection, const gchar *sender, + const gchar *object, const gchar *interface, const gchar *label, + GVariant *parameters, GDBusMethodInvocation *invocation, + gpointer user_data) { + + GVariant *return_value = NULL; + + if (!strcmp(label, "GetServerInformation")) + return_value = g_variant_new("(ssss)", + "tiramisu", "Sweets", "1.0", "1.2"); + else if (!strcmp(label, "Notify")) { + output_notification(parameters); + return_value = g_variant_new("(u)", ++notification_id); + } else + print("Unhandled: %s %s\n", label, sender); + + g_dbus_method_invocation_return_value(invocation, return_value); + g_dbus_connection_flush(connection, NULL, NULL, NULL); + +} diff --git a/tiramisu.h b/tiramisu.h @@ -12,10 +12,43 @@ extern GDBusConnection *dbus_connection; extern GDBusNodeInfo *introspection; extern GMainLoop *main_loop; +extern unsigned int notification_id; + #ifdef DEBUG #define print(...) fprintf(stderr, __VA_ARGS__); #else #define print(...) (void)(__VA_ARGS__); #endif +#define INTROSPECTION_XML "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"\ + "<node name=\"/org/freedesktop/Notifications\">\n"\ + " <interface name=\"org.freedesktop.Notifications\">\n"\ + " <method name=\"Notify\">\n"\ + " <arg direction=\"in\" type=\"s\" name=\"app_name\"/>\n"\ + " <arg direction=\"in\" type=\"u\""\ + " name=\"replaces_id\"/>\n"\ + " <arg direction=\"in\" type=\"s\" name=\"app_icon\"/>\n"\ + " <arg direction=\"in\" type=\"s\" name=\"summary\"/>\n"\ + " <arg direction=\"in\" type=\"s\" name=\"body\"/>\n"\ + " <arg direction=\"in\" type=\"as\" name=\"actions\"/>\n"\ + " <arg direction=\"in\" type=\"a{sv}\" name=\"hints\"/>\n"\ + " <arg direction=\"in\" type=\"i\""\ + " name=\"expire_timeout\"/>\n"\ + " <arg direction=\"out\" type=\"u\""\ + " name=\"id\"/>\n"\ + " </method>\n"\ + " <method name=\"GetServerInformation\">\n"\ + " <arg direction=\"out\" type=\"s\" name=\"name\"/>\n"\ + " <arg direction=\"out\" type=\"s\" name=\"vendor\"/>\n"\ + " <arg direction=\"out\" type=\"s\" name=\"version\"/>\n"\ + " <arg direction=\"out\" type=\"s\" name=\"spec_version\"/>\n"\ + " </method>\n"\ + " </interface>\n"\ + "</node>" + gboolean stop_main_loop(gpointer); +void bus_acquired(GDBusConnection*, const gchar*, gpointer); +void name_acquired(GDBusConnection*, const gchar*, gpointer); +void name_lost(GDBusConnection*, const gchar*, gpointer); +void method_handler(GDBusConnection*, const gchar*, const gchar*, const gchar*, + const gchar*, GVariant*, GDBusMethodInvocation*, gpointer);