git: 9front

Download patch

ref: 133036cf2397e1e0db66538d3a83cd3660b34b96
parent: fd0b5ac84bae00043d79660d627f511e31bb0146
parent: 1de2653cd337fbf319ef8aa34a8b13ff122d6a76
author: cinap_lenrek <cinap_lenrek@centraldogma>
date: Mon May 9 04:32:55 EDT 2011

merge

--- a/.hgignore
+++ b/.hgignore
@@ -1,6 +1,10 @@
 syntax: regexp
 ^sys/src/.*\.[ao]?[12578vqki]?$
+^sys/src/(.*/)?[12578vqki]\..*$
 ^sys/lib/python/.*\.(pyo|pyc|exe)$
 ^(dev|fd|net|srv|env|root|boot|mnt|n|bin|tmp)/
 ^(386|68000|68020|alpha|amd64|arm|power|power64|sparc|sparc64)/(bin|lib)/
+^386/(9(pc|boot).*|pbs|mbr|init)
 ^acme/bin/(386|68000|68020|alpha|amd64|arm|power|power64|sparc|sparc64)/
+^usr/
+^sys/lib/pkg
\ No newline at end of file
--- a/cfg/cirno/cpurc
+++ b/cfg/cirno/cpurc
@@ -6,10 +6,9 @@
 # ip/ipconfig -g your-gateway ether /net/ether0 your-ip-address your-subnet-mask
 
 # example: adjust to fit your network
-ip/ipconfig -g 192.168.0.1 ether /net/ether0 192.168.0.2 255.255.255.0
-ndb/dns -rs
+#ip/ipconfig -g 192.168.0.1 ether /net/ether0 192.168.0.2 255.255.255.0
+#ndb/dns -rs
+#aux/timesync -Ln pool.ntp.org
 
 # outgoing mail will appear to originate from this domain
-site=9front
-
-ntp=pool.ntp.org
+#site=9front
--- a/cfg/cirno/termrc
+++ b/cfg/cirno/termrc
@@ -1,8 +1,14 @@
+#!/bin/rc
+# cpu-specific startup
+
+# Since booting from venti could have started loopback,
+# don't test for existing interfaces, just use ipconfig.
+# ip/ipconfig -g your-gateway ether /net/ether0 your-ip-address your-subnet-mask
+
 # example: adjust to fit your network
-ip/ipconfig -g 192.168.0.1 ether /net/ether0 192.168.0.2 255.255.255.0
-ndb/dns -r
+#ip/ipconfig -g 192.168.0.1 ether /net/ether0 192.168.0.2 255.255.255.0
+#ndb/dns -r
+#aux/timesync -Ln pool.ntp.org
 
 # outgoing mail will appear to originate from this domain
-site=9front
-
-ntp=pool.ntp.org
+#site=9front
--- a/lib/ndb/auth
+++ b/lib/ndb/auth
@@ -9,3 +9,5 @@
 #           hostid=bootes
 #                uid=!sys uid=!adm uid=*
 # 
+hostid=bootes
+	uid=!sys uid=!adm uid=*
--- a/lib/ndb/local
+++ b/lib/ndb/local
@@ -8,9 +8,9 @@
 
 auth=sources.cs.bell-labs.com authdom=outside.plan9.bell-labs.com
 
-auth=cirno.9front authdom=9front
+#auth=cirno.9front authdom=9front
 
-ntp=pool.ntp.org
+#ntp=pool.ntp.org
 
 #
 #  because the public demands the name localsource
@@ -18,12 +18,12 @@
 ip=127.0.0.1 sys=localhost dom=localhost
 
 # example: adjust to fit your network
-ipnet=9front ip=192.168.0.0 ipmask=255.255.255.0
-	auth=cirno.9front
-	cpu=cirno.9front
-	dns=192.168.0.2
-	dnsdom=9front
-	smtp=cirno.9front
-
-ip=192.168.0.1 sys=gw dom=gw.9front
-ip=192.168.0.2 sys=cirno dom=cirno.9front
+#ipnet=9front ip=192.168.0.0 ipmask=255.255.255.0
+#	auth=cirno.9front
+#	cpu=cirno.9front
+#	dns=192.168.0.2
+#	dnsdom=9front
+#	smtp=cirno.9front
+#
+#ip=192.168.0.1 sys=gw dom=gw.9front
+#ip=192.168.0.2 sys=cirno dom=cirno.9front
--- /dev/null
+++ b/rc/bin/hold
@@ -1,0 +1,6 @@
+#!/bin/rc
+{
+	echo holdon >[1=3]
+	cat $1 > /dev/cons
+	cat /dev/cons > $1
+} >[3]/dev/consctl
--- a/sys/src/9/pc/sdata.c
+++ b/sys/src/9/pc/sdata.c
@@ -2037,6 +2037,7 @@
 		case (0x24CB<<16)|0x8086:	/* 82801DB (ICH4, High-End) */
 		case (0x24DB<<16)|0x8086:	/* 82801EB (ICH5) */
 		case (0x25A3<<16)|0x8086:	/* 6300ESB (E7210) */
+		case (0x2653<<16)|0x8086:	/* 82801FBM SATA */
 		case (0x266F<<16)|0x8086:	/* 82801FB (ICH6) */
 		case (0x27DF<<16)|0x8086:	/* 82801G SATA (ICH7) */
 		case (0x27C0<<16)|0x8086:	/* 82801GB SATA AHCI (ICH7) */
--- a/sys/src/cmd/cwfs/con.c
+++ b/sys/src/cmd/cwfs/con.c
@@ -740,11 +740,22 @@
 	print("%ld out of %ld files used\n", n, conf.nfile);
 }
 
+void
+cmd_chatty(int argc, char *argv[])
+{
+	if(argc < 2) {
+		print("cmd_chatty: usage: chatty n\n");
+		return;
+	}
+	chatty = atoi(argv[1]);
+}
+
 static void
 installcmds(void)
 {
 	cmd_install("allow", "-- disable permission checking", cmd_allow);
 	cmd_install("cfs", "[file] -- set current filesystem", cmd_cfs);
+	cmd_install("chatty", "n -- set chattiness", cmd_chatty);
 	cmd_install("clean", "file [bno [addr]] -- block print/fix", cmd_clean);
 	cmd_install("check", "[options]", cmd_check);
 	cmd_install("clri", "[file ...] -- purge files/dirs", cmd_clri);
--- /dev/null
+++ b/sys/src/cmd/pkg/create
@@ -1,0 +1,21 @@
+#!/bin/rc -e
+
+i=`{basename $1}
+d=$1
+echo Creating $i
+C=`{pwd}
+@{
+rfork en
+cd $d
+mkdir /tmp/$i
+mk
+divergefs -p /tmp/$i /
+mk install clean
+unmount /
+}
+cd /tmp/$i/files
+rm -r env
+tar cv * | bzip2 -9 > $C/$i.tbz
+cd /tmp
+rm -r $i
+echo Created $C/$i.tbz
--- /dev/null
+++ b/sys/src/cmd/pkg/install
@@ -1,0 +1,12 @@
+#!/bin/rc -e
+
+cd /
+mkdir -p /sys/lib/pkg
+if (test -s /sys/lib/pkg/$1) {
+	echo $i already installed
+	exit
+}
+echo Installing $1
+hget http://pkg.violetti.org/$cputype/$1.tbz | bunzip2 | pkg/unpkg>[2]/sys/lib/pkg/$1
+echo Done
+
--- /dev/null
+++ b/sys/src/cmd/pkg/list
@@ -1,0 +1,3 @@
+#!/bin/rc
+
+hget http://pkg.violetti.org/$cputype | htmlfmt | grep '\.tbz' | sed -e 's/\.tbz$//'
--- /dev/null
+++ b/sys/src/cmd/pkg/mkfile
@@ -1,0 +1,20 @@
+</$objtype/mkfile
+
+all: $O.unpkg
+	echo
+
+$O.unpkg: unpkg.c
+	$CC unpkg.c
+	$LD -o $O.unpkg unpkg.$O
+
+install:V: $O.unpkg
+	mkdir -p /$objtype/bin/pkg
+	cp $O.unpkg /$objtype/bin/pkg/unpkg
+	cp create install list remove /$objtype/bin/pkg
+
+clean:
+	rm -f $O.unpkg *.$O
+
+nuke: clean
+	rm -f /$objtype/bin/pkg/*
+
--- /dev/null
+++ b/sys/src/cmd/pkg/remove
@@ -1,0 +1,18 @@
+#!/bin/rc -e
+
+cd /
+if(test -s /sys/lib/pkg/$1) {
+	fs=(`{cat /sys/lib/pkg/$1 | awk '{print $1}'})
+	ss=(`{cat /sys/lib/pkg/$1 | awk '{print $2}'})
+	for(i in `{seq $#fs}) {
+		s=`{sha1sum $fs($i) | awk '{print $1}' | tr a-z A-Z}
+		if(test $s '=' $ss($i)) {
+			echo D $fs($i)
+			rm $fs($i)
+		} 
+		if not {
+			echo M $fs($i) NOT DELETING
+		}
+	}
+	rm /sys/lib/pkg/$1
+}
--- /dev/null
+++ b/sys/src/cmd/pkg/unpkg.c
@@ -1,0 +1,100 @@
+#include <u.h>
+#include <libc.h>
+#include <mp.h>
+#include <libsec.h>
+
+struct th {
+  char *name;
+  ulong perm;
+  ulong size;
+  char type;
+  char *user, *group;
+};
+
+static char *sndup(char* s, ulong n) {
+	char *d, *p;
+	p = memchr(s, 0, n);
+	if(p)
+		n = p-s;
+	d = malloc(n+1);
+	memcpy(d,s,n);
+	d[n] = 0;
+	return d;
+}
+
+
+int readheader(int fd, struct th* th) {
+  int i;
+  char b[512];
+  if(readn(fd, b, 512) != 512) return -1;
+  
+  // Check for end of archive
+  for(i=0; i<512; i++) {
+	if(b[i]!=0) goto rhok;
+  }
+  if(readn(fd, b, 512) != 512) return -1;
+  for(i=0; i<512; i++) {
+	if(b[i]!=0) return -1;
+  }
+  return 0;
+
+ rhok:
+  th->name = sndup(b, 100);
+  th->perm = strtoul(b+100, nil, 8);
+  th->size = strtoul(b+124, nil, 8);
+  th->type = b[156];
+  th->user = sndup(b+265, 32);
+  th->group= sndup(b+297, 32);
+  return 1;
+}
+
+int main(void) {
+  while(1) {
+	struct th th;
+	ulong off;
+	uchar b[512];
+	DigestState *s;
+	int wfd;
+	int r = readheader(0, &th);
+	if(r <= 0) return r;
+
+	switch(th.type) {
+	case '5':
+		create(th.name, OREAD, DMDIR|th.perm);
+		break;
+	case '0': case 0:
+		print("A %s\n", th.name);
+		r = access(th.name, 0);
+		if(r == 0) {
+			print("File already exists: %s\n", th.name);
+			return -1;
+		}
+		if((wfd = create(th.name, OWRITE, th.perm)) < 0) {
+			print("Create failed: %s\n", th.name);
+			return -1;
+		}
+		s = nil;
+		for(off=0; off<th.size; off+=512) {
+			int n = th.size-off;
+			n = n<512 ? n : 512;
+			if(readn(0, b, 512) != 512) return -1;
+			if(write(wfd, b, n) != n) return -1;
+			s = sha1(b, n, nil, s);
+		}
+
+		uchar digest[20], hdigest[41];
+		sha1(nil, 0, digest, s);
+		enc16((char*)hdigest, 41, digest, 20);
+		fprint(2, "%s\t%s\n", th.name, hdigest);
+		close(wfd);
+		break;
+	default:
+		print("Unknown file type '%c'\n", th.type);
+		return -1;
+	}
+
+	free(th.name);
+	free(th.user);
+	free(th.group);
+  }
+}
--- a/sys/src/cmd/ramfs.c
+++ b/sys/src/cmd/ramfs.c
@@ -157,9 +157,11 @@
 	int p[2];
 	int fd;
 	int stdio = 0;
+	int mountflags;
 
 	service = "ramfs";
 	defmnt = "/tmp";
+	mountflags = 0;
 	ARGBEGIN{
 	case 'i':
 		defmnt = 0;
@@ -186,9 +188,20 @@
 		defmnt = 0;
 		service = EARGF(usage());
 		break;
+	case 'b':
+		mountflags |= MBEFORE;
+		break;
+	case 'c':
+		mountflags |= MCREATE;
+		break;
+	case 'a':
+		mountflags |= MAFTER;
+		break;
 	default:
 		usage();
 	}ARGEND
+	if(mountflags == 0)
+		mountflags = MREPL | MCREATE;
 
 	if(pipe(p) < 0)
 		error("pipe failed");
@@ -239,7 +252,7 @@
 		break;
 	default:
 		close(p[0]);	/* don't deadlock if child fails */
-		if(defmnt && mount(p[1], -1, defmnt, MREPL|MCREATE, "") < 0)
+		if(defmnt && mount(p[1], -1, defmnt, mountflags, "") < 0)
 			error("mount failed");
 	}
 	exits(0);
@@ -902,6 +915,6 @@
 void
 usage(void)
 {
-	fprint(2, "usage: %s [-Dipsu] [-m mountpoint] [-S srvname]\n", argv0);
+	fprint(2, "usage: %s [-Dipsubac] [-m mountpoint] [-S srvname]\n", argv0);
 	exits("usage");
 }
--- /dev/null
+++ b/sys/src/games/glendy.c
@@ -1,0 +1,530 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <event.h>
+
+enum{
+	/* difficulty levels (how many circles are initially occupied) */
+	DEasy,	/* 10≤x<15 */
+	DMed,	/* 5≤x<10 */
+	DHard,	/* 0≤x<5 */
+
+	/* dynamic? original game has a fixed grid size, but we don't need to abide by it */
+	SzX = 11,
+	SzY = 11, 
+
+	Border = 10,
+	/* movement directions */
+	NE,
+	E,
+	SE,
+	SW,
+	W,
+	NW,
+
+	Won = 1,	/* game-ending states */
+	Lost = 2,
+};
+
+Font *font;
+
+int difficulty = DMed;
+int finished;
+
+int grid[SzX][SzY];
+int ogrid[SzX][SzY];	/* so we can restart levels */
+
+Image	*gl;	/* glenda */
+Image 	*glm;	/* glenda's mask */
+Image	*cc; /* clicked */
+Image	*ec; /* empty; not clicked */
+Image 	*bg;
+Image 	*lost;
+Image	*won;
+
+
+char *mbuttons[] = 
+{
+	"easy",
+	"medium",
+	"hard",
+	0
+};
+
+char *rbuttons[] = 
+{
+	"new",
+	"reset",
+	"exit",
+	0
+};
+
+Menu mmenu = 
+{
+	mbuttons,
+};
+
+Menu rmenu =
+{
+	rbuttons,
+};
+
+Image *
+eallocimage(Rectangle r, int repl, uint color)
+{
+	Image *tmp;
+
+	tmp = allocimage(display, r, screen->chan, repl, color);
+	if(tmp == nil)
+		sysfatal("cannot allocate buffer image: %r");
+
+	return tmp;
+}
+
+Image *
+eloadfile(char *path)
+{
+	Image *img;
+	int fd;
+
+	fd = open(path, OREAD);
+	if(fd < 0) {
+		fprint(2, "cannot open image file %s: %r\n", path);
+		exits("image");
+	}
+	img = readimage(display, fd, 0);
+	if(img == nil)
+		sysfatal("cannot load image: %r");
+	close(fd);
+	
+	return img;
+}
+
+
+void
+allocimages(void)
+{
+	Rectangle one = Rect(0, 0, 1, 1);
+	
+	cc = eallocimage(one, 1, 0x777777FF);
+	ec = eallocimage(one, 1, DPalegreen);
+	bg = eallocimage(one, 1, DPurpleblue);
+	lost = eallocimage(one, 1, DRed);
+	won = eallocimage(one, 1, DGreen);
+	gl = eloadfile("/lib/face/48x48x4/g/glenda.1");
+
+	glm = allocimage(display, Rect(0, 0, 48, 48), gl->chan, 1, DCyan);
+	if(glm == nil)
+        		sysfatal("cannot allocate mask: %r");
+
+    	draw(glm, glm->r, display->white, nil, ZP);
+    	gendraw(glm, glm->r, display->black, ZP, gl, gl->r.min);
+    	freeimage(gl);
+    	gl = display->black;
+
+
+}
+
+/* unnecessary calculations here, but it's fine */
+Point
+board2pix(int x, int y)
+{
+	float d, rx, ry, yh;
+	int nx, ny;
+
+	d = (float)(Dx(screen->r) > Dy(screen->r)) ? Dy(screen->r) -20 : Dx(screen->r) -20;
+	rx = d/(float)SzX;
+	rx = rx/2.0;
+	ry = d/(float)SzY;
+	ry = ry/2.0;
+
+	yh = ry/3.73205082;
+
+	nx = (int)((float)x*rx*2.0+rx +(y%2?rx:0.0)); /* nx = x*(2rx) + rx + rx (conditional) */
+	ny = (int)((float)y*(ry*2.0-(y>0?yh:0.0)) + ry); /* ny = y*(2ry-yh) +ry */
+	return Pt(nx, ny);
+}
+
+Point 
+pix2board(int x, int y)
+{
+	float d, rx, ry, yh;
+	int ny, nx;
+
+	/* XXX: float→int causes small rounding errors */
+
+	d = (float)(Dx(screen->r) > Dy(screen->r)) ? Dy(screen->r) -20: Dx(screen->r)-20;
+	rx = d/(float)SzX;
+	rx = rx/2.0;
+	ry =d/(float)SzY;
+	ry = ry/2.0;
+
+	yh = ry/3.73205082;
+
+	/* reverse board2pix() */
+	ny = (int)(((float)y - ry)/(2*ry - ((y>2*ry)?yh:0.0)) + 0.5); /* ny = (y - ry)/(2ry-yh) */
+	nx = (int)(((float)x - rx - (ny%2?rx:0.0))/(rx*2.0) + 0.5); /* nx = (x - rx - rx)/2rx */
+	
+	if (nx >= SzX)
+		nx = SzX-1;
+	if (ny >=SzY)
+		ny = SzY-1;
+
+	return Pt(nx, ny);
+}
+
+void
+initlevel(void)
+{
+	int i, cnt = 10, x, y;
+
+	for(x = 0; x < SzX; x++)
+		for(y = 0; y < SzY; y++)
+			ogrid[x][y] = 100;
+
+	switch(difficulty){
+	case DEasy:
+		cnt = 10 + nrand(5);
+		break;
+	case DMed:
+		cnt = 5 + nrand(5);
+		break;
+	case DHard:
+		cnt = nrand(5);
+		break;
+	}
+	for(i = 0; i < cnt; i++) {
+		do {
+			x = nrand(SzX);
+			y = nrand(SzY);
+		} while(ogrid[x][y] != 100);
+		ogrid[x][y] = 999;
+	}
+
+	ogrid[SzX/2][SzY/2] = 1000;
+
+	memcpy(grid, ogrid, sizeof grid);
+
+	finished = 0;
+
+}
+
+void
+drawlevel(void)
+{
+	Point p;
+	int  x, y, rx, ry, d;
+	char *s = nil;
+
+	if(finished)
+		draw(screen, screen->r, finished==Won?won:lost, nil, ZP);
+	else
+		draw(screen, screen->r, bg, nil, ZP);
+
+	d = (Dx(screen->r) > Dy(screen->r)) ? Dy(screen->r) -20: Dx(screen->r) -20;
+	rx = (int)ceil((float)(d-2*Border)/(float)SzX)/2;
+	ry = (int)ceil((float)(d-2*Border)/(float)SzY)/2;
+
+	for(x = 0; x < SzX; x++) {
+		for(y = 0; y < SzY; y++) {
+			p = board2pix(x, y);
+			switch(grid[x][y]){
+			case 999: 
+				fillellipse(screen, addpt(screen->r.min, p), rx, ry, cc, ZP);
+				break;
+			case 1000:
+				p = addpt(screen->r.min, p);
+				fillellipse(screen, p, rx, ry, ec, ZP);
+				p = subpt(p, Pt(24, 24));
+				draw(screen, Rpt(p, addpt(p, Pt(48, 48))), gl, glm, ZP);
+				break;
+			default:
+				fillellipse(screen, addpt(screen->r.min, p), rx, ry, ec, ZP);
+				USED(s);
+				/* uncomment the following to see game state and field scores */
+				/*s = smprint("%d", grid[x][y]);
+				string(screen, addpt(screen->r.min, p), display->black, ZP, font, s);
+				free(s);
+				*/
+				break;
+			}
+		}
+	}
+	flushimage(display, 1);
+}
+
+void
+domove(int dir, int x, int y)
+{
+	if(x == 0 || x == SzX-1 || y == 0 || y == SzY-1)
+		goto done;
+
+	switch(dir){
+	case NE:
+		if(y%2)
+			grid[x+1][y-1] = 1000;
+		else	
+			grid[x][y-1] = 1000;
+		break;
+	case E:
+		grid[x+1][y] = 1000;
+		break;
+	case SE:
+		if(y%2)
+			grid[x+1][y+1] = 1000;
+		else
+			grid[x][y+1] = 1000;
+		break;
+	case SW:
+		if(y%2)
+			grid[x][y+1] = 1000;
+		else
+			grid[x-1][y+1] = 1000;
+		break;
+	case W:
+		grid[x-1][y] = 1000;
+		break;
+	case NW:
+		if(y%2)
+			grid[x][y-1] = 1000;
+		else
+			grid[x-1][y-1] = 1000;
+		break;
+	}
+done:
+	grid[x][y] = 100;
+}
+
+Point
+findglenda(void)
+{
+	int x, y;
+	for(x = 0; x < SzX; x++)
+		for(y = 0; y < SzY; y++)
+			if(grid[x][y] == 1000)
+				return Pt(x, y);
+	return Pt(-1, -1);
+}
+
+int 
+checknext(int dir, int x, int y)
+{
+	switch(dir){
+	case NE: 
+		return grid[x+(y%2?1:0)][y-1];
+	case E:
+		return grid[x+1][y];
+	case SE:
+		return grid[x+(y%2?1:0)][y+1];
+	case SW:
+		return grid[x+(y%2?0:-1)][y+1];
+	case W:
+		return grid[x-1][y];
+	case NW:
+		return grid[x+(y%2?0:-1)][y-1];
+	default:
+		sysfatal("andrey messed up big time");
+	}
+	return 1000;
+}
+/* the following two routines constitute the "game AI"
+* they score the field based on the number of moves
+* required to reach the edge from a particular point
+* scores > 100 are "dead spots" (this assumes the field 
+* is not larger than ~100*2
+* 
+* routines need to run at least twice to ensure a field is properly
+* scored: there are errors that creep up due to the nature of 
+* traversing the board
+*/
+int 
+score1(int x, int y) {
+	int dir, min = 999, next;
+
+	if(x == 0 || x == SzX-1 || y == 0 || y == SzY-1)
+		return 1; 		/* we can always escape from the edges */
+
+	for(dir = NE; dir <= NW; dir++) {
+		next = checknext(dir, x, y);
+		if(next < min)
+			min = next;
+	}
+	return 1+min;
+}
+
+void
+calc(void)
+{
+	int i, x, y;
+	for(i = 0; i < SzX; i++) /* assumes SzX = SzY */
+		for(x = i; x < SzX-i; x++)
+			for(y = i; y < SzY-i; y++)
+				if(grid[x][y] != 999)
+					grid[x][y] = score1(x, y);
+}
+
+void
+nextglenda(void)
+{
+	int min =1000, next, dir, nextdir = 0, count = 0;
+	Point p = findglenda();
+
+	calc();
+	calc();
+	calc();
+
+	grid[p.x][p.y] = 1000;
+	
+	for(dir = NE; dir <= NW; dir++) {
+		next = checknext(dir, p.x, p.y);
+		if(next < min) {
+			min = next;
+			nextdir = dir;
+			++count;
+		} else if(next == min) {
+			nextdir = (nrand(++count) == 0)?dir:nextdir;
+		}
+	}
+	if(min < 100)
+		domove(nextdir, p.x, p.y);
+	else
+		finished = Won;
+
+	if(eqpt(findglenda(), Pt(-1, -1)))
+		finished = Lost;
+}
+
+int
+checkfinished(void)
+{
+	int i, j;
+	for(i = 0; i < SzX; i++)
+		for(j = 0; j < SzY; j++)
+			if(grid[i][j] == 'E')
+				return 0;
+	return 1;
+}
+
+void
+move(Point m)
+{
+	Point p, nm;
+	int x, y;
+
+	nm = subpt(m, screen->r.min);
+
+	/* figure out where the click falls */
+	p = pix2board(nm.x, nm.y);
+	
+	if(grid[p.x][p.y] >= 999)
+		return;
+
+	/* reset the board scores */
+	grid[p.x][p.y] = 999;
+	for(x = 0; x < SzX; x++)
+		for(y = 0; y < SzY; y++)
+			if(grid[x][y] != 999 && grid[x][y] != 1000)
+				grid[x][y] = 100;
+	
+	nextglenda();
+}
+
+void
+resize(void)
+{
+	int fd, size = (Dx(screen->r) > Dy(screen->r)) ? Dy(screen->r) + 20 : Dx(screen->r)+20; 
+
+	fd = open("/dev/wctl", OWRITE);
+	if(fd >= 0){
+		fprint(fd, "resize -dx %d -dy %d", size, size);
+		close(fd);
+	}
+
+}
+
+
+void
+eresized(int new)
+{
+	if(new && getwindow(display, Refnone) < 0)
+		sysfatal("can't reattach to window");
+	
+	drawlevel();
+}
+
+void 
+main(int argc, char **argv)
+{
+	Mouse m;
+	Event ev;
+	int e, mousedown=0;
+	char *fontname;
+
+	USED(argv, argc);
+
+	if(initdraw(nil, nil, "glendy") < 0)
+		sysfatal("initdraw failed: %r");
+	einit(Emouse);
+
+	resize();
+
+	srand(time(0));
+
+	allocimages();
+	initlevel();	/* must happen before "eresized" */
+	eresized(0);
+
+	fontname = "/lib/font/bit/lucidasans/unicode.8.font";
+	if((font = openfont(display, fontname)) == nil)
+		sysfatal("font '%s' not found", fontname);	
+
+	for(;;) {
+		e = event(&ev);
+		switch(e) {
+		case Emouse:
+			m = ev.mouse;
+			if(m.buttons == 0) {
+				if(mousedown && !finished) {
+					mousedown = 0;
+					move(m.xy);
+					drawlevel();
+				}
+			}
+			if(m.buttons&1) {
+				mousedown = 1;
+			}
+			if(m.buttons&2) {
+				switch(emenuhit(2, &m, &mmenu)) {
+				case 0:
+					difficulty = DEasy;
+					initlevel();
+					break;
+				case 1:				
+					difficulty = DMed;
+					initlevel();
+					break;
+				case 2:
+					difficulty = DHard;
+					initlevel();
+					break;
+				}
+				drawlevel();
+			}
+			if(m.buttons&4) {
+				switch(emenuhit(3, &m, &rmenu)) {
+				case 0:
+					initlevel();
+					break;
+				case 1:
+					memcpy(grid, ogrid, sizeof grid);
+					finished = 0;
+					break;
+				case 2:
+					exits(nil);
+				}
+				drawlevel();
+			}
+			break;
+		}
+	}
+}
--- a/sys/src/games/mkfile
+++ b/sys/src/games/mkfile
@@ -9,6 +9,7 @@
 	life\
 	memo\
 	mole\
+	glendy\
 
 OFILES=
 HFILES=
--- a/usr/glenda/bin/rc/riostart
+++ b/usr/glenda/bin/rc/riostart
@@ -1,3 +1,3 @@
 #!/bin/rc
 window 0,0,161,117 stats -lmisce
-window
+window -scroll
--