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).