shithub: guifs

Download patch

ref: 7aeae86d36a1a04e93bb4be2216cb735acfab714
parent: 7c6a945996a1d5510ff1412320ac7d07a0f82851
author: Peter Mikkelsen <[email protected]>
date: Sun Feb 11 10:18:27 EST 2024

More work

--- a/graphics.c
+++ b/graphics.c
@@ -10,21 +10,56 @@
 Mousectl *mouse;
 Keyboardctl *keyboard;
 Channel *updatechan;
+Channel *mkcolourchan;
+Channel *newcolourchan;
 
 void
 drawgui(GuiElement *g)
 {
+	rlock(&g->lock);
 	GuiSpec spec = guispecs[g->type];
-	spec.draw(g);
-	for(int i = 0; i < g->nchildren; i++)
-		drawgui(g->children[i]);
-}
 
-void
-drawnone(GuiElement *g)
-{
-	Image *bg = getprop(g, Pbackground).colour->image;
-	draw(screen, g->rect, bg, nil, ZP);
+	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;
+		Rectangle r;
+
+		/* top part */
+		r.min.x = g->border.min.x;
+		r.min.y = g->border.min.y;
+		r.max.x = g->border.max.x;
+		r.max.y = g->rect.min.y;
+		draw(screen, r, bc, nil, ZP);
+
+		/* right part */
+		r.min.x = g->rect.max.x;
+		r.min.y = g->rect.min.y;
+		r.max.x = g->border.max.x;
+		r.max.y = g->border.max.y;
+		draw(screen, r, bc, nil, ZP);
+
+		/* bottom part */
+		r.min.x = g->border.min.x;
+		r.min.y = g->rect.max.y;
+		r.max.x = g->border.max.x;
+		r.max.y = g->border.max.y;
+		draw(screen, r, bc, nil, ZP);
+
+		/* left part */
+		r.min.x = g->border.min.x;
+		r.min.y = g->border.min.y;
+		r.max.x = g->rect.min.x;
+		r.max.y = g->border.max.y;
+		draw(screen, r, bc, nil, ZP);
+	}
+
+	if(Dx(g->rect) > 0 && Dy(g->rect) > 0){
+		spec.draw(g);
+
+		for(int i = 0; i < g->nchildren; i++)
+			drawgui(g->children[i]);
+	}
+	runlock(&g->lock);
 }
 
 void
@@ -37,13 +72,10 @@
 Colour *
 mkcolour(ulong c)
 {
-	lockdisplay(display);
-	Colour *col = emalloc(sizeof(Colour));
-	col->image = allocimage(display, Rect(0,0,1,1), screen->chan, 1, c);
-	col->code = c;
-	unlockdisplay(display);
-
-	return col;
+	Colour *res = nil;
+	send(mkcolourchan, &c);
+	recv(newcolourchan, &res);
+	return res;
 }
 
 void
@@ -70,9 +102,9 @@
 guiproc(void *)
 {
 	int i;
+	ulong c;
 	if(initdraw(nil, nil, "guifs") < 0)
 		sysfatal("initdraw failed");
-	display->locking = 1;
 
 	if((mouse = initmouse(nil, screen)) == nil)
 		sysfatal("initmouse failed");
@@ -83,6 +115,7 @@
 		Aupdategui,
 		Aresize,
 		Amouse,
+		Amkcolour,
 		Aaltend,
 	};
 	Alt a[] = {
@@ -92,14 +125,14 @@
 			{mouse->resizec, nil, CHANRCV},
 		[Amouse] =
 			{mouse->c, &mouse->Mouse, CHANRCV},
+		[Amkcolour] =
+			{mkcolourchan, &c, CHANRCV},
 		[Aaltend] =
 			{nil, nil, CHANEND},
 	};
 
 	while(1){
-		unlockdisplay(display);
 		int which = alt(a);
-		lockdisplay(display);
 
 		switch(which){
 		case Aupdategui:
@@ -110,6 +143,14 @@
 			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);
+			}
 		}
 	}
 }
@@ -118,6 +159,8 @@
 initgraphics(void)
 {
 	updatechan = chancreate(sizeof(int), 0);
+	mkcolourchan = chancreate(sizeof(ulong), 0);
+	newcolourchan = chancreate(sizeof(Colour *), 0);
 	proccreate(guiproc, nil, mainstacksize);
 	updategui(1);
 }
--- a/guifs.h
+++ b/guifs.h
@@ -1,14 +1,29 @@
 enum {
 	Pbackground,
+	Pborder,
+	Pmargin,
+	Ppadding,
+	Porientation,
+	Pbordercolour,
 	Pmax,
 };
 
 enum {
-	Gnone,
+	nbaseprops = 4
+};
+
+enum {
 	Gcontainer,
 	Gmax,
 };
+
+enum {
+	Horizontal,
+	Vertical,
+};
+
 typedef struct Colour Colour;
+typedef struct Spacing Spacing;
 typedef union PropVal PropVal;
 typedef struct PropSpec PropSpec;
 typedef struct Prop Prop;
@@ -20,8 +35,17 @@
 	ulong code;
 };
 
+struct Spacing {
+	int up;
+	int right;
+	int down;
+	int left;
+};
+
 union PropVal {
 	Colour *colour;
+	Spacing *spacing;
+	int orientation;
 };
 
 struct PropSpec {
@@ -42,10 +66,12 @@
 	void (*draw)(GuiElement *);
 	void (*layout)(GuiElement *, Rectangle);
 	int nprops;
-	int proptags[];
+	int *proptags;
 };
 
 struct GuiElement {
+	RWLock lock;
+
 	int type;
 	int id;
 
@@ -63,13 +89,14 @@
 	int nprops;
 	Prop *props;
 
+	Rectangle border;
 	Rectangle rect;
-	
 };
 
 extern GuiElement *root;
 extern PropSpec propspecs[Pmax];
 extern GuiSpec guispecs[Gmax];
+extern int baseprops[nbaseprops];
 
 void *emalloc(ulong);
 
@@ -77,10 +104,8 @@
 void initgraphics(void);
 void layout(GuiElement *, Rectangle);
 void updategui(int);
-void drawnone(GuiElement *);
 void drawcontainer(GuiElement *);
 
-void layoutnone(GuiElement *, Rectangle);
 void layoutcontainer(GuiElement *, Rectangle);
 
 PropVal getprop(GuiElement *, int);
--- a/guispec.c
+++ b/guispec.c
@@ -4,7 +4,8 @@
 
 #include "guifs.h"
 
+int containerprops[] = {Pbackground, Porientation};
+
 GuiSpec guispecs[Gmax] = {
-	[Gnone]	=	{ "none",	drawnone,	layoutnone,	1,	{Pbackground}},
-	[Gcontainer] =	{ "container",	drawcontainer,	layoutcontainer,	1,	{Pbackground}},
+	[Gcontainer] = { "container",	drawcontainer,	layoutcontainer,	nelem(containerprops),	containerprops}
 };
\ No newline at end of file
--- a/layout.c
+++ b/layout.c
@@ -4,40 +4,65 @@
 
 #include "guifs.h"
 
-void
-layout(GuiElement *g, Rectangle r)
+Rectangle
+subspacing(Rectangle r, Spacing *s)
 {
-	GuiSpec spec = guispecs[g->type];
-
-	g->rect = r;
-	spec.layout(g, r);
+	r.min.x += s->left;
+	r.min.y += s->up;
+	r.max.x -= s->right;
+	r.max.y -= s->down;
+	return r;
 }
 
 void
-layoutnone(GuiElement *g, Rectangle r)
+layout(GuiElement *g, Rectangle r0)
 {
-	USED(g);
-	USED(r);
+	GuiSpec spec = guispecs[g->type];
+
+	Spacing *margin = getprop(g, Pmargin).spacing;
+	Spacing *border = getprop(g, Pborder).spacing;
+	Spacing *padding = getprop(g, Ppadding).spacing;
+
+	/* Subtract margin to get the outer border rect */
+	Rectangle r1 = subspacing(r0, margin);
+	
+	/* Subtract border widths to get the inner border rect */
+	Rectangle r2 = subspacing(r1, border);
+	
+	/* Subtract padding to get the content rect */
+	Rectangle r3 = subspacing(r2, padding);
+
+	wlock(&g->lock);
+	g->border = r1;
+	g->rect = r2;
+	wunlock(&g->lock);
+
+	rlock(&g->lock);
+	spec.layout(g, r3);
+	runlock(&g->lock);
 }
 
 void
 layoutcontainer(GuiElement *g, Rectangle r)
 {
-	USED(g);
-	USED(r);
-
 	if(g->nchildren == 0)
 		return;
 
-	int margin = 10;
+	int orientation = getprop(g, Porientation).orientation;
 
-	r = insetrect(r, 10);
-	int width = Dx(r) - (margin * (g->nchildren - 1));
-	width = width / g->nchildren;
-	r.max.x = r.min.x + width;
+	int dx = 0;
+	int dy = 0;
 
+	if(orientation == Horizontal){
+		dx = Dx(r) / g->nchildren;
+		r.max.x = r.min.x + dx;
+	}else if(orientation == Vertical){
+		dy = Dy(r) / g->nchildren;
+		r.max.y = r.min.y + dy;
+	}
+
 	for(int i = 0; i < g->nchildren; i++){
 		layout(g->children[i], r);
-		r = rectaddpt(r, Pt(width+margin, 0));
+		r = rectaddpt(r, Pt(dx, dy));
 	}
 }
\ No newline at end of file
--- a/main.c
+++ b/main.c
@@ -13,6 +13,8 @@
 #define Eoffset	"can't write to this file at non-zero offset"
 #define Ebadctl	"bad ctl message"
 
+QLock guilock = 0;
+
 char *username;
 
 enum {
@@ -88,19 +90,35 @@
 void
 settype(GuiElement *g, int type)
 {
+	
 	GuiSpec spec = guispecs[type];
-	free(g->props);
-	g->type = type;
+	int nprops = spec.nprops + nbaseprops;
+	/* Allocate the props before locking the gui element, as some of the
+	 * allocations might cause this thread to block 
+	 */
 
-	g->nprops = spec.nprops;
-	g->props = emalloc(sizeof(Prop) * spec.nprops);
+	Prop *props = emalloc(sizeof(Prop) * nprops);
+	for(int i = 0; i < nbaseprops; i++){
+		int tag = baseprops[i];
+		props[i].tag = tag;
+		props[i].val = propspecs[tag].def();
+		props[i].qid = mkpropqid(tag);
+	}
 	for(int i = 0; i < spec.nprops; i++){
 		int tag = spec.proptags[i];
-		g->props[i].tag = tag;
-		g->props[i].val = propspecs[tag].def();
-		g->props[i].qid = mkpropqid(tag);
+		props[i+nbaseprops].tag = tag;
+		props[i+nbaseprops].val = propspecs[tag].def();
+		props[i+nbaseprops].qid = mkpropqid(tag);
 	}
 
+	wlock(&g->lock);
+	/* TODO: free old propvals */ 
+	free(g->props);
+	g->type = type;
+	g->nprops = nprops;
+	g->props = props;
+	wunlock(&g->lock);
+
 	updategui(0); /* redraw everything */
 }
 
@@ -108,7 +126,6 @@
 newgui(GuiElement *parent)
 {
 	GuiElement *g = emalloc(sizeof(GuiElement));
-	memset(g, 0, sizeof(GuiElement));
 	g->parent = parent;
 	g->qid = mkqid(Qdir);
 	g->qclone = mkqid(Qclone);
@@ -118,9 +135,11 @@
 
 	if(parent){
 		g->id = parent->nchildren;
+		wlock(&parent->lock);
 		parent->nchildren++;
 		parent->children = erealloc(parent->children, parent->nchildren * sizeof(GuiElement *));
 		parent->children[g->id] = g;
+		wunlock(&parent->lock);
 	}
 
 	settype(g, Gcontainer);
@@ -137,10 +156,11 @@
 		return nil;
 	}
 
-	if(id < g->nchildren)
-		return g->children[id];
+	rlock(&g->lock);
+	GuiElement *child = (id < g->nchildren) ? g->children[id] : nil;
+	runlock(&g->lock);
 
-	return nil;
+	return child;
 }
 
 void
@@ -164,6 +184,7 @@
 {
 	GuiElement *g = fid->aux;
 	GuiElement *child;
+	char *err = nil;
 
 	switch(QID_TYPE(fid->qid)){
 	case Qdir:
@@ -174,41 +195,43 @@
 				fid->aux = g->parent;
 				*qid = g->parent->qid;
 			}
-			return nil;
-		}else if(strcmp(name, "clone") == 0){
+		}else if(strcmp(name, "clone") == 0)
 			*qid = g->qclone;
-			return nil;
-		}else if(strcmp(name, "event") == 0){
+		else if(strcmp(name, "event") == 0)
 			*qid = g->qevent;
-			return nil;
-		}else if(strcmp(name, "type") == 0){
+		else if(strcmp(name, "type") == 0)
 			*qid = g->qtype;
-			return nil;
-		}else if(strcmp(name, "props") == 0){
+		else if(strcmp(name, "props") == 0)
 			*qid = g->qprops;
-			return nil;
-		}else if(child = findchild(g, name)){
+		else if(child = findchild(g, name)){
 			fid->aux = child;
 			*qid = child->qid;
-			return nil;
-		}
-		return Eexist;
+		}else
+			err = Eexist;
+		break;
 	case Qprops:
-		if(strcmp(name, "..") == 0){
+		if(strcmp(name, "..") == 0)
 			*qid = g->qid;
-			return nil;
-		}
-		for(int i = 0; i < g->nprops; i++){
-			PropSpec spec = propspecs[g->props[i].tag];
-			if(strcmp(name, spec.name) == 0){
-				*qid = g->props[i].qid;
-				return nil;
+		else{
+			rlock(&g->lock);
+			int ok = 0;
+			for(int i = 0; i < g->nprops && ok == 0; i++){
+				PropSpec spec = propspecs[g->props[i].tag];
+				if(strcmp(name, spec.name) == 0){
+					*qid = g->props[i].qid;
+					ok = 1;
+				}
 			}
+			runlock(&g->lock);
+			if(!ok)
+				err = Eexist;
 		}
-		return Eexist;
+		break;
 	default:
-		return Enodir;
+		err = Enodir;
+		break;
 	}
+	return err;
 }
 
 char *
@@ -310,7 +333,10 @@
 	}else
 		n -= Fmax;
 
-	if(g && n < g->nchildren){
+	rlock(&g->lock);
+	int done = n >= g->nchildren;
+
+	if(!done){
 		GuiElement *child = g->children[n];
 
 		d->mode = DMDIR|0555;
@@ -319,10 +345,10 @@
 		char buf[64];
 		snprint(buf, sizeof(buf), "%d", child->id);
 		d->name = estrdup9p(buf);
-		return 0;
 	}
+	runlock(&g->lock);
 
-	return -1;
+	return done ? -1 : 0;
 }
 
 int
@@ -334,14 +360,20 @@
 	d->gid = estrdup9p(username);
 	d->muid = estrdup9p(username);
 
-	if(n >= g->nprops)
-		return -1;
+	int done;
 
-	PropSpec spec = propspecs[g->props[n].tag];
-	d->mode = 0666;
-	d->name = estrdup9p(spec.name);
-	d->qid = g->props[n].qid;
-	return 0;
+	rlock(&g->lock);
+	done = n >= g->nprops;
+
+	if(!done){
+		PropSpec spec = propspecs[g->props[n].tag];
+		d->mode = 0666;
+		d->name = estrdup9p(spec.name);
+		d->qid = g->props[n].qid;
+	}
+	runlock(&g->lock);
+
+	return done ? -1 : 0;
 }
 
 void
@@ -365,7 +397,9 @@
 		readstr(r, "eveeent\n");
 		break;
 	case Qtype:
+		rlock(&g->lock);
 		snprint(buf, sizeof(buf), "%s\n", guispecs[g->type].name);
+		runlock(&g->lock);
 		readstr(r, buf);
 		break;
 	case Qprops:
--- a/props.c
+++ b/props.c
@@ -1,19 +1,58 @@
 #include <u.h>
 #include <libc.h>
 #include <draw.h>
+#include <ctype.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
 
 #include "guifs.h"
 
 #define Eparse "could not parse property"
 
+int
+allspace(char *r)
+{
+	while(*r){
+		if(!isspace(*r))
+			return 0;
+		r++;
+	}
+	return 1;
+}
+
 PropVal
 defbackground(void)
 {
 	PropVal v;
-	v.colour = mkcolour(DBlack);
+	v.colour = mkcolour(DWhite);
 	return v;
 }
 
+PropVal
+defspacing(void)
+{
+	PropVal v;
+	v.spacing = emalloc(sizeof(Spacing));
+	return v;
+}
+
+PropVal
+deforientation(void)
+{
+	PropVal v;
+	v.orientation = Horizontal;
+	return v;
+}
+
+PropVal
+defbordercolour(void)
+{
+	PropVal v;
+	v.colour = mkcolour(DRed);
+	return v;
+}
+
 char *
 printcolour(PropVal p)
 {
@@ -24,37 +63,129 @@
 }
 
 char *
+printspacing(PropVal p)
+{
+	int bufsize = 256;
+	char *buf = emalloc(bufsize);
+	snprint(buf, bufsize, "%d %d %d %d\n", p.spacing->up, p.spacing->right, p.spacing->down, p.spacing->left);
+	return buf;
+}
+
+char *
+printorientation(PropVal p)
+{
+	char *str;
+	switch(p.orientation){
+	case Horizontal:
+		str = estrdup9p("horizontal\n");
+		break;
+	case Vertical:
+		str = estrdup9p("vertical\n");
+		break;
+	default:
+		str = estrdup9p("???\n");
+		break;
+	}
+	return str;
+}
+
+char *
 parsecolour(char *str, PropVal *p)
 {
 	char *r;
 	ulong c = strtoul(str, &r, 16);
-	if((r - str) != 8)
+	if((r - str) != 8 || !allspace(r))
 		return Eparse;
 	(*p).colour = mkcolour(c);
 	return nil;
 }
 
+char *
+parsespacing(char *str, PropVal *p)
+{
+	USED(p);
+	char *fields[5];
+	int spacings[4];
+
+	int n = getfields(str, fields, nelem(fields), 0, " ");
+	if(!(n == 4 || n == 2 || n == 1))
+		return Eparse;
+
+	for(int i = 0; i < n; i++){
+		char *r;
+		spacings[i] = strtol(fields[i], &r, 10);
+		if(!allspace(r))
+			return Eparse;
+	}
+
+	Spacing *s = emalloc(sizeof(Spacing));
+	switch(n){
+	case 1:
+		s->up = s->down = s->left = s->right = spacings[0];
+		break;
+	case 2:
+		s->up = s->down = spacings[0];
+		s->left = s->right = spacings[1];
+		break;
+	case 4:
+		s->up = spacings[0];
+		s->right = spacings[1];
+		s->down = spacings[2];
+		s->left = spacings[3];
+		break;
+	}
+	(*p).spacing = s;
+
+	return nil;
+}
+
+char *
+parseorientation(char *str, PropVal *p)
+{
+	if(strncmp(str, "horizontal", 10) == 0 && allspace(str+10))
+		(*p).orientation = Horizontal;
+	else if(strncmp(str, "vertical", 8) == 0 && allspace(str+8))
+		(*p).orientation = Vertical;
+	else
+		return Eparse;
+	return nil;
+}
+
 PropVal
 getprop(GuiElement *g, int tag)
 {
-	for(int i = 0; i < g->nprops; i++)
+	PropVal *v = nil;
+	rlock(&g->lock);
+	for(int i = 0; i < g->nprops && v == nil; i++)
 		if(g->props[i].tag == tag)
-			return g->props[i].val;
-	sysfatal("invalid prop for this gui element");
+			v = &g->props[i].val;
+	runlock(&g->lock);
+
+	if(v == nil)
+		sysfatal("invalid prop for this gui element");
+	else
+		return *v;
 }
 
 void
 setprop(GuiElement *g, int tag, PropVal val)
 {
+	wlock(&g->lock);
 	/* TODO: free old propval */
 	for(int i = 0; i < g->nprops; i++)
-		if(g->props[i].tag == tag){
+		if(g->props[i].tag == tag)
 			g->props[i].val = val;
-			updategui(0);
-			return;
-		}
+	wunlock(&g->lock);
+	updategui(0);
 }
 
 PropSpec propspecs[Pmax] = {
 	[Pbackground] = {"background",	defbackground,	printcolour,	parsecolour},
-};
\ No newline at end of file
+	[Pborder] = {"border", 	defspacing,	printspacing,	parsespacing},
+	[Pmargin] = {"margin", 	defspacing,	printspacing,	parsespacing},
+	[Ppadding] = {"padding", defspacing,	printspacing,	parsespacing},
+	[Porientation] = {"orientation",	deforientation,	printorientation,	parseorientation},
+	[Pbordercolour] = {"bordercolour",	defbordercolour,	printcolour,	parsecolour},
+};
+
+int baseprops[nbaseprops] = {Pborder, Pmargin, Ppadding, Pbordercolour};
\ No newline at end of file
--- a/test.rc
+++ b/test.rc
@@ -2,16 +2,18 @@
 
 rfork n
 
+delay=0.25
+
 guifs -s testgui
 
 dir=/mnt/gui
-sleep 1
+sleep $delay
 
-colours=(FF0000 00FF00 0000FF FFFF00 00FFFF FF00FF FFFFFF)
+colours=(FF0000 00FF00 0000FF FFFF00 00FFFF FF00FF FFFFFF 333333)
 for(colour in $colours){
-	dir=$dir/`{cat $dir/clone}			# Create a new sub element in the gui (for now, always a "container" with a small margin)
+	dir=$dir/`{cat $dir/clone}			# Create a new sub element in the gui (for now, always a "container")
 	echo $colour^FF >> $dir/props/background	# Set the background
-	sleep 1						# Wait a bit
+	sleep $delay					# Wait a bit
 }
 
 # Now do the same, but don't nest the elements
@@ -18,9 +20,31 @@
 for(colour in $colours){
 	subdir=$dir/`{cat $dir/clone}
 	echo $colour^FF >> $subdir/props/background
-	sleep 1
+	sleep $delay
 }
 
-# when the script ends, the old text window draws over the gui :joyd: I will fix that later.
+# Add some padding to all elements
+for(f in `{walk /mnt/gui/ | grep 'padding$'}){
+	echo 10 >> $f
+	sleep $delay
+}
+
+# Add a border to the innermost elements
+for(f in `{walk /mnt/gui | grep $dir'/[0-9]+/props/border$'}){
+	echo 8888CCFF >> $f^colour
+	echo 5 >> $f
+	sleep $delay
+}
+
+# Add some margin to the innermost elements
+for(f in `{walk /mnt/gui | grep $dir'/[0-9]+/props/margin'}){
+	echo 5 >> $f
+	sleep $delay
+}
+
+# Make the inner container vertical
+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