#include #include #include #include #include "wordwrap.h" /* TODO: * Make getentry() return a random line when the DB has more than one match. * Allow setting the fonts. * Pick better default fonts, esp. on Plan 9. * Allow setting background and text color. * Make tabs bigger. * Maybe handle html-style newlines so we can use the original dataase? * BUGS: * Crashes if the database doesn't have a match for hh:mm. */ Image *background; Font *textfont; Font *matchfont; Font *workfont; Font *authorfont; char* textfontname; char* matchfontname; char* workfontname; char* authorfontname; char* quotelist; char* look; typedef struct Entry Entry; struct Entry { char* hhmm; char* match; char* message; char* work; char* author; char* nsfw; }; Entry entry; void usage(void) { fprint(2, "usage: %s [-q file] [-t hh:mm]\n", argv0); exits("usage"); } void init(void) { if(initdraw(nil, nil, argv0) < 0) { fprint(2, "%s: initdraw failed: %r\n", argv0); exits("initdraw"); } background = allocimagemix(display, DBlack, 0x443344FF); if(background==nil) { fprint(2, "%s: can't create images: %r\n", argv0); exits("image"); } textfont = openfont(display, textfontname); if(textfont == nil) { fprint(2, "%s: openfont failed for textfont: %r\n", argv0); exits("textfont"); } matchfont = openfont(display, matchfontname); if(matchfont == nil) { fprint(2, "%s: openfont failed for matchfont: %r\n", argv0); exits("matchfont"); } workfont = openfont(display, workfontname); if(workfont == nil) { fprint(2, "%s: openfont failed for workfont: %r\n", argv0); exits("workfont"); } authorfont = openfont(display, authorfontname); if(authorfont == nil) { fprint(2, "%s: openfont failed for authorfont: %r\n", argv0); exits("authorfont"); } draw(screen, screen->r, background, nil, ZP); } /* * Wordwrap uses getfileds to split its input into lines, then words, so we * might be missing a space or newline on its input. * XXX Should be called from wordwrap() instead of drawmesg(). */ Point fixsep(char c, int margin, Point p, Font *font) { switch(c) { case ' ': p.x += stringsize(font, " ").x; break; case '\n': p = Pt(screen->r.min.x+margin, p.y + stringsize(font, " ").y); break; } return p; } void drawmesg() { int margin, matchlen; Point insert; char *work, *before, *match, *after; margin = 40; matchlen = strlen(entry.match); before = strdup(entry.message); match = strdup(cistrstr(before, entry.match)); after = strdup(match+matchlen); *(match+matchlen) = '\0'; *(cistrstr(before, entry.match)) = '\0'; draw(screen, screen->r, background, nil, ZP); insert = addpt(Pt(margin, margin), screen->r.min); if(*before) { insert = wordwrap(before, textfont, insert, margin); insert = fixsep(before[strlen(before) -1], margin, insert, textfont); } insert = wordwrap(match, matchfont, insert, margin); if(match[strlen(match) -1] == ' ') insert.x += stringsize(textfont, " ").x; if(*after) { insert = fixsep(after[0], margin, insert, textfont); insert = wordwrap(after, textfont, insert, margin); } insert = Pt(screen->r.min.x+margin, insert.y+stringsize(textfont, " ").y); work = smprint("— %s, ", entry.work); insert = Pt(screen->r.max.x - 2*margin - stringwidth(workfont, work) - stringwidth(authorfont, entry.author), insert.y + stringsize(workfont, work).y); insert = string(screen, insert, display->white, ZP, workfont, work); insert = string(screen, insert, display->white, ZP, authorfont, entry.author); flushimage(display, 1); } void eresized(int new) { if(new && getwindow(display, Refmesg) < 0) fprint(2,"can't reattach to window"); draw(screen, screen->r, background, nil, ZP); drawmesg(); flushimage(display, 1); } char* escape(char* in) { char *out, c, next; int inp, outp; inp = outp = 0; out = malloc(strlen(in)); while (c = in[inp++]) { switch(c) { case '\\': next = in[inp++]; switch(next) { case 'n': c = '\n'; break; default: out[outp++] = '\\'; c = next; } break; /* The original litclock.csv uses html-style
tags... in a few varieites. */ // case '<': // break; } out[outp++] = c; } return out; } void getentry(char* hhmm, int nsfw) { int p[2], kid, pid; char line[4096]; if(pipe(p) < 0) sysfatal("can't make a pipe: %r"); switch(kid = fork()){ case -1: fprint(2, "%s: cannot fork\n", argv0); exits("fork error"); case 0: dup(p[0], 1); close(p[1]); execl(look, "look", hhmm, quotelist, (char *)nil); sysfatal("can't exec look: %r"); default: close(p[0]); readn(p[1], line, (long)sizeof line); while ((pid = waitpid()) != kid && pid != -1) continue; close(p[1]); } if (strlen(line) == 0) { fprint(2, "%s: no entry for %s; not setting hhmm.\n", argv0, hhmm); return; } entry.hhmm = strtok(line, "|"); entry.match = strtok(0, "|"); entry.message = escape(strtok(0, "|")); entry.work = strtok(0, "|"); entry.author = strtok(0, "|"); return; } void main(int argc, char *argv[]) { char* hhmm, *ohhmm; Tm* now; Event e; int Etimer, tflag; matchfontname = "/lib/font/bit/lucida/unicode.32.font"; textfontname = "/lib/font/bit/lucida/unicode.32.font"; workfontname = "/lib/font/bit/lucida/unicode.24.font"; authorfontname = "/lib/font/bit/lucida/unicode.24.font"; look = "/bin/look"; if ((strcmp(getenv("service"), "unix") == 0) || strcmp(getenv("service"), "") == 0) { textfontname = "/mnt/font/Cochin/48a/font"; matchfontname = "/mnt/font/Cochin-Bold/48a/font"; workfontname = "/mnt/font/Cochin-Italic/48a/font"; authorfontname = "/mnt/font/Cochin/48a/font"; look = "/usr/bin/look"; } quotelist = smprint("%s/lib/litclock.csv", getenv("home")); tflag = 0; ARGBEGIN { case 'q': quotelist = EARGF(usage()); break; case 't': tflag++; ohhmm = hhmm = EARGF(usage()); break; default: usage(); } ARGEND init(); ohhmm = ""; /* We don't need mouse events, but resize events are sent over /dev/mouse, */ /* so we won't see them unless we ask for mouse events somehow. */ einit(Ekeyboard|Emouse); Etimer = etimer(0, 1000); for (;;) { if(!tflag) { now = localtime(time(0)); hhmm = smprint("%.2d:%.2d", now->hour, now->min); } if(strcmp(hhmm, ohhmm) != 0) { getentry(hhmm, 0); drawmesg(); ohhmm = hhmm; } switch(eread(Ekeyboard|Etimer, &e)) { case Ekeyboard: if(e.kbdc==0x7F || e.kbdc=='q') exits(0); break; default: break; } } }