diff --git a/.circleci/config.yml b/.circleci/config.yml index d21b045..9609b9e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -56,12 +56,6 @@ workflows: requires: - misc-doxygen - Alpine - - compileandtest: - name: Ubuntu 14.04 - distro: ubuntu-trusty - requires: - - misc-doxygen - - Alpine - compileandtest: name: Ubuntu 16.04 distro: ubuntu-xenial diff --git a/.travis.yml b/.travis.yml index de3d0ed..999c6a9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ addons: - libnotify-dev - libgtk-3-dev - valgrind -dist: trusty +dist: xenial sudo: false language: c diff --git a/config.mk b/config.mk index 05380d9..c242dd9 100644 --- a/config.mk +++ b/config.mk @@ -23,7 +23,7 @@ LDFLAGS_DEBUG := pkg_config_packs := gio-2.0 \ gdk-pixbuf-2.0 \ - "glib-2.0 >= 2.36" \ + "glib-2.0 >= 2.44" \ pangocairo \ x11 \ xinerama \ diff --git a/src/dbus.c b/src/dbus.c index bc07096..8ed9c81 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -145,7 +145,6 @@ static struct notification *dbus_message_to_notification(const gchar *sender, GV struct notification *n = notification_create(); - n->actions = g_malloc0(sizeof(struct actions)); n->dbus_client = g_strdup(sender); n->dbus_valid = true; @@ -178,8 +177,15 @@ static struct notification *dbus_message_to_notification(const gchar *sender, GV n->body = g_variant_dup_string(content, NULL); break; case 5: - if (g_variant_is_of_type(content, G_VARIANT_TYPE_STRING_ARRAY)) - n->actions->actions = g_variant_dup_strv(content, &(n->actions->count)); + if (g_variant_is_of_type(content, G_VARIANT_TYPE_STRING_ARRAY)) { + gsize amount; + const gchar **out = g_variant_get_strv(content, &amount); + + for(gsize i = 0; i+1 < amount; i+=2) + g_hash_table_insert(n->actions, g_strdup(out[i]), g_strdup(out[i+1])); + + g_free(out); + } break; case 6: if (g_variant_is_of_type(content, G_VARIANT_TYPE_DICTIONARY)) { @@ -283,9 +289,6 @@ static struct notification *dbus_message_to_notification(const gchar *sender, GV g_variant_iter_free(iter); } - if (n->actions->count < 1) - g_clear_pointer(&n->actions, actions_free); - notification_init(n); return n; } diff --git a/src/menu.c b/src/menu.c index 6c97919..1c52a5a 100644 --- a/src/menu.c +++ b/src/menu.c @@ -141,6 +141,27 @@ void open_browser(const char *in) g_free(url); } +char *notification_dmenu_string(struct notification *n) +{ + char *dmenu_str = NULL; + + gpointer p_key; + gpointer p_value; + GHashTableIter iter; + g_hash_table_iter_init(&iter, n->actions); + while (g_hash_table_iter_next(&iter, &p_key, &p_value)) { + + char *key = (char*) p_key; + char *value = (char*) p_value; + + char *act_str = g_strdup_printf("#%s (%s) [%d,%s]", value, n->summary, n->id, key); + dmenu_str = string_append(dmenu_str, act_str, "\n"); + + g_free(act_str); + } + return dmenu_str; +} + /* * Notify the corresponding client * that an action has been invoked @@ -148,39 +169,49 @@ void open_browser(const char *in) void invoke_action(const char *action) { struct notification *invoked = NULL; - char *action_identifier = NULL; + uint id; - char *appname_begin = strchr(action, '['); - if (!appname_begin) { + char *data_start, *data_comma, *data_end; + + /* format: # ()[,] */ + data_start = strrchr(action, '['); + if (!data_start) { LOG_W("Invalid action: '%s'", action); return; } - appname_begin++; - int appname_len = strlen(appname_begin) - 1; // remove ] - int action_len = strlen(action) - appname_len - 3; // remove space, [, ] - for (const GList *iter = queues_get_displayed(); iter; - iter = iter->next) { + id = strtol(++data_start, &data_comma, 10); + if (*data_comma != ',') { + LOG_W("Invalid action: '%s'", action); + return; + } + + data_end = strchr(data_comma+1, ']'); + if (!data_end) { + LOG_W("Invalid action: '%s'", action); + return; + } + + char *action_key = g_strndup(data_comma+1, data_end-data_comma-1); + + for (const GList *iter = queues_get_displayed(); + iter; + iter = iter->next) { struct notification *n = iter->data; - if (g_str_has_prefix(appname_begin, n->appname) && strlen(n->appname) == appname_len) { - if (!n->actions) - continue; + if (n->id != id) + continue; - for (int i = 0; i < n->actions->count; i += 2) { - char *a_identifier = n->actions->actions[i]; - char *name = n->actions->actions[i + 1]; - if (g_str_has_prefix(action, name) && strlen(name) == action_len) { - invoked = n; - action_identifier = a_identifier; - break; - } - } + if (g_hash_table_contains(n->actions, action_key)) { + invoked = n; + break; } } - if (invoked && action_identifier) { - signal_action_invoked(invoked, action_identifier); + if (invoked && action_key) { + signal_action_invoked(invoked, action_key); } + + g_free(action_key); } /** @@ -289,7 +320,7 @@ static gpointer context_menu_thread(gpointer data) // Reference and lock the notification if we need it - if (n->urls || n->actions) { + if (n->urls || g_hash_table_size(n->actions)) { notification_ref(n); struct notification_lock *nl = @@ -302,13 +333,12 @@ static gpointer context_menu_thread(gpointer data) locked_notifications = g_list_prepend(locked_notifications, nl); } + char *dmenu_str = notification_dmenu_string(n); + dmenu_input = string_append(dmenu_input, dmenu_str, "\n"); + g_free(dmenu_str); + if (n->urls) dmenu_input = string_append(dmenu_input, n->urls, "\n"); - - if (n->actions) - dmenu_input = - string_append(dmenu_input, n->actions->dmenu_str, - "\n"); } dmenu_output = invoke_dmenu(dmenu_input); diff --git a/src/notification.c b/src/notification.c index 9657cc1..f9bde8a 100644 --- a/src/notification.c +++ b/src/notification.c @@ -25,7 +25,6 @@ static void notification_extract_urls(struct notification *n); static void notification_format_message(struct notification *n); -static void notification_dmenu_string(struct notification *n); /* see notification.h */ const char *enum_to_string_fullscreen(enum behavior_fullscreen in) @@ -75,16 +74,16 @@ void notification_print(const struct notification *n) printf("\t}\n"); g_free(urls); } - - if (n->actions) { - printf("\tactions:\n"); - printf("\t{\n"); - for (int i = 0; i < n->actions->count; i += 2) { - printf("\t\t[%s,%s]\n", n->actions->actions[i], - n->actions->actions[i + 1]); - } + if (g_hash_table_size(n->actions) == 0) { + printf("\tactions: {}\n"); + } else { + gpointer p_key, p_value; + GHashTableIter iter; + g_hash_table_iter_init(&iter, n->actions); + printf("\tactions: {\n"); + while (g_hash_table_iter_next(&iter, &p_key, &p_value)) + printf("\t\t\"%s\": \"%s\"\n", (char*)p_key, (char*)p_value); printf("\t}\n"); - printf("\tactions_dmenu: %s\n", n->actions->dmenu_str); } printf("\tscript: %s\n", n->script); printf("}\n"); @@ -189,17 +188,6 @@ int notification_is_duplicate(const struct notification *a, const struct notific && a->urgency == b->urgency; } -/* see notification.h */ -void actions_free(struct actions *a) -{ - if (!a) - return; - - g_strfreev(a->actions); - g_free(a->dmenu_str); - g_free(a); -} - /* see notification.h */ void rawimage_free(struct raw_image *i) { @@ -253,7 +241,7 @@ void notification_unref(struct notification *n) g_free(n->colors.frame); g_free(n->stack_tag); - actions_free(n->actions); + g_hash_table_unref(n->actions); rawimage_free(n->raw_icon); notification_private_free(n->priv); @@ -318,6 +306,8 @@ struct notification *notification_create(void) n->fullscreen = FS_SHOW; + n->actions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + return n; } @@ -377,7 +367,6 @@ void notification_init(struct notification *n) /* UPDATE derived fields */ notification_extract_urls(n); - notification_dmenu_string(n); notification_format_message(n); } @@ -508,23 +497,6 @@ static void notification_extract_urls(struct notification *n) g_free(urls_text); } -static void notification_dmenu_string(struct notification *n) -{ - if (n->actions) { - g_clear_pointer(&n->actions->dmenu_str, g_free); - for (int i = 0; i < n->actions->count; i += 2) { - char *human_readable = n->actions->actions[i + 1]; - string_replace_char('[', '(', human_readable); // kill square brackets - string_replace_char(']', ')', human_readable); - - char *act_str = g_strdup_printf("#%s [%s]", human_readable, n->appname); - if (act_str) { - n->actions->dmenu_str = string_append(n->actions->dmenu_str, act_str, "\n"); - g_free(act_str); - } - } - } -} void notification_update_text_to_render(struct notification *n) { @@ -536,14 +508,14 @@ void notification_update_text_to_render(struct notification *n) /* print dup_count and msg */ if ((n->dup_count > 0 && !settings.hide_duplicate_count) - && (n->actions || n->urls) && settings.show_indicators) { + && (g_hash_table_size(n->actions) || n->urls) && settings.show_indicators) { buf = g_strdup_printf("(%d%s%s) %s", n->dup_count, - n->actions ? "A" : "", + g_hash_table_size(n->actions) ? "A" : "", n->urls ? "U" : "", msg); - } else if ((n->actions || n->urls) && settings.show_indicators) { + } else if ((g_hash_table_size(n->actions) || n->urls) && settings.show_indicators) { buf = g_strdup_printf("(%s%s) %s", - n->actions ? "A" : "", + g_hash_table_size(n->actions) ? "A" : "", n->urls ? "U" : "", msg); } else if (n->dup_count > 0 && !settings.hide_duplicate_count) { buf = g_strdup_printf("(%d) %s", n->dup_count, msg); @@ -584,16 +556,16 @@ void notification_update_text_to_render(struct notification *n) /* see notification.h */ void notification_do_action(const struct notification *n) { - if (n->actions) { - if (n->actions->count == 2) { - signal_action_invoked(n, n->actions->actions[0]); + if (g_hash_table_size(n->actions)) { + if (g_hash_table_contains(n->actions, "default")) { + signal_action_invoked(n, "default"); return; } - for (int i = 0; i < n->actions->count; i += 2) { - if (STR_EQ(n->actions->actions[i], "default")) { - signal_action_invoked(n, n->actions->actions[i]); - return; - } + if (g_hash_table_size(n->actions) == 1) { + GList *keys = g_hash_table_get_keys(n->actions); + signal_action_invoked(n, keys->data); + g_list_free(keys); + return; } context_menu(); diff --git a/src/notification.h b/src/notification.h index cc1e26e..f8f23e8 100644 --- a/src/notification.h +++ b/src/notification.h @@ -36,12 +36,6 @@ struct raw_image { unsigned char *data; }; -struct actions { - char **actions; - char *dmenu_str; - gsize count; -}; - typedef struct _notification_private NotificationPrivate; struct notification_colors { @@ -69,7 +63,7 @@ struct notification { gint64 timestamp; /**< arrival time */ gint64 timeout; /**< time to display */ - struct actions *actions; + GHashTable *actions; enum markup_mode markup; const char *format; @@ -127,13 +121,6 @@ void notification_ref(struct notification *n); */ void notification_init(struct notification *n); -/** - * Free the actions structure - * - * @param a (nullable): Pointer to #actions - */ -void actions_free(struct actions *a); - /** * Free a #raw_image *