notification.{c,h}
This commit is contained in:
parent
e07744fd49
commit
4e92d61804
3
Makefile
3
Makefile
@ -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
1
dbus.c
@ -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
2
dbus.h
@ -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
430
dunst.c
@ -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(""", "\"", 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("<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
42
dunst.h
@ -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
130
menu.c
@ -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
424
notification.c
Normal 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(""", "\"", 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("<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
48
notification.h
Normal 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);
|
Loading…
x
Reference in New Issue
Block a user