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;
+}
--
⑨