diff options
author | Peter Mikkelsen <petermikkelsen10@gmail.com> | 2024-02-11 00:04:31 +0000 |
---|---|---|
committer | Peter Mikkelsen <petermikkelsen10@gmail.com> | 2024-02-11 00:04:31 +0000 |
commit | 7c6a945996a1d5510ff1412320ac7d07a0f82851 (patch) | |
tree | f77259f26b5c33307ba8ebc670951ed0acce1b00 | |
parent | b5a96b97ee2f3fd30e5935d2b3c2c6e0d96c4640 (diff) |
Start working on it
-rw-r--r-- | graphics.c | 123 | ||||
-rw-r--r-- | guifs.h | 87 | ||||
-rw-r--r-- | guispec.c | 10 | ||||
-rw-r--r-- | layout.c | 43 | ||||
-rw-r--r-- | main.c | 456 | ||||
-rw-r--r-- | mkfile | 10 | ||||
-rw-r--r-- | props.c | 60 | ||||
-rwxr-xr-x | test.rc | 26 |
8 files changed, 811 insertions, 4 deletions
diff --git a/graphics.c b/graphics.c new file mode 100644 index 0000000..7339d8f --- /dev/null +++ b/graphics.c @@ -0,0 +1,123 @@ +#include <u.h> +#include <libc.h> +#include <thread.h> +#include <draw.h> +#include <mouse.h> +#include <keyboard.h> + +#include "guifs.h" + +Mousectl *mouse; +Keyboardctl *keyboard; +Channel *updatechan; + +void +drawgui(GuiElement *g) +{ + 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); +} + +void +drawcontainer(GuiElement *g) +{ + Image *bg = getprop(g, Pbackground).colour->image; + draw(screen, g->rect, bg, nil, ZP); +} + +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; +} + +void +updategui(int new) +{ + /* Trigger a call to resized by sending a message */ + send(updatechan, &new); +} + +void +resized(int new) +{ + if(new && getwindow(display, Refnone) < 0) + sysfatal("can't reattach to window: %r"); + + if(root != nil){ + layout(root, screen->r); + drawgui(root); + flushimage(display, 1); + } +} + +void +guiproc(void *) +{ + int i; + if(initdraw(nil, nil, "guifs") < 0) + sysfatal("initdraw failed"); + display->locking = 1; + + if((mouse = initmouse(nil, screen)) == nil) + sysfatal("initmouse failed"); + if((keyboard = initkeyboard(nil)) == nil) + sysfatal("initkeyboard failed"); + + enum { + Aupdategui, + Aresize, + Amouse, + Aaltend, + }; + Alt a[] = { + [Aupdategui] = + {updatechan, &i, CHANRCV}, + [Aresize] = + {mouse->resizec, nil, CHANRCV}, + [Amouse] = + {mouse->c, &mouse->Mouse, CHANRCV}, + [Aaltend] = + {nil, nil, CHANEND}, + }; + + while(1){ + unlockdisplay(display); + int which = alt(a); + lockdisplay(display); + + switch(which){ + case Aupdategui: + resized(i); + break; + case Aresize: + resized(1); + break; + case Amouse: + break; + } + } +} + +void +initgraphics(void) +{ + updatechan = chancreate(sizeof(int), 0); + proccreate(guiproc, nil, mainstacksize); + updategui(1); +} @@ -0,0 +1,87 @@ +enum { + Pbackground, + Pmax, +}; + +enum { + Gnone, + Gcontainer, + Gmax, +}; +typedef struct Colour Colour; +typedef union PropVal PropVal; +typedef struct PropSpec PropSpec; +typedef struct Prop Prop; +typedef struct GuiSpec GuiSpec; +typedef struct GuiElement GuiElement; + +struct Colour { + Image *image; + ulong code; +}; + +union PropVal { + Colour *colour; +}; + +struct PropSpec { + char *name; + PropVal (*def)(void); + char *(*print)(PropVal); + char *(*parse)(char *, PropVal *); +}; + +struct Prop { + int tag; + PropVal val; + Qid qid; +}; + +struct GuiSpec { + char *name; + void (*draw)(GuiElement *); + void (*layout)(GuiElement *, Rectangle); + int nprops; + int proptags[]; +}; + +struct GuiElement { + int type; + int id; + + Qid qid; + Qid qclone; + Qid qevent; + Qid qtype; + Qid qprops; + + int nchildren; + GuiElement **children; + + GuiElement *parent; + + int nprops; + Prop *props; + + Rectangle rect; + +}; + +extern GuiElement *root; +extern PropSpec propspecs[Pmax]; +extern GuiSpec guispecs[Gmax]; + +void *emalloc(ulong); + +Colour *mkcolour(ulong); +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); +void setprop(GuiElement *, int, PropVal);
\ No newline at end of file diff --git a/guispec.c b/guispec.c new file mode 100644 index 0000000..ddd0cd5 --- /dev/null +++ b/guispec.c @@ -0,0 +1,10 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> + +#include "guifs.h" + +GuiSpec guispecs[Gmax] = { + [Gnone] = { "none", drawnone, layoutnone, 1, {Pbackground}}, + [Gcontainer] = { "container", drawcontainer, layoutcontainer, 1, {Pbackground}}, +};
\ No newline at end of file diff --git a/layout.c b/layout.c new file mode 100644 index 0000000..76df9c7 --- /dev/null +++ b/layout.c @@ -0,0 +1,43 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> + +#include "guifs.h" + +void +layout(GuiElement *g, Rectangle r) +{ + GuiSpec spec = guispecs[g->type]; + + g->rect = r; + spec.layout(g, r); +} + +void +layoutnone(GuiElement *g, Rectangle r) +{ + USED(g); + USED(r); +} + +void +layoutcontainer(GuiElement *g, Rectangle r) +{ + USED(g); + USED(r); + + if(g->nchildren == 0) + return; + + int margin = 10; + + r = insetrect(r, 10); + int width = Dx(r) - (margin * (g->nchildren - 1)); + width = width / g->nchildren; + r.max.x = r.min.x + width; + + for(int i = 0; i < g->nchildren; i++){ + layout(g->children[i], r); + r = rectaddpt(r, Pt(width+margin, 0)); + } +}
\ No newline at end of file @@ -1,8 +1,460 @@ #include <u.h> #include <libc.h> +#include <fcall.h> +#include <thread.h> +#include <9p.h> +#include <draw.h> + +#include "guifs.h" + +#define Eexist "file does not exist" +#define Enodir "not a directory" +#define Eperm "permission denied" +#define Eoffset "can't write to this file at non-zero offset" +#define Ebadctl "bad ctl message" + +char *username; + +enum { + Qdir, + Qclone, + Qevent, + Qtype, + Qprops, + + Qprop, +}; + +enum { + Fclone, + Fevent, + Ftype, + Fprops, + Fmax, +}; + +GuiElement *root; + +#define QID_TYPE(q) ((q.path) & 0xFF) +#define QID_PROP(q) (((q.path) >> 8) & 0xFF) + +void * +emalloc(ulong size) +{ + void *p = mallocz(size, 1); + if(!p) + sysfatal("malloc failed"); + return p; +} + +void * +erealloc(void *p, ulong size) +{ + p = realloc(p, size); + if(!p) + sysfatal("realloc failed"); + return p; +} + +Qid +mkqid(int type) +{ + static int id = 0; + + Qid q; + q.vers = 0; + q.path = (type & 0xFFFF) | (id << 16); + id++; + switch(type){ + case Qdir: + case Qprops: + q.type = QTDIR; + break; + case Qclone: + case Qevent: + case Qtype: + q.type = QTFILE; + break; + } + return q; +} + +Qid +mkpropqid(int proptag) +{ + return mkqid(Qprop | ((proptag & 0xFF) << 8)); +} + +void +settype(GuiElement *g, int type) +{ + GuiSpec spec = guispecs[type]; + free(g->props); + g->type = type; + + g->nprops = spec.nprops; + g->props = emalloc(sizeof(Prop) * spec.nprops); + 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); + } + + updategui(0); /* redraw everything */ +} + +GuiElement * +newgui(GuiElement *parent) +{ + GuiElement *g = emalloc(sizeof(GuiElement)); + memset(g, 0, sizeof(GuiElement)); + g->parent = parent; + g->qid = mkqid(Qdir); + g->qclone = mkqid(Qclone); + g->qevent = mkqid(Qevent); + g->qtype = mkqid(Qtype); + g->qprops = mkqid(Qprops); + + if(parent){ + g->id = parent->nchildren; + parent->nchildren++; + parent->children = erealloc(parent->children, parent->nchildren * sizeof(GuiElement *)); + parent->children[g->id] = g; + } + + settype(g, Gcontainer); + + return g; +} + +GuiElement * +findchild(GuiElement *g, char *name) +{ + char *r; + uvlong id = strtoull(name, &r, 10); + if(*r != 0){ + return nil; + } + + if(id < g->nchildren) + return g->children[id]; + + return nil; +} void -main(void) +fsattach(Req *r) { - print("hello\n"); + if(root == nil){ + GuiElement *g = newgui(nil); + root = g; + settype(g, Gcontainer); + } + + r->fid->aux = root; + r->fid->qid = root->qid; + r->ofcall.qid = r->fid->qid; + + respond(r, nil); +} + +char * +fswalk1(Fid *fid, char *name, Qid *qid) +{ + GuiElement *g = fid->aux; + GuiElement *child; + + switch(QID_TYPE(fid->qid)){ + case Qdir: + if(strcmp(name, "..") == 0){ + if(g->parent == nil) // Root element + *qid = g->qid; + else{ + fid->aux = g->parent; + *qid = g->parent->qid; + } + return nil; + }else if(strcmp(name, "clone") == 0){ + *qid = g->qclone; + return nil; + }else if(strcmp(name, "event") == 0){ + *qid = g->qevent; + return nil; + }else if(strcmp(name, "type") == 0){ + *qid = g->qtype; + return nil; + }else if(strcmp(name, "props") == 0){ + *qid = g->qprops; + return nil; + }else if(child = findchild(g, name)){ + fid->aux = child; + *qid = child->qid; + return nil; + } + return Eexist; + case Qprops: + 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; + } + } + return Eexist; + default: + return Enodir; + } +} + +char * +fsclone(Fid *old, Fid *new) +{ + new->aux = old->aux; + return nil; +} + +void +fsopen(Req *r) +{ + GuiElement *g = r->fid->aux; + + switch(QID_TYPE(r->fid->qid)){ + case Qdir: + case Qevent: + case Qclone: + case Qprops: + if(r->ifcall.mode != OREAD){ + respond(r, Eperm); + return; + } + break; + } + + if(QID_TYPE(r->fid->qid) == Qclone){ + /* Create a new child gui element */ + GuiElement *child = newgui(g); + + assert(r->fid->qid.vers == child->id); + + /* Update qid version, so a read reports the correct child id */ + assert(memcmp(&r->fid->qid, &g->qclone, sizeof(Qid)) == 0); + g->qclone.vers++; + r->fid->qid = g->qclone; + r->ofcall.qid = g->qclone; + } + + respond(r, nil); +} + +void +fsstat(Req *r) +{ + r->d.qid = r->fid->qid; + r->d.uid = estrdup9p(username); + r->d.gid = estrdup9p(username); + r->d.muid = estrdup9p(username); + switch(QID_TYPE(r->fid->qid)){ + case Qdir: + r->d.name = estrdup9p("/"); + r->d.mode = 0555|DMDIR; + break; + case Qprops: + r->d.name = estrdup9p("/"); + r->d.mode = 0555|DMDIR; + break; + } + + respond(r, nil); +} + +int +dirtreegen(int n, Dir *d, void *aux) +{ + GuiElement *g = aux; + + d->uid = estrdup9p(username); + d->gid = estrdup9p(username); + d->muid = estrdup9p(username); + + if(n < Fmax){ + d->length = 0; + + switch(n){ + case Fclone: + d->mode = 0444; + d->name = estrdup9p("clone"); + d->qid = g->qclone; + break; + case Fevent: + d->mode = 0444; + d->name = estrdup9p("event"); + d->qid = g->qevent; + break; + case Ftype: + d->mode = 0666; + d->name = estrdup9p("type"); + d->qid = g->qtype; + break; + case Fprops: + d->mode = 0555|DMDIR; + d->name = estrdup9p("props"); + d->qid = g->qprops; + break; + } + return 0; + }else + n -= Fmax; + + if(g && n < g->nchildren){ + GuiElement *child = g->children[n]; + + d->mode = DMDIR|0555; + d->qid = child->qid; + + char buf[64]; + snprint(buf, sizeof(buf), "%d", child->id); + d->name = estrdup9p(buf); + return 0; + } + + return -1; +} + +int +proptreegen(int n, Dir *d, void *aux) +{ + GuiElement *g = aux; + + d->uid = estrdup9p(username); + d->gid = estrdup9p(username); + d->muid = estrdup9p(username); + + if(n >= g->nprops) + return -1; + + PropSpec spec = propspecs[g->props[n].tag]; + d->mode = 0666; + d->name = estrdup9p(spec.name); + d->qid = g->props[n].qid; + return 0; +} + +void +fsread(Req *r) +{ + GuiElement *g = r->fid->aux; + char buf[256]; + + switch(QID_TYPE(r->fid->qid)){ + case Qdir: + dirread9p(r, dirtreegen, g); + break; + case Qclone: + snprint(buf, sizeof(buf), "%uld\n", r->fid->qid.vers-1); + 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"); + break; + case Qtype: + snprint(buf, sizeof(buf), "%s\n", guispecs[g->type].name); + readstr(r, buf); + break; + case Qprops: + dirread9p(r, proptreegen, g); + break; + case Qprop: + { + int tag = QID_PROP(r->fid->qid); + PropSpec spec = propspecs[tag]; + PropVal val = getprop(g, tag); + char *str = spec.print(val); + readstr(r, str); + free(str); + } + break; + } + respond(r, nil); +} + +void +fswrite(Req *r) +{ + GuiElement *g = r->fid->aux; + char *err = nil; + + switch(QID_TYPE(r->fid->qid)){ + case Qtype: + err = "Can't switch type"; + break; + case Qprop: + { + int tag = QID_PROP(r->fid->qid); + PropSpec spec = propspecs[tag]; + PropVal val; + + char *buf = emalloc(r->ifcall.count + 1); + buf[r->ifcall.count] = 0; + memcpy(buf, r->ifcall.data, r->ifcall.count); + err = spec.parse(buf, &val); + if(err == nil) + setprop(g, tag, val); + free(buf); + } + } + + respond(r, err); +} + +Srv fs = { + .attach = fsattach, + .walk1 = fswalk1, + .clone = fsclone, + .open = fsopen, + .stat = fsstat, + .read = fsread, + .write = fswrite, +}; + +void +usage(void) +{ + fprint(2, "usage: %s [-D] [-m mountpoint] [-s srvname] \n", argv0); + exits("usage"); +} + +void +threadmain(int argc, char **argv) +{ + char *mtpt = "/mnt/gui"; + char *srvname = nil; + ARGBEGIN{ + case 'D': + chatty9p++; + break; + case 'm': + mtpt = EARGF(usage()); + break; + case 's': + srvname = EARGF(usage()); + break; + default: + usage(); + }ARGEND; + + if(argc > 1) + usage(); + + username = getuser(); + initgraphics(); + threadpostmountsrv(&fs, srvname, mtpt, MREPL); + exits(nil); } @@ -1,7 +1,13 @@ </$objtype/mkfile TARG=guifs -OFILES=main.$O -BIN=$home/bin/$objtype/bin +OFILES=\ + main.$O\ + props.$O\ + guispec.$O\ + graphics.$O\ + layout.$O\ + +BIN=$home/bin/$objtype </sys/src/cmd/mkone @@ -0,0 +1,60 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> + +#include "guifs.h" + +#define Eparse "could not parse property" + +PropVal +defbackground(void) +{ + PropVal v; + v.colour = mkcolour(DBlack); + return v; +} + +char * +printcolour(PropVal p) +{ + int bufsize = 64; + char *buf = emalloc(bufsize); + snprint(buf, bufsize, "%08ulX\n", p.colour->code); + return buf; +} + +char * +parsecolour(char *str, PropVal *p) +{ + char *r; + ulong c = strtoul(str, &r, 16); + if((r - str) != 8) + return Eparse; + (*p).colour = mkcolour(c); + return nil; +} + +PropVal +getprop(GuiElement *g, int tag) +{ + for(int i = 0; i < g->nprops; i++) + if(g->props[i].tag == tag) + return g->props[i].val; + sysfatal("invalid prop for this gui element"); +} + +void +setprop(GuiElement *g, int tag, PropVal val) +{ + /* TODO: free old propval */ + for(int i = 0; i < g->nprops; i++) + if(g->props[i].tag == tag){ + g->props[i].val = val; + updategui(0); + return; + } +} + +PropSpec propspecs[Pmax] = { + [Pbackground] = {"background", defbackground, printcolour, parsecolour}, +};
\ No newline at end of file @@ -0,0 +1,26 @@ +#!/bin/rc + +rfork n + +guifs -s testgui + +dir=/mnt/gui +sleep 1 + +colours=(FF0000 00FF00 0000FF FFFF00 00FFFF FF00FF FFFFFF) +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) + echo $colour^FF >> $dir/props/background # Set the background + sleep 1 # Wait a bit +} + +# Now do the same, but don't nest the elements +for(colour in $colours){ + subdir=$dir/`{cat $dir/clone} + echo $colour^FF >> $subdir/props/background + sleep 1 +} + +# when the script ends, the old text window draws over the gui :joyd: I will fix that later. +# For now, i just make the script sleep for a long time +sleep 1000000 |