shithub: blie

Download patch

ref: 12c7ae8759ec211ff3b3eed75d73fc673b1314bf
parent: 0e8796746fad950ab61be075de5638fdcbd6fc32
author: sirjofri <sirjofri@sirjofri.de>
date: Wed Aug 7 13:00:24 EDT 2024

adds first interactive mode

--- a/blie.c
+++ b/blie.c
@@ -3,6 +3,8 @@
 #include <draw.h>
 #include <memdraw.h>
 #include <event.h>
+#include <keyboard.h>
+#include <cursor.h>
 #include <bio.h>
 #include "blie.h"
 
@@ -17,6 +19,57 @@
 char Nlayers[] = "layers";
 char *root = nil;
 
+Vdata vdata = {
+	.layerwinwidth = 150,
+	.keyoffset = 10,
+};
+
+static void
+initvdata(void)
+{
+	Point p;
+	
+	p = stringsize(font, "|H");
+	vdata.fontheight = p.y;
+	
+	vdata.gray = allocimagemix(display, DBlack, DWhite);
+}
+
+typedef struct Estate Estate;
+struct Estate {
+	Editor *ed;
+	Layer *l;
+};
+Estate estate;
+
+typedef struct Dstate Dstate;
+struct Dstate {
+	Point offset;
+	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;
+
+void
+changecursor(Cursor *c, Image *i, Point off)
+{
+	dstate.cursor = c;
+	dstate.cursimg = i;
+	dstate.curoffset = off;
+}
+
 int
 loadfile(void)
 {
@@ -74,11 +127,265 @@
 		writememimage(1, img[(i+1)%2]);
 }
 
+static void
+redrawdrawing(void)
+{
+	Memimage *img[2]; /* swapchain */
+	Memimage *ti; /* intermediate image */
+	int i;
+	int nw;
+	
+	img[0] = nil;
+	img[1] = nil;
+	i = (foreachlayer(docomp, img) + 1) % 2;
+	
+	ti = allocmemimage(panels.drawing->r, panels.drawing->chan);
+	memfillcolor(ti, DBlack);
+	memimagedraw(ti, ti->r, img[i], dstate.offset, nil, ZP, S);
+	
+	nw = ti->width * Dy(ti->r) * sizeof(ulong);
+	draw(panels.drawing, panels.drawing->r, display->black, nil, ZP);
+	loadimage(panels.drawing, panels.drawing->r, ti->data->bdata, nw);
+	
+	freememimage(ti);
+	
+	if (!(estate.ed && estate.ed->overlay))
+		return;
+	
+	estate.ed->overlay(estate.l, panels.drawing, dstate.offset);
+}
+
+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);
+	}
+}
+
+static void
+redraw(void)
+{
+	redrawlayers(panels.layers, estate.l);
+	line(panels.layers, panels.layers->r.min,
+		Pt(panels.layers->r.min.x, panels.layers->r.max.y),
+		Endsquare, Endsquare, 0, display->black, ZP);
+	redrawdrawing();
+	
+	redrawtools();
+	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
+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);
+}
+
 void
+eresized(int new)
+{
+	if (new && getwindow(display, Refnone) < 0)
+		sysfatal("resize failed: %r");
+	screeninit();
+	redraw();
+}
+
+static void
+activatelayer(Layer *l)
+{
+	estate.ed = l->editor;
+	estate.l = l;
+	screeninit();
+	redraw();
+}
+
+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
+handlemouse(Event ev)
+{
+	int n;
+	Point xy;
+	Mouse *m = &ev.mouse;
+	xy = m->xy;
+	n = 0;
+	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);
+		switch (n) {
+		case 1:
+			redrawdrawing();
+			break;
+		case 2:
+			redrawtools();
+			break;
+		case 3:
+			redraw();
+			break;
+		}
+		return;
+	}
+	if (estate.ed && ptinrect(m->xy, panels.drawing->r)) {
+		m->xy = subpt(m->xy, panels.drawing->r.min);
+		if (estate.ed->drawinput)
+			n = estate.ed->drawinput(estate.l, Emouse, ev);
+		switch (n) {
+		case 1:
+			redrawdrawing();
+			break;
+		case 2:
+			redrawtools();
+			break;
+		case 3:
+			redraw();
+			break;
+		}
+		esetcursor(dstate.cursor);
+		drawcursor(xy, 0);
+		return;
+	}
+}
+
+/* return 1 = handled */
+static int
+handledrawingkey(int kbdc)
+{
+	switch (kbdc) {
+	case Kup:
+		dstate.offset.y += vdata.keyoffset;
+		goto Redraw;
+	case Kdown:
+		dstate.offset.y -= vdata.keyoffset;
+		goto Redraw;
+	case Kleft:
+		dstate.offset.x += vdata.keyoffset;
+		goto Redraw;
+	case Kright:
+		dstate.offset.x -= vdata.keyoffset;
+		goto Redraw;
+	}
+	return 0;
+Redraw:
+	redrawdrawing();
+	return 1;
+}
+
+static void
+handlekeyboard(Event ev)
+{
+	Mouse *m = &ev.mouse;
+	
+	/* global keys */
+	switch (ev.kbdc) {
+	case 'q':
+	case Kdel:
+		exits(nil);
+	}
+	
+	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)
+			estate.ed->toolinput(estate.l, Ekeyboard, ev);
+		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);
+			estate.ed->drawinput(estate.l, Ekeyboard, ev);
+		}
+		return;
+	}
+}
+
+void
 main(int argc, char **argv)
 {
 	int outputonly = 0;
 	
+	Event ev;
+	int e;
+	
 	ARGBEGIN{
 	case 'c':
 		outputonly++;
@@ -110,6 +417,26 @@
 		exits(nil);
 	}
 	
-	fprint(2, "interactive mode is not implemented yet!\n");
-	exits(nil);
+	if (initdraw(nil, nil, "blie") < 0)
+		sysfatal("initdraw: %r");
+	
+	einit(Emouse|Ekeyboard);
+	
+	initvdata();
+	estate.ed = nil;
+	estate.l = nil;
+	screeninit();
+	redraw();
+	
+	for (;;) {
+		e = event(&ev);
+		switch (e) {
+		case Emouse:
+			handlemouse(ev);
+			break;
+		case Ekeyboard:
+			handlekeyboard(ev);
+			break;
+		}
+	}
 }
--- a/blie.h
+++ b/blie.h
@@ -1,6 +1,8 @@
 typedef struct Layer Layer;
 typedef struct Editor Editor;
+typedef struct Vdata Vdata;
 
+extern Vdata vdata;
 extern int bliedebug;
 
 struct Layer {
@@ -16,15 +18,29 @@
 void addlayer(char *name);
 void movelayer(Layer*, int);
 void savelayermeta(Layer*);
+void redrawlayers(Image*, Layer*);
+void clicklayer(Mouse, void (*f)(Layer*));
 int foreachlayer(void (*f)(Layer*, int, void*), void*);
 
+void changecursor(Cursor*, Image*, Point);
+
+struct Vdata {
+	int layerwinwidth; /* width of layers window */
+	int fontheight; /* height of font */
+	int keyoffset; /* offset on key input */
+	Image *gray;
+};
+
 struct Editor {
 	char *name;
 	Memimage *(*composite)(Layer*, Memimage*);
 	Memimage *(*raw)(Layer*);
 	Memimage *(*mask)(Layer*);
-	Memimage *(*overlay)(Layer*);
-	void (*input)(Layer*, Event);
+	void (*overlay)(Layer*, Image*, Point);
+	Rectangle (*toolrect)(Layer*);
+	void (*drawtools)(Layer*, Image*);
+	int (*drawinput)(Layer*, int, Event);
+	int (*toolinput)(Layer*, int, Event);
 	int (*savedata)(Layer*);
 };
 
--- a/layer.c
+++ b/layer.c
@@ -188,3 +188,70 @@
 	
 	return i;
 }
+
+int layerheight;
+
+static void
+redrawlayer(Layer *l, int n, void *aux)
+{
+	void **data;
+	Image *img;
+	Layer *active;
+	Point p;
+	Rectangle r;
+	
+	data = (void**)aux;
+	img = data[0];
+	active = data[1];
+	
+	p = img->r.min;
+	p.y += n * layerheight;
+	
+	if (l == active) {
+		r.min = r.max = p;
+		r.max.x += vdata.layerwinwidth;
+		r.max.y += layerheight;
+		draw(img, r, vdata.gray, nil, ZP);
+	}
+	string(img, addpt(p, Pt(2, 2)), display->black, ZP, font, l->label);
+}
+
+void
+redrawlayers(Image *img, Layer *active)
+{
+	void *data[2];
+	data[0] = img;
+	data[1] = active;
+	layerheight = vdata.fontheight + 10;
+	draw(img, img->r, display->white, nil, ZP);
+	foreachlayer(redrawlayer, data);
+}
+
+static void
+checkclick(Layer *l, int n, void *aux)
+{
+	void **a;
+	void (*factivate)(Layer*);
+	int *w;
+	
+	a = (void**)aux;
+	factivate = a[0];
+	w = a[1];
+	
+	if (n == *w)
+		factivate(l);
+}
+
+void
+clicklayer(Mouse m, void (*f)(Layer*))
+{
+	void *aux[2];
+	int d;
+	
+	aux[0] = f;
+	aux[1] = &d;
+	
+	d = m.xy.y / layerheight;
+	
+	foreachlayer(checkclick, aux);
+}
--- a/p9image.c
+++ b/p9image.c
@@ -3,8 +3,21 @@
 #include <draw.h>
 #include <memdraw.h>
 #include <event.h>
+#include <cursor.h>
 #include "blie.h"
 
+Cursor ccircle = {
+	{-7, -7},
+	{0xFF, 0xFF, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x07,
+	 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07,
+	 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07,
+	 0xe0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xFF, 0xFF},
+	{0x00, 0x00, 0x7f, 0xfe, 0x40, 0x02, 0x40, 0x02,
+	 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02,
+	 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02,
+	 0x40, 0x02, 0x40, 0x02, 0x7f, 0xfe, 0x00, 0x00}
+};
+
 typedef struct Data Data;
 struct Data {
 	Memimage *img;
@@ -12,6 +25,19 @@
 	Drawop op;
 };
 
+typedef enum {
+	Composite,
+	Img,
+	Mask,
+} Mode;
+
+typedef struct Tstate Tstate;
+struct Tstate {
+	Mode mode;
+	Image *circle;
+};
+Tstate tstate;
+
 static void
 p9init(Layer *l)
 {
@@ -96,13 +122,66 @@
 	return d->mask;
 }
 
-static Memimage*
-p9overlay(Layer *l)
+static void
+p9overlay(Layer *l, Image *i, Point offset)
 {
+	Data *data;
+	Memimage *mi, *tmi;
+	Image *tii;
+	int nw;
+	Rectangle r;
+	
 	p9init(l);
-	return nil;
+	data = (Data*)l->data;
+	
+	switch (tstate.mode) {
+	case Composite:
+		break;
+	case Img:
+		mi = data->img;
+		goto Mout;
+	case Mask:
+		mi = data->mask;
+		goto Mout;
+	}
+	changecursor(nil, nil, ZP);
+	return;
+Mout:
+	if (!tstate.circle) {
+		tstate.circle = allocimage(display, Rect(0, 0, 41, 41), RGBA32, 0, DTransparent);
+		ellipse(tstate.circle, Pt(20, 20), 19, 19, 0, display->white, ZP);
+		ellipse(tstate.circle, Pt(20, 20), 20, 20, 0, display->black, ZP);
+	}
+	changecursor(&ccircle, tstate.circle, Pt(-20, -20));
+	r.min.x = r.min.y = 0;
+	r.max.x = Dx(i->r);
+	r.max.y = Dy(i->r);
+	tmi = allocmemimage(r, i->chan);
+	memfillcolor(tmi, DBlack);
+	memimagedraw(tmi, rectsubpt(mi->r, offset), mi, ZP, nil, ZP, SoverD);
+	
+	tii = allocimage(display, r, i->chan, 0, DTransparent);
+	nw = tmi->width * Dy(tmi->r) * sizeof(ulong);
+	loadimage(tii, tii->r, tmi->data->bdata, nw);
+	draw(i, i->r, tii, nil, ZP);
+	
+	freeimage(tii);
+	freememimage(tmi);
 }
 
+static Rectangle
+p9toolrect(Layer *l)
+{
+	return Rect(0, 0, 200, 50);
+}
+
+static void
+p9drawtools(Layer *l, Image *i)
+{
+	
+	draw(i, insetrect(i->r, 5), display->white, nil, ZP);
+}
+
 static int
 p9savedata(Layer *l)
 {
@@ -110,17 +189,53 @@
 	return 1;
 }
 
-static void
-p9input(Layer *l, Event)
+static int
+p9drawinput(Layer *l, int e, Event)
 {
 	p9init(l);
+	switch (e) {
+	case Ekeyboard:
+		break;
+	case Emouse:
+		break;
+	}
+	return 0;
 }
 
+static int
+p9toolinput(Layer *l, int e, Event ev)
+{
+	p9init(l);
+	if (e != Emouse)
+		return 0;
+	
+	if (!ev.mouse.buttons)
+		return 0;
+	
+	switch (ev.mouse.xy.x / 50) {
+	case 0:
+		tstate.mode = Composite;
+		break;
+	case 1:
+		tstate.mode = Img;
+		break;
+	case 2:
+		tstate.mode = Mask;
+		break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
 Editor p9image = {
 	.name = "p9img",
 	.raw = p9raw,
 	.mask = p9mask,
 	.overlay = p9overlay,
+	.toolrect = p9toolrect,
+	.drawtools = p9drawtools,
 	.savedata = p9savedata,
-	.input = p9input
+	.drawinput = p9drawinput,
+	.toolinput = p9toolinput,
 };
--- a/words
+++ b/words
@@ -51,3 +51,35 @@
 	p9img
 	My fancy layer B
 	SatopD
+
+
+INTERACTIVE MODE
+
+Invocation: Just call the program without -c flag.
+
+Usage:
+
+When starting, you can see three panels:
+
+- Top: tool bar (currently empty)
+- Bottom right: layers bar (lists layers from top to bottom)
+- Bottom left: drawing
+
+Most input reacts depending on where the mouse pointer is. If the
+pointer is on top of the layers window, the input will be forwarded
+to layer management, for example. Exceptions are the following
+commands, which work globally:
+
+- q | del: exit
+
+Drawing controls:
+
+- Up, Left, Down, Right: pan image
+
+Layer controls:
+
+- click: select layer
+
+Selecting a layer also changes the "editor". The editor is a
+module that takes over part of the control, depending on the
+type (see first section about file format).