first commit
This commit is contained in:
333
noti.c
Normal file
333
noti.c
Normal file
@@ -0,0 +1,333 @@
|
||||
#include <xcb/xcb.h>
|
||||
#include "dat.h"
|
||||
#include "fn.h"
|
||||
|
||||
static xcb_connection_t *xconn;
|
||||
static xcb_screen_t *scr;
|
||||
static xcb_window_t wins[Maxnotify];
|
||||
static xcb_gcontext_t gc;
|
||||
static Noti notifs[Maxnotify];
|
||||
static vlong createdat[Maxnotify];
|
||||
static int expanded[Maxnotify];
|
||||
static u32int *img;
|
||||
static int imgcap;
|
||||
static int depth;
|
||||
static int head, count;
|
||||
|
||||
static int
|
||||
nth(int i)
|
||||
{
|
||||
return (head - count + i + Maxnotify) % Maxnotify;
|
||||
}
|
||||
|
||||
static int
|
||||
wraplines(Rune *r, int n, int maxw)
|
||||
{
|
||||
int i, x, adv, lines;
|
||||
|
||||
if(n <= 0)
|
||||
return 0;
|
||||
x = 0;
|
||||
lines = 1;
|
||||
for(i = 0; i < n; i++){
|
||||
adv = fontadvance(r[i]);
|
||||
if(x + adv > maxw && x > 0){
|
||||
x = 0;
|
||||
lines++;
|
||||
}
|
||||
x += adv;
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
static int
|
||||
notiheight(int slot)
|
||||
{
|
||||
int textw, sl, bl, h;
|
||||
Noti *n;
|
||||
|
||||
if(!expanded[nth(slot)])
|
||||
return Winh;
|
||||
n = ¬ifs[nth(slot)];
|
||||
textw = Winw - 2*Padding;
|
||||
sl = wraplines(n->summary.r, n->summary.n, textw);
|
||||
bl = wraplines(n->body.r, n->body.n, textw);
|
||||
if(sl < 1) sl = 1;
|
||||
if(bl < 1) bl = 1;
|
||||
h = 2*Padding + (sl + bl) * Lineh;
|
||||
if(h < Winh)
|
||||
h = Winh;
|
||||
return h;
|
||||
}
|
||||
|
||||
static void
|
||||
drawline(u32int *buf, int w, int h, int x, int y, Rune *r, int n, int maxw)
|
||||
{
|
||||
int i, adv, used, ellaw;
|
||||
|
||||
used = 0;
|
||||
for(i = 0; i < n; i++)
|
||||
used += fontadvance(r[i]);
|
||||
if(used <= maxw){
|
||||
for(i = 0; i < n; i++){
|
||||
putfont(buf, w, h, x, y, r[i]);
|
||||
x += fontadvance(r[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
ellaw = fontadvance(0x2026);
|
||||
used = 0;
|
||||
for(i = 0; i < n; i++){
|
||||
adv = fontadvance(r[i]);
|
||||
if(used + adv + ellaw > maxw)
|
||||
break;
|
||||
putfont(buf, w, h, x, y, r[i]);
|
||||
x += adv;
|
||||
used += adv;
|
||||
}
|
||||
putfont(buf, w, h, x, y, 0x2026);
|
||||
}
|
||||
|
||||
static void
|
||||
drawwrap(u32int *buf, int w, int h, int x0, int y0, Rune *r, int n, int maxw)
|
||||
{
|
||||
int i, x, y, adv;
|
||||
|
||||
x = x0;
|
||||
y = y0;
|
||||
for(i = 0; i < n; i++){
|
||||
adv = fontadvance(r[i]);
|
||||
if(x + adv > x0 + maxw && x > x0){
|
||||
x = x0;
|
||||
y += Lineh;
|
||||
}
|
||||
if(y >= h)
|
||||
break;
|
||||
putfont(buf, w, h, x, y, r[i]);
|
||||
x += adv;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
draw(Noti *n, int wh, int isexpanded)
|
||||
{
|
||||
int i, total, textw, y, sl;
|
||||
|
||||
total = Winw * wh;
|
||||
if(total > imgcap){
|
||||
free(img);
|
||||
img = emalloc(total * sizeof(u32int));
|
||||
imgcap = total;
|
||||
}
|
||||
for(i = 0; i < total; i++)
|
||||
img[i] = Colbg;
|
||||
textw = Winw - 2*Padding;
|
||||
y = Padding;
|
||||
if(isexpanded){
|
||||
sl = wraplines(n->summary.r, n->summary.n, textw);
|
||||
if(sl < 1) sl = 1;
|
||||
drawwrap(img, Winw, wh, Padding, y, n->summary.r, n->summary.n, textw);
|
||||
y += sl * Lineh;
|
||||
drawwrap(img, Winw, wh, Padding, y, n->body.r, n->body.n, textw);
|
||||
} else {
|
||||
drawline(img, Winw, wh, Padding, y, n->summary.r, n->summary.n, textw);
|
||||
y += Lineh;
|
||||
drawline(img, Winw, wh, Padding, y, n->body.r, n->body.n, textw);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
redraw(void)
|
||||
{
|
||||
int i, x, y, wh;
|
||||
u32int mask, vals[3];
|
||||
|
||||
for(i = 0; i < Maxnotify; i++){
|
||||
if(wins[i]){
|
||||
xcb_destroy_window(xconn, wins[i]);
|
||||
wins[i] = 0;
|
||||
}
|
||||
}
|
||||
y = Margin;
|
||||
for(i = 0; i < count && i < maxshow; i++){
|
||||
wh = notiheight(i);
|
||||
x = scr->width_in_pixels - Winw - Margin;
|
||||
wins[i] = xcb_generate_id(xconn);
|
||||
mask = XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK;
|
||||
vals[0] = Colbg;
|
||||
vals[1] = 1;
|
||||
vals[2] = XCB_EVENT_MASK_BUTTON_PRESS;
|
||||
xcb_create_window(xconn, XCB_COPY_FROM_PARENT, wins[i], scr->root,
|
||||
x, y, Winw, wh, 1,
|
||||
XCB_WINDOW_CLASS_INPUT_OUTPUT, scr->root_visual, mask, vals);
|
||||
xcb_map_window(xconn, wins[i]);
|
||||
draw(¬ifs[nth(i)], wh, expanded[nth(i)]);
|
||||
xcb_put_image(xconn, XCB_IMAGE_FORMAT_Z_PIXMAP, wins[i], gc,
|
||||
Winw, wh, 0, 0, 0, depth, Winw * wh * 4, (u8int*)img);
|
||||
y += wh + Gap;
|
||||
}
|
||||
xcb_flush(xconn);
|
||||
}
|
||||
|
||||
static void
|
||||
emitclose(u32int id, u32int reason)
|
||||
{
|
||||
CloseEv ev;
|
||||
|
||||
ev.id = id;
|
||||
ev.reason = reason;
|
||||
nbsend(closec, &ev);
|
||||
}
|
||||
|
||||
static void
|
||||
push(Noti *n)
|
||||
{
|
||||
int i;
|
||||
CloseEv ev;
|
||||
|
||||
if(n->id != 0){
|
||||
for(i = 0; i < count; i++){
|
||||
if(notifs[nth(i)].id == n->id){
|
||||
notifs[nth(i)] = *n;
|
||||
createdat[nth(i)] = nsec() + (vlong)timeout * 1000000LL;
|
||||
expanded[nth(i)] = 0;
|
||||
redraw();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(count == Maxnotify){
|
||||
ev.id = notifs[head].id;
|
||||
ev.reason = 4;
|
||||
nbsend(closec, &ev);
|
||||
}
|
||||
notifs[head] = *n;
|
||||
createdat[head] = nsec() + (vlong)timeout * 1000000LL;
|
||||
expanded[head] = 0;
|
||||
head = (head + 1) % Maxnotify;
|
||||
if(count < Maxnotify)
|
||||
count++;
|
||||
if(soundpath != nil && fork() == 0){
|
||||
execl("/usr/bin/aplay", "aplay", "-q", soundpath, nil);
|
||||
_exit(1);
|
||||
}
|
||||
redraw();
|
||||
}
|
||||
|
||||
static void
|
||||
hide(int slot, int reason)
|
||||
{
|
||||
if(slot < 0 || slot >= count)
|
||||
return;
|
||||
if(reason > 0)
|
||||
emitclose(notifs[nth(slot)].id, reason);
|
||||
for(; slot < count - 1; slot++){
|
||||
notifs[nth(slot)] = notifs[nth(slot + 1)];
|
||||
createdat[nth(slot)] = createdat[nth(slot + 1)];
|
||||
expanded[nth(slot)] = expanded[nth(slot + 1)];
|
||||
}
|
||||
head = (head - 1 + Maxnotify) % Maxnotify;
|
||||
count--;
|
||||
redraw();
|
||||
}
|
||||
|
||||
static void
|
||||
closebyid(u32int id)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < count; i++){
|
||||
if(notifs[nth(i)].id == id){
|
||||
hide(i, 3);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
delexpired(void)
|
||||
{
|
||||
vlong now;
|
||||
int c0;
|
||||
|
||||
now = nsec();
|
||||
c0 = count;
|
||||
while(count > 0){
|
||||
if(expanded[nth(0)])
|
||||
break;
|
||||
if(createdat[nth(0)] >= now)
|
||||
break;
|
||||
emitclose(notifs[nth(0)].id, 1);
|
||||
count--;
|
||||
}
|
||||
if(count != c0)
|
||||
redraw();
|
||||
}
|
||||
|
||||
static int
|
||||
findslotbywin(xcb_window_t win)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < maxshow; i++)
|
||||
if(wins[i] == win)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
wininit(void)
|
||||
{
|
||||
int n;
|
||||
xcb_screen_iterator_t iter;
|
||||
|
||||
xconn = xcb_connect(nil, &n);
|
||||
if(xconn == nil || xcb_connection_has_error(xconn))
|
||||
die("xcb_connect");
|
||||
iter = xcb_setup_roots_iterator(xcb_get_setup(xconn));
|
||||
while(n-- > 0)
|
||||
xcb_screen_next(&iter);
|
||||
scr = iter.data;
|
||||
if(scr == nil)
|
||||
die("no screen");
|
||||
depth = scr->root_depth;
|
||||
gc = xcb_generate_id(xconn);
|
||||
xcb_create_gc(xconn, gc, scr->root, 0, nil);
|
||||
fontinit(fontpath);
|
||||
}
|
||||
|
||||
void
|
||||
notithread(void*)
|
||||
{
|
||||
Noti n;
|
||||
xcb_generic_event_t *ev;
|
||||
xcb_button_press_event_t *be;
|
||||
u32int closeid;
|
||||
int slot;
|
||||
|
||||
threadsetname("noti");
|
||||
wininit();
|
||||
for(;;){
|
||||
while((ev = xcb_poll_for_event(xconn)) != nil){
|
||||
if((ev->response_type & ~0x80) == XCB_BUTTON_PRESS){
|
||||
be = (xcb_button_press_event_t*)ev;
|
||||
slot = findslotbywin(be->event);
|
||||
if(slot >= 0){
|
||||
if(be->detail == 1){
|
||||
expanded[nth(slot)] = !expanded[nth(slot)];
|
||||
redraw();
|
||||
} else if(be->detail == 3){
|
||||
hide(slot, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
free(ev);
|
||||
}
|
||||
while(nbrecv(notic, &n) > 0)
|
||||
push(&n);
|
||||
while(nbrecv(closereqc, &closeid) > 0)
|
||||
closebyid(closeid);
|
||||
delexpired();
|
||||
sleep(100);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user