ref: 2f61e9979e69ab014cdd44ea71fa787ea3454446
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();
}
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);
clicklayer(*m, activatelayer);
}
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;
}
}
}