diff --git a/Makefile b/Makefile index 2cf2f85..128a90f 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,8 @@ SRC = draw.c \ option_parser.c \ settings.c \ rules.c \ - menu.c + menu.c \ + notification.c OBJ = ${SRC:.c=.o} all: doc options dunst service diff --git a/dbus.c b/dbus.c index 2904001..c5dc31a 100644 --- a/dbus.c +++ b/dbus.c @@ -4,6 +4,7 @@ #include #include "dunst.h" #include "dbus.h" +#include "notification.h" GDBusConnection *dbus_conn; diff --git a/dbus.h b/dbus.h index f6e56bb..4175785 100644 --- a/dbus.h +++ b/dbus.h @@ -5,6 +5,8 @@ #include +#include "notification.h" + int initdbus(void); void dbus_tear_down(int id); /* void dbus_poll(int timeout); */ diff --git a/dunst.c b/dunst.c index ce22745..a084d46 100644 --- a/dunst.c +++ b/dunst.c @@ -33,6 +33,7 @@ #include "dbus.h" #include "utils.h" #include "rules.h" +#include "notification.h" #include "option_parser.h" #include "settings.h" @@ -70,17 +71,17 @@ int height_limit; int font_h; /* index of colors fit to urgency level */ -static const char *color_strings[2][3]; -static Atom utf8; -static DC *dc; -static Window win; -static bool visible = false; -static dimension_t geometry; -static XScreenSaverInfo *screensaver_info; -static dimension_t window_dim; -static bool pause_display = false; -static unsigned long framec; -static unsigned long sep_custom_col; +const char *color_strings[2][3]; +Atom utf8; +DC *dc; +Window win; +bool visible = false; +dimension_t geometry; +XScreenSaverInfo *screensaver_info; +dimension_t window_dim; +bool pause_display = false; +unsigned long framec; +unsigned long sep_custom_col; GMainLoop *mainloop = NULL; bool timer_active = false; @@ -88,7 +89,6 @@ bool timer_active = false; bool dunst_grab_errored = false; bool force_redraw = false; -int next_notification_id = 1; /* notification lists */ GQueue *queue = NULL; /* all new notifications get into here */ @@ -99,13 +99,6 @@ GSList *rules = NULL; // {{{ FUNCTION DEFINITIONS -/* notifications */ -int notification_cmp(const void *a, const void *b); -int notification_cmp_data(const void *a, const void *b, void *data); -void notification_run_script(notification *n); -int notification_close(notification * n, int reason); -void notification_print(notification *n); -char *notification_fix_markup(char *str); /* window */ void x_win_draw(void); @@ -150,405 +143,6 @@ void pause_signal_handler(int sig); -// {{{ NOTIFICATION - - /* - * print a human readable representation - * of the given notification to stdout. - */ -void notification_print(notification * n) -{ // {{{ - printf("{\n"); - printf("\tappname: '%s'\n", n->appname); - printf("\tsummary: '%s'\n", n->summary); - printf("\tbody: '%s'\n", n->body); - printf("\ticon: '%s'\n", n->icon); - printf("\turgency: %d\n", n->urgency); - printf("\tformatted: '%s'\n", n->msg); - printf("\tid: %d\n", n->id); - if (n->urls) { - printf("\turls\n"); - printf("\t{\n"); - printf("%s\n", n->urls); - printf("\t}\n"); - } - - 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]); - } - printf("actions_dmenu: %s\n", n->actions->dmenu_str); - printf("\t]\n"); - } - printf("\tscript: %s\n", n->script); - printf("}\n"); -} -// }}} - - /* - * Run the script associated with the - * given notification. - */ -void notification_run_script(notification *n) -{ // {{{ - if (!n->script || strlen(n->script) < 1) - return; - - char *appname = n->appname ? n->appname : ""; - char *summary = n->summary ? n->summary : ""; - char *body = n->body ? n->body : ""; - char *icon = n->icon ? n->icon : ""; - - char *urgency; - switch (n->urgency) { - case LOW: - urgency = "LOW"; - break; - case NORM: - urgency = "NORMAL"; - break; - case CRIT: - urgency = "CRITICAL"; - break; - default: - urgency = "NORMAL"; - break; - } - - int pid1 = fork(); - - if (pid1) { - int status; - waitpid(pid1, &status, 0); - } else { - int pid2 = fork(); - if (pid2) { - exit(0); - } else { - int ret = execlp(n->script, n->script, - appname, - summary, - body, - icon, - urgency, - (char *) NULL - ); - if (ret != 0) { - PERR("Unable to run script", errno); - exit(EXIT_FAILURE); - } - } - } -} -// }}} - - /* - * Helper function to compare to given - * notifications. - */ -int notification_cmp(const void *va, const void *vb) -{ // {{{ - notification *a = (notification*) va; - notification *b = (notification*) vb; - - if (!settings.sort) - return 1; - - if (a->urgency != b->urgency) { - return b->urgency - a->urgency; - } else { - return a->timestamp - b->timestamp; - } -} -// }}} - - /* - * Wrapper for notification_cmp to match glib's - * compare functions signature. - */ -int notification_cmp_data(const void *va, const void *vb, void *data) -{ // {{{ - return notification_cmp(va, vb); -} -// }}} - - - /* - * Free the memory used by the given notification. - */ -void notification_free(notification * n) -{ // {{{ - if (n == NULL) - return; - free(n->appname); - free(n->summary); - free(n->body); - free(n->icon); - free(n->msg); - free(n->dbus_client); - free(n); -} -// }}} - - /* - * Strip any markup from text - */ - -char *notification_fix_markup(char *str) -{ // {{{ - char *replace_buf, *start, *end; - - if (str == NULL) { - return NULL; - } - - str = string_replace_all(""", "\"", str); - str = string_replace_all("'", "'", str); - str = string_replace_all("&", "&", str); - str = string_replace_all("<", "<", str); - str = string_replace_all(">", ">", str); - - /* remove tags */ - str = string_replace_all("", "", str); - str = string_replace_all("", "", str); - str = string_replace_all("
", " ", str); - str = string_replace_all("
", " ", str); - str = string_replace_all("
", " ", str); - str = string_replace_all("", "", str); - str = string_replace_all("", "", str); - str = string_replace_all("", "", str); - str = string_replace_all("", "", str); - str = string_replace_all("", "", str); - - start = strstr(str, ""); - if (end != NULL) { - replace_buf = strndup(start, end - start + 1); - str = string_replace(replace_buf, "", str); - free(replace_buf); - } - } - start = strstr(str, ""); - if (end != NULL) { - replace_buf = strndup(start, end - start + 2); - str = string_replace(replace_buf, "", str); - free(replace_buf); - } - } - return str; - -} -// }}} - - /* - * Initialize the given notification and add it to - * the queue. Replace notification with id if id > 0. - */ -int notification_init(notification * n, int id) -{ // {{{ - const char *fg = NULL; - const char *bg = NULL; - - if (n == NULL) - return -1; - - if (strcmp("DUNST_COMMAND_PAUSE", n->summary) == 0) { - pause_display = true; - return 0; - } - - if (strcmp("DUNST_COMMAND_RESUME", n->summary) == 0) { - pause_display = false; - return 0; - } - - n->script = NULL; - - n->format = settings.format; - - rule_apply_all(n); - - n->msg = string_replace("%a", n->appname, g_strdup(n->format)); - n->msg = string_replace("%s", n->summary, n->msg); - if (n->icon) { - n->msg = string_replace("%I", basename(n->icon), n->msg); - n->msg = string_replace("%i", n->icon, n->msg); - } - n->msg = string_replace("%b", n->body, n->msg); - if (n->progress) { - char pg[10]; - sprintf(pg, "[%3d%%]", n->progress - 1); - n->msg = string_replace("%p", pg, n->msg); - } else { - n->msg = string_replace("%p", "", n->msg); - } - - n->msg = notification_fix_markup(n->msg); - - while (strstr(n->msg, "\\n") != NULL) - n->msg = string_replace("\\n", "\n", n->msg); - - if (settings.ignore_newline) - while (strstr(n->msg, "\n") != NULL) - n->msg = string_replace("\n", " ", n->msg); - - n->msg = g_strstrip(n->msg); - - - n->dup_count = 0; - - /* check if n is a duplicate */ - for (GList *iter = g_queue_peek_head_link(queue); iter; iter = iter->next) { - notification *orig = iter->data; - if (strcmp(orig->appname, n->appname) == 0 - && strcmp(orig->msg, n->msg) == 0) { - orig->dup_count++; - notification_free(n); - wake_up(); - return orig->id; - } - } - - for (GList *iter = g_queue_peek_head_link(displayed); iter; iter = iter->next) { - notification *orig = iter->data; - if (strcmp(orig->appname, n->appname) == 0 - && strcmp(orig->msg, n->msg) == 0) { - orig->dup_count++; - orig->start = time(NULL); - notification_free(n); - wake_up(); - return orig->id; - } - } - - /* urgency > CRIT -> array out of range */ - n->urgency = n->urgency > CRIT ? CRIT : n->urgency; - - if (n->color_strings[ColFG]) { - fg = n->color_strings[ColFG]; - } else { - fg = color_strings[ColFG][n->urgency]; - } - - if (n->color_strings[ColBG]) { - bg = n->color_strings[ColBG]; - } else { - bg = color_strings[ColBG][n->urgency]; - } - - n->colors = initcolor(dc, fg, bg); - - n->timeout = n->timeout == -1 ? settings.timeouts[n->urgency] : n->timeout; - n->start = 0; - - n->timestamp = time(NULL); - - n->redisplayed = false; - - if (id == 0) { - n->id = ++next_notification_id; - } else { - notification_close_by_id(id, -1); - n->id = id; - } - - if (strlen(n->msg) == 0) { - notification_close(n, 2); - printf("skipping notification: %s %s\n", n->body, n->summary); - } else { - g_queue_insert_sorted(queue, n, notification_cmp_data, NULL); - } - - char *tmp = g_strconcat(n->summary, " ", n->body, NULL); - - n->urls = extract_urls(tmp); - - - if (n->actions) { - n->actions->dmenu_str = NULL; - for (int i = 0; i < n->actions->count; i += 2) { - char *human_readable = n->actions->actions[i+1]; - printf("debug: %s\n", n->appname); - printf("debug: %s\n", human_readable); - char *tmp = g_strdup_printf("%s %s", n->appname, human_readable); - printf("debug: %s\n", tmp); - - n->actions->dmenu_str = string_append(n->actions->dmenu_str, - g_strdup_printf("%s(%s)", - n->appname, - human_readable), "\n"); - } - } - - free(tmp); - - - if (settings.print_notifications) - notification_print(n); - - return n->id; -} -// }}} - - /* - * Close the notification that has id. - * - * reasons: - * -1 -> notification is a replacement, no NotificationClosed signal emitted - * 1 -> the notification expired - * 2 -> the notification was dismissed by the user_data - * 3 -> The notification was closed by a call to CloseNotification - */ -int notification_close_by_id(int id, int reason) -{ // {{{ - notification *target = NULL; - - for (GList *iter = g_queue_peek_head_link(displayed); iter; iter = iter->next) { - notification *n = iter->data; - if (n->id == id) { - g_queue_remove(displayed, n); - g_queue_push_tail(history, n); - target = n; - break; - } - } - - for (GList *iter = g_queue_peek_head_link(queue); iter; iter = iter->next) { - notification *n = iter->data; - if (n->id == id) { - g_queue_remove(queue, n); - g_queue_push_tail(history, n); - target = n; - break; - } - } - - if (reason > 0 && reason < 4 && target != NULL) { - notificationClosed(target, reason); - } - - wake_up(); - return reason; -} -// }}} - - /* - * Close the given notification. SEE notification_close_by_id. - */ -int notification_close(notification * n, int reason) -{ // {{{ - if (n == NULL) - return -1; - return notification_close_by_id(n->id, reason); -} -// }}} - -// }}} diff --git a/dunst.h b/dunst.h index 959e64b..77bef41 100644 --- a/dunst.h +++ b/dunst.h @@ -11,9 +11,6 @@ #define PERR(msg, errnum) printf("(%d) %s : %s\n", __LINE__, (msg), (strerror(errnum))) #define LENGTH(X) (sizeof X / sizeof X[0]) -#define LOW 0 -#define NORM 1 -#define CRIT 2 #define ColLast 2 #define ColFG 1 @@ -37,36 +34,7 @@ typedef struct _screen_info { dimension_t dim; } screen_info; -typedef struct _actions { - char **actions; - char *dmenu_str; - gsize count; -} Actions; -typedef struct _notification { - char *appname; - char *summary; - char *body; - char *icon; - char *msg; - const char *format; - char *dbus_client; - time_t start; - time_t timestamp; - int timeout; - int urgency; - bool redisplayed; /* has been displayed before? */ - int id; - int dup_count; - ColorSet *colors; - char *color_strings[2]; - - int progress; /* percentage + 1, 0 to hide */ - int line_count; - const char *script; - char *urls; - Actions *actions; -} notification; typedef struct _keyboard_shortcut { @@ -83,11 +51,17 @@ typedef struct _render_text { } render_text; extern int verbosity; +extern GQueue *queue; extern GQueue *displayed; +extern GQueue *history; +extern GSList *rules; +extern bool pause_display; +extern const char *color_strings[2][3]; +extern DC *dc; + + /* return id of notification */ -int notification_init(notification * n, int id); -int notification_close_by_id(int id, int reason); gboolean run(void *data); void wake_up(void); diff --git a/menu.c b/menu.c index 43f3aff..29d7945 100644 --- a/menu.c +++ b/menu.c @@ -74,80 +74,80 @@ char *extract_urls( const char * to_match) */ void open_browser(const char *url) { // {{{ - int browser_pid1 = fork(); +int browser_pid1 = fork(); - if (browser_pid1) { - int status; - waitpid(browser_pid1, &status, 0); - } else { - int browser_pid2 = fork(); - if (browser_pid2) { - exit(0); - } else { - char *browser_cmd = string_append(settings.browser, url, " "); - char **cmd = g_strsplit(browser_cmd, " ", 0); - execvp(cmd[0], cmd); +if (browser_pid1) { + int status; + waitpid(browser_pid1, &status, 0); +} else { + int browser_pid2 = fork(); + if (browser_pid2) { + exit(0); + } else { + char *browser_cmd = string_append(settings.browser, url, " "); + char **cmd = g_strsplit(browser_cmd, " ", 0); + execvp(cmd[0], cmd); + } +} +} +// }}} + + + /* + * Notify the corresponding client + * that an action has been invoked + */ +void invoke_action(const char *action) +{ // {{{ + notification *invoked = NULL; + char *action_identifier = NULL; + + char *name_begin = strstr(action, "("); + if (!name_begin) { + printf("invalid action: %s\n", action); + return; + } + name_begin++; + + + for (GList *iter = g_queue_peek_head_link(displayed); iter; iter = iter->next) { + notification *n = iter->data; + if (g_str_has_prefix(action, n->appname)) { + if (! n->actions) + 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(name_begin, name)) { + invoked = n; + action_identifier = a_identifier; + break; + } + } } } + + if (invoked && action_identifier) { + actionInvoked(invoked, action_identifier); + } } // }}} - - /* - * Notify the corresponding client - * that an action has been invoked - */ -void invoke_action(const char *action) -{ // {{{ - notification *invoked = NULL; - char *action_identifier = NULL; - - char *name_begin = strstr(action, "("); - if (!name_begin) { - printf("invalid action: %s\n", action); - return; - } - name_begin++; - - - for (GList *iter = g_queue_peek_head_link(displayed); iter; iter = iter->next) { - notification *n = iter->data; - if (g_str_has_prefix(action, n->appname)) { - if (! n->actions) - 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(name_begin, name)) { - invoked = n; - action_identifier = a_identifier; - break; - } - } - } - } - - if (invoked && action_identifier) { - actionInvoked(invoked, action_identifier); - } -} -// }}} - - /* - * Dispatch whatever has been returned - * by the menu. - */ + /* + * Dispatch whatever has been returned + * by the menu. + */ void dispatch_menu_result(const char *input) { // {{{ - char *maybe_url = extract_urls(input); - if (maybe_url) { - open_browser(maybe_url); - free(maybe_url); - return; - } + char *maybe_url = extract_urls(input); + if (maybe_url) { + open_browser(maybe_url); + free(maybe_url); + return; + } - invoke_action(input); + invoke_action(input); } // }}} diff --git a/notification.c b/notification.c new file mode 100644 index 0000000..cd23320 --- /dev/null +++ b/notification.c @@ -0,0 +1,424 @@ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +#include "dbus.h" +#include "draw.h" +#include "notification.h" +#include "dunst.h" +#include "utils.h" +#include "settings.h" +#include "rules.h" +#include "menu.h" + + +int next_notification_id = 1; + + + +// {{{ NOTIFICATION + + /* + * print a human readable representation + * of the given notification to stdout. + */ +void notification_print(notification * n) +{ // {{{ + printf("{\n"); + printf("\tappname: '%s'\n", n->appname); + printf("\tsummary: '%s'\n", n->summary); + printf("\tbody: '%s'\n", n->body); + printf("\ticon: '%s'\n", n->icon); + printf("\turgency: %d\n", n->urgency); + printf("\tformatted: '%s'\n", n->msg); + printf("\tid: %d\n", n->id); + if (n->urls) { + printf("\turls\n"); + printf("\t{\n"); + printf("%s\n", n->urls); + printf("\t}\n"); + } + + 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]); + } + printf("actions_dmenu: %s\n", n->actions->dmenu_str); + printf("\t]\n"); + } + printf("\tscript: %s\n", n->script); + printf("}\n"); +} +// }}} + + /* + * Run the script associated with the + * given notification. + */ +void notification_run_script(notification *n) +{ // {{{ + if (!n->script || strlen(n->script) < 1) + return; + + char *appname = n->appname ? n->appname : ""; + char *summary = n->summary ? n->summary : ""; + char *body = n->body ? n->body : ""; + char *icon = n->icon ? n->icon : ""; + + char *urgency; + switch (n->urgency) { + case LOW: + urgency = "LOW"; + break; + case NORM: + urgency = "NORMAL"; + break; + case CRIT: + urgency = "CRITICAL"; + break; + default: + urgency = "NORMAL"; + break; + } + + int pid1 = fork(); + + if (pid1) { + int status; + waitpid(pid1, &status, 0); + } else { + int pid2 = fork(); + if (pid2) { + exit(0); + } else { + int ret = execlp(n->script, n->script, + appname, + summary, + body, + icon, + urgency, + (char *) NULL + ); + if (ret != 0) { + PERR("Unable to run script", errno); + exit(EXIT_FAILURE); + } + } + } +} +// }}} + + /* + * Helper function to compare to given + * notifications. + */ +int notification_cmp(const void *va, const void *vb) +{ // {{{ + notification *a = (notification*) va; + notification *b = (notification*) vb; + + if (!settings.sort) + return 1; + + if (a->urgency != b->urgency) { + return b->urgency - a->urgency; + } else { + return a->timestamp - b->timestamp; + } +} +// }}} + + /* + * Wrapper for notification_cmp to match glib's + * compare functions signature. + */ +int notification_cmp_data(const void *va, const void *vb, void *data) +{ // {{{ + return notification_cmp(va, vb); +} +// }}} + + + /* + * Free the memory used by the given notification. + */ +void notification_free(notification * n) +{ // {{{ + if (n == NULL) + return; + free(n->appname); + free(n->summary); + free(n->body); + free(n->icon); + free(n->msg); + free(n->dbus_client); + free(n); +} +// }}} + + /* + * Strip any markup from text + */ + +char *notification_fix_markup(char *str) +{ // {{{ + char *replace_buf, *start, *end; + + if (str == NULL) { + return NULL; + } + + str = string_replace_all(""", "\"", str); + str = string_replace_all("'", "'", str); + str = string_replace_all("&", "&", str); + str = string_replace_all("<", "<", str); + str = string_replace_all(">", ">", str); + + /* remove tags */ + str = string_replace_all("", "", str); + str = string_replace_all("", "", str); + str = string_replace_all("
", " ", str); + str = string_replace_all("
", " ", str); + str = string_replace_all("
", " ", str); + str = string_replace_all("", "", str); + str = string_replace_all("", "", str); + str = string_replace_all("", "", str); + str = string_replace_all("", "", str); + str = string_replace_all("
", "", str); + + start = strstr(str, ""); + if (end != NULL) { + replace_buf = strndup(start, end - start + 1); + str = string_replace(replace_buf, "", str); + free(replace_buf); + } + } + start = strstr(str, ""); + if (end != NULL) { + replace_buf = strndup(start, end - start + 2); + str = string_replace(replace_buf, "", str); + free(replace_buf); + } + } + return str; + +} +// }}} + + /* + * Initialize the given notification and add it to + * the queue. Replace notification with id if id > 0. + */ +int notification_init(notification * n, int id) +{ // {{{ + const char *fg = NULL; + const char *bg = NULL; + + if (n == NULL) + return -1; + + if (strcmp("DUNST_COMMAND_PAUSE", n->summary) == 0) { + pause_display = true; + return 0; + } + + if (strcmp("DUNST_COMMAND_RESUME", n->summary) == 0) { + pause_display = false; + return 0; + } + + n->script = NULL; + + n->format = settings.format; + + rule_apply_all(n); + + n->msg = string_replace("%a", n->appname, g_strdup(n->format)); + n->msg = string_replace("%s", n->summary, n->msg); + if (n->icon) { + n->msg = string_replace("%I", basename(n->icon), n->msg); + n->msg = string_replace("%i", n->icon, n->msg); + } + n->msg = string_replace("%b", n->body, n->msg); + if (n->progress) { + char pg[10]; + sprintf(pg, "[%3d%%]", n->progress - 1); + n->msg = string_replace("%p", pg, n->msg); + } else { + n->msg = string_replace("%p", "", n->msg); + } + + n->msg = notification_fix_markup(n->msg); + + while (strstr(n->msg, "\\n") != NULL) + n->msg = string_replace("\\n", "\n", n->msg); + + if (settings.ignore_newline) + while (strstr(n->msg, "\n") != NULL) + n->msg = string_replace("\n", " ", n->msg); + + n->msg = g_strstrip(n->msg); + + + n->dup_count = 0; + + /* check if n is a duplicate */ + for (GList *iter = g_queue_peek_head_link(queue); iter; iter = iter->next) { + notification *orig = iter->data; + if (strcmp(orig->appname, n->appname) == 0 + && strcmp(orig->msg, n->msg) == 0) { + orig->dup_count++; + notification_free(n); + wake_up(); + return orig->id; + } + } + + for (GList *iter = g_queue_peek_head_link(displayed); iter; iter = iter->next) { + notification *orig = iter->data; + if (strcmp(orig->appname, n->appname) == 0 + && strcmp(orig->msg, n->msg) == 0) { + orig->dup_count++; + orig->start = time(NULL); + notification_free(n); + wake_up(); + return orig->id; + } + } + + /* urgency > CRIT -> array out of range */ + n->urgency = n->urgency > CRIT ? CRIT : n->urgency; + + if (n->color_strings[ColFG]) { + fg = n->color_strings[ColFG]; + } else { + fg = color_strings[ColFG][n->urgency]; + } + + if (n->color_strings[ColBG]) { + bg = n->color_strings[ColBG]; + } else { + bg = color_strings[ColBG][n->urgency]; + } + + n->colors = initcolor(dc, fg, bg); + + n->timeout = n->timeout == -1 ? settings.timeouts[n->urgency] : n->timeout; + n->start = 0; + + n->timestamp = time(NULL); + + n->redisplayed = false; + + if (id == 0) { + n->id = ++next_notification_id; + } else { + notification_close_by_id(id, -1); + n->id = id; + } + + if (strlen(n->msg) == 0) { + notification_close(n, 2); + printf("skipping notification: %s %s\n", n->body, n->summary); + } else { + g_queue_insert_sorted(queue, n, notification_cmp_data, NULL); + } + + char *tmp = g_strconcat(n->summary, " ", n->body, NULL); + + n->urls = extract_urls(tmp); + + + if (n->actions) { + n->actions->dmenu_str = NULL; + for (int i = 0; i < n->actions->count; i += 2) { + char *human_readable = n->actions->actions[i+1]; + printf("debug: %s\n", n->appname); + printf("debug: %s\n", human_readable); + char *tmp = g_strdup_printf("%s %s", n->appname, human_readable); + printf("debug: %s\n", tmp); + + n->actions->dmenu_str = string_append(n->actions->dmenu_str, + g_strdup_printf("%s(%s)", + n->appname, + human_readable), "\n"); + } + } + + free(tmp); + + + if (settings.print_notifications) + notification_print(n); + + return n->id; +} +// }}} + + /* + * Close the notification that has id. + * + * reasons: + * -1 -> notification is a replacement, no NotificationClosed signal emitted + * 1 -> the notification expired + * 2 -> the notification was dismissed by the user_data + * 3 -> The notification was closed by a call to CloseNotification + */ +int notification_close_by_id(int id, int reason) +{ // {{{ + notification *target = NULL; + + for (GList *iter = g_queue_peek_head_link(displayed); iter; iter = iter->next) { + notification *n = iter->data; + if (n->id == id) { + g_queue_remove(displayed, n); + g_queue_push_tail(history, n); + target = n; + break; + } + } + + for (GList *iter = g_queue_peek_head_link(queue); iter; iter = iter->next) { + notification *n = iter->data; + if (n->id == id) { + g_queue_remove(queue, n); + g_queue_push_tail(history, n); + target = n; + break; + } + } + + if (reason > 0 && reason < 4 && target != NULL) { + notificationClosed(target, reason); + } + + wake_up(); + return reason; +} +// }}} + + /* + * Close the given notification. SEE notification_close_by_id. + */ +int notification_close(notification * n, int reason) +{ // {{{ + if (n == NULL) + return -1; + return notification_close_by_id(n->id, reason); +} +// }}} + +// }}} diff --git a/notification.h b/notification.h new file mode 100644 index 0000000..e6b5687 --- /dev/null +++ b/notification.h @@ -0,0 +1,48 @@ +#pragma once + +#include "draw.h" + +#define LOW 0 +#define NORM 1 +#define CRIT 2 + +typedef struct _actions { + char **actions; + char *dmenu_str; + gsize count; +} Actions; + +typedef struct _notification { + char *appname; + char *summary; + char *body; + char *icon; + char *msg; + const char *format; + char *dbus_client; + time_t start; + time_t timestamp; + int timeout; + int urgency; + bool redisplayed; /* has been displayed before? */ + int id; + int dup_count; + ColorSet *colors; + char *color_strings[2]; + + int progress; /* percentage + 1, 0 to hide */ + int line_count; + const char *script; + char *urls; + Actions *actions; +} notification; + + +int notification_init(notification * n, int id); +int notification_close_by_id(int id, int reason); +int notification_cmp(const void *a, const void *b); +int notification_cmp_data(const void *a, const void *b, void *data); +void notification_run_script(notification *n); +int notification_close(notification * n, int reason); +void notification_print(notification *n); +char *notification_fix_markup(char *str); diff --git a/rules.h b/rules.h index 56c8e4b..baccdf4 100644 --- a/rules.h +++ b/rules.h @@ -4,6 +4,7 @@ #include #include "dunst.h" +#include "notification.h" // }}} // {{{ STRUCTS