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:
parent
a359b74901
commit
52885ca6ad
@ -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
|
||||
|
@ -14,7 +14,7 @@ addons:
|
||||
- libnotify-dev
|
||||
- libgtk-3-dev
|
||||
- valgrind
|
||||
dist: trusty
|
||||
dist: xenial
|
||||
sudo: false
|
||||
language: c
|
||||
|
||||
|
@ -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 \
|
||||
|
15
src/dbus.c
15
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;
|
||||
}
|
||||
|
86
src/menu.c
86
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: #<human readable> (<summary>)[<id>,<action>] */
|
||||
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);
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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
|
||||
*
|
||||
|
Loading…
x
Reference in New Issue
Block a user