commit 7f9dd0e3e7c4db8802660611aa173afefb51c85f Author: Sascha Kruse Date: Wed Sep 7 15:33:09 2011 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a4cb2bf --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +dnotify +*.o +core +vgcore.* diff --git a/LICENSE.dmenu b/LICENSE.dmenu new file mode 100644 index 0000000..25eb571 --- /dev/null +++ b/LICENSE.dmenu @@ -0,0 +1,27 @@ +MIT/X Consortium License + +© 2010-2011 Connor Lane Smith +© 2006-2011 Anselm R Garbe +© 2009 Gottox +© 2009 Markus Schnalke +© 2009 Evan Gates +© 2006-2008 Sander van Dijk +© 2006-2007 Michał Janeczek + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..efb5d95 --- /dev/null +++ b/Makefile @@ -0,0 +1,48 @@ +# dunst - Notification-daemon +# See LICENSE file for copyright and license details. + +include config.mk + +SRC = draw.c dunst.c +OBJ = ${SRC:.c=.o} + +all: options dunst + +options: + @echo dunst build options: + @echo "CFLAGS = ${CFLAGS}" + @echo "LDFLAGS = ${LDFLAGS}" + @echo "CC = ${CC}" + +.c.o: + @echo CC -c $< + @${CC} -c $< ${CFLAGS} + +${OBJ}: config.mk + +dunst: dunst.o draw.o + @echo CC -o $@ + @${CC} ${CFLAGS} -o $@ dunst.o draw.o ${LDFLAGS} + +clean: + @echo cleaning + @rm -f ${OBJ} + @rm -f dunst + +install: all + @echo installing executables to ${DESTDIR}${PREFIX}/bin + @mkdir -p ${DESTDIR}${PREFIX}/bin + @cp -f dunst ${DESTDIR}${PREFIX}/bin + @chmod 755 ${DESTDIR}${PREFIX}/bin/dunst + @echo installing manual pages to ${DESTDIR}${MANPREFIX}/man1 + @mkdir -p ${DESTDIR}${MANPREFIX}/man1 + @cp -f dunst.1 ${DESTDIR}${MANPREFIX}/man1/ + @chmod 644 ${DESTDIR}${MANPREFIX}/man1/dunst.1 + +uninstall: + @echo removing executables from ${DESTDIR}${PREFIX}/bin + @rm -f ${DESTDIR}${PREFIX}/bin/dunst + @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 + @rm -f ${DESTDIR}${MANPREFIX}/man1/dunst + +.PHONY: all options clean dist install uninstall diff --git a/TODO b/TODO new file mode 100644 index 0000000..eb09da1 --- /dev/null +++ b/TODO @@ -0,0 +1 @@ +-terminal only mode aka. don't show window diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..6f31c20 --- /dev/null +++ b/config.mk @@ -0,0 +1,22 @@ +# paths +PREFIX = /usr/local +MANPREFIX = ${PREFIX}/share/man + +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + +# Xinerama, comment if you don't want it +XINERAMALIBS = -lXinerama +XINERAMAFLAGS = -DXINERAMA + +# includes and libs +INCS = -I${X11INC} -I/usr/lib/dbus-1.0/include -I/usr/include/dbus-1.0 +LIBS = -L${X11LIB} -lX11 -ldbus-1 -lpthread -lrt ${XINERAMALIBS} + +# flags +CPPFLAGS = -D_BSD_SOURCE -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} +CFLAGS = -g -ansi -pedantic -Wall -Os ${INCS} ${CPPFLAGS} +LDFLAGS = -s ${LIBS} + +# compiler and linker +CC = cc diff --git a/draw.c b/draw.c new file mode 100644 index 0000000..d0beee8 --- /dev/null +++ b/draw.c @@ -0,0 +1,176 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include +#include +#include "draw.h" + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define DEFAULTFN "fixed" + +static Bool loadfont(DC *dc, const char *fontstr); + +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); +} + +void +drawtext(DC *dc, const char *text, unsigned long col[ColLast]) { + 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++] = '.'); + + drawrect(dc, 0, 0, dc->w, dc->h, True, BG(dc, col)); + drawtextn(dc, buf, mn, col); +} + +void +drawtextn(DC *dc, const char *text, size_t n, unsigned long col[ColLast]) { + int x = dc->x + dc->font.height/2; + int y = dc->y + dc->font.ascent+1; + + XSetForeground(dc->dpy, dc->gc, FG(dc, col)); + if(dc->font.set) + 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; + + 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); +} + +void +freedc(DC *dc) { + 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); + XFreeGC(dc->dpy, dc->gc); + XCloseDisplay(dc->dpy); + free(dc); +} + +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; +} + +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"); + + 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) { + if(!loadfont(dc, fontstr ? fontstr : DEFAULTFN)) { + if(fontstr != NULL) + fprintf(stderr, "cannot load font '%s'\n", fontstr); + if(fontstr == NULL || !loadfont(dc, DEFAULTFN)) + eprintf("cannot load font '%s'\n", DEFAULTFN); + } + dc->font.height = dc->font.ascent + dc->font.descent; +} + +Bool +loadfont(DC *dc, const char *fontstr) { + char *def, **missing, **names; + int i, n = 1; + XFontStruct **xfonts; + + if(!*fontstr) + return False; + if((dc->font.set = XCreateFontSet(dc->dpy, fontstr, &missing, &n, &def))) + n = XFontsOfFontSet(dc->font.set, &xfonts, &names); + else if((dc->font.xfont = XLoadQueryFont(dc->dpy, fontstr))) + xfonts = &dc->font.xfont; + else + n = 0; + + 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); + } + if(missing) + XFreeStringList(missing); + return (dc->font.set || dc->font.xfont); +} + +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) { + 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, DefaultScreen(dc->dpy))); +} + +int +textnw(DC *dc, const char *text, size_t len) { + 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; +} diff --git a/draw.h b/draw.h new file mode 100644 index 0000000..43a57bf --- /dev/null +++ b/draw.h @@ -0,0 +1,35 @@ +/* See LICENSE file for copyright and license details. */ + +#define FG(dc, col) ((col)[(dc)->invert ? ColBG : ColFG]) +#define BG(dc, col) ((col)[(dc)->invert ? ColFG : ColBG]) + +enum { ColBG, ColFG, ColBorder, ColLast }; + +typedef struct { + int x, y, w, h; + Bool invert; + Display *dpy; + GC gc; + Pixmap canvas; + struct { + int ascent; + int descent; + int height; + int width; + XFontSet set; + XFontStruct *xfont; + } font; +} DC; /* draw context */ + +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, unsigned long col[ColLast]); +void drawtextn(DC *dc, const char *text, size_t n, unsigned long col[ColLast]); +void eprintf(const char *fmt, ...); +void freedc(DC *dc); +unsigned long getcolor(DC *dc, const char *colstr); +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); diff --git a/dunst.1 b/dunst.1 new file mode 100644 index 0000000..4e56052 --- /dev/null +++ b/dunst.1 @@ -0,0 +1,54 @@ +.TH DNOTIFY 1 +.SH NAME +dunst \- dmenu\-ish universal notification system +.SH SYNOPSIS +.B dunst +.RB [ \-b ] +.RB [ \-fn +.IR font ] +.RB [ \-nb +.IR color ] +.RB [ \-nf +.IR color ] +.RB [ \-to +.IR secs ] +.P +.SH DESCRIPTION +.B dunst +is a lightweight notification\-daemon for the libnotify. It can also be used as a standalone notification system. Dnotify displays messages received via dbus or as commandline argument in a fashion similar to dmenu and additionally prints them to stdout. Notifications can be closed via mouseclick. +.SH OPTIONS +.TP +.B \-h/\-\-help +display help message. +.TP +.B \-b +dunst appears at the bottom of the screen. +.TP +.BI \-fn/ " font" +defines the font or font set used. +.TP +.BI \-nb/\-bg " color" +defines the background color. #RGB, #RRGGBB and X color names are supported. +.TP +.BI \-nf/\-fg " color" +defines the background color. +.TP +.BI \-msg " msg" +display msg instead of listening to notifications. This option can be used multiple times. +.TP +.BI \-to " secs" +display each message for secs seconds. +.SH AUTHOR +written by Sascha Kruse +.SH COPYRIGHT +Parts of the code are taken from dmenu (especially draw.c and draw.h). +Read LICENCE.dmenu and look at http://tools.suckless.org/dmenu. +.TP +Some snippets in dunst_dbus.c are taken from twmn. See http://github.com/sboli/twmn. +.TP +If you feel that copyrights are violated, please send me an e-mail to knopwob@googlemail.com. +.SH SEE also +.IR dwm (1), +.IR dmenu (1), +.IR twmn (1), +.IR notify-send (1) diff --git a/dunst.c b/dunst.c new file mode 100644 index 0000000..6cdba0c --- /dev/null +++ b/dunst.c @@ -0,0 +1,303 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef XINERAMA +#include +#endif + +#include "draw.h" + +#define INRECT(x,y,rx,ry,rw,rh) ((x) >= (rx) && (x) < (rx)+(rw) && (y) >= (ry) && (y) < (ry)+(rh)) +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) + +/* structs */ +typedef struct _msg_queue_t { + char *msg; + struct _msg_queue_t *next; + time_t start; +} msg_queue_t; + + +/* global variables */ +static int bh, mw, mh; +static int lines = 0; +static const char *font = NULL; +static const char *normbgcolor = "#cccccc"; +static const char *normfgcolor = "#000000"; +static const char *selbgcolor = "#0066ff"; +static const char *selfgcolor = "#ffffff"; +static unsigned long normcol[ColLast]; +static unsigned long selcol[ColLast]; +static Atom utf8; +static Bool topbar = True; +static DC *dc; +static Window win; +static double global_timeout = 10; +static msg_queue_t *msgqueuehead = NULL; +static time_t now; +static int loop = True; +static int visible = False; + + +/* list functions */ +msg_queue_t *append(msg_queue_t *queue, char *msg); +msg_queue_t *pop(msg_queue_t *queue); + + +/* misc funtions */ +void drawmsg(const char *msg); +void handleXEvents(void); +void hide_win(void); +void run(void); +void setup(void); +void show_win(void); +void usage(int exit_status); + +#include "dunst_dbus.h" + +msg_queue_t* +append(msg_queue_t *queue, char *msg) { + msg_queue_t *new = malloc(sizeof(msg_queue_t)); + msg_queue_t *last; + new->msg = msg; + printf("%s\n", new->msg); + new->next = NULL; + if(queue == NULL) { + new->start = now; + return new; + } + for(last = queue; last->next; last = last->next); + last->next = new; + return queue; +} + +msg_queue_t* +pop(msg_queue_t *queue) { + msg_queue_t *new_head; + if(queue == NULL) { + return NULL; + } + if(queue->next == NULL) { + free(queue->msg); + free(queue); + return NULL; + } + new_head = queue->next; + new_head->start = now; + drawmsg(new_head->msg); + free(queue); + return new_head; +} + +void +drawmsg(const char *msg) { + dc->x = 0; + dc->y = 0; + dc->h = 0; + drawrect(dc, 0, 0, mw, mh, True, BG(dc, normcol)); + drawtext(dc, msg, normcol); + + mapdc(dc, win, mw, mh); +} + +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, mw, mh); + 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 && msgqueuehead != NULL) { + msgqueuehead = pop(msgqueuehead); + if(msgqueuehead == NULL) { + hide_win(); + } + } + break; + } + } +} + + +void +hide_win(void) { + if(!visible) { + /* window is already hidden */ + return; + } + XUngrabButton(dc->dpy, AnyButton, AnyModifier, win); + XUnmapWindow(dc->dpy, win); + XFlush(dc->dpy); + visible = False; +} + +void +run(void) { + + while(True) { + /* dbus_poll blocks for max 2 seconds, if no events are present */ + if(loop) { + dbus_poll(); + } + now = time(&now); + if(msgqueuehead != NULL) { + show_win(); + if(difftime(now, msgqueuehead->start) > global_timeout) { + msgqueuehead = pop(msgqueuehead); + if(msgqueuehead == NULL) { + hide_win(); + } + + } + handleXEvents(); + } else if (!loop) { + break; + } + } +} + +void +setup(void) { + int x, y, screen = DefaultScreen(dc->dpy); + Window root = RootWindow(dc->dpy, screen); + XSetWindowAttributes wa; +#ifdef XINERAMA + int n; + XineramaScreenInfo *info; +#endif + + normcol[ColBG] = getcolor(dc, normbgcolor); + normcol[ColFG] = getcolor(dc, normfgcolor); + selcol[ColBG] = getcolor(dc, selbgcolor); + selcol[ColFG] = getcolor(dc, selfgcolor); + + utf8 = XInternAtom(dc->dpy, "UTF8_STRING", False); + + /* menu geometry */ + bh = dc->font.height + 2; + lines = MAX(lines, 0); + mh = (lines + 1) * bh; +#ifdef XINERAMA + if((info = XineramaQueryScreens(dc->dpy, &n))) { + int i, di; + unsigned int du; + Window dw; + + XQueryPointer(dc->dpy, root, &dw, &dw, &x, &y, &di, &di, &du); + for(i = 0; i < n-1; i++) + if(INRECT(x, y, info[i].x_org, info[i].y_org, info[i].width, info[i].height)) + break; + x = info[i].x_org; + y = info[i].y_org + (topbar ? 0 : info[i].height - mh); + mw = info[i].width; + XFree(info); + } + else +#endif + { + x = 0; + y = topbar ? 0 : DisplayHeight(dc->dpy, screen) - mh; + mw = DisplayWidth(dc->dpy, screen); + } + + /* menu window */ + wa.override_redirect = True; + wa.background_pixmap = ParentRelative; + wa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask | ButtonPressMask; + win = XCreateWindow(dc->dpy, root, x, y, mw, mh, 0, + DefaultDepth(dc->dpy, screen), CopyFromParent, + DefaultVisual(dc->dpy, screen), + CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); + + XMapRaised(dc->dpy, win); + resizedc(dc, mw, mh); +} + +void +show_win(void) { + if(visible == True) { + /* window is already visible */ + return; + } + if(msgqueuehead == NULL) { + /* there's nothing to show */ + return; + } + XMapRaised(dc->dpy, win); + XGrabButton(dc->dpy, AnyButton, AnyModifier, win, False, + BUTTONMASK, GrabModeAsync, GrabModeSync, None, None); + XFlush(dc->dpy); + drawmsg(msgqueuehead->msg); + visible = True; +} + + +int +main(int argc, char *argv[]) { + + int i; + + now = time(&now); + + for(i = 1; i < argc; i++) { + if(!strcmp(argv[i], "-b")) + topbar = False; + else if(!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { + usage(EXIT_SUCCESS); + } + else if(!strcmp(argv[i], "-fn")) + font = argv[++i]; + else if(!strcmp(argv[i], "-nb") || !strcmp(argv[i], "-bg")) + normbgcolor = argv[++i]; + else if(!strcmp(argv[i], "-nf") || !strcmp(argv[i], "-fg")) + normfgcolor = argv[++i]; + else if(!strcmp(argv[i], "-to")) + global_timeout = atoi(argv[++i]); + else if(!strcmp(argv[i], "-msg")) { + if(i+1 == argc) { + usage(EXIT_FAILURE); + } + msgqueuehead = append(msgqueuehead, argv[++i]); + loop = False; + } + else + usage(EXIT_FAILURE); + } + + if(loop) { + initdbus(); + } + dc = initdc(); + initfont(dc, font); + setup(); + if(msgqueuehead != NULL) { + show_win(); + } + run(); + return 0; +} + +void +usage(int exit_status) { + fputs("usage: dunst [-h/--help] [-b] [-fn font]\n[-nb/-bg color] [-nf/-fg color] [-to secs] [-msg msg]\n", stderr); + exit(exit_status); +} diff --git a/dunst_dbus.c b/dunst_dbus.c new file mode 100644 index 0000000..116912d --- /dev/null +++ b/dunst_dbus.c @@ -0,0 +1,210 @@ +#include + +#define DBUS_POLL_TIMEOUT 1000 + +DBusError dbus_err; +DBusConnection *dbus_conn; +dbus_uint32_t dbus_serial = 0; + + +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); + } + + 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; + + /* make timeout smaller if we are displaying a message + * to improve responsivness for mouse clicks + */ + if(msgqueuehead == NULL) { + dbus_connection_read_write(dbus_conn, DBUS_POLL_TIMEOUT); + } else { + dbus_connection_read_write(dbus_conn, 100); + } + + 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.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; + + const char *caps[1] = {"body"}; + dbus_serial++; + + reply = dbus_message_new_method_return(dmsg); + if(!reply) { + return; + } + + 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; + } + + dbus_connection_flush(dbus_conn); + dbus_message_unref(reply); +} + +void +closeNotification(DBusMessage *dmsg) { + fprintf(stderr, "closeNotification to be implemented\n"); +} + +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); + } + + + + 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); +} + + +void +notify(DBusMessage *dmsg) { + DBusMessage *reply; + DBusMessageIter args; + + int id = 23; + const char *appname; + const char *summary; + const char *body; + const char *icon; + char *msg; + dbus_uint32_t nid=0; + dbus_int32_t expires=-1; + + dbus_serial++; + dbus_message_iter_init(dmsg, &args); + dbus_message_iter_get_basic(&args, &appname); + dbus_message_iter_next( &args ); + dbus_message_iter_get_basic(&args, &nid); + dbus_message_iter_next( &args ); + dbus_message_iter_get_basic(&args, &icon); + dbus_message_iter_next( &args ); + dbus_message_iter_get_basic(&args, &summary); + dbus_message_iter_next( &args ); + dbus_message_iter_get_basic(&args, &body); + dbus_message_iter_next( &args ); + dbus_message_iter_next( &args ); + dbus_message_iter_next( &args ); + dbus_message_iter_get_basic(&args, &expires); + + + if(strlen(body) > 0) { + msg = malloc( + strlen(appname) + +strlen(summary) + +strlen(body) + +strlen(": -- ") + +5); + sprintf(msg, "%s: %s -- %s", appname, summary, body); + } else { + msg = malloc( + strlen(appname) + +strlen(summary) + +strlen(": ") + +5); + sprintf(msg, "%s: %s", appname, summary); + } + + msgqueuehead = append(msgqueuehead, msg); + + 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_unref(reply); +} diff --git a/dunst_dbus.h b/dunst_dbus.h new file mode 100644 index 0000000..0e8aad5 --- /dev/null +++ b/dunst_dbus.h @@ -0,0 +1,10 @@ +#include + +void initdbus(void); +void dbus_poll(void); +void notify(DBusMessage *msg); +void getCapabilities(DBusMessage *dmsg); +void closeNotification(DBusMessage *dmsg); +void getServerInformation(DBusMessage *dmsg); + +#include "dunst_dbus.c"