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