Initial commit
This commit is contained in:
commit
7f9dd0e3e7
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
dnotify
|
||||
*.o
|
||||
core
|
||||
vgcore.*
|
27
LICENSE.dmenu
Normal file
27
LICENSE.dmenu
Normal file
@ -0,0 +1,27 @@
|
||||
MIT/X Consortium License
|
||||
|
||||
© 2010-2011 Connor Lane Smith <cls@lubutu.com>
|
||||
© 2006-2011 Anselm R Garbe <anselm@garbe.us>
|
||||
© 2009 Gottox <gottox@s01.de>
|
||||
© 2009 Markus Schnalke <meillo@marmaro.de>
|
||||
© 2009 Evan Gates <evan.gates@gmail.com>
|
||||
© 2006-2008 Sander van Dijk <a dot h dot vandijk at gmail dot com>
|
||||
© 2006-2007 Michał Janeczek <janeczek at gmail dot com>
|
||||
|
||||
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.
|
48
Makefile
Normal file
48
Makefile
Normal file
@ -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
|
22
config.mk
Normal file
22
config.mk
Normal file
@ -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
|
176
draw.c
Normal file
176
draw.c
Normal file
@ -0,0 +1,176 @@
|
||||
/* See LICENSE file for copyright and license details. */
|
||||
#include <locale.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <X11/Xlib.h>
|
||||
#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;
|
||||
}
|
35
draw.h
Normal file
35
draw.h
Normal file
@ -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);
|
54
dunst.1
Normal file
54
dunst.1
Normal file
@ -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 <knopwob@googlemail.com>
|
||||
.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)
|
303
dunst.c
Normal file
303
dunst.c
Normal file
@ -0,0 +1,303 @@
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xutil.h>
|
||||
#ifdef XINERAMA
|
||||
#include <X11/extensions/Xinerama.h>
|
||||
#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);
|
||||
}
|
210
dunst_dbus.c
Normal file
210
dunst_dbus.c
Normal file
@ -0,0 +1,210 @@
|
||||
#include <dbus/dbus.h>
|
||||
|
||||
#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);
|
||||
}
|
10
dunst_dbus.h
Normal file
10
dunst_dbus.h
Normal file
@ -0,0 +1,10 @@
|
||||
#include <dbus/dbus.h>
|
||||
|
||||
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"
|
Loading…
x
Reference in New Issue
Block a user