notification.{c,h}

This commit is contained in:
Sascha Kruse 2013-02-21 18:02:20 +00:00
parent e07744fd49
commit 4e92d61804
9 changed files with 563 additions and 518 deletions

View File

@ -10,7 +10,8 @@ SRC = draw.c \
option_parser.c \ option_parser.c \
settings.c \ settings.c \
rules.c \ rules.c \
menu.c menu.c \
notification.c
OBJ = ${SRC:.c=.o} OBJ = ${SRC:.c=.o}
all: doc options dunst service all: doc options dunst service

1
dbus.c
View File

@ -4,6 +4,7 @@
#include <gio/gio.h> #include <gio/gio.h>
#include "dunst.h" #include "dunst.h"
#include "dbus.h" #include "dbus.h"
#include "notification.h"
GDBusConnection *dbus_conn; GDBusConnection *dbus_conn;

2
dbus.h
View File

@ -5,6 +5,8 @@
#include <dbus/dbus.h> #include <dbus/dbus.h>
#include "notification.h"
int initdbus(void); int initdbus(void);
void dbus_tear_down(int id); void dbus_tear_down(int id);
/* void dbus_poll(int timeout); */ /* void dbus_poll(int timeout); */

430
dunst.c
View File

@ -33,6 +33,7 @@
#include "dbus.h" #include "dbus.h"
#include "utils.h" #include "utils.h"
#include "rules.h" #include "rules.h"
#include "notification.h"
#include "option_parser.h" #include "option_parser.h"
#include "settings.h" #include "settings.h"
@ -70,17 +71,17 @@ int height_limit;
int font_h; int font_h;
/* index of colors fit to urgency level */ /* index of colors fit to urgency level */
static const char *color_strings[2][3]; const char *color_strings[2][3];
static Atom utf8; Atom utf8;
static DC *dc; DC *dc;
static Window win; Window win;
static bool visible = false; bool visible = false;
static dimension_t geometry; dimension_t geometry;
static XScreenSaverInfo *screensaver_info; XScreenSaverInfo *screensaver_info;
static dimension_t window_dim; dimension_t window_dim;
static bool pause_display = false; bool pause_display = false;
static unsigned long framec; unsigned long framec;
static unsigned long sep_custom_col; unsigned long sep_custom_col;
GMainLoop *mainloop = NULL; GMainLoop *mainloop = NULL;
bool timer_active = false; bool timer_active = false;
@ -88,7 +89,6 @@ bool timer_active = false;
bool dunst_grab_errored = false; bool dunst_grab_errored = false;
bool force_redraw = false; bool force_redraw = false;
int next_notification_id = 1;
/* notification lists */ /* notification lists */
GQueue *queue = NULL; /* all new notifications get into here */ GQueue *queue = NULL; /* all new notifications get into here */
@ -99,13 +99,6 @@ GSList *rules = NULL;
// {{{ FUNCTION DEFINITIONS // {{{ 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 */ /* window */
void x_win_draw(void); 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("&quot;", "\"", str);
str = string_replace_all("&apos;", "'", str);
str = string_replace_all("&amp;", "&", str);
str = string_replace_all("&lt;", "<", str);
str = string_replace_all("&gt;", ">", str);
/* remove tags */
str = string_replace_all("<b>", "", str);
str = string_replace_all("</b>", "", str);
str = string_replace_all("<br>", " ", str);
str = string_replace_all("<br/>", " ", str);
str = string_replace_all("<br />", " ", str);
str = string_replace_all("<i>", "", str);
str = string_replace_all("</i>", "", str);
str = string_replace_all("<u>", "", str);
str = string_replace_all("</u>", "", str);
str = string_replace_all("</a>", "", str);
start = strstr(str, "<a href");
if (start != NULL) {
end = strstr(str, ">");
if (end != NULL) {
replace_buf = strndup(start, end - start + 1);
str = string_replace(replace_buf, "", str);
free(replace_buf);
}
}
start = strstr(str, "<img src");
if (start != NULL) {
end = 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);
}
// }}}
// }}}

42
dunst.h
View File

@ -11,9 +11,6 @@
#define PERR(msg, errnum) printf("(%d) %s : %s\n", __LINE__, (msg), (strerror(errnum))) #define PERR(msg, errnum) printf("(%d) %s : %s\n", __LINE__, (msg), (strerror(errnum)))
#define LENGTH(X) (sizeof X / sizeof X[0]) #define LENGTH(X) (sizeof X / sizeof X[0])
#define LOW 0
#define NORM 1
#define CRIT 2
#define ColLast 2 #define ColLast 2
#define ColFG 1 #define ColFG 1
@ -37,36 +34,7 @@ typedef struct _screen_info {
dimension_t dim; dimension_t dim;
} screen_info; } 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 { typedef struct _keyboard_shortcut {
@ -83,11 +51,17 @@ typedef struct _render_text {
} render_text; } render_text;
extern int verbosity; extern int verbosity;
extern GQueue *queue;
extern GQueue *displayed; 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 */ /* return id of notification */
int notification_init(notification * n, int id);
int notification_close_by_id(int id, int reason);
gboolean run(void *data); gboolean run(void *data);
void wake_up(void); void wake_up(void);

130
menu.c
View File

@ -74,80 +74,80 @@ char *extract_urls( const char * to_match)
*/ */
void open_browser(const char *url) void open_browser(const char *url)
{ // {{{ { // {{{
int browser_pid1 = fork(); int browser_pid1 = fork();
if (browser_pid1) { if (browser_pid1) {
int status; int status;
waitpid(browser_pid1, &status, 0); waitpid(browser_pid1, &status, 0);
} else { } else {
int browser_pid2 = fork(); int browser_pid2 = fork();
if (browser_pid2) { if (browser_pid2) {
exit(0); exit(0);
} else { } else {
char *browser_cmd = string_append(settings.browser, url, " "); char *browser_cmd = string_append(settings.browser, url, " ");
char **cmd = g_strsplit(browser_cmd, " ", 0); char **cmd = g_strsplit(browser_cmd, " ", 0);
execvp(cmd[0], cmd); 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);
}
} }
// }}} // }}}
/*
/* * Dispatch whatever has been returned
* Notify the corresponding client * by the menu.
* 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.
*/
void dispatch_menu_result(const char *input) void dispatch_menu_result(const char *input)
{ // {{{ { // {{{
char *maybe_url = extract_urls(input); char *maybe_url = extract_urls(input);
if (maybe_url) { if (maybe_url) {
open_browser(maybe_url); open_browser(maybe_url);
free(maybe_url); free(maybe_url);
return; return;
} }
invoke_action(input); invoke_action(input);
} }
// }}} // }}}

424
notification.c Normal file
View File

@ -0,0 +1,424 @@
#define _GNU_SOURCE
#include <time.h>
#include <glib.h>
#include <errno.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/wait.h>
#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("&quot;", "\"", str);
str = string_replace_all("&apos;", "'", str);
str = string_replace_all("&amp;", "&", str);
str = string_replace_all("&lt;", "<", str);
str = string_replace_all("&gt;", ">", str);
/* remove tags */
str = string_replace_all("<b>", "", str);
str = string_replace_all("</b>", "", str);
str = string_replace_all("<br>", " ", str);
str = string_replace_all("<br/>", " ", str);
str = string_replace_all("<br />", " ", str);
str = string_replace_all("<i>", "", str);
str = string_replace_all("</i>", "", str);
str = string_replace_all("<u>", "", str);
str = string_replace_all("</u>", "", str);
str = string_replace_all("</a>", "", str);
start = strstr(str, "<a href");
if (start != NULL) {
end = strstr(str, ">");
if (end != NULL) {
replace_buf = strndup(start, end - start + 1);
str = string_replace(replace_buf, "", str);
free(replace_buf);
}
}
start = strstr(str, "<img src");
if (start != NULL) {
end = 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);
}
// }}}
// }}}

48
notification.h Normal file
View File

@ -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);

View File

@ -4,6 +4,7 @@
#include <glib.h> #include <glib.h>
#include "dunst.h" #include "dunst.h"
#include "notification.h"
// }}} // }}}
// {{{ STRUCTS // {{{ STRUCTS