#include #include #include #include #include #include #include #include #include #include #include //#include //#include #include #include //#include #include //#include extern const char *__progname; typedef enum { BKS_NASCENT = 1, BKS_NONE, BKS_DISCONNECTED, BKS_SCANNING, BKS_CATCHUP, BKS_LIVE, BKS_RESCAN, BKS_PASSIVE_LIVE, BKS_PASSIVE_SCAN } BKSTATE; typedef struct bk BK; typedef struct es ES; struct es { char *b; int a; int l; } ; struct bk { BK *flink; BK *blink; char *id; int idlen; char *backingname; ES stat; XCharStruct namesize; BKSTATE state; int pct; int pht; int xsetup; Window win; Window textwin; Window barwin; int y0; int lastw; } ; static XrmDatabase db; static const char *defaults = "\ *Foreground: white\n\ *Background: black\n\ *LiveColour: #0f0\n\ *CatchupColour: #f00\n\ *ScanColour: #00f\n\ *RescanColour: #f0f\n\ *PassiveLiveColour: #696\n\ *PassiveScanColour: #669\n\ *BarBackground: #585858\n\ *BorderColour: white\n\ *BorderWidth: 1\n\ *BorderMargin: 2\n\ *Name: lbwatch\n\ *IconName: lbwatch\n\ *LBD: ::1/20000\n\ "; static char *displayname; static char *geometryspec; static char *foreground; static char *background; static char *livecstr; static char *catchupcstr; static char *scancstr; static char *rescancstr; static char *passivelivecstr; static char *passivescancstr; static char *barbgcstr; static char *bordercstr; static char *borderwstr; static char *bordermstr; static char *name; static char *visualstr; static char *iconname; static char *fontname; static int synch = 0; /* grrr, preempts sync */ static char *lbdstr; static const char *resname = "lbwatch"; static const char *resclass = "Random"; static char *xrmstr = 0; static int argc; static char **argv; static Display *disp; static Screen *scr; static int width; static int height; static int depth; static Window rootwin; static Colormap defcmap; static Visual *visual; static int (*preverr)(Display *, XErrorEvent *); static int (*prevIOerr)(Display *); static XColor fgcolour; static XColor bgcolour; static XColor livecolour; static XColor catchupcolour; static XColor scancolour; static XColor rescancolour; static XColor passivelivecolour; static XColor passivescancolour; static XColor barbgcolour; static XColor bdcolour; static GC wingc; static Window topwin; static Colormap wincmap; static int margin; static int borderwidth; static XFontStruct *font; static int deffont; static int baselineskip; static int prev_topy; static int junk_direction; static int junk_ascent; static int junk_descent; #define XTE_JUNK &junk_direction,&junk_ascent,&junk_descent static BK *bksf; static BK *bksb; static int lbdfd; static int lbdid; static ES lbdb; static void (*lbd_rdchk)(int); static int xflushid; static int xfd; static int xid; #define Cisdigit(x) isdigit((unsigned char)(x)) static void *deconst(const volatile void *s) { return((((const volatile char *)s)-((const volatile char *)0))+(char *)0); } static void es_init(ES *es) { es->b = 0; es->a = 0; es->l = 0; } static void es_free(ES *es) { free(es->b); es->b = 0; es->a = 0; es->l = 0; } static void es_space(ES *es, int n) { if (n < es->l) abort(); if (es->a < n) es->b = realloc(es->b,es->a=n); } static void es_room(ES *es, int n) { if (n < 0) abort(); es_space(es,es->l+n); } static char *es_buf(ES *es) { return(es->b); } static int es_len(ES *es) { return(es->l); } static int es_avail(ES *es) { return(es->a-es->l); } static int es_fillby(ES *es, int n) { return(es->l+=n); } static void es_setlen(ES *es, int l) { es->l = l; } static void saveargv(int ac, char **av) { int i; int nc; char *abuf; argc = ac; argv = (char **) malloc((ac+1)*sizeof(char *)); nc = 1; for (i=0;i 0) { skip --; continue; } if (**av != '-') { fprintf(stderr,"%s: unrecognized argument `%s'\n",__progname,*av); errs ++; continue; } if (0) { needarg:; fprintf(stderr,"%s: %s needs a following argument\n",__progname,*av); errs ++; continue; } #define WANTARG() do { if (++skip >= ac) goto needarg; } while (0) if (!strcmp(*av,"-display")) { WANTARG(); displayname = av[skip]; continue; } if (!strcmp(*av,"-geometry")) { WANTARG(); geometryspec = av[skip]; continue; } if (!strcmp(*av,"-foreground") || !strcmp(*av,"-fg")) { WANTARG(); foreground = av[skip]; continue; } if (!strcmp(*av,"-background") || !strcmp(*av,"-bg")) { WANTARG(); background = av[skip]; continue; } if (!strcmp(*av,"-bordercolour") || !strcmp(*av,"-bd") || !strcmp(*av,"-bordercolor")) { WANTARG(); bordercstr = av[skip]; continue; } if (!strcmp(*av,"-borderwidth") || !strcmp(*av,"-bw")) { WANTARG(); borderwstr = av[skip]; continue; } if (!strcmp(*av,"-bordermargin") || !strcmp(*av,"-bm")) { WANTARG(); bordermstr = av[skip]; continue; } if (!strcmp(*av,"-visual")) { WANTARG(); visualstr = av[skip]; continue; } if (!strcmp(*av,"-name")) { WANTARG(); name = av[skip]; continue; } if (!strcmp(*av,"-iconname")) { WANTARG(); iconname = av[skip]; continue; } if (!strcmp(*av,"-font") || !strcmp(*av,"-fn")) { WANTARG(); fontname = av[skip]; continue; } if (!strcmp(*av,"-resname")) { WANTARG(); resname = av[skip]; continue; } if (!strcmp(*av,"-resclass")) { WANTARG(); resclass = av[skip]; continue; } if (!strcmp(*av,"-xrm")) { WANTARG(); strappend(&xrmstr,"\n"); strappend(&xrmstr,av[skip]); continue; } if (!strcmp(*av,"-sync")) { synch = 1; continue; } if (!strcmp(*av,"-lbd")) { WANTARG(); lbdstr = av[skip]; continue; } #undef WANTARG fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs ++; } if (errs) exit(1); } static Display *open_display(const char *disp) { Display *rv; rv = XOpenDisplay(disp); if (rv == 0) { fprintf(stderr,"%s: can't open display %s\n",__progname,XDisplayName(disp)); exit(1); } return(rv); } static void setup_db(void) { char *str; XrmDatabase db2; db = XrmGetStringDatabase(defaults); str = XResourceManagerString(disp); if (str) { db2 = XrmGetStringDatabase(str); XrmMergeDatabases(db2,&db); } #if defined(XlibSpecificationRelease) && (XlibSpecificationRelease >= 5) str = XScreenResourceString(scr); if (str) { db2 = XrmGetStringDatabase(str); XrmMergeDatabases(db2,&db); } #endif if (xrmstr) { db2 = XrmGetStringDatabase(xrmstr); XrmMergeDatabases(db2,&db); } } static void maybeset(char **strp, char *str) { if (str && !*strp) *strp = str; } static char *get_default_value(const char *name, const char *class) { char *type; XrmValue value; int nl; int cl; static int buflen = -1; static int rnlen = 0; static int rclen = 0; static char *nbuf; static char *cbuf; nl = strlen(name); cl = strlen(class); if ((nl+1+rnlen >= buflen) || (cl+1+rclen >= buflen)) { if (buflen < 0) { rnlen = strlen(resname); rclen = strlen(resclass); buflen = 10; nbuf = malloc(buflen); cbuf = malloc(buflen); } if (buflen <= nl+1+rnlen) buflen = nl+1+rnlen + 1; if (buflen <= cl+1+rclen) buflen = cl+1+rclen + 1; nbuf = realloc(nbuf,buflen); cbuf = realloc(cbuf,buflen); } sprintf(nbuf,"%s.%s",resname,name); sprintf(cbuf,"%s.%s",resclass,class); if (XrmGetResource(db,nbuf,cbuf,&type,&value) == False) return(0); return(value.addr); } static void lbd_connect(void) { struct addrinfo hints; char *slash; char *addr; char *port; int l; int e; int e2; int s; struct addrinfo *ai0; struct addrinfo *ai; char host[NI_MAXHOST]; char serv[NI_MAXSERV]; slash = index(lbdstr,'/'); if (slash == 0) { fprintf(stderr,"%s: %s: invalid LBD contact point (no slash)\n",__progname,lbdstr); exit(1); } port = slash + 1; l = slash - lbdstr; addr = malloc(l+1); bcopy(lbdstr,addr,l); addr[l] = '\0'; hints.ai_flags = 0; hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; hints.ai_addrlen = 0; hints.ai_canonname = 0; hints.ai_addr = 0; hints.ai_next = 0; e = getaddrinfo(addr,port,&hints,&ai0); if (e) { fprintf(stderr,"%s: %s: can't resolve: %s\n",__progname,lbdstr,gai_strerror(e)); exit(1); } if (! ai0) { fprintf(stderr,"%s: %s: resolved but zero addresses\n",__progname,lbdstr); exit(1); } for (ai=ai0;ai;ai=ai->ai_next) { s = socket(ai->ai_family,ai->ai_socktype,ai->ai_protocol); if (s < 0) { fprintf(stderr,"%s: socket (AF %d): %s\n",__progname,ai->ai_family,strerror(errno)); continue; } if (connect(s,ai->ai_addr,ai->ai_addrlen) < 0) { e = errno; e2 = getnameinfo(ai->ai_addr,ai->ai_addrlen,&host[0],NI_MAXHOST,&serv[0],NI_MAXSERV,NI_NUMERICHOST|NI_NUMERICSERV); if (e2) { fprintf(stderr,"%s: connect (can't print address: %s): %s\n",__progname,strerror(e2),strerror(e)); } else { fprintf(stderr,"%s: connect %s/%s: %s\n",__progname,&host[0],&serv[0],strerror(e)); } close(s); continue; } lbdfd = s; return; } fprintf(stderr,"%s: %s: all attempts failed\n",__progname,lbdstr); exit(1); } static void rd_lbd(void *arg __attribute__((__unused__))) { int nr; es_room(&lbdb,256); nr = read(lbdfd,es_buf(&lbdb)+es_len(&lbdb),es_avail(&lbdb)); if (nr < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: return; break; } fprintf(stderr,"%s: lbd read: %s\n",__progname,strerror(errno)); exit(1); } if (nr == 0) { fprintf(stderr,"%s: lbd read EOF\n",__progname); exit(1); } es_fillby(&lbdb,nr); (*lbd_rdchk)(nr); } static char *b_l_dup(const char *b, int l) { char *t; t = malloc(l+1); bcopy(b,t,l); t[l] = '\0'; return(t); } static void relayout(void) { BK *b; int y; int maxw; XWindowChanges chg; unsigned int chgmask; y = 0; maxw = 0; for (b=bksf;b;b=b->flink) if (b->namesize.width > maxw) maxw = b->namesize.width; for (b=bksf;b;b=b->flink) { if ((y != b->y0) || (maxw != b->lastw)) { b->y0 = y; XMoveResizeWindow(disp,b->win,0,y,100+maxw+(3*margin),baselineskip+(2*margin)); } y += baselineskip + (2 * margin) + borderwidth; } if (y != prev_topy) { chgmask = 0; chg.width = 100 + maxw + (3 * margin); chgmask |= CWWidth; chg.height = y - borderwidth; chgmask |= CWHeight; XReconfigureWMWindow(disp,topwin,XScreenNumberOfScreen(scr),chgmask,&chg); if (bksf) { XMapWindow(disp,topwin); } else { XWithdrawWindow(disp,topwin,XScreenNumberOfScreen(scr)); } prev_topy = y; } } static BK *bk_new(const char *id, int idlen, const char *bf, int bflen) { BK *b; b = malloc(sizeof(BK)); b->id = malloc(idlen); bcopy(id,b->id,idlen); b->idlen = idlen; b->backingname = b_l_dup(bf,bflen); es_init(&b->stat); b->state = BKS_NASCENT; b->xsetup = 0; return(b); } static BK *bk_by_id(const char *id, int idlen) { BK *b; for (b=bksf;b;b=b->flink) if ((b->idlen == idlen) && !bcmp(b->id,id,idlen)) return(b); return(0); } static void bk_teardown(BK *b) { if (b->flink) b->flink->blink = b->blink; else bksb = b->blink; if (b->blink) b->blink->flink = b->flink; else bksf = b->flink; free(b->id); free(b->backingname); es_free(&b->stat); if (b->xsetup) { XDestroyWindow(disp,b->win); } free(b); } static void bk_append(BK *b) { b->flink = 0; b->blink = bksb; if (bksb) bksb->flink = b; else bksf = b; bksb = b; } static void bk_reposition(BK *b) { BK *b2; if (b->flink) b->flink->blink = b->blink; else bksb = b->blink; if (b->blink) b->blink->flink = b->flink; else bksf = b->flink; for (b2=bksf;;b2=b2->flink) { if (! b2) { bk_append(b); return; } if ((b2->state == BKS_NASCENT) || (strcmp(b2->backingname,b->backingname) > 0)) { if (b2->blink) b2->blink->flink = b; else bksf = b; b->blink = b2->blink; b2->blink = b; b->flink = b2; return; } } } static void bk_setup_x(BK *b) { unsigned long int attrmask; XSetWindowAttributes attr; if (b->xsetup) return; attrmask = 0; attr.background_pixel = bgcolour.pixel; attrmask |= CWBackPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.colormap = wincmap; attrmask |= CWColormap; b->win = XCreateWindow(disp,topwin,-2,-2,1,1,0,depth,InputOutput,CopyFromParent,attrmask,&attr); attrmask = 0; attr.background_pixel = bgcolour.pixel; attrmask |= CWBackPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.event_mask = ExposureMask; attrmask |= CWEventMask; attr.colormap = wincmap; attrmask |= CWColormap; b->textwin = XCreateWindow(disp,b->win,100+(2*margin),margin,32767,baselineskip,0,depth,InputOutput,CopyFromParent,attrmask,&attr); attrmask = 0; attr.background_pixel = barbgcolour.pixel; attrmask |= CWBackPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.event_mask = ExposureMask; attrmask |= CWEventMask; attr.colormap = wincmap; attrmask |= CWColormap; b->barwin = XCreateWindow(disp,b->win,margin,margin,100,baselineskip,0,depth,InputOutput,CopyFromParent,attrmask,&attr); b->y0 = -1; b->lastw = -1; XTextExtents(font,b->backingname,strlen(b->backingname),XTE_JUNK,&b->namesize); b->xsetup = 1; XMapWindow(disp,b->textwin); XMapWindow(disp,b->barwin); XMapWindow(disp,b->win); } static void bk_retext(BK *b) { XSetFunction(disp,wingc,GXcopy); XSetForeground(disp,wingc,fgcolour.pixel); XDrawString(disp,b->textwin,wingc,0,font->ascent,b->backingname,strlen(b->backingname)); } static void bk_draw_pht(BK *b, unsigned long int pixel) { if (b->pht >= 0) { XSetForeground(disp,wingc,pixel); if (b->pht >= 100) { XFillRectangle(disp,b->barwin,wingc,0,0,b->pht/100,9); XFillRectangle(disp,b->barwin,wingc,0,0,(b->pht>=1000)?100:(b->pht/10),3); XFillRectangle(disp,b->barwin,wingc,0,0,100,1); } else { if (b->pht >= 10) XFillRectangle(disp,b->barwin,wingc,0,0,b->pht/10,3); XFillRectangle(disp,b->barwin,wingc,0,0,b->pht,1); } } } static void bk_redisplay(BK *b) { switch (b->state) { case BKS_NASCENT: break; case BKS_NONE: break; case BKS_DISCONNECTED: break; case BKS_SCANNING: XSetFunction(disp,wingc,GXcopy); XSetForeground(disp,wingc,barbgcolour.pixel); if (b->pct < 100) XFillRectangle(disp,b->barwin,wingc,b->pct,0,100-b->pct,baselineskip); XSetForeground(disp,wingc,scancolour.pixel); if (b->pct > 0) XFillRectangle(disp,b->barwin,wingc,0,0,b->pct,baselineskip); bk_draw_pht(b,catchupcolour.pixel); break; case BKS_CATCHUP: XSetFunction(disp,wingc,GXcopy); XSetForeground(disp,wingc,barbgcolour.pixel); bk_draw_pht(b,catchupcolour.pixel); break; case BKS_LIVE: XSetFunction(disp,wingc,GXcopy); XSetForeground(disp,wingc,livecolour.pixel); XFillRectangle(disp,b->barwin,wingc,0,0,100,baselineskip); break; case BKS_RESCAN: XSetFunction(disp,wingc,GXcopy); XSetForeground(disp,wingc,rescancolour.pixel); XFillRectangle(disp,b->barwin,wingc,0,0,100,baselineskip); break; case BKS_PASSIVE_LIVE: XSetFunction(disp,wingc,GXcopy); XSetForeground(disp,wingc,passivelivecolour.pixel); XFillRectangle(disp,b->barwin,wingc,0,0,100,baselineskip); bk_draw_pht(b,catchupcolour.pixel); break; case BKS_PASSIVE_SCAN: XSetFunction(disp,wingc,GXcopy); XSetForeground(disp,wingc,passivescancolour.pixel); XFillRectangle(disp,b->barwin,wingc,0,0,100,baselineskip); bk_draw_pht(b,catchupcolour.pixel); break; } } static void expose_event(Drawable win, int x, int y, int w, int h, int count) { BK *b; x=x; y=y; w=w; h=h; for (b=bksf;b;b=b->flink) { if (win == b->textwin) { if (count == 0) bk_retext(b); } else if (win == b->barwin) { if (count == 0) bk_redisplay(b); } } } /* * I would normally use scanf to parse numbers, with (for this program) * an input stream that reads from memory. But scanf is too broken. * It's broken in a minor way in that %*u and %*lu are the same thing * (there is no room in the spec for %u to reject numbers that fit * unsigned long but not unsigned int, if any such exist); it's broken * in a much more major way in that %u and %lu accept negative * numbers. * * Unfortunately strtoul also accepts negative numbers. I don't know * what the hell its designers were thinking, since this leaves no way * to reject signed input without cobbling together one's own! * * So this is my cobbling-together. It looks sorta like scanf, except * that (a) the input comes from pointer-and-length and (b) the escape * sequences are different. Specifically: * * ~p Accepts a number from 0 through 100, without leading * 0s. Argument: unsigned int *. * * ~l Accept any sequence of digits but does not assign it * anywhere. * * ~P Accepts a number from 0 through 100, with exactly two * decimal places and no leading 0s. The value assigned * is 100 times this number (ie, 0-10000). Argument: * unsigned int *. * * Return value is 1 if the format matches the entire buf/buflen * string, 0 otherwise. If 0 is returned, some/all of the arguments * may be stored through. */ static int num_parse(const char *buf, int buflen, const char *fmt, ...) { __label__ done_; va_list ap; const char *fp; int bx; int rv; void done(int ret) { rv = ret; goto done_; } va_start(ap,fmt); bx = 0; fp = fmt; while <"fmt"> (1) { switch (*fp++) { case '\0': done(bx==buflen); break; default: if (bx >= buflen) done(0); if (buf[bx++] != fp[-1]) done(0); break; case '~': switch (*fp++) { default: fprintf(stderr,"%s: bad format ~%c to num_parse\n",__progname,fp[-1]); exit(1); break; case 'p': if (bx >= buflen) done(0); if (! Cisdigit(buf[bx])) done(0); if ((buflen-bx >= 3) && !bcmp(buf+bx,"100",3)) { *va_arg(ap,unsigned int *) = 100; bx += 3; continue; } if ((buflen-bx == 1) || !Cisdigit(buf[bx+1])) { *va_arg(ap,unsigned int *) = buf[bx++] - '0'; continue; } *va_arg(ap,unsigned int *) = ((buf[bx]-'0') * 10) + (buf[bx+1] - '0'); bx += 2; continue; break; case 'P': if (buflen-bx < 4) done(0); if (! Cisdigit(buf[bx])) done(0); if ((buflen-bx >= 6) && !bcmp(buf+bx,"100.00",6)) { *va_arg(ap,unsigned int *) = 10000; bx += 6; continue; } if ((buf[bx+1] == '.') && Cisdigit(buf[bx+2]) && Cisdigit(buf[bx+3])) { *va_arg(ap,unsigned int *) = ((buf[bx]-'0') * 100) + ((buf[bx+2]-'0') * 10) + (buf[bx+3]-'0'); bx += 4; continue; } if (Cisdigit(buf[bx+1]) && (buf[bx+2] == '.') && Cisdigit(buf[bx+3]) && Cisdigit(buf[bx+4])) { *va_arg(ap,unsigned int *) = ((buf[bx]-'0') * 1000) + ((buf[bx+1]-'0') * 100) + ((buf[bx+3]-'0') * 10) + (buf[bx+4]-'0'); bx += 5; continue; } done(0); break; case 'l': if (bx >= buflen) done(0); if (! Cisdigit(buf[bx])) done(0); while ((bx < buflen) && Cisdigit(buf[bx])) bx ++; break; } break; } } done_:; va_end(ap); return(rv); } /* * See the comment on num_parse, above, for why I use it here. */ static void lbd_line_status(const char *id, int idlen, const char *arg, int arglen) { BK *b; int x; char *cx; unsigned int v1; unsigned int v2; const char *a; int al; b = bk_by_id(id,idlen); if (! b) { fprintf(stderr,"%s: can't find any BK with ID %.*s\n",__progname,idlen,id); return; } x = 0; while (1) { if (x >= arglen) { fprintf(stderr,"%s: %s: status ends with colon: %.*s\n",__progname,b->backingname,arglen,arg); return; } cx = memchr(arg+x,':',arglen-x); if (! cx) break; x = (cx + 1) - arg; } a = arg + x; al = arglen - x; if ((al == 5) && !bcmp(a," live",5)) { b->state = BKS_LIVE; } else if ((al == 7) && !bcmp(a," rescan",7)) { b->state = BKS_RESCAN; } else if ((al == 13) && !bcmp(a," disconnected",13)) { b->state = BKS_DISCONNECTED; } else if ((al == 17) && !bcmp(a," passive (rescan)",17)) { b->state = BKS_PASSIVE_SCAN; b->pct = 0; b->pht = 0; } else if ((al == 23) && !bcmp(a," passive (disconnected)",23)) { b->state = BKS_DISCONNECTED; } else if ((al > 10) && !bcmp(a," scanning ",10)) { if (num_parse(a+10,al-10,"~p%/0",&v1)) { b->state = BKS_SCANNING; b->pct = v1; b->pht = 0; } else if (num_parse(a+10,al-10,"~p%/~l/~P%",&v1,&v2)) { b->state = BKS_SCANNING; b->pct = v1; b->pht = v2; } else { fprintf(stderr,"%s: %s: can't parse scanning line: %.*s\n",__progname,b->backingname,arglen,arg); return; } } else if ((al > 9) && !bcmp(a," catchup ",9)) { if ((al == 10) && (a[9] == '0')) { b->state = BKS_CATCHUP; b->pht = 0; } else if (num_parse(a+9,al-9,"~l/~P%",&v1)) { b->state = BKS_CATCHUP; b->pht = v1; } else { fprintf(stderr,"%s: %s: can't parse catchup line: %.*s\n",__progname,b->backingname,arglen,arg); return; } } else if ((al > 19) && !bcmp(a," passive (scanning ",19)) { if (num_parse(a+19,al-19,"~p%/0)",&v1)) { b->state = BKS_PASSIVE_SCAN; b->pct = v1; b->pht = 0; } else if (num_parse(a+19,al-19,"~p%/~l/~P%)",&v1,&v2)) { b->state = BKS_PASSIVE_SCAN; b->pct = v1; b->pht = v2; } else { fprintf(stderr,"%s: %s: can't parse passive scanning line: %.*s\n",__progname,b->backingname,arglen,arg); return; } } else if ((al > 9) && !bcmp(a," passive ",9)) { if ((al == 10) && (a[9] == '0')) { b->state = BKS_PASSIVE_LIVE; b->pht = 0; } else if (num_parse(a+9,al-9,"~l/~P%",&v1)) { b->state = BKS_PASSIVE_LIVE; b->pht = v1; } else { fprintf(stderr,"%s: %s: can't parse passive line: %.*s\n",__progname,b->backingname,arglen,arg); return; } } else { fprintf(stderr,"%s: %s: can't identify line type: %.*s\n",__progname,b->backingname,arglen,arg); return; } bk_redisplay(b); } static void lbd_line_connect(const char *id, int idlen, const char *arg, int arglen) { BK *b; while (1) { b = bk_by_id(id,idlen); if (! b) break; bk_teardown(b); } b = bk_new(id,idlen,arg,arglen); b->state = BKS_NASCENT; bk_append(b); } static void lbd_line_up(const char *id, int idlen, const char *arg, int arglen) { BK *b; b = bk_by_id(id,idlen); if (b == 0) { b = bk_new(id,idlen,arg,arglen); b->state = BKS_NONE; bk_append(b); } bk_setup_x(b); bk_reposition(b); relayout(); bk_redisplay(b); } static void lbd_line_dead(const char *id, int idlen, const char *arg __attribute__((__unused__)), int arglen __attribute__((__unused__))) { BK *b; b = bk_by_id(id,idlen); if (b) bk_teardown(b); relayout(); } static void lbd_line(const char *data, int len) { int opx; int oplen; int idx; int idlen; int argx; int arglen; int x; if ((len < 6) || bcmp(data,"WATCH ",6)) { fprintf(stderr,"%s: non-WATCH line: %.*s\n",__progname,len,data); return; } opx = 6; for (x=opx;(x= len) { fprintf(stderr,"%s: WATCH line with no second space: %.*s\n",__progname,len,data); return; } oplen = x - opx; idx = x + 1; for (x=idx;(x= len) { fprintf(stderr,"%s: WATCH line with no third space: %.*s\n",__progname,len,data); return; } idlen = x - idx; argx = x + 1; arglen = len - argx; if ((oplen == 6) && !bcmp(data+opx,"STATUS",6)) { lbd_line_status(data+idx,idlen,data+argx,arglen); } else if ((oplen == 7) && !bcmp(data+opx,"CONNECT",7)) { lbd_line_connect(data+idx,idlen,data+argx,arglen); } else if ((oplen == 2) && !bcmp(data+opx,"UP",2)) { lbd_line_up(data+idx,idlen,data+argx,arglen); } else if ((oplen == 4) && !bcmp(data+opx,"DEAD",4)) { lbd_line_dead(data+idx,idlen,data+argx,arglen); } else { fprintf(stderr,"%s: WATCH line with unknown op: %.*s\n",__progname,len,data); return; } } static void lbd_rdchk_watch(int n) { char *eb; int el; char *nl; int bx; if (n < 1) abort(); el = es_len(&lbdb); if (n > el) abort(); bx = 0; eb = es_buf(&lbdb); while (1) { nl = (n > 0) ? memchr(eb+(el-n),'\n',n) : 0; if (! nl) { if (bx > 0) { el = es_fillby(&lbdb,-bx); if (el) bcopy(eb+bx,eb,el); } return; } lbd_line(eb+bx,nl-(eb+bx)); bx = (nl + 1) - eb; n = el - bx; } } static void lbd_rdchk_step2(int n) { char *eb; int el; eb = es_buf(&lbdb); el = es_len(&lbdb); if (n != el) abort(); if (eb[0] != '\n') { fprintf(stderr,"%s: lbd didn't follow @ with newline (2)\n",__progname); exit(1); } lbd_rdchk = &lbd_rdchk_watch; el = es_fillby(&lbdb,-1); if (el) { bcopy(eb+1,eb,el); lbd_rdchk_watch(el); } } static void lbd_rdchk_step1(int n) { char *eb; int el; char *at; int x; if (n < 1) abort(); el = es_len(&lbdb); if (n > el) abort(); eb = es_buf(&lbdb); at = memchr(eb+(el-n),'@',n); if (! at) return; x = at - eb; if (x == el-1) { es_setlen(&lbdb,0); lbd_rdchk = &lbd_rdchk_step2; return; } if (eb[x+1] == '\n') { lbd_rdchk = &lbd_rdchk_watch; el = es_fillby(&lbdb,-(x+2)); if (el) { bcopy(eb+x+2,eb,el); lbd_rdchk_watch(el); } return; } fprintf(stderr,"%s: lbd didn't follow @ with newline (1)\n",__progname); exit(1); } static void lbd_setup(void) { const char *s; int l; int w; s = "prompt\necho @\nwatch\n"; l = strlen(s); w = write(lbdfd,s,l); if (w < 0) { fprintf(stderr,"%s: write to lbd: %s\n",__progname,strerror(errno)); exit(1); } if (w != l) { fprintf(stderr,"%s: write to lbd: did %d, wanted %d\n",__progname,w,l); exit(1); } fcntl(lbdfd,F_SETFL,fcntl(lbdfd,F_GETFL,0)|O_NONBLOCK); lbdid = aio_add_poll(lbdfd,&aio_rwtest_always,&aio_rwtest_never,&rd_lbd,0,0); es_init(&lbdb); lbd_rdchk = &lbd_rdchk_step1; } static void setup_lbd(void) { bksf = 0; bksb = 0; lbd_connect(); lbd_setup(); } static void setup_visual(void) { char *cp; XVisualInfo *xvi; int nvi; XVisualInfo template; long int mask; int i; int vpref; visual = 0; depth = XDefaultDepthOfScreen(scr); if (visualstr == 0) return; template.screen = XScreenNumberOfScreen(scr); mask = VisualScreenMask | VisualClassMask; for (cp=visualstr;*cp;cp++) *cp = tolower((unsigned char)*cp); if (!strcmp(visualstr,"staticgray")) template.class = StaticGray; else if (!strcmp(visualstr,"grayscale")) template.class = GrayScale; else if (!strcmp(visualstr,"staticcolour")) template.class = StaticColor; else if (!strcmp(visualstr,"pseudocolour")) template.class = PseudoColor; else if (!strcmp(visualstr,"directcolour")) template.class = DirectColor; else if (!strcmp(visualstr,"truecolour")) template.class = TrueColor; else if (!strcmp(visualstr,"staticcolor")) template.class = StaticColor; else if (!strcmp(visualstr,"pseudocolor")) template.class = PseudoColor; else if (!strcmp(visualstr,"directcolor")) template.class = DirectColor; else if (!strcmp(visualstr,"truecolor")) template.class = TrueColor; else { unsigned long int id; id = strtol(visualstr,&cp,0); if (*cp) { fprintf(stderr,"%s: %s: invalid visual option\n",__progname,visualstr); exit(1); } template.visualid = (VisualID) id; mask |= VisualIDMask; mask &= ~VisualClassMask; } xvi = XGetVisualInfo(disp,mask,&template,&nvi); if (xvi == 0) { fprintf(stderr,"%s: no matching visual found\n",__progname); exit(1); } if (nvi == 0) { fprintf(stderr,"%s: what? XGetVisualInfo returned non-nil but zero count?\n",__progname); exit(1); } vpref = 0; for (i=1;i xvi[vpref].depth) vpref = i; visual = xvi[vpref].visual; depth = xvi[vpref].depth; XFree((char *)xvi); } static void setup_font(void) { deffont = 1; if (fontname) { font = XLoadQueryFont(disp,fontname); if (! font) { fprintf(stderr,"%s: can't load font %s, using server default\n",__progname,fontname); } else { deffont = 0; } } if (deffont) { font = XQueryFont(disp,XGContextFromGC(XDefaultGCOfScreen(scr))); } baselineskip = font->ascent + font->descent; } static void setup_colour(char *str, XColor *col) { if (XParseColor(disp,wincmap,str,col) == 0) { fprintf(stderr,"%s: bad colour `%s'\n",__progname,str); exit(1); } while (1) { if (XAllocColor(disp,wincmap,col) == 0) { if (wincmap != defcmap) { fprintf(stderr,"%s: can't allocate colourmap cell for colour `%s'\n",__progname,str); exit(1); } wincmap = XCopyColormapAndFree(disp,wincmap); continue; } break; } } static void setup_colours(void) { wincmap = defcmap; if (visual) wincmap = XCreateColormap(disp,rootwin,visual,AllocNone); setup_colour(foreground,&fgcolour); setup_colour(background,&bgcolour); setup_colour(livecstr,&livecolour); setup_colour(catchupcstr,&catchupcolour); setup_colour(scancstr,&scancolour); setup_colour(rescancstr,&rescancolour); setup_colour(passivelivecstr,&passivelivecolour); setup_colour(passivescancstr,&passivescancolour); setup_colour(barbgcstr,&barbgcolour); setup_colour(bordercstr,&bdcolour); } static void setup_numbers(void) { if (bordermstr) margin = atoi(bordermstr); if (borderwstr) borderwidth = atoi(borderwstr); } static void setup_windows(void) { int i; int x; int y; unsigned int w; unsigned int h; int bits; unsigned long int attrmask; unsigned long int gcvalmask; XGCValues gcval; XSetWindowAttributes attr; XTextProperty wn_prop; XTextProperty in_prop; XSizeHints normal_hints; XWMHints wm_hints; XClassHint class_hints; static struct { int value; int gravity; } gravities[] = { { XValue|YValue, NorthWestGravity }, { XValue|YValue|XNegative, NorthEastGravity }, { XValue|YValue|YNegative, SouthWestGravity }, { XValue|YValue|XNegative|YNegative, SouthEastGravity } }; x = 0; y = 0; bits = XParseGeometry(geometryspec,&x,&y,&w,&h); if (bits & XNegative) x = width + x - w; if (bits & YNegative) y = height + y - h; w = 1; h = 1; attrmask = 0; attr.background_pixel = bdcolour.pixel; attrmask |= CWBackPixel; attr.border_pixel = bdcolour.pixel; attrmask |= CWBorderPixel; attr.backing_store = NotUseful; attrmask |= CWBackingStore; attr.event_mask = KeyPressMask; attrmask |= CWEventMask; attr.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask; attrmask |= CWDontPropagate; attr.colormap = wincmap; attrmask |= CWColormap; topwin = XCreateWindow(disp,rootwin,x,y,w,h,borderwidth,depth,InputOutput,visual?visual:CopyFromParent,attrmask,&attr); wn_prop.value = (unsigned char *) name; wn_prop.encoding = XA_STRING; wn_prop.format = 8; wn_prop.nitems = strlen((char *)wn_prop.value); in_prop.value = (unsigned char *) iconname; in_prop.encoding = XA_STRING; in_prop.format = 8; in_prop.nitems = strlen((char *)in_prop.value); normal_hints.flags = PMinSize | PResizeInc | PWinGravity; normal_hints.x = x; normal_hints.y = y; normal_hints.win_gravity = CenterGravity; for (i=0;i<(sizeof(gravities)/sizeof(gravities[0]));i++) { if ((bits & (XValue|YValue|XNegative|YNegative)) == gravities[i].value) { normal_hints.win_gravity = gravities[i].gravity; } } normal_hints.flags |= (bits & (XValue|YValue)) ? USPosition : PPosition; normal_hints.width = w; normal_hints.height = h; normal_hints.flags |= PSize; normal_hints.min_width = 1; normal_hints.min_height = 1; normal_hints.width_inc = 1; normal_hints.height_inc = 1; wm_hints.flags = InputHint; wm_hints.input = True; class_hints.res_name = deconst(resname); class_hints.res_class = deconst(resclass); XSetWMProperties(disp,topwin,&wn_prop,&in_prop,argv,argc,&normal_hints,&wm_hints,&class_hints); gcvalmask = 0; if (! deffont) { gcval.font = font->fid; gcvalmask |= GCFont; } gcval.foreground = fgcolour.pixel; gcvalmask |= GCForeground; gcval.background = bgcolour.pixel; gcvalmask |= GCBackground; wingc = XCreateGC(disp,topwin,gcvalmask,&gcval); prev_topy = 0; } static void handle_event(XEvent *e) { switch (e->type) { default: break; case Expose: /* XExposeEvent - xexpose */ expose_event((Drawable)e->xexpose.window,e->xexpose.x,e->xexpose.y,e->xexpose.width,e->xexpose.height,e->xexpose.count); break; case GraphicsExpose: /* XGraphicsExposeEvent - xgraphicsexpose */ printf("GraphicsExpose\n"); // redisplay(e->xgraphicsexpose.drawable,e->xgraphicsexpose.x,e->xgraphicsexpose.y,e->xgraphicsexpose.width,e->xgraphicsexpose.height,e->xgraphicsexpose.count); break; case ButtonPress: /* XButtonPressedEvent - XButtonEvent - xbutton */ printf("ButtonPress\n"); // if (e->xbutton.send_event) return; // buttonpress(e->xbutton.window,e->xbutton.subwindow,e->xbutton.x,e->xbutton.y,e->xbutton.state,e->xbutton.button,e->xbutton.time); break; case SelectionClear: /* XSelectionClearEvent - xselectionclear */ printf("SelectionClear\n"); // if ( e->xselectionclear.send_event || // (e->xselectionclear.window != topwin) ) return; // clear_selection(); break; case SelectionRequest: /* XSelectionRequestEvent - xselectionrequest */ printf("SelectionRequest\n"); // if (e->xselectionrequest.owner != topwin) return; // selection_request(e->xselectionrequest.requestor,e->xselectionrequest.selection,e->xselectionrequest.target,e->xselectionrequest.property,e->xselectionrequest.time); break; } } static void rd_x(void *arg __attribute__((__unused__))) { if (XPending(disp) > 0) { do { XEvent e; XNextEvent(disp,&e); handle_event(&e); } while (XQLength(disp) > 0); } } static int flush_x(void *arg __attribute__((__unused__))) { XFlush(disp); return(AIO_BLOCK_NIL); } static void setup_watchers(void) { xflushid = aio_add_block(&flush_x,0); xfd = XConnectionNumber(disp); xid = aio_add_poll(xfd,&aio_rwtest_always,&aio_rwtest_never,&rd_x,0,0); } static void mainloop(void) { while (1) { aio_pre_poll(); if (aio_do_poll() < 0) { if (errno == EINTR) continue; fprintf(stderr,"%s: poll: %s\n",__progname,strerror(errno)); exit(1); } aio_post_poll(); } } static int err(Display *d, XErrorEvent *ee) { return((*preverr)(d,ee)); } static int ioerr(Display *d) { return((*prevIOerr)(d)); } int main(int, char **); int main(int ac, char **av) { aio_poll_init(); saveargv(ac,av); handleargs(ac,av); XrmInitialize(); disp = open_display(displayname); if (synch) XSynchronize(disp,True); preverr = XSetErrorHandler(&err); prevIOerr = XSetIOErrorHandler(&ioerr); scr = XDefaultScreenOfDisplay(disp); width = XWidthOfScreen(scr); height = XHeightOfScreen(scr); rootwin = XRootWindowOfScreen(scr); defcmap = XDefaultColormapOfScreen(scr); setup_db(); maybeset(&geometryspec,get_default_value("geometry","Geometry")); maybeset(&fontname,get_default_value("font","Font")); maybeset(&foreground,get_default_value("foreground","Foreground")); maybeset(&background,get_default_value("background","Background")); maybeset(&livecstr,get_default_value("liveColour","LiveColour")); maybeset(&catchupcstr,get_default_value("catchupColour","CatchupColour")); maybeset(&scancstr,get_default_value("scanColour","ScanColour")); maybeset(&rescancstr,get_default_value("rescanColour","RescanColour")); maybeset(&passivelivecstr,get_default_value("passveLiveColour","PassiveLiveColour")); maybeset(&passivescancstr,get_default_value("passveScanColour","PassiveScanColour")); maybeset(&barbgcstr,get_default_value("barBackground","BarBackground")); maybeset(&bordercstr,get_default_value("borderColour","BorderColour")); maybeset(&borderwstr,get_default_value("borderWidth","BorderWidth")); maybeset(&bordermstr,get_default_value("borderMargin","BorderMargin")); maybeset(&name,get_default_value("name","Name")); maybeset(&iconname,get_default_value("iconName","IconName")); maybeset(&visualstr,get_default_value("visual","Visual")); maybeset(&lbdstr,get_default_value("lbd","LBD")); setup_lbd(); setup_visual(); setup_font(); setup_colours(); setup_numbers(); setup_windows(); setup_watchers(); mainloop(); return(0); }