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:
|
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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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 \
|
||||||
|
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();
|
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;
|
||||||
}
|
}
|
||||||
|
78
src/menu.c
78
src/menu.c
@ -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);
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
*
|
*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user