#include "paint.h" Point swatchdim; Image *ants; Channel *winmc; Channel *winsc; Channel *wincc; Channel *cmdc; Canvas *canvasglobal; Widget *colorpalettewidget; Widget *toolboxwidget; mainstacksize = 32*1024; extern void (*_sysfatal)(char*, va_list); int colors[] = { 0x000000FF, 0xFFFFFFFF, 0x68372BFF, 0x70A4B2FF, 0x6F3D86FF, 0x588D43FF, 0x352879FF, 0xB8C76FFF, 0x6F4F25FF, 0x433900FF, 0x9A6759FF, 0x444444FF, 0x6C6C6CFF, 0x9AD284FF, 0x6C5EB5FF, 0x959595FF, }; CanvasTool noptool = { widgetmousenop, }; static Image *rootbg, *αbg; static uchar rootbgbits[] = { 0x00, 0x00, 0x00, 0x00, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x00, 0x00, 0x00, 0x00, }; static Bitmap brootbg = { 8, 2, GREY8, rootbgbits, }; #define X8(x) x, x, x, x, x, x, x, x #define COMMA , static uchar αbgbits[] = { X8(X8(0xBF) COMMA X8(0x3F)), X8(X8(0x3F) COMMA X8(0xBF)), }; #undef X8 #undef COMMA static Bitmap bαbg = { 16, 16, GREY8, αbgbits, }; static uchar antsbits[] = { 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, }; static Bitmap bants = { 8, 8, GREY1, antsbits, }; static Rectangle swatchrect(Point min, int swatchidx) { Point max; min.x += swatchdim.x * swatchidx; max = addpt(min, swatchdim); return Rpt(min, max); } Brush * getbrush(int button) { int i; for(i = 0; button >>= 1; i++) ; return &brushes[i]; } static void clipborder(Image *dst, Rectangle bound, Rectangle r, int i, Image *src) { Rectangle x; if(i < 0) { r = insetrect(r, i); i = -i; } /* top */ x = Rect(r.min.x, r.min.y, r.max.x, r.min.y+i); if(rectclip(&x, bound)) draw(dst, x, src, nil, Pt(x.min.x, 0)); /* bottom */ x = Rect(r.min.x, r.max.y-i, r.max.x, r.max.y); if(rectclip(&x, bound)) draw(dst, x, src, nil, Pt(x.min.x, Dy(src->r)/2)); /* left */ x = Rect(r.min.x, r.min.y+i, r.min.x+i, r.max.y-i); if(rectclip(&x, bound)) draw(dst, x, src, nil, Pt(0, x.min.y)); /* right */ x = Rect(r.max.x-i, r.min.y+i, r.max.x, r.max.y-i); if(rectclip(&x, bound)) draw(dst, x, src, nil, Pt(Dx(src->r)/2, x.min.y)); } void antsrect(Image *dst, Rectangle clipr, Rectangle r) { if(ants == nil && (ants = loadbitmap(bants)) == nil) ants = display->white; clipborder(dst, clipr, r, 2, ants); } static ulong randbg(void) { return hsvtorgb((truerand() | 0x3F3FFF) & 0xFFFF7FFF); } int loadcanvas(Canvas *c, int fd) { ImgHdr hdr; ulong chan; int r; if(readimghdr(fd, &hdr) < 0) { werrstr("header: %r"); return -1; } hdr.r = rectsubpt(hdr.r, hdr.r.min); if(!eqrect(hdr.r, c->s->mem->r)) { chan = c->s->mem->chan; freememimage(c->s->mem); if((c->s->mem = allocmemimage(hdr.r, chan)) == nil) sysfatal("loadcanvas: alloc: %r"); /* throw away any calculations the tool made that depended on the canvas size */ deselecttool(toolboxwidget); toolboxwidget->redraw(toolboxwidget, RDinc); } r = 0; if(loadmem(c->s->mem, hdr, fd) < 0) { werrstr("load: %r"); r = -1; } rootfanout->resize(rootfanout, INFDIM); c->s->dirty = c->s->mem->r; c->f->sync(c->f); return r; } static Point getcanvasdim(Widget *w) { Canvas *c; Rectangle r; c = w->aux; r = ((CanvasSync *)c->s)->mem->r; return mulpt(Pt(Dx(r), Dy(r)), c->mag); } static void scrollcanvas(Widget *w, Point pos) { Canvas *c; Point newpos, Δ; Rectangle imgr; /* the area of the Canvas.img that needs to be synced */ c = w->aux; newpos.x = pos.x == ~0 ? c->pos.x : pos.x; newpos.y = pos.y == ~0 ? c->pos.y : pos.y; Δ = subpt(newpos, c->pos); c->pos = newpos; if(eqpt(Δ, ZP)) return; drawop(c->img, c->img->r, c->img, nil, Δ, S); if(pos.x != ~0 && pos.y != ~0) { fprint(2, "scrollcanvas: scrolling two dimensions at once isn't implemented\n"); } else if(pos.x != ~0) { imgr = Rect(0, 0, abs(Δ.x), c->img->r.max.y); if(Δ.x > 0) imgr = rectaddpt(imgr, Pt(c->img->r.max.x - abs(Δ.x), 0)); } else if(pos.y != ~0) { imgr = Rect(0, 0, c->img->r.max.x, abs(Δ.y)); if(Δ.y > 0) imgr = rectaddpt(imgr, Pt(0, c->img->r.max.y - abs(Δ.y))); } rectclip(&imgr, c->img->r); if(synccanvasimg(c, imgr) < 0) fprint(2, "magsync %R: %r\n", imgr); } static int canvasmouse(Widget *w, Mousectl *mctl) { return ((Canvas *)w->aux)->tool.mouse(w, mctl); } static int synccanvas(Widget *w) { Canvas *c; c = w->aux; if(synccanvasmem(c, c->s->dirty) < 0) { fprint(2, "magsync canvas %#p: %R: %r\n", w, c->s->dirty); return -1; } return 0; } static void redrawcanvaswidget(Widget *w, RedrawMode redrawmode) { Canvas *c; Rectangle memr, canvr, r; c = w->aux; if(redrawmode == RDall) memr = c->s->mem->r; else memr = c->s->dirty; canvr = rectsubpt(scalerect(memr, c->mag), c->pos); if(rectclip(&canvr, rectsubpt(w->r, w->r.min)) == 0) return; r = rectaddpt(canvr, w->r.min); if(c->img->chan == RGBA32) draw(w->screen, r, αbg, nil, canvr.min); draw(w->screen, r, c->img, nil, canvr.min); if(c->sel.active) antsrect(w->screen, w->r, mtosrect(c->sel.r, w)); } static Rdims canvasdims(Widget *w) { Canvas *c; Rdims d; c = w->aux; d.min = Pt(1, 1); d.max = mulpt(rectdim(c->s->mem->r), c->mag); d.des = d.max; return d; } static void resizecanvaswidget(Widget *w, Point dim) { Canvas *c; Point idim; w->r = rectaddpt(Rpt(ZP, dim), w->r.min); /* adjust the position so undefined areas aren't exposed */ c = w->aux; idim = mulpt(c->s->mem->r.max, c->mag); c->pos.x = min(c->pos.x, idim.x - dim.x); c->pos.y = min(c->pos.y, idim.y - dim.y); if(c->img == nil || !eqrect(c->img->r, Rpt(ZP, dim))) { if(c->img != nil) freeimage(c->img); if((c->img = allocimage(display, Rpt(ZP, dim), c->s->mem->chan, 0, DNofill)) == nil) sysfatal("resizecanvaswidget: alloc img: %r"); if(synccanvasimg(c, c->img->r) < 0) fprint(2, "resizecanvaswidget %#p: %r\n", w); } } static void freecanvaswidget(Widget *w) { Canvas *c; c = w->aux; fanoutremove(c->f, w); if(c->f->nw == 0) { freememimage(c->s->mem); free(c->s); } if(c->img != nil) freeimage(c->img); free(c); freewidget(w); } static Widget * zeroxcanvas(Widget *w) { Canvas *c; Widget *z; if((c = malloc(sizeof(Canvas))) == nil) return nil; /* return errstr unmodified */ *c = *(Canvas *)w->aux; c->img = nil; if((z = mkwidget(ZR, canvasmouse, synccanvas, redrawcanvaswidget, canvasdims, resizecanvaswidget, freecanvaswidget, c)) == nil ) { freeimage(c->img); free(c); return nil; } fanoutadd(c->f, z); return z; } static Widget * mkcanvaswidget(Point idim) { Widget *w; Canvas *c; CanvasSync *s; c = emalloc(sizeof(*c)); c->s = s = emalloc(sizeof(*s)); if((c->f = mkfanout()) == nil) sysfatal("mkcanvaswidget: mkfanout: %r"); c->mag = 1; c->pos = ZP; c->img = nil; if((s->mem = allocmemimage(Rpt(ZP, idim), RGBA32)) == nil) sysfatal("mkcanvaswidget: alloc: %r"); memfillcolor(s->mem, randbg()); c->tool = noptool; c->sel.active = 0; if((w = mkwidget(ZR, canvasmouse, synccanvas, redrawcanvaswidget, canvasdims, resizecanvaswidget, freecanvaswidget, c )) == nil) sysfatal("mkcanvaswidget: mkwidget: %r"); fanoutadd(c->f, w); return w; } typedef struct Brushaux { Memimage *mem; Image *img; Memimage *c; } Brushaux; static int syncbrushes(Widget *w) { Brushaux *aux; int i, n; Rectangle r; aux = w->aux; memfillcolor(aux->mem, 0x7F7F7FFF); n = 3; for(i = 0; i < n; i++) { memfillcolor(aux->c, brushes[i].c); r = Rect(i*Dx(aux->mem->r)/n, 0, (i+1)*Dx(aux->mem->r)/n, Dy(aux->mem->r)); memimagedraw(aux->mem, r, aux->c, ZP, brushes[i].p, ZP, SoverD); } if(syncimg(aux->img, aux->mem, aux->mem->r) < 0) { fprint(2, "brush widget: sync: %r\n"); return -1; } return 0; } static void redrawbrushes(Widget *w, RedrawMode) { Brushaux *aux; aux = w->aux; // draw(w->screen, w->r, αbg, nil, ZP); draw(w->screen, w->r, aux->img, nil, ZP); } void freebrushwidget(Widget *w) { Brushaux *aux; aux = w->aux; freememimage(aux->mem); freeimage(aux->img); freememimage(aux->c); free(aux); free(w); } static Widget * mkbrushwidget(void) { Widget *w; Brushaux *aux; Rectangle r; r = Rect(0, 0, swatchdim.x * 3, swatchdim.y * 1.5); aux = emalloc(sizeof(*aux)); if((aux->mem = allocmemimage(r, RGBA32)) == nil || (aux->img = allocimage(display, r, RGBA32, 0, DNofill)) == nil || (aux->c = allocmemimage(Rect(0, 0, 1, 1), RGBA32)) == nil ) sysfatal("mkbrushwidget: %r"); aux->c->flags |= Frepl; aux->c->clipr = INFR; if((w = mkwidget(r, widgetmousenop, syncbrushes, redrawbrushes, widgetdimsnop, widgetresizenop, freebrushwidget, aux)) == nil ) sysfatal("mkbrushwidget: %r"); w->sync(w); return w; } static int patternmouse(Widget *w, Mousectl *mctl) { Brush *brush, old; int b0; Point m; if((mctl->buttons & 7) == 0) return 0; brush = getbrush(mctl->buttons); old = *brush; b0 = mctl->buttons; do { m = subpt(clamppt(mctl->xy, w->r), w->r.min); brush->p = patterns[m.x / swatchdim.x]; brushwidget->sync(brushwidget); brushwidget->redraw(brushwidget, RDall); readmouse(mctl); } while((mctl->buttons & 7) == b0); if(mctl->buttons == 0) return 0; *brush = old; brushwidget->sync(brushwidget); brushwidget->redraw(brushwidget, RDall); while(mctl->buttons) readmouse(mctl); return 0; } static void redrawpatternwidget(Widget *w, RedrawMode) { draw(w->screen, w->r, w->aux, nil, ZP); } static void freepatternwidget(Widget *w) { freeimage(w->aux); freewidget(w); } static Widget * mkpatternwidget(void) { Widget *w; Rectangle r, swatchr; Memimage *mem; Image *img; int i; r = Rect(0, 0, swatchdim.x * npatterns, swatchdim.y); if((mem = allocmemimage(r, RGB24)) == nil || (img = allocimage(display, r, mem->chan, 0, DNofill)) == nil ) { sysfatal("mkpatternwidget: alloc: %r"); return nil; } memfillcolor(mem, 0xD7D7D7FF); for(i = 0; i < npatterns; i++) { swatchr = Rect(swatchdim.x * i, 0, swatchdim.x * (i+1), swatchdim.y); memimagedraw(mem, swatchr, memblack, ZP, patterns[i], ZP, SoverD); } if(syncimg(img, mem, mem->r) < 0) sysfatal("mkpatternwidget: sync: %r"); freememimage(mem); if((w = mkwidget(r, patternmouse, widgetsyncnop, redrawpatternwidget, widgetdimsnop, widgetresizenop, freepatternwidget, img)) == nil ) sysfatal("mkpatternwidget: mwidget: %r"); return w; } static int colorpalettemouse(Widget *w, Mousectl *mctl) { Brush *brush, old; int b0; Point m; if((mctl->buttons & 7) == 0) return 0; brush = getbrush(mctl->buttons); old = *brush; b0 = mctl->buttons; do { m = subpt(clamppt(mctl->xy, w->r), w->r.min); brush->c = colors[m.x / swatchdim.x]; brushwidget->sync(brushwidget); brushwidget->redraw(brushwidget, RDall); setcolorpickercolor(colorpickerwidget, brush->c); colorpickerwidget->redraw(colorpickerwidget, RDall); readmouse(mctl); } while((mctl->buttons & 7) == b0); if(mctl->buttons == 0) return 0; *brush = old; brushwidget->sync(brushwidget); brushwidget->redraw(brushwidget, RDall); while(mctl->buttons) readmouse(mctl); return 0; } static void redrawcolorpalettewidget(Widget *w, RedrawMode redrawmode) { Image *img; img = w->aux; draw(w->screen, w->r, img, nil, ZP); if(redrawmode == RDall) { border(w->screen, w->r, -1, display->black, ZP); drawshadow(w->screen, w->r); } } static void freecolorpalettewidget(Widget *w) { freeimage(w->aux); freewidget(w); } static Widget * mkcolorpalettewidget(void) { Widget *w; Rectangle r, swatchr; Memimage *mem, *c; Image *img; int i; r = Rect(0, 0, swatchdim.x * nelem(colors), swatchdim.y); if((mem = allocmemimage(r, RGB24)) == nil) sysfatal("mkcolorpalettewidget: alloc mem: %r"); if((img = allocimage(display, r, RGB24, 0, DNofill)) == nil) sysfatal("mkcolorpalettewidget: alloc img: %r"); if((c = allocmemimage(Rect(0, 0, 1, 1), RGB24)) == nil) sysfatal("mkcolorpalettewidget: alloc c: %r"); c->flags = Frepl; c->clipr = INFR; for(i = 0; i < nelem(colors); i++) { swatchr = Rect(swatchdim.x * i, 0, swatchdim.x * (i+1), swatchdim.y); memfillcolor(c, colors[i]); memimagedraw(mem, swatchr, c, ZP, nil, ZP, SoverD); } if(syncimg(img, mem, mem->r) < 0) sysfatal("mkcolorpalettewidget: sync: %r"); freememimage(mem); freememimage(c); w = mkwidget(r, colorpalettemouse, widgetsyncnop, redrawcolorpalettewidget, widgetdimsnop, widgetresizenop, freecolorpalettewidget, img ); if(w == nil) sysfatal("mkcolorpalettewidget: mkwidget: %r"); return w; } static void resizeroot(Widget *w, Point) { Image **screen; Point dim; screen = w->aux; dim = rectdim((*screen)->r); w->r = rectaddpt(Rpt(ZP, dim), w->r.min); widgetresizedescend(w, dim); } static void initcolors(void) { int i; for(i = 0; i < nelem(colors); i++) colors[i] = setalpha(colors[i], 0.95*0xFF); } static void initbrush(void) { Memimage **p; int i; Bitmap *b; Rectangle r; int d; patterns = emalloc(npatterns*sizeof(Memimage)); p = patterns; for(i = 0; i < npatterns; i++) { b = patternbitmaps[i]; if((*p = allocmemimage(Rect(0, 0, b->Δx, b->Δy), b->chan)) == nil) sysfatal("alloc pattern: %r"); (*p)->flags |= Frepl; (*p)->clipr = INFR; r = Rect(0, 0, b->Δx, b->Δy); if((d = chantodepth(b->chan)) == 0) sysfatal("initbrush: bad chan"); if(loadmemimage(*p, (*p)->r, b->data, b->Δy * bytesperline(r, d)) < 0) sysfatal("load pattern: %r"); p++; } brushes[0] = (Brush){0xE7E7E7FF, patterns[3]}; brushes[1] = (Brush){0xE7E7E7FF, patterns[0]}; brushes[2] = (Brush){0x181818FF, patterns[0]}; } static void kbdproc(void *arg) { Keyboardctl *kctl = arg; Rune r; for(;;) { recv(kctl->c, &r); switch(r) { case Kdel: threadexitsall(nil); } } } static void freewindow(Window *win) { if(win->screen != nil) freeimage(win->screen); if(win->_screen != nil) { if(win->_screen->image != nil) freeimage(win->_screen->image); freescreen(win->_screen); } if(win->kctl != nil) closekeyboard(win->kctl); if(win->rawmctl != nil) closemouse(win->rawmctl); if(win->rawmc != nil) chanfree(win->rawmc); if(win->mctl != nil) { if(win->mctl->c != nil) chanfree(win->mctl->c); free(win->mctl); } free(win); } static Window * _mkwindow(int new, int pid) { Window *win; char buf[64]; buf[0] = '\0'; if(pid) sprint(buf, "-pid %d", pid); if(new && newwindow(buf) < 0) { /* forks the namespace */ werrstr("newwindow: %r"); return nil; } win = emalloc(sizeof(Window)); memset(win, 0, sizeof(Window)); if(atoifile("/dev/winid", &win->id) < 0) goto err; if((win->kctl = initkeyboard(nil)) == nil) goto err; if((win->rawmctl = initmouse(nil, screen)) == nil) goto err; win->rawmc = chancreate(sizeof(Mouse), 0); win->mctl = emalloc(sizeof(Mousectl)); *win->mctl = *win->rawmctl; win->mctl->c = chancreate(sizeof(Mouse), 0); win->w = nil; win->screen = nil; win->_screen = nil; return win; err: freewindow(win); return nil; } static void mkwindowproc(void *argvp) { void **argv; Channel *c; int new, pid; struct { int err; void *p; } result; argv = argvp; c = argv[0]; new = (uintptr)argv[1]; pid = (uintptr)argv[2]; if(!new) rfork(RFNOTEG); if(result.err = (result.p = _mkwindow(new, pid)) == nil) result.p = smprint("%r"); send(c, &result); } static int onhangup(void *, char *note) { if(strcmp(note, "hangup") == 0) sendp(wincc, *threaddata()); threadexits(note); return 1; } static void hangupproc(void *argvp) { void **argv; int new; Channel *c; Window *win; argv = argvp; new = (uintptr)argv[0]; c = argv[1]; threadsetname("hangup"); if(new) rfork(RFNOTEG); if(threadnotify(onhangup, 1) < 0) { fprint(2, "hangupproc: notify\n"); return; } win = recvp(c); chanfree(c); if(win == nil) return; *threaddata() = win; threadsetname("hangup %d", win->id); while(sleep(69105)) yield(); } static Window * mkwindow(int new) { void *argv1[2], *argv2[3]; Channel *rc, *hc; int id; struct { int err; void *p; } result; rc = chancreate(sizeof(result), 0); hc = chancreate(sizeof(Window *), 0); argv1[0] = (void *)new; argv1[1] = hc; id = proccreate(hangupproc, argv1, 1024); argv2[0] = rc; argv2[1] = (void *)new; argv2[2] = (void *)threadpid(id); proccreate(mkwindowproc, argv2, 4*1024); recv(rc, &result); chanfree(rc); if(result.err) { werrstr("%s", result.p); if(result.p != nil) free(result.p); sendp(hc, nil); return nil; } sendp(hc, result.p); return result.p; } static Window * mkcanvaswindow(Widget *c) { Window *win; Widget *bg, *margin, *layout; if((win = mkwindow(1)) == nil) { werrstr("mkwindow: %r"); return nil; } win->w = mkwidget(ZR, widgetmousedescend, widgetsyncnop, widgetredrawdescend, widgetdimsdescend, resizeroot, freewidget, &win->screen ); addchildwidget(win->w, bg = mkbg(rootbg), ZP); addchildwidget(bg, margin = mkmargin(16, 24, 16, 16), ZP); addchildwidget(margin, layout = mkxautolayout(), ZP); addchildwidget(layout, mkshadow(mkscrollbars(c, getcanvasdim, scrollcanvas)), ZP); addchildwidget(layout, mkmargin(16, 0, 0, 0), ZP); addchildwidget(layout, mkshadow(mktoolboxwidget(c)), ZP); return win; } static int wingetwindow(Window *win) { char winname[64]; snprint(winname, sizeof winname, "/dev/wsys/%d/winname", win->id); if(gengetwindow(display, winname, &win->screen, &win->_screen, Refnone) < 0) return -1; return 0; } /* FIXME - print window name in errors */ static void windowproc(void *arg) { Window *win; enum { AMouse, ASize, AEnd }; Alt a[AEnd+1]; int i; win = arg; threadsetname("window %d", win->id); fanoutadd(rootfanout, win->w); a[AMouse].c = win->rawmctl->c; a[AMouse].v = &win->rawmctl->Mouse; a[AMouse].op = CHANRCV; a[ASize].c = win->mctl->resizec; a[ASize].v = nil; a[ASize].op = CHANRCV; a[AEnd].op = CHANEND; if(win->screen == nil) goto asize; for(;;) { switch(i = alt(a)) { case AMouse: if(chanclosing(a[i].c) == 0) continue; sendp(winmc, win); send(win->rawmc, &win->rawmctl->Mouse); break; case ASize: if(chanclosing(a[i].c) == 0) continue; asize: if(wingetwindow(win) < 0) { fprint(2, "getwindow: %r\n"); continue; } sendp(winsc, win); break; default: /* widgetproc called freewindow and closed everything */ threadexits(nil); } } } Widget * newzeroxwindow(Widget *canvas) { Widget *z; Window *win; if((z = zeroxcanvas(canvas)) == nil) return nil; if((win = mkcanvaswindow(z)) == nil) { werrstr("mkwindow: %r"); z->free(z); return nil; } if(wingetwindow(win) < 0) { werrstr("getwindow: %r"); freewindow(win); z->free(z); return nil; } movewidget(win->w, win->screen->r.min, win->screen); win->w->resize(win->w, rectdim(win->screen->r)); threadcreate(windowproc, win, 8*1024); return z; } static void focusproc(void *argvp) { void **argv; Channel *mc, *sc; Window *win, *focused; enum { AMouse, AFocus, AEnd }; Alt a1[3], a2[AEnd+1]; Mouse m, zm; argv = argvp; mc = argv[0]; sc = argv[1]; threadsetname("focus"); a1[0].c = winmc; a1[0].v = &win; a1[0].op = CHANRCV; a1[1].c = winsc; a1[1].v = &win; a1[1].op = CHANRCV; a1[2].op = CHANEND; a2[AMouse].op = CHANNOP; a2[AFocus].c = mc; a2[AFocus].v = &win; a2[AFocus].op = CHANSND; a2[AEnd].op = CHANEND; focused = nil; while((win = recvp(winmc)) != nil) { recv(win->rawmc, &m); if(win != focused) { if(focused != nil) { zm = (Mouse){0, INFPT, m.msec}; a2[AMouse].c = focused->mctl->c; a2[AMouse].v = &zm; a2[AMouse].op = CHANSND; } alt(a2); focused = win; } win->mctl->Mouse = m; a2[AMouse].c = win->mctl->c; a2[AMouse].v = &m; a2[AMouse].op = CHANSND; alt(a2); } abort(); } static void mousetest(Widget *w, Mousectl *mctl) { Image *src; Point p; if(mctl->buttons == 2) { draw(w->screen, w->screen->r, display->white, nil, ZP); while(mctl->buttons) readmouse(mctl); return; } src = mctl->buttons == 4 ? display->white : display->black; p = mctl->xy; while(mctl->buttons) { line(w->screen, p, mctl->xy, 1, 1, 3, src, ZP); p = mctl->xy; readmouse(mctl); } } static void execcmdproc(void *argvp) { void **argv; Cmd *cmd; Channel *done; argv = argvp; cmd = argv[0]; done = argv[1]; execcmd(canvasglobal, cmd); sendp(done, nil); } static void widgetproc(void *) { Window *win; Channel *focusmc, *focussc, *done; Cmd *cmd; void *fargv[2], *eargv[2]; enum { AMouse, ACmd, ASize, AClose, AEnd }; Alt a1[AEnd+1], a2[3]; threadsetname("widget"); fargv[0] = focusmc = chancreate(sizeof(Window *), 0); fargv[1] = focussc = chancreate(sizeof(Window *), 0); done = chancreate(sizeof(void *), 0); threadcreate(focusproc, fargv, 1024); a1[AMouse].c = focusmc; a1[AMouse].v = &win; a1[AMouse].op = CHANRCV; a1[ACmd].c = cmdc; a1[ACmd].v = &cmd; a1[ACmd].op = CHANRCV; a1[ASize].c = winsc; a1[ASize].v = &win; a1[ASize].op = CHANRCV; a1[AClose].c = wincc; a1[AClose].v = &win; a1[AClose].op = CHANRCV; a1[AEnd].op = CHANEND; a2[0].c = focusmc; a2[0].v = &win; a2[0].op = CHANRCV; a2[1].c = done; a2[1].v = nil; a2[1].op = CHANRCV; a2[2].op = CHANEND; for(;;) { flushimage(display, 1); switch(alt(a1)) { case AMouse: S1: fastsetcursor(win->mctl, nil); win->w->mouse(win->w, win->mctl); break; case ACmd: eargv[0] = cmd; eargv[1] = done; proccreate(execcmdproc, eargv, 32*1024); for(;;) { switch(alt(a2)) { case 0: fastsetcursor(win->mctl, &busycursor); while(win->mctl->buttons) readmouse(win->mctl); break; case 1: goto S1; } } case ASize: movewidget(win->w, win->screen->r.min, win->screen); win->w->resize(win->w, rectdim(win->screen->r)); win->w->redraw(win->w, RDall); break; case AClose: /* were threadkill used here, windowproc's alt would never call dequeue and would leak qentry */ fanoutremove(rootfanout, win->w); if(rootfanout->nw == 0) threadexitsall(nil); win->w->free(win->w); freewindow(win); break; default: abort(); } } } static void usage(void) { fprint(2, "usage: %s\n", argv0); threadexitsall("usage"); } void threadmain(int argc, char **argv) { Point canvasdim, canvaspos, patternpos, colorpalettepos, colorpickerpos; Widget *root, *bg, *canvaswidget; Window *win; ARGBEGIN { default: usage(); } ARGEND if(initdraw(nil, nil, argv0) < 0) sysfatal("initdraw: %r"); memimageinit(); INFPT = Pt(0x3FFFFFFF, 0x3FFFFFFF); INFDIM = Pt(0x3FFFFFFF, 0x3FFFFFFF); INFR = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF); Egreg = "could not connect to database"; if((rootbg = loadbitmap(brootbg)) == nil) sysfatal("load rootbg: %r"); if((αbg = loadbitmap(bαbg)) == nil) sysfatal("load αbg: %r"); initbrush(); initcolors(); rootfanout = mkfanout(); canvasdim = Pt(480, 360); canvaspos = Pt(80, 32); swatchdim = Pt(32, 32); patternpos = Pt(canvaspos.x, canvaspos.y + canvasdim.y + 32); colorpalettepos = Pt(patternpos.x, patternpos.y + swatchdim.y + 16); colorpickerpos = Pt(canvaspos.x + canvasdim.x + 32, canvaspos.y); root = mkwidget(ZR, widgetmousedescend, widgetsyncnop, widgetredrawdescend, widgetdimsnop, resizeroot, freewidget, nil ); if(root == nil) sysfatal("make root widget: %r"); addchildwidget(root, bg = mkbg(rootbg), ZP); addchildwidget(bg, mkshadow(mkpatternwidget()), patternpos); addchildwidget(bg, (colorpalettewidget = mkcolorpalettewidget()), colorpalettepos); addchildwidget(bg, (colorpickerwidget = mkcolorpickerwidget(brushes[0].c)), colorpickerpos); addchildwidget(bg, mkshadow(brushwidget = mkbrushwidget()), Pt(624, 424)); canvaswidget = mkcanvaswidget(Pt(500, 500)); addchildwidget(bg, mkshadow(toolboxwidget = mktoolboxwidget(canvaswidget)), Pt(16, 32)); addchildwidget(bg, mkshadow(mkfileopswidget(canvaswidget)), Pt(toolboxwidget->r.min.x, toolboxwidget->r.max.y + 32) ); addchildwidget(bg, mklayoutmaxwidget(canvasdim, mkshadow(mkscrollbars(canvaswidget, getcanvasdim, scrollcanvas)) ), canvaspos); if((win = mkwindow(0)) == nil) sysfatal("mkwindow: %r"); win->w = root; root->aux = &win->screen; rfork(RFNOTEG); /* send rio's hangup note to the hangupproc instead of here */ logproccreate(); threadcreate(kbdproc, win->kctl, 1024); winmc = chancreate(sizeof(Window *), 0); winsc = chancreate(sizeof(Window *), 0); wincc = chancreate(sizeof(Window *), 0); cmdc = chancreate(sizeof(Cmd *), 0); threadcreate(windowproc, win, 8*1024); if(newcmdwindow(cmdc) < 0) fprint(2, "newcmdwindow: %r\n"); canvasglobal = canvaswidget->aux; widgetproc(nil); }