ref: 204d030a6e6bb71cbc4cdf581b91c8c01242d656
author: qwx <[email protected]>
date: Thu Mar 4 05:04:11 EST 2021
import
--- /dev/null
+++ b/INDEX
@@ -1,0 +1,10 @@
+calc learning yacc
+cpick color picker: take snapshot of screen, then scroll to this window's position for colors
+hcol display a hex color
+lr obsolete walk(1)-like
+mev visualize mouse grabbing
+rfx mouse accuracy game
+solt first attempts at plan9 programming
+spd typing accuracy game (try with /lib/theo!)
+tev print keyboard/mouse events
+wstat use syscall(1) instead
--- /dev/null
+++ b/calc.c
@@ -1,0 +1,402 @@
+
+#line 2 "/usr/qwx/p/misc/calc.y"
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+
+int yylex(void);
+int yyparse(void);
+void yyerror(char*);
+
+Biobuf *bf;
+
+#line 14 "/usr/qwx/p/misc/calc.y"
+typedef union {
+ int i;
+} YYSTYPE;
+extern int yyerrflag;
+#ifndef YYMAXDEPTH
+#define YYMAXDEPTH 150
+#endif
+YYSTYPE yylval;
+YYSTYPE yyval;
+#define NUM 57346
+#define YYEOFCODE 1
+#define YYERRCODE 2
+
+#line 36 "/usr/qwx/p/misc/calc.y"
+
+
+void
+yyerror(char *s)
+{
+ fprint(2, "%s\n", s);
+}
+
+int
+getnum(int c)
+{
+ char s[64], *p;
+
+ for(p=s, *p++=c; isdigit(c=Bgetc(bf));)
+ if(p < s+sizeof(s)-1)
+ *p++ = c;
+ *p = 0;
+ Bungetc(bf);
+ return strtol(s, nil, 10);
+}
+
+int
+yylex(void)
+{
+ int n, c;
+
+ do
+ c = Bgetc(bf);
+ while(c != '\n' && isspace(c));
+ if(isdigit(c)){
+ yylval.i = getnum(c);
+ return NUM;
+ }else if(c == 'd'){
+ n = getnum(Bgetc(bf));
+ yylval.i = nrand(n != 0 ? n : 1);
+ return NUM;
+ }else
+ return c;
+}
+
+void
+main(int argc, char **argv)
+{
+ ARGBEGIN{
+ }ARGEND
+ if((bf = Bfdopen(0, OREAD)) == nil)
+ sysfatal("Bfdopen: %r");
+ yyparse();
+}
+short yyexca[] =
+{-1, 1,
+ 1, -1,
+ -2, 0,
+};
+#define YYNPROD 10
+#define YYPRIVATE 57344
+#define YYLAST 31
+short yyact[] =
+{
+ 6, 7, 8, 9, 10, 2, 1, 17, 0, 0,
+ 11, 0, 12, 13, 14, 15, 16, 6, 7, 8,
+ 9, 10, 5, 3, 8, 9, 10, 0, 0, 0,
+ 4
+};
+short yypact[] =
+{
+-1000, 19, 12,-1000, 19,-1000, 19, 19, 19, 19,
+ 19, -5, 17, 17,-1000,-1000,-1000,-1000
+};
+short yypgo[] =
+{
+ 0, 5, 6
+};
+short yyr1[] =
+{
+ 0, 2, 2, 1, 1, 1, 1, 1, 1, 1
+};
+short yyr2[] =
+{
+ 0, 0, 3, 1, 3, 3, 3, 3, 3, 3
+};
+short yychk[] =
+{
+-1000, -2, -1, 4, 11, 10, 5, 6, 7, 8,
+ 9, -1, -1, -1, -1, -1, -1, 12
+};
+short yydef[] =
+{
+ 1, -2, 0, 3, 0, 2, 0, 0, 0, 0,
+ 0, 0, 5, 6, 7, 8, 9, 4
+};
+short yytok1[] =
+{
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 10, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 9, 0, 0,
+ 11, 12, 7, 5, 0, 6, 0, 8
+};
+short yytok2[] =
+{
+ 2, 3, 4
+};
+long yytok3[] =
+{
+ 0
+};
+
+#line 1 "/sys/lib/yaccpar"
+#define YYFLAG -1000
+#define yyclearin yychar = -1
+#define yyerrok yyerrflag = 0
+
+#ifdef yydebug
+#include "y.debug"
+#else
+#define yydebug 0
+char* yytoknames[1]; /* for debugging */
+char* yystates[1]; /* for debugging */
+#endif
+
+/* parser for yacc output */
+
+int yynerrs = 0; /* number of errors */
+int yyerrflag = 0; /* error recovery flag */
+
+extern int fprint(int, char*, ...);
+extern int sprint(char*, char*, ...);
+
+char*
+yytokname(int yyc)
+{
+ static char x[16];
+
+ if(yyc > 0 && yyc <= sizeof(yytoknames)/sizeof(yytoknames[0]))
+ if(yytoknames[yyc-1])
+ return yytoknames[yyc-1];
+ sprint(x, "<%d>", yyc);
+ return x;
+}
+
+char*
+yystatname(int yys)
+{
+ static char x[16];
+
+ if(yys >= 0 && yys < sizeof(yystates)/sizeof(yystates[0]))
+ if(yystates[yys])
+ return yystates[yys];
+ sprint(x, "<%d>\n", yys);
+ return x;
+}
+
+long
+yylex1(void)
+{
+ long yychar;
+ long *t3p;
+ int c;
+
+ yychar = yylex();
+ if(yychar <= 0) {
+ c = yytok1[0];
+ goto out;
+ }
+ if(yychar < sizeof(yytok1)/sizeof(yytok1[0])) {
+ c = yytok1[yychar];
+ goto out;
+ }
+ if(yychar >= YYPRIVATE)
+ if(yychar < YYPRIVATE+sizeof(yytok2)/sizeof(yytok2[0])) {
+ c = yytok2[yychar-YYPRIVATE];
+ goto out;
+ }
+ for(t3p=yytok3;; t3p+=2) {
+ c = t3p[0];
+ if(c == yychar) {
+ c = t3p[1];
+ goto out;
+ }
+ if(c == 0)
+ break;
+ }
+ c = 0;
+
+out:
+ if(c == 0)
+ c = yytok2[1]; /* unknown char */
+ if(yydebug >= 3)
+ fprint(2, "lex %.4lux %s\n", yychar, yytokname(c));
+ return c;
+}
+
+int
+yyparse(void)
+{
+ struct
+ {
+ YYSTYPE yyv;
+ int yys;
+ } yys[YYMAXDEPTH], *yyp, *yypt;
+ short *yyxi;
+ int yyj, yym, yystate, yyn, yyg;
+ long yychar;
+ YYSTYPE save1, save2;
+ int save3, save4;
+
+ save1 = yylval;
+ save2 = yyval;
+ save3 = yynerrs;
+ save4 = yyerrflag;
+
+ yystate = 0;
+ yychar = -1;
+ yynerrs = 0;
+ yyerrflag = 0;
+ yyp = &yys[-1];
+ goto yystack;
+
+ret0:
+ yyn = 0;
+ goto ret;
+
+ret1:
+ yyn = 1;
+ goto ret;
+
+ret:
+ yylval = save1;
+ yyval = save2;
+ yynerrs = save3;
+ yyerrflag = save4;
+ return yyn;
+
+yystack:
+ /* put a state and value onto the stack */
+ if(yydebug >= 4)
+ fprint(2, "char %s in %s", yytokname(yychar), yystatname(yystate));
+
+ yyp++;
+ if(yyp >= &yys[YYMAXDEPTH]) {
+ yyerror("yacc stack overflow");
+ goto ret1;
+ }
+ yyp->yys = yystate;
+ yyp->yyv = yyval;
+
+yynewstate:
+ yyn = yypact[yystate];
+ if(yyn <= YYFLAG)
+ goto yydefault; /* simple state */
+ if(yychar < 0)
+ yychar = yylex1();
+ yyn += yychar;
+ if(yyn < 0 || yyn >= YYLAST)
+ goto yydefault;
+ yyn = yyact[yyn];
+ if(yychk[yyn] == yychar) { /* valid shift */
+ yychar = -1;
+ yyval = yylval;
+ yystate = yyn;
+ if(yyerrflag > 0)
+ yyerrflag--;
+ goto yystack;
+ }
+
+yydefault:
+ /* default state action */
+ yyn = yydef[yystate];
+ if(yyn == -2) {
+ if(yychar < 0)
+ yychar = yylex1();
+
+ /* look through exception table */
+ for(yyxi=yyexca;; yyxi+=2)
+ if(yyxi[0] == -1 && yyxi[1] == yystate)
+ break;
+ for(yyxi += 2;; yyxi += 2) {
+ yyn = yyxi[0];
+ if(yyn < 0 || yyn == yychar)
+ break;
+ }
+ yyn = yyxi[1];
+ if(yyn < 0)
+ goto ret0;
+ }
+ if(yyn == 0) {
+ /* error ... attempt to resume parsing */
+ switch(yyerrflag) {
+ case 0: /* brand new error */
+ yyerror("syntax error");
+ yynerrs++;
+ if(yydebug >= 1) {
+ fprint(2, "%s", yystatname(yystate));
+ fprint(2, "saw %s\n", yytokname(yychar));
+ }
+
+ case 1:
+ case 2: /* incompletely recovered error ... try again */
+ yyerrflag = 3;
+
+ /* find a state where "error" is a legal shift action */
+ while(yyp >= yys) {
+ yyn = yypact[yyp->yys] + YYERRCODE;
+ if(yyn >= 0 && yyn < YYLAST) {
+ yystate = yyact[yyn]; /* simulate a shift of "error" */
+ if(yychk[yystate] == YYERRCODE)
+ goto yystack;
+ }
+
+ /* the current yyp has no shift onn "error", pop stack */
+ if(yydebug >= 2)
+ fprint(2, "error recovery pops state %d, uncovers %d\n",
+ yyp->yys, (yyp-1)->yys );
+ yyp--;
+ }
+ /* there is no state on the stack with an error shift ... abort */
+ goto ret1;
+
+ case 3: /* no shift yet; clobber input char */
+ if(yydebug >= 2)
+ fprint(2, "error recovery discards %s\n", yytokname(yychar));
+ if(yychar == YYEOFCODE)
+ goto ret1;
+ yychar = -1;
+ goto yynewstate; /* try again in the same state */
+ }
+ }
+
+ /* reduction by production yyn */
+ if(yydebug >= 2)
+ fprint(2, "reduce %d in:\n\t%s", yyn, yystatname(yystate));
+
+ yypt = yyp;
+ yyp -= yyr2[yyn];
+ yyval = (yyp+1)->yyv;
+ yym = yyn;
+
+ /* consult goto table to find next state */
+ yyn = yyr1[yyn];
+ yyg = yypgo[yyn];
+ yyj = yyg + yyp->yys + 1;
+
+ if(yyj >= YYLAST || yychk[yystate=yyact[yyj]] != -yyn)
+ yystate = yyact[yyg];
+ switch(yym) {
+
+case 2:
+#line 26 "/usr/qwx/p/misc/calc.y"
+{ print("%d\n", yypt[-1].yyv.i); } break;
+case 3:
+#line 28 "/usr/qwx/p/misc/calc.y"
+{ yyval.i = yypt[-0].yyv.i; } break;
+case 4:
+#line 29 "/usr/qwx/p/misc/calc.y"
+{ yyval.i = yypt[-1].yyv.i; } break;
+case 5:
+#line 30 "/usr/qwx/p/misc/calc.y"
+{ yyval.i = yypt[-2].yyv.i + yypt[-0].yyv.i; } break;
+case 6:
+#line 31 "/usr/qwx/p/misc/calc.y"
+{ yyval.i = yypt[-2].yyv.i - yypt[-0].yyv.i; } break;
+case 7:
+#line 32 "/usr/qwx/p/misc/calc.y"
+{ yyval.i = yypt[-2].yyv.i * yypt[-0].yyv.i; } break;
+case 8:
+#line 33 "/usr/qwx/p/misc/calc.y"
+{ yyval.i = yypt[-2].yyv.i / yypt[-0].yyv.i; } break;
+case 9:
+#line 34 "/usr/qwx/p/misc/calc.y"
+{ yyval.i = yypt[-2].yyv.i % yypt[-0].yyv.i; } break;
+ }
+ goto yystack; /* stack new state and value */
+}
--- /dev/null
+++ b/calc.tab.h
@@ -1,0 +1,6 @@
+
+typedef union {
+ int i;
+} YYSTYPE;
+extern YYSTYPE yylval;
+#define NUM 57346
--- /dev/null
+++ b/calc.y
@@ -1,0 +1,84 @@
+%{
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+
+int yylex(void);
+int yyparse(void);
+void yyerror(char*);
+
+Biobuf *bf;
+%}
+
+%union{
+ int i;
+}
+
+%token<i> NUM
+%type<i> expr
+
+%left '+' '-'
+%left '*' '/' '%'
+
+%%
+
+input: | input expr '\n' { print("%d\n", $2); }
+
+expr: NUM { $$ = $1; }
+ | '(' expr ')' { $$ = $2; }
+ | expr '+' expr { $$ = $1 + $3; }
+ | expr '-' expr { $$ = $1 - $3; }
+ | expr '*' expr { $$ = $1 * $3; }
+ | expr '/' expr { $$ = $1 / $3; }
+ | expr '%' expr { $$ = $1 % $3; }
+
+%%
+
+void
+yyerror(char *s)
+{
+ fprint(2, "%s\n", s);
+}
+
+int
+getnum(int c)
+{
+ char s[64], *p;
+
+ for(p=s, *p++=c; isdigit(c=Bgetc(bf));)
+ if(p < s+sizeof(s)-1)
+ *p++ = c;
+ *p = 0;
+ Bungetc(bf);
+ return strtol(s, nil, 10);
+}
+
+int
+yylex(void)
+{
+ int n, c;
+
+ do
+ c = Bgetc(bf);
+ while(c != '\n' && isspace(c));
+ if(isdigit(c)){
+ yylval.i = getnum(c);
+ return NUM;
+ }else if(c == 'd'){
+ n = getnum(Bgetc(bf));
+ yylval.i = nrand(n != 0 ? n : 1);
+ return NUM;
+ }else
+ return c;
+}
+
+void
+main(int argc, char **argv)
+{
+ ARGBEGIN{
+ }ARGEND
+ if((bf = Bfdopen(0, OREAD)) == nil)
+ sysfatal("Bfdopen: %r");
+ yyparse();
+}
--- /dev/null
+++ b/cpick.c
@@ -1,0 +1,109 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#include <keyboard.h>
+#include <mouse.h>
+
+/* there should really be a color picker in paint(1) instead */
+
+Keyboardctl *kc;
+Mousectl *mc;
+Image *pic, *boxc;
+Point pan;
+Rectangle box;
+
+void
+putcol(Point m)
+{
+ char s[32];
+ uchar buf[4];
+ u32int v;
+ Point p;
+
+ p = addpt(screen->r.min, pan);
+ unloadimage(pic, rectsubpt(Rpt(m, Pt(m.x+1, m.y+1)), p), buf, sizeof buf);
+ loadimage(boxc, boxc->r, buf, sizeof buf);
+ draw(screen, rectaddpt(box, p), boxc, nil, ZP);
+ v = buf[0] << 16 | buf[1] << 8 | buf[2];
+ snprint(s, sizeof s, "%#08ux", v);
+ string(screen, addpt(box.min, p), display->black, ZP, font, s);
+ flushimage(display, 1);
+}
+
+void
+redraw(void)
+{
+ draw(screen, rectaddpt(pic->r, addpt(screen->r.min, pan)), pic, nil, ZP);
+ flushimage(display, 1);
+}
+
+Image *
+iconv(Image *i)
+{
+ Image *ni;
+
+ if(i->chan == XBGR32)
+ return i;
+ if((ni = allocimage(display, i->r, XBGR32, 0, DNofill)) == nil)
+ sysfatal("allocimage: %r");
+ draw(ni, ni->r, i, nil, ZP);
+ freeimage(i);
+ return ni;
+}
+
+void
+threadmain(int, char **)
+{
+ int fd;
+ Mouse m, om;
+ Rune r;
+
+ if(initdraw(nil, nil, "cpick") < 0)
+ sysfatal("initdraw: %r");
+ if((fd = open("/dev/screen", OREAD)) < 0)
+ sysfatal("open: %r");
+ if((pic = readimage(display, fd, 0)) == nil)
+ sysfatal("readimage: %r");
+ close(fd);
+ pic = iconv(pic);
+ box = screen->r;
+ if((boxc = allocimage(display, Rect(0,0,1,1), pic->chan, 1, DNofill)) == nil)
+ sysfatal("allocimage: %r");
+ redraw();
+ if((kc = initkeyboard(nil)) == nil)
+ sysfatal("initkeyboard: %r");
+ if((mc = initmouse(nil, _screen->display->image)) == nil)
+ sysfatal("initmouse: %r");
+ Alt a[] = {
+ {mc->resizec, nil, CHANRCV},
+ {mc->c, &m, CHANRCV},
+ {kc->c, &r, CHANRCV},
+ {nil, nil, CHANEND}
+ };
+ for(;;){
+ switch(alt(a)){
+ case 0:
+ if(getwindow(display, Refnone) < 0)
+ sysfatal("getwindow: %r");
+ redraw();
+ break;
+ case 1:
+ if((m.buttons & 4) == 4){
+ pan.x += m.xy.x - om.xy.x;
+ pan.y += m.xy.y - om.xy.y;
+ redraw();
+ }else
+ putcol(m.xy);
+ om = m;
+ break;
+ case 2:
+ switch(r){
+ case Kdel: case 'q': threadexitsall(nil);
+ }
+ break;
+ default:
+ threadexitsall("alt: %r");
+ }
+ }
+}
--- /dev/null
+++ b/hcol.c
@@ -1,0 +1,94 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <thread.h>
+#include <keyboard.h>
+#include <mouse.h>
+
+char id[16];
+Point ps;
+Image *col;
+Keyboardctl *kc;
+Mousectl *mc;
+
+void
+plaster(void)
+{
+ draw(screen, screen->r, col, nil, ZP);
+ string(screen, ps, display->black, ZP, font, id);
+ flushimage(display, 1);
+}
+
+Image *
+eallocim(ulong col)
+{
+ Image *i;
+
+ i = allocimage(display, Rect(0,0,1,1), RGB24, 1, col);
+ if(i == nil)
+ sysfatal("allocimage: %r");
+ return i;
+}
+
+void
+setid(u32int n)
+{
+ sprint(id, "%#ux", n);
+}
+
+void
+reset(void)
+{
+ ps = addpt(screen->r.min, stringsize(font, "A"));
+ plaster();
+}
+
+void
+threadmain(int, char **)
+{
+ u32int n;
+ char line[16];
+ Rune r;
+
+ if(initdraw(nil, nil, "hcol") < 0)
+ sysfatal("initdraw: %r");
+ kc = initkeyboard(nil);
+ if(kc == nil)
+ sysfatal("initkeyboard: %r");
+ mc = initmouse(nil, screen);
+ if(mc == nil)
+ sysfatal("initmouse: %r");
+ col = eallocim(DBlack);
+ setid(0);
+ reset();
+
+ Alt a[] = {
+ {mc->c, nil, CHANRCV},
+ {mc->resizec, nil, CHANRCV},
+ {kc->c, &r, CHANRCV},
+ {nil, nil, CHANEND}
+ };
+ for(;;)
+ switch(alt(a)){
+ case 1:
+ if(getwindow(display, Refnone) < 0)
+ sysfatal("getwindow: %r");
+ reset();
+ break;
+ case 2:
+ if(r == Kdel)
+ threadexitsall(nil);
+ memset(line, 0, sizeof line);
+ runetochar(line, &r);
+ if(enter(nil, line, 12, mc, kc, nil) < 0)
+ break;
+ n = strtoul(line, nil, 0);
+ setid(n);
+ freeimage(col);
+ col = eallocim(n<<8);
+ if(col == nil)
+ sysfatal("allocimage: %r");
+ plaster();
+ break;
+ }
+}
--- /dev/null
+++ b/lr.c
@@ -1,0 +1,159 @@
+/* lr - retarded recursive file list */
+#include <u.h>
+#include <libc.h>
+#include <String.h>
+
+extern void du(char*, Dir*);
+extern void err(char*);
+extern int seen(Dir*);
+extern int warn(char*);
+
+char *fmt = "%s\n";
+char *readbuf;
+int dflag;
+int nflag;
+
+void
+usage(void)
+{
+ fprint(2, "usage: %s [-dnQ] [file ...]\n", argv0);
+ exits("usage");
+}
+
+void
+main(int argc, char *argv[])
+{
+ int doqt;
+ char *s;
+
+ doqt = 1;
+ ARGBEGIN{
+ case 'd': dflag++; break;
+ case 'n': nflag++; dflag++; break;
+ case 'Q': doqt = 0; break;
+ default: usage();
+ }ARGEND
+ if(doqt){
+ doquote = needsrcquote;
+ quotefmtinstall();
+ fmt = "%q\n";
+ }
+ if(argc == 0)
+ du(".", dirstat("."));
+ else
+ while(*argv != nil){
+ s = *argv++;
+ du(s, dirstat(s));
+ }
+ exits(nil);
+}
+
+void
+dufile(char *name, Dir *d)
+{
+ String *s;
+
+ s = s_copy(name);
+ s_append(s, "/");
+ s_append(s, d->name);
+ print(fmt, s_to_c(s));
+ s_free(s);
+}
+
+void
+du(char *name, Dir *dir)
+{
+ int fd, i, n;
+ Dir *buf, *d;
+ String *s;
+
+ if(dir == nil){
+ warn(name);
+ return;
+ }
+ if((dir->qid.type & QTDIR) == 0){
+ if(!nflag)
+ print(fmt, name);
+ return;
+ }
+
+ fd = open(name, OREAD);
+ if(fd < 0){
+ warn(name);
+ return;
+ }
+ n = dirreadall(fd, &buf);
+ if(n < 0)
+ warn(name);
+ close(fd);
+ for(i=n, d=buf; i>0; i--, d++){
+ if((d->qid.type & QTDIR) == 0){
+ if(!nflag)
+ dufile(name, d);
+ continue;
+ }
+ if(strcmp(d->name, ".") == 0 || strcmp(d->name, "..") == 0
+ || seen(d))
+ continue; /* don't get stuck */
+
+ s = s_copy(name);
+ s_append(s, "/");
+ s_append(s, d->name);
+ du(s_to_c(s), d);
+ s_free(s);
+ }
+ free(buf);
+ if(dflag)
+ print(fmt, name);
+}
+
+#define NCACHE 256 /* must be power of two */
+
+typedef struct
+{
+ Dir* cache;
+ int n;
+ int max;
+} Cache;
+Cache cache[NCACHE];
+
+int
+seen(Dir *dir)
+{
+ Dir *dp;
+ int i;
+ Cache *c;
+
+ c = &cache[dir->qid.path&(NCACHE-1)];
+ dp = c->cache;
+ for(i=0; i<c->n; i++, dp++)
+ if(dir->qid.path == dp->qid.path &&
+ dir->type == dp->type &&
+ dir->dev == dp->dev)
+ return 1;
+ if(c->n == c->max){
+ if (c->max == 0)
+ c->max = 8;
+ else
+ c->max += c->max/2;
+ c->cache = realloc(c->cache, c->max*sizeof(Dir));
+ if(c->cache == nil)
+ err("malloc failure");
+ }
+ c->cache[c->n++] = *dir;
+ return 0;
+}
+
+void
+err(char *s)
+{
+ fprint(2, "du: %s: %r\n", s);
+ exits(s);
+}
+
+int
+warn(char *s)
+{
+ fprint(2, "du: %s: %r\n", s);
+ return 0;
+}
--- /dev/null
+++ b/mev.c
@@ -1,0 +1,167 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <keyboard.h>
+#include <mouse.h>
+
+int grx = 200 / 4, gry = 200 / 4;
+Image *gc, *uc, *nc;
+int mson, fix;
+Point p0, mp;
+Rectangle grabr, rt;
+
+void
+reset(void)
+{
+ Point p;
+
+ p0 = divpt(addpt(screen->r.min, screen->r.max), 2);
+ p = Pt(grx, gry);
+ grabr = Rpt(subpt(p0, p), addpt(p0, p));
+ rt = Rpt(screen->r.min, addpt(screen->r.min, Pt(8*12, font->height)));
+ draw(screen, screen->r, display->black, nil, ZP);
+ flushimage(display, 1);
+}
+
+void
+mproc(void)
+{
+ int fd, n, nerr;
+ char buf[1+5*12], s[16];
+ Mouse m;
+ Point p;
+ Rectangle r;
+
+ fd = open("/dev/mouse", ORDWR);
+ if(fd < 0)
+ sysfatal("open /dev/mouse: %r");
+ memset(&m, 0, sizeof m);
+ nerr = 0;
+ r = Rect(0,0,1,1);
+ mp = p0;
+ for(;;){
+ n = read(fd, buf, sizeof buf);
+ if(n != 1+4*12){
+ if(n < 0 || ++nerr > 10)
+ break;
+ continue;
+ }
+ nerr = 0;
+ switch(*buf){
+ case 'r':
+ if(getwindow(display, Refnone) < 0)
+ sysfatal("resize failed: %r");
+ reset();
+ case 'm':
+ p.x = strtol(buf+1+0*12, nil, 10);
+ p.y = strtol(buf+1+1*12, nil, 10);
+ if(!mson){
+ draw(screen, rectaddpt(r, p), nc, nil, ZP);
+ flushimage(display, 1);
+ break;
+ }
+ if(fix){
+ fix = 0;
+ goto res;
+ }
+ draw(screen, rt, display->black, nil, ZP);
+ sprint(s, "%d,%d", p.x - mp.x, p.y - mp.y);
+ string(screen, screen->r.min, uc, ZP, font, s);
+ draw(screen, rectaddpt(r, mp), uc, nil, ZP);
+ draw(screen, rectaddpt(r, p), gc, nil, ZP);
+ flushimage(display, 1);
+ if(!ptinrect(p, grabr)){
+ res:
+ fprint(fd, "m%d %d", p0.x, p0.y);
+ p = p0;
+ }
+ mp = p;
+ break;
+ }
+ }
+}
+
+void
+grab(void)
+{
+ static char nocurs[2*4+2*2*16];
+ static int fd = -1;
+
+ mson ^= 1;
+ if(mson){
+ fd = open("/dev/cursor", ORDWR|OCEXEC);
+ if(fd < 0){
+ fprint(2, "grab: %r\n");
+ return;
+ }
+ write(fd, nocurs, sizeof nocurs);
+ fix++;
+ }else if(fd >= 0){
+ close(fd);
+ fd = -1;
+ }
+}
+
+void
+usage(void)
+{
+ fprint(2, "usage: %s [-x dx] [-y dy]\n", argv0);
+ sysfatal("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ int n, fd, mpid;
+ char buf[256];
+ Rune r;
+
+ ARGBEGIN{
+ case 'x': grx = strtol(EARGF(usage()), nil, 0); break;
+ case 'y': gry = strtol(EARGF(usage()), nil, 0); break;
+ default: usage();
+ }ARGEND
+ if(initdraw(nil, nil, "mev") < 0)
+ sysfatal("initdraw: %r");
+ gc = allocimage(display, Rect(0,0,1,1), RGB24, 1, DGreen);
+ uc = allocimage(display, Rect(0,0,1,1), RGB24, 1, DRed);
+ nc = allocimage(display, Rect(0,0,1,1), RGB24, 1, DBlue);
+ reset();
+ mpid = rfork(RFPROC|RFMEM);
+ switch(mpid){
+ case -1:
+ sysfatal("fork: %r");
+ case 0:
+ mproc();
+ exits(nil);
+ }
+ fd = open("/dev/kbd", OREAD);
+ if(fd < 0)
+ sysfatal("open: %r");
+ for(;;){
+ if(buf[0] != 0){
+ n = strlen(buf)+1;
+ memmove(buf, buf+n, sizeof(buf)-n);
+ }
+ if(buf[0] == 0){
+ n = read(fd, buf, sizeof(buf)-1);
+ if(n <= 0)
+ break;
+ buf[n-1] = 0;
+ buf[n] = 0;
+ }
+ if(buf[0] == 'c'){
+ chartorune(&r, buf+1);
+ if(utfrune(buf, Kdel))
+ break;
+ else if(utfrune(buf, ' '))
+ grab();
+ else if(utfrune(buf, 'b')){
+ draw(screen, screen->r, display->black, nil, ZP);
+ flushimage(display, 1);
+ }
+ }
+ }
+ postnote(PNPROC, mpid, "shutdown");
+ exits(nil);
+}
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,22 @@
+</$objtype/mkfile
+TARG=\
+ calc\
+ cpick\
+ hcol\
+ lr\
+ mev\
+ rfx\
+ spd\
+ tev\
+ wstat\
+
+HFILES=
+YFLAGS=-D
+</sys/src/cmd/mkmany
+BIN=$home/bin/$objtype
+
+%.tab.h %.tab.c:D: %.y
+ $YACC $YFLAGS -s $stem $prereq
+
+(calc).c:R: \1.tab.c
+ mv $stem1.tab.c $stem1.c
--- /dev/null
+++ b/rfx.c
@@ -1,0 +1,444 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <thread.h>
+#include <keyboard.h>
+#include <mouse.h>
+#include <cursor.h>
+
+typedef struct Tg Tg;
+struct Tg{
+ Point;
+ Point v;
+ int r;
+ Tg *p;
+ Tg *n;
+};
+Tg troot;
+Tg *tg;
+
+vlong tics;
+int ntg;
+int hits, misses, misclicks;
+uint bmask = 1;
+int oldb;
+enum{
+ NSEC = 1000000000
+};
+/* recommend these are set well outside the confort range, but not so much so
+ * that every game lasts less than a minute */
+vlong div, div0 = NSEC/20; /* tic duration (ns) */
+int tddd = 220; /* tic duration decrease delay (tics) */
+int Δ0 = 100; /* tic duration delta factor */
+int mtdt = 5; /* tics/tddd % mtdt: delay to tgmax++ */
+int floatdt = 25; /* floating text delay (tics) */
+int newdt = 8; /* target spawn delay (tics) */
+int dmax, rmax = 24; /* max target radius (px) */
+int tgmax = 3; /* initial max number of targets onscreen */
+int missmax = 10; /* max targets let go */
+int nogrow; /* don't grow and shrink targets */
+int done;
+
+enum{
+ CBACK,
+ CTARG,
+ CMISS,
+ CTXT,
+ CEND
+};
+Image *col[CEND];
+Image *fb;
+Rectangle winxy; /* spawning area */
+Mousectl *mctl;
+Keyboardctl *kctl;
+Channel *ticc;
+
+#define trr() (rmax+1 - t->r % (rmax+1))
+#define tr() (nogrow ? rmax : t->r <= rmax ? t->r : trr())
+
+Image *
+eallocim(Rectangle r, int repl, ulong col)
+{
+ Image *i;
+
+ i = allocimage(display, r, screen->chan, repl, col);
+ if(i == nil)
+ sysfatal("allocimage: %r");
+ return i;
+}
+
+void
+tic(void *)
+{
+ vlong t, dt, δ;
+
+ t = nsec();
+ dt = div/1000000;
+ for(;;){
+ if(dt >= 0){
+ sleep(dt);
+ if(nbsend(ticc, nil) < 0)
+ break;
+ }
+ δ = div;
+ dt = (t + 2*δ - nsec()) / 1000000;
+ t += δ;
+ }
+}
+
+Tg *
+push(void)
+{
+ Tg *t;
+
+ t = mallocz(sizeof *t, 1);
+ if(t == nil)
+ sysfatal("talloc: %r");
+ t->p = tg->p;
+ t->n = tg;
+ tg->p->n = t;
+ tg->p = t;
+ return t;
+}
+
+void
+pop(Tg *t)
+{
+ t->p->n = t->n;
+ t->n->p = t->p;
+ free(t);
+}
+
+void
+nuke(void)
+{
+ while(tg->n != tg)
+ pop(tg->n);
+ ntg = 0;
+}
+
+void
+drw(Tg *t)
+{
+ int r;
+
+ r = tr();
+ fillellipse(fb, *t, r, r, col[CTARG], ZP);
+}
+
+void
+clr(Tg *t)
+{
+ draw(fb, Rpt(*t, t->v), col[CBACK], nil, ZP);
+}
+
+void
+txt(char *s, Tg *t, Image *i)
+{
+ Point o;
+
+ if(t == nil)
+ o = subpt(divpt(winxy.max, 2), divpt(stringsize(font, s), 2));
+ else{
+ o = divpt(stringsize(font, s), 2);
+ t->v = addpt(*t, o);
+ t->x -= o.x;
+ t->y -= o.y;
+ o = *t;
+ }
+ string(fb, o, i, ZP, font, s);
+}
+
+void
+misclk(Point o)
+{
+ Tg *t;
+
+ /* FIXME: often doesn't do anything */
+ fillellipse(fb, o, 1, 1, col[CTARG], ZP);
+ t = push();
+ t->v = addpt(o, Pt(2,2));
+ t->x = o.x - 1;
+ t->y = o.y - 1;
+ t->r = dmax+1;
+ misclicks++;
+}
+
+void
+hit(Tg *t)
+{
+ int r;
+
+ r = tr();
+ fillellipse(fb, *t, r, r, col[CBACK], ZP);
+ pop(t);
+ ntg--;
+ hits++;
+}
+
+void
+miss(Tg *t)
+{
+ int r;
+
+ r = nogrow ? rmax : 1;
+ fillellipse(fb, *t, r, r, col[CBACK], ZP);
+ txt("miss", t, col[CMISS]);
+ misses++;
+ ntg--;
+}
+
+void
+grow(Tg *t)
+{
+ if(nogrow)
+ return;
+ drw(t);
+}
+
+void
+shrink(Tg *t)
+{
+ int r;
+
+ if(nogrow)
+ return;
+ r = trr();
+ fillellipse(fb, *t, r, r, col[CBACK], ZP);
+ r--;
+ fillellipse(fb, *t, r, r, col[CTARG], ZP);
+}
+
+void
+spawn(void)
+{
+ Tg *t;
+
+ t = push();
+ ntg++;
+ t->x = winxy.min.x + nrand(winxy.max.x - winxy.min.x);
+ t->y = winxy.min.y + nrand(winxy.max.y - winxy.min.y);
+ if(nogrow){
+ t->r = rmax;
+ drw(t);
+ }
+}
+
+void
+check(int b, Point m)
+{
+ Point o;
+ Tg *t;
+
+ /* FIXME: implement counters for each button */
+ USED(b);
+ m = subpt(m, screen->r.min);
+ for(t=tg->n; t != tg; t=t->n){
+ if(t->r > dmax)
+ continue;
+ o = subpt(*t, m);
+ if(hypot(o.x, o.y) < tr()){
+ hit(t);
+ return;
+ }
+ }
+ misclk(m);
+}
+
+void
+plaster(void)
+{
+ draw(screen, screen->r, fb, nil, ZP);
+ flushimage(display, 1);
+}
+
+void
+score(void)
+{
+ char s[64];
+
+ sprint(s, "hit %d misses %d misclicks %d", hits, misses, misclicks);
+ txt(s, nil, col[CTXT]);
+ plaster();
+}
+
+void
+adv(void)
+{
+ Tg *t;
+
+ tics++;
+ if(tics % tddd == 0){
+ div -= div0 / Δ0;
+ if(tics / tddd % mtdt == 0)
+ tgmax++;
+ }
+
+ for(t=tg->n; t != tg; t=t->n){
+ t->r++;
+ if(t->r <= rmax)
+ grow(t);
+ else if(t->r > rmax && t->r <= dmax)
+ shrink(t);
+ else if(t->r == dmax+1){
+ miss(t);
+ if(misses >= missmax){
+ done++;
+ score();
+ }
+ }else if(t->r > dmax+floatdt){
+ clr(t);
+ pop(t);
+ }
+ }
+ if(tics % newdt == 0 && ntg < tgmax)
+ spawn();
+}
+
+void
+dreset(void)
+{
+ Tg *t;
+
+ winxy = rectsubpt(insetrect(screen->r, rmax), screen->r.min);
+ if(Dx(winxy) < 3*dmax || Dy(winxy) < 3*dmax)
+ sysfatal("window too small");
+ freeimage(fb);
+ fb = eallocim(Rect(0,0,Dx(screen->r),Dy(screen->r)), 0, DNofill);
+ draw(fb, fb->r, col[CBACK], nil, ZP);
+ for(t=tg->n; t != tg; t=t->n){
+ if(t->r > dmax)
+ continue;
+ drw(t);
+ }
+ if(done)
+ score();
+ plaster();
+}
+
+void
+init(void)
+{
+ div = div0;
+ tics = 0;
+ hits = misses = misclicks = 0;
+ done = 0;
+}
+
+void
+usage(void)
+{
+ print("%s [-g] [-d newdt] [-h div0] [-i Δ0] [-m nmiss] [-o rmax] [-r tddd] [-t 1-5]\n", argv0);
+ threadexitsall("usage");
+}
+
+void
+threadmain(int argc, char *argv[])
+{
+ Rectangle d;
+ Rune r;
+ Mouse m;
+
+ ARGBEGIN{
+ case 'd':
+ newdt = strtol(EARGF(usage()), nil, 0);
+ break;
+ case 'g':
+ nogrow++;
+ break;
+ case 'h':
+ div0 = strtoll(EARGF(usage()), nil, 0);
+ if(div0 <= 0)
+ sysfatal("invalid divisor %lld", div0);
+ div0 = NSEC / div0;
+ break;
+ case 'i':
+ Δ0 = strtol(EARGF(usage()), nil, 0);
+ if(Δ0 <= 0)
+ sysfatal("invalid divisor divisor %d", Δ0);
+ break;
+ case 'm':
+ missmax = strtol(EARGF(usage()), nil, 0);
+ break;
+ case 'o':
+ rmax = strtol(EARGF(usage()), nil, 0);
+ break;
+ case 'r':
+ tddd = strtol(EARGF(usage()), nil, 0);
+ break;
+ case 't':
+ bmask = 31 >> 5 - strtol(EARGF(usage()), nil, 0);
+ break;
+ default:
+ usage();
+ }ARGEND
+ dmax = rmax*2;
+
+ tg = &troot;
+ tg->n = tg->p = tg;
+ if(initdraw(nil, nil, "rfx") < 0)
+ sysfatal("initdraw: %r");
+ d = Rect(0,0,1,1);
+ col[CBACK] = display->black;
+ col[CTARG] = eallocim(d, 1, 0x990000ff);
+ col[CMISS] = eallocim(d, 1, 0x440000ff);
+ col[CTXT] = eallocim(d, 1, 0xbb4400ff);
+ dreset();
+
+ mctl = initmouse(nil, screen);
+ if(mctl == nil)
+ sysfatal("initmouse: %r");
+ kctl = initkeyboard(nil);
+ if(kctl == nil)
+ sysfatal("initkeyboard: %r");
+ ticc = chancreate(sizeof(int), 0);
+ if(ticc == nil)
+ sysfatal("chancreate: %r");
+ srand(time(nil));
+ init();
+ if(proccreate(tic, nil, 8192) < 0)
+ sysfatal("proccreate tproc: %r");
+
+ enum{ATIC, AMOUSE, ARESIZE, AKEY, AEND};
+ Alt a[AEND+1] = {
+ [ATIC] {ticc, nil, CHANRCV},
+ [AMOUSE] {mctl->c, &m, CHANRCV},
+ [ARESIZE] {mctl->resizec, nil, CHANRCV},
+ [AKEY] {kctl->c, &r, CHANRCV},
+ [AEND] {nil, nil, CHANEND}
+ };
+ for(;;){
+ switch(alt(a)){
+ case ATIC:
+ if(done)
+ break;
+ adv();
+ plaster();
+ break;
+ case AMOUSE:
+ if(!done && m.buttons & bmask && m.buttons & ~oldb)
+ check(m.buttons, m.xy);
+ oldb = m.buttons;
+ break;
+ case ARESIZE:
+ if(getwindow(display, Refnone) < 0)
+ sysfatal("getwindow: %r");
+ dreset();
+ break;
+ case AKEY:
+ switch(r){
+ case 'n':
+ nuke();
+ init();
+ draw(fb, fb->r, col[CBACK], nil, ZP);
+ plaster();
+ break;
+ case 'q':
+ case Kdel:
+ nuke();
+ threadexitsall(nil);
+ }
+ break;
+ }
+ }
+}
--- /dev/null
+++ b/solt.c
@@ -1,0 +1,294 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <thread.h>
+#include <keyboard.h>
+#include <mouse.h>
+#include <cursor.h>
+
+/* FIXME: shitty model, shitty implementation */
+
+enum{
+ NCARDS = 52,
+ NFLAVOR = 4,
+ NSTACKS = 8,
+ NHAND = NCARDS - ((NSTACKS+1)*(NSTACKS+2))/2 + 1
+};
+
+int c[NCARDS];
+
+typedef struct Card Card;
+struct Card{
+ int v;
+ char l[5];
+ Card* n;
+};
+Card *d[NCARDS];
+Card *s[NSTACKS];
+Card *h[NHAND];
+
+Image *ncol; /* string color */
+Image *selcol; /* selection color */
+Point spt; /* string pos */
+
+Mouse m;
+int resize;
+int kfd, mfd;
+
+
+void *
+emalloc(ulong n)
+{
+ void *b;
+
+ if((b = malloc(n)) == nil)
+ sysfatal("%s: %r", argv0);
+ return b;
+}
+
+void
+shuffle(void)
+{
+ int i, j, n;
+ int t[NCARDS];
+
+ srand(time(nil));
+ for(i = 0; i < NCARDS; i++)
+ t[i] = rand();
+ for(i = 0; i < NCARDS; i++){
+ n = 0;
+ for(j = 0; j < NCARDS; j++)
+ if(t[n] > t[j])
+ n = j;
+ c[i] = n;
+ t[n] = 0x10000;
+ }
+}
+
+char
+mkflavor(int n)
+{
+ switch(n % NFLAVOR){
+ case 0: return 'd';
+ case 1: return 'p';
+ case 2: return 'u';
+ case 3: return 'n';
+ }
+ return ' ';
+}
+
+void
+mkdeck(void)
+{
+ int i, v;
+
+ for(i = 0; i < NCARDS; i++){
+ d[i] = emalloc(sizeof **d);
+ v = c[i];
+ d[i]->v = v;
+ sprint(d[i]->l, "%d%c", 1 + v/NFLAVOR, mkflavor(v));
+ d[i]->n = nil;
+ }
+}
+
+void
+arrange(void)
+{
+ int i, j, n = 0;
+ Card *p;
+
+ for(i = 0; i < NSTACKS; i++)
+ s[i] = d[n++];
+ for(i = 0; i < NSTACKS; i++){
+ p = s[i];
+ for(j = NSTACKS-i-1; j < NSTACKS; j++)
+ p = p->n = d[n++];
+ }
+ for(i = 0; i < NHAND; i++)
+ h[i] = d[n++];
+}
+
+void
+drw(void)
+{
+ int i, n;
+ Card *p;
+ char b[10];
+ Point sxy = spt;
+
+ /* TODO: clear line */
+
+ for(i = 0; i < NSTACKS; i++){
+ p = s[i];
+ n = 0;
+ if(p == nil)
+ sprint(b, " () ");
+ else{
+ while(p->n != nil){
+ n++;
+ p = p->n;
+ }
+ snprint(b, 10, "(%d)%s ", n, p->l);
+ }
+ string(screen, sxy, ncol, ZP, font, b);
+ sxy.x += 8 * font->width;
+ }
+ flushimage(display, 1);
+}
+
+void
+select(Point *p)
+{
+ /* TODO: selecting a card */
+ USED(p);
+}
+
+void
+kth(void *)
+{
+ int k;
+ char buf[256];
+ char *s;
+ Rune r;
+
+ if((kfd = open("/dev/kbd", OREAD)) < 0)
+ sysfatal("%s: open /dev/kbd: %r", argv0);
+ for(;;){
+ if(read(kfd, buf, sizeof(buf)-1) <= 0)
+ sysfatal("%s: read /dev/kbd: %r", argv0);
+ if(buf[0] == 'c' && utfrune(buf, Kdel))
+ threadexitsall(nil);
+ if(buf[0] != 'k' && buf[0] != 'K')
+ continue;
+
+ s = buf + 1;
+ k = 0;
+ while(*s != 0){
+ s += chartorune(&r, s);
+ switch(r){
+ case Kdel:
+ threadexitsall(nil);
+ }
+ }
+ }
+ /* TODO: make other threads exit cleanly? send to threadmain? */
+}
+
+void
+mth(void *)
+{
+ int n, nerr;
+ Mouse m;
+ char buf[1+5*12];
+
+ if((mfd = open("/dev/mouse", OREAD)) < 0)
+ sysfatal("open: %r");
+ memset(&m, 0, sizeof m);
+ nerr = 0;
+
+ for(;;){
+ n = read(mfd, buf, sizeof buf);
+ if(n != 1+4*12){
+ fprint(2, "mproc: bad count %d not 49: %r\n", n);
+ if(n < 0 || ++nerr > 10)
+ break;
+ continue;
+ }
+ nerr = 0;
+
+ switch(buf[0]){
+ case 'r':
+ resize = 1;
+ /* TODO: something */
+ break;
+ case 'm':
+ m.xy.x = atoi(buf+1+0*12);
+ m.xy.y = atoi(buf+1+1*12);
+ m.buttons = atoi(buf+1+2*12);
+ //m.msec = atoi(buf+1+3*12);
+ break;
+ }
+ }
+}
+
+void
+croak(void)
+{
+ Card **p = d;
+
+ close(kfd);
+ close(mfd);
+ while(p != d + NCARDS)
+ free(*p++);
+ freeimage(ncol);
+ freeimage(selcol);
+}
+
+void
+reset(void)
+{
+ draw(screen, screen->r, display->black, nil, ZP);
+ shuffle();
+ mkdeck();
+ arrange();
+ drw();
+}
+
+void
+init(void)
+{
+ /* TODO: why the difference between proc and thread? */
+
+ if(proccreate(kth, nil, mainstacksize) < 0)
+ sysfatal("%s threadcreate kproc: %r", argv0);
+ if(proccreate(mth, nil, mainstacksize) < 0)
+ sysfatal("%s threadcreate mproc: %r", argv0);
+
+ if(initdraw(nil, nil, "solt") < 0)
+ sysfatal("%s initdraw: %r", argv0);
+ ncol = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x551100ff);
+ if(ncol == nil)
+ sysfatal("%s allocimage: %r", argv0);
+ selcol = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x882200ff);
+ if(selcol == nil)
+ sysfatal("%s allocimage: %r", argv0);
+ print("selcol %p %p %d\n", selcol->display->bufp, selcol->display->buf, selcol->display->bufsize);
+ /* problem: font->width == 0 at this point? */
+ spt = Pt(screen->r.min.x + Dx(screen->r)/2 - 8*NSTACKS*4,
+ screen->r.min.y + Dy(screen->r)/2);
+
+ reset();
+ atexit(croak);
+}
+
+void
+usage(void)
+{
+ print("usage: %s\n", argv0);
+ threadexitsall("usage");
+}
+
+void
+threadmain(int argc, char *argv[])
+{
+ ARGBEGIN{
+ default:
+ usage();
+ }ARGEND
+
+ /* TODO: playing the game */
+ /* TODO: hand, 4 finish spots, 4 off spots */
+ /* TODO: mouse menu (exit, save card, stack card, restart, new, ) */
+ /* TODO: moving shit around */
+ /* TODO: resize handling */
+ /* TODO: exit by communicating by channel */
+ /* TODO: calculate distance between card slots programatically */
+
+ init();
+ /* FIXME: do shit (setup channels) */
+ for(;;){
+ /*if(recv(kchan, nil) < 0)
+ threadexitsall("%s recv: %r");*/
+ sleep(100);
+ }
+}
--- /dev/null
+++ b/spd.c
@@ -1,0 +1,413 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <draw.h>
+#include <thread.h>
+#include <keyboard.h>
+#include <mouse.h>
+#include <cursor.h>
+
+enum{
+ KMAX = 24 * UTFmax,
+ DICTLEN = 32<<10,
+ TMAX = 32,
+};
+char dict[DICTLEN][KMAX];
+int ndic;
+char *df = "/lib/words";
+typedef struct Txt Txt;
+struct Txt{
+ Point;
+ char s[KMAX];
+ char *m;
+ int d;
+ Image *c;
+ Txt *p;
+ Txt *n;
+};
+Txt t0, *tl, *cur;
+
+vlong tics;
+int nt, words, miss, mistyp;
+enum{
+ NSEC = 1000000000
+};
+vlong div, div0 = NSEC/2; /* tic duration (ns) */
+int dage = 16;
+int tddd = 64; /* tic duration decrease delay (tics) */
+int newdt = 3; /* target spawn delay (tics) */
+int missmax = 10; /* max targets let go */
+long tm0;
+int done;
+
+enum{
+ CBACK,
+ CTXT,
+ CSTALE,
+ COLD,
+ CCUR,
+ CEND
+};
+Image *fb, *col[CEND];
+Point fs, wc;
+int dy;
+Mousectl *mc;
+Keyboardctl *kc;
+Channel *tc;
+
+Image *
+eallocim(Rectangle r, int repl, ulong col)
+{
+ Image *i;
+
+ i = allocimage(display, r, screen->chan, repl, col);
+ if(i == nil)
+ sysfatal("allocimage: %r");
+ return i;
+}
+
+void
+tic(void *)
+{
+ vlong t, dt, δ;
+
+ t = nsec();
+ dt = div/1000000;
+ for(;;){
+ if(dt >= 0){
+ sleep(dt);
+ if(nbsend(tc, nil) < 0)
+ break;
+ }
+ δ = div;
+ dt = (t + 2*δ - nsec()) / 1000000;
+ t += δ;
+ }
+}
+
+void
+pop(Txt *t)
+{
+ t->p->n = t->n;
+ t->n->p = t->p;
+ nt--;
+ free(t);
+ if(cur == t)
+ cur = nil;
+}
+
+void
+nuke(void)
+{
+ while(tl->n != tl)
+ pop(tl->n);
+}
+
+void
+drw(Txt *t, Image *c)
+{
+ if(c == nil)
+ c = t->c;
+ string(fb, *t, c, ZP, font, t->s);
+}
+
+void
+drwcur(void)
+{
+ char c;
+
+ c = *cur->m;
+ *cur->m = 0;
+ drw(cur, col[CCUR]);
+ *cur->m = c;
+}
+
+void
+spawn(void)
+{
+ Txt *t;
+
+ t = mallocz(sizeof *t, 1);
+ if(t == nil)
+ sysfatal("talloc: %r");
+ t->p = tl->p;
+ t->n = tl;
+ tl->p->n = t;
+ tl->p = t;
+ nt++;
+ sprint(t->s, dict[nrand(ndic)]);
+ t->c = col[CTXT];
+ t->y = nrand(dy);
+ drw(t, nil);
+}
+
+void
+plaster(void)
+{
+ draw(screen, screen->r, fb, nil, ZP);
+ flushimage(display, 1);
+}
+
+void
+score(void)
+{
+ Point o;
+ long t;
+ char s[72];
+
+ /* FIXME */
+ /* this wpm figure is bullshit */
+ t = (time(nil) - tm0) / 60;
+ sprint(s, "words %d misses %d mistypes %d in %lds",
+ words, miss, mistyp, t);
+ o = subpt(wc, divpt(stringsize(font, s), 2));
+ string(fb, o, col[CCUR], ZP, font, s);
+ plaster();
+}
+
+void
+adv(void)
+{
+ Txt *t;
+
+ tics++;
+ if(tics % tddd == 0)
+ div -= div0 / 16;
+
+ for(t=tl->n; t != tl; t=t->n){
+ t->d++;
+ drw(t, col[CBACK]);
+ t->x += fs.x;
+ if(t->d % dage == 0){
+ switch(t->d / dage){
+ case 1: t->c = col[CSTALE]; break;
+ case 2: t->c = col[COLD]; break;
+ case 3:
+ miss++;
+ if(miss >= missmax){
+ done++;
+ score();
+ }
+ pop(t);
+ continue;
+ }
+ }
+ drw(t, nil);
+ }
+ if(cur != nil)
+ drwcur();
+ if(tics % newdt == 0 && nt < TMAX)
+ spawn();
+ if(tl->n == tl){
+ spawn();
+ tics += newdt - tics % newdt;
+ }
+}
+
+int
+check(Rune k)
+{
+ Txt *t;
+
+ if(cur == nil){
+ for(t=tl->n; t != tl; t=t->n)
+ if(utfrune(t->s, k) == t->s){
+ t->m = t->s + runelen(k);
+ cur = t;
+ drwcur();
+ return 1;
+ }
+ }else if(utfrune(cur->m, k) == cur->m){
+ cur->m += runelen(k);
+ if(cur->m >= cur->s + strlen(cur->s)){
+ words++;
+ drw(cur, col[CBACK]);
+ pop(cur);
+ cur = nil;
+ }else
+ drwcur();
+ return 1;
+ }
+ mistyp++;
+ return 0;
+}
+
+void
+dreset(void)
+{
+ Txt *t;
+
+ dy = Dy(screen->r) - fs.y;
+ if(dy < 4 * fs.y || Dx(screen->r) < KMAX * fs.x / 8)
+ sysfatal("window too small");
+ wc = Pt(Dx(screen->r)/2, Dy(screen->r)/2);
+ freeimage(fb);
+ fb = eallocim(Rect(0,0,Dx(screen->r),Dy(screen->r)), 0, DNofill);
+ draw(fb, fb->r, col[CBACK], nil, ZP);
+ for(t=tl->n; t != tl; t=t->n)
+ drw(t, nil);
+ if(done)
+ score();
+ plaster();
+}
+
+void
+init(void)
+{
+ div = div0;
+ tics = 0;
+ words = miss = mistyp = 0;
+ tm0 = time(nil);
+ done = 0;
+ spawn();
+ plaster();
+}
+
+void
+menu(void)
+{
+ enum{NEW, QUIT};
+ static char *item[] = {
+ [NEW] "new",
+ [QUIT] "quit",
+ nil
+ };
+ static Menu m = {
+ item, nil, 0
+ };
+
+ switch(menuhit(3, mc, &m, nil)){
+ case NEW:
+ nuke();
+ init();
+ draw(fb, fb->r, col[CBACK], nil, ZP);
+ plaster();
+ break;
+ case QUIT:
+ nuke();
+ threadexitsall(nil);
+ }
+}
+
+void
+load(char *f)
+{
+ int n;
+ char *s, (*p)[KMAX];
+ Biobuf *bf;
+
+ bf = Bopen(f, OREAD);
+ if(bf == nil)
+ sysfatal("load: %r");
+ p = dict;
+ while(s = Brdline(bf, '\n'), s != nil && (char*)p < dict[nelem(dict)]){
+ n = Blinelen(bf);
+ if(n > sizeof *p)
+ n = sizeof *p;
+ memcpy(*p, s, n-1);
+ ndic++;
+ p++;
+ }
+ Bterm(bf);
+}
+
+void
+usage(void)
+{
+ print("%s [-d n] [-h hz] [-m n] [-o tics] [-r tics] [dict]\n", argv0);
+ threadexitsall("usage");
+}
+
+void
+threadmain(int argc, char *argv[])
+{
+ double d;
+ Rectangle r;
+ Rune k;
+ Mouse m;
+
+ ARGBEGIN{
+ case 'd':
+ newdt = strtol(EARGF(usage()), nil, 0);
+ break;
+ case 'h':
+ d = strtod(EARGF(usage()), nil);
+ if(d <= 0.0)
+ sysfatal("invalid divisor %f", d);
+ div0 = NSEC / d;
+ break;
+ case 'm':
+ missmax = strtol(EARGF(usage()), nil, 0);
+ break;
+ case 'o':
+ dage = strtol(EARGF(usage()), nil, 0);
+ break;
+ case 'r':
+ tddd = strtol(EARGF(usage()), nil, 0);
+ break;
+ default:
+ usage();
+ }ARGEND
+ if(argc > 0)
+ df = *argv;
+ load(df);
+
+ tl = &t0;
+ tl->n = tl->p = tl;
+
+ if(initdraw(nil, nil, "spd") < 0)
+ sysfatal("initdraw: %r");
+ fs = stringsize(font, "A");
+ r = Rect(0,0,1,1);
+ col[CBACK] = display->black;
+ col[CTXT] = eallocim(r, 1, 0xbb4400ff);
+ col[CSTALE] = eallocim(r, 1, 0x990000ff);
+ col[COLD] = eallocim(r, 1, 0x440000ff);
+ col[CCUR] = eallocim(r, 1, 0xdd9300ff);
+ dreset();
+
+ mc = initmouse(nil, screen);
+ if(mc == nil)
+ sysfatal("initmouse: %r");
+ kc = initkeyboard(nil);
+ if(kc == nil)
+ sysfatal("initkeyboard: %r");
+ tc = chancreate(sizeof(int), 0);
+ if(tc == nil)
+ sysfatal("chancreate: %r");
+ srand(time(nil));
+ init();
+ if(proccreate(tic, nil, 8192) < 0)
+ sysfatal("proccreate tproc: %r");
+
+ enum{ATIC, AMOUSE, ARESIZE, AKEY, AEND};
+ Alt a[AEND+1] = {
+ [ATIC] {tc, nil, CHANRCV},
+ [AMOUSE] {mc->c, &m, CHANRCV},
+ [ARESIZE] {mc->resizec, nil, CHANRCV},
+ [AKEY] {kc->c, &k, CHANRCV},
+ [AEND] {nil, nil, CHANEND}
+ };
+ for(;;)
+ switch(alt(a)){
+ case ATIC:
+ if(done)
+ break;
+ adv();
+ plaster();
+ break;
+ case AMOUSE:
+ if(m.buttons & 4)
+ menu();
+ break;
+ case ARESIZE:
+ if(getwindow(display, Refnone) < 0)
+ sysfatal("getwindow: %r");
+ dreset();
+ break;
+ case AKEY:
+ if(check(k))
+ plaster();
+ break;
+ }
+}
--- /dev/null
+++ b/tev.c
@@ -1,0 +1,98 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <keyboard.h>
+#include <mouse.h>
+
+void
+mproc(void)
+{
+ int fd, n, nerr;
+ char buf[1+5*12];
+ Mouse m;
+
+ fd = open("/dev/mouse", ORDWR);
+ if(fd < 0)
+ sysfatal("open /dev/mouse: %r");
+ memset(&m, 0, sizeof m);
+ nerr = 0;
+ for(;;){
+ n = read(fd, buf, sizeof buf);
+ if(n != 1+4*12){
+ if(n < 0 || ++nerr > 10)
+ break;
+ continue;
+ }
+ nerr = 0;
+ switch(*buf){
+ case 'r':
+ print("[r]\n");
+ /* wet floor */
+ case 'm':
+ m.xy.x = atoi(buf+1+0*12);
+ m.xy.y = atoi(buf+1+1*12);
+ m.buttons = atoi(buf+1+2*12);
+ m.msec = atoi(buf+1+3*12);
+ print("[m] %d,%d %d %lud\n", m.xy.x, m.xy.y, m.buttons, m.msec);
+ break;
+ }
+ }
+}
+
+void
+main(int, char **)
+{
+ int n, fd, mpid;
+ char buf[256], *s;
+ Rune r;
+
+ mpid = fork();
+ switch(mpid){
+ case -1:
+ sysfatal("fork: %r");
+ case 0:
+ mproc();
+ exits(nil);
+ }
+ fd = open("/dev/kbd", OREAD);
+ if(fd < 0)
+ sysfatal("open: %r");
+ for(;;){
+ if(buf[0] != 0){
+ n = strlen(buf)+1;
+ memmove(buf, buf+n, sizeof(buf)-n);
+ }
+ if(buf[0] == 0){
+ print("\n");
+ n = read(fd, buf, sizeof(buf)-1);
+ if(n <= 0)
+ break;
+ buf[n-1] = 0;
+ buf[n] = 0;
+ }
+ s = buf+1;
+ switch(buf[0]){
+ case 'c':
+ chartorune(&r, s);
+ print("[c] %C %#ux ", r, r);
+ if(utfrune(buf, Kdel))
+ goto done;
+ break;
+ case 'k':
+ case 'K':
+ print("[%c] ", buf[0]);
+ while(*s){
+ s += chartorune(&r, s);
+ print("%C %#ux ", r, r);
+ if(r == Kdel)
+ goto done;
+ }
+ break;
+ default:
+ print("unknown message %c\n", *buf);
+ }
+ }
+done:
+ postnote(PNPROC, mpid, "shutdown");
+ exits(nil);
+}
--- /dev/null
+++ b/wstat.c
@@ -1,0 +1,31 @@
+#include <u.h>
+#include <libc.h>
+
+#define usage() (sysfatal("usage: no."))
+
+void
+main(int argc, char **argv)
+{
+ int fd;
+ Dir d;
+
+ nulldir(&d);
+ ARGBEGIN{
+ case 'g': d.gid = EARGF(usage()); break;
+ case 'l': d.length = strtoll(EARGF(usage()), nil, 0); break;
+ case 'm': d.mtime = strtoll(EARGF(usage()), nil, 0); break;
+ case 'n': d.name = EARGF(usage()); break;
+ case 'p': d.mode = strtoll(EARGF(usage()), nil, 0); break;
+ case 'u': d.uid = EARGF(usage()); break;
+ default: usage();
+ }ARGEND
+ if(*argv == nil)
+ usage();
+ fd = open(*argv, OWRITE);
+ if(fd < 0)
+ sysfatal("open: %r");
+ if(dirfwstat(fd, &d) < 0)
+ sysfatal("dirfwstat: %r");
+ close(fd);
+ exits(nil);
+}