diff options
-rw-r--r-- | graphics.c | 41 | ||||
-rw-r--r-- | guifs.h | 18 | ||||
-rw-r--r-- | guispec.c | 1 | ||||
-rw-r--r-- | layout.c | 9 | ||||
-rw-r--r-- | main.c | 162 | ||||
-rw-r--r-- | mkfile | 1 | ||||
-rw-r--r-- | props.c | 19 | ||||
-rwxr-xr-x | test.rc | 22 |
8 files changed, 218 insertions, 55 deletions
@@ -7,6 +7,7 @@ #include "guifs.h" +Point mousexy; Mousectl *mouse; Keyboardctl *keyboard; Channel *updatechan; @@ -21,7 +22,7 @@ drawgui(GuiElement *g) 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 @@ drawgui(GuiElement *g) 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 @@ drawcontainer(GuiElement *g) 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 @@ guiproc(void *) { int i; ulong c; + Rune r; + if(initdraw(nil, nil, "guifs") < 0) sysfatal("initdraw failed"); @@ -127,25 +130,28 @@ guiproc(void *) 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 @@ guiproc(void *) 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; } } } @@ -30,6 +30,7 @@ typedef struct Spacing Spacing; 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 @@ struct Prop { Qid qid; }; +struct Event { + Rune r; +}; + struct GuiSpec { char *name; void (*draw)(GuiElement *); @@ -85,6 +90,7 @@ struct GuiElement { Qid qevent; Qid qtype; Qid qprops; + Qid qwait; int nchildren; GuiElement **children; @@ -94,11 +100,16 @@ struct GuiElement { 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 drawtextbox(GuiElement *); 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 @@ -1,6 +1,7 @@ #include <u.h> #include <libc.h> #include <draw.h> +#include <thread.h> #include "guifs.h" @@ -1,6 +1,7 @@ #include <u.h> #include <libc.h> #include <draw.h> +#include <thread.h> #include "guifs.h" @@ -19,9 +20,9 @@ layout(GuiElement *g, Rectangle r0) { 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 @@ layoutcontainer(GuiElement *g, Rectangle r) if(g->nchildren == 0) return; - int orientation = getprop(g, Porientation).orientation; + int orientation = getprop(g, Porientation, 1).orientation; int dx = 0; int dy = 0; @@ -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 @@ enum { Qclone, Qevent, Qtype, + Qwait, Qprops, Qprop, @@ -31,10 +33,34 @@ enum { 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 @@ mkqid(int type) case Qclone: case Qevent: case Qtype: + case Qwait: q.type = QTFILE; break; } @@ -132,6 +159,9 @@ newgui(GuiElement *parent) 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; @@ -170,6 +200,7 @@ fsattach(Req *r) GuiElement *g = newgui(nil); root = g; settype(g, Gcontainer); + updategui(1); } r->fid->aux = root; @@ -208,6 +239,8 @@ fswalk1(Fid *fid, char *name, Qid *qid) *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,15 +283,17 @@ void fsopen(Req *r) { GuiElement *g = r->fid->aux; + char *err = nil; switch(QID_TYPE(r->fid->qid)){ case Qdir: 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 @@ fsopen(Req *r) 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 @@ dirtreegen(int n, Dir *d, void *aux) 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 @@ dirtreegen(int n, Dir *d, void *aux) 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 @@ fsread(Req *r) 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 @@ fsread(Req *r) runlock(&g->lock); readstr(r, buf); break; + case Qwait: + /* intentionally left blank */ + return; case Qprops: dirread9p(r, proptreegen, g); break; @@ -419,7 +510,7 @@ fsread(Req *r) { 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 @@ fswrite(Req *r) 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 @@ fswrite(Req *r) 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 @@ threadmain(int argc, char **argv) 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); } @@ -7,6 +7,7 @@ OFILES=\ guispec.$O\ graphics.$O\ layout.$O\ + event.$O\ BIN=$home/bin/$objtype @@ -226,14 +226,16 @@ parsetext(char *str, PropVal *p) } 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 @@ getprop(GuiElement *g, int tag) } 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] = { @@ -1,13 +1,8 @@ #!/bin/rc -rfork n - -delay=0.25 - -guifs -s testgui +delay=0.1 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 @@ for(f in `{walk /mnt/gui | grep $dir'/[0-9]+/props/margin'}){ 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 |