ref: 4bcb73eb20638b1564d6757405508ea63ccf5f14
parent: 892668527d0e1f705ab39c7da828c54d9dc4cc6e
author: jmq <jmq@jmq.sh>
date: Sat Dec 7 23:38:34 EST 2024
improve input and shortcuts
--- a/bindings.c
+++ b/bindings.c
@@ -7,7 +7,6 @@
#include "fns.h"
Binding * BindingRoot = nil;
-static Point MoveMark = {0};typedef struct DefaultBinding
{@@ -17,6 +16,19 @@
void (*func)(int);
} DefaultBinding;
+// made with
+// grep '^Binding[a-zA-Z][a-zA-Z]*' bindings.c | sed 's/^(.*)$/static void \1;/g'
+static void BindingQuit(int);
+static void BindingStroke(int e);
+static void BindingZoomIn(int);
+static void BindingZoomOut(int);
+static void BindingMoveCanvas(int e);
+static void BindingMoveLayer(int e);
+static void BindingClearLayer(int e);
+static void BindingSwitchMode(int);
+static void BindingResetCanvas(int);
+static void BindingResetCurrentLayer(int);
+
static void
BindingQuit(int)
{@@ -49,15 +61,9 @@
}
static void
-movecanvas(void)
-{- CanvasAt = addpt(CanvasAt, subpt(MousePosition, MoveMark));
- CanvasMoved = DrawAllLayers = 1;
-}
-
-static void
BindingMoveCanvas(int e)
{+ static Point MoveMark = {0};static int movetask = -1;
if (e == BEenter)
MoveMark = MousePosition;
@@ -68,19 +74,130 @@
}
static void
-BindingClearLayer(int e)
+BindingMoveLayer(int e)
{- if (e != BEenter)
- return;
+ static Point MoveMark = {0};+ static int movetask = -1;
+ if (e == BEenter)
+ MoveMark = MousePosition;
+ else if (e == BEleave && CurrentLayer != nil) {+ CurrentLayer->offset = addpt(CurrentLayer->offset,
+ subpt(MoveMark, MousePosition));
+ CurrentLayer->changed = 1;
+ clearview();
+ }
+}
+static void
+BindingClearLayer(int)
+{if (CurrentLayer != nil && newlayerimage(CurrentLayer))
sysfatal("newlayerimage: %r");+ clearview();
}
+static void
+BindingSwitchMode(int)
+{+ static int mode = 0;
+ Binding * bm, * br;
+ char * s;
+
+ removebindings(BMouse, 2);
+ removebindings(BKeyboard, 'r');
+ if ((bm = addbinding(BMouse, 2)) == nil)
+ sysfatal("addbinding: %r");+ if ((br = addbinding(BKeyboard, 'r')) == nil)
+ sysfatal("addbinding: %r");+ s = "";
+ switch(mode)
+ {+ case 0:
+ bm->func = BindingMoveLayer;
+ br->func = BindingResetCurrentLayer;
+ s = "layer";
+ mode = 1;
+ break;
+ case 1:
+ bm->func = BindingMoveCanvas;
+ br->func = BindingResetCanvas;
+ s = "canvas";
+ mode = 0;
+ break;
+ }
+ ntfprint("changed to %s mode\n", s);+}
+
+static void
+BindingResetCanvas(int)
+{+ TargetZoom = 1;
+ applyzoom();
+ CanvasAt = Pt(0, 0);
+ centercanvas();
+}
+
+static void
+BindingResetCurrentLayer(int)
+{+ if (CurrentLayer == nil)
+ return;
+ CurrentLayer->offset = Pt(0, 0);
+ clearview();
+}
+
+static void
+BindingNewLayer(int)
+{+ newlayer();
+}
+
+static void
+BindingNextLayer(int)
+{+ setcurrentlayer(CurrentLayer->next);
+}
+
+static void
+BindingPreviousLayer(int)
+{+ setcurrentlayer(CurrentLayer->prev);
+}
+
+static void
+BindingDeleteCurrentLayer(int)
+{+ ntfprint("not yet implemented");+}
+
+static void
+BindingSave(int)
+{+ int fd;
+ char s[32];
+
+ snprint(s, 32, "/tmp/pain.XXXXXX");
+ mktemp(s);
+ fd = create(s, OWRITE, 0755);
+ if (fd < 0) {+ ntfprint("failed to save image to %s", s);+ }
+ writeimage(fd, ViewImage, 0);
+ close(fd);
+ ntfprint("current canvas saved to %s", s);+}
+
DefaultBinding DefaultBindings[] =
{ {BKeyboard, 'q', nil, BindingQuit}, {BKeyboard, 'c', nil, BindingClearLayer},+ {BKeyboard, 'm', nil, BindingSwitchMode},+ {BKeyboard, 'r', nil, BindingResetCanvas},+ {BKeyboard, 'n', nil, BindingNewLayer},+ {BKeyboard, 'd', nil, BindingDeleteCurrentLayer},+ {BKeyboard, 'o', nil, BindingPreviousLayer},+ {BKeyboard, 'p', nil, BindingNextLayer},+ {BKeyboard, 's', nil, BindingSave}, {BMouse, 1, nil, BindingStroke}, {BMouse, 8, nil, BindingZoomIn}, {BMouse, 16, nil, BindingZoomOut},@@ -117,7 +234,7 @@
n = e->next;
freebinding(e);
if (e == BindingRoot)
- BindingRoot = nil;
+ BindingRoot = e->next;
rc++;
}
@@ -159,11 +276,14 @@
rc = 0;
for (b = BindingRoot; b != nil; b = b->next) {- e = BEleave;
- if (b->type != type || b->code != code) {- if (b->state == BEleave)
+ if (b->type != type)
+ continue;
+
+ e = b->type == BKeyboard ? BEenter : BEleave;
+ if (b->code != code) {+ if (b->state == BEleave || b->type == BKeyboard)
continue;
- } else {+ } else if(b->type == BMouse) { switch (b->state) {case BEenter:
e = BEin;
--- a/dat.h
+++ b/dat.h
@@ -2,7 +2,8 @@
{Image * image;
struct Layer * prev, * next;
- int changed;
+ int changed, id;
+ Point offset;
} Layer;
enum
@@ -23,6 +24,7 @@
int code;
int type;
int state;
+ int pressed, lastPressed;
char * command;
void (*func)(int);
} Binding;
@@ -38,6 +40,7 @@
extern Layer * LayerRoot;
extern Layer * CurrentLayer;
extern Binding * BindingRoot;
+extern Image * ViewImage;
extern int RunLoop;
extern int ZoomSensitivity;
extern Point MousePosition;
@@ -46,6 +49,7 @@
extern int CanvasMoved;
extern int DrawAllLayers;
extern ulong LastUpdate;
+extern int TargetZoom;
// extern Layer * CurrentLayer = nil;
// extern Image * Background = nil;
// extern Image * Canvas = nil;
--- a/fns.h
+++ b/fns.h
@@ -8,11 +8,13 @@
ulong msec(void);
void setbackground(ulong col);
void drawcanvas(void);
+void applyzoom(void);
void zoom(int);
void zoomin(void);
void zoomout(void);
void stroke(void);
void clearlayer(Layer *);
+void clearview(void);
void setbrushcolor(ulong col);
int resizeimage(Image *, Rectangle, int, Image *, Point);
int newtask(ulong, void (*)(void));
@@ -19,7 +21,10 @@
int removetask(int);
void runtasks(void);
int newlayerimage(Layer *);
+void centercanvas(void);
Layer * newlayer(void);
+void ntfprint(char *, ...);
+void setcurrentlayer(Layer *);
Point globaltoscreenpt(Point p);
Point globaltoscreenatcanvaspt(Point p);
--- a/main.c
+++ b/main.c
@@ -8,16 +8,19 @@
#define MAXLAYERS 10
#define BACKGROUNDCOLOR 0x22272EFF
-#define DEFAULTLAYERCOLOR DWhite
+#define DEFAULTLAYERCOLOR DTransparent
+#define DEFAULTCANVASBACKGROUNDCOLOR 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 DEFAULTCANVASSIZE RECTANGLE(1920, 1080)
#define STROKEEND Endsquare
#define TASKLISTSIZE 2
-#define ZOOMAPPLYDELAY 250
+#define NOTIFICATIONFONTCOLOR DBlack
+#define NOTIFICATIONBACKGROUNDCOLOR 0x72DEC2FF
+#define ZOOMAPPLYDELAY (1000/6)
int CanvasMoved = 1;
Rectangle CanvasSize;
@@ -30,9 +33,12 @@
Image * ClearImage = nil;
int CurrentKeyboard = 0;
Image * BrushImage = nil;
+Image * NotificationImage = nil;
+Image * NotificationBackgroundImage = nil;
+char * NotificationString = nil;
Image * ViewImage = nil;
Image * ZoomedImage = nil;
-Image * OverlayImage = nil;
+Image * CanvasBackgroundImage = nil;
ulong LastUpdate = 0;
int DrawAllLayers = 0;
int Zoom = 1;
@@ -45,7 +51,7 @@
int RunLoop = 1;
Task TaskList[TASKLISTSIZE] = {0};int ZoomTaskId = -1;
-int DrawOverlay = 0;
+uint LayerIDCounter = 0;
int
newlayerimage(Layer * l)
@@ -58,7 +64,7 @@
if (l->image != nil)
freeimage(l->image);
- l->image = allocimage(display, CanvasSize, RGBA32, 1, DEFAULTLAYERCOLOR);
+ l->image = allocimage(display, CanvasSize, RGBA32, 0, DEFAULTLAYERCOLOR);
if (l->image == nil) { werrstr("newlayer: %r");return -1;
@@ -67,6 +73,15 @@
return 0;
}
+void
+setcurrentlayer(Layer * l)
+{+ if (l == nil)
+ return;
+ ntfprint("switched to layer %d", l->id);+ CurrentLayer = l;
+}
+
Layer *
newlayer(void)
{@@ -77,10 +92,13 @@
l = calloc(1, sizeof(Layer));
if (l == nil)
- sysfatal("newlayer: failed to allocate layer");+ sysfatal("calloc: %r");l->changed = 1;
+
if (newlayerimage(l))
sysfatal("newlayerimage: %r");+
+ l->id = LayerIDCounter++;
if (RootLayer == nil) {RootLayer = l;
CurrentLayer = l;
@@ -89,11 +107,40 @@
;
e->next = l;
l->prev = e;
+ ntfprint("create new layer %d", l->id);}
return l;
}
void
+shownotification(void)
+{+ if (NotificationImage == nil)
+ if ((NotificationImage = allocimage(display, RECTANGLE(1, 1), RGBA32, 1, NOTIFICATIONFONTCOLOR)) == nil)
+ sysfatal("allocimage: %r");+ if (NotificationBackgroundImage == nil)
+ if ((NotificationBackgroundImage = allocimage(display, RECTANGLE(1, 1), RGBA32, 1, NOTIFICATIONBACKGROUNDCOLOR)) == nil)
+ sysfatal("allocimage: %r");+ if (NotificationString == nil)
+ return;
+ stringbg(screen, screen->r.min, NotificationImage, ZP, font, NotificationString, NotificationBackgroundImage, ZP);
+}
+
+void
+ntfprint(char * fmt, ...)
+{+ va_list arg;
+
+ if (NotificationString != nil)
+ free(NotificationString);
+
+ va_start(arg, fmt);
+ NotificationString = vsmprint(fmt, arg);
+ va_end(arg);
+ shownotification();
+}
+
+void
drawcanvas(void)
{int sr;
@@ -100,10 +147,11 @@
Rectangle vr;
sr = 0;
+
if (ViewImage == nil) {- ViewImage = allocimage(display, CanvasSize, RGBA32, 1, DEFAULTLAYERCOLOR);
+ ViewImage = allocimage(display, CanvasSize, RGBA32, 1, DEFAULTCANVASBACKGROUNDCOLOR);
if (ViewImage == nil)
- sysfatal("v allocimage: %r");+ sysfatal("allocimage: %r");sr = 1;
}
@@ -116,7 +164,7 @@
for (Layer * l = RootLayer; l != nil; l = l->next)
if (sr || l->changed || DrawAllLayers) {- draw(ViewImage, ViewImage->r, l->image, nil, ZP);
+ draw(ViewImage, ViewImage->r, l->image, nil, l->offset);
l->changed = 0;
sr = 1;
}
@@ -135,10 +183,9 @@
draw(screen, screen->r, Background, nil, ZP);
draw(screen, RECTOPPT(ZoomedSize, screentoglobalatcanvaspt), ZoomedImage, nil, ZP);
}
-
+ shownotification();
CanvasMoved = 0;
DrawAllLayers = 0;
- DrawOverlay = 0;
}
ulong
@@ -214,7 +261,7 @@
);
}
-static void
+void
applyzoom(void)
{Zoom = TargetZoom;
@@ -226,6 +273,7 @@
DrawAllLayers = 1;
ZoomTaskId = -1;
+ ntfprint("zoom set to %d", Zoom);}
void
@@ -234,17 +282,15 @@
Rectangle zs;
Point ca;
- if ((TargetZoom + z) <= 1)
+ if ((TargetZoom + z) <= 1 || z == 0)
TargetZoom = 1;
else
TargetZoom += z;
+ if (TargetZoom >= 9)
+ TargetZoom = 9;
+
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) {@@ -254,21 +300,19 @@
}
void
-movecanvas()
-{-}
-
-void
stroke()
{Point t, f;
- Rectangle viewAtCanvas = {0};+ Layer * l;
+ Rectangle viewAtCanvas, offsetView;
f = PastMousePosition;
t = MousePosition;
+ l = CurrentLayer;
viewAtCanvas = rectaddpt(ZoomedSize, CanvasAt);
- if (!ptinrect(t, viewAtCanvas))
+ offsetView = rectsubpt(viewAtCanvas, mulpt(l->offset, Zoom));
+ if (!ptinrect(t, viewAtCanvas) || !ptinrect(t, offsetView))
return;
if (!ptinrect(f, viewAtCanvas))
@@ -276,14 +320,19 @@
if (BrushImage == nil)
setbrushcolor(DBlack);
-
+
+ // Draw on the image layer
f = screentocanvaspt(subpt(f, viewAtCanvas.min));
t = screentocanvaspt(subpt(t, viewAtCanvas.min));
- line(CurrentLayer->image, f, t, STROKEEND, STROKEEND, 1, BrushImage, ZP);
+ line(l->image, addpt(f, l->offset), addpt(t, l->offset), STROKEEND, STROKEEND, 1, BrushImage, ZP);
+
+ // Draw on the zoomed image
f = mulpt(f, Zoom);
t = mulpt(t, Zoom);
if (ZoomedImage != nil)
- line(ZoomedImage, f, t, STROKEEND, STROKEEND, 1*Zoom, BrushImage, ZP);
+ line(ZoomedImage, addpt(f, l->offset), addpt(t, l->offset), STROKEEND, STROKEEND, 1*Zoom, BrushImage, ZP);
+
+ // Draw on the screen
f = screentoglobalatcanvaspt(f);
t = screentoglobalatcanvaspt(t);
line(screen, f, t, STROKEEND, STROKEEND, 1*Zoom, BrushImage, ZP);
@@ -337,14 +386,21 @@
l == RootLayer ? DEFAULTLAYERCOLOR : DTransparent)) == nil)
sysfatal("clearlayer: %r");l->changed = 1;
- if (ViewImage != nil) {- freeimage(ViewImage);
- ViewImage = nil;
- }
DrawAllLayers = 1;
+ clearview();
}
void
+clearview(void)
+{+ if (ViewImage == nil)
+ return;
+
+ freeimage(ViewImage);
+ ViewImage = nil;
+}
+
+void
setbrushimage(Image * i)
{if (BrushImage == nil)
@@ -378,15 +434,6 @@
}
}
-void
-setoverlaysize(Rectangle r)
-{- if (OverlayImage != nil)
- freeimage(OverlayImage);
- OverlayImage = allocimage(display, r, RGBA32, 1, DTransparent);
- DrawOverlay = 1;
-}
-
static void
handlekeyboard(int kbdc)
{@@ -413,6 +460,13 @@
}
void
+centercanvas(void)
+{+ CanvasAt = addpt(CanvasAt, Pt(Dx(screen->r)/2, Dy(screen->r)/2));
+ CanvasAt = subpt(CanvasAt, Pt((Dx(ZoomedSize)/2), (Dy(ZoomedSize)/2)));
+}
+
+void
main(int argc, char * argv[])
{ulong d;
@@ -432,13 +486,13 @@
if ((BorderImage = allocimage(display, RECTANGLE(1, 1), RGBA32, 1, DBlack)) == nil)
sysfatal("allocimage: %r");-
- setoverlaysize(screen->r);
- CanvasSize = ZoomedSize = DEFAULTCANVASSIZE;
- setcanvassize(DEFAULTCANVASSIZE);
+
+ setcanvassize(RECTANGLE(Dx(screen->r), Dy(screen->r)));
+ ZoomedSize = CanvasSize;
newlayer();
applyzoom();
setdefaultbindings();
+ centercanvas();
einit(Emouse | Ekeyboard);
etimer(0, UPDATEPERIOD);
--
⑨