ref: 769b10f0ceafba306a3b53bee27f633e67ab11d3
dir: /wctl.c/
#include "inc.h" /* * TODO: i feel like this could use some cleanup */ 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, }; 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", 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 }; static int word(char **sp, char *tab[]) { char *s, *t; int i; s = *sp; while(isspacerune(*s)) s++; t = s; while(*s!='\0' && !isspacerune(*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; } 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; } Wctlcmd parsewctl(char *s, Rectangle r) { Wctlcmd cmd; int n, nt, param, xy, sign; char *f[2], *t; cmd.pid = 0; cmd.hidden = FALSE; cmd.scrolling = scrolling; cmd.dir = nil; cmd.error = nil; cmd.cmd = word(&s, cmds); if(cmd.cmd < 0) goto Lose; if(cmd.cmd == New) r = newrect(); while((param = word(&s, params)) >= 0){ switch(param){ /* special cases */ case Hidden: cmd.hidden = TRUE; continue; case Scrolling: cmd.scrolling = TRUE; continue; case Noscrolling: cmd.scrolling = FALSE; continue; case R: r.min.x = riostrtol(s, &t); if(t == s) goto Lose; s = t; r.min.y = riostrtol(s, &t); if(t == s) goto Lose; s = t; r.max.x = riostrtol(s, &t); if(t == s) goto Lose; s = t; r.max.y = riostrtol(s, &t); if(t == s) goto Lose; s = t; continue; } while(isspacerune(*s)) s++; if(param == Cd){ cmd.dir = s; if((nt = gettokens(cmd.dir, f, nelem(f), " \t\r\n\v\f")) < 1) goto Lose; n = strlen(cmd.dir); if(cmd.dir[0] == '\'' && cmd.dir[n-1] == '\'') (cmd.dir++)[n-1] = '\0'; /* drop quotes */ s += n+(nt-1); continue; } sign = 0; if(*s == '-'){ sign = -1; s++; }else if(*s == '+'){ sign = +1; s++; } if(!isdigitrune(*s)) goto Lose; xy = riostrtol(s, &s); switch(param){ case -1: cmd.error = "unrecognized wctl parameter"; return cmd; 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: cmd.id = xy; break; case PID: cmd.pid = xy; break; } } cmd.r = rectonscreen(rectaddpt(r, screen->r.min)); while(isspacerune(*s)) s++; if(cmd.cmd != New && *s != '\0'){ cmd.error = "extraneous text in wctl message"; return cmd; } cmd.args = s; return cmd; Lose: cmd.error = "unrecognized wctl command"; return cmd; } char* wctlcmd(Window *w, Rectangle r, int cmd) { switch(cmd){ case Move: if(!goodrect(r)) return Ebadwr; wmove(w, r.min); break; case Resize: if(!goodrect(r)) return Ebadwr; wresize(w, r); break; // TODO: these three work somewhat differently in rio case Top: wraise(w); break; case Bottom: wlower(w); break; case Current: if(w->hidden) return "window is hidden"; wfocus(w); wraise(w); break; case Hide: switch(whide(w)){ case -1: return "window already hidden"; case 0: return "hide failed"; } break; case Unhide: switch(wunhide(w)){ case -1: return "window not hidden"; case 0: return "hide failed"; } break; case Delete: wdelete(w); break; case Scroll: w->scrolling = TRUE; xshow(&w->text, w->text.nr); wsendmsg(w, Wakeup); break; case Noscroll: w->scrolling = FALSE; wsendmsg(w, Wakeup); break; default: return "invalid wctl message"; } return nil; } char* wctlnew(Wctlcmd cmd) { Window *w; char *argv[4], **args; w = wcreate(cmd.r, cmd.hidden, cmd.scrolling); assert(w); args = nil; if(cmd.pid == 0){ argv[0] = "rc"; argv[1] = "-c"; while(isspacerune(*cmd.args)) cmd.args++; if(*cmd.args == '\0'){ argv[1] = "-i"; argv[2] = nil; }else{ argv[2] = cmd.args; argv[3] = nil; } args = argv; } if(wincmd(w, cmd.pid, cmd.dir, args) == 0) return "window creation failed"; return nil; } char* writewctl(Window *w, char *data) { Rectangle r; Wctlcmd cmd; if(w == nil) r = ZR; else r = rectsubpt(w->img->r, screen->r.min); cmd = parsewctl(data, r); if(cmd.error) return cmd.error; if(cmd.id != 0){ w = wfind(cmd.id); if(w == nil) return "no such window id"; } if(w == nil && cmd.cmd != New) return "command needs to be run within a window"; switch(cmd.cmd){ case New: return wctlnew(cmd); case Set: if(cmd.pid > 0) wsetpid(w, cmd.pid, 0); return nil; default: incref(w); cmd.error = wctlcmd(w, cmd.r, cmd.cmd); wrelease(w); return cmd.error; } }