shithub: guifs

Download patch

ref: 3a0e0b8bb6e7fd9bcf510167980d9715e3faeb7b
parent: 0ad54d136ef33d61172658e97b538f7cd6a91c26
author: Peter Mikkelsen <petermikkelsen10@gmail.com>
date: Thu Feb 15 15:39:09 EST 2024

Even more work :)

--- a/graphics.c
+++ b/graphics.c
@@ -7,6 +7,7 @@
 
 #include "guifs.h"
 
+Point mousexy;
 Mousectl *mouse;
 Keyboardctl *keyboard;
 Channel *updatechan;
@@ -21,7 +22,7 @@
 
 	if(memcmp(&g->rect, &g->border, sizeof(Rectangle)) != 0 && Dx(g->border) > 0 && Dy(g->border) > 0){
 		/* draw the border first */
-		Image *bc = getprop(g, Pbordercolour).colour->image;
+		Image *bc = getprop(g, Pbordercolour, 1).colour->image;
 		Rectangle r;
 
 		/* top part */
@@ -55,7 +56,7 @@
 
 	if(Dx(g->rect) > 0 && Dy(g->rect) > 0){
 		/* Draw the background  */
-		Image *bg = getprop(g, Pbackground).colour->image;
+		Image *bg = getprop(g, Pbackground, 1).colour->image;
 		draw(screen, g->rect, bg, nil, ZP);
 
 		spec.draw(g);
@@ -75,8 +76,8 @@
 void
 drawtextbox(GuiElement *g)
 {
-	Rune *text = getprop(g, Ptext).text;
-	Image *fg = getprop(g, Ptextcolour).colour->image;
+	Rune *text = getprop(g, Ptext, 1).text;
+	Image *fg = getprop(g, Ptextcolour, 1).colour->image;
 	
 
 	runestring(screen, g->content.min, fg, ZP, font, text);
@@ -116,6 +117,8 @@
 {
 	int i;
 	ulong c;
+	Rune r;
+
 	if(initdraw(nil, nil, "guifs") < 0)
 		sysfatal("initdraw failed");
 
@@ -127,25 +130,28 @@
 	enum {
 		Aupdategui,
 		Aresize,
-		Amouse,
 		Amkcolour,
+		Amouse,
+		Akeyboard,
 		Aaltend,
 	};
-	Alt a[] = {
+	Alt alts[] = {
 		[Aupdategui] =
 			{updatechan, &i, CHANRCV},
 		[Aresize] =
 			{mouse->resizec, nil, CHANRCV},
-		[Amouse] =
-			{mouse->c, &mouse->Mouse, CHANRCV},
 		[Amkcolour] =
 			{mkcolourchan, &c, CHANRCV},
+		[Amouse] =
+			{mouse->c, &mouse->Mouse, CHANRCV},
+		[Akeyboard] =
+			{keyboard->c, &r, CHANRCV},
 		[Aaltend] =
 			{nil, nil, CHANEND},
 	};
 
 	while(1){
-		int which = alt(a);
+		int which = alt(alts);
 
 		switch(which){
 		case Aupdategui:
@@ -154,16 +160,27 @@
 		case Aresize:
 			resized(1);
 			break;
-		case Amouse:
-			break;
 		case Amkcolour:
 			{
-	
 				Colour *col = emalloc(sizeof(Colour));
 				col->image = allocimage(display, Rect(0,0,1,1), screen->chan, 1, c);
 				col->code = c;
 				send(newcolourchan, &col);
 			}
+			break;
+		case Amouse:
+			mousexy = mouse->Mouse.xy;
+			if(!root)
+				break;
+			if(mouseevent(mouse->Mouse.buttons))
+				resized(0);
+			break;
+		case Akeyboard:
+			if(!root)
+				break;
+			if(keyboardevent(r))
+				resized(1);
+			break;
 		}
 	}
 }
--- a/guifs.h
+++ b/guifs.h
@@ -30,6 +30,7 @@
 typedef union PropVal PropVal;
 typedef struct PropSpec PropSpec;
 typedef struct Prop Prop;
+typedef struct Event Event;
 typedef struct GuiSpec GuiSpec;
 typedef struct GuiElement GuiElement;
 
@@ -65,6 +66,10 @@
 	Qid qid;
 };
 
+struct Event {
+	Rune r;
+};
+
 struct GuiSpec {
 	char *name;
 	void (*draw)(GuiElement *);
@@ -85,6 +90,7 @@
 	Qid qevent;
 	Qid qtype;
 	Qid qprops;
+	Qid qwait;
 
 	int nchildren;
 	GuiElement **children;
@@ -94,11 +100,16 @@
 	int nprops;
 	Prop *props;
 
+	int listening; /* the user is reading from the 'event' file */
+	Channel *events;
+	char *currentevents;
+
 	Rectangle border;
 	Rectangle rect;
 	Rectangle content;
 };
 
+extern Point mousexy;
 extern GuiElement *root;
 extern PropSpec propspecs[Pmax];
 extern GuiSpec guispecs[Gmax];
@@ -117,5 +128,8 @@
 void layoutcontainer(GuiElement *, Rectangle);
 void layouttextbox(GuiElement *, Rectangle);
 
-PropVal getprop(GuiElement *, int);
-void setprop(GuiElement *, int, PropVal);
\ No newline at end of file
+PropVal getprop(GuiElement *, int, int);
+void setprop(GuiElement *, int, PropVal, int);
+
+int mouseevent(int);
+int keyboardevent(Rune);
\ No newline at end of file
--- a/guispec.c
+++ b/guispec.c
@@ -1,6 +1,7 @@
 #include <u.h>
 #include <libc.h>
 #include <draw.h>
+#include <thread.h>
 
 #include "guifs.h"
 
--- a/layout.c
+++ b/layout.c
@@ -1,6 +1,7 @@
 #include <u.h>
 #include <libc.h>
 #include <draw.h>
+#include <thread.h>
 
 #include "guifs.h"
 
@@ -19,9 +20,9 @@
 {
 	GuiSpec spec = guispecs[g->type];
 
-	Spacing *margin = getprop(g, Pmargin).spacing;
-	Spacing *border = getprop(g, Pborder).spacing;
-	Spacing *padding = getprop(g, Ppadding).spacing;
+	Spacing *margin = getprop(g, Pmargin, 1).spacing;
+	Spacing *border = getprop(g, Pborder, 1).spacing;
+	Spacing *padding = getprop(g, Ppadding, 1).spacing;
 
 	/* Subtract margin to get the outer border rect */
 	Rectangle r1 = subspacing(r0, margin);
@@ -49,7 +50,7 @@
 	if(g->nchildren == 0)
 		return;
 
-	int orientation = getprop(g, Porientation).orientation;
+	int orientation = getprop(g, Porientation, 1).orientation;
 
 	int dx = 0;
 	int dy = 0;
--- a/main.c
+++ b/main.c
@@ -12,6 +12,7 @@
 #define Eperm	"permission denied"
 #define Eoffset	"can't write to this file at non-zero offset"
 #define Ebadctl	"bad ctl message"
+#define Einuse	"file in use"
 
 QLock guilock = 0;
 
@@ -22,6 +23,7 @@
 	Qclone,
 	Qevent,
 	Qtype,
+	Qwait,
 	Qprops,
 
 	Qprop,
@@ -31,10 +33,34 @@
 	Fclone,
 	Fevent,
 	Ftype,
+	Fwait,
 	Fprops,
 	Fmax,
 };
 
+void fsattach(Req *);
+char *fswalk1(Fid *, char *, Qid *);
+char *fsclone(Fid *, Fid *);
+void fsopen(Req *);
+void fsstat(Req *);
+void fsread(Req *);
+void fswrite(Req *);
+void fsforker(void (*)(void*), void *, int);
+void fsdestroyfid(Fid *);
+
+Srv fs = {
+	.attach = fsattach,
+	.walk1 = fswalk1,
+	.clone = fsclone,
+	.open = fsopen,
+	.stat = fsstat,
+	.read = fsread,
+	.write = fswrite,
+
+	.destroyfid = fsdestroyfid,
+	.forker = fsforker,
+};
+
 GuiElement *root;
 
 #define QID_TYPE(q)	((q.path) & 0xFF)
@@ -75,6 +101,7 @@
 	case Qclone:
 	case Qevent:
 	case Qtype:
+	case Qwait:
 		q.type = QTFILE;
 		break;
 	}
@@ -132,7 +159,10 @@
 	g->qevent = mkqid(Qevent);
 	g->qtype = mkqid(Qtype);
 	g->qprops = mkqid(Qprops);
+	g->qwait = mkqid(Qwait);
 
+	g->events = chancreate(sizeof(char *), 0);
+
 	if(parent){
 		g->id = parent->nchildren;
 		wlock(&parent->lock);
@@ -170,6 +200,7 @@
 		GuiElement *g = newgui(nil);
 		root = g;
 		settype(g, Gcontainer);
+		updategui(1);
 	}
 
 	r->fid->aux = root;
@@ -208,6 +239,8 @@
 			*qid = g->qtype;
 		else if(strcmp(name, "props") == 0)
 			*qid = g->qprops;
+		else if(strcmp(name, "wait") == 0)
+			*qid = g->qwait;
 		else if(child = findchild(g, name)){
 			fid->aux = child;
 			*qid = child->qid;
@@ -250,6 +283,7 @@
 fsopen(Req *r)
 {
 	GuiElement *g = r->fid->aux;
+	char *err = nil;
 
 	switch(QID_TYPE(r->fid->qid)){
 	case Qdir:
@@ -256,9 +290,10 @@
 	case Qevent:
 	case Qclone:
 	case Qprops:
+	case Qwait:
 		if(r->ifcall.mode != OREAD){
-			respond(r, Eperm);
-			return;
+			err = Eperm;
+			goto Lend;
 		}
 		break;
 	}
@@ -276,7 +311,18 @@
 		r->ofcall.qid = g->qclone;
 	}
 
-	respond(r, nil);
+	if(QID_TYPE(r->fid->qid) == Qevent){
+		wlock(&g->lock);
+		if(g->listening){
+			err = Einuse;
+			r->fid->aux = nil;
+		}else
+			g->listening = 1;
+		wunlock(&g->lock);
+	}
+
+Lend:
+	respond(r, err);
 }
 
 void
@@ -324,7 +370,7 @@
 			d->qid = g->qclone;
 			break;
 		case Fevent:
-			d->mode = 0444;
+			d->mode = 0444|DMEXCL;
 			d->name = estrdup9p("event");
 			d->qid = g->qevent;
 			break;
@@ -333,6 +379,11 @@
 			d->name = estrdup9p("type");
 			d->qid = g->qtype;
 			break;
+		case Fwait:
+			d->mode = 0444;
+			d->name = estrdup9p("wait");
+			d->qid = g->qwait;
+			break;
 		case Fprops:
 			d->mode = 0555|DMDIR;
 			d->name = estrdup9p("props");
@@ -401,10 +452,47 @@
 		readstr(r, buf);
 		break;
 	case Qevent:
-		/* in another thread, wait for events on a channel
-		 * and call readstr on each of them individually.
-		 */
-		readstr(r, "eveeent\n");
+		{
+			/* get all the messages we can, and add them to g->currentevents */
+			char *event;
+			int mustrecv = 0;
+			ulong currentsize;
+			ulong eventsize;
+Lretry:
+			srvrelease(&fs);
+			if(mustrecv){
+				recv(g->events, &event);
+				goto Lgotevent;
+			}
+
+			while(nbrecv(g->events, &event)){
+Lgotevent:			currentsize = g->currentevents ? strlen(g->currentevents) : 0;
+				eventsize = strlen(event);
+
+				wlock(&g->lock);
+				g->currentevents = erealloc(g->currentevents, currentsize+eventsize+1);
+				memcpy(g->currentevents+currentsize, event, eventsize);
+				g->currentevents[currentsize+eventsize] = 0;
+				wunlock(&g->lock);
+				free(event);
+			}
+
+			rlock(&g->lock);
+			if(g->currentevents == nil){
+				runlock(&g->lock);
+				recv(g->events, &event);
+				goto Lgotevent;
+			}else{
+				srvacquire(&fs);
+				readstr(r, g->currentevents);
+				if(r->ofcall.count == 0){
+					runlock(&g->lock);
+					mustrecv = 1;
+					goto Lretry;
+				}
+			}
+			runlock(&g->lock);
+		}
 		break;
 	case Qtype:
 		rlock(&g->lock);
@@ -412,6 +500,9 @@
 		runlock(&g->lock);
 		readstr(r, buf);
 		break;
+	case Qwait:
+		/* intentionally left blank */
+		return;
 	case Qprops:
 		dirread9p(r, proptreegen, g);
 		break;
@@ -419,7 +510,7 @@
 		{
 			int tag = QID_PROP(r->fid->qid);
 			PropSpec spec = propspecs[tag];
-			PropVal val = getprop(g, tag);
+			PropVal val = getprop(g, tag, 1);
 			char *str = spec.print(val);
 			readstr(r, str);
 			free(str);
@@ -474,7 +565,7 @@
 			memcpy(buf, r->ifcall.data, r->ifcall.count);
 			err = spec.parse(buf, &val);
 			if(err == nil)
-				setprop(g, tag, val);
+				setprop(g, tag, val, 1);
 			free(buf);
 		}
 	}
@@ -482,20 +573,31 @@
 	respond(r, err);
 }
 
-Srv fs = {
-	.attach = fsattach,
-	.walk1 = fswalk1,
-	.clone = fsclone,
-	.open = fsopen,
-	.stat = fsstat,
-	.read = fsread,
-	.write = fswrite,
-};
+void
+fsforker(void (*fn)(void*), void *arg, int rflag)
+{
+	/* same as threadsrvforker, but stay in the same note group */
+	rflag &= ~RFNOTEG;
+	procrfork(fn, arg, 32*1024, rflag);
+}
 
 void
+fsdestroyfid(Fid *fid)
+{
+	GuiElement *g = fid->aux;
+
+	if(g != nil && QID_TYPE(fid->qid) == Qevent){
+		wlock(&g->lock);
+		g->listening = 0;
+		free(g->currentevents);
+		wunlock(&g->lock);
+	}
+}
+
+void
 usage(void)
 {
-	fprint(2, "usage: %s [-D] [-m mountpoint] [-s srvname] \n", argv0);
+	fprint(2, "usage: %s [-D] [-m mountpoint] [-s srvname] command\n", argv0);
 	exits("usage");
 }
 
@@ -518,11 +620,29 @@
 		usage();
 	}ARGEND;
 
-	if(argc > 1)
+	if(argc == 0)
 		usage();
 
 	username = getuser();
 	initgraphics();
 	threadpostmountsrv(&fs, srvname, mtpt, MREPL);
+
+	int pid = fork();
+	switch(pid){
+	case 0:		/* child process */
+		exec(argv[0], argv);
+		sysfatal("exec: %r");
+		break;
+	case -1:	/* error */
+		sysfatal("fork: %r");
+		break;
+	}
+
+	/* parent process */
+	int wpid;
+	do
+		wpid = waitpid();
+	while(wpid != -1 && wpid != pid);
+	postnote(PNGROUP, getpid(), "interrupt");
 	exits(nil);
 }
--- a/mkfile
+++ b/mkfile
@@ -7,6 +7,7 @@
 	guispec.$O\
 	graphics.$O\
 	layout.$O\
+	event.$O\
 
 BIN=$home/bin/$objtype
 
--- a/props.c
+++ b/props.c
@@ -226,14 +226,16 @@
 }
 
 PropVal
-getprop(GuiElement *g, int tag)
+getprop(GuiElement *g, int tag, int lock)
 {
 	PropVal *v = nil;
-	rlock(&g->lock);
+	if(lock)
+		rlock(&g->lock);
 	for(int i = 0; i < g->nprops && v == nil; i++)
 		if(g->props[i].tag == tag)
 			v = &g->props[i].val;
-	runlock(&g->lock);
+	if(lock)
+		runlock(&g->lock);
 
 	if(v == nil)
 		sysfatal("invalid prop for this gui element");
@@ -242,15 +244,18 @@
 }
 
 void
-setprop(GuiElement *g, int tag, PropVal val)
+setprop(GuiElement *g, int tag, PropVal val, int lock)
 {
-	wlock(&g->lock);
+	if(lock)
+		wlock(&g->lock);
 	/* TODO: free old propval */
 	for(int i = 0; i < g->nprops; i++)
 		if(g->props[i].tag == tag)
 			g->props[i].val = val;
-	wunlock(&g->lock);
-	updategui(0);
+	if(lock){
+		wunlock(&g->lock);
+		updategui(0); /* Can't update gui if the user has write-locked g */
+	}
 }
 
 PropSpec propspecs[Pmax] = {
--- a/test.rc
+++ b/test.rc
@@ -1,13 +1,8 @@
 #!/bin/rc
 
-rfork n
+delay=0.1
 
-delay=0.25
-
-guifs -s testgui
-
 dir=/mnt/gui
-sleep $delay
 
 # split the window vertically into two, and make the top one a textbox
 echo vertical >> $dir/props/orientation
@@ -62,6 +57,15 @@
 echo vertical >> $dir/props/orientation
 show 'echo vertical >> '^$dir/props/orientation
 
-# when the script ends, the old text window draws over the gui. I will fix that later.
-# For now, i just make the script sleep for a long time
-sleep 1000000
+fn printevents {
+	while(event = `''{read}){
+		show $1': '$event
+	}
+}
+
+# Attach an event printer to the innermost elements
+for(f in `{walk /mnt/gui | grep $dir'/[0-9]+/event'}){
+	printevents $f <$f >[2]/dev/null &
+}
+
+wait