ref: 7455a7027cde79e0304bade55b6616983e2e8e1a
dir: /blie.c/
#include <u.h> #include <libc.h> #include <draw.h> #include <memdraw.h> #include <event.h> #include <keyboard.h> #include <cursor.h> #include <bio.h> #include "blie.h" void usage(void) { fprint(2, "usage: %s [-cd] dir\n", argv0); exits(nil); } int bliedebug = 0; int headless = 0; char Nlayers[] = "layers"; char *root = nil; Vdata vdata = { .layerwinwidth = 150, .keyoffset = 10, .keyzoom = 0.2, }; static Memimage* memcol(int chan, int color) { Memimage *i; i = allocmemimage(Rect(0, 0, 1, 1), chan); memfillcolor(i, color); i->flags |= Fsimple|Frepl; i->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF); return i; } static void initvdata(void) { Point p; p = stringsize(font, "|H"); vdata.fontheight = p.y; vdata.gray = allocimagemix(display, DBlack, DWhite); vdata.mgray = allocmemimage(Rect(0, 0, 1, 1), GREY8); memfillcolor(vdata.mgray, 0xaaaaaaff); vdata.mgray->flags |= Frepl|Fbytes|Fsimple; vdata.transparent = allocimage(display, Rect(0, 0, 50, 50), RGB24, 1, 0xccccccff); draw(vdata.transparent, Rect(0, 25, 25, 50), vdata.gray, nil, ZP); draw(vdata.transparent, Rect(25, 0, 50, 25), vdata.gray, nil, ZP); vdata.mtransparent = allocmemimage(Rect(0, 0, 50, 50), RGB24); vdata.mtransparent->flags |= Frepl|Fbytes; memfillcolor(vdata.mtransparent, 0xccccccff); vdata.mgray->clipr = vdata.mtransparent->r; memimagedraw(vdata.mtransparent, Rect(0, 25, 25, 50), vdata.mgray, ZP, nil, ZP, S); memimagedraw(vdata.mtransparent, Rect(25, 0, 50, 25), vdata.mgray, ZP, nil, ZP, S); } Vstate vstate = { .zoom = 1.0, .offset = { 0, 0 }, .dirty = 3, .maxquality = 1, }; void setdrawingdirty(int d) { vstate.dirty = d; } void setmodedirty(int d) { vstate.mdirty = d; } typedef struct Estate Estate; struct Estate { Editor *ed; Layer *l; }; Estate estate; typedef struct Dstate Dstate; struct Dstate { Cursor *cursor; Image *cursimg; Image *cursorblit; Point bloffset; Point curoffset; }; Dstate dstate; typedef struct Panels Panels; struct Panels { Image *tools; Image *layers; Image *drawing; }; Panels panels; Screen *scr; #define NWIN 12 Image *windows[NWIN]; void (*winfunc[NWIN])(Image*); static Image** addwindow(Image *i, void (*f)(Image*)) { int n; for (n = 0; n < NWIN; n++) { if (!windows[n]) { windows[n] = i; winfunc[n] = f; if (f) f(windows[n]); return &windows[n]; } } sysfatal("no more windows available!"); } void changecursor(Cursor *c, Image *i, Point off) { dstate.cursor = c; dstate.cursimg = i; dstate.curoffset = off; } static void initlayer(Layer *l, int, int, void*) { if (l->editor && l->editor->initlayer) l->editor->initlayer(l); } int loadfile(void) { Biobuf *b; int fd; char *s; b = Bopen(Nlayers, OREAD); if (!b) { fd = create(Nlayers, OREAD, 0666); close(fd); b = Bopen(Nlayers, OREAD); } if (!b) sysfatal("unable to open layers file: %r"); while (s = Brdstr(b, '\n', 1)) addlayer(s); foreachlayer(initlayer, nil); return 1; } int nextlayerdirty; // while docomp, is next layer dirty? static void docomp(Layer *l, int id, int, void *aux) { int i; Memimage **img = (Memimage**)aux; Editor *ed = l->editor; Memimage *(*c)(Layer*, Memimage*); i = (id + 1)%2; if (bliedebug) fprint(2, "composite: %s\n", l->name); c = ed->composite ? ed->composite : ecomposite; if (nextlayerdirty) dirtylayer(l); if (!l->composited) { l->composited = c(l, img[i]); nextlayerdirty = 1; } img[id%2] = l->composited; img[i] = nil; } void outputcomposite(void) { Memimage *img[2]; /* swapchain */ int i; img[0] = nil; img[1] = nil; i = (foreachlayer(docomp, img) + 1) % 2; nextlayerdirty = 0; writememimage(1, img[i]); } static void redrawdrawing(void) { Memimage *img[2]; /* swapchain */ int i; draw(panels.drawing, panels.drawing->r, vdata.transparent, nil, ZP); if (estate.ed && estate.ed->overlay) { if (estate.ed->overlay(estate.l, nil)) { estate.ed->overlay(estate.l, panels.drawing); goto Cursorfix; } } img[0] = nil; img[1] = nil; i = (foreachlayer(docomp, img) + 1) % 2; nextlayerdirty = 0; sampleview(panels.drawing, img[i], 1); if (estate.ed && estate.ed->overlay) estate.ed->overlay(estate.l, panels.drawing); /* fix cursor */ Cursorfix: if (dstate.cursorblit) { freeimage(dstate.cursorblit); dstate.cursorblit = nil; } } static void redrawtools(void) { if (estate.ed) { estate.ed->drawtools(estate.l, panels.tools); } else { draw(panels.tools, panels.tools->r, display->white, nil, ZP); } line(panels.tools, Pt(panels.tools->r.min.x, panels.tools->r.max.y-1), subpt(panels.tools->r.max, Pt(0, 1)), Endsquare, Endsquare, 0, display->black, ZP); } static void redraw(int force) { int i; redrawlayers(panels.layers, estate.l); if (force) setdrawingdirty(Dpan); redrawdrawing(); redrawtools(); for (i = 0; i < NWIN; i++) { if (windows[i]) { topwindow(windows[i]); if (winfunc[i]) winfunc[i](windows[i]); } } flushimage(display, 1); } static void screeninit(void) { Rectangle tr; freescreen(scr); scr = allocscreen(screen, display->black, 0); freeimage(panels.tools); freeimage(panels.layers); freeimage(panels.drawing); if (estate.ed) { tr = rectaddpt(estate.ed->toolrect(estate.l), screen->r.min); tr.max.x = screen->r.max.x; } else { tr = screen->r; tr.max.y = tr.min.y + 10; } panels.tools = allocwindow(scr, tr, 0, 0xCCCCCCFF); tr.min.y = tr.max.y; tr.min.x = screen->r.max.x - vdata.layerwinwidth; tr.max = screen->r.max; panels.layers = allocwindow(scr, tr, 0, 0xCCCCCCFF); tr.min.x = screen->r.min.x; tr.min.y = panels.tools->r.max.y; tr.max.x = panels.layers->r.min.x; tr.max.y = screen->r.max.y; panels.drawing = allocwindow(scr, tr, 0, 0xCC00CCFF); flushimage(display, 1); } Image** reqwin(Rectangle r, ulong color, void (*f)(Image*)) { Image *i; i = allocwindow(scr, r, 0, color); // originwindow(i, Pt(0, 0), r.min); return addwindow(i, f); } void unreqwin(Image **i) { freeimage(*i); *i = nil; winfunc[i-windows] = nil; } void eresized(int new) { if (new && getwindow(display, Refnone) < 0) sysfatal("resize failed: %r"); screeninit(); redraw(1); } static void activatelayer(Layer *l) { estate.ed = l->editor; estate.l = l; screeninit(); redraw(1); } static void drawcursor(Point xy, int hide) { Rectangle r; if (dstate.cursorblit) { draw(screen, dstate.cursorblit->r, dstate.cursorblit, nil, dstate.cursorblit->r.min); freeimage(dstate.cursorblit); dstate.cursorblit = nil; } if (hide || !dstate.cursimg) return; r = dstate.cursimg->r; r = rectaddpt(r, addpt(xy, dstate.curoffset)); dstate.cursorblit = allocimage(display, r, screen->chan, 0, DWhite); drawop(dstate.cursorblit, dstate.cursorblit->r, screen, nil, dstate.cursorblit->r.min, S); drawop(screen, r, dstate.cursimg, nil, ZP, SoverD); #ifdef NOP /* debug display */ drawop(screen, screen->r, dstate.cursimg, nil, ZP, SoverD); drawop(screen, screen->r, dstate.cursorblit, nil, addpt(Pt(-50, 0), dstate.cursorblit->r.min), S); #endif } static void condredraw(Redrawwin w) { if (vstate.mdirty) { redraw(1); vstate.mdirty = 0; return; } if (w & Rdrawing) redrawdrawing(); if (w & Rtools) redrawtools(); if (w & Rlayers) redrawlayers(panels.layers, estate.l); } static void handlemouse(Event ev) { Redrawwin n; Point xy; Mouse *m = &ev.mouse; xy = m->xy; vstate.lastmouse = ev.mouse; vstate.mousepos = xy; n = Rnil; if (ptinrect(m->xy, panels.layers->r)) { esetcursor(nil); drawcursor(m->xy, 1); if (m->buttons) { m->xy = subpt(m->xy, panels.layers->r.min); n = clicklayer(ev, estate.l, panels.layers, activatelayer); condredraw(n); } return; } if (estate.ed && ptinrect(m->xy, panels.tools->r)) { esetcursor(nil); drawcursor(m->xy, 1); m->xy = subpt(m->xy, panels.tools->r.min); if (estate.ed->toolinput) n = estate.ed->toolinput(estate.l, Emouse, ev); condredraw(n); return; } if (estate.ed && ptinrect(m->xy, panels.drawing->r)) { m->xy = subpt(m->xy, panels.drawing->r.min); m->xy = scalepos(m->xy); if (estate.ed->drawinput) n = estate.ed->drawinput(estate.l, Emouse, ev); condredraw(n); esetcursor(dstate.cursor); drawcursor(xy, 0); return; } } /* return 1 = handled */ static int handledrawingkey(int kbdc) { switch (kbdc) { case Kup: vstate.offset.y += vdata.keyoffset; goto Dirtypan; case Kdown: vstate.offset.y -= vdata.keyoffset; goto Dirtypan; case Kleft: vstate.offset.x += vdata.keyoffset; goto Dirtypan; case Kright: vstate.offset.x -= vdata.keyoffset; goto Dirtypan; case '.': vstate.zoom += vdata.keyzoom; goto Dirtyzoom; case ',': vstate.zoom -= vdata.keyzoom; if (vstate.zoom < 0.1) vstate.zoom = 0.1; goto Dirtyzoom; } return 0; Dirtyzoom: vstate.dirty |= Dzoom; Dirtypan: vstate.dirty |= Dpan; redrawdrawing(); return 1; } static void savetools(void) { if (!(estate.l && estate.ed)) return; if (estate.ed->savetools) estate.ed->savetools(estate.l); } static void savelayer(Layer *l, int, int, void*) { if (l->editor && l->editor->savedata) l->editor->savedata(l); } static void savecurrentlayer(void) { savelayer(estate.l, 0, 0, nil); } static void savelayers(void) { foreachlayer(savelayer, nil); } static Redrawwin askcommand(Event ev) { char cmd[256]; char *args[5]; int n; cmd[0] = 0; if (!eenter("cmd", cmd, sizeof(cmd), &ev.mouse)) return Rnil; n = tokenize(cmd, args, 5); if (n == 2 && strcmp(args[0], "quality") == 0) { if (strcmp(args[1], "0") == 0 || strcmp(args[1], "nearest") == 0) { vstate.maxquality = 0; setdrawingdirty(Dzoom); return Rdrawing; } if (strcmp(args[1], "1") == 0 || strcmp(args[1], "bilinear") == 0) { vstate.maxquality = 1; setdrawingdirty(Dzoom); return Rdrawing; } return Rnil; } if (args[0][0] == 'w') { switch (args[0][1]) { case 'a': savelayers(); savetools(); return Rnil; case 't': savetools(); return Rnil; case 0: savecurrentlayer(); return Rnil; } return Rnil; } if (n == 2 && strcmp(args[0], "e") == 0) { // TODO: export all to file args[1] return Rnil; } return Rnil; } static void handlekeyboard(Event ev) { Redrawwin w; Mouse *m = &ev.mouse; /* global keys */ switch (ev.kbdc) { case 'q': case Kdel: exits(nil); case '\t': w = askcommand(ev); condredraw(w); return; } if (ptinrect(m->xy, panels.layers->r)) { /* functionality: delete layer, add layer, change layer */ /* l - label (prompt) a - add new layer (prompt editor, name, label) d - delete layer pgup - move layer up pgdn - move layer down */ return; } if (estate.ed && ptinrect(m->xy, panels.tools->r)) { m->xy = subpt(m->xy, panels.tools->r.min); if (estate.ed->toolinput) { w = estate.ed->toolinput(estate.l, Ekeyboard, ev); condredraw(w); } return; } if (ptinrect(m->xy, panels.drawing->r)) { if (handledrawingkey(ev.kbdc)) return; if (estate.ed && estate.ed->drawinput) { m->xy = subpt(m->xy, panels.drawing->r.min); w = estate.ed->drawinput(estate.l, Ekeyboard, ev); condredraw(w); } return; } } void main(int argc, char **argv) { int outputonly = 0; Event ev; int e; ARGBEGIN{ case 'c': outputonly++; break; case 'd': bliedebug++; break; }ARGEND; root = ""; if (argc) { root = argv[0]; } rfork(RFNAMEG); if (chdir(root)) sysfatal("cannot chdir: %r"); if (memimageinit()) sysfatal("memimageinit: %r"); headless = outputonly; if (!headless) { if (initdraw(nil, nil, "blie") < 0) sysfatal("initdraw: %r"); initvdata(); } loadeditors(); if (!loadfile()) sysfatal("cannot load data: %r"); if (outputonly) { outputcomposite(); exits(nil); } einit(Emouse|Ekeyboard); estate.ed = nil; estate.l = nil; screeninit(); redraw(1); for (;;) { e = event(&ev); switch (e) { case Emouse: handlemouse(ev); break; case Ekeyboard: handlekeyboard(ev); break; } } }