shithub: wl3d

Download patch

ref: 3891487cd9fb5088d99344d33cf4e37e0abf7f9b
parent: 4c670f454f76120455674cd064d3f9bbb2e0d2aa
author: Konstantinn Bonnet <[email protected]>
date: Fri Mar 25 22:18:30 EDT 2016

basic multithread keyboard and mouse, timing, ordering

- fix wrong reference palette in fadeout, making it appear too fast
- new bug: timecode ripped from aiju won't work if fps < Tb Hz

--- a/dat.h
+++ b/dat.h
@@ -9,7 +9,22 @@
 };
 extern int ver;
 extern char *ext;
-extern int debug, nointro;
+extern int grabon;
+enum{
+	K↑,
+	K↓,
+	K←,
+	K→,
+	Krun,
+	Kfire,
+	Kopen,
+	Kstrafe,
+	Kmenu,
+	Ke
+};
+extern int cson, kbon, mson;
+extern Rune keys[];
+extern int (*step)(void);
 
 enum{
 	Vw = 320,
@@ -26,7 +41,7 @@
 	C0,
 	Cred,
 	Cwht = Cred+6,
-	Caux = Cwht+3,
+	Cfad = Cwht+3,
 	Csod,
 	Cend
 };
@@ -166,6 +181,11 @@
 	Spear = Sottodeath,
 	Sangeltired = Sotto,
 	Ssend = Sfett,
+
+	Mintro = 7,
+	Mmenu = 14,
+	Mnazjazz = 18,
+	Mtower = 23,
 
 	Pbackdrop = 0,
 	Pmouselback,
--- a/fns.h
+++ b/fns.h
@@ -1,11 +1,13 @@
 void*	emalloc(ulong);
 void*	erealloc(void *, ulong);
+void	grab(int);
+void	toss(void);
 void	flush(void);
 void	dat(char *);
-void	palpic(uchar *);
-void	fadeout(int);
-void	fadein(int);
 void	out(void);
+void	fade(void);
+void	fadeop(int, u32int, int);
+void	palpic(uchar *);
 void	put(int, int, int, int, uchar *, int);
 int	txt(int, int, char *, int);
 int	txtnl(int, int, char *, int);
@@ -12,11 +14,12 @@
 int	txtw(char *s);
 void	fill(int);
 void	pic(int, int, int);
-void	demos(void);
-void	init(void);
+int	estep(void);
+int	mstep(void);
+void	init(int);
 int	rnd(void);
-void	vbl(int);
-void	delay(int);
+int	gstep(void);
+int	dstep(void);
 void	initg(int);
 void	stopmus(void);
 void	playmus(int);
--- a/gm.c
+++ b/gm.c
@@ -1,8 +1,26 @@
 #include <u.h>
 #include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#include <mouse.h>
+#include <keyboard.h>
 #include "dat.h"
 #include "fns.h"
 
+extern Channel *kbc, *msc;
+
+Rune keys[Ke] = {
+	[K↑] Kup,
+	[K↓] Kdown,
+	[K←] Kleft,
+	[K→] Kright,
+	[Krun] Kshift,
+	[Kfire] Kctl,
+	[Kopen] ' ',
+	[Kstrafe] Kalt,
+	[Kmenu] Kesc
+};
+
 static int rndi, rndt[] = {
   0, 8, 109, 220, 222, 241, 149, 107, 75, 248, 254, 140, 16, 66, 74, 21,
   211, 47, 80, 242, 154, 27, 205, 128, 161, 89, 77, 36, 95, 110, 85, 48,
@@ -29,16 +47,17 @@
 	return rndt[rndi];
 }
 
-void
-vbl(int n)
+int
+gstep(void)
 {
-	sleep(1000/70 * n);
+	return 0;
 }
 
-void
-delay(int n)
+int
+dstep(void)
 {
-	vbl(Tb * n);
+	step = mstep;
+	return 0;
 }
 
 void
@@ -45,4 +64,8 @@
 initg(int r)
 {
 	rndi = r ? time(nil) & 0xff : 0;
+	cson = 0;
+	toss();
+	kbon++;
+	grab(1);
 }
--- a/mn.c
+++ b/mn.c
@@ -1,9 +1,16 @@
 #include <u.h>
 #include <libc.h>
+#include <thread.h>
+#include <keyboard.h>
 #include "dat.h"
 #include "fns.h"
 
+extern Channel *csc;
+
 typedef struct Score Score;
+typedef struct Menu Menu;
+typedef struct Seq Seq;
+
 struct Score{
 	char name[58];
 	int n;
@@ -22,9 +29,59 @@
 
 static void (*clear)(void);
 static void (*stripe)(int);
-static void (*scores)(void);
-static void (*title)(void);
 
+struct Seq{
+	int dt;
+	void (*f)(void);
+};
+struct Menu{
+	void (*draw)(void);
+	int nq;
+	Seq *sq;
+	Menu *m;
+};
+static Menu *mp;
+static Seq *msq;
+static int tc;
+
+static void waitkb(void);
+static void menuk(void);
+static void menu(void);
+static void pants(void);
+static void demo(void);
+static void scores(void);
+static void creds(void);
+static void title(void);
+static void intro(void);
+
+static Seq menuq[] = {{1, menuk}};
+static Seq mfadeq[] = {{30, fade}};
+static Seq pantsq[] = {{30, fade}, {600*Tb, waitkb}, {30, fade}};
+static Seq demoq[] = {{1, demo}};
+static Seq loopq[] = {{30, fade}, {10*Tb, waitkb}, {30, fade}};
+static Seq titleq[] = {{30, fade}, {15*Tb, waitkb}, {30, fade}};
+static Seq introq[] = {{30, fade}, {7*Tb, waitkb}, {30, fade}};
+enum{
+	Lmenu,
+	Lmfade,
+	Lpants,
+	Ldemo,
+	Lscores,
+	Lcreds,
+	Ltitle,
+	Lintro
+};
+static Menu ml[] = {
+	[Lmenu] {menu, nelem(menuq), menuq, ml+Lmenu},
+	[Lmfade] {nil, nelem(mfadeq), mfadeq, ml+Lmenu},
+	[Lpants] {pants, nelem(pantsq), pantsq, ml+Lmenu},
+	[Ldemo] {demo, nelem(demoq), demoq, ml+Ltitle},
+	[Lscores] {scores, nelem(loopq), loopq, ml+Ldemo},
+	[Lcreds] {creds, nelem(loopq), loopq, ml+Lscores},
+	[Ltitle] {title, nelem(titleq), titleq, ml+Lcreds},
+	[Lintro] {intro, nelem(introq), introq, ml+Ltitle},
+};
+
 static void
 wlclear(void)
 {
@@ -50,32 +107,69 @@
 }
 
 static void
+fixedw(char *s)
+{
+	char c;
+
+	while(c = *s, c != 0)
+		*s++ = c - '0' + 129;
+}
+
+static void
+reset(Menu *m, int pal0)
+{
+	mp = m;
+	tc = 0;
+	if(pal0)
+		pal = pals[C0];
+	msq = m->sq;
+	if(mp->draw != nil){
+		mp->draw();
+		fadeop(msq->dt, 0, 0);
+	}else
+		fadeop(msq->dt, 0, 1);
+}
+
+static void
+waitkb(void)
+{
+	if(nbrecv(csc, nil) > 0)
+		reset(ml+Lmfade, 0);
+}
+
+static void
+menuk(void)
+{
+	Rune r;
+
+	while(nbrecv(csc, &r) != 0);
+}
+
+static void
 pants(void)
 {
-	fadeout(45);
-	playmus(18);
 	pic(0, 0, pict[Pid1]);
 	pic(0, 80, pict[Pid2]);
 	palpic(exts[Eid].p);
-	fadein(30);
-	/* wait for input */
-	delay(15);
-	fadeout(45);
-	pal = pals[C0];
-	/* draw main menu */
+	playmus(Mnazjazz);
 }
 
 static void
-fixedw(char *s)
+menu(void)
 {
-	char c;
+	playmus(Mmenu);
+	grab(0);
+	step = estep;
+}
 
-	while(c = *s, c != 0)
-		*s++ = c - '0' + 129;
+static void
+demo(void)
+{
+	step = dstep;
 }
 
 static void
-wlscores(void)
+scores(void)
 {
 	int x, y;
 	char a[16], b[16];
@@ -133,9 +227,16 @@
 }
 
 static void
-wltitle(void)
+creds(void)
 {
+	pic(0, 0, pict[Pcreds]);
+}
+
+static void
+title(void)
+{
 	pic(0, 0, pict[Ptitle1]);
+	playmus(Mintro);
 }
 static void
 sdtitle(void)
@@ -143,6 +244,7 @@
 	pic(0, 0, pict[Ptitle1]);
 	pic(0, 80, pict[Ptitle2]);
 	palpic(exts[Etitpal].p);
+	playmus(Mtower);
 }
 
 static void
@@ -150,53 +252,47 @@
 {
 	fill(0x82);
 	pic(216, 110, pict[Ppg13]);
-	fadein(40);
-	delay(7);
-	fadeout(40);
+	playmus(Mintro);
 }
+static void
+sdintro(void)
+{
+	fill(0x82);
+	pic(216, 110, pict[Ppg13]);
+	playmus(Mtower);
+}
 
-void
-init(void)
+int
+estep(void)
 {
-	if(ver < SDM){
-		clear = wlclear;
-		stripe = wlstripe;
-		scores = wlscores;
-		title = wltitle;
-	}else{
-		clear = sdclear;
-		stripe = sdstripe;
-		scores = sdscores;
-		title = sdtitle;
+	return -1;
+}
+
+int
+mstep(void)
+{
+	tc++;
+	msq->f();
+	if(tc == msq->dt){
+		if(++msq == mp->sq + mp->nq)
+			reset(mp->m, 1);
+		tc = 0;
 	}
-	if(!nointro)
-		intro();
+	return 0;
 }
 
 void
-demos(void)
+init(int nointro)
 {
-	for(;;){
-		if(nointro)
-			goto stop;
-		title();
-		fadein(30);
-		delay(15);
-		fadeout(40);
-		pal = pals[C0];
-
-		pic(0, 0, pict[Pcreds]);
-		fadein(30);
-		delay(10);
-		fadeout(40);
-
-		scores();
-		fadein(30);
-		delay(10);
-		fadeout(40);
-
-		continue;
-	stop:
-		break;
+	clear = wlclear;
+	stripe = wlstripe;
+	if(ver >= SDM){
+		clear = sdclear;
+		stripe = sdstripe;
+		ml[Lintro].draw = sdintro;
+		ml[Ltitle].draw = sdtitle;
+		ml[Lscores].draw = sdscores;
 	}
+	reset(nointro ? ml+Lmenu : ml+Lintro, 0);
+	cson++;
 }
--- a/rend.c
+++ b/rend.c
@@ -12,16 +12,18 @@
 int scale, npx;
 uchar *px;
 static uchar pxb[Va];
+static void (*ffp)(void);
+static int fi, fdt, fr, fg, fb;
+static u32int *fref;
 
 static void
-modpal(u32int *d, u32int *s, u32int c, int n, int steps)
+fadeout(void)
 {
-	int r, g, b, u, v, w;
-	u32int p, *e;
+	int u, v, w;
+	u32int p, *s, *d, *e;
 
-	b = (c&0xff) * 255 / 63;
-	g = (c>>8&0xff) * 255 / 63;
-	r = (c>>16&0xff) * 255 / 63;
+	s = fref;
+	d = pal;
 	e = d+nelem(pals[0]);
 	while(d < e){
 		p = *s++;
@@ -28,68 +30,67 @@
 		u = p & 0xff;
 		v = p>>8 & 0xff;
 		w = p>>16 & 0xff;
-		u = u + (b-u) * n/steps;
-		v = v + (g-v) * n/steps;
-		w = w + (r-w) * n/steps;
+		u = u + (fb-u) * fi/fdt;
+		v = v + (fg-v) * fi/fdt;
+		w = w + (fr-w) * fi/fdt;
 		*d++ = w<<16 | v<<8 | u;
 	}
 }
 
-void
-palpic(uchar *s)
+static void
+fadein(void)
 {
-	u32int *p, *e;
+	int u, v, w;
+	u32int p, *s, *d, *e;
 
-	p = pals[Csod];
-	e = p + nelem(pals[Csod]);
-	while(p < e){
-		*p++ = s[0]*255/63<<16 | s[1]*255/63<<8 | s[2]*255/63;
-		s += 3;
+	s = fref;
+	d = pal;
+	e = d+nelem(pals[0]);
+	while(d < e){
+		p = *s++;
+		u = (p & 0xff) * fi/fdt;
+		v = (p>>8 & 0xff) * fi/fdt;
+		w = (p>>16 & 0xff) * fi/fdt;
+		*d++ = w<<16 | v<<8 | u;
 	}
-	pal = pals[Csod];
 }
 
 void
-fadeout(int steps)
+fade(void)
 {
-	int i;
-	u32int *o;
-
-	o = pal;
-	pal = pals[Caux];
-	vbl(1);
-	for(i=0; i<steps; i++){
-		modpal(pals[Caux], pal, 0, i, steps);
-		vbl(1);
-		out();
+	ffp();
+	out();
+	if(fi == fdt && ffp == fadein){
+		ffp = fadeout;
+		fi = 0;
 	}
-	pal = o;
+	fi++;
 }
 
 void
-fadein(int steps)
+fadeop(int dt, u32int c, int noin)
 {
-	int i, u, v, w;
-	u32int *o, p, *s, *d, *e;
+	fdt = dt;
+	fb = (c & 0xff) * 255 / 63;
+	fg = (c>>8 & 0xff) * 255 / 63;
+	fr = (c>>16 & 0xff) * 255 / 63;
+	fref = pal;
+	pal = pals[Cfad];
+	fi = 1;
+	ffp = noin ? fadeout : fadein;
+}
 
-	o = pal;
-	pal = pals[Caux];
-	vbl(1);
-	for(i=0; i<steps; i++){
-		s = o;
-		d = pal;
-		e = d+nelem(pals[Caux]);
-		while(d < e){
-			p = *s++;
-			u = (p & 0xff) * i/steps;
-			v = (p>>8 & 0xff) * i/steps;
-			w = (p>>16 & 0xff) * i/steps;
-			*d++ = w<<16 | v<<8 | u;
-		}
-		vbl(1);
-		out();
+void
+palpic(uchar *s)
+{
+	u32int *p, *e;
+
+	p = pal = pals[Csod];
+	e = p + nelem(pals[0]);
+	while(p < e){
+		*p++ = s[0]*255/63<<16 | s[1]*255/63<<8 | s[2]*255/63;
+		s += 3;
 	}
-	pal = o;
 }
 
 void
--- a/wl3d.c
+++ b/wl3d.c
@@ -9,11 +9,139 @@
 
 mainstacksize = 16*1024;
 char *ext = "wl6";
-int debug, nointro;
+int grabon;
+int cson, kbon, mson;
+int (*step)(void);
+Channel *csc, *kbc, *msc;
 
-static Rectangle fbr;
+enum{
+	BILLION = 1000000000,
+	MILLION = 1000000
+};
+static Point p0;
+static Rectangle fbr, grabr;
 static Image *fb;
+static Channel *reszc;
 
+static void
+mproc(void *)
+{
+	int n, fd, nerr;
+	char buf[1+5*12], *px, *py, *pb;
+	Point o, p;
+	Mouse m;
+
+	fd = open("/dev/mouse", ORDWR);
+	if(fd < 0)
+		sysfatal("mproc: %r");
+	nerr = 0;
+	px = buf+1;
+	py = px + 12;
+	pb = py + 12;
+	o = p0;
+	for(;;){
+		n = read(fd, buf, sizeof buf);
+		if(n != 1+4*12){
+			if(n < 0 || ++nerr > 10)
+				break;
+			fprint(2, "mproc: bad count %d not 49: %r\n", n);
+			continue;
+		}
+		nerr = 0;
+		switch(*buf){
+		case 'r':
+			send(reszc, nil);
+			/* wet floor */
+		case 'm':
+			if(!mson)
+				break;
+			p.x = strtol(px, nil, 10);
+			p.y = strtol(py, nil, 10);
+			m.xy.x = p.x - o.x;
+			m.xy.y = o.y - p.y;
+			m.buttons = *pb;
+			nbsend(msc, &m);
+			if(!ptinrect(p, grabr)){
+				fprint(fd, "m%d %d", p0.x, p0.y);
+				p = p0;
+			}
+			o = p;
+		}
+	}
+}
+
+static void
+kproc(void *)
+{
+	int n, k, fd;
+	char c, buf[256], *s;
+	Rune r, *a;
+
+	fd = open("/dev/kbd", OREAD);
+	if(fd < 0)
+		sysfatal("kproc: %r");
+	memset(buf, 0, sizeof buf);
+	for(;;){
+		n = read(fd, buf, sizeof(buf)-1);
+		if(n <= 0)
+			break;
+		c = *buf;
+		if(c == 'c' && cson){
+			chartorune(&r, buf+1);
+			send(csc, &r);
+		}
+		if(c != 'k' || c != 'K' || !kbon)
+			continue;
+		s = buf+1;
+		k = 0;
+		while(*s != 0){
+			s += chartorune(&r, s);
+			for(a=keys; a<keys+Ke; a++)
+				if(r == *a){
+					k |= 1<<a-keys;
+					break;
+				}
+		}
+		send(kbc, &k);
+	}
+}
+
+static void
+resetfb(void)
+{
+	Point p, d;
+
+	scale = Dx(screen->r) / Vw;
+	if(scale <= 0)
+		scale = 1;
+	else if(scale > 10)
+		scale = 10;
+	p = divpt(addpt(screen->r.min, screen->r.max), 2);
+	d = Pt(Vw/2 * scale, Vh/2 * scale);
+	fbr = Rpt(subpt(p, d), addpt(p, d));
+	d = Pt(Vh/4, Vh/4);
+	grabr = Rpt(subpt(p, d), addpt(p, d));
+	p0 = p;
+
+	freeimage(fb);
+	free(px);
+	npx = Vt * scale;
+	px = emalloc(npx);
+	fb = allocimage(display, Rect(0,0,Vw*scale,scale==1 ? Vh : 1), RGB24, 1, 0);
+	if(fb == nil)
+		sysfatal("resetfb: %r");
+
+	draw(screen, screen->r, display->black, nil, ZP);
+	out();
+}
+
+static void
+usage(void)
+{
+	fprint(2, "usage: %s [-23dios] [-m dir] [-w map] [-x difficulty]\n", argv0);
+	threadexits("usage");
+}
+
 void *
 emalloc(ulong n)
 {
@@ -36,6 +164,35 @@
 }
 
 void
+grab(int on)
+{
+	static char nocurs[2*4+2*2*16];
+	static int fd = -1;
+
+	if(mson == on)
+		return;
+	if(mson = on && grabon){
+		fd = open("/dev/cursor", ORDWR|OCEXEC);
+		if(fd < 0){
+			fprint(2, "grab: %r\n");
+			return;
+		}
+		write(fd, nocurs, sizeof nocurs);
+	}else if(fd >= 0){
+		close(fd);
+		fd = -1;
+	}
+}
+
+void
+toss(void)
+{
+	while(nbrecv(csc, nil) != 0);
+	while(nbrecv(msc, nil) != 0);
+	while(nbrecv(kbc, nil) != 0);
+}
+
+void
 flush(void)
 {
 	Rectangle r;
@@ -57,49 +214,20 @@
 	flushimage(display, 1);
 }
 
-static void
-resetfb(void)
-{
-	Point p, d;
-
-	scale = Dx(screen->r) / Vw;
-	if(scale <= 0)
-		scale = 1;
-	else if(scale > 10)
-		scale = 10;
-	p = divpt(addpt(screen->r.min, screen->r.max), 2);
-	d = Pt(Vw/2 * scale, Vh/2 * scale);
-	fbr = Rpt(subpt(p, d), addpt(p, d));
-
-	freeimage(fb);
-	free(px);
-	npx = Vt * scale;
-	px = emalloc(npx);
-	fb = allocimage(display, Rect(0,0,Vw*scale,scale==1 ? Vh : 1), RGB24, 1, 0);
-	if(fb == nil)
-		sysfatal("resetfb: %r");
-
-	draw(screen, screen->r, display->black, nil, ZP);
-}
-
-static void
-usage(void)
-{
-	fprint(2, "usage: %s [-23Ddios] [-m dir] [-w map] [-x difficulty]\n", argv0);
-	threadexits("usage");
-}
-
 void
 threadmain(int argc, char **argv)
 {
+	int n;
+	vlong t0, t, dt, Δ;
 	char *datdir = "/sys/games/lib/wl3d/";
 
+	n = 0;
+	step = mstep;
 	ARGBEGIN{
 	case '2': ext = "sd2"; break;
 	case '3': ext = "sd3"; break;
-	case 'D': debug++; break;
 	case 'd': ext = "wl1"; break;
-	case 'i': nointro++; break;
+	case 'i': n++; break;
 	case 'm': datdir = EARGF(usage()); break;
 	case 'o': ext = "sdm"; break;
 	case 's': ext = "sod"; break;
@@ -113,9 +241,37 @@
 	if(initdraw(nil, nil, "wl3d") < 0)
 		sysfatal("initdraw: %r");
 	resetfb();
+	kbc = chancreate(sizeof(int), 20);
+	csc = chancreate(sizeof(Rune), 20);
+	reszc = chancreate(sizeof(int), 2);
+	msc = chancreate(sizeof(Mouse), 0);
+	if(kbc == nil || csc == nil | reszc == nil || msc == nil)
+		sysfatal("chancreate: %r");
+	if(proccreate(kproc, nil, 8192) < 0 || proccreate(mproc, nil, 8192) < 0)
+		sysfatal("proccreate: %r");
 
-	init();
-	demos();
-
+	init(n);
+	t0 = Δ = 0;
+	for(;;){
+		if(nbrecv(reszc, nil) != 0){
+			if(getwindow(display, Refnone) < 0)
+				sysfatal("resize failed: %r");
+			resetfb();
+		}
+		if(step() < 0)
+			break;
+		t = nsec();
+		dt = 0;
+		if(t0 != 0){
+			dt = BILLION/Tb - (t - t0) - Δ;
+			if(dt >= MILLION)
+				sleep(dt/MILLION);
+		}
+		t0 = nsec();
+		if(dt != 0){
+			dt = (t0 - t) - (dt / MILLION) * MILLION;
+			Δ += (dt - Δ) / 100;
+		}
+	}
 	threadexitsall(nil);
 }