diff --git a/config.def.h b/config.def.h index 7fddce7..45c0a87 100644 --- a/config.def.h +++ b/config.def.h @@ -37,6 +37,11 @@ int startup_notification = False; /* monitor to display notifications on */ int monitor = 0; +/* path to dmenu */ +char *dmenu = "/usr/bin/dmenu"; + +char *browser = "/usr/bin/firefox"; + /* follow focus to different monitor and display notifications there? * possible values: * FOLLOW_NONE @@ -60,6 +65,9 @@ keyboard_shortcut close_all_ks = {.str = "none", keyboard_shortcut history_ks = {.str = "none", .code = 0, .sym = NoSymbol,.is_valid = False}; /* ignore this */ +keyboard_shortcut context_ks = {.str = "none", + .code = 0, .sym = NoSymbol,.is_valid = False}; /* ignore this */ + rule_t default_rules[] = { /* name can be any unique string. It is used to identify the rule in dunstrc to override it there */ diff --git a/dunst.c b/dunst.c index bdd9255..350bcc1 100644 --- a/dunst.c +++ b/dunst.c @@ -102,6 +102,7 @@ void hide_win(void); void move_all_to_history(void); void print_version(void); str_array *extract_urls(const char *str); +void context_menu(void); void r_line_cache_init(r_line_cache *c); void r_line_cache_append(r_line_cache *c, const char *s, ColorSet *col, bool continues); @@ -153,6 +154,57 @@ str_array *extract_urls( const char * to_match) return urls; } +void context_menu(void) { + char *dmenu_input = NULL; + + n_queue *iter = displayed; + + while (iter) { + for (int i = 0; i < iter->n->urls->count; i++) { + dmenu_input = string_append(dmenu_input, + (iter->n->urls->strs)[i], "\n"); + } + iter = iter->next; + } + + int child_io[2]; + int parent_io[2]; + pipe(child_io); + pipe(parent_io); + int pid = fork(); + + if (pid == 0) { + close(child_io[1]); + close(parent_io[0]); + close(0); + dup(child_io[0]); + close(1); + dup(parent_io[1]); + execlp(dmenu, dmenu, (char *) NULL); + } else { + close(child_io[0]); + close(parent_io[1]); + write(child_io[1], dmenu_input, strlen(dmenu_input)); + close(child_io[1]); + + char buf[1024]; + size_t len = read(parent_io[0], buf, 1023); + if (len == 0) + return; + buf[len - 1] = '\0'; + } + + close(child_io[1]); + + int browser_pid = fork(); + + if (browser_pid == 0) { + execlp(browser, browser, (char *) NULL); + } else { + return; + } +} + void pause_signal_handler(int sig) { if (sig == SIGUSR1) { @@ -812,6 +864,11 @@ void handleXEvents(void) && close_all_ks.mask == ev.xkey.state) { move_all_to_history(); } + if (context_ks.str + && XLookupKeysym(&ev.xkey, 0) == context_ks.sym + && context_ks.mask == ev.xkey.state) { + context_menu(); + } } } } @@ -1149,6 +1206,7 @@ void hide_win() { ungrab_key(&close_ks); ungrab_key(&close_all_ks); + ungrab_key(&context_ks); XUngrabButton(dc->dpy, AnyButton, AnyModifier, win); XUnmapWindow(dc->dpy, win); @@ -1264,6 +1322,7 @@ void setup(void) init_shortcut(&close_ks); init_shortcut(&close_all_ks); init_shortcut(&history_ks); + init_shortcut(&context_ks); grab_key(&close_ks); ungrab_key(&close_ks); @@ -1271,6 +1330,8 @@ void setup(void) ungrab_key(&close_all_ks); grab_key(&history_ks); ungrab_key(&history_ks); + grab_key(&context_ks); + ungrab_key(&context_ks); colors[LOW] = initcolor(dc, lowfgcolor, lowbgcolor); colors[NORM] = initcolor(dc, normfgcolor, normbgcolor); @@ -1344,6 +1405,7 @@ void map_win(void) grab_key(&close_ks); grab_key(&close_all_ks); + grab_key(&context_ks); setup_error_handler(); XGrabButton(dc->dpy, AnyButton, AnyModifier, win, false, BUTTONMASK, GrabModeAsync, GrabModeSync, None, None); @@ -1498,6 +1560,10 @@ void load_options(char *cmdline_config_path) startup_notification = option_get_bool("global", "startup_notification", "-startup_notification", false, "print notification on startup"); + + dmenu = option_get_string("global", "dmenu", "-dmenu", dmenu, "path to dmenu"); + browser = option_get_string("global", "browser", "-browser", browser, "path to browser"); + lowbgcolor = option_get_string("urgency_low", "background", "-lb", lowbgcolor, "Background color for notifcations with low urgency"); @@ -1543,6 +1609,11 @@ void load_options(char *cmdline_config_path) history_ks.str, "Shortcut to pop the last notification from history"); + context_ks.str = + option_get_string("shortcuts", "context", "-context_key", + context_ks.str, + "Shortcut for context menu"); + print_notifications = cmdline_get_bool("-print", false, "Print notifications to cmdline (DEBUG)"); diff --git a/dunstrc b/dunstrc index 6797c8a..311df0d 100644 --- a/dunstrc +++ b/dunstrc @@ -94,6 +94,13 @@ # automatically after a crash. startup_notification = false + # dmenu path + dmenu = "/usr/bin/dmenu" + + # browser for opening urls in context menu + browser = /usr/bin/firefox + + [shortcuts] # shortcuts are specified as [modifier+][modifier+]...key # available modifiers are 'ctrl', 'mod1' (the alt-key), 'mod2', 'mod3' @@ -110,6 +117,9 @@ # On the US keyboard layout 'grave' is normally above TAB and left of '1'. history = ctrl+grave + # context menu + context = ctrl+shift+period + [urgency_low] # IMPORTANT: colors have to be defined in quotation marks. # Otherwise the '#' and following would be interpreted as a comment. diff --git a/utils.c b/utils.c index ef1b133..90f7026 100644 --- a/utils.c +++ b/utils.c @@ -1,3 +1,5 @@ +#define _GNU_SOURCE + #include #include #include @@ -46,6 +48,22 @@ char *string_replace(const char *needle, const char *replacement, return tmp; } +char *string_append(char *a, const char *b, const char *sep) +{ + if (!a) + return strdup(b); + + char *new; + if (!sep) + asprintf(&new, "%s%s", a, b); + else + asprintf(&new, "%s%s%s", a, sep, b); + free(a); + + return new; + +} + int digit_count(int i) { int len = 0; diff --git a/utils.h b/utils.h index 3d3774f..ab1e8d6 100644 --- a/utils.h +++ b/utils.h @@ -8,6 +8,8 @@ char *lskip(char *str); char *string_replace(const char *needle, const char *replacement, char *haystack); +char *string_append(char *a, const char *b, const char *sep); + /* exit with an error message */ void die(char *msg, int exit_value);