shithub: notif

ref: 345b8b783165ada4d53c50130fe2046e8627ea40
dir: /notif.c/

View raw version
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <plumb.h>

#define Eplumb 128

typedef struct Notif Notif;
struct Notif {
	char *title;
	char *message;
	char *button;
	char *cmd;
	Plumbmsg *pmsg;
	Rectangle r;
	Notif *next;
};

Notif *notif = nil;

Image* i_sep;
Image* i_back;
ulong c_sep = DGreyblue;
ulong c_back = DWhite;
Font *f_title;

int
initimages(void)
{
	i_sep = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, c_sep);
	if (!i_sep) return -1;
	
	f_title = openfont(display, "/lib/font/bit/lucida/unicode.12.font");
	if (!f_title) f_title = font;
	
	i_back = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, c_back);
	if (!i_back) i_back = display->white;
	return 0;
}

int
drawnotif(Notif *n, Image *dst, Point p)
{
	Point ss;
	int lpad = 8;
	int i = 0;
	Rectangle r;
	r.min = p;
	
	if (n->title){
		string(dst, addpt(p, Pt(lpad-2, i)), display->black, ZP,
			f_title, n->title);
		i += f_title->height;
	}
	
	if (n->message){
		i += 3;
		string(dst, addpt(p, Pt(lpad, i)), i_sep, ZP, font, n->message);
		i += font->height;
	}
	
	if (n->button){
		i += 3;
		string(dst, addpt(p, Pt(lpad+3, i)), display->black, ZP,
			font, n->button);
		ss = stringsize(font, n->button);
		border(dst,
			rectaddpt(
				Rpt(Pt(0, 0), addpt(ss, Pt(5, 0))),
				addpt(p, Pt(lpad, i))),
			-1, display->black, p);
		i += font->height;
	}
	i += 5;
	r.max = Pt(dst->r.max.x, p.y+i);
	n->r = r;
	return i;
}

int
drawseparator(Image *dst, Point p)
{
	draw(dst, rectaddpt(Rect(0, 0, dst->r.max.x, 1), p),
		i_sep, nil, ZP);
	return 5;
}

void
redraw(Image* screen)
{
	Notif *n = notif;
	int i = 0;
	
	draw(screen, screen->r, i_back, nil, ZP);
	
	while (n) {
		i += drawnotif(n, screen, addpt(screen->r.min, Pt(0, i)));
		i += drawseparator(screen, addpt(screen->r.min, Pt(0, i)));
		n = n->next;
	}
	
	flushimage(display, 1);
}

void
eresized(int new)
{
	if (new && getwindow(display, Refnone) < 0)
		fprint(2, "can't reattach to window");
	redraw(screen);
}

void
addnotif(Notif *n)
{
	Notif *l;
	
	if (!n)
		return;
	
	if (!notif) {
		notif = n;
		notif->next = nil;
		return;
	}
	
	l = notif;
	while (l && l->next)
		l = l->next;
	
	n->next = nil;
	l->next = n;
}

Notif*
crnotif(Plumbmsg *pm)
{
	char *msg;
	char *title;
	char *cmd;
	char *btn;
	Notif *n;
	
	msg = plumblookup(pm->attr, "message");
	title = plumblookup(pm->attr, "title");
	cmd = plumblookup(pm->attr, "cmd");
	btn = plumblookup(pm->attr, "button");
	
	n = malloc(sizeof(Notif));
	if (!n)
		sysfatal("malloc: %r");
	
	n->message = msg;
	n->title = title;
	n->cmd = cmd;
	n->button = btn;
	n->pmsg = pm;
	
	return n;
}

Notif*
getnotif(Point p)
{
	Notif *n;
	n = notif;
	
	while (n) {
		if (ptinrect(p, n->r))
			return n;
		n = n->next;
	}
	return nil;
}

void
removenotify(Notif *n)
{
	Notif *p;
	
	if (!n)
		return;
	
	p = notif;
	
	if (p == n){
		notif = n->next;
		goto Out;
	}
	
	while (p && p->next != n)
		p = p->next;
	
	if (!p)
		return;
	
	p->next = n->next;
Out:
	plumbfree(n->pmsg);
	free(n);
	redraw(screen);
}

void
execnotify(Notif *n)
{
	int fd;
	char* wd = nil;
	Plumbmsg *pmsg;
	
	if (!n)
		return;
	
	fd = plumbopen("send", OWRITE);
	if (fd < 0)
		sysfatal("unable to open send channel: %r");
	
	if (n->cmd && strcmp(n->cmd, "") != 0){
		if (n->pmsg)
			wd = n->pmsg->wdir;
		plumbsendtext(fd, "notif", nil, wd, n->cmd);
		goto Out;
	}
	/* if no command given, assume data is a valid message */
	if (n->pmsg && n->pmsg->data){
		pmsg = plumbunpack(n->pmsg->data, n->pmsg->ndata);
		if (pmsg)
			plumbsend(fd, pmsg);
		goto Out;
	}
	
Out:
	close(fd);
}

void
main(int argc, char **argv)
{
	Event e;
	Mouse m;
	Plumbmsg *pm;
	Notif *n;
	
	USED(argc, argv);
	
	if (initdraw(0, 0, "notif") < 0)
		sysfatal("initdraw failed");
	
	if (initimages() < 0)
		sysfatal("initimages failed");
	
	redraw(screen);
	
	einit(Emouse);
	eplumb(Eplumb, "notify");
	
	for (;;){
		switch (event(&e)){
		case Emouse:
			m = e.mouse;
			if (m.buttons & 1){ // lmb, discard
				n = getnotif(m.xy);
				removenotify(n);
				break;
			}
			else if (m.buttons & 4){ // rmb, execute
				n = getnotif(m.xy);
				execnotify(n);
				removenotify(n);
				break;
			}
			break;
		case Eplumb:
			pm = e.v;
			if (pm->ndata == 0)
				break;
			n = crnotif(pm);
			addnotif(n);
			redraw(screen);
			break;
		}
	}
}