shithub: pain

Download patch

ref: 1b81533742fa32d4ba26f7fcacb942becfd89c50
author: jmq <jmq@jmq.sh>
date: Sun Nov 17 21:23:34 EST 2024

initial commit

--- /dev/null
+++ b/bindings.c
@@ -1,0 +1,131 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <event.h>
+
+#include "dat.h"
+#include "fns.h"
+
+Binding * BindingRoot = nil;
+
+typedef struct DefaultBinding
+{
+	int type;
+	int code;
+	char * command;
+	void (*func)(void);
+} DefaultBinding;
+
+DefaultBinding DefaultBindings[] = 
+{
+	{BKeyboard, 'q', nil, quitloop},
+	{BMouse, 1, nil, stroke}
+};
+
+void
+freebinding(Binding * b)
+{
+	if (b == nil)
+		return;
+	if (b->command != nil)
+		free(b->command);
+
+	free(b);
+}
+
+int
+removebindings(int type, int code)
+{
+	int rc;
+	Binding * e, * n;
+	
+	rc = 0;
+	for (e = BindingRoot; e != nil; e = n) {
+		if (e->type != type || e->code != code) {
+			n = e->next;
+			continue;
+		}
+		if (e->prev != nil)
+			e->prev->next = e->next;
+		if (e->next != nil)
+			e->next->prev = e->prev;
+		n = e->next;
+		freebinding(e);
+		if (e == BindingRoot)
+			BindingRoot = nil;
+		rc++;
+	}
+	
+	return rc;
+}
+
+Binding *
+addbinding(int type, int code)
+{
+	Binding * b, * e;
+
+	if ((b = calloc(sizeof(Binding), 1)) == nil) {
+		werrstr("failed to allocate Binding");
+		return nil;
+	}
+	b->next = b->prev = nil;
+	
+	b->type = type;
+	b->code = code;
+	if (BindingRoot != nil) {
+		for (e = BindingRoot; e->next != nil; e = e->next)
+			;
+		e->next = b;
+		b->prev = e;
+	} else
+		BindingRoot = b;
+
+	return b;
+}
+
+int
+runbindings(int type, int code)
+{
+	int rc;
+	Binding * b;
+	// print("handling bindings for %d %d\n", type,code);
+
+	rc = 0;
+	for (b = BindingRoot; b != nil; b = b->next) {
+		if (b->type != type || b->code != code)
+			continue;
+		
+		if (b->func != nil)
+			b->func();
+		if (b->command != nil)
+			NOOP();	// TODO: Add shell command support
+		rc++;
+	}
+	return rc;
+}
+
+Binding *
+adduniquebinding(int type, int code)
+{
+	removebindings(type, code);
+	return addbinding(type, code);
+}
+
+void
+setdefaultbindings()
+{
+	int i;
+	Binding * b;
+	DefaultBinding * db;
+	
+	for (i = 0; i < ARRLEN(DefaultBindings); i++) {
+		db = &DefaultBindings[i];
+		if ((b = addbinding(db->type, db->code)) == nil)
+			sysfatal("addbinding: %r");
+	
+		if (db->command != nil)
+			b->command = strdup(db->command);
+		if (db->func != nil)
+			b->func = db->func;
+	}
+}
\ No newline at end of file
--- /dev/null
+++ b/dat.h
@@ -1,0 +1,30 @@
+typedef struct Layer
+{
+ 	Image * image;
+ 	struct Layer * prev, * next;
+	int changed;
+} Layer;
+
+enum
+{
+	BMouse,
+	BKeyboard
+};
+
+typedef struct Binding
+{
+	struct Binding * prev, * next;
+	int code;
+	int type;
+	char * command;
+	void (*func)(void);
+} Binding;
+
+// extern Rectangle CanvasSize = {0};
+// extern Point CanvasAt = {0};
+extern Layer * LayerRoot = nil;
+extern Binding * BindingRoot = nil;
+extern int RunLoop = 0;
+// extern Layer * CurrentLayer = nil;
+// extern Image * Background = nil;
+// extern Image * Canvas = nil;
--- /dev/null
+++ b/events.c
@@ -1,0 +1,71 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <thread.h>
+#include <mouse.h>
+#include <keyboard.h>
+#include <cursor.h>
+
+#define EventsReady()	(keyboardctl != nil && mousectl != nil)
+#define CheckEventsReady() if(!EventsReady()) { werrstr(Enotready); return -1; }
+
+#include "dat.h"
+#include "fns.h"
+
+static Events events = {0};
+static Keyboardctl * keyboardctl = nil;
+static Mousectl * mousectl = nil;
+static char * Enotready = "events not initialized";
+
+int
+eventsread(Events * events)
+{
+	Rune k[20];
+
+	CheckEventsReady();
+	if (events == nil) {
+		werrstr("events must not be nil");
+		return -1;
+	}
+
+// 	if (keyboardctl->c != nil || recv(keyboardctl->c, k))
+// 		return -1;
+
+	events->keys = k[0];
+
+	if (readmouse(mousectl) < 0)
+		return -1;
+	events->mouse = *mousectl;
+
+	return 1;
+}
+
+void
+eventsclose()
+{
+	if (keyboardctl != nil)
+		closekeyboard(keyboardctl);
+	keyboardctl = nil;
+	
+	if (mousectl != nil)
+		closemouse(mousectl);
+	mousectl = nil;
+}
+
+int
+eventsinit(void)
+{
+	if (keyboardctl != nil && mousectl != nil)
+		return -1;
+	
+	if ((mousectl = initmouse(nil, screen)) == nil)
+		return -1;
+	if ((keyboardctl = initkeyboard(nil)) == nil ||
+		(ctlkeyboard(keyboardctl, "rawon")) < 0) {
+		eventsclose();
+		return -1;
+	}
+	
+
+	return 1;
+}
\ No newline at end of file
--- /dev/null
+++ b/fns.h
@@ -1,0 +1,35 @@
+#define BIT(b)	(0x01 << (b))
+#define RECTANGLE(w, h) ((Rectangle){(Point){(0),(0)}, (Point){(w),(h)}})
+#define NOOP() ((void)(nil))
+#define ARRLEN(a) ((sizeof(a))/(sizeof(a[0])))
+
+ulong msec(void);
+void setbackground(ulong col);
+void drawcanvas(void);
+void zoom(int);
+void stroke(void);
+void clearlayer(Layer *);
+void setbrushcolor(ulong col);
+int resizeimage(Image *, Rectangle, int, Image *, Point);
+
+Point globaltoscreenpt(Point p);
+Point globaltoscreenatcanvaspt(Point p);
+Point screentocanvaspt(Point p);
+Point canvastoscreenpt(Point p);
+Point screentoglobalpt(Point p);
+Point screentoglobalatcanvaspt(Point p);
+
+// fs
+int fsinit(char *);
+void fsclose(void);
+
+// bindings
+void freebinding(Binding * b);
+int removebindings(int, int);
+Binding * addbinding(int, int);
+int runbindings(int, int);
+Binding * adduniquebinding(int, int);
+void setdefaultbindings(void);
+
+// utils
+void quitloop(void);
--- /dev/null
+++ b/fs.c
@@ -1,0 +1,86 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include <draw.h>
+#include <mouse.h>
+
+#include "dat.h"
+#include "fns.h"
+
+static void fsopen(Req * req);
+static void fsread(Req * req);
+static void fswrite(Req * req);
+static void fswstat(Req * req);
+static void fscreate(Req * req);
+static void fsdestroyfid(Fid * req);
+static void fsstart(Srv * srv);
+static void fsdetroyfile(File * f);
+
+static char srvpath[128] = {0};
+
+static Srv fs = {
+	.open =			fsopen,
+	.read =			fsread,
+	.write = 		fswrite,
+	.wstat = 		fswstat,
+	.create = 		fscreate,
+	.destroyfid = 	fsdestroyfid,
+	.start = 		fsstart,
+};
+
+static void fsopen(Req * req) {
+	respond(req, nil);
+}
+static void fsread(Req * req) {
+	respond(req,nil);
+}
+static void fswrite(Req * req) {
+	respond(req,nil);
+}
+static void fswstat(Req * req) {
+	respond(req,nil);
+}
+static void fscreate(Req * req) {
+	respond(req,nil);
+}
+static void fsdestroyfid(Fid * req) {
+	USED(req);
+}
+static void fsstart(Srv * srv) {
+	USED(srv);
+}
+static void fsdestroyfile(File * f) {
+	USED(f);
+}
+
+void
+fsclose(void)
+{
+	if (srvpath[0] == 0)
+		remove(srvpath);
+	srvpath[0] = 0;
+}
+
+int
+fsinit(char * srvname)
+{
+	int rc;
+
+	fs.tree = alloctree(nil, nil, DMDIR|0755, fsdestroyfile);
+	if (fs.tree == nil) {
+		return -1;
+	}
+
+	snprint(srvpath, sizeof(srvpath), "/srv/%s", srvname);
+	remove(srvpath);
+
+	rc = postsrv(&fs, srvname);
+	if (rc == 0) {
+		exits(nil);
+	} else
+		return rc;
+}
+
--- /dev/null
+++ b/main.c
@@ -1,0 +1,358 @@
+#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)
+
+int CanvasMoved = 1;
+Rectangle CanvasSize;
+Rectangle ZoomedSize;
+Point CanvasAt = {0};
+Layer * RootLayer = nil;
+Layer * CurrentLayer = nil;
+Image * Background = nil;
+Image * ClearImage = nil;
+int CurrentKeyboard = 0;
+Image * BrushImage = nil;
+Image * ViewImage = nil;
+Image * ZoomedImage = nil;
+ulong LastUpdate = 0;
+int DrawAllLayers = 0;
+int Zoom = 1;
+int ZoomSensitivity = 1;
+Mouse CurrentMouse = {0};
+Mouse PastMouse = {0};
+Point MousePosition = {0};
+Point PastMousePosition = {0};
+int RunLoop = 1;
+
+Layer *
+newlayer(ulong col)
+{
+	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;
+	l->image = allocimage(display, CanvasSize, RGBA32, 1, col);
+	if (l->image == nil)
+		sysfatal("newlayer: %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;
+		}
+
+	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) {
+		draw(screen, screen->r, Background, nil, ZP);
+		draw(screen, RECTOPPT(ZoomedSize, screentoglobalatcanvaspt), ZoomedImage, nil, ZP);
+	}
+	CanvasMoved = 0;
+	DrawAllLayers = 0;
+}
+
+ulong
+msec(void)
+{
+	return (ulong)(nsec()/1000);
+}
+
+void
+setbackground(ulong col)
+{	
+	if (Background != nil)
+		freeimage(Background);
+
+	Background = allocimage(display, RECTANGLE(1, 1), RGBA32, 1, col);
+	if (Background == nil)
+		sysfatal("setbackground: %r");
+}
+
+void
+zoom(int z)
+{
+	int mc;
+	int osx, osy;
+	Point c;
+	
+	mc = Zoom > 1;
+	if ((Zoom + z) <= 1)
+		Zoom = 1;
+	else
+		Zoom += z;
+ 
+	c = subpt(CanvasAt, MousePosition);
+getsize:
+	osx = Dx(ZoomedSize);
+	osy = Dy(ZoomedSize);
+	
+	if ((osx == 0 || osy == 0) && 
+		Dx(CanvasSize) != 0 && Dy(CanvasSize) != 0) {
+		ZoomedSize = CanvasSize;
+		goto getsize;
+	}
+
+	ZoomedSize.min = mulpt(ZoomedSize.min, Zoom);
+	ZoomedSize.max = mulpt(ZoomedSize.max, Zoom);
+	
+	if (ZoomedImage != nil) {
+		freeimage(ZoomedImage);
+		ZoomedImage = nil;
+	}
+	if (mc || Zoom > 1)
+		CanvasAt = addpt(
+			Pt(Dx(ZoomedSize)*c.x/osx, Dy(ZoomedSize)*c.y/osy), 
+			MousePosition
+		);
+ 	DrawAllLayers = 1;
+}
+
+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, Enddisc, Enddisc, 1, BrushImage, ZP);
+	f = mulpt(f, Zoom);
+	t = mulpt(t, Zoom);
+	if (ZoomedImage != nil)
+		line(ZoomedImage, f, t, Enddisc, Enddisc, 1*Zoom, BrushImage, ZP);
+	f = screentoglobalpt(f);
+	t = screentoglobalpt(t);
+	line(screen, f, t, Enddisc, Enddisc, 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);
+	}
+}
+
+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[])
+{
+	int d;
+	Event e;
+
+	USED(argc);
+	USED(argv);
+
+	if (initdraw(nil, nil, "pain") < 0)
+		sysfatal("initdraw: %r\n");
+
+	if (fsinit("pain") < 1)
+		sysfatal("fsinit: %r");
+
+	if (!atexit(fsclose))
+		sysfatal("atexit: %r");
+	
+	setcanvassize(DEFAULTCANVASSIZE);
+	newlayer(DEFAULTLAYERCOLOR);
+	zoom(0);
+	setdefaultbindings();
+
+	einit(Emouse | Ekeyboard);
+	drawcanvas();
+	for(;RunLoop;) {
+		d = msec()-LastUpdate;
+	 	if (d <= UPDATEPERIOD)
+	 		sleep(d - UPDATEPERIOD);
+
+		switch (event(&e))
+		{
+		case Ekeyboard: 
+			handlekeyboard(e.kbdc);
+			break;
+		case Emouse:
+			handlemouse(e.mouse);
+			break; 
+		}
+
+		drawcanvas();
+		LastUpdate = msec();
+	}
+
+	return;
+}
\ No newline at end of file
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,14 @@
+< /$objtype/mkfile
+
+TARG=pain
+OFILES=			\
+	main.$O 	\
+	fs.$O		\
+	resize.$O	\
+	bindings.$O	\
+	utils.$O
+
+HFILES=fns.h dat.h
+BIN=$home/bin/$objtype
+
+< /sys/src/cmd/mkone
--- /dev/null
+++ b/resize.c
@@ -1,0 +1,95 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+#include "dat.h"
+#include "fns.h"
+
+// Inspired by /sys/src/games/doom/i_video.c:/^convproc
+// and /sys/src/cmd/page.c:/^zoomdraw
+// Thanks sigrid!
+int
+resizeimage(Image * di, Rectangle dr, int scale, Image * si, Point sp)
+{
+	int i, c, sh;
+	Image * t, * ci;
+
+	if (scale < 1) {
+		werrstr("scale < 1");
+		return -1;
+	} else if (scale == 1) {
+		draw(di, dr, si, nil, addpt(sp, dr.min));
+		return 1;
+	}
+
+	sh = Dy(si->r);
+	if ((t = allocimage(display, dr, si->chan, 1, DNofill)) == nil) {
+		werrstr("allocimage: %r");
+		return -1;
+	}
+
+	if ((ci = allocimage(display, Rect(0, 0, 1, sh), si->chan, 1, DNofill)) == nil) {
+		werrstr("allocimage: %r");
+		freeimage(t);
+		return -1;
+	}
+
+	for (i = dr.min.x; i < dr.max.x; i+=scale) {
+		draw(ci, ci->r, si, nil, addpt(sp, Pt(i/scale, dr.min.y/scale)));
+		draw(t, Rect(i, dr.min.y, i + scale, dr.max.y), ci, nil, ZP);
+	}
+	freeimage(ci);
+	
+	if ((ci = allocimage(display, Rect(0, 0, Dx(dr), 1), si->chan, 1, DNofill)) == nil) {
+		werrstr("allocimage: %r");
+		freeimage(t);
+		return -1;
+	}
+	c = dr.min.y;
+ 	for (i = dr.min.y; i < dr.max.y; i+=scale) {
+		draw(ci, ci->r, t, nil, Pt(dr.min.x, c++));
+ 		draw(di, Rect(dr.min.x, i, dr.max.x, i + scale), ci, nil, ZP);
+	}
+
+	freeimage(t);
+	freeimage(ci);
+
+// 	x = dr.min.y;
+// 	for (i = sp.y; i < sh; i++) 
+// 		draw(di, Rect(dr.min.x, x, dr.max.x, (x += scale)), t, nil, dr.min);
+// 	freeimage(t);
+
+// 	
+// 	// Draw the cursor for vertical line
+//  	if (t == nil || ) {
+//  		if (t != nil)
+//  			freeimage(t);
+//  	 	t = allocimage(display, Rect(0, 0, sw*scale, sh), si->chan, 1, DNofill);
+//  	 	if (t == nil) {
+//  	 		return -1;
+//  	 	}
+//  	}
+
+// 	// print("min.x = %d, min.y = %d, max.x = %d, max.y = %d\n",
+// 	//	clipr.min.x, clipr.min.y, clipr.max.x, clipr.max.y);
+// 	r = Rect(0, 0, 1, sh);
+// 	for (i = 0; i < sw; i++) {
+// 		cr = Rect(i*scale, 0, (i+1)*scale, r.max.y);
+// 		if (!rectclip(&cr, clipr))
+// 			continue;
+// 		draw(t, cr, si, nil, Pt(i, 0));
+// 	}
+
+//  	// Draw the cursor for vertical lines
+// 	cr = di->clipr;
+//     r = Rect(0, 0, sw*scale, 1);
+//    	for (i = 0; i < sh; i++) {
+// 		cr = Rect(0, i*scale, r.max.x, (i+1)*scale);
+// 		if (!rectclip(&cr, clipr))
+// 			continue;
+//    		draw(di, cr, t, nil, Pt(0, i));
+// 	}
+
+
+	return 1;
+}
--- /dev/null
+++ b/utils.c
@@ -1,0 +1,12 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+#include "dat.h"
+#include "fns.h"
+
+void
+quitloop(void)
+{
+	RunLoop = 0;
+}
--