#include #include #include #include #include #include #include #include #include #include #include "dat.h" #include "fns.h" #include char Ebadwr[] = "bad rectangle in wctl request"; char Ewalloc[] = "window allocation failed in wctl request"; /* >= Top are disallowed if mouse button is pressed */ enum { New, Resize, Move, Scroll, Noscroll, Set, Top, Bottom, Current, Hide, Unhide, Delete, Background, }; static char *cmds[] = { [New] = "new", [Resize] = "resize", [Move] = "move", [Scroll] = "scroll", [Noscroll] = "noscroll", [Set] = "set", [Top] = "top", [Bottom] = "bottom", [Current] = "current", [Hide] = "hide", [Unhide] = "unhide", [Delete] = "delete", [Background] = "background", nil }; enum { Cd, Deltax, Deltay, Hidden, Id, Maxx, Maxy, Minx, Miny, PID, R, Scrolling, Noscrolling, }; static char *params[] = { [Cd] = "-cd", [Deltax] = "-dx", [Deltay] = "-dy", [Hidden] = "-hide", [Id] = "-id", [Maxx] = "-maxx", [Maxy] = "-maxy", [Minx] = "-minx", [Miny] = "-miny", [PID] = "-pid", [R] = "-r", [Scrolling] = "-scroll", [Noscrolling] = "-noscroll", nil }; /* * Check that newly created window will be of manageable size */ int goodrect(Rectangle r) { if(!eqrect(canonrect(r), r)) return 0; /* reasonable sizes only please */ if(Dx(r) > BIG*Dx(screen->r)) return 0; if(Dy(r) > BIG*Dx(screen->r)) return 0; if(Dx(r) < 100 || Dy(r) < 3*font->height) return 0; /* window must be on screen */ if(!rectXrect(screen->r, r)) return 0; /* must have some screen and border visible so we can move it out of the way */ if(rectinrect(screen->r, insetrect(r, Borderwidth))) return 0; return 1; } static int word(char **sp, char *tab[]) { char *s, *t; int i; s = *sp; while(isspace(*s)) s++; t = s; while(*s!='\0' && !isspace(*s)) s++; for(i=0; tab[i]!=nil; i++) if(strncmp(tab[i], t, strlen(tab[i])) == 0){ *sp = s; return i; } return -1; } int set(int sign, int neg, int abs, int pos) { if(sign < 0) return neg; if(sign > 0) return pos; return abs; } Rectangle newrect(void) { static int i = 0; int minx, miny, dx, dy; dx = min(600, Dx(screen->r) - 2*Borderwidth); dy = min(400, Dy(screen->r) - 2*Borderwidth); minx = 32 + 16*i; miny = 32 + 16*i; i++; i %= 10; return Rect(minx, miny, minx+dx, miny+dy); } void shift(int *minp, int *maxp, int min, int max) { if(*maxp > max){ *minp += max-*maxp; *maxp = max; } if(*minp < min){ *maxp += min-*minp; if(*maxp > max) *maxp = max; *minp = min; } } Rectangle rectonscreen(Rectangle r) { shift(&r.min.x, &r.max.x, screen->r.min.x, screen->r.max.x); shift(&r.min.y, &r.max.y, screen->r.min.y, screen->r.max.y); return r; } /* permit square brackets, in the manner of %R */ int riostrtol(char *s, char **t) { int n; while(*s!='\0' && (*s==' ' || *s=='\t' || *s=='[')) s++; if(*s == '[') s++; n = strtol(s, t, 10); if(*t != s) while((*t)[0] == ']') (*t)++; return n; } int parsewctl(char **argp, Rectangle r, Rectangle *rp, int *pidp, int *idp, int *hiddenp, int *scrollingp, char **cdp, char *s, char *err) { int cmd, param, xy, sign; char *t; *pidp = 0; *hiddenp = 0; *scrollingp = scrolling; *cdp = nil; cmd = word(&s, cmds); if(cmd < 0){ strcpy(err, "unrecognized wctl command"); return -1; } if(cmd == New) r = newrect(); strcpy(err, "missing or bad wctl parameter"); while((param = word(&s, params)) >= 0){ switch(param){ /* special cases */ case Hidden: *hiddenp = 1; continue; case Scrolling: *scrollingp = 1; continue; case Noscrolling: *scrollingp = 0; continue; case R: r.min.x = riostrtol(s, &t); if(t == s) return -1; s = t; r.min.y = riostrtol(s, &t); if(t == s) return -1; s = t; r.max.x = riostrtol(s, &t); if(t == s) return -1; s = t; r.max.y = riostrtol(s, &t); if(t == s) return -1; s = t; continue; } while(isspace(*s)) s++; if(param == Cd){ *cdp = s; while(*s && !isspace(*s)) s++; if(*s != '\0') *s++ = '\0'; continue; } sign = 0; if(*s == '-'){ sign = -1; s++; }else if(*s == '+'){ sign = +1; s++; } if(!isdigit(*s)) return -1; xy = riostrtol(s, &s); switch(param){ case -1: strcpy(err, "unrecognized wctl parameter"); return -1; case Minx: r.min.x = set(sign, r.min.x-xy, xy, r.min.x+xy); break; case Miny: r.min.y = set(sign, r.min.y-xy, xy, r.min.y+xy); break; case Maxx: r.max.x = set(sign, r.max.x-xy, xy, r.max.x+xy); break; case Maxy: r.max.y = set(sign, r.max.y-xy, xy, r.max.y+xy); break; case Deltax: r.max.x = set(sign, r.max.x-xy, r.min.x+xy, r.max.x+xy); break; case Deltay: r.max.y = set(sign, r.max.y-xy, r.min.y+xy, r.max.y+xy); break; case Id: if(idp != nil) *idp = xy; break; case PID: if(pidp != nil) *pidp = xy; break; } } *rp = rectonscreen(rectaddpt(r, screen->r.min)); while(isspace(*s)) s++; if((cmd!=New && cmd!=Background) && *s!='\0'){ strcpy(err, "extraneous text in wctl message"); return -1; } if(argp) *argp = s; return cmd; } int wctlnew(Rectangle rect, char *arg, int pid, int hideit, int scrollit, char *dir, char *err) { char **argv; Image *i; if(!goodrect(rect)){ strcpy(err, Ebadwr); return -1; } argv = emalloc(4*sizeof(char*)); argv[0] = "rc"; argv[1] = "-c"; while(isspace(*arg)) arg++; if(*arg == '\0'){ argv[1] = "-i"; argv[2] = nil; }else{ argv[2] = arg; argv[3] = nil; } if(hideit) i = allocimage(display, rect, screen->chan, 0, DNofill); else i = allocwindow(wscreen, rect, Refbackup, DNofill); if(i == nil){ strcpy(err, Ewalloc); return -1; } new(i, hideit, scrollit, pid, dir, "/bin/rc", argv); free(argv); /* when new() returns, argv and args have been copied */ return 1; } int wctlcmd(Window *w, Rectangle r, int cmd, char *err) { Image *i; switch(cmd){ case Move: r = Rect(r.min.x, r.min.y, r.min.x+Dx(w->screenr), r.min.y+Dy(w->screenr)); r = rectonscreen(r); /* fall through */ case Resize: if(!goodrect(r)){ strcpy(err, Ebadwr); return -1; } if(Dx(w->screenr) > 0){ if(eqrect(r, w->screenr)) return 1; if(w != input){ strcpy(err, "window not current"); return -1; } i = allocwindow(wscreen, r, Refbackup, DNofill); } else { /* hidden */ if(eqrect(r, w->i->r)) return 1; i = allocimage(display, r, w->i->chan, 0, DNofill); r = ZR; } if(i == nil){ strcpy(err, Ewalloc); return -1; } wsendctlmesg(w, Reshaped, r, i); return 1; case Scroll: w->scrolling = 1; wshow(w, w->nr); wsendctlmesg(w, Wakeup, ZR, nil); return 1; case Noscroll: w->scrolling = 0; wsendctlmesg(w, Wakeup, ZR, nil); return 1; case Top: wtopme(w); return 1; case Bottom: wbottomme(w); return 1; case Current: if(Dx(w->screenr)<=0){ strcpy(err, "window is hidden"); return -1; } wtopme(w); wcurrent(w); return 1; case Hide: switch(whide(w)){ case -1: strcpy(err, "window already hidden"); return -1; case 0: strcpy(err, "hide failed"); return -1; default: break; } return 1; case Unhide: switch(wunhide(w)){ case -1: strcpy(err, "window not hidden"); return -1; case 0: strcpy(err, "hide failed"); return -1; default: break; } return 1; case Delete: wsendctlmesg(w, Deleted, ZR, nil); return 1; } strcpy(err, "invalid wctl message"); return -1; } static void wctlbackground(char *file) { char *n; if((n = strchr(file, '\n')) != nil) *n = '\0'; freeimage(background); iconinit(file); extern void resized(int); resized(0); } int writewctl(Xfid *x, char *err) { int cnt, cmd, id, hideit, scrollit, pid; char *arg, *dir; Rectangle r; Window *w; w = x->f->w; cnt = x->count; x->data[cnt] = '\0'; id = 0; r = rectsubpt(w->screenr, screen->r.min); cmd = parsewctl(&arg, r, &r, &pid, &id, &hideit, &scrollit, &dir, x->data, err); if(cmd < 0) return -1; if(id != 0){ w = wlookid(id); if(w == 0){ strcpy(err, "no such window id"); return -1; } } switch(cmd){ case New: return wctlnew(r, arg, pid, hideit, scrollit, dir, err); case Set: if(pid > 0) wsetpid(w, pid, 0); return 1; case Background: if(*arg == '\0'){ strcpy(err, "invalid argument"); return -1; } wctlbackground(arg); return 1; } incref(w); id = wctlcmd(w, r, cmd, err); wclose(w); return id; } void wctlthread(void *v) { char *buf, *arg, *dir; int cmd, id, pid, hideit, scrollit; Rectangle rect; char err[ERRMAX]; Channel *c; c = v; threadsetname("WCTLTHREAD"); for(;;){ buf = recvp(c); cmd = parsewctl(&arg, ZR, &rect, &pid, &id, &hideit, &scrollit, &dir, buf, err); switch(cmd){ case New: wctlnew(rect, arg, pid, hideit, scrollit, dir, err); break; case Background: if(*arg == '\0') break; wctlbackground(arg); break; } free(buf); } } void wctlproc(void *v) { char *buf; int n, eofs; Channel *c; threadsetname("WCTLPROC"); c = v; eofs = 0; for(;;){ buf = emalloc(messagesize); n = read(wctlfd, buf, messagesize-1); /* room for \0 */ if(n < 0) break; if(n == 0){ if(++eofs > 20) break; continue; } eofs = 0; buf[n] = '\0'; sendp(c, buf); } }