Use a hashtable for notification actions

As g_strv_contains is only available in GLib >= 2.44, we have to bump
the GLib requirements and the CI distros, too.
This commit is contained in:
Benedikt Heine 2018-11-27 18:34:32 +01:00
parent a359b74901
commit 52885ca6ad
7 changed files with 94 additions and 108 deletions

View File

@ -56,12 +56,6 @@ workflows:
requires: requires:
- misc-doxygen - misc-doxygen
- Alpine - Alpine
- compileandtest:
name: Ubuntu 14.04
distro: ubuntu-trusty
requires:
- misc-doxygen
- Alpine
- compileandtest: - compileandtest:
name: Ubuntu 16.04 name: Ubuntu 16.04
distro: ubuntu-xenial distro: ubuntu-xenial

View File

@ -14,7 +14,7 @@ addons:
- libnotify-dev - libnotify-dev
- libgtk-3-dev - libgtk-3-dev
- valgrind - valgrind
dist: trusty dist: xenial
sudo: false sudo: false
language: c language: c

View File

@ -23,7 +23,7 @@ LDFLAGS_DEBUG :=
pkg_config_packs := gio-2.0 \ pkg_config_packs := gio-2.0 \
gdk-pixbuf-2.0 \ gdk-pixbuf-2.0 \
"glib-2.0 >= 2.36" \ "glib-2.0 >= 2.44" \
pangocairo \ pangocairo \
x11 \ x11 \
xinerama \ xinerama \

View File

@ -145,7 +145,6 @@ static struct notification *dbus_message_to_notification(const gchar *sender, GV
struct notification *n = notification_create(); struct notification *n = notification_create();
n->actions = g_malloc0(sizeof(struct actions));
n->dbus_client = g_strdup(sender); n->dbus_client = g_strdup(sender);
n->dbus_valid = true; 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); n->body = g_variant_dup_string(content, NULL);
break; break;
case 5: case 5:
if (g_variant_is_of_type(content, G_VARIANT_TYPE_STRING_ARRAY)) if (g_variant_is_of_type(content, G_VARIANT_TYPE_STRING_ARRAY)) {
n->actions->actions = g_variant_dup_strv(content, &(n->actions->count)); 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; break;
case 6: case 6:
if (g_variant_is_of_type(content, G_VARIANT_TYPE_DICTIONARY)) { 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); g_variant_iter_free(iter);
} }
if (n->actions->count < 1)
g_clear_pointer(&n->actions, actions_free);
notification_init(n); notification_init(n);
return n; return n;
} }

View File

@ -141,6 +141,27 @@ void open_browser(const char *in)
g_free(url); 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 * Notify the corresponding client
* that an action has been invoked * that an action has been invoked
@ -148,39 +169,49 @@ void open_browser(const char *in)
void invoke_action(const char *action) void invoke_action(const char *action)
{ {
struct notification *invoked = NULL; struct notification *invoked = NULL;
char *action_identifier = NULL; uint id;
char *appname_begin = strchr(action, '['); char *data_start, *data_comma, *data_end;
if (!appname_begin) {
/* format: #<human readable> (<summary>)[<id>,<action>] */
data_start = strrchr(action, '[');
if (!data_start) {
LOG_W("Invalid action: '%s'", action); LOG_W("Invalid action: '%s'", action);
return; 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; 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) { iter = iter->next) {
struct notification *n = iter->data; struct notification *n = iter->data;
if (g_str_has_prefix(appname_begin, n->appname) && strlen(n->appname) == appname_len) { if (n->id != id)
if (!n->actions)
continue; continue;
for (int i = 0; i < n->actions->count; i += 2) { if (g_hash_table_contains(n->actions, action_key)) {
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; invoked = n;
action_identifier = a_identifier;
break; break;
} }
} }
}
if (invoked && action_key) {
signal_action_invoked(invoked, action_key);
} }
if (invoked && action_identifier) { g_free(action_key);
signal_action_invoked(invoked, action_identifier);
}
} }
/** /**
@ -289,7 +320,7 @@ static gpointer context_menu_thread(gpointer data)
// Reference and lock the notification if we need it // 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); notification_ref(n);
struct notification_lock *nl = struct notification_lock *nl =
@ -302,13 +333,12 @@ static gpointer context_menu_thread(gpointer data)
locked_notifications = g_list_prepend(locked_notifications, nl); 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) if (n->urls)
dmenu_input = string_append(dmenu_input, n->urls, "\n"); 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); dmenu_output = invoke_dmenu(dmenu_input);

View File

@ -25,7 +25,6 @@
static void notification_extract_urls(struct notification *n); static void notification_extract_urls(struct notification *n);
static void notification_format_message(struct notification *n); static void notification_format_message(struct notification *n);
static void notification_dmenu_string(struct notification *n);
/* see notification.h */ /* see notification.h */
const char *enum_to_string_fullscreen(enum behavior_fullscreen in) const char *enum_to_string_fullscreen(enum behavior_fullscreen in)
@ -75,16 +74,16 @@ void notification_print(const struct notification *n)
printf("\t}\n"); printf("\t}\n");
g_free(urls); g_free(urls);
} }
if (g_hash_table_size(n->actions) == 0) {
if (n->actions) { printf("\tactions: {}\n");
printf("\tactions:\n"); } else {
printf("\t{\n"); gpointer p_key, p_value;
for (int i = 0; i < n->actions->count; i += 2) { GHashTableIter iter;
printf("\t\t[%s,%s]\n", n->actions->actions[i], g_hash_table_iter_init(&iter, n->actions);
n->actions->actions[i + 1]); 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("\t}\n");
printf("\tactions_dmenu: %s\n", n->actions->dmenu_str);
} }
printf("\tscript: %s\n", n->script); printf("\tscript: %s\n", n->script);
printf("}\n"); printf("}\n");
@ -189,17 +188,6 @@ int notification_is_duplicate(const struct notification *a, const struct notific
&& a->urgency == b->urgency; && 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 */ /* see notification.h */
void rawimage_free(struct raw_image *i) 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->colors.frame);
g_free(n->stack_tag); g_free(n->stack_tag);
actions_free(n->actions); g_hash_table_unref(n->actions);
rawimage_free(n->raw_icon); rawimage_free(n->raw_icon);
notification_private_free(n->priv); notification_private_free(n->priv);
@ -318,6 +306,8 @@ struct notification *notification_create(void)
n->fullscreen = FS_SHOW; n->fullscreen = FS_SHOW;
n->actions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
return n; return n;
} }
@ -377,7 +367,6 @@ void notification_init(struct notification *n)
/* UPDATE derived fields */ /* UPDATE derived fields */
notification_extract_urls(n); notification_extract_urls(n);
notification_dmenu_string(n);
notification_format_message(n); notification_format_message(n);
} }
@ -508,23 +497,6 @@ static void notification_extract_urls(struct notification *n)
g_free(urls_text); 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) 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 */ /* print dup_count and msg */
if ((n->dup_count > 0 && !settings.hide_duplicate_count) 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", buf = g_strdup_printf("(%d%s%s) %s",
n->dup_count, n->dup_count,
n->actions ? "A" : "", g_hash_table_size(n->actions) ? "A" : "",
n->urls ? "U" : "", msg); 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", buf = g_strdup_printf("(%s%s) %s",
n->actions ? "A" : "", g_hash_table_size(n->actions) ? "A" : "",
n->urls ? "U" : "", msg); n->urls ? "U" : "", msg);
} else if (n->dup_count > 0 && !settings.hide_duplicate_count) { } else if (n->dup_count > 0 && !settings.hide_duplicate_count) {
buf = g_strdup_printf("(%d) %s", n->dup_count, msg); buf = g_strdup_printf("(%d) %s", n->dup_count, msg);
@ -584,17 +556,17 @@ void notification_update_text_to_render(struct notification *n)
/* see notification.h */ /* see notification.h */
void notification_do_action(const struct notification *n) void notification_do_action(const struct notification *n)
{ {
if (n->actions) { if (g_hash_table_size(n->actions)) {
if (n->actions->count == 2) { if (g_hash_table_contains(n->actions, "default")) {
signal_action_invoked(n, n->actions->actions[0]); signal_action_invoked(n, "default");
return; return;
} }
for (int i = 0; i < n->actions->count; i += 2) { if (g_hash_table_size(n->actions) == 1) {
if (STR_EQ(n->actions->actions[i], "default")) { GList *keys = g_hash_table_get_keys(n->actions);
signal_action_invoked(n, n->actions->actions[i]); signal_action_invoked(n, keys->data);
g_list_free(keys);
return; return;
} }
}
context_menu(); context_menu();
} else if (n->urls) { } else if (n->urls) {

View File

@ -36,12 +36,6 @@ struct raw_image {
unsigned char *data; unsigned char *data;
}; };
struct actions {
char **actions;
char *dmenu_str;
gsize count;
};
typedef struct _notification_private NotificationPrivate; typedef struct _notification_private NotificationPrivate;
struct notification_colors { struct notification_colors {
@ -69,7 +63,7 @@ struct notification {
gint64 timestamp; /**< arrival time */ gint64 timestamp; /**< arrival time */
gint64 timeout; /**< time to display */ gint64 timeout; /**< time to display */
struct actions *actions; GHashTable *actions;
enum markup_mode markup; enum markup_mode markup;
const char *format; const char *format;
@ -127,13 +121,6 @@ void notification_ref(struct notification *n);
*/ */
void notification_init(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 * Free a #raw_image
* *