diff --git a/Makefile b/Makefile index bb516b7..3d62693 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ include config.mk -SRC = draw.c dunst.c ini.c +SRC = draw.c dunst.c list.c dunst_dbus.c ini.c OBJ = ${SRC:.c=.o} all: doc options dunst @@ -20,9 +20,9 @@ options: ${OBJ}: config.mk -dunst: draw.o dunst.o ini.o +dunst: draw.o dunst.o list.o dunst_dbus.o ini.o @echo CC -o $@ - @${CC} ${CFLAGS} -o $@ dunst.o draw.o ini.o ${LDFLAGS} + @${CC} ${CFLAGS} -o $@ dunst.o draw.o list.o dunst_dbus.o ini.o ${LDFLAGS} clean: @echo cleaning diff --git a/draw.c b/draw.c index e76f119..7733dfe 100644 --- a/draw.c +++ b/draw.c @@ -42,207 +42,234 @@ DEALINGS IN THE SOFTWARE. #define MIN(a, b) ((a) < (b) ? (a) : (b)) void -drawrect(DC *dc, int x, int y, unsigned int w, unsigned int h, Bool fill, unsigned long color) { - XSetForeground(dc->dpy, dc->gc, color); - if(fill) - XFillRectangle(dc->dpy, dc->canvas, dc->gc, dc->x + x, dc->y + y, w, h); - else - XDrawRectangle(dc->dpy, dc->canvas, dc->gc, dc->x + x, dc->y + y, w-1, h-1); +drawrect(DC * dc, int x, int y, unsigned int w, unsigned int h, Bool fill, + unsigned long color) +{ + XSetForeground(dc->dpy, dc->gc, color); + if (fill) + XFillRectangle(dc->dpy, dc->canvas, dc->gc, dc->x + x, + dc->y + y, w, h); + else + XDrawRectangle(dc->dpy, dc->canvas, dc->gc, dc->x + x, + dc->y + y, w - 1, h - 1); } -void -drawtext(DC *dc, const char *text, ColorSet *col) { - char buf[BUFSIZ]; - size_t mn, n = strlen(text); +void drawtext(DC * dc, const char *text, ColorSet * col) +{ + char buf[BUFSIZ]; + size_t mn, n = strlen(text); - /* shorten text if necessary */ - for(mn = MIN(n, sizeof buf); textnw(dc, text, mn) + dc->font.height/2 > dc->w; mn--) - if(mn == 0) - return; - memcpy(buf, text, mn); - if(mn < n) - for(n = MAX(mn-3, 0); n < mn; buf[n++] = '.'); + /* shorten text if necessary */ + for (mn = MIN(n, sizeof buf); + textnw(dc, text, mn) + dc->font.height / 2 > dc->w; mn--) + if (mn == 0) + return; + memcpy(buf, text, mn); + if (mn < n) + for (n = MAX(mn - 3, 0); n < mn; buf[n++] = '.') ; - drawrect(dc, 0, 0, dc->w, dc->h, True, col->BG); - drawtextn(dc, buf, mn, col); + drawrect(dc, 0, 0, dc->w, dc->h, True, col->BG); + drawtextn(dc, buf, mn, col); } -void -drawtextn(DC *dc, const char *text, size_t n, ColorSet *col) { - int x = dc->x + dc->font.height/2; - int y = dc->y + dc->font.ascent+1; +void drawtextn(DC * dc, const char *text, size_t n, ColorSet * col) +{ + int x = dc->x + dc->font.height / 2; + int y = dc->y + dc->font.ascent + 1; - XSetForeground(dc->dpy, dc->gc, col->FG); - if(dc->font.xft_font) { - if (!dc->xftdraw) - eprintf("error, xft drawable does not exist"); - XftDrawStringUtf8(dc->xftdraw, &col->FG_xft, - dc->font.xft_font, x, y, (unsigned char*)text, n); - } else if(dc->font.set) { - printf("XmbDrawString\n"); - XmbDrawString(dc->dpy, dc->canvas, dc->font.set, dc->gc, x, y, text, n); - } else { - XSetFont(dc->dpy, dc->gc, dc->font.xfont->fid); - XDrawString(dc->dpy, dc->canvas, dc->gc, x, y, text, n); - } + XSetForeground(dc->dpy, dc->gc, col->FG); + if (dc->font.xft_font) { + if (!dc->xftdraw) + eprintf("error, xft drawable does not exist"); + XftDrawStringUtf8(dc->xftdraw, &col->FG_xft, + dc->font.xft_font, x, y, + (unsigned char *)text, n); + } else if (dc->font.set) { + printf("XmbDrawString\n"); + XmbDrawString(dc->dpy, dc->canvas, dc->font.set, dc->gc, x, y, + text, n); + } else { + XSetFont(dc->dpy, dc->gc, dc->font.xfont->fid); + XDrawString(dc->dpy, dc->canvas, dc->gc, x, y, text, n); + } } -void -eprintf(const char *fmt, ...) { - va_list ap; +void eprintf(const char *fmt, ...) +{ + va_list ap; - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); - if(fmt[0] != '\0' && fmt[strlen(fmt)-1] == ':') { - fputc(' ', stderr); - perror(NULL); - } - exit(EXIT_FAILURE); + if (fmt[0] != '\0' && fmt[strlen(fmt) - 1] == ':') { + fputc(' ', stderr); + perror(NULL); + } + exit(EXIT_FAILURE); } -void -freecol(DC *dc, ColorSet *col) { - if(col) { - if(&col->FG_xft) - XftColorFree(dc->dpy, DefaultVisual(dc->dpy, DefaultScreen(dc->dpy)), - DefaultColormap(dc->dpy, DefaultScreen(dc->dpy)), &col->FG_xft); - free(col); - } +void freecol(DC * dc, ColorSet * col) +{ + if (col) { + if (&col->FG_xft) + XftColorFree(dc->dpy, + DefaultVisual(dc->dpy, + DefaultScreen(dc->dpy)), + DefaultColormap(dc->dpy, + DefaultScreen(dc->dpy)), + &col->FG_xft); + free(col); + } } -void -freedc(DC *dc) { - if(dc->font.xft_font) { - XftFontClose(dc->dpy, dc->font.xft_font); - XftDrawDestroy(dc->xftdraw); - } - if(dc->font.set) - XFreeFontSet(dc->dpy, dc->font.set); - if(dc->font.xfont) - XFreeFont(dc->dpy, dc->font.xfont); - if(dc->canvas) - XFreePixmap(dc->dpy, dc->canvas); - if(dc->gc) - XFreeGC(dc->dpy, dc->gc); - if(dc->dpy) - XCloseDisplay(dc->dpy); - if(dc) - free(dc); +void freedc(DC * dc) +{ + if (dc->font.xft_font) { + XftFontClose(dc->dpy, dc->font.xft_font); + XftDrawDestroy(dc->xftdraw); + } + if (dc->font.set) + XFreeFontSet(dc->dpy, dc->font.set); + if (dc->font.xfont) + XFreeFont(dc->dpy, dc->font.xfont); + if (dc->canvas) + XFreePixmap(dc->dpy, dc->canvas); + if (dc->gc) + XFreeGC(dc->dpy, dc->gc); + if (dc->dpy) + XCloseDisplay(dc->dpy); + if (dc) + free(dc); } -unsigned long -getcolor(DC *dc, const char *colstr) { - Colormap cmap = DefaultColormap(dc->dpy, DefaultScreen(dc->dpy)); - XColor color; +unsigned long getcolor(DC * dc, const char *colstr) +{ + Colormap cmap = DefaultColormap(dc->dpy, DefaultScreen(dc->dpy)); + XColor color; - if(!XAllocNamedColor(dc->dpy, cmap, colstr, &color, &color)) - eprintf("cannot allocate color '%s'\n", colstr); - return color.pixel; + if (!XAllocNamedColor(dc->dpy, cmap, colstr, &color, &color)) + eprintf("cannot allocate color '%s'\n", colstr); + return color.pixel; } -ColorSet * -initcolor(DC *dc, const char * foreground, const char * background) { - ColorSet * col = (ColorSet *)malloc(sizeof(ColorSet)); - if(!col) - eprintf("error, cannot allocate memory for color set"); - col->BG = getcolor(dc, background); - col->FG = getcolor(dc, foreground); - if(dc->font.xft_font) - if(!XftColorAllocName(dc->dpy, DefaultVisual(dc->dpy, DefaultScreen(dc->dpy)), - DefaultColormap(dc->dpy, DefaultScreen(dc->dpy)), foreground, &col->FG_xft)) - eprintf("error, cannot allocate xft font color '%s'\n", foreground); - return col; +ColorSet *initcolor(DC * dc, const char *foreground, const char *background) +{ + ColorSet *col = (ColorSet *) malloc(sizeof(ColorSet)); + if (!col) + eprintf("error, cannot allocate memory for color set"); + col->BG = getcolor(dc, background); + col->FG = getcolor(dc, foreground); + if (dc->font.xft_font) + if (!XftColorAllocName + (dc->dpy, DefaultVisual(dc->dpy, DefaultScreen(dc->dpy)), + DefaultColormap(dc->dpy, DefaultScreen(dc->dpy)), + foreground, &col->FG_xft)) + eprintf("error, cannot allocate xft font color '%s'\n", + foreground); + return col; } -DC * -initdc(void) { - DC *dc; +DC *initdc(void) +{ + DC *dc; - if(!setlocale(LC_CTYPE, "") || !XSupportsLocale()) - fputs("no locale support\n", stderr); - if(!(dc = calloc(1, sizeof *dc))) - eprintf("cannot malloc %u bytes:", sizeof *dc); - if(!(dc->dpy = XOpenDisplay(NULL))) - eprintf("cannot open display\n"); + if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) + fputs("no locale support\n", stderr); + if (!(dc = calloc(1, sizeof *dc))) + eprintf("cannot malloc %u bytes:", sizeof *dc); + if (!(dc->dpy = XOpenDisplay(NULL))) + eprintf("cannot open display\n"); - dc->gc = XCreateGC(dc->dpy, DefaultRootWindow(dc->dpy), 0, NULL); - XSetLineAttributes(dc->dpy, dc->gc, 1, LineSolid, CapButt, JoinMiter); - return dc; + dc->gc = XCreateGC(dc->dpy, DefaultRootWindow(dc->dpy), 0, NULL); + XSetLineAttributes(dc->dpy, dc->gc, 1, LineSolid, CapButt, JoinMiter); + return dc; } -void -initfont(DC *dc, const char *fontstr) { - char *def, **missing, **names; - int i, n; - XFontStruct **xfonts; +void initfont(DC * dc, const char *fontstr) +{ + char *def, **missing, **names; + int i, n; + XFontStruct **xfonts; - missing = NULL; - if((dc->font.xfont = XLoadQueryFont(dc->dpy, fontstr))) { - dc->font.ascent = dc->font.xfont->ascent; - dc->font.descent = dc->font.xfont->descent; - dc->font.width = dc->font.xfont->max_bounds.width; - } else if((dc->font.set = XCreateFontSet(dc->dpy, fontstr, &missing, &n, &def))) { - n = XFontsOfFontSet(dc->font.set, &xfonts, &names); - for(i = 0; i < n; i++) { - dc->font.ascent = MAX(dc->font.ascent, xfonts[i]->ascent); - dc->font.descent = MAX(dc->font.descent, xfonts[i]->descent); - dc->font.width = MAX(dc->font.width, xfonts[i]->max_bounds.width); - } - } else if((dc->font.xft_font = XftFontOpenName(dc->dpy, DefaultScreen(dc->dpy), fontstr))) { - dc->font.ascent = dc->font.xft_font->ascent; - dc->font.descent = dc->font.xft_font->descent; - dc->font.width = dc->font.xft_font->max_advance_width; - } else { - eprintf("cannot load font '%s'\n", fontstr); - } - if(missing) - XFreeStringList(missing); - dc->font.height = dc->font.ascent + dc->font.descent; - return; + missing = NULL; + if ((dc->font.xfont = XLoadQueryFont(dc->dpy, fontstr))) { + dc->font.ascent = dc->font.xfont->ascent; + dc->font.descent = dc->font.xfont->descent; + dc->font.width = dc->font.xfont->max_bounds.width; + } else + if ((dc->font.set = + XCreateFontSet(dc->dpy, fontstr, &missing, &n, &def))) { + n = XFontsOfFontSet(dc->font.set, &xfonts, &names); + for (i = 0; i < n; i++) { + dc->font.ascent = + MAX(dc->font.ascent, xfonts[i]->ascent); + dc->font.descent = + MAX(dc->font.descent, xfonts[i]->descent); + dc->font.width = + MAX(dc->font.width, xfonts[i]->max_bounds.width); + } + } else + if ((dc->font.xft_font = + XftFontOpenName(dc->dpy, DefaultScreen(dc->dpy), fontstr))) { + dc->font.ascent = dc->font.xft_font->ascent; + dc->font.descent = dc->font.xft_font->descent; + dc->font.width = dc->font.xft_font->max_advance_width; + } else { + eprintf("cannot load font '%s'\n", fontstr); + } + if (missing) + XFreeStringList(missing); + dc->font.height = dc->font.ascent + dc->font.descent; + return; } -void -mapdc(DC *dc, Window win, unsigned int w, unsigned int h) { - XCopyArea(dc->dpy, dc->canvas, win, dc->gc, 0, 0, w, h, 0, 0); +void mapdc(DC * dc, Window win, unsigned int w, unsigned int h) +{ + XCopyArea(dc->dpy, dc->canvas, win, dc->gc, 0, 0, w, h, 0, 0); } -void -resizedc(DC *dc, unsigned int w, unsigned int h) { - int screen = DefaultScreen(dc->dpy); - if(dc->canvas) - XFreePixmap(dc->dpy, dc->canvas); +void resizedc(DC * dc, unsigned int w, unsigned int h) +{ + int screen = DefaultScreen(dc->dpy); + if (dc->canvas) + XFreePixmap(dc->dpy, dc->canvas); - dc->w = w; - dc->h = h; - dc->canvas = XCreatePixmap(dc->dpy, DefaultRootWindow(dc->dpy), w, h, - DefaultDepth(dc->dpy, screen)); - if(dc->xftdraw) { - XftDrawDestroy(dc->xftdraw); - } - if(dc->font.xft_font) { - dc->xftdraw = XftDrawCreate(dc->dpy, dc->canvas, DefaultVisual(dc->dpy,screen), DefaultColormap(dc->dpy,screen)); - if(!(dc->xftdraw)) - eprintf("error, cannot create xft drawable\n"); - } + dc->w = w; + dc->h = h; + dc->canvas = XCreatePixmap(dc->dpy, DefaultRootWindow(dc->dpy), w, h, + DefaultDepth(dc->dpy, screen)); + if (dc->xftdraw) { + XftDrawDestroy(dc->xftdraw); + } + if (dc->font.xft_font) { + dc->xftdraw = + XftDrawCreate(dc->dpy, dc->canvas, + DefaultVisual(dc->dpy, screen), + DefaultColormap(dc->dpy, screen)); + if (!(dc->xftdraw)) + eprintf("error, cannot create xft drawable\n"); + } } -int -textnw(DC *dc, const char *text, size_t len) { - if(dc->font.xft_font) { - XGlyphInfo gi; - XftTextExtentsUtf8(dc->dpy, dc->font.xft_font, (const FcChar8*)text, len, &gi); - return gi.width; - } else if(dc->font.set) { - XRectangle r; - XmbTextExtents(dc->font.set, text, len, NULL, &r); - return r.width; - } - return XTextWidth(dc->font.xfont, text, len); +int textnw(DC * dc, const char *text, size_t len) +{ + if (dc->font.xft_font) { + XGlyphInfo gi; + XftTextExtentsUtf8(dc->dpy, dc->font.xft_font, + (const FcChar8 *)text, len, &gi); + return gi.width; + } else if (dc->font.set) { + XRectangle r; + XmbTextExtents(dc->font.set, text, len, NULL, &r); + return r.width; + } + return XTextWidth(dc->font.xfont, text, len); } -int -textw(DC *dc, const char *text) { - return textnw(dc, text, strlen(text)) + dc->font.height; +int textw(DC * dc, const char *text) +{ + return textnw(dc, text, strlen(text)) + dc->font.height; } + +/* vim: set ts=8 sw=8 tw=0: */ diff --git a/draw.h b/draw.h index 630ff2f..84f1cfe 100644 --- a/draw.h +++ b/draw.h @@ -36,42 +36,44 @@ DEALINGS IN THE SOFTWARE. #include typedef struct { - int x, y, w, h; - Bool invert; - Display *dpy; - GC gc; - Pixmap canvas; - XftDraw *xftdraw; - struct { - int ascent; - int descent; - int height; - int width; - XFontSet set; - XFontStruct *xfont; - XftFont *xft_font; - } font; -} DC; /* draw context */ + int x, y, w, h; + Bool invert; + Display *dpy; + GC gc; + Pixmap canvas; + XftDraw *xftdraw; + struct { + int ascent; + int descent; + int height; + int width; + XFontSet set; + XFontStruct *xfont; + XftFont *xft_font; + } font; +} DC; /* draw context */ typedef struct { - unsigned long FG; - XftColor FG_xft; - unsigned long BG; + unsigned long FG; + XftColor FG_xft; + unsigned long BG; } ColorSet; -void drawrect(DC *dc, int x, int y, unsigned int w, unsigned int h, Bool fill, unsigned long color); -void drawtext(DC *dc, const char *text, ColorSet *col); -void drawtextn(DC *dc, const char *text, size_t n, ColorSet *col); -void freecol(DC *dc, ColorSet *col); +void drawrect(DC * dc, int x, int y, unsigned int w, unsigned int h, Bool fill, + unsigned long color); +void drawtext(DC * dc, const char *text, ColorSet * col); +void drawtextn(DC * dc, const char *text, size_t n, ColorSet * col); +void freecol(DC * dc, ColorSet * col); void eprintf(const char *fmt, ...); -void freedc(DC *dc); -unsigned long getcolor(DC *dc, const char *colstr); -ColorSet *initcolor(DC *dc, const char *foreground, const char *background); +void freedc(DC * dc); +unsigned long getcolor(DC * dc, const char *colstr); +ColorSet *initcolor(DC * dc, const char *foreground, const char *background); DC *initdc(void); -void initfont(DC *dc, const char *fontstr); -void mapdc(DC *dc, Window win, unsigned int w, unsigned int h); -void resizedc(DC *dc, unsigned int w, unsigned int h); -int textnw(DC *dc, const char *text, size_t len); -int textw(DC *dc, const char *text); +void initfont(DC * dc, const char *fontstr); +void mapdc(DC * dc, Window win, unsigned int w, unsigned int h); +void resizedc(DC * dc, unsigned int w, unsigned int h); +int textnw(DC * dc, const char *text, size_t len); +int textw(DC * dc, const char *text); #endif +/* vim: set ts=8 sw=8 tw=0: */ diff --git a/dunst.c b/dunst.c index f51179b..c436c7d 100644 --- a/dunst.c +++ b/dunst.c @@ -21,6 +21,8 @@ #include "dunst.h" #include "draw.h" +#include "dunst_dbus.h" +#include "list.h" #include "ini.h" #define INRECT(x,y,rx,ry,rw,rh) ((x) >= (rx) && (x) < (rx)+(rw) && (y) >= (ry) && (y) < (ry)+(rh)) @@ -38,24 +40,25 @@ /* structs */ typedef struct _screen_info { - int scr; - dimension_t dim; + int scr; + dimension_t dim; } screen_info; - /* global variables */ char *font = "-*-terminus-medium-r-*-*-16-*-*-*-*-*-*-*"; char *normbgcolor = "#1793D1"; char *normfgcolor = "#DDDDDD"; char *critbgcolor = "#ffaaaa"; char *critfgcolor = "#000000"; -char *lowbgcolor = "#aaaaff"; +char *lowbgcolor = "#aaaaff"; char *lowfgcolor = "#000000"; -char *format = "%s %b"; /* default format */ +char *format = "%s %b"; /* default format */ int timeouts[] = { 10, 10, 0 }; /* low, normal, critical */ -char *geom = "0x0"; /* geometry */ -int sort = True; /* sort messages by urgency */ -int indicate_hidden = True; /* show count of hidden messages */ + +char *geom = "0x0"; /* geometry */ +int height_limit; +int sort = True; /* sort messages by urgency */ +int indicate_hidden = True; /* show count of hidden messages */ char *key_string = NULL; char *history_key_string = NULL; KeySym mask = 0; @@ -63,7 +66,6 @@ int idle_threshold = 0; int verbosity = 0; - rule_t *rules = NULL; /* index of colors fit to urgency level */ static ColorSet *colors[3]; @@ -71,8 +73,6 @@ static const char *color_strings[2][3]; static Atom utf8; static DC *dc; static Window win; -static msg_queue_t *msgqueue = NULL; -static msg_queue_t *history = NULL; static time_t now; static int visible = False; static KeySym key = NoSymbol; @@ -83,1066 +83,1089 @@ static XScreenSaverInfo *screensaver_info; static int font_h; static char *config_file; -/* list functions */ -msg_queue_t *add(msg_queue_t *queue, msg_queue_t *msg); -msg_queue_t *delete(msg_queue_t *elem); -msg_queue_t *pop(msg_queue_t *list, msg_queue_t **target); -int list_len(msg_queue_t *list); - +/* notification lists */ +list *notification_queue = NULL; /* all new notifications get into here */ +list *displayed_notifications = NULL; /* currently displayed notifications */ +list *notification_history = NULL; /* history of displayed notifications */ /* misc funtions */ -void apply_rules(msg_queue_t *msg); +void apply_rules(notification * n); void check_timeouts(void); -void delete_all_msg(void); -void delete_msg(msg_queue_t *elem); -void drawmsg(void); +void draw_notifications(void); void dunst_printf(int level, const char *fmt, ...); char *fix_markup(char *str); void handle_mouse_click(XEvent ev); void handleXEvents(void); void history_pop(void); -void initmsg(msg_queue_t *msg); rule_t *initrule(void); int is_idle(void); -char *string_replace(const char *needle, const char *replacement, char *haystack); +char *string_replace(const char *needle, const char *replacement, + char *haystack); char *strtrim(char *str); void run(void); void setup(void); -void show_win(void); void usage(int exit_status); +void hide_window(); +l_node *most_important(list * l); +void draw_win(void); +void hide_win(void); +void move_all_to_history(void); -#include "dunst_dbus.h" - - -void -print_rule(rule_t *r) { - dunst_printf(DEBUG, "%s %s %s %s %s %d %d %s %s %s\n", - r->name, - r->appname, - r->summary, - r->body, - r->icon, - r->timeout, - r->urgency, - r->fg, - r->bg, - r->format); -} - -void -print_rules(void) { - rule_t *cur = rules; - dunst_printf(DEBUG, "current rules:\n"); - if (cur == NULL) { - dunst_printf(DEBUG, "no rules present\n"); - return; - } - while(cur->next) { - print_rule(cur); - cur = cur->next; - } -} - -msg_queue_t* -add(msg_queue_t *queue, msg_queue_t *new) { - msg_queue_t *i; - msg_queue_t *prev = NULL; - - new->next = NULL; - if(queue == NULL) { - return new; - } - - if(sort) { - for(i = queue; i->next; i = i->next) { - if(new->urgency > i->urgency) { - if (!prev) { - /* we are at the first element */ - queue = new; - new->next = i; - return queue; - } else { - prev->next = new; - new->next = i; - return queue; - } - } - prev = i; - } - /* we've reached the end of the queue */ - i->next = new; - new->next = NULL; - } else { - for(i = queue; i->next; i = i->next); - i->next = new; - } - return queue; - -} - -void -apply_rules(msg_queue_t *msg) { - rule_t *cur = rules; - while(cur != NULL) { - if((!cur->appname || !fnmatch(cur->appname, msg->appname, 0)) - && (!cur->summary || !fnmatch(cur->summary, msg->summary, 0)) - && (!cur->body || !fnmatch(cur->body, msg->body, 0)) - && (!cur->icon || !fnmatch(cur->icon, msg->icon, 0))) { - dunst_printf(DEBUG, "matched rule: %s\n", cur->name); - msg->timeout = cur->timeout != -1 ? cur->timeout : msg->timeout; - msg->urgency = cur->urgency != -1 ? cur->urgency : msg->urgency; - msg->color_strings[ColFG] = cur->fg ? cur->fg : msg->color_strings[ColFG]; - msg->color_strings[ColBG] = cur->bg ? cur->bg : msg->color_strings[ColBG]; - msg->format = cur->format ? cur->format : msg->format; - } - - cur = cur->next; - } -} - -msg_queue_t* -delete(msg_queue_t *elem) { - msg_queue_t *prev; - msg_queue_t *next; - if(msgqueue == NULL || elem == NULL) { - return NULL; - } - - if(elem == msgqueue) { - next = elem->next; - history = add(history, elem); - return next; - } - - next = elem->next; - for(prev = msgqueue; prev != NULL; prev = prev->next) { - if(prev->next == elem) { - break; - } - } - if(prev == NULL) { - /* the element wasn't in the list */ - return msgqueue; - } - - prev->next = next; - history = add(history, elem); - return msgqueue; -} - -int list_len(msg_queue_t *list) { - int count = 0; - msg_queue_t *i; - if(list == NULL) { - return 0; - } - for(i = list; i != NULL; i = i->next) { - count++; - } - return count; -} - -msg_queue_t* -pop(msg_queue_t *list, msg_queue_t **target) { - msg_queue_t *prev = NULL; - msg_queue_t *cur = NULL; - - if (!list) { - *target = NULL; - return list; - } - - if (!list->next) { - *target = list; - return NULL; - } - - cur = list->next; - prev = list; - while(cur->next) { - cur = cur->next; - prev = prev->next; - } - - prev->next = NULL; - - *target = cur; - return list; -} - -void -check_timeouts(void) { - msg_queue_t *cur, *next; - - cur = msgqueue; - while(cur != NULL) { - if(is_idle()) { - cur->start = now; - cur = cur->next; - continue; - } - if(cur->start == 0 || cur->timeout == 0) { - cur = cur->next; - continue; - } - if(difftime(now, cur->start) > cur->timeout) { - next = cur->next; - delete_msg(cur); - cur = next; +int cmp_notification(notification * a, notification * b) +{ + if (a->urgency != b->urgency) { + return a->urgency - b->urgency; } else { - cur = cur->next; + return b->timestamp - a->timestamp; } - } } -void -delete_all_msg(void) { - while (msgqueue != NULL) { - delete_msg(NULL); - } +l_node *most_important(list * l) +{ + + if (l == NULL || l_is_empty(l)) { + return NULL; + } + + if (sort) { + l_node *iter; + notification *max; + l_node *node_max; + notification *data; + + max = l->head->data; + node_max = l->head; + for (iter = l->head; iter; iter = iter->next) { + data = (notification *) iter->data; + if (cmp_notification(max, data) < 0) { + max = data; + node_max = iter; + } + } + return node_max; + } else { + return l->head; + } } -void -delete_msg(msg_queue_t *elem) { - msg_queue_t *cur; - msg_queue_t *min; - int visible_count = 0; - if(msgqueue == NULL) { - return; - } - if(elem == NULL) { - /* delete the oldest element */ - min = msgqueue; - for(elem = msgqueue; elem->next != NULL; elem = elem->next) { - if(elem->start > 0 && min->start > elem->start) { - min = elem; - } +void print_rule(rule_t * r) +{ + dunst_printf(DEBUG, "%s %s %s %s %s %d %d %s %s %s\n", + r->name, + r->appname, + r->summary, + r->body, + r->icon, r->timeout, r->urgency, r->fg, r->bg, r->format); +} + +void print_rules(void) +{ + rule_t *cur = rules; + dunst_printf(DEBUG, "current rules:\n"); + if (cur == NULL) { + dunst_printf(DEBUG, "no rules present\n"); + return; } - elem = min; - } - msgqueue = delete(elem); - for(cur = msgqueue; cur != NULL; cur = cur->next) { - if(cur->start != 0) { - visible_count++; + while (cur->next) { + print_rule(cur); + cur = cur->next; } - } - if(visible_count == 0) { - /* hide window */ - if(!visible) { - /* window is already hidden */ - return; +} + +void apply_rules(notification * n) +{ + rule_t *cur = rules; + while (cur != NULL) { + if ((!cur->appname || !fnmatch(cur->appname, n->appname, 0)) + && (!cur->summary || !fnmatch(cur->summary, n->summary, 0)) + && (!cur->body || !fnmatch(cur->body, n->body, 0)) + && (!cur->icon || !fnmatch(cur->icon, n->icon, 0))) { + dunst_printf(DEBUG, "matched rule: %s\n", cur->name); + n->timeout = + cur->timeout != -1 ? cur->timeout : n->timeout; + n->urgency = + cur->urgency != -1 ? cur->urgency : n->urgency; + n->color_strings[ColFG] = + cur->fg ? cur->fg : n->color_strings[ColFG]; + n->color_strings[ColBG] = + cur->bg ? cur->bg : n->color_strings[ColBG]; + n->format = cur->format ? cur->format : n->format; + } + + cur = cur->next; } +} + +void check_timeouts(void) +{ + l_node *iter; + notification *current; + l_node *next; + + /* nothing to do */ + if (l_is_empty(displayed_notifications)) { + return; + } + + iter = displayed_notifications->head; + while (iter != NULL) { + current = (notification *) iter->data; + + /* don't timeout when user is idle */ + if (is_idle()) { + current->start = now; + iter = iter->next; + continue; + } + + /* skip hidden and sticky messages */ + if (current->start == 0 || current->timeout == 0) { + iter = iter->next; + continue; + } + + /* remove old message */ + if (difftime(now, current->start) > current->timeout) { + /* l_move changes iter->next, so we need to store it beforehand */ + next = iter->next; + l_move(displayed_notifications, notification_history, + iter); + iter = next; + continue; + + } else { + iter = iter->next; + } + } +} + +void update_lists() +{ + l_node *to_move; + notification *n; + check_timeouts(); + + /* move notifications from queue to displayed */ + while (l_length(displayed_notifications) < height_limit + && !l_is_empty(notification_queue)) { + + if (l_length(displayed_notifications) >= height_limit) { + /* the list is full */ + break; + } + + to_move = most_important(notification_queue); + n = (notification *) to_move->data; + n->start = now; + + /* TODO get notifications pushed back into + * notification_queue if there's a more important + * message waiting there + */ + + l_move(notification_queue, displayed_notifications, to_move); + + l_sort(displayed_notifications, cmp_notification); + + } +} + +void draw_win(void) +{ + int width, x, y, height; + unsigned int len = l_length(displayed_notifications); + l_node *iter; + notification *data; + char hidden[128]; + ColorSet *hidden_colors = colors[NORM]; + dc->x = 0; + dc->y = 0; + dc->h = 0; + + /* a height of 0 doesn't make sense, so we define it as 1 */ + if (geometry.h == 0) { + geometry.h = 1; + } + + height = MIN(geometry.h, len); + + if (indicate_hidden && !l_is_empty(notification_queue)) { + sprintf(hidden, "(%d more)", l_length(notification_queue)); + height++; + } + + /* + * calculate the width + */ + + if (geometry.mask & HeightValue) { + if (geometry.h) { + /* code */ + } + } + /* defined width */ + if (geometry.mask & WidthValue) { + + /* dynamic width */ + if (geometry.w == 0) { + width = 0; + + for (iter = displayed_notifications->head; + iter; iter = iter->next) { + data = (notification *) iter->data; + width = MAX(width, textw(dc, data->msg)); + } + + /* we also have the "(x more)" message into account */ + if (indicate_hidden + && !l_is_empty(displayed_notifications)) { + width = MAX(width, textw(dc, hidden)); + } + + } else { + width = geometry.w; + } + } else { + width = scr.dim.w; + } + + /* + * calculate window position + */ + + if (geometry.mask & XNegative) { + x = (scr.dim.x + (scr.dim.w - width)) + geometry.x; + } else { + x = scr.dim.x + geometry.x; + } + + if (geometry.mask & YNegative) { + y = (scr.dim.h + geometry.y) - height * font_h; + } else { + y = 0 + geometry.y; + } + + /* resize window and draw background */ + resizedc(dc, width, height * font_h); + XResizeWindow(dc->dpy, win, width, height * font_h); + drawrect(dc, 0, 0, width, height * font_h, True, colors[NORM]->BG); + + /* draw notifications */ + for (iter = displayed_notifications->head; iter; iter = iter->next) { + data = (notification *) iter->data; + + drawrect(dc, 0, dc->y, width, font_h, True, data->colors->BG); + drawtext(dc, data->msg, data->colors); + dc->y += font_h; + + /* the "(x more)" message should have the same BG-color as + * the last displayed_notifications + */ + hidden_colors = data->colors; + } + + /* actually draw the "(x more)" message */ + if (indicate_hidden && !l_is_empty(notification_queue)) { + drawrect(dc, 0, dc->y, width, font_h, True, hidden_colors->BG); + + drawtext(dc, hidden, hidden_colors); + dc->y += font_h; + } + + /* move and map window */ + XMoveWindow(dc->dpy, win, x, y); + mapdc(dc, win, width, height * font_h); +} + +void dunst_printf(int level, const char *fmt, ...) +{ + va_list ap; + + if (level > verbosity) { + return; + } + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +char +*fix_markup(char *str) +{ + char *replace_buf, *start, *end; + + if (str == NULL) { + return NULL; + } + + str = string_replace(""", "\"", str); + str = string_replace("'", "'", str); + str = string_replace("&", "&", str); + str = string_replace("<", "<", str); + str = string_replace(">", ">", str); + + str = string_replace("\n", " ", str); + /* remove tags */ + str = string_replace("", "", str); + str = string_replace("", "", str); + str = string_replace("
", " ", str); + str = string_replace("
", " ", str); + str = string_replace("
", " ", str); + str = string_replace("", "", str); + str = string_replace("", "", str); + str = string_replace("", "", str); + str = string_replace("", "", str); + str = string_replace("", "", 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; + +} + +void handle_mouse_click(XEvent ev) +{ + l_node *iter = displayed_notifications->head; + int i; + if (ev.xbutton.button == Button3) { + move_all_to_history(); + } else if (ev.xbutton.button == Button1) { + i = ev.xbutton.y / font_h; + for (i = i; i > 0; i--) { + /* if the user clicks on the "(x more)" message, + * keep iter at the last displayed message and + * remove that instead + */ + if (iter->next) { + iter = iter->next; + } + } + l_move(displayed_notifications, notification_history, iter); + } +} + +void handleXEvents(void) +{ + XEvent ev; + l_node *iter; + while (XPending(dc->dpy) > 0) { + XNextEvent(dc->dpy, &ev); + switch (ev.type) { + case Expose: + if (ev.xexpose.count == 0) + draw_win(); + mapdc(dc, win, scr.dim.w, font_h); + break; + case SelectionNotify: + if (ev.xselection.property == utf8) + break; + case VisibilityNotify: + if (ev.xvisibility.state != VisibilityUnobscured) + XRaiseWindow(dc->dpy, win); + break; + case ButtonPress: + if (ev.xbutton.window == win) { + handle_mouse_click(ev); + } + break; + case KeyPress: + if (XLookupKeysym(&ev.xkey, 0) == key) { + if (!l_is_empty(displayed_notifications)) { + for (iter = + displayed_notifications->head; + iter->next; iter = iter->next) ; + l_move(displayed_notifications, + notification_history, iter); + } + } + if (XLookupKeysym(&ev.xkey, 0) == history_key) { + history_pop(); + } + } + } +} + +void move_all_to_history() +{ + l_node *node; + + while (!l_is_empty(displayed_notifications)) { + node = displayed_notifications->head; + l_move(displayed_notifications, notification_history, node); + } + while (!l_is_empty(notification_queue)) { + node = notification_queue->head; + l_move(notification_queue, notification_history, node); + } +} + +void history_pop(void) +{ + l_node *iter; + notification *data; + + /* nothing to do */ + if (l_is_empty(notification_history)) { + return; + } + + for (iter = notification_history->head; iter->next; iter = iter->next) ; + data = (notification *) iter->data; + data->redisplayed = True; + data->start = 0; + l_move(notification_history, notification_queue, iter); + + if (!visible) { + map_win(); + } +} + +void init_notification(notification * n) +{ + const char *fg = NULL; + const char *bg = NULL; + n->format = format; + apply_rules(n); + + n->msg = string_replace("%a", n->appname, strdup(n->format)); + n->msg = string_replace("%s", n->summary, n->msg); + n->msg = string_replace("%i", n->icon, n->msg); + n->msg = string_replace("%I", basename(n->icon), n->msg); + n->msg = string_replace("%b", n->body, n->msg); + + n->msg = fix_markup(n->msg); + n->msg = strtrim(n->msg); + /* 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 ? timeouts[n->urgency] : n->timeout; + n->start = 0; + + n->timestamp = now; + + n->redisplayed = False; + + dunst_printf(MSG, "%s\n", n->msg); + dunst_printf(INFO, + "{\n appname: %s\n summary: %s\n body: %s\n icon: %s\n urgency: %d\n timeout: %d\n}", + n->appname, n->summary, n->body, n->icon, + n->urgency, n->timeout); + + l_push(notification_queue, n); +} + +rule_t *initrule(void) +{ + rule_t *r = malloc(sizeof(rule_t)); + r->name = NULL; + r->appname = NULL; + r->summary = NULL; + r->body = NULL; + r->icon = NULL; + r->timeout = -1; + r->urgency = -1; + r->fg = NULL; + r->bg = NULL; + r->format = NULL; + r->next = NULL; + + return r; +} + +int is_idle(void) +{ + XScreenSaverQueryInfo(dc->dpy, DefaultRootWindow(dc->dpy), + screensaver_info); + if (idle_threshold == 0) { + return False; + } + return screensaver_info->idle / 1000 > idle_threshold; +} + +char *string_replace(const char *needle, const char *replacement, + char *haystack) +{ + char *tmp, *start; + int size; + start = strstr(haystack, needle); + if (start == NULL) { + return haystack; + } + + size = (strlen(haystack) - strlen(needle)) + strlen(replacement) + 1; + tmp = calloc(sizeof(char), size); + memset(tmp, '\0', size); + + strncpy(tmp, haystack, start - haystack); + tmp[start - haystack] = '\0'; + + sprintf(tmp + strlen(tmp), "%s%s", replacement, start + strlen(needle)); + free(haystack); + + if (strstr(tmp, needle)) { + return string_replace(needle, replacement, tmp); + } else { + return tmp; + } +} + +char *strtrim(char *str) +{ + char *end; + while (isspace(*str)) + str++; + + end = str + strlen(str) - 1; + while (isspace(*end)) { + *end = '\0'; + end--; + } + + return str; + +} + +void run(void) +{ + while (True) { + dbus_poll(200); + now = time(&now); + + /* move messages from notification_queue to displayed_notifications */ + update_lists(); + if (l_length(displayed_notifications) > 0) { + if (!visible) { + map_win(); + } else { + draw_win(); + } + } else { + if (visible) { + hide_win(); + } + } + handleXEvents(); + } +} + +void hide_win() +{ XUngrabButton(dc->dpy, AnyButton, AnyModifier, win); XUnmapWindow(dc->dpy, win); XFlush(dc->dpy); visible = False; - } else { - drawmsg(); - } } -void -drawmsg(void) { - int width, x, y, height, drawn_msg_count, i; - unsigned int len = list_len(msgqueue); - msg_queue_t *cur_msg = msgqueue; - char hidden[128]; - int hidden_count = 0; - int hidden_color_idx = NORM; - dc->x = 0; - dc->y = 0; - dc->h = 0; - /* a height of 0 doesn't make sense, so we define it as 1 */ - if(geometry.h == 0) { - geometry.h = 1; - } - - height = MIN(geometry.h, len); - drawn_msg_count = height; - hidden_count = len - height; - hidden_count = indicate_hidden && hidden_count > 0 ? hidden_count : 0; - sprintf(hidden, "(%d more)", hidden_count); - if(hidden_count) - height++; - - if(geometry.mask & WidthValue) { - if(geometry.w == 0) { - width = 0; - for(i = 0; i < height; i++){ - width = MAX(width, textw(dc, cur_msg->msg)); - if(hidden_count) - width = MAX(width, textw(dc,hidden)); - cur_msg = cur_msg->next; - } - } else { - width = geometry.w; - } - } else { - width = scr.dim.w; - } - - cur_msg = msgqueue; - - if(geometry.mask & XNegative) { - x = (scr.dim.x + (scr.dim.w - width)) + geometry.x; - } else { - x = scr.dim.x + geometry.x; - } - - if(geometry.mask & YNegative) { - y = (scr.dim.h + geometry.y) - height*font_h; - } else { - y = 0 + geometry.y; - } - - resizedc(dc, width, height*font_h); - XResizeWindow(dc->dpy, win, width, height*font_h); - drawrect(dc, 0, 0, width, height*font_h, True, colors[NORM]->BG); - - for(i = 0; i < drawn_msg_count; i++) { - if(cur_msg->start == 0) - cur_msg->start = now; - - drawrect(dc, 0, dc->y, width, font_h, True, cur_msg->colors->BG); - drawtext(dc, cur_msg->msg, cur_msg->colors); - - dc->y += font_h; - hidden_color_idx = cur_msg->urgency; - cur_msg = cur_msg->next; - } - - if(hidden_count) { - drawrect(dc, 0, dc->y, width, font_h, True, colors[NORM]->BG); - - drawtext(dc, hidden, colors[hidden_color_idx]); - dc->y += font_h; - } - - - XMoveWindow(dc->dpy, win, x, y); - - mapdc(dc, win, width, height*font_h); -} - -void -dunst_printf(int level, const char *fmt, ...) { - va_list ap; - - if(level > verbosity) { - return; - } - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); -} - -char -*fix_markup(char *str) { - char *replace_buf, *start, *end; - - if(str == NULL) { - return NULL; - } - - str = string_replace(""", "\"", str); - str = string_replace("'", "'", str); - str = string_replace("&", "&", str); - str = string_replace("<", "<", str); - str = string_replace(">", ">", str); - - str = string_replace("\n", " ", str); - /* remove tags */ - str = string_replace("", "", str); - str = string_replace("", "", str); - str = string_replace("
", " ", str); - str = string_replace("
", " ", str); - str = string_replace("
", " ", str); - str = string_replace("", "", str); - str = string_replace("", "", str); - str = string_replace("", "", str); - str = string_replace("", "", str); - str = string_replace("
", "", 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; - -} - - -void -handle_mouse_click(XEvent ev) { - msg_queue_t *cur_msg = msgqueue; - int i; - if(ev.xbutton.button == Button3) { - delete_all_msg(); - } else if(ev.xbutton.button == Button1) { - i = ev.xbutton.y / font_h; - for(i = i; i > 0; i--) { - cur_msg = cur_msg->next; - } - delete_msg(cur_msg); - } -} - -void -handleXEvents(void) { - XEvent ev; - while(XPending(dc->dpy) > 0) { - XNextEvent(dc->dpy, &ev); - switch(ev.type) { - case Expose: - if(ev.xexpose.count == 0) - mapdc(dc, win, scr.dim.w, font_h); - break; - case SelectionNotify: - if(ev.xselection.property == utf8) - break; - case VisibilityNotify: - if(ev.xvisibility.state != VisibilityUnobscured) - XRaiseWindow(dc->dpy, win); - break; - case ButtonPress: - if(ev.xbutton.window == win) { - handle_mouse_click(ev); - } - break; - case KeyPress: - if(XLookupKeysym(&ev.xkey, 0) == key) { - delete_msg(NULL); - } - if(XLookupKeysym(&ev.xkey, 0) == history_key) { - history_pop(); - } - } - } -} - -void -history_pop(void) { - msg_queue_t *elem = NULL; - history = pop(history, &elem); - if(!elem) { - return; - } - msgqueue = add(msgqueue, elem); - elem->timeout = elem->timeout == -1 ? timeouts[elem->urgency] : elem->timeout; - - elem->start = 0; - drawmsg(); -} - -void -initmsg(msg_queue_t *msg) { - const char *fg = NULL; - const char *bg = NULL; - msg->format = format; - apply_rules(msg); - - msg->msg = string_replace("%a", msg->appname, strdup(msg->format)); - msg->msg = string_replace("%s", msg->summary, msg->msg); - msg->msg = string_replace("%i", msg->icon, msg->msg); - msg->msg = string_replace("%I", basename(msg->icon), msg->msg); - msg->msg = string_replace("%b", msg->body, msg->msg); - - msg->msg = fix_markup(msg->msg); - msg->msg = strtrim(msg->msg); - /* urgency > CRIT -> array out of range */ - msg->urgency = msg->urgency > CRIT ? CRIT : msg->urgency; - - - - if (msg->color_strings[ColFG]) { - fg = msg->color_strings[ColFG]; - } else { - fg = color_strings[ColFG][msg->urgency]; - } - - if (msg->color_strings[ColBG]) { - bg = msg->color_strings[ColBG]; - } else { - bg = color_strings[ColBG][msg->urgency]; - } - - msg->colors = initcolor(dc, fg, bg); - - msg->timeout = msg->timeout == -1 ? timeouts[msg->urgency] : msg->timeout; - msg->start = 0; - - dunst_printf(MSG, "%s\n", msg->msg); - dunst_printf(INFO, "{\n appname: %s\n summary: %s\n body: %s\n icon: %s\n urgency: %d\n timeout: %d\n}", - msg->appname, msg->summary, msg->body, msg->icon, msg->urgency, msg->timeout); - -} - -rule_t * -initrule(void) { - rule_t *r = malloc(sizeof(rule_t)); - r->name = NULL; - r->appname = NULL; - r->summary = NULL; - r->body = NULL; - r->icon = NULL; - r->timeout = -1; - r->urgency = -1; - r->fg = NULL; - r->bg = NULL; - r->format = NULL; - r->next = NULL; - - return r; -} - -int -is_idle(void) +void setup(void) { - XScreenSaverQueryInfo(dc->dpy, DefaultRootWindow(dc->dpy), - screensaver_info); - if(idle_threshold == 0) { - return False; - } - return screensaver_info->idle / 1000 > idle_threshold; -} - -char * -string_replace(const char *needle, const char *replacement, char *haystack) { - char *tmp, *start; - int size; - start = strstr(haystack, needle); - if(start == NULL) { - return haystack; - } - - size = (strlen(haystack) - strlen(needle)) + strlen(replacement) + 1; - tmp = calloc(sizeof(char), size); - memset(tmp, '\0', size); - - strncpy(tmp, haystack, start-haystack); - tmp[start-haystack] = '\0'; - - sprintf(tmp+strlen(tmp), "%s%s", replacement, start+strlen(needle)); - free(haystack); - - if(strstr(tmp, needle)) { - return string_replace(needle, replacement, tmp); - } else { - return tmp; - } -} - -char * -strtrim(char *str) { - char *end; - while(isspace(*str)) str++; - - end = str + strlen(str) - 1; - while(isspace(*end)) { - *end = '\0'; - end--; - } - - return str; - -} - -void -run(void) { - - while(True) { - /* dbus_poll blocks for max 2 seconds, if no events are present */ - dbus_poll(); - now = time(&now); - if(msgqueue != NULL) { - show_win(); - check_timeouts(); - } - handleXEvents(); - } -} - -void -setup(void) { - Window root; - XSetWindowAttributes wa; - KeyCode code; + Window root; + XSetWindowAttributes wa; + KeyCode code; #ifdef XINERAMA - int n; - XineramaScreenInfo *info; + int n; + XineramaScreenInfo *info; #endif - if(scr.scr < 0) { - scr.scr = DefaultScreen(dc->dpy); - } - root = RootWindow(dc->dpy, DefaultScreen(dc->dpy)); - utf8 = XInternAtom(dc->dpy, "UTF8_STRING", False); + notification_queue = l_init(); + notification_history = l_init(); + displayed_notifications = l_init(); + if (scr.scr < 0) { + scr.scr = DefaultScreen(dc->dpy); + } + root = RootWindow(dc->dpy, DefaultScreen(dc->dpy)); - /* menu geometry */ - font_h = dc->font.height + FONT_HEIGHT_BORDER; + utf8 = XInternAtom(dc->dpy, "UTF8_STRING", False); + + /* menu geometry */ + font_h = dc->font.height + FONT_HEIGHT_BORDER; #ifdef XINERAMA - if((info = XineramaQueryScreens(dc->dpy, &n))) { - if(scr.scr >= n) { - fprintf(stderr, "Monitor %d not found\n", scr.scr); - exit(EXIT_FAILURE); - } - scr.dim.x = info[scr.scr].x_org; - scr.dim.y = info[scr.scr].y_org; - scr.dim.h = info[scr.scr].height; - scr.dim.w = info[scr.scr].width; - XFree(info); - } - else + if ((info = XineramaQueryScreens(dc->dpy, &n))) { + if (scr.scr >= n) { + fprintf(stderr, "Monitor %d not found\n", scr.scr); + exit(EXIT_FAILURE); + } + scr.dim.x = info[scr.scr].x_org; + scr.dim.y = info[scr.scr].y_org; + scr.dim.h = info[scr.scr].height; + scr.dim.w = info[scr.scr].width; + XFree(info); + } else #endif - { - scr.dim.x = 0; - scr.dim.y = 0; - scr.dim.w = DisplayWidth(dc->dpy, scr.scr); - scr.dim.h = DisplayHeight(dc->dpy, scr.scr); - } - /* menu window */ - wa.override_redirect = True; - wa.background_pixmap = ParentRelative; - wa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask | ButtonPressMask; - win = XCreateWindow(dc->dpy, root, scr.dim.x, scr.dim.y, scr.dim.w, font_h, 0, - DefaultDepth(dc->dpy, DefaultScreen(dc->dpy)), CopyFromParent, - DefaultVisual(dc->dpy, DefaultScreen(dc->dpy)), - CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); - - /* grab keys */ - if(key != NoSymbol) { - code = XKeysymToKeycode(dc->dpy, key); - XGrabKey(dc->dpy, code, mask, root, True, GrabModeAsync, GrabModeAsync); - } - - if (history_key != NoSymbol) { - code = XKeysymToKeycode(dc->dpy, history_key); - XGrabKey(dc->dpy, code, mask, root, True, GrabModeAsync, GrabModeAsync); - } -} - -void -show_win(void) { - if(visible || msgqueue == NULL) { - return; - } - - XMapRaised(dc->dpy, win); - XGrabButton(dc->dpy, AnyButton, AnyModifier, win, False, - BUTTONMASK, GrabModeAsync, GrabModeSync, None, None); - XFlush(dc->dpy); - drawmsg(); - visible = True; -} - -void -parse_cmdline(int argc, char *argv[]) { - - int c; - while(1) { - static struct option long_options[] = { - {"help", no_argument, NULL, 'h'}, - {"fn", required_argument, NULL, 'F'}, - {"nb", required_argument, NULL, 'n'}, - {"nf", required_argument, NULL, 'N'}, - {"lb", required_argument, NULL, 'l'}, - {"lf", required_argument, NULL, 'L'}, - {"cb", required_argument, NULL, 'c'}, - {"cf", required_argument, NULL, 'C'}, - {"to", required_argument, NULL, 't'}, - {"lto", required_argument, NULL, '0'}, - {"nto", required_argument, NULL, '1'}, - {"cto", required_argument, NULL, '2'}, - {"mon", required_argument, NULL, 'm'}, - {"format", required_argument, NULL, 'f'}, - {"key", required_argument, NULL, 'k'}, - {"history_key", required_argument, NULL, 'K'}, - {"geometry", required_argument, NULL, 'g'}, - {"config", required_argument, NULL, 'r'}, - {"mod", required_argument, NULL, 'M'}, - {"ns", no_argument, NULL, 'x'}, - {0,0,0,0} - }; - - - int option_index = 0; - - c = getopt_long_only(argc, argv, "bhsv", long_options, &option_index); - - if(c == -1) { - break; - } - - switch(c) { - case 0: - break; - case 'h': - usage(EXIT_SUCCESS); - break; - case 'F': - font = optarg; - break; - case 'n': - normbgcolor = optarg; - break; - case 'N': - normfgcolor = optarg; - break; - case 'l': - lowbgcolor = optarg; - break; - case 'L': - lowfgcolor = optarg; - break; - case 'c': - critbgcolor = optarg; - break; - case 'C': - critfgcolor = optarg; - break; - case 't': - timeouts[0] = atoi(optarg); - timeouts[1] = timeouts[0]; - break; - case '0': - timeouts[0] = atoi(optarg); - break; - case '1': - timeouts[1] = atoi(optarg); - break; - case '2': - timeouts[2] = atoi(optarg); - break; - case 'm': - scr.scr = atoi(optarg); - break; - case 'f': - format = optarg; - break; - case 'k': - key = XStringToKeysym(optarg); - break; - case 'K': - history_key = XStringToKeysym(optarg); - break; - case 'g': - geometry.mask = XParseGeometry(optarg, - &geometry.x, &geometry.y, - &geometry.w, &geometry.h); - break; - case 'M': - if(!strcmp(optarg, "ctrl")) { - mask |= ControlMask; - } - else if(!strcmp(optarg, "mod1")) { - mask |= Mod1Mask; - } - else if(!strcmp(optarg, "mod2")) { - mask |= Mod2Mask; - } - else if(!strcmp(optarg, "mod3")) { - mask |= Mod3Mask; - } - else if(!strcmp(optarg, "mod4")) { - mask |= Mod4Mask; - } else { - fprintf(stderr, "Unable to find mask: %s\n", optarg); - fprintf(stderr, "See manpage for list of available masks\n"); - } - break; - case 's': - sort = True; - break; - case 'r': - /* this option is parsed elsewhere. This is just to supress - * error message */ - break; - case 'x': - sort = False; - break; - case 'v': - verbosity++; - break; - default: - usage(EXIT_FAILURE); - break; + scr.dim.x = 0; + scr.dim.y = 0; + scr.dim.w = DisplayWidth(dc->dpy, scr.scr); + scr.dim.h = DisplayHeight(dc->dpy, scr.scr); + } + /* menu window */ + wa.override_redirect = True; + wa.background_pixmap = ParentRelative; + wa.event_mask = + ExposureMask | KeyPressMask | VisibilityChangeMask | + ButtonPressMask; + win = + XCreateWindow(dc->dpy, root, scr.dim.x, scr.dim.y, scr.dim.w, + font_h, 0, DefaultDepth(dc->dpy, + DefaultScreen(dc->dpy)), + CopyFromParent, DefaultVisual(dc->dpy, + DefaultScreen(dc->dpy)), + CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); + + /* grab keys */ + if (key != NoSymbol) { + code = XKeysymToKeycode(dc->dpy, key); + XGrabKey(dc->dpy, code, mask, root, True, GrabModeAsync, + GrabModeAsync); + } + + if (history_key != NoSymbol) { + code = XKeysymToKeycode(dc->dpy, history_key); + XGrabKey(dc->dpy, code, mask, root, True, GrabModeAsync, + GrabModeAsync); } - } } -static int -dunst_ini_get_boolean(const char *value) { +void map_win(void) +{ + /* window is already mapped or there's nothing to show */ + if (visible || l_is_empty(displayed_notifications)) { + return; + } - switch (value[0]) - { + XMapRaised(dc->dpy, win); + XGrabButton(dc->dpy, AnyButton, AnyModifier, win, False, + BUTTONMASK, GrabModeAsync, GrabModeSync, None, None); + XFlush(dc->dpy); + draw_win(); + visible = True; +} + +void parse_cmdline(int argc, char *argv[]) +{ + + int c; + while (1) { + static struct option long_options[] = { + {"help", no_argument, NULL, 'h'}, + {"fn", required_argument, NULL, 'F'}, + {"nb", required_argument, NULL, 'n'}, + {"nf", required_argument, NULL, 'N'}, + {"lb", required_argument, NULL, 'l'}, + {"lf", required_argument, NULL, 'L'}, + {"cb", required_argument, NULL, 'c'}, + {"cf", required_argument, NULL, 'C'}, + {"to", required_argument, NULL, 't'}, + {"lto", required_argument, NULL, '0'}, + {"nto", required_argument, NULL, '1'}, + {"cto", required_argument, NULL, '2'}, + {"mon", required_argument, NULL, 'm'}, + {"format", required_argument, NULL, 'f'}, + {"key", required_argument, NULL, 'k'}, + {"history_key", required_argument, NULL, 'K'}, + {"geometry", required_argument, NULL, 'g'}, + {"config", required_argument, NULL, 'r'}, + {"mod", required_argument, NULL, 'M'}, + {"ns", no_argument, NULL, 'x'}, + {0, 0, 0, 0} + }; + + int option_index = 0; + + c = getopt_long_only(argc, argv, "bhsv", long_options, + &option_index); + + if (c == -1) { + break; + } + + switch (c) { + case 0: + break; + case 'h': + usage(EXIT_SUCCESS); + break; + case 'F': + font = optarg; + break; + case 'n': + normbgcolor = optarg; + break; + case 'N': + normfgcolor = optarg; + break; + case 'l': + lowbgcolor = optarg; + break; + case 'L': + lowfgcolor = optarg; + break; + case 'c': + critbgcolor = optarg; + break; + case 'C': + critfgcolor = optarg; + break; + case 't': + timeouts[0] = atoi(optarg); + timeouts[1] = timeouts[0]; + break; + case '0': + timeouts[0] = atoi(optarg); + break; + case '1': + timeouts[1] = atoi(optarg); + break; + case '2': + timeouts[2] = atoi(optarg); + break; + case 'm': + scr.scr = atoi(optarg); + break; + case 'f': + format = optarg; + break; + case 'k': + key = XStringToKeysym(optarg); + break; + case 'K': + history_key = XStringToKeysym(optarg); + break; + case 'g': + geometry.mask = XParseGeometry(optarg, + &geometry.x, &geometry.y, + &geometry.w, + &geometry.h); + break; + case 'M': + if (!strcmp(optarg, "ctrl")) { + mask |= ControlMask; + } else if (!strcmp(optarg, "mod1")) { + mask |= Mod1Mask; + } else if (!strcmp(optarg, "mod2")) { + mask |= Mod2Mask; + } else if (!strcmp(optarg, "mod3")) { + mask |= Mod3Mask; + } else if (!strcmp(optarg, "mod4")) { + mask |= Mod4Mask; + } else { + fprintf(stderr, "Unable to find mask: %s\n", + optarg); + fprintf(stderr, + "See manpage for list of available masks\n"); + } + break; + case 's': + sort = True; + break; + case 'r': + /* this option is parsed elsewhere. This is just to supress + * error message */ + break; + case 'x': + sort = False; + break; + case 'v': + verbosity++; + break; + default: + usage(EXIT_FAILURE); + break; + } + } +} + +static int dunst_ini_get_boolean(const char *value) +{ + + switch (value[0]) { case 'y': case 'Y': case 't': case 'T': case '1': - return True; + return True; case 'n': case 'N': case 'f': case 'F': case '0': - return False; + return False; default: - return False; - } + return False; + } } -static rule_t * -dunst_rules_find_or_create(const char *section) { +static rule_t *dunst_rules_find_or_create(const char *section) +{ - rule_t *current_rule = rules, *last_rule; + rule_t *current_rule = rules, *last_rule; - while (current_rule && strcmp(current_rule->name, section) != 0) { - current_rule = current_rule->next; - } + while (current_rule && strcmp(current_rule->name, section) != 0) { + current_rule = current_rule->next; + } - if (current_rule) { - return current_rule; - } + if (current_rule) { + return current_rule; + } - dunst_printf(DEBUG, "adding rule %s\n", section); + dunst_printf(DEBUG, "adding rule %s\n", section); - current_rule = initrule(); - current_rule->name = strdup(section); + current_rule = initrule(); + current_rule->name = strdup(section); - last_rule = rules; - while (last_rule && last_rule->next) { - last_rule = last_rule->next; - } + last_rule = rules; + while (last_rule && last_rule->next) { + last_rule = last_rule->next; + } - if (last_rule == NULL) { - last_rule = current_rule; - rules = last_rule; - } else { - last_rule->next = current_rule; - } + if (last_rule == NULL) { + last_rule = current_rule; + rules = last_rule; + } else { + last_rule->next = current_rule; + } - return current_rule; + return current_rule; } -static char * -dunst_ini_get_string(const char *value) { +static char *dunst_ini_get_string(const char *value) +{ - char *s; + char *s; - if (value[0] == '"') - s = strdup(value + 1); - else - s = strdup(value); + if (value[0] == '"') + s = strdup(value + 1); + else + s = strdup(value); - if (s[strlen(s) - 1] == '"') - s[strlen(s) - 1] = '\0'; + if (s[strlen(s) - 1] == '"') + s[strlen(s) - 1] = '\0'; - return s; + return s; } static int dunst_ini_handle(void *user_data, const char *section, - const char *name, const char *value) { + const char *name, const char *value) +{ - if (strcmp(section, "global") == 0) { - if (strcmp(name, "font") == 0) - font = dunst_ini_get_string(value); - else if (strcmp(name, "format") == 0) - format = dunst_ini_get_string(value); - else if (strcmp(name, "sort") == 0) - sort = dunst_ini_get_boolean(value); - else if (strcmp(name, "indicate_hidden") == 0) - indicate_hidden = dunst_ini_get_boolean(value); - else if (strcmp(name, "key") == 0) - key_string = dunst_ini_get_string(value); - else if (strcmp(name, "history_key") == 0) - history_key_string = dunst_ini_get_string(value); - else if (strcmp(name, "idle_threshold") == 0) - idle_threshold = atoi(value); - else if (strcmp(name, "geometry") == 0) { - geom = dunst_ini_get_string(value); - geometry.mask = XParseGeometry(geom, - &geometry.x, &geometry.y, - &geometry.w, &geometry.h); - } else if (strcmp(name, "modifier") == 0) { - char *mod = dunst_ini_get_string(value); + if (strcmp(section, "global") == 0) { + if (strcmp(name, "font") == 0) + font = dunst_ini_get_string(value); + else if (strcmp(name, "format") == 0) + format = dunst_ini_get_string(value); + else if (strcmp(name, "sort") == 0) + sort = dunst_ini_get_boolean(value); + else if (strcmp(name, "indicate_hidden") == 0) + indicate_hidden = dunst_ini_get_boolean(value); + else if (strcmp(name, "key") == 0) + key_string = dunst_ini_get_string(value); + else if (strcmp(name, "history_key") == 0) + history_key_string = dunst_ini_get_string(value); + else if (strcmp(name, "idle_threshold") == 0) + idle_threshold = atoi(value); + else if (strcmp(name, "geometry") == 0) { + geom = dunst_ini_get_string(value); + geometry.mask = XParseGeometry(geom, + &geometry.x, &geometry.y, + &geometry.w, + &geometry.h); + } else if (strcmp(name, "modifier") == 0) { + char *mod = dunst_ini_get_string(value); - if (mod == NULL) { - mask = 0; - } else if (!strcmp(mod, "ctrl")) { - mask = ControlMask; - } else if (!strcmp(mod, "mod4")) { - mask = Mod4Mask; - } else if (!strcmp(mod, "mod3")) { - mask = Mod3Mask; - } else if (!strcmp(mod, "mod2")) { - mask = Mod2Mask; - } else if (!strcmp(mod, "mod1")) { - mask = Mod1Mask; + if (mod == NULL) { + mask = 0; + } else if (!strcmp(mod, "ctrl")) { + mask = ControlMask; + } else if (!strcmp(mod, "mod4")) { + mask = Mod4Mask; + } else if (!strcmp(mod, "mod3")) { + mask = Mod3Mask; + } else if (!strcmp(mod, "mod2")) { + mask = Mod2Mask; + } else if (!strcmp(mod, "mod1")) { + mask = Mod1Mask; + } else { + mask = 0; + } + free(mod); + } + } else if (strcmp(section, "urgency_low") == 0) { + if (strcmp(name, "background") == 0) + lowbgcolor = dunst_ini_get_string(value); + else if (strcmp(name, "foreground") == 0) + lowfgcolor = dunst_ini_get_string(value); + else if (strcmp(name, "timeout") == 0) + timeouts[LOW] = atoi(value); + } else if (strcmp(section, "urgency_normal") == 0) { + if (strcmp(name, "background") == 0) + normbgcolor = dunst_ini_get_string(value); + else if (strcmp(name, "foreground") == 0) + normfgcolor = dunst_ini_get_string(value); + else if (strcmp(name, "timeout") == 0) + timeouts[NORM] = atoi(value); + } else if (strcmp(section, "urgency_critical") == 0) { + if (strcmp(name, "background") == 0) + critbgcolor = dunst_ini_get_string(value); + else if (strcmp(name, "foreground") == 0) + critfgcolor = dunst_ini_get_string(value); + else if (strcmp(name, "timeout") == 0) + timeouts[CRIT] = atoi(value); } else { - mask = 0; + + rule_t *current_rule = dunst_rules_find_or_create(section); + + if (strcmp(name, "appname") == 0) + current_rule->appname = dunst_ini_get_string(value); + else if (strcmp(name, "summary") == 0) + current_rule->summary = dunst_ini_get_string(value); + else if (strcmp(name, "body") == 0) + current_rule->body = dunst_ini_get_string(value); + else if (strcmp(name, "icon") == 0) + current_rule->icon = dunst_ini_get_string(value); + else if (strcmp(name, "timeout") == 0) + current_rule->timeout = atoi(value); + else if (strcmp(name, "urgency") == 0) { + const char *urg = value; + + if (strcmp(urg, "low") == 0) + current_rule->urgency = LOW; + else if (strcmp(urg, "normal") == 0) + current_rule->urgency = NORM; + else if (strcmp(urg, "critical") == 0) + current_rule->urgency = CRIT; + else + fprintf(stderr, + "unknown urgency: %s, ignoring\n", urg); + } else if (strcmp(name, "foreground") == 0) + current_rule->fg = dunst_ini_get_string(value); + else if (strcmp(name, "background") == 0) + current_rule->bg = dunst_ini_get_string(value); + else if (strcmp(name, "format") == 0) + current_rule->format = dunst_ini_get_string(value); } - free (mod); - } - } else if (strcmp(section, "urgency_low") == 0) { - if (strcmp(name, "background") == 0) - lowbgcolor = dunst_ini_get_string(value); - else if (strcmp(name, "foreground") == 0) - lowfgcolor = dunst_ini_get_string(value); - else if (strcmp(name, "timeout") == 0) - timeouts[LOW] = atoi(value); - } else if (strcmp(section, "urgency_normal") == 0) { - if (strcmp(name, "background") == 0) - normbgcolor = dunst_ini_get_string(value); - else if (strcmp(name, "foreground") == 0) - normfgcolor = dunst_ini_get_string(value); - else if (strcmp(name, "timeout") == 0) - timeouts[NORM] = atoi(value); - } else if (strcmp(section, "urgency_critical") == 0) { - if (strcmp(name, "background") == 0) - critbgcolor = dunst_ini_get_string(value); - else if (strcmp(name, "foreground") == 0) - critfgcolor = dunst_ini_get_string(value); - else if (strcmp(name, "timeout") == 0) - timeouts[CRIT] = atoi(value); - } else { - rule_t *current_rule = dunst_rules_find_or_create(section); - - if (strcmp(name, "appname") == 0) - current_rule->appname = dunst_ini_get_string(value); - else if (strcmp(name, "summary") == 0) - current_rule->summary = dunst_ini_get_string(value); - else if (strcmp(name, "body") == 0) - current_rule->body = dunst_ini_get_string(value); - else if (strcmp(name, "icon") == 0) - current_rule->icon = dunst_ini_get_string(value); - else if (strcmp(name, "timeout") == 0) - current_rule->timeout = atoi(value); - else if (strcmp(name, "urgency") == 0) { - const char *urg = value; - - if (strcmp(urg, "low") == 0) - current_rule->urgency = LOW; - else if (strcmp(urg, "normal") == 0) - current_rule->urgency = NORM; - else if (strcmp(urg, "critical") == 0) - current_rule->urgency = CRIT; - else - fprintf(stderr, "unknown urgency: %s, ignoring\n", urg); - } else if (strcmp(name, "foreground") == 0) - current_rule->fg = dunst_ini_get_string(value); - else if (strcmp(name, "background") == 0) - current_rule->bg = dunst_ini_get_string(value); - else if (strcmp(name, "format") == 0) - current_rule->format = dunst_ini_get_string(value); - } - - return 1; + return 1; } -void -parse_dunstrc(void) { +void parse_dunstrc(void) +{ - xdgHandle xdg; - FILE *config_file; + xdgHandle xdg; + FILE *config_file; - dunst_printf(DEBUG, "Begin parsing of dunstrc\n"); + xdgInitHandle(&xdg); - xdgInitHandle(&xdg); - - config_file = xdgConfigOpen("dunst/dunstrc", "r", &xdg); - if (config_file == NULL) { - /* Fall back to just "dunstrc", which was used before 2012-06-23 - * (before v0.2). */ - config_file = xdgConfigOpen("dunstrc", "r", &xdg); + config_file = xdgConfigOpen("dunst/dunstrc", "r", &xdg); if (config_file == NULL) { - puts("no dunstrc found -> skipping\n"); - xdgWipeHandle(&xdg); - return; + /* Fall back to just "dunstrc", which was used before 2012-06-23 + * (before v0.2). */ + config_file = xdgConfigOpen("dunstrc", "r", &xdg); + if (config_file == NULL) { + puts("no dunstrc found -> skipping\n"); + xdgWipeHandle(&xdg); + return; + } } - } - if (ini_parse_file(config_file, dunst_ini_handle, NULL) < 0) { - puts("dunstrc could not be parsed -> skipping\n"); - } - - fclose(config_file); - xdgWipeHandle(&xdg); - - print_rules(); -} - - -void -parse_cmdline_for_config_file(int argc, char *argv[]) { - int i; - for (i = 0; i < argc; i++) { - if(strstr(argv[i], "-config") != 0) { - if (i + 1 == argc) { - printf("Invalid commandline: -config needs argument\n"); - } - config_file = argv[++i]; - return; + if (ini_parse_file(config_file, dunst_ini_handle, NULL) < 0) { + puts("dunstrc could not be parsed -> skipping\n"); } - } + + fclose(config_file); + xdgWipeHandle(&xdg); + + print_rules(); } -int -main(int argc, char *argv[]) { - - now = time(&now); - dc = initdc(); - geometry.mask = XParseGeometry(geom, - &geometry.x, &geometry.y, - &geometry.w, &geometry.h); - - parse_cmdline_for_config_file(argc, argv); - parse_dunstrc(); - parse_cmdline(argc, argv); - key = key_string ? XStringToKeysym(key_string) : NoSymbol; - history_key = history_key_string ? XStringToKeysym(history_key_string) : NoSymbol; - screensaver_info = XScreenSaverAllocInfo(); - - initdbus(); - initfont(dc, font); - colors[LOW] = initcolor(dc, lowfgcolor, lowbgcolor); - colors[NORM] = initcolor(dc, normfgcolor, normbgcolor); - colors[CRIT] = initcolor(dc, critfgcolor, critbgcolor); - - color_strings[ColFG][LOW] = lowfgcolor; - color_strings[ColFG][NORM] = normfgcolor; - color_strings[ColFG][CRIT] = critfgcolor; - - color_strings[ColBG][LOW] = lowbgcolor; - color_strings[ColBG][NORM] = normbgcolor; - color_strings[ColBG][CRIT] = critbgcolor; - setup(); - if(msgqueue != NULL) { - show_win(); - } - run(); - return 0; +void parse_cmdline_for_config_file(int argc, char *argv[]) +{ + int i; + for (i = 0; i < argc; i++) { + if (strstr(argv[i], "-config") != 0) { + if (i + 1 == argc) { + printf + ("Invalid commandline: -config needs argument\n"); + } + config_file = argv[++i]; + return; + } + } } -void -usage(int exit_status) { - fputs("usage: dunst [-h/--help] [-v] [-geometry geom] [-fn font] [-format fmt]\n[-nb color] [-nf color] [-lb color] [-lf color] [-cb color] [ -cf color]\n[-to secs] [-lto secs] [-cto secs] [-nto secs] [-key key] [-history_key key] [-mod modifier] [-mon n] [-config dunstrc]\n", stderr); - exit(exit_status); +int main(int argc, char *argv[]) +{ + + now = time(&now); + dc = initdc(); + geometry.mask = XParseGeometry(geom, + &geometry.x, &geometry.y, + &geometry.w, &geometry.h); + + parse_cmdline_for_config_file(argc, argv); + parse_dunstrc(); + parse_cmdline(argc, argv); + key = key_string ? XStringToKeysym(key_string) : NoSymbol; + history_key = + history_key_string ? XStringToKeysym(history_key_string) : NoSymbol; + screensaver_info = XScreenSaverAllocInfo(); + + initdbus(); + initfont(dc, font); + colors[LOW] = initcolor(dc, lowfgcolor, lowbgcolor); + colors[NORM] = initcolor(dc, normfgcolor, normbgcolor); + colors[CRIT] = initcolor(dc, critfgcolor, critbgcolor); + + color_strings[ColFG][LOW] = lowfgcolor; + color_strings[ColFG][NORM] = normfgcolor; + color_strings[ColFG][CRIT] = critfgcolor; + + color_strings[ColBG][LOW] = lowbgcolor; + color_strings[ColBG][NORM] = normbgcolor; + color_strings[ColBG][CRIT] = critbgcolor; + setup(); + + height_limit = indicate_hidden ? geometry.h - 1 : geometry.w; + run(); + return 0; } + +void usage(int exit_status) +{ + fputs + ("usage: dunst [-h/--help] [-v] [-geometry geom] [-fn font] [-format fmt]\n[-nb color] [-nf color] [-lb color] [-lf color] [-cb color] [ -cf color]\n[-to secs] [-lto secs] [-cto secs] [-nto secs] [-key key] [-history_key key] [-mod modifier] [-mon n] [-config dunstrc]\n", + stderr); + exit(exit_status); +} + +/* vim: set ts=8 sw=8 tw=0: */ diff --git a/dunst.h b/dunst.h index b14a323..795c818 100644 --- a/dunst.h +++ b/dunst.h @@ -14,43 +14,48 @@ #define ColBG 0 typedef struct _rule_t { - char *name; - /* filters */ - char *appname; - char *summary; - char *body; - char *icon; + char *name; + /* filters */ + char *appname; + char *summary; + char *body; + char *icon; - /* actions */ - int timeout; - int urgency; - char *fg; - char *bg; - const char *format; + /* actions */ + int timeout; + int urgency; + char *fg; + char *bg; + const char *format; - struct _rule_t *next; + struct _rule_t *next; } rule_t; -typedef struct _msg_queue_t { - char *appname; - char *summary; - char *body; - char *icon; - char *msg; - const char *format; - struct _msg_queue_t *next; - time_t start; - int timeout; - int urgency; - ColorSet *colors; - char *color_strings[2]; -} msg_queue_t; +typedef struct _notification { + char *appname; + char *summary; + char *body; + char *icon; + char *msg; + const char *format; + time_t start; + time_t timestamp; + int timeout; + int urgency; + int redisplayed; /* has been displayed before? */ + ColorSet *colors; + char *color_strings[2]; +} notification; typedef struct _dimension_t { - int x; - int y; - unsigned int h; - unsigned int w; - int mask; + int x; + int y; + unsigned int h; + unsigned int w; + int mask; } dimension_t; #endif +/* vim: set ts=8 sw=8 tw=0: */ + +void init_notification(notification * n); +void map_win(void); diff --git a/dunst_dbus.c b/dunst_dbus.c index 10e5cbd..662406e 100644 --- a/dunst_dbus.c +++ b/dunst_dbus.c @@ -3,59 +3,57 @@ #include #include "dunst.h" +#include "list.h" -#define DBUS_POLL_TIMEOUT 200 +#include "dunst_dbus.h" DBusError dbus_err; DBusConnection *dbus_conn; dbus_uint32_t dbus_serial = 0; - static const char *introspect = "" + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " " " ""; -"" -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -" " -""; - -void dbus_introspect(DBusMessage * dmsg) { +void dbus_introspect(DBusMessage * dmsg) +{ DBusMessage *reply; DBusMessageIter args; @@ -69,287 +67,273 @@ void dbus_introspect(DBusMessage * dmsg) { } -void -initdbus(void) { - int ret; - dbus_error_init(&dbus_err); - dbus_conn = dbus_bus_get(DBUS_BUS_SESSION, &dbus_err); - if(dbus_error_is_set(&dbus_err)) { - fprintf(stderr, "Connection Error (%s)\n", dbus_err.message); - dbus_error_free(&dbus_err); - } - if(dbus_conn == NULL) { - fprintf(stderr, "dbus_con == NULL\n"); - exit(EXIT_FAILURE); - } +void initdbus(void) +{ + int ret; + dbus_error_init(&dbus_err); + dbus_conn = dbus_bus_get(DBUS_BUS_SESSION, &dbus_err); + if (dbus_error_is_set(&dbus_err)) { + fprintf(stderr, "Connection Error (%s)\n", dbus_err.message); + dbus_error_free(&dbus_err); + } + if (dbus_conn == NULL) { + fprintf(stderr, "dbus_con == NULL\n"); + exit(EXIT_FAILURE); + } - ret = dbus_bus_request_name(dbus_conn, "org.freedesktop.Notifications", - DBUS_NAME_FLAG_REPLACE_EXISTING, &dbus_err); - if(dbus_error_is_set(&dbus_err)) { - fprintf(stderr, "Name Error (%s)\n", dbus_err.message); - } - if(DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret) { - fprintf(stderr, "There's already another notification-daemon running\n"); - exit(EXIT_FAILURE); - } + ret = dbus_bus_request_name(dbus_conn, "org.freedesktop.Notifications", + DBUS_NAME_FLAG_REPLACE_EXISTING, &dbus_err); + if (dbus_error_is_set(&dbus_err)) { + fprintf(stderr, "Name Error (%s)\n", dbus_err.message); + } + if (DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret) { + fprintf(stderr, + "There's already another notification-daemon running\n"); + exit(EXIT_FAILURE); + } - dbus_bus_add_match(dbus_conn, - "type='signal',interface='org.freedesktop.Notifications'", - &dbus_err); - if(dbus_error_is_set(&dbus_err)) { - fprintf(stderr, "Match error (%s)\n", dbus_err.message); - exit(EXIT_FAILURE); - } + dbus_bus_add_match(dbus_conn, + "type='signal',interface='org.freedesktop.Notifications'", + &dbus_err); + if (dbus_error_is_set(&dbus_err)) { + fprintf(stderr, "Match error (%s)\n", dbus_err.message); + exit(EXIT_FAILURE); + } } -void -dbus_poll(void) { - DBusMessage *dbus_msg; +void dbus_poll(int timeout) +{ + DBusMessage *dbus_msg; - /* make timeout smaller if we are displaying a message - * to improve responsivness for mouse clicks - */ - if(msgqueue == NULL) { - dbus_connection_read_write(dbus_conn, DBUS_POLL_TIMEOUT); - } else { - dbus_connection_read_write(dbus_conn, 100); - } + dbus_connection_read_write(dbus_conn, timeout); - dbus_msg = dbus_connection_pop_message(dbus_conn); - /* we don't have a new message */ - if(dbus_msg == NULL) { - return; - } + dbus_msg = dbus_connection_pop_message(dbus_conn); + /* we don't have a new message */ + if (dbus_msg == NULL) { + return; + } - if (dbus_message_is_method_call(dbus_msg,"org.freedesktop.DBus.Introspectable", "Introspect")) { - dbus_introspect(dbus_msg); - } + if (dbus_message_is_method_call + (dbus_msg, "org.freedesktop.DBus.Introspectable", "Introspect")) { + dbus_introspect(dbus_msg); + } - if(dbus_message_is_method_call(dbus_msg, - "org.freedesktop.Notifications","Notify")) { - notify(dbus_msg); - } - if(dbus_message_is_method_call(dbus_msg, - "org.freedesktop.Notifications", "GetCapabilities")) { - getCapabilities(dbus_msg); - } - if(dbus_message_is_method_call(dbus_msg, - "org.freedesktop.Notifications", "GetServerInformation")) { - getServerInformation(dbus_msg); - } - if(dbus_message_is_method_call(dbus_msg, - "org.freedesktop.Notifications", "CloseNotification")) { - closeNotification(dbus_msg); - } - dbus_message_unref(dbus_msg); + if (dbus_message_is_method_call(dbus_msg, + "org.freedesktop.Notifications", + "Notify")) { + notify(dbus_msg); + } + if (dbus_message_is_method_call(dbus_msg, + "org.freedesktop.Notifications", + "GetCapabilities")) { + getCapabilities(dbus_msg); + } + if (dbus_message_is_method_call(dbus_msg, + "org.freedesktop.Notifications", + "GetServerInformation")) { + getServerInformation(dbus_msg); + } + if (dbus_message_is_method_call(dbus_msg, + "org.freedesktop.Notifications", + "CloseNotification")) { + closeNotification(dbus_msg); + } + dbus_message_unref(dbus_msg); } -void -getCapabilities(DBusMessage *dmsg) { - DBusMessage* reply; - DBusMessageIter args; - DBusMessageIter subargs; +void getCapabilities(DBusMessage * dmsg) +{ + DBusMessage *reply; + DBusMessageIter args; + DBusMessageIter subargs; - const char *caps[1] = {"body"}; - dbus_serial++; + const char *caps[1] = { "body" }; + dbus_serial++; - reply = dbus_message_new_method_return(dmsg); - if(!reply) { - return; - } + reply = dbus_message_new_method_return(dmsg); + if (!reply) { + return; + } - dbus_message_iter_init_append(reply, &args); + dbus_message_iter_init_append(reply, &args); - if(!dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &subargs ) - || !dbus_message_iter_append_basic(&subargs, DBUS_TYPE_STRING, caps) - || !dbus_message_iter_close_container(&args, &subargs) - || !dbus_connection_send(dbus_conn, reply, &dbus_serial)) { - fprintf(stderr, "Unable to reply"); - return; - } + if (!dbus_message_iter_open_container + (&args, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &subargs) + || !dbus_message_iter_append_basic(&subargs, DBUS_TYPE_STRING, caps) + || !dbus_message_iter_close_container(&args, &subargs) + || !dbus_connection_send(dbus_conn, reply, &dbus_serial)) { + fprintf(stderr, "Unable to reply"); + return; + } - dbus_connection_flush(dbus_conn); - dbus_message_unref(reply); + dbus_connection_flush(dbus_conn); + dbus_message_unref(reply); } -void -closeNotification(DBusMessage *dmsg) { - DBusMessage *reply; - reply = dbus_message_new_method_return(dmsg); - if(!reply) { - return; - } - dbus_connection_send(dbus_conn, reply, &dbus_serial); - dbus_connection_flush(dbus_conn); +void closeNotification(DBusMessage * dmsg) +{ + DBusMessage *reply; + reply = dbus_message_new_method_return(dmsg); + if (!reply) { + return; + } + dbus_connection_send(dbus_conn, reply, &dbus_serial); + dbus_connection_flush(dbus_conn); } -void -getServerInformation(DBusMessage *dmsg) { - DBusMessage *reply; - DBusMessageIter args; - char *param = ""; - const char *info[4] = {"dunst", "dunst", "2011", "2011"}; +void getServerInformation(DBusMessage * dmsg) +{ + DBusMessage *reply; + DBusMessageIter args; + char *param = ""; + const char *info[4] = { "dunst", "dunst", "2011", "2011" }; + if (!dbus_message_iter_init(dmsg, &args)) { + } else if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type(&args)) { + } else { + dbus_message_iter_get_basic(&args, ¶m); + } - if (!dbus_message_iter_init(dmsg, &args)) { - } - else if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type(&args)) { - } - else { - dbus_message_iter_get_basic(&args, ¶m); - } + reply = dbus_message_new_method_return(dmsg); + dbus_message_iter_init_append(reply, &args); + if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &info[0]) + || !dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, + &info[1]) + || !dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, + &info[2]) + || !dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, + &info[3])) { + fprintf(stderr, "Unable to fill arguments"); + return; + } + dbus_serial++; + if (!dbus_connection_send(dbus_conn, reply, &dbus_serial)) { + fprintf(stderr, "Out Of Memory!\n"); + exit(EXIT_FAILURE); + } + dbus_connection_flush(dbus_conn); - reply = dbus_message_new_method_return(dmsg); - - dbus_message_iter_init_append(reply, &args); - if(!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &info[0]) - || !dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &info[1]) - || !dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &info[2]) - || !dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &info[3])) { - fprintf(stderr, "Unable to fill arguments"); - return; - } - - dbus_serial++; - if (!dbus_connection_send(dbus_conn, reply, &dbus_serial)) { - fprintf(stderr, "Out Of Memory!\n"); - exit(EXIT_FAILURE); - } - dbus_connection_flush(dbus_conn); - - dbus_message_unref(reply); + dbus_message_unref(reply); } - -static void -_extract_basic(int type, DBusMessageIter *iter, void *target) { - int iter_type = dbus_message_iter_get_arg_type(iter); - if (iter_type != type) { - dunst_printf(DEBUG, "Invalid dbus notification: expected type %d but got %d.\n", - type, iter_type); - } else { - dbus_message_iter_get_basic(iter, target); - } +static void _extract_basic(int type, DBusMessageIter * iter, void *target) +{ + int iter_type = dbus_message_iter_get_arg_type(iter); + if (iter_type == type) { + dbus_message_iter_get_basic(iter, target); + } } static void _extract_hint(const char *name, const char *hint_name, - DBusMessageIter *hint, void *target) { + DBusMessageIter * hint, void *target) +{ - DBusMessageIter hint_value; + DBusMessageIter hint_value; - if(!strcmp(hint_name, name)) { - dunst_printf(DEBUG, "%s found\n", name); - dbus_message_iter_next(hint); - dbus_message_iter_recurse(hint, &hint_value); - do { - dbus_message_iter_get_basic(&hint_value, target); - } while(dbus_message_iter_next(hint)); - } + if (!strcmp(hint_name, name)) { + dbus_message_iter_next(hint); + dbus_message_iter_recurse(hint, &hint_value); + do { + dbus_message_iter_get_basic(&hint_value, target); + } while (dbus_message_iter_next(hint)); + } } -void -notify(DBusMessage *dmsg) { - DBusMessage *reply; - DBusMessageIter args; - DBusMessageIter hints; - DBusMessageIter hint; - char *hint_name; +void notify(DBusMessage * dmsg) +{ + DBusMessage *reply; + DBusMessageIter args; + DBusMessageIter hints; + DBusMessageIter hint; + char *hint_name; - int i; - int id = 23; - const char *appname = NULL; - const char *summary = NULL; - const char *body = NULL; - const char *icon = NULL; - const char *fgcolor = NULL; - const char *bgcolor = NULL; - int urgency = 1; - msg_queue_t *msg = malloc(sizeof(msg_queue_t)); - dbus_uint32_t nid=0; - dbus_int32_t expires=-1; + int i; + int id = 23; + const char *appname = NULL; + const char *summary = NULL; + const char *body = NULL; + const char *icon = NULL; + const char *fgcolor = NULL; + const char *bgcolor = NULL; + int urgency = 1; + notification *n = malloc(sizeof(notification)); + dbus_uint32_t nid = 0; + dbus_int32_t expires = -1; - dbus_serial++; - dunst_printf(DEBUG, "new dbus message\n"); - dbus_message_iter_init(dmsg, &args); + dbus_serial++; + dbus_message_iter_init(dmsg, &args); - dunst_printf(DEBUG, "extracting appname\n"); - _extract_basic(DBUS_TYPE_STRING, &args, &appname); + _extract_basic(DBUS_TYPE_STRING, &args, &appname); - dbus_message_iter_next( &args ); - dunst_printf(DEBUG, "extracting nid\n"); - _extract_basic(DBUS_TYPE_UINT32, &args, &nid); + dbus_message_iter_next(&args); + _extract_basic(DBUS_TYPE_UINT32, &args, &nid); - dbus_message_iter_next( &args ); - dunst_printf(DEBUG, "extracting icon\n"); - _extract_basic(DBUS_TYPE_STRING, &args, &icon); + dbus_message_iter_next(&args); + _extract_basic(DBUS_TYPE_STRING, &args, &icon); - dbus_message_iter_next( &args ); - dunst_printf(DEBUG, "extracting summary\n"); - _extract_basic(DBUS_TYPE_STRING, &args, &summary); + dbus_message_iter_next(&args); + _extract_basic(DBUS_TYPE_STRING, &args, &summary); - dbus_message_iter_next( &args ); - dunst_printf(DEBUG, "extracting body\n"); - _extract_basic(DBUS_TYPE_STRING, &args, &body); + dbus_message_iter_next(&args); + _extract_basic(DBUS_TYPE_STRING, &args, &body); - dbus_message_iter_next( &args ); - dbus_message_iter_next( &args ); + dbus_message_iter_next(&args); + dbus_message_iter_next(&args); - dunst_printf(DEBUG, "extracting hints\n"); - dbus_message_iter_recurse(&args, &hints); - dbus_message_iter_next( &args ); + dbus_message_iter_recurse(&args, &hints); + dbus_message_iter_next(&args); - dunst_printf(DEBUG, "extracting expires\n"); - _extract_basic(DBUS_TYPE_INT32, &args, &expires); + _extract_basic(DBUS_TYPE_INT32, &args, &expires); - - dunst_printf(DEBUG, "extracting hints\n"); - while (dbus_message_iter_get_arg_type(&hints) != DBUS_TYPE_INVALID) { - dbus_message_iter_recurse(&hints, &hint); - while (dbus_message_iter_get_arg_type(&hint) != DBUS_TYPE_INVALID) { - if(dbus_message_iter_get_arg_type(&hint) != DBUS_TYPE_STRING) { - dbus_message_iter_next(&hint); - continue; - } - dbus_message_iter_get_basic(&hint, &hint_name); - _extract_hint("urgency", hint_name, &hint, &urgency); - _extract_hint("fgcolor", hint_name, &hint, &fgcolor); - _extract_hint("bgcolor", hint_name, &hint, &bgcolor); - dbus_message_iter_next(&hint); + while (dbus_message_iter_get_arg_type(&hints) != DBUS_TYPE_INVALID) { + dbus_message_iter_recurse(&hints, &hint); + while (dbus_message_iter_get_arg_type(&hint) != + DBUS_TYPE_INVALID) { + if (dbus_message_iter_get_arg_type(&hint) != + DBUS_TYPE_STRING) { + dbus_message_iter_next(&hint); + continue; + } + dbus_message_iter_get_basic(&hint, &hint_name); + _extract_hint("urgency", hint_name, &hint, &urgency); + _extract_hint("fgcolor", hint_name, &hint, &fgcolor); + _extract_hint("bgcolor", hint_name, &hint, &bgcolor); + dbus_message_iter_next(&hint); + } + dbus_message_iter_next(&hints); } - dbus_message_iter_next(&hints); - } - - if(expires > 0) { - /* do some rounding */ - expires = (expires+500)/1000; - if(expires < 1) { - expires = 1; + if (expires > 0) { + /* do some rounding */ + expires = (expires + 500) / 1000; + if (expires < 1) { + expires = 1; + } } - } - msg->appname = strdup(appname); - msg->summary = strdup(summary); - msg->body = strdup(body); - msg->icon = strdup(icon); - msg->timeout = expires; - msg->urgency = urgency; - for(i = 0; i < ColLast; i++) { - msg->color_strings[i] = NULL; - } - msg->color_strings[ColFG] = fgcolor == NULL ? NULL : strdup(fgcolor); - msg->color_strings[ColBG] = bgcolor == NULL ? NULL : strdup(bgcolor); - initmsg(msg); - msgqueue = add(msgqueue, msg); - drawmsg(); + n->appname = strdup(appname); + n->summary = strdup(summary); + n->body = strdup(body); + n->icon = strdup(icon); + n->timeout = expires; + n->urgency = urgency; + for (i = 0; i < ColLast; i++) { + n->color_strings[i] = NULL; + } + n->color_strings[ColFG] = fgcolor == NULL ? NULL : strdup(fgcolor); + n->color_strings[ColBG] = bgcolor == NULL ? NULL : strdup(bgcolor); + init_notification(n); + map_win(); - reply = dbus_message_new_method_return(dmsg); + reply = dbus_message_new_method_return(dmsg); - dbus_message_iter_init_append(reply, &args); - dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &id); - dbus_connection_send(dbus_conn, reply, &dbus_serial); + dbus_message_iter_init_append(reply, &args); + dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &id); + dbus_connection_send(dbus_conn, reply, &dbus_serial); - dbus_message_unref(reply); + dbus_message_unref(reply); } + +/* vim: set ts=8 sw=8 tw=0: */ diff --git a/dunst_dbus.h b/dunst_dbus.h index e2cbb47..f5b3fc8 100644 --- a/dunst_dbus.h +++ b/dunst_dbus.h @@ -1,12 +1,17 @@ /* copyright 2012 Sascha Kruse and contributors (see LICENSE for licensing information) */ +#ifndef _DUNST_DBUS_H +#define _DUNST_DBUS_H + #include void initdbus(void); -void dbus_poll(void); -void notify(DBusMessage *msg); -void getCapabilities(DBusMessage *dmsg); -void closeNotification(DBusMessage *dmsg); -void getServerInformation(DBusMessage *dmsg); +void dbus_poll(int timeout); +void notify(DBusMessage * msg); +void getCapabilities(DBusMessage * dmsg); +void closeNotification(DBusMessage * dmsg); +void getServerInformation(DBusMessage * dmsg); -#include "dunst_dbus.c" +#endif + +/* vim: set ts=8 sw=8 tw=0: */