ref: 892668527d0e1f705ab39c7da828c54d9dc4cc6e
dir: /main.c/
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include "dat.h"
#include "fns.h"
#define MAXLAYERS 10
#define BACKGROUNDCOLOR 0x22272EFF
#define DEFAULTLAYERCOLOR DWhite
#define CLEARCOLOR DTransparent
#define FPS 30
#define UPDATEPERIOD (1000/FPS)
#define Ar(r) (Dx(r)*Dy(r))
#define RECTOPPT(r, op) ((Rectangle){op(r.min),op(r.max)})
#define DEFAULTCANVASSIZE RECTANGLE(640, 640)
#define STROKEEND Endsquare
#define TASKLISTSIZE 2
#define ZOOMAPPLYDELAY 250
int CanvasMoved = 1;
Rectangle CanvasSize;
Rectangle ZoomedSize;
Point CanvasAt = {0};
Layer * RootLayer = nil;
Layer * CurrentLayer = nil;
Image * Background = nil;
Image * BorderImage = nil;
Image * ClearImage = nil;
int CurrentKeyboard = 0;
Image * BrushImage = nil;
Image * ViewImage = nil;
Image * ZoomedImage = nil;
Image * OverlayImage = nil;
ulong LastUpdate = 0;
int DrawAllLayers = 0;
int Zoom = 1;
int TargetZoom = 1;
int ZoomSensitivity = 1;
Mouse CurrentMouse = {0};
Mouse PastMouse = {0};
Point MousePosition = {0};
Point PastMousePosition = {0};
int RunLoop = 1;
Task TaskList[TASKLISTSIZE] = {0};
int ZoomTaskId = -1;
int DrawOverlay = 0;
int
newlayerimage(Layer * l)
{
if (l == nil) {
werrstr("l = nil");
return -1;
}
if (l->image != nil)
freeimage(l->image);
l->image = allocimage(display, CanvasSize, RGBA32, 1, DEFAULTLAYERCOLOR);
if (l->image == nil) {
werrstr("newlayer: %r");
return -1;
}
l->changed = 1;
return 0;
}
Layer *
newlayer(void)
{
Layer * l = nil, * e = nil;
USED(l);
USED(e);
l = calloc(1, sizeof(Layer));
if (l == nil)
sysfatal("newlayer: failed to allocate layer");
l->changed = 1;
if (newlayerimage(l))
sysfatal("newlayerimage: %r");
if (RootLayer == nil) {
RootLayer = l;
CurrentLayer = l;
} else {
for (e = RootLayer; e->next != nil; e = e->next)
;
e->next = l;
l->prev = e;
}
return l;
}
void
drawcanvas(void)
{
int sr;
Rectangle vr;
sr = 0;
if (ViewImage == nil) {
ViewImage = allocimage(display, CanvasSize, RGBA32, 1, DEFAULTLAYERCOLOR);
if (ViewImage == nil)
sysfatal("v allocimage: %r");
sr = 1;
}
if (ZoomedImage == nil) {
ZoomedImage = allocimage(display, ZoomedSize, RGBA32, 1, DNofill);
if (ZoomedImage == nil)
sysfatal("z allocimage: %r");
sr = 1;
}
for (Layer * l = RootLayer; l != nil; l = l->next)
if (sr || l->changed || DrawAllLayers) {
draw(ViewImage, ViewImage->r, l->image, nil, ZP);
l->changed = 0;
sr = 1;
}
if (Background == nil)
setbackground(BACKGROUNDCOLOR);
vr = rectaddpt(ZoomedSize, CanvasAt);
if (rectclip(&vr, Rect(0, 0, Dx(screen->r), Dy(screen->r))) && ((CanvasMoved && Ar(vr) != Ar(ZoomedSize)) || sr)) {
vr = rectsubpt(vr, CanvasAt);
if (resizeimage(ZoomedImage, vr, Zoom, ViewImage, ZP) < 0)
sysfatal("resizeimage: %r");
}
if (CanvasMoved || sr) {
draw(screen, screen->r, Background, nil, ZP);
draw(screen, RECTOPPT(ZoomedSize, screentoglobalatcanvaspt), ZoomedImage, nil, ZP);
}
CanvasMoved = 0;
DrawAllLayers = 0;
DrawOverlay = 0;
}
ulong
msec(void)
{
return (ulong)(nsec()/1000000);
}
int newtask(ulong s, void (*f)(void))
{
uint i;
for (i = 0; i < ARRLEN(TaskList); i++) {
if (TaskList[i].func != nil)
continue;
TaskList[i] = (Task) { s, f };
return i;
}
return -1;
}
int removetask(int id)
{
if (id < 0 || id >= ARRLEN(TaskList))
return -1;
TaskList[id] = (Task) {0, nil};
return 0;
}
void runtasks(void)
{
Task * t;
ulong i;
for (i = 0; i < ARRLEN(TaskList); i++) {
t = &TaskList[i];
if (t->func == nil || t->start > msec())
continue;
t->func();
removetask(i);
}
}
void
setbackground(ulong col)
{
if (Background != nil)
freeimage(Background);
Background = allocimage(display, RECTANGLE(1, 1), RGBA32, 1, col);
if (Background == nil)
sysfatal("setbackground: %r");
}
static void
zoomrect(Rectangle * zs, Point * ca, int z)
{
int osx, osy;
Point c;
c = subpt(CanvasAt, MousePosition);
osx = Dx(ZoomedSize);
osy = Dy(ZoomedSize);
zs->min = mulpt(CanvasSize.min, z);
zs->max = mulpt(CanvasSize.max, z);
*ca = addpt(
Pt(Dx(ZoomedSize)*c.x/osx, Dy(ZoomedSize)*c.y/osy),
MousePosition
);
}
static void
applyzoom(void)
{
Zoom = TargetZoom;
zoomrect(&ZoomedSize, &CanvasAt, Zoom);
if (ZoomedImage != nil) {
freeimage(ZoomedImage);
ZoomedImage = nil;
}
DrawAllLayers = 1;
ZoomTaskId = -1;
}
void
zoom(int z)
{
Rectangle zs;
Point ca;
if ((TargetZoom + z) <= 1)
TargetZoom = 1;
else
TargetZoom += z;
zoomrect(&zs, &ca, TargetZoom);
if (OverlayImage != nil) {
// zs = rectaddpt(zs, ca);
// border(OverlayImage, zs, 2, BorderImage, ZP);
DrawOverlay = 1;
}
removetask(ZoomTaskId);
if ((ZoomTaskId = newtask(msec() + ZOOMAPPLYDELAY, applyzoom)) < 0) {
werrstr("newtask: no tasks available");
return;
}
}
void
movecanvas()
{
}
void
stroke()
{
Point t, f;
Rectangle viewAtCanvas = {0};
f = PastMousePosition;
t = MousePosition;
viewAtCanvas = rectaddpt(ZoomedSize, CanvasAt);
if (!ptinrect(t, viewAtCanvas))
return;
if (!ptinrect(f, viewAtCanvas))
f = t;
if (BrushImage == nil)
setbrushcolor(DBlack);
f = screentocanvaspt(subpt(f, viewAtCanvas.min));
t = screentocanvaspt(subpt(t, viewAtCanvas.min));
line(CurrentLayer->image, f, t, STROKEEND, STROKEEND, 1, BrushImage, ZP);
f = mulpt(f, Zoom);
t = mulpt(t, Zoom);
if (ZoomedImage != nil)
line(ZoomedImage, f, t, STROKEEND, STROKEEND, 1*Zoom, BrushImage, ZP);
f = screentoglobalatcanvaspt(f);
t = screentoglobalatcanvaspt(t);
line(screen, f, t, STROKEEND, STROKEEND, 1*Zoom, BrushImage, ZP);
}
Point
globaltoscreenpt(Point p)
{
return subpt(p, screen->r.min);
}
Point
globaltoscreenatcanvaspt(Point p)
{
return addpt(globaltoscreenpt(p), CanvasAt);
}
Point
screentocanvaspt(Point p)
{
return (Point){(p.x * (Dx(CanvasSize)) / Dx(ZoomedSize)),
(p.y * (Dy(CanvasSize)) / Dy(ZoomedSize))};
}
Point
canvastoscreenpt(Point p)
{
return (Point){(p.x * (Dx(ZoomedSize)) / Dx(CanvasSize)),
(p.y * (Dy(ZoomedSize)) / Dy(CanvasSize))};
}
Point
screentoglobalpt(Point p)
{
return addpt(p, screen->r.min);
}
Point
screentoglobalatcanvaspt(Point p)
{
return addpt(screentoglobalpt(p), CanvasAt);
}
void
clearlayer(Layer * l)
{
if (l->image != nil)
freeimage(l->image);
if ((l->image = allocimage(display, CanvasSize, RGBA32, 1,
l == RootLayer ? DEFAULTLAYERCOLOR : DTransparent)) == nil)
sysfatal("clearlayer: %r");
l->changed = 1;
if (ViewImage != nil) {
freeimage(ViewImage);
ViewImage = nil;
}
DrawAllLayers = 1;
}
void
setbrushimage(Image * i)
{
if (BrushImage == nil)
freeimage(BrushImage);
BrushImage = i;
}
void
setbrushcolor(ulong c)
{
Image * i;
i = allocimage(display, RECTANGLE(1, 1), RGBA32, 1, c);
if (i == nil)
sysfatal("setbrushcolor: %r");
setbrushimage(i);
}
void
setcanvassize(Rectangle r)
{
CanvasSize = r;
Image * i = nil;
USED(i);
for (Layer * l = RootLayer; l != nil; l = l->next) {
i = allocimage(display, r, RGBA32, 1, DTransparent);
if (i == nil)
sysfatal("setcanvassize: %r");
draw(i, i->r, l->image, nil, ZP);
}
}
void
setoverlaysize(Rectangle r)
{
if (OverlayImage != nil)
freeimage(OverlayImage);
OverlayImage = allocimage(display, r, RGBA32, 1, DTransparent);
DrawOverlay = 1;
}
static void
handlekeyboard(int kbdc)
{
runbindings(BKeyboard, CurrentKeyboard = kbdc);
}
static void
handlemouse(Mouse mouse)
{
CurrentMouse = mouse;
MousePosition = globaltoscreenpt(mouse.xy);
runbindings(BMouse, mouse.buttons);
PastMousePosition = MousePosition;
}
void
eresized(int)
{
if(getwindow(display, Refnone) < 0)
sysfatal("getwindow: %r");
setbackground(BACKGROUNDCOLOR);
CanvasMoved = 1;
drawcanvas();
}
void
main(int argc, char * argv[])
{
ulong d;
Event e;
USED(argc);
USED(argv);
if (initdraw(nil, nil, "pain") < 0)
sysfatal("initdraw: %r\n");
// if (fsinit(nil) < 1)
// sysfatal("fsinit: %r");
// if (!atexit(fsclose))
// sysfatal("atexit: %r");
if ((BorderImage = allocimage(display, RECTANGLE(1, 1), RGBA32, 1, DBlack)) == nil)
sysfatal("allocimage: %r");
setoverlaysize(screen->r);
CanvasSize = ZoomedSize = DEFAULTCANVASSIZE;
setcanvassize(DEFAULTCANVASSIZE);
newlayer();
applyzoom();
setdefaultbindings();
einit(Emouse | Ekeyboard);
etimer(0, UPDATEPERIOD);
drawcanvas();
for(;RunLoop;) {
runtasks();
switch (event(&e))
{
case Ekeyboard:
handlekeyboard(e.kbdc);
break;
case Emouse:
handlemouse(e.mouse);
break;
case 0:
break;
}
d = msec();
if (d < LastUpdate)
LastUpdate = 0;
d -= LastUpdate;
if (d < UPDATEPERIOD)
sleep(1);
else {
drawcanvas();
LastUpdate = msec();
}
}
return;
}