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