shithub: pain

Download patch

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