29static bool is_initialized =
false;
30static regex_t url_regex;
32static gpointer context_menu_thread(gpointer data);
35 GList *locked_notifications;
49 "\\<(https?://|ftps?://|news://|mailto:|file://|www\\.)"
50 "[-[:alnum:]_\\@;/?:&=%$.+!*\x27,~#]*"
51 "(\\([-[:alnum:]_\\@;/?:&=%$.+!*\x27,~#]*\\)|[-[:alnum:]_\\@;/?:&=%$+*~])+";
52 int code = regcomp(&url_regex, regex, REG_EXTENDED | REG_ICASE);
55 regerror(code, &url_regex, error_buf,
sizeof(error_buf));
56 LOG_W(
"Failed to compile URL-matching regex: %s", error_buf);
59 is_initialized =
true;
64void regex_teardown(
void)
68 is_initialized =
false;
81 const char *p = to_match;
85 int nomatch = regexec(&url_regex, p, 1, &m, 0);
87 if (nomatch || m.rm_so == -1)
90 int start = m.rm_so + (p - to_match);
91 int finish = m.rm_eo + (p - to_match);
93 char *match = g_strndup(to_match + start, finish - start);
106void open_browser(
const char *in)
109 LOG_C(
"Unable to open browser: No browser command set.");
115 if (*in ==
'[' && (end = strstr(in,
"] ")))
116 url = g_strdup(end + 2);
120 int argc = 2+g_strv_length(
settings.browser_cmd);
121 char **argv = g_malloc_n(argc,
sizeof(
char*));
123 memcpy(argv,
settings.browser_cmd, argc *
sizeof(
char*));
132 | G_SPAWN_SEARCH_PATH
133 | G_SPAWN_STDOUT_TO_DEV_NULL
134 | G_SPAWN_STDERR_TO_DEV_NULL,
141 LOG_C(
"Cannot spawn browser: %s", err->message);
151 char *dmenu_str = NULL;
156 g_hash_table_iter_init(&iter, n->actions);
157 while (g_hash_table_iter_next(&iter, &p_key, &p_value)) {
159 char *key = (
char*) p_key;
160 char *value = (
char*) p_value;
162 char *act_str = g_strdup_printf(
"#%s (%s) [%d,%s]", value, n->summary, n->id, key);
174void invoke_action(
const char *action)
179 char *data_start, *data_comma, *data_end;
182 data_start = strrchr(action,
'[');
184 LOG_W(
"Invalid action: '%s'", action);
188 id = strtol(++data_start, &data_comma, 10);
189 if (*data_comma !=
',') {
190 LOG_W(
"Invalid action: '%s'", action);
194 data_end = strchr(data_comma+1,
']');
196 LOG_W(
"Invalid action: '%s'", action);
200 char *action_key = g_strndup(data_comma+1, data_end-data_comma-1);
209 if (g_hash_table_contains(n->actions, action_key)) {
215 if (invoked && action_key) {
216 signal_action_invoked(invoked, action_key);
232 char *in = g_strdup(input);
236 invoke_action(in + 1);
237 else if (in[0] !=
'\0')
252 LOG_C(
"Unable to open dmenu: No dmenu command set.");
264 g_spawn_async_with_pipes(NULL,
268 | G_SPAWN_SEARCH_PATH,
278 LOG_C(
"Cannot spawn dmenu: %s", err->message);
281 size_t wlen = strlen(dmenu_input);
282 ssize_t n = write(dunst_to_dmenu, dmenu_input, wlen);
283 if (n < 0 || (
size_t)n != wlen) {
284 LOG_W(
"Cannot feed dmenu with input: %s", strerror(errno));
286 close(dunst_to_dmenu);
288 ssize_t rlen = read(dmenu_to_dunst, buf,
sizeof(buf));
289 close(dmenu_to_dunst);
292 ret = g_strndup(buf, rlen);
294 LOG_W(
"Didn't receive input from dmenu.");
305 GList *locked_notifications = NULL;
311 if (n->
urls || g_hash_table_size(n->actions)) {
312 notification_lock(n);
313 locked_notifications = g_list_prepend(locked_notifications, n);
317 return g_list_reverse(locked_notifications);
328 if (menu_ctx.locked_notifications) {
329 LOG_W(
"Context menu already running, refusing to rerun");
333 menu_ctx.locked_notifications = notifications;
336 g_thread_unref(g_thread_try_new(
"dmenu",
342 LOG_C(
"Cannot start thread to call dmenu: %s", err->message);
347static gboolean context_menu_result_dispatch(gpointer user_data)
349 char *dmenu_output = (
char*)user_data;
353 for (GList *iter = menu_ctx.locked_notifications; iter; iter = iter->next) {
355 notification_unlock(n);
356 if (n->marked_for_closure) {
358 if (dmenu_output != NULL)
360 n->marked_for_closure = 0;
372 menu_ctx.locked_notifications = NULL;
374 g_list_free(menu_ctx.locked_notifications);
375 g_free(dmenu_output);
379 return G_SOURCE_REMOVE;
382static gpointer context_menu_thread(gpointer data)
386 char *dmenu_input = NULL;
389 for (GList *iter = menu_ctx.locked_notifications; iter; iter = iter->next) {
392 char *dmenu_str = notification_dmenu_string(n);
401 g_timeout_add(50, context_menu_result_dispatch, dmenu_output);
DBus support and implementation of the Desktop Notifications Specification.
Logging subsystem and helpers.
Notification type definitions.
void queues_notification_remove(struct notification *n, enum reason reason)
Remove the given notification from all queues.
GList * queues_get_displayed(void)
Receive the current list of displayed notifications.
void queues_notification_close(struct notification *n, enum reason reason)
Close the given notification.
Queues for history, waiting and displayed notifications.
Type definitions for settings.
guint8 marked_for_removal
If set, the notification is marked for removal in history.
char * urls
urllist delimited by '\n'
char * string_append(char *a, const char *b, const char *sep)
Append b to string a, then concatenate both with sep (if they are non-empty).
String, time and other various helpers.
#define STR_FULL(s)
Test if a string is non-NULL and not empty.
#define ASSERT_OR_RET(expr, val)
Assert that expr evaluates to true, if not return val.