shithub: patch

Download patch

ref: e6b63eabaef2185b7c788d6cbb7df939328267c6
author: qwx <qwx@sciops.net>
date: Wed Jan 8 23:41:19 EST 2020

add currently used patches

--- /dev/null
+++ b/9fs-9paste
@@ -1,0 +1,12 @@
+diff -r 2c3766a9188f rc/bin/9fs
+--- a/rc/bin/9fs	Sun May 21 17:27:30 2017 +0200
++++ b/rc/bin/9fs	Mon May 22 08:03:42 2017 +0200
+@@ -70,6 +70,8 @@
+ 	vacfs -m /n/`{basename $1 .vac} `{cat $score}
+ case wiki
+ 	srv -m 'net!plan9.bell-labs.com!wiki' wiki /mnt/wiki
++case 9paste
++	srv -n fs.9paste.net 9paste /n/9paste
+ case *
+ 	switch($#*){
+ 	case 1
--- /dev/null
+++ b/9nusbrc-nesusbjoy
@@ -1,0 +1,12 @@
+diff -r 5adefb206fa3 sys/src/9/boot/nusbrc
+--- a/sys/src/9/boot/nusbrc	Tue Aug 07 23:46:58 2018 +0200
++++ b/sys/src/9/boot/nusbrc	Wed Aug 08 06:14:09 2018 +0300
+@@ -15,6 +15,8 @@
+ 		if(~ $#* 5 && ! test -e /env/nousbhname)
+ 			id=$1:$5
+ 		switch($2$3){
++		case 17810a99 17810a96 05832060
++			# let nusb/joy take this one
+ 		case 0b957720 0b95772a 0db0a877 13b10018 15577720 20013c05 07d13c05 05ac1402
+ 			nusb/ether -t a88772 $etherargs $id
+ 		case 0b951780 14eaab11 17370039 0411006e 050d5055
--- /dev/null
+++ b/acme-col
@@ -1,0 +1,283 @@
+diff -r 49bd5e4c9bde sys/src/cmd/acme/acme.c
+--- a/sys/src/cmd/acme/acme.c	Wed Feb 07 18:53:08 2018 +0000
++++ b/sys/src/cmd/acme/acme.c	Sun Feb 18 20:30:34 2018 +0100
+@@ -863,18 +863,18 @@
+ 	Image *tmp;
+ 
+ 	/* Blue */
+-	tagcols[BACK] = allocimagemix(display, DPalebluegreen, DWhite);
+-	tagcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreygreen);
+-	tagcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue);
+-	tagcols[TEXT] = display->black;
+-	tagcols[HTEXT] = display->black;
++	tagcols[BACK] = display->black;
++	tagcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x111111FF);
++	tagcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x440000FF);
++	tagcols[TEXT] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x770000FF);
++	tagcols[HTEXT] = tagcols[TEXT];
+ 
+ 	/* Yellow */
+-	textcols[BACK] = allocimagemix(display, DPaleyellow, DWhite);
+-	textcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow);
+-	textcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DYellowgreen);
+-	textcols[TEXT] = display->black;
+-	textcols[HTEXT] = display->black;
++	textcols[BACK] = display->black;
++	textcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x440000FF);
++	textcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x111111FF);
++	textcols[TEXT] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x884400FF);
++	textcols[HTEXT] = textcols[TEXT];
+ 
+ 	if(button){
+ 		freeimage(button);
+@@ -894,12 +894,12 @@
+ 	r.max.x -= 2;
+ 	border(modbutton, r, 2, tagcols[BORD], ZP);
+ 	r = insetrect(r, 2);
+-	tmp = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DMedblue);
++	tmp = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x884400FF);
+ 	draw(modbutton, r, tmp, nil, ZP);
+ 	freeimage(tmp);
+ 
+ 	r = button->r;
+-	colbutton = allocimage(display, r, screen->chan, 0, DPurpleblue);
++	colbutton = allocimage(display, r, screen->chan, 0, 0x440000FF);
+ 
+ 	but2col = allocimage(display, r, screen->chan, 1, 0xAA0000FF);
+ 	but3col = allocimage(display, r, screen->chan, 1, 0x006600FF);
+diff -r 49bd5e4c9bde sys/src/cmd/acme/cols.c
+--- a/sys/src/cmd/acme/cols.c	Wed Feb 07 18:53:08 2018 +0000
++++ b/sys/src/cmd/acme/cols.c	Sun Feb 18 20:30:34 2018 +0100
+@@ -17,7 +17,7 @@
+ 	Rectangle r1;
+ 	Text *t;
+ 
+-	draw(screen, r, display->white, nil, ZP);
++	draw(screen, r, display->black, nil, ZP);
+ 	c->r = r;
+ 	c->w = nil;
+ 	c->nw = 0;
+@@ -30,7 +30,7 @@
+ 	t->what = Columntag;
+ 	r1.min.y = r1.max.y;
+ 	r1.max.y += Border;
+-	draw(screen, r1, display->black, nil, ZP);
++	draw(screen, r1, tagcols[BORD], nil, ZP);
+ 	textinsert(t, 0, L"New Cut Paste Snarf Sort Zerox Delcol ", 38, TRUE);
+ 	textsetselect(t, t->file->nc, t->file->nc);
+ 	draw(screen, t->scrollr, colbutton, nil, colbutton->r.min);
+@@ -79,7 +79,7 @@
+ 		r1.max.y = min(y, v->body.r.min.y+v->body.nlines*v->body.font->height);
+ 		r1.min.y = winresize(v, r1, FALSE);
+ 		r1.max.y = r1.min.y+Border;
+-		draw(screen, r1, display->black, nil, ZP);
++		draw(screen, r1, tagcols[BORD], nil, ZP);
+ 		r.min.y = r1.max.y;
+ 	}
+ 	if(w == nil){
+@@ -134,7 +134,7 @@
+ 	c->nw--;
+ 	c->w = realloc(c->w, c->nw*sizeof(Window*));
+ 	if(c->nw == 0){
+-		draw(screen, r, display->white, nil, ZP);
++		draw(screen, r, display->black, nil, ZP);
+ 		return;
+ 	}
+ 	if(i == c->nw){		/* extend last window down */
+@@ -189,7 +189,7 @@
+ 	draw(screen, c->tag.scrollr, colbutton, nil, colbutton->r.min);
+ 	r1.min.y = r1.max.y;
+ 	r1.max.y += Border;
+-	draw(screen, r1, display->black, nil, ZP);
++	draw(screen, r1, tagcols[BORD], nil, ZP);
+ 	r1.max.y = r.max.y;
+ 	for(i=0; i<c->nw; i++){
+ 		w = c->w[i];
+@@ -200,7 +200,7 @@
+ 			r1.max.y = r1.min.y+(Dy(w->r)+Border)*Dy(r)/Dy(c->r);
+ 		r2 = r1;
+ 		r2.max.y = r2.min.y+Border;
+-		draw(screen, r2, display->black, nil, ZP);
++		draw(screen, r2, tagcols[BORD], nil, ZP);
+ 		r1.min.y = r2.max.y;
+ 		r1.min.y = winresize(w, r1, FALSE);
+ 	}
+@@ -256,7 +256,7 @@
+ 			r.max.y = r.min.y+Dy(w->r)+Border;
+ 		r1 = r;
+ 		r1.max.y = r1.min.y+Border;
+-		draw(screen, r1, display->black, nil, ZP);
++		draw(screen, r1, tagcols[BORD], nil, ZP);
+ 		r.min.y = r1.max.y;
+ 		y = winresize(w, r, FALSE);
+ 	}
+@@ -358,7 +358,7 @@
+ 		}
+ 		r.min.y = v->r.max.y;
+ 		r.max.y += Border;
+-		draw(screen, r, display->black, nil, ZP);
++		draw(screen, r, tagcols[BORD], nil, ZP);
+ 		y1 = r.max.y;
+ 	}
+ 	/* scan to see new size of everyone below */
+@@ -390,7 +390,7 @@
+ 	if(i < c->nw-1){
+ 		r.min.y = r.max.y;
+ 		r.max.y += Border;
+-		draw(screen, r, display->black, nil, ZP);
++		draw(screen, r, tagcols[BORD], nil, ZP);
+ 		for(j=i+1; j<c->nw; j++)
+ 			ny[j] -= (y2-r.max.y);
+ 	}
+@@ -410,7 +410,7 @@
+ 		if(j < c->nw-1){	/* no border on last window */
+ 			r.min.y = v->r.max.y;
+ 			r.max.y += Border;
+-			draw(screen, r, display->black, nil, ZP);
++			draw(screen, r, tagcols[BORD], nil, ZP);
+ 		}
+ 		y1 = r.max.y;
+ 	}
+@@ -498,7 +498,7 @@
+ 	}
+ 	r.min.y = v->r.max.y;
+ 	r.max.y = r.min.y+Border;
+-	draw(screen, r, display->black, nil, ZP);
++	draw(screen, r, tagcols[BORD], nil, ZP);
+ 	r.min.y = r.max.y;
+ 	if(i == c->nw-1)
+ 		r.max.y = c->r.max.y;
+diff -r 49bd5e4c9bde sys/src/cmd/acme/rows.c
+--- a/sys/src/cmd/acme/rows.c	Wed Feb 07 18:53:08 2018 +0000
++++ b/sys/src/cmd/acme/rows.c	Sun Feb 18 20:30:34 2018 +0100
+@@ -18,7 +18,7 @@
+ 	Rectangle r1;
+ 	Text *t;
+ 
+-	draw(screen, r, display->white, nil, ZP);
++	draw(screen, r, display->black, nil, ZP);
+ 	row->r = r;
+ 	row->col = nil;
+ 	row->ncol = 0;
+@@ -32,7 +32,7 @@
+ 	t->col = nil;
+ 	r1.min.y = r1.max.y;
+ 	r1.max.y += Border;
+-	draw(screen, r1, display->black, nil, ZP);
++	draw(screen, r1, tagcols[BORD], nil, ZP);
+ 	textinsert(t, 0, L"Newcol Kill Putall Dump Exit ", 29, TRUE);
+ 	textsetselect(t, t->file->nc, t->file->nc);
+ }
+@@ -63,7 +63,7 @@
+ 		r = d->r;
+ 		if(Dx(r) < 100)
+ 			return nil;
+-		draw(screen, r, display->white, nil, ZP);
++		draw(screen, r, display->black, nil, ZP);
+ 		r1 = r;
+ 		r1.max.x = min(x, r.max.x-50);
+ 		if(Dx(r1) < 50)
+@@ -71,7 +71,7 @@
+ 		colresize(d, r1);
+ 		r1.min.x = r1.max.x;
+ 		r1.max.x = r1.min.x+Border;
+-		draw(screen, r1, display->black, nil, ZP);
++		draw(screen, r1, tagcols[BORD], nil, ZP);
+ 		r.min.x = r1.max.x;
+ 	}
+ 	if(c == nil){
+@@ -105,7 +105,7 @@
+ 	textresize(&row->tag, r1);
+ 	r1.min.y = r1.max.y;
+ 	r1.max.y += Border;
+-	draw(screen, r1, display->black, nil, ZP);
++	draw(screen, r1, tagcols[BORD], nil, ZP);
+ 	r.min.y = r1.max.y;
+ 	r1 = r;
+ 	r1.max.x = r1.min.x;
+@@ -119,7 +119,7 @@
+ 		if(i > 0){
+ 			r2 = r1;
+ 			r2.max.x = r2.min.x+Border;
+-			draw(screen, r2, display->black, nil, ZP);
++			draw(screen, r2, tagcols[BORD], nil, ZP);
+ 			r1.min.x = r2.max.x;
+ 		}
+ 		colresize(c, r1);
+@@ -178,14 +178,14 @@
+ 		p.x = c->r.max.x-80-Scrollwid;
+ 	r = d->r;
+ 	r.max.x = c->r.max.x;
+-	draw(screen, r, display->white, nil, ZP);
++	draw(screen, r, display->black, nil, ZP);
+ 	r.max.x = p.x;
+ 	colresize(d, r);
+ 	r = c->r;
+ 	r.min.x = p.x;
+ 	r.max.x = r.min.x;
+ 	r.max.x += Border;
+-	draw(screen, r, display->black, nil, ZP);
++	draw(screen, r, tagcols[BORD], nil, ZP);
+ 	r.min.x = r.max.x;
+ 	r.max.x = c->r.max.x;
+ 	colresize(c, r);
+@@ -210,7 +210,7 @@
+ 	row->ncol--;
+ 	row->col = realloc(row->col, row->ncol*sizeof(Column*));
+ 	if(row->ncol == 0){
+-		draw(screen, r, display->white, nil, ZP);
++		draw(screen, r, display->black, nil, ZP);
+ 		return;
+ 	}
+ 	if(i == row->ncol){		/* extend last column right */
+@@ -221,7 +221,7 @@
+ 		c = row->col[i];
+ 		r.max.x = c->r.max.x;
+ 	}
+-	draw(screen, r, display->white, nil, ZP);
++	draw(screen, r, display->black, nil, ZP);
+ 	colresize(c, r);
+ }
+ 
+@@ -543,12 +543,12 @@
+ 			r2.min.x = x+Border;
+ 			if(Dx(r1) < 50 || Dx(r2) < 50)
+ 				continue;
+-			draw(screen, Rpt(r1.min, r2.max), display->white, nil, ZP);
++			draw(screen, Rpt(r1.min, r2.max), display->black, nil, ZP);
+ 			colresize(c1, r1);
+ 			colresize(c2, r2);
+ 			r2.min.x = x;
+ 			r2.max.x = x+Border;
+-			draw(screen, r2, display->black, nil, ZP);
++			draw(screen, r2, tagcols[BORD], nil, ZP);
+ 		}
+ 		if(i >= row->ncol)
+ 			rowadd(row, nil, x);
+diff -r 49bd5e4c9bde sys/src/cmd/acme/text.c
+--- a/sys/src/cmd/acme/text.c	Wed Feb 07 18:53:08 2018 +0000
++++ b/sys/src/cmd/acme/text.c	Sun Feb 18 20:30:34 2018 +0100
+@@ -1141,12 +1141,12 @@
+ 				pt1 = pt0;
+ 				reg = region(q, p0);
+ 				if(reg == 0)
+-					frdrawsel0(f, pt0, p0, p1, col, display->white);
++					frdrawsel0(f, pt0, p0, p1, col, display->black);
+ 			}
+ 			qt = frptofchar(f, q);
+ 			if(reg > 0){
+ 				if(q > p1)
+-					frdrawsel0(f, pt1, p1, q, col, display->white);
++					frdrawsel0(f, pt1, p1, q, col, display->black);
+ 
+ 				else if(q < p1)
+ 					selrestore(f, qt, q, p1);
+@@ -1154,7 +1154,7 @@
+ 				if(q > p1)
+ 					selrestore(f, pt1, p1, q);
+ 				else
+-					frdrawsel0(f, qt, q, p1, col, display->white);
++					frdrawsel0(f, qt, q, p1, col, display->black);
+ 			}
+ 			p1 = q;
+ 			pt1 = qt;
--- /dev/null
+++ b/colors-col
@@ -1,0 +1,23 @@
+diff -r 20a0f09456ec sys/src/cmd/colors.c
+--- a/sys/src/cmd/colors.c	Sun Apr 17 07:33:35 2016 +0200
++++ b/sys/src/cmd/colors.c	Mon Apr 18 06:29:26 2016 +0200
+@@ -28,7 +28,7 @@
+ 	}
+ 
+ 	ny = n/nx;
+-	draw(screen, screen->r, display->white, nil, ZP);
++	draw(screen, screen->r, display->black, nil, ZP);
+ 	r = insetrect(screen->r, 5);
+ 	r.min.y+=20;
+ 	b.max.y=r.min.y;
+@@ -155,8 +155,8 @@
+ 							rgb&0xFF,
+ 							(rgb<<8) | 0xFF);
+ 						p = addpt(screen->r.min, Pt(2,2));
+-						draw(screen, Rpt(p, addpt(p, stringsize(font, buf))), display->white, nil, p);
+-						string(screen, p, display->black, ZP, font, buf);
++						draw(screen, Rpt(p, addpt(p, stringsize(font, buf))), display->black, nil, p);
++						string(screen, p, display->white, ZP, font, buf);
+ 						prev=i;
+ 						break;
+ 					}
--- /dev/null
+++ b/doom-autorun
@@ -1,0 +1,44 @@
+doom: add autorun configuration option
+
+an autorun option avoids having the shift key swapping mouse buttons 2
+and 3 when playing with the mouse (or remapping the run key...)
+
+diff -r bd0ff7078273 sys/src/games/doom/g_game.c
+--- a/sys/src/games/doom/g_game.c	Mon Aug 28 19:45:49 2017 +0200
++++ b/sys/src/games/doom/g_game.c	Wed Aug 30 11:09:52 2017 +0200
+@@ -153,6 +153,7 @@
+ int		key_use;
+ int		key_strafe;
+ int		key_speed; 
++int		autorun;
+  
+ int             mousebfire; 
+ int             mousebstrafe; 
+@@ -252,7 +253,7 @@
+  
+     strafe = gamekeydown[key_strafe] || mousebuttons[mousebstrafe] 
+ 	|| joybuttons[joybstrafe]; 
+-    speed = gamekeydown[key_speed] || joybuttons[joybspeed];
++    speed = autorun || gamekeydown[key_speed] || joybuttons[joybspeed];
+  
+     forward = side = 0;
+     
+diff -r bd0ff7078273 sys/src/games/doom/m_misc.c
+--- a/sys/src/games/doom/m_misc.c	Mon Aug 28 19:45:49 2017 +0200
++++ b/sys/src/games/doom/m_misc.c	Wed Aug 30 11:09:52 2017 +0200
+@@ -172,6 +172,7 @@
+ extern int	viewwidth;
+ extern int	viewheight;
+ 
++extern int	autorun;
+ extern int	mouseSensitivity;
+ extern int	showMessages;
+ 
+@@ -213,6 +214,7 @@
+     {"key_use",&key_use, ' '},
+     {"key_strafe",&key_strafe, KEY_RALT},
+     {"key_speed",&key_speed, KEY_RSHIFT},
++    {"autorun",&autorun, 0},
+ 
+     {"use_mouse",&usemouse, 1},
+     {"mouseb_fire",&mousebfire,0},
--- /dev/null
+++ b/doom-chat
@@ -1,0 +1,120 @@
+diff -Naur a/sys/src/games/doom/hu_lib.c b/sys/src/games/doom/hu_lib.c
+--- a/sys/src/games/doom/hu_lib.c	Wed Jan 18 02:13:23 2012
++++ b/sys/src/games/doom/hu_lib.c	Wed Aug 12 17:58:24 2015
+@@ -315,22 +315,15 @@
+ // wrapper function for handling general keyed input.
+ // returns true if it ate the key
+ boolean
+-HUlib_keyInIText
+-( hu_itext_t*	it,
+-  unsigned char ch )
++HUlib_keyInIText(hu_itext_t* it, uchar c)
+ {
+-
+-    if (ch >= ' ' && ch <= '_') 
+-  	HUlib_addCharToTextLine(&it->l, (char) ch);
+-    else 
+-	if (ch == KEY_BACKSPACE) 
+-	    HUlib_delCharFromIText(it);
+-	else 
+-	    if (ch != KEY_ENTER) 
+-		return false; // did not eat key
+-
+-    return true; // ate the key
+-
++	if(isprint(c))
++		HUlib_addCharToTextLine(&it->l, c);
++	else if(c == KEY_BACKSPACE)
++		HUlib_delCharFromIText(it);
++	else if(c != KEY_ENTER)
++		return false;
++	return true;
+ }
+ 
+ void HUlib_drawIText(hu_itext_t* it)
+diff -Naur a/sys/src/games/doom/hu_stuff.c b/sys/src/games/doom/hu_stuff.c
+--- a/sys/src/games/doom/hu_stuff.c	Wed Jan 18 02:13:23 2012
++++ b/sys/src/games/doom/hu_stuff.c	Wed Aug 12 17:57:21 2015
+@@ -541,6 +541,9 @@
+         return false;
+     }
+ 
++	if(ev->type == ev_keyup)
++		return false;
++
+     if (!chat_on)
+     {
+ 	if (ev->data1 == HU_MSGREFRESH)
+diff -Naur a/sys/src/games/doom/i_video.c b/sys/src/games/doom/i_video.c
+--- a/sys/src/games/doom/i_video.c	Wed Jul 29 13:45:35 2015
++++ b/sys/src/games/doom/i_video.c	Wed Aug 12 18:03:33 2015
+@@ -5,10 +5,13 @@
+ #include "v_video.h"	// screens[]
+ #include "d_main.h"	// D_PostEvent
+ 
++#include <ctype.h>
+ #include <draw.h>
+ #include <mouse.h>
+ #include <keyboard.h>
+ 
++extern boolean chat_on;
++
+ static int resized;
+ static int mouseactive;
+ 
+@@ -245,6 +248,7 @@
+ 	int kfd, n;
+ 	Rune r;
+ 	event_t e;
++	int shifton = 0, lastk = 0, laste = 0;
+ 
+ 	if((kfd = open("/dev/kbd", OREAD)) < 0)
+ 		sysfatal("can't open kbd: %r");
+@@ -262,21 +266,32 @@
+ 		case 'c':
+ 			chartorune(&r, buf+1);
+ 			if(r){
++#define HU_INPUTTOGGLE 't'	/* FIXME */
++				/* catch dup, but only once */
++				if((chat_on && laste == 'k' && lastk == buf[1])
++				|| !chat_on && r == HU_INPUTTOGGLE)
++					continue;
+ 				e.data1 = r;
+ 				e.type = ev_char;
+ 				D_PostEvent(&e);
+ 			}
+ 			/* no break */
+ 		default:
++			laste = buf[0];
+ 			continue;
+ 		case 'k':
+ 			s = buf+1;
+ 			while(*s){
++				lastk = *s;
+ 				s += chartorune(&r, s);
+ 				if(utfrune(buf2+1, r) == nil){
+ 					if(e.data1 = runetokey(r)){
++						if(chat_on && shifton && isprint(lastk))
++							e.data1 = _toupper(e.data1);
+ 						e.type = ev_keydown;
+ 						D_PostEvent(&e);
++						if(r == Kshift)
++							shifton++;
+ 					}
+ 				}
+ 			}
+@@ -289,11 +304,14 @@
+ 					if(e.data1 = runetokey(r)){
+ 						e.type = ev_keyup;
+ 						D_PostEvent(&e);
++						if(r == Kshift)
++							shifton = 0;
+ 					}
+ 				}
+ 			}
+ 			break;
+ 		}
++		laste = buf[0];
+ 		strcpy(buf2, buf);
+ 	}
+ 	close(kfd);
--- /dev/null
+++ b/doom-limits
@@ -1,0 +1,173 @@
+this lifts a bunch of limits in the engine for stupid bullshit wads that
+don't honor them. this should not be merged, ever. these inflate a bunch
+of arrays, and make doom use much more memory.
+. savegame buffer overflow: for maps that are too large; does not work
+  correctly, loading several times in game will eventually crash (will not
+  corrupt anything though)
+
+diff -Naur a/sys/src/games/doom/g_game.c b/sys/src/games/doom/g_game.c
+--- a/sys/src/games/doom/g_game.c	Wed Jul 29 13:45:35 2015
++++ b/sys/src/games/doom/g_game.c	Tue Aug 11 15:47:02 2015
+@@ -68,7 +68,7 @@
+ #include "g_game.h"
+ 
+ 
+-#define SAVEGAMESIZE	0x2c000
++#define SAVEGAMESIZE	0x200000
+ #define SAVESTRINGSIZE	24
+ 
+ 
+@@ -137,6 +137,7 @@
+ short		consistancy[MAXPLAYERS][BACKUPTICS]; 
+  
+ byte*		savebuffer;
++void *savbuf;	/* FIXME */
+  
+  
+ // 
+@@ -1261,7 +1262,10 @@
+     sprintf (name,SAVEGAMENAME"%d.dsg",savegameslot); 
+     description = savedescription; 
+ 	 
+-    save_p = savebuffer = screens[1]+0x4000; 
++//    if(save_p == nil || save_p == savebuffer + SAVESTRINGSIZE)
++	if(savbuf == nil)
++		savbuf = Z_Malloc(SAVEGAMESIZE, PU_STATIC, 0);
++	save_p = savebuffer = savbuf;
+ 	 
+     memcpy (save_p, description, SAVESTRINGSIZE); 
+     save_p += SAVESTRINGSIZE; 
+@@ -1296,7 +1300,7 @@
+     players[consoleplayer].message = GGSAVED; 
+ 
+     // draw the pattern into the back screen
+-    R_FillBackScreen ();	
++    R_FillBackScreen ();
+ } 
+  
+ 
+diff -Naur a/sys/src/games/doom/i_system.c b/sys/src/games/doom/i_system.c
+--- a/sys/src/games/doom/i_system.c	Wed Jul 29 18:58:52 2015
++++ b/sys/src/games/doom/i_system.c	Tue Aug 11 15:22:39 2015
+@@ -11,7 +11,7 @@
+ #include "g_game.h"
+ #include "m_misc.h"
+ 
+-int mb_used = 6;	/* 6MB heap */
++int mb_used = 12;	/* 12MB heap */
+ 
+ void I_Init (void)
+ {
+diff -Naur a/sys/src/games/doom/p_enemy.c b/sys/src/games/doom/p_enemy.c
+--- a/sys/src/games/doom/p_enemy.c	Mon Jul 27 20:08:20 2015
++++ b/sys/src/games/doom/p_enemy.c	Tue Aug 11 15:21:28 2015
+@@ -262,7 +262,7 @@
+ fixed_t	xspeed[8] = {FRACUNIT,47000,0,-47000,-FRACUNIT,-47000,0,47000};
+ fixed_t yspeed[8] = {0,47000,FRACUNIT,47000,0,-47000,-FRACUNIT,-47000};
+ 
+-#define MAXSPECIALCROSS	8
++#define MAXSPECIALCROSS	128
+ 
+ extern	line_t*	spechit[MAXSPECIALCROSS];
+ extern	int	numspechit;
+diff -Naur a/sys/src/games/doom/p_local.h b/sys/src/games/doom/p_local.h
+--- a/sys/src/games/doom/p_local.h	Sun Aug  2 19:57:59 2015
++++ b/sys/src/games/doom/p_local.h	Tue Aug 11 15:21:28 2015
+@@ -153,7 +153,7 @@
+     }			d;
+ } intercept_t;
+ 
+-#define MAXINTERCEPTS	128
++#define MAXINTERCEPTS	512
+ 
+ extern intercept_t	intercepts[MAXINTERCEPTS];
+ extern intercept_t*	intercept_p;
+diff -Naur a/sys/src/games/doom/p_map.c b/sys/src/games/doom/p_map.c
+--- a/sys/src/games/doom/p_map.c	Mon Jul 27 20:08:20 2015
++++ b/sys/src/games/doom/p_map.c	Tue Aug 11 15:21:28 2015
+@@ -62,7 +62,7 @@
+ 
+ // keep track of special lines as they are hit,
+ // but don't process them until the move is proven valid
+-#define MAXSPECIALCROSS		8
++#define MAXSPECIALCROSS		128
+ 
+ line_t*		spechit[MAXSPECIALCROSS];
+ int		numspechit;
+diff -Naur a/sys/src/games/doom/p_spec.c b/sys/src/games/doom/p_spec.c
+--- a/sys/src/games/doom/p_spec.c	Mon Jul 27 20:08:20 2015
++++ b/sys/src/games/doom/p_spec.c	Tue Aug 11 15:21:28 2015
+@@ -78,7 +78,7 @@
+ 
+ 
+ 
+-#define MAXANIMS                32
++#define MAXANIMS                256
+ 
+ extern anim_t	anims[MAXANIMS];
+ extern anim_t*	lastanim;
+@@ -321,7 +321,7 @@
+ // Note: this should be doable w/o a fixed array.
+ 
+ // 20 adjoining sectors max!
+-#define MAX_ADJOINING_SECTORS    	20
++#define MAX_ADJOINING_SECTORS    	128
+ 
+ fixed_t
+ P_FindNextHighestFloor
+diff -Naur a/sys/src/games/doom/r_bsp.c b/sys/src/games/doom/r_bsp.c
+--- a/sys/src/games/doom/r_bsp.c	Mon Jul 27 20:08:20 2015
++++ b/sys/src/games/doom/r_bsp.c	Tue Aug 11 15:21:28 2015
+@@ -85,7 +85,7 @@
+ } cliprange_t;
+ 
+ 
+-#define MAXSEGS		32
++#define MAXSEGS		SCREENWIDTH/2+1
+ 
+ // newend is one past the last valid seg
+ cliprange_t*	newend;
+diff -Naur a/sys/src/games/doom/r_defs.h b/sys/src/games/doom/r_defs.h
+--- a/sys/src/games/doom/r_defs.h	Mon Jul 27 20:08:20 2015
++++ b/sys/src/games/doom/r_defs.h	Tue Aug 11 15:21:28 2015
+@@ -52,7 +52,7 @@
+ #define SIL_TOP			2
+ #define SIL_BOTH		3
+ 
+-#define MAXDRAWSEGS		256
++#define MAXDRAWSEGS		1024
+ 
+ 
+ 
+diff -Naur a/sys/src/games/doom/r_plane.c b/sys/src/games/doom/r_plane.c
+--- a/sys/src/games/doom/r_plane.c	Mon Jul 27 20:08:20 2015
++++ b/sys/src/games/doom/r_plane.c	Tue Aug 11 15:21:28 2015
+@@ -47,14 +47,14 @@
+ //
+ 
+ // Here comes the obnoxious "visplane".
+-#define MAXVISPLANES	128
++#define MAXVISPLANES	1024
+ visplane_t		visplanes[MAXVISPLANES];
+ visplane_t*		lastvisplane;
+ visplane_t*		floorplane;
+ visplane_t*		ceilingplane;
+ 
+ // ?
+-#define MAXOPENINGS	SCREENWIDTH*64
++#define MAXOPENINGS	SCREENWIDTH*200
+ short			openings[MAXOPENINGS];
+ short*			lastopening;
+ 
+diff -Naur a/sys/src/games/doom/r_things.h b/sys/src/games/doom/r_things.h
+--- a/sys/src/games/doom/r_things.h	Mon Jul 27 20:08:20 2015
++++ b/sys/src/games/doom/r_things.h	Tue Aug 11 15:21:28 2015
+@@ -24,7 +24,7 @@
+ #define __R_THINGS__
+ 
+ 
+-#define MAXVISSPRITES  	128
++#define MAXVISSPRITES  	1024
+ 
+ extern vissprite_t	vissprites[MAXVISSPRITES];
+ extern vissprite_t*	vissprite_p;
--- /dev/null
+++ b/doom-mouseglitches
@@ -1,0 +1,59 @@
+doom: fix glitchy mouse movement
+
+- fix stuttering mouse movement by using all mouse deltas during a tic, not
+  just the last one (G_Responder can be called more than once per tic)
+- change mouse filtering and sensitivity calculation: divide mouse delta by
+  half, casting to double, and only then multiply by sensitivity
+
+10 * Δ * (sens+5) / 10  →  Δ * 0.5 * sens
+with sens in [1;10]
+
+diff -r 121def0aed4d sys/src/games/doom/g_game.c
+--- a/sys/src/games/doom/g_game.c	Thu Jul 12 09:33:33 2018 +0200
++++ b/sys/src/games/doom/g_game.c	Sat Jul 14 09:32:51 2018 +0200
+@@ -566,8 +566,8 @@
+ 	mousebuttons[0] = ev->data1 & 1; 
+ 	mousebuttons[1] = ev->data1 & 2; 
+ 	mousebuttons[2] = ev->data1 & 4; 
+-	mousex = ev->data2*(mouseSensitivity+5)/10; 
+-	mousey = ev->data3*(mouseSensitivity+5)/10; 
++	mousex += ev->data2*0.5*(mouseSensitivity+1); 
++	mousey += ev->data3*0.5*(mouseSensitivity+1); 
+ 	return true;    // eat events 
+  
+       case ev_joystick: 
+diff -r 121def0aed4d sys/src/games/doom/i_video.c
+--- a/sys/src/games/doom/i_video.c	Thu Jul 12 09:33:33 2018 +0200
++++ b/sys/src/games/doom/i_video.c	Sat Jul 14 09:32:51 2018 +0200
+@@ -362,8 +362,8 @@
+ 			
+ 			e.type = ev_mouse;
+ 			e.data1 = m.buttons;
+-			e.data2 = 10*(m.xy.x - om.xy.x);
+-			e.data3 = 10*(om.xy.y - m.xy.y);
++			e.data2 = m.xy.x - om.xy.x;
++			e.data3 = om.xy.y - m.xy.y;
+ 			D_PostEvent(&e);
+ 			om = m;
+ 
+diff -r 121def0aed4d sys/src/games/doom/m_menu.c
+--- a/sys/src/games/doom/m_menu.c	Thu Jul 12 09:33:33 2018 +0200
++++ b/sys/src/games/doom/m_menu.c	Sat Jul 14 09:32:51 2018 +0200
+@@ -945,7 +945,7 @@
+ 		       W_CacheLumpName(msgNames[showMessages],PU_CACHE));
+ 
+     M_DrawThermo(OptionsDef.x,OptionsDef.y+LINEHEIGHT*(mousesens+1),
+-		 10,mouseSensitivity);
++		 9,mouseSensitivity);
+ 	
+     M_DrawThermo(OptionsDef.x,OptionsDef.y+LINEHEIGHT*(scrnsize+1),
+ 		 9,screenSize);
+@@ -1099,7 +1099,7 @@
+ 	    mouseSensitivity--;
+ 	break;
+       case 1:
+-	if (mouseSensitivity < 9)
++	if (mouseSensitivity < 8)
+ 	    mouseSensitivity++;
+ 	break;
+     }
--- /dev/null
+++ b/doom-mousegrab
@@ -1,0 +1,32 @@
+doom: re-center mouse more often to limit it escaping
+
+when a mouse motion reaches grabout's limits without crossing them, a following
+event in which the mouse passes the remaining distance between grabout and the
+window's limits will have the mouse jump out of the window.
+this happens too frequently on terminals with a good drawing speed.
+
+to reduce this, make grabout smaller, so that the distance to cross in one
+event is larger. Dx(screen->r)/4 is sufficient for the terminals i have tested
+this on.
+
+diff -r 0a3cf47fce65 sys/src/games/doom/i_video.c
+--- a/sys/src/games/doom/i_video.c	Mon Jun 27 00:36:54 2016 +0200
++++ b/sys/src/games/doom/i_video.c	Thu Jun 30 17:51:32 2016 +0300
+@@ -44,7 +44,7 @@
+ 	draw(screen, screen->r, display->black, nil, ZP);
+ 
+ 	center = addpt(screen->r.min, Pt(Dx(screen->r)/2, Dy(screen->r)/2));
+-	grabout = insetrect(screen->r, Dx(screen->r)/8);
++	grabout = insetrect(screen->r, Dx(screen->r)/4);
+ 
+ 	if((pid = rfork(RFPROC|RFMEM)) == 0){
+ 		kbdproc();
+@@ -102,7 +102,7 @@
+ 		draw(screen, screen->r, display->black, nil, ZP);
+ 
+ 		center = addpt(screen->r.min, Pt(Dx(screen->r)/2, Dy(screen->r)/2));
+-		grabout = insetrect(screen->r, Dx(screen->r)/8);
++		grabout = insetrect(screen->r, Dx(screen->r)/4);
+ 	}
+ 
+ 	scale = Dx(screen->r)/SCREENWIDTH;
--- /dev/null
+++ b/doom-naivescaleup
@@ -1,0 +1,64 @@
+ups max scale in an idiotic way
+
+diff -r 7ee51d8d1eb9 sys/src/games/doom/i_video.c
+--- a/sys/src/games/doom/i_video.c	Thu May 11 19:34:54 2017 +0000
++++ b/sys/src/games/doom/i_video.c	Mon May 15 14:46:26 2017 +0200
+@@ -91,7 +91,7 @@
+ 	Rectangle r;
+ 	int y, scale;
+ 	uchar *s, *e, *d, *m;
+-	uchar buf[SCREENWIDTH*3*4];
++	uchar buf[SCREENWIDTH*3*12];
+ 
+ 	if(resized){
+ 		resized = 0;
+@@ -108,8 +108,8 @@
+ 	scale = Dx(screen->r)/SCREENWIDTH;
+ 	if(scale <= 0)
+ 		scale = 1;
+-	else if(scale > 4)
+-		scale = 4;
++	else if(scale > 12)
++		scale = 12;
+ 
+ 	/* where to draw the scaled row */
+ 	r = rectsubpt(rectaddpt(Rect(0, 0, scale*SCREENWIDTH, scale), center),
+@@ -127,6 +127,38 @@
+ 		for(; s < e; s++){
+ 			m = &cmap[*s * 3];
+ 			switch(scale){
++			case 12:
++				*d++ = m[2];
++				*d++ = m[1];
++				*d++ = m[0];
++			case 11:
++				*d++ = m[2];
++				*d++ = m[1];
++				*d++ = m[0];
++			case 10:
++				*d++ = m[2];
++				*d++ = m[1];
++				*d++ = m[0];
++			case 9:
++				*d++ = m[2];
++				*d++ = m[1];
++				*d++ = m[0];
++			case 8:
++				*d++ = m[2];
++				*d++ = m[1];
++				*d++ = m[0];
++			case 7:
++				*d++ = m[2];
++				*d++ = m[1];
++				*d++ = m[0];
++			case 6:
++				*d++ = m[2];
++				*d++ = m[1];
++				*d++ = m[0];
++			case 5:
++				*d++ = m[2];
++				*d++ = m[1];
++				*d++ = m[0];
+ 			case 4:
+ 				*d++ = m[2];
+ 				*d++ = m[1];
--- /dev/null
+++ b/doom-nodclick
@@ -1,0 +1,99 @@
+doom: remove mouse double click and don't move forward with button 3
+
+diff -r bd0ff7078273 sys/src/games/doom/g_game.c
+--- a/sys/src/games/doom/g_game.c	Mon Aug 28 19:45:49 2017 +0200
++++ b/sys/src/games/doom/g_game.c	Wed Aug 30 10:20:53 2017 +0200
+@@ -187,13 +187,6 @@
+ int             mousex;
+ int		mousey;         
+ 
+-int             dclicktime;
+-int		dclickstate;
+-int		dclicks; 
+-int             dclicktime2;
+-int		dclickstate2;
+-int		dclicks2;
+-
+ // joystick values are repeated 
+ int             joyxmove;
+ int		joyymove;
+@@ -235,7 +228,6 @@
+ { 
+     int		i; 
+     boolean	strafe;
+-    boolean	bstrafe; 
+     int		speed;
+     int		tspeed; 
+     int		forward;
+@@ -329,11 +321,7 @@
+ 	cmd->buttons |= BT_ATTACK; 
+  
+     if (gamekeydown[key_use] || joybuttons[joybuse] ) 
+-    { 
+ 	cmd->buttons |= BT_USE;
+-	// clear double clicks if hit use button 
+-	dclicks = 0;                   
+-    } 
+ 
+     // chainsaw overrides 
+     for (i=0 ; i<NUMWEAPONS-1 ; i++)        
+@@ -346,58 +334,7 @@
+     
+     // mouse
+     if (mousebuttons[mousebforward]) 
+-	forward += forwardmove[speed];
+-    
+-    // forward double click
+-    if (mousebuttons[mousebforward] != dclickstate && dclicktime > 1 ) 
+-    { 
+-	dclickstate = mousebuttons[mousebforward]; 
+-	if (dclickstate) 
+-	    dclicks++; 
+-	if (dclicks == 2) 
+-	{ 
+-	    cmd->buttons |= BT_USE; 
+-	    dclicks = 0; 
+-	} 
+-	else 
+-	    dclicktime = 0; 
+-    } 
+-    else 
+-    { 
+-	dclicktime += ticdup; 
+-	if (dclicktime > 20) 
+-	{ 
+-	    dclicks = 0; 
+-	    dclickstate = 0; 
+-	} 
+-    }
+-    
+-    // strafe double click
+-    bstrafe =
+-	mousebuttons[mousebstrafe] 
+-	|| joybuttons[joybstrafe]; 
+-    if (bstrafe != dclickstate2 && dclicktime2 > 1 ) 
+-    { 
+-	dclickstate2 = bstrafe; 
+-	if (dclickstate2) 
+-	    dclicks2++; 
+-	if (dclicks2 == 2) 
+-	{ 
+-	    cmd->buttons |= BT_USE; 
+-	    dclicks2 = 0; 
+-	} 
+-	else 
+-	    dclicktime2 = 0; 
+-    } 
+-    else 
+-    { 
+-	dclicktime2 += ticdup; 
+-	if (dclicktime2 > 20) 
+-	{ 
+-	    dclicks2 = 0; 
+-	    dclickstate2 = 0; 
+-	} 
+-    } 
++	cmd->buttons |= BT_USE; 
+  
+     forward += mousey; 
+     if (strafe) 
--- /dev/null
+++ b/doom-skywallhit
@@ -1,0 +1,76 @@
+bullet puffs and plasma splashes don't spawn, and rockets don't explode,
+when fired against a linedef bridging two sectors of differing heights but
+with a sky texture. example: ultimate doom e4m6: fire at the green
+structure surrounded by a moat of lava outside, or at the moat's walls.
+the "sky hack" is meant to show the sky (in this particular configuration)
+rather than a texture, and to make projectiles disappear when fired at this
+"sky". however, the check for the wall's attributes and the projectiles
+coordinates is wrong.
+this patches fixes that. however, it affects gameplay, and as such, demos
+will desync anytime this mechanism is encountered. it is therefore turned
+off by default.
+not sure if this should be merged.
+TODO: better explanation, more testing
+
+diff -Naur a/sys/src/games/doom/d_main.c b/sys/src/games/doom/d_main.c
+--- a/sys/src/games/doom/d_main.c	Sun Aug  2 19:57:59 2015
++++ b/sys/src/games/doom/d_main.c	Fri Aug 21 15:09:01 2015
+@@ -94,6 +94,8 @@
+ boolean		noztele;
+ boolean		nobounce;
+ 
++/* demo breaking bug fixes */
++boolean		noskyabs;
+ 
+ //extern int soundVolume;
+ //extern  int	sfxVolume;
+@@ -743,6 +745,8 @@
+ 	noztele = 1;
+     if (M_CheckParm ("-nobounce") && (gamemode == commercial || gamemode == registered))
+ 	nobounce = 1;
++    if (M_CheckParm ("-noskyabs"))
++	noskyabs = 1;
+     if (M_CheckParm ("-altdeath"))
+ 	deathmatch = 2;
+     else if (M_CheckParm ("-deathmatch"))
+diff -Naur a/sys/src/games/doom/p_local.h b/sys/src/games/doom/p_local.h
+--- a/sys/src/games/doom/p_local.h	Sun Aug  2 19:57:59 2015
++++ b/sys/src/games/doom/p_local.h	Fri Aug 21 15:05:02 2015
+@@ -62,6 +62,7 @@
+ 
+ extern	boolean	noztele;
+ extern	boolean	nobounce;
++extern	boolean noskyabs;
+ 
+ 
+ //
+diff -Naur a/sys/src/games/doom/p_map.c b/sys/src/games/doom/p_map.c
+--- a/sys/src/games/doom/p_map.c	Mon Jul 27 20:08:20 2015
++++ b/sys/src/games/doom/p_map.c	Fri Aug 21 15:10:30 2015
+@@ -958,7 +958,8 @@
+ 		return false;
+ 	    
+ 	    // it's a sky hack wall
+-	    if	(li->backsector && li->backsector->ceilingpic == skyflatnum)
++	    if (li->backsector != nil && li->backsector->ceilingpic == skyflatnum
++	    && (!noskyabs || z > li->backsector->ceilingheight))
+ 		return false;		
+ 	}
+ 
+diff -Naur a/sys/src/games/doom/p_mobj.c b/sys/src/games/doom/p_mobj.c
+--- a/sys/src/games/doom/p_mobj.c	Sun Aug  2 19:57:59 2015
++++ b/sys/src/games/doom/p_mobj.c	Fri Aug 21 15:03:11 2015
+@@ -173,9 +173,10 @@
+ 	    else if (mo->flags & MF_MISSILE)
+ 	    {
+ 		// explode a missile
+-		if (ceilingline &&
+-		    ceilingline->backsector &&
+-		    ceilingline->backsector->ceilingpic == skyflatnum)
++		if (ceilingline != nil &&
++		    ceilingline->backsector != nil &&
++		    ceilingline->backsector->ceilingpic == skyflatnum &&
++		    (!noskyabs || mo->z > ceilingline->backsector->ceilingheight))
+ 		{
+ 		    // Hack to prevent missiles exploding
+ 		    // against the sky.
--- /dev/null
+++ b/doom-spy
@@ -1,0 +1,57 @@
+diff -Naur a/sys/src/games/doom/g_game.c b/sys/src/games/doom/g_game.c
+--- a/sys/src/games/doom/g_game.c	Mon Aug 31 01:13:18 2015
++++ b/sys/src/games/doom/g_game.c	Wed Sep 16 08:16:55 2015
+@@ -510,6 +510,9 @@
+ 	    displayplayer++; 
+ 	    if (displayplayer == MAXPLAYERS) 
+ 		displayplayer = 0; 
++		/* FIXME */
++		extern player_t *plyr;
++		plyr = &players[displayplayer];
+ 	} while (!playeringame[displayplayer] && displayplayer != consoleplayer); 
+ 	return true; 
+     }
+diff -Naur a/sys/src/games/doom/s_sound.c b/sys/src/games/doom/s_sound.c
+--- a/sys/src/games/doom/s_sound.c	Sun Jul 29 23:01:33 2012
++++ b/sys/src/games/doom/s_sound.c	Wed Sep 16 08:16:08 2015
+@@ -38,6 +38,7 @@
+ 
+ #include "doomstat.h"
+ 
++extern player_t *plyr;
+ 
+ // Purpose?
+ const char snd_prefixen[]
+@@ -281,16 +282,16 @@
+ 
+   // Check to see if it is audible,
+   //  and if not, modify the params
+-  if (origin && origin != players[consoleplayer].mo)
++  if (origin && origin != plyr->mo)
+   {
+-    rc = S_AdjustSoundParams(players[consoleplayer].mo,
++    rc = S_AdjustSoundParams(plyr->mo,
+ 			     origin,
+ 			     &volume,
+ 			     &sep,
+ 			     &pitch);
+ 	
+-    if ( origin->x == players[consoleplayer].mo->x
+-	 && origin->y == players[consoleplayer].mo->y)
++    if ( origin->x == plyr->mo->x
++	 && origin->y == plyr->mo->y)
+     {	
+       sep = NORM_SEP;
+     }
+diff -Naur a/sys/src/games/doom/st_stuff.c b/sys/src/games/doom/st_stuff.c
+--- a/sys/src/games/doom/st_stuff.c	Mon Aug 17 23:00:05 2015
++++ b/sys/src/games/doom/st_stuff.c	Wed Sep 16 08:14:48 2015
+@@ -264,7 +264,7 @@
+ 
+ 	    
+ // main player in game
+-static player_t*	plyr; 
++player_t*	plyr; 
+ 
+ // ST_Start() has just been called
+ static boolean		st_firsttime;
--- /dev/null
+++ b/doom-udpip
@@ -1,0 +1,507 @@
+port udp/ip code from release.
+this works, but parts of the code suck and need to be altered. what sucks
+is that the doom networking protocol is shit. it's a peer-to-peer thing
+where each node needs to send frame information (ticcmd's) to every other
+node, and receive ticcmd's from every other node. any node that has a
+slower connection or is a slower machine will bring the game down for
+everyone. also, high bandwidth required.
+
+shitfuck n2: no port uses this protocol, since it sucks, and everyone
+implements their own incompatible crap, so no one port can talk to another,
+besides maybe the ports back to DOS. the code in i_net.c is unlikely to
+change too much though, unless this implementation is amended.
+one idea is to ask for chocolate-doom's networking protocol specs.
+chocolate-doom works fairly well, and supports a bunch of different OS',
+including openbsd, and being able to connect with 9front doom to another
+OS' shit is advanteageous, at least because live servers with bunches of
+players will be accessible. 9front doom and chocolate-doom _should_ be
+compatible, but idk.
+as it is, sdldoom might be able to connect to 9front doom with this patch,
+but good luck making sdldoom work.
+
+player 0 is special, and sets the game settings, sending them to the other
+nodes.
+testing this is more difficult without other patches because of the issues
+in the sound code, so try with doom-sndmus.patch and doom-sleep.patch.
+
+syntax: doom [-srv port] [-pn 0-3] [-net host!port...]
+defaults: udp port 666, player number 0
+
+example for a 3 player game, two nodes being on the same host (for shits):
+u14% games/doom -altdeath -nomonsters -warp7 -net u6!666 u14!667
+u6% games/doom -pn 1 -net u14!666 u14!667
+u14-1% games/doom -pn 2 -srv 667 -net u14!666 u6!666
+
+TODO: cf FIXME's in the code, conreq shit, more testing(?)
+
+diff -Naur a/sys/src/games/doom/i_net.c b/sys/src/games/doom/i_net.c
+--- a/sys/src/games/doom/i_net.c	Wed Jan 18 01:13:24 2012
++++ b/sys/src/games/doom/i_net.c	Wed Sep 16 06:45:07 2015
+@@ -20,142 +20,298 @@
+ //
+ //-----------------------------------------------------------------------------
+ 
+-static const char
+-rcsid[] = "$Id: m_bbox.c,v 1.1 1997/02/03 22:45:10 b1 Exp $";
+-
+ #include "doomdef.h"
+ #include "doomstat.h"
+-
+-// #include "i_system.h"
+-// #include "d_event.h"
+-#include "d_net.h"
++#include <bio.h>
++#include <ndb.h>
++#include <ip.h>
+ #include "m_argv.h"
+-
++#include "i_system.h"
++#include "d_net.h"
+ #include "i_net.h"
++#include "w_wad.h"
+ 
++typedef struct Addr Addr;
+ 
+-//
+-// I_InitNetwork
+-//
+-void I_InitNetwork (void)
+-{
+-printf("PORTME i_net.c I_InitNetwork (use 9P)\n");
++enum{
++	HDRSZ = 16+16+16+2+2	/* sizeof Udphdr w/o padding */
++};
+ 
+-	doomcom = malloc (sizeof(*doomcom));
+-	memset (doomcom, 0, sizeof(*doomcom));
++static char lsrv[6] = "666";
+ 
+-	/* set up for network */
+-	doomcom->ticdup = 1;
+-	doomcom->extratics = 0;
++struct Addr{
++	Udphdr h;
++	char srv[6];	/* convenience */
++	int ready;	/* is connected to udp!*!lsrv */
++	long called;
++};
++static Addr raddr[MAXNETNODES];
+ 
+-//	netsend = PacketSend;
+-//	netget = PacketGet;
+-//	netgame = true;
++static int ucfd;
++static int udfd;
++static int upfd[2];
++static int upid;
+ 
+-	/* parse player number and host list */
+-//	doomcom->consoleplayer = myargv[i+1][0]-'1';
+ 
+-	doomcom->numnodes = 1;	// this node for sure
++static void
++conreq(doomdata_t *d)
++{
++	int fd;
++	long t;
++	char ip[64];
++	Addr *p;
++
++	p = &raddr[doomcom->remotenode];
++
++	t = time(nil);
++	if(t - p->called < 1)
++		return;
++
++	snprint(ip, sizeof ip, "%I", p->h.raddr);
++	if((fd = dial(netmkaddr(ip, "udp", p->srv), lsrv, nil, nil)) < 0)
++		sysfatal("dial: %r");
++	if(write(fd, d, doomcom->datalength) != doomcom->datalength)
++		sysfatal("conreq: %r");
++	close(fd);
++	p->called = t;
++}
+ 
+-	doomcom->id = DOOMCOM_ID;
+-	doomcom->numplayers = doomcom->numnodes;
++static void
++dsend(void)
++{
++	int i;
++	uchar buf[HDRSZ+sizeof(doomdata_t)];
++	doomdata_t d;
++
++	hnputl(&d.checksum, netbuffer->checksum);
++	d.player = netbuffer->player;
++	d.retransmitfrom = netbuffer->retransmitfrom;
++	d.starttic = netbuffer->starttic;
++	d.numtics = netbuffer->numtics;
++
++	for(i = 0; i < netbuffer->numtics; i++){
++		d.cmds[i].forwardmove = netbuffer->cmds[i].forwardmove;
++		d.cmds[i].sidemove = netbuffer->cmds[i].sidemove;
++		hnputs(&d.cmds[i].angleturn, netbuffer->cmds[i].angleturn);
++		hnputs(&d.cmds[i].consistancy, netbuffer->cmds[i].consistancy);
++		d.cmds[i].chatchar = netbuffer->cmds[i].chatchar;
++		d.cmds[i].buttons = netbuffer->cmds[i].buttons;
++	}
+ 
+-/*
+-    boolean		trueval = true;
+-    int			i;
+-    int			p;
+-    struct hostent*	hostentry;	// host information entry
+-	
+-    doomcom = malloc (sizeof (*doomcom) );
+-    memset (doomcom, 0, sizeof(*doomcom) );
+-    
+-    // set up for network
+-    i = M_CheckParm ("-dup");
+-    if (i && i< myargc-1)
+-    {
+-	doomcom->ticdup = myargv[i+1][0]-'0';
+-	if (doomcom->ticdup < 1)
+-	    doomcom->ticdup = 1;
+-	if (doomcom->ticdup > 9)
+-	    doomcom->ticdup = 9;
+-    }
+-    else
+-	doomcom-> ticdup = 1;
+-	
+-    if (M_CheckParm ("-extratic"))
+-	doomcom-> extratics = 1;
+-    else
+-	doomcom-> extratics = 0;
+-		
+-    p = M_CheckParm ("-port");
+-    if (p && p<myargc-1)
+-    {
+-	DOOMPORT = atoi (myargv[p+1]);
+-	printf ("using alternate port %i\n",DOOMPORT);
+-    }
+-    
+-    // parse network game options,
+-    //  -net <consoleplayer> <host> <host> ...
+-    i = M_CheckParm ("-net");
+-    if (!i)
+-    {
+-	// single player game
+-	netgame = false;
+-	doomcom->id = DOOMCOM_ID;
+-	doomcom->numplayers = doomcom->numnodes = 1;
+-	doomcom->deathmatch = false;
+-	doomcom->consoleplayer = 0;
+-	return;
+-    }
+-
+-    netsend = PacketSend;
+-    netget = PacketGet;
+-    netgame = true;
+-
+-    // parse player number and host list
+-    doomcom->consoleplayer = myargv[i+1][0]-'1';
+-
+-    doomcom->numnodes = 1;	// this node for sure
+-	
+-    i++;
+-    while (++i < myargc && myargv[i][0] != '-')
+-    {
+-	sendaddress[doomcom->numnodes].sin_family = AF_INET;
+-	sendaddress[doomcom->numnodes].sin_port = htons(DOOMPORT);
+-	if (myargv[i][0] == '.')
+-	{
+-	    sendaddress[doomcom->numnodes].sin_addr.s_addr 
+-		= inet_addr (myargv[i]+1);
++	if(!raddr[doomcom->remotenode].ready){
++		conreq(&d);
++		return;
+ 	}
++	memcpy(buf, &raddr[doomcom->remotenode].h, HDRSZ);
++	memcpy(buf+HDRSZ, &d, sizeof d);
++
++	i = doomcom->datalength + HDRSZ;
++	if(write(udfd, buf, i) != i)
++		sysfatal("dsend: %r");
++}
++
++static void
++drecv(void)
++{
++	int n;
++	ushort i;
++	doomdata_t d;
++
++	if(filelength(upfd[1]) < 1){
++		doomcom->remotenode = -1;
++		return;
++	}
++	if((n = read(upfd[1], &d, sizeof d)) <= 0
++	|| read(upfd[1], &i, sizeof i) <= 0)
++		sysfatal("drecv: %r");
++
++	doomcom->remotenode = i;
++	doomcom->datalength = n;
++
++	/* FIXME: proper read/write from/to struct */
++	netbuffer->checksum = nhgetl(&d.checksum);
++	netbuffer->player = d.player;
++	netbuffer->retransmitfrom = d.retransmitfrom;
++	netbuffer->starttic = d.starttic;
++	netbuffer->numtics = d.numtics;
++	for(i = 0; i < netbuffer->numtics; i++){
++		netbuffer->cmds[i].forwardmove = d.cmds[i].forwardmove;
++		netbuffer->cmds[i].sidemove = d.cmds[i].sidemove;
++		netbuffer->cmds[i].angleturn = nhgets(&d.cmds[i].angleturn);
++		netbuffer->cmds[i].consistancy = nhgets(&d.cmds[i].consistancy);
++		netbuffer->cmds[i].chatchar = d.cmds[i].chatchar;
++		netbuffer->cmds[i].buttons = d.cmds[i].buttons;
++	}
++}
++
++static void
++uproc(void)
++{
++	int n;
++	ushort i;
++	uchar buf[HDRSZ+sizeof(doomdata_t)];
++	Udphdr h;
++
++	for(;;){
++		if((n = read(udfd, buf, sizeof buf)) <= 0)
++			break;
++		memcpy(&h, buf, HDRSZ);
++
++		for(i = 0; i < doomcom->numnodes; i++)
++			if(equivip6(h.raddr, raddr[i].h.raddr)
++			&& nhgets(h.rport) == nhgets(raddr[i].h.rport))
++				break;
++		if(i == doomcom->numnodes)
++			continue;	/* ignore messages from strangers */
++		if(!raddr[i].ready){	/* FIXME: urgh */
++			raddr[i].ready++;
++			memcpy(&raddr[i].h, &h, sizeof h);
++		}
++
++		if(write(upfd[0], buf+HDRSZ, n - HDRSZ) != n - HDRSZ
++		|| write(upfd[0], &i, sizeof i) != sizeof i)
++			break;
++	}
++}
++
++void
++I_NetCmd(void)
++{
++	if(doomcom->command == CMD_SEND)
++		dsend();
++	else if(doomcom->command == CMD_GET)
++		drecv();
+ 	else
+-	{
+-	    hostentry = gethostbyname (myargv[i]);
+-	    if (!hostentry)
+-		I_Error ("gethostbyname: couldn't find %s", myargv[i]);
+-	    sendaddress[doomcom->numnodes].sin_addr.s_addr 
+-		= *(int *)hostentry->h_addr_list[0];
+-	}
+-	doomcom->numnodes++;
+-    }
+-	
+-    doomcom->id = DOOMCOM_ID;
+-    doomcom->numplayers = doomcom->numnodes;
+-*/
++		I_Error("invalid netcmd %d", doomcom->command);
++}
++
++void
++I_ShutdownNet(void)
++{
++	postnote(PNPROC, upid, "shutdown");
++	close(upfd[0]);
++	close(upfd[1]);
++	close(udfd);
++	close(ucfd);
++}
++
++static void
++initudp(void)
++{
++	int pid;
++	char data[64], adir[40];
++
++	/* FIXME */
++	//if(myipaddr(raddr[0].h.raddr, nil) < 0)
++	//	sysfatal("myipaddr: %r");
++
++	if((ucfd = announce(netmkaddr("*", "udp", lsrv), adir)) < 0)
++		sysfatal("announce: %r");
++	if(fprint(ucfd, "headers") < 0)
++		sysfatal("failed to set headers mode: %r");
++	snprint(data, sizeof data, "%s/data", adir);
++	if((udfd = open(data, ORDWR)) < 0)
++		sysfatal("open: %r");
++
++	if(pipe(upfd) < 0)
++		sysfatal("pipe: %r");
++	if((pid = rfork(RFPROC|RFMEM|RFFDG)) == 0){
++		uproc();
++		exits(nil);
++	}
++	upid = pid;
++}
++
++static void
++csip(char *s, Addr *a)	/* raddr!rsrv */
++{
++	int fd, n;
++	char buf[128], *f[3];
++
++	/* FIXME: get netmnt... */
++
++	if((fd = open("/net/cs", ORDWR)) < 0)
++		sysfatal("open: %r");
++
++	snprint(buf, sizeof buf, "udp!%s", s);
++	n = strlen(buf);
++	if(write(fd, buf, n) != n)
++		sysfatal("translating %s: %r", s);
++
++	seek(fd, 0, 0);
++	if((n = read(fd, buf, sizeof(buf)-1)) <= 0)
++		sysfatal("reading cs tables: %r");
++	buf[n] = 0;
++	close(fd);
++
++	if(getfields(buf, f, 3, 1, " !") < 2)
++		sysfatal("bad cs entry %s", buf);
++
++	if(parseip(a->h.raddr, f[1]) < 0)
++		sysfatal("parseip: %r");
++	hnputs(a->h.rport, atoi(f[2]));	/* FIXME */
++	strncpy(a->srv, f[2], sizeof(a->srv)-1);
++}
++
++static int
++netopts(void)
++{
++	int i;
++
++	if((i = M_CheckParm("-dup")) && i < myargc - 1){
++		doomcom->ticdup = myargv[i+1][0] - '0';
++		if(doomcom->ticdup < 1)
++			doomcom->ticdup = 1;
++		if(doomcom->ticdup > 9)
++			doomcom->ticdup = 9;
++	}
++
++	if(M_CheckParm("-extratic"))
++		doomcom->extratics = 1;
++
++	if((i = M_CheckParm("-srv")) && i < myargc - 1)
++		strncpy(lsrv, myargv[i+1], sizeof(lsrv)-1);
++
++	/* [0-3], default 0; player 0 is special */
++	if((i = M_CheckParm("-pn")) && i < myargc - 1)
++		doomcom->consoleplayer = myargv[i+1][0] - '0';
++
++	/* FIXME: d_net.c: don't use remoteaddr=0 as special case (max+1?) */
++	/* remote host address list: -net raddr!rsrv.. */
++	if((i = M_CheckParm("-net")) == 0){
++		/* single player game */
++		doomcom->id = DOOMCOM_ID;
++		doomcom->numplayers = doomcom->numnodes = 1;
++		doomcom->deathmatch = false;
++		netgame = false;
++		return -1;
++	}
++	doomcom->numnodes++;	/* raddr[0] is special cased because ??? */
++	while(++i < myargc && myargv[i][0] != '-'){
++		csip(myargv[i], &raddr[doomcom->numnodes]);
++		doomcom->numnodes++;
++	}
++
++	return 0;
+ }
+ 
++void
++I_InitNetwork(void)
++{
++	doomcom = malloc(sizeof *doomcom);
++	memset(doomcom, 0, sizeof *doomcom);
++
++	doomcom->ticdup = 1;
++	doomcom->extratics = 0;
++	if(netopts() < 0)
++		return;
++	if(doomcom->numnodes < 2)
++		I_Error("netgame with a single node");
++	doomcom->id = DOOMCOM_ID;
++	doomcom->numplayers = doomcom->numnodes;
++
++	fmtinstall('I', eipfmt);
++	initudp();
+ 
+-void I_NetCmd (void)
+-{
+-/*
+-    if (doomcom->command == CMD_SEND)
+-    {
+-	netsend ();
+-    }
+-    else if (doomcom->command == CMD_GET)
+-    {
+-	netget ();
+-    }
+-    else
+-	I_Error ("Bad net cmd: %i\n",doomcom->command);
+-*/
++	netgame = true;
+ }
+diff -Naur a/sys/src/games/doom/i_net.h b/sys/src/games/doom/i_net.h
+--- a/sys/src/games/doom/i_net.h	Wed Jan 18 01:13:23 2012
++++ b/sys/src/games/doom/i_net.h	Wed Sep 16 06:36:09 2015
+@@ -35,6 +35,7 @@
+ 
+ void I_InitNetwork (void);
+ void I_NetCmd (void);
++void I_ShutdownNet (void);
+ 
+ 
+ #endif
+diff -Naur a/sys/src/games/doom/i_system.c b/sys/src/games/doom/i_system.c
+--- a/sys/src/games/doom/i_system.c	Wed Feb  5 08:58:15 2014
++++ b/sys/src/games/doom/i_system.c	Wed Sep 16 06:36:09 2015
+@@ -6,6 +6,7 @@
+ #include "i_system.h"
+ #include "i_sound.h"
+ #include "i_video.h"
++#include "i_net.h"
+ 
+ #include "d_net.h"
+ #include "g_game.h"
+@@ -41,6 +42,7 @@
+ void I_Quit (void)
+ {
+ 	D_QuitNetGame ();
++	I_ShutdownNet();
+ 	I_ShutdownSound();
+ 	I_ShutdownMusic();
+ 	M_SaveDefaults ();
+@@ -85,6 +87,7 @@
+ 	G_CheckDemoStatus();
+ 
+     D_QuitNetGame ();
++    I_ShutdownNet();
+     I_ShutdownGraphics();
+ 
+     exits("I_Error");
+diff -Naur a/sys/src/games/doom/w_wad.h b/sys/src/games/doom/w_wad.h
+--- a/sys/src/games/doom/w_wad.h	Wed Jan 18 01:13:24 2012
++++ b/sys/src/games/doom/w_wad.h	Wed Sep 16 06:38:33 2015
+@@ -67,6 +67,7 @@
+ void*	W_CacheLumpNum (int lump, int tag);
+ void*	W_CacheLumpName (char* name, int tag);
+ 
++vlong	filelength(int);
+ 
+ 
+ 
--- /dev/null
+++ b/doomn-sawup
@@ -1,0 +1,60 @@
+does not work. second part should also be removed.
+
+diff -Naur a/sys/src/games/doom/i_sound.c b/sys/src/games/doom/i_sound.c
+--- a/sys/src/games/doom/i_sound.c	Wed Jul 29 13:45:35 2015
++++ b/sys/src/games/doom/i_sound.c	Sun Aug  2 23:51:32 2015
+@@ -333,33 +333,21 @@
+ 	int			rightvol;
+ 	int			leftvol;
+ 
+-	/* Chainsaw troubles.
+-	** Play these sound effects only one at a time. */
+-	if ( id == sfx_sawup ||
+-	     id == sfx_sawidl ||
+-	     id == sfx_sawful ||
+-	     id == sfx_sawhit ||
+-	     id == sfx_stnmov ||
+-	     id == sfx_pistol )
++	for (i=0 ; (i<NUM_CHANNELS) && (channels[i]) ; i++)
+ 	{
+-		/* Loop all channels, check. */
+-		for (i=0 ; i < NUM_CHANNELS ; i++)
+-		{
+-			/* Active and using the same SFX? */
+-			if( (channels[i]) && (channelids[i] == id) )
+-			{
+-				/* Reset. */
++		if(id == sfx_sawidl && channelids[i] == sfx_sawup)	/* don't play during sawup */
++			return 0;
++		if(channelids[i] == id)
++			switch(id){	/* to be played only one at a time */
++			case sfx_sawup:
++			case sfx_sawidl:
++			case sfx_sawful:
++			case sfx_sawhit:
++			case sfx_stnmov:
++			case sfx_pistol:
+ 				channels[i] = 0;
+-				/* We are sure that iff,
+-				**  there will only be one. */
+-				break;
++				continue;
+ 			}
+-		}
+-	}
+-
+-	/* Loop all channels to find oldest SFX. */
+-	for (i=0 ; (i<NUM_CHANNELS) && (channels[i]) ; i++)
+-	{
+ 		if(channelstart[i] < oldest)
+ 		{
+ 			oldestnum = i;
+@@ -383,7 +371,7 @@
+ 	*/
+ 	channels[slot] = (uchar*) S_sfx[id].data;
+ 	/* Set pointer to end of raw data. */
+-	channelsend[slot] = channels[slot] + lengths[id];
++	channelsend[slot] = channels[slot] + (id == sfx_sawful ? lengths[id] / 2 : lengths[id]);
+ 
+ 	/* Reset current handle number, limited to 0..100. */
+ 	if (!handlenums)
--- /dev/null
+++ b/doomn-sleep
@@ -1,0 +1,17 @@
+this is necessary with doom-sndmus.patch to prevent nsec() being called
+trillions of times per frame because the game is running too fast...
+(i think)
+
+diff -Naur a/sys/src/games/doom/d_net.c b/sys/src/games/doom/d_net.c
+--- a/sys/src/games/doom/d_net.c	Fri May 15 05:42:00 2015
++++ b/sys/src/games/doom/d_net.c	Wed Sep 16 07:47:17 2015
+@@ -722,7 +722,8 @@
+ 	{
+ 	    M_Ticker ();
+ 	    return;
+-	} 
++	}
++	sleep(1);
+     }
+     
+     // run the count * ticdup dics
--- /dev/null
+++ b/doomn-waitvbl
@@ -1,0 +1,19 @@
+I_WaitVBL is used as a delay twice: once in the netcode on disconnect, to
+wait a little before sending notice to the next node, and once on exit, to
+allow playing one last sound effect.
+requires doom-sndmus.patch (since sound is handled on a different proc).
+
+diff -Naur a/sys/src/games/doom/i_video.c b/sys/src/games/doom/i_video.c
+--- a/sys/src/games/doom/i_video.c	Wed Jul 29 13:45:35 2015
++++ b/sys/src/games/doom/i_video.c	Tue Aug 11 14:25:16 2015
+@@ -185,8 +185,9 @@
+ {
+ }
+ 
+-void I_WaitVBL(int)
++void I_WaitVBL(int s)
+ {
++	sleep(s * (1000/70));
+ }
+ 
+ 
--- /dev/null
+++ b/dport-run
@@ -1,0 +1,67 @@
+--- /mnt/git/branch/heads/master/tree/run.c	Thu Jun 11 15:28:29 2015
++++ run.c	Sun Jan  5 12:25:02 2020
+@@ -100,15 +100,32 @@ setfb(void)
+ 	if(fprint(fd, "size 1280x1024x32 x8b8g8r8") < 0)
+ //	if(fprint(fd, "size 1280x1024x16 r5g6b5") < 0)
+ 		sysfatal("fprint: %r");
+-	if(fprint(fd, "addr %#ux", v) < 0)
++	if(fprint(fd, "addr %#p", v) < 0)
+ 		sysfatal("fprint: %r");
+ }
+ 
+ void
++setmouse(void *v)
++{
++	int fd;
++	static int shat;
++
++	if(shat)
++		return;
++	if((fd = open("/dev/mousectl", OWRITE)) < 0)
++		return;
++	if(fprint(fd, "addr %#p", v) < 0)
++		fprint(2, "shit: %r\n");
++	else
++		shat++;
++	close(fd);
++}
++
++void
+ main()
+ {
+ 	ulong *r;
+-	uchar *rr, s, s1;
++	uchar *rr, s;
+ 	ulong addr;
+ 	int twolane, fast, emph, swing;
+ 	
+@@ -140,16 +157,17 @@ main()
+ 	addr = getpa();
+ 	r[START] = addr;
+ 	r[END] = addr + 1280*1024*4;
+-//	setfb();
++	setfb();
+ 
+ 	r[CTRL] = 1<<31;
+ 	//goto manual;
+ 	for(;;){
+ 		sleep(100);
+-		print("%ux %x\n", r[STS], rr[0x202]);
++		setmouse(r+CURS);
++		print("%lux %x\n", r[STS], rr[0x202]);
+ 	}
+ 
+-manual:
++//manual:
+ 	r[CTRL] = 1 << 13 | swing << 6 | emph << 4 | twolane << 1 | fast;
+ 	rr[TRAINING_LANE0_SET] = emph << 4 | swing;
+ 	rr[LINK_BW_SET] = fast ? 0x0A : 0x06;
+@@ -177,7 +195,7 @@ manual:
+ 
+ 	for(;;){
+ 		sleep(1000);
+-		print("%x %x %x\n", r[STS], rr[0x202], rr[0x204]);
++		print("%lx %x %x\n", r[STS], rr[0x202], rr[0x204]);
+ 	}
+ 	
+ /*	r[HVACT] = 1024 << 16 | 768;
--- /dev/null
+++ b/faces-col
@@ -1,0 +1,86 @@
+diff -r a620b482772f sys/src/cmd/faces/main.c
+--- a/sys/src/cmd/faces/main.c	Sun Mar 18 07:53:10 2018 +0100
++++ b/sys/src/cmd/faces/main.c	Wed Mar 21 02:18:49 2018 +0200
+@@ -53,8 +53,10 @@
+ 	0x18, 0x00, 0x00, 0x10, 0x00
+ };
+ 
+-Image	*blue;		/* full arrow */
+-Image	*bgrnd;		/* pale blue background color */
++Image	*arrow;		/* full arrow */
++Image	*smallfg;
++Image	*bgrnd;		/* background color */
++Image	*facebg;	/* background for face */
+ Image	*left;		/* left-pointing arrow mask */
+ Image	*right;		/* right-pointing arrow mask */
+ Font	*tinyfont;
+@@ -97,11 +99,13 @@
+ 	initplumb();
+ 
+ 	/* make background color */
+-	bgrnd = allocimagemix(display, DPalebluegreen, DWhite);
+-	blue = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x008888FF);	/* blue-green */
++	bgrnd = display->black;
++	arrow = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x550000FF);
++	smallfg = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x884400FF);
++	facebg = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xCCCCCCFF);
+ 	left = allocimage(display, leftright, GREY1, 0, DWhite);
+ 	right = allocimage(display, leftright, GREY1, 0, DWhite);
+-	if(bgrnd==nil || blue==nil || left==nil || right==nil){
++	if(bgrnd==nil || arrow==nil || smallfg == nil || facebg == nil || left==nil || right==nil){
+ 		fprint(2, "faces: can't create images: %r\n");
+ 		exits("image");
+ 	}
+@@ -113,7 +117,7 @@
+ 	tinyfont = openfont(display, "/lib/font/bit/misc/ascii.5x7.font");
+ 	if(tinyfont == nil)
+ 		tinyfont = font;
+- 	mediumfont = openfont(display, "/lib/font/bit/misc/unicode.6x13.font");
++ 	mediumfont = openfont(display, "/lib/font/bit/vga/unicode.font");
+  	if(mediumfont == nil)
+  		mediumfont = font;
+ 	datefont = font;
+@@ -138,7 +142,7 @@
+ 	r.max.x = enddate.x;
+ 	r.max.y = enddate.y+datefont->height;
+ 	draw(screen, r, bgrnd, nil, ZP);
+-	string(screen, r.min, display->black, ZP, datefont, date);
++	string(screen, r.min, smallfg, ZP, datefont, date);
+ }
+ 
+ void
+@@ -257,18 +261,19 @@
+ 		return;
+ 	r = facerect(i-first);
+ 	draw(screen, r, bgrnd, nil, ZP);
++	draw(screen, Rpt(r.min, addpt(r.min, Pt(Facesize, Facesize))), facebg, nil, ZP);
+ 	draw(screen, r, f->bit, f->mask, ZP);
+ 	r.min.y += Facesize;
+-	center(mediumfont, r.min, f->str[Suser], display->black);
++	center(mediumfont, r.min, f->str[Suser], smallfg);
+ 	r.min.y += mediumfont->height;
+ 	tstr = facetime(f, &f->recent);
+-	center(mediumfont, r.min, tstr, display->black);
++	center(mediumfont, r.min, tstr, facebg);
+ 	if(f->unknown){
+ 		r.min.y -= mediumfont->height + tinyfont->height + 2;
+ 		for(p.x=-1; p.x<=1; p.x++)
+ 			for(p.y=-1; p.y<=1; p.y++)
+-				center(tinyfont, addpt(r.min, p), f->str[Sdomain], display->white);
+-		center(tinyfont, r.min, f->str[Sdomain], display->black);
++				center(tinyfont, addpt(r.min, p), f->str[Sdomain], display->black);
++		center(tinyfont, r.min, f->str[Sdomain], facebg);
+ 	}
+ }
+ 
+@@ -307,8 +312,8 @@
+ 	leftr = rectaddpt(leftright, p);
+ 	p.x += Dx(leftright) + Facesep;
+ 	rightr = rectaddpt(leftright, p);
+-	draw(screen, leftr, first>0? blue : bgrnd, left, leftright.min);
+-	draw(screen, rightr, last<nfaces? blue : bgrnd, right, leftright.min);
++	draw(screen, leftr, first>0? arrow : bgrnd, left, leftright.min);
++	draw(screen, rightr, last<nfaces? arrow : bgrnd, right, leftright.min);
+ }
+ 
+ void
--- /dev/null
+++ b/file-mod
@@ -1,0 +1,11 @@
+diff -r 121def0aed4d sys/src/cmd/file.c
+--- a/sys/src/cmd/file.c	Thu Jul 12 09:33:33 2018 +0200
++++ b/sys/src/cmd/file.c	Fri Jul 13 13:39:52 2018 +1200
+@@ -920,6 +920,7 @@
+ } offstrs[] = {
+ 	32*1024, "\001CD001\001",	"ISO9660 CD image",	7,	"application/x-iso9660-image",
+ 	32*4, "DICM",	"DICOM medical imaging data",	4,	"application/dicom",
++	1080, "M.K.",	"Amiga module",	4,	"audio/mod",
+ 	0, 0, 0, 0, 0
+ };
+ 
--- /dev/null
+++ b/fplot-abs
@@ -1,0 +1,19 @@
+diff -r 82ca4d784349 sys/src/cmd/fplot.c
+--- a/sys/src/cmd/fplot.c	Sun Jul 01 23:35:23 2018 -0400
++++ b/sys/src/cmd/fplot.c	Thu Jul 05 04:41:48 2018 +0200
+@@ -61,6 +61,7 @@
+ void oexp(void) { *sp = exp(*sp); }
+ void olog(void) { *sp = log10(*sp); }
+ void oln(void) { *sp = log(*sp); }
++void oabs(void) { *sp = fabs(*sp); }
+ 
+ struct Operator {
+ 	char *s;
+@@ -85,6 +86,7 @@
+ 	"exp",	OUNARY,		0,	300,	oexp,
+ 	"log",	OUNARY,		0,	300,	olog,
+ 	"ln",	OUNARY,		0,	300,	oln,
++	"abs",	OUNARY,		0,	300,	oabs,
+ };
+ 
+ struct Constant {
--- /dev/null
+++ b/fplot-col
@@ -1,0 +1,117 @@
+diff -r 82ca4d784349 sys/src/cmd/fplot.c
+--- a/sys/src/cmd/fplot.c	Sun Jul 01 23:35:23 2018 -0400
++++ b/sys/src/cmd/fplot.c	Thu Jul 05 04:44:35 2018 +0200
+@@ -366,7 +366,9 @@
+ 		if(x >= picx || y >= picy || x < 0 || y < 0)
+ 			return;
+ 		p = imagedata + (picx * y + x) * 3;
+-		p[0] = p[1] = p[2] = 0;
++		p[0] = 0;
++		p[1] = 0x44;
++		p[2] = 0x88;
+ 	} else
+ 		draw(screen, Rect(x, y, x + 1, y + 1), color, nil, ZP);
+ }
+@@ -450,7 +452,7 @@
+ 		lr = rectsubpt(lr, Pt(0, lr.min.y - ny));
+ 	}
+ 	if(rectinrect(lr, screen->r) && (lr.min.x > x || lr.max.x <= x)){
+-		string(screen, lr.min, display->black, ZP, display->defaultfont, buf);
++		string(screen, lr.min, color, ZP, display->defaultfont, buf);
+ 		return 1;
+ 	}
+ 	return 0;
+@@ -472,7 +474,7 @@
+ 		lr = rectsubpt(lr, Pt(lr.min.x - nx, 0));
+ 	}
+ 	if(rectinrect(lr, screen->r) && (lr.min.y > y || lr.max.y <= y)){
+-		string(screen, lr.min, display->black, ZP, display->defaultfont, buf);
++		string(screen, lr.min, color, ZP, display->defaultfont, buf);
+ 		return 1;
+ 	}
+ 	return 0;
+@@ -539,25 +541,25 @@
+ 		x = deconvx(&screen->r, 0);
+ 	else
+ 		x = screen->r.min.x+5;
+-	line(screen, Pt(x, screen->r.min.y), Pt(x, screen->r.max.y), Endarrow, 0, 0, display->black, ZP);
++	line(screen, Pt(x, screen->r.min.y), Pt(x, screen->r.max.y), Endarrow, 0, 0, color, ZP);
+ 	if(ymin < 0 && ymax > 0)
+ 		y = deconvy(&screen->r, 0);
+ 	else
+ 		y = screen->r.max.y-5;
+-	line(screen, Pt(screen->r.min.x, y), Pt(screen->r.max.x, y), 0, Endarrow, 0, display->black, ZP);
++	line(screen, Pt(screen->r.min.x, y), Pt(screen->r.max.x, y), 0, Endarrow, 0, color, ZP);
+ 	nx = ticks(xmin, xmax, &dx, &mx);
+ 	tickfmt(dx, mx, nx, fmt);
+ 	for(i = 0; i <= nx; i++){
+ 		p = deconvx(&screen->r, dx*i+mx);
+ 		if(xticklabel(fmt, dx*i+mx, p, x, y))
+-			line(screen, Pt(p, y), Pt(p, y-5), 0, 0, 0, display->black, ZP);
++			line(screen, Pt(p, y), Pt(p, y-5), 0, 0, 0, color, ZP);
+ 	}
+ 	ny = ticks(ymin, ymax, &dy, &my);
+ 	tickfmt(dy, my, ny, fmt);
+ 	for(i = 0; i <= ny; i++){
+ 		p = deconvy(&screen->r, dy*i+my);
+ 		if(yticklabel(fmt, dy*i+my, p, x, y))
+-			line(screen, Pt(x, p), Pt(x+5, p), 0, 0, 0, display->black, ZP);
++			line(screen, Pt(x, p), Pt(x+5, p), 0, 0, 0, color, ZP);
+ 	}
+ }
+ 
+@@ -566,7 +568,6 @@
+ {
+ 	int i;
+ 	
+-	color = display->black;
+ 	for(i = 0; i < nfns; i++)
+ 		drawgraph(&fns[i], &screen->r);
+ 	if(!aflag)
+@@ -603,7 +604,7 @@
+ 	xmax = xmax_;
+ 	ymin = ymin_;
+ 	ymax = ymax_;
+-	draw(screen, screen->r, display->white, nil, ZP);
++	draw(screen, screen->r, display->black, nil, ZP);
+ 	drawgraphs();
+ }
+ 
+@@ -617,7 +618,7 @@
+ 	ymax = zoomst[nzoomst - 1].ymax;
+ 	zoomst = realloc(zoomst, sizeof(FRectangle) * --nzoomst);
+ 	if(zoomst == nil && nzoomst != 0) sysfatal("realloc: %r");
+-	draw(screen, screen->r, display->white, nil, ZP);
++	draw(screen, screen->r, display->black, nil, ZP);
+ 	drawgraphs();
+ }
+ 
+@@ -687,7 +688,6 @@
+ 	parsefns(argc, argv);
+ 	if(cflag) {
+ 		imagedata = emalloc(picx * picy * 3);
+-		memset(imagedata, 0xFF, picx * picy * 3);
+ 		print("%11s %11d %11d %11d %11d ", "r8g8b8", 0, 0, picx, picy);
+ 		r.min.x = r.min.y = 0;
+ 		r.max.x = picx;
+@@ -699,7 +699,11 @@
+ 	} else {
+ 		if(initdraw(nil, nil, "fplot") < 0)
+ 			sysfatal("initdraw: %r");
++		color = allocimage(display, Rect(0,0,1,1), RGB24, 1, 0x884400ff);
++		if(color == nil)
++			sysfatal("allocimage: %r");
+ 		einit(Emouse | Ekeyboard);
++		draw(screen, screen->r, display->black, nil, ZP);
+ 		drawgraphs();
+ 		for(;;) {
+ 			switch(event(&e)) {
+@@ -720,7 +724,7 @@
+ 						zoomst[nzoomst++] = (FRectangle){xmin, xmax, ymin, ymax};
+ 						ymin = gymin-0.05*(gymax-gymin);
+ 						ymax = gymax+0.05*(gymax-gymin);
+-						draw(screen, screen->r, display->white, nil, ZP);
++						draw(screen, screen->r, display->black, nil, ZP);
+ 						drawgraphs();
+ 					}
+ 					break;
--- /dev/null
+++ b/gb-nosleep
@@ -1,0 +1,56 @@
+diff -r e899bb299ed2 sys/src/games/gb/apu.c
+--- a/sys/src/games/gb/apu.c	Fri Feb 10 22:39:47 2017 +0100
++++ b/sys/src/games/gb/apu.c	Sat Feb 11 20:06:13 2017 +0200
+@@ -475,7 +475,7 @@
+ 	if(sbufp == sbuf)
+ 		return 0;
+ 	cl = clock;
+-	rc = write(fd, sbuf, (sbufp - sbuf) * 2);
++	rc = nosleep ? 1 : write(fd, sbuf, (sbufp - sbuf) * 2);
+ 	if(rc > 0)
+ 		sbufp -= (rc+1)/2;
+ 	if(sbufp < sbuf)
+diff -r e899bb299ed2 sys/src/games/gb/dat.h
+--- a/sys/src/games/gb/dat.h	Fri Feb 10 22:39:47 2017 +0100
++++ b/sys/src/games/gb/dat.h	Sat Feb 11 20:06:13 2017 +0200
+@@ -25,6 +25,7 @@
+ extern u8int mode;
+ extern u8int mbc, feat;
+ extern int keys, scale;
++extern int nosleep;
+ 
+ enum {
+ 	JOYP = 0x00,
+diff -r e899bb299ed2 sys/src/games/gb/gb.c
+--- a/sys/src/games/gb/gb.c	Fri Feb 10 22:39:47 2017 +0100
++++ b/sys/src/games/gb/gb.c	Sat Feb 11 20:06:13 2017 +0200
+@@ -12,7 +12,7 @@
+ Rectangle picr;
+ Image *bg, *tmp;
+ Mousectl *mc;
+-int keys, paused, framestep, backup;
++int keys, paused, framestep, backup, nosleep;
+ QLock pauselock;
+ int savefd = -1, saveframes;
+ ulong clock;
+@@ -285,6 +285,9 @@
+ 				}
+ 				framestep = !framestep;
+ 				break;
++			case ' ':
++				nosleep = !nosleep;
++				break;
+ 			}
+ 		}
+ 		k &= ~(k << 1 & 0x0a | k >> 1 & 0x05);
+@@ -352,7 +355,9 @@
+ 	flushimage(display, 1);
+ 	if(profile)
+ 		timing();
+-	if(audioout() < 0){
++	if(nosleep)
++		old = nsec();
++	if(audioout() < 0 && !nosleep){
+ 		new = nsec();
+ 		diff = 0;
+ 		if(old != 0){
--- /dev/null
+++ b/ircrc-jpmfix
@@ -1,0 +1,121 @@
+from _jpm: http://www.plan9.fi/up/ircrc
+fixes hanging connections at startup
+
+diff -r 94844cc7f1fd rc/bin/ircrc
+--- a/rc/bin/ircrc	Tue Mar 01 21:27:37 2016 -0500
++++ b/rc/bin/ircrc	Sat Mar 05 23:05:00 2016 +0200
+@@ -6,7 +6,7 @@
+ port=6667
+ realname='<nil>'
+ target=''
+-netdir=()
++pipe=/n/ircrc
+ nick=$user
+ pass=()
+ tls=0
+@@ -17,8 +17,7 @@
+ }
+ 
+ fn sigint sigterm {
+-	if (! ~ $#netdir 0)
+-		echo  QUIT : Leaving... > $netdir/data
++	echo  QUIT : Leaving... > $pipe/data
+ }
+ 
+ fn mshift {
+@@ -33,29 +32,39 @@
+ fn work {
+         if(~ $#serverpass 1)
+                 echo PASS $serverpass > $netdir/data
+-	echo USER $user foo bar :$realname > $netdir/data
+-	echo NICK $nick > $netdir/data
++	echo USER $user foo bar :$realname >[1=3]
++	echo NICK $nick >[1=3]
+ 	if (~ $#pass 1)
+-		echo PRIVMSG 'nickserv :'identify $"pass > $netdir/data
++		echo PRIVMSG 'nickserv :'identify $"pass >[1=3]
+ 	if(! ~ $target ''){
+ 		title
+-		echo JOIN $target > $netdir/data
++		echo JOIN $target >[1=3]
+ 	}
+ 	if(~ $target *,*)
+ 		target = `{echo $target | awk -F',' '{print $NF}'}
+ 	while (cmd=`{read}) {
+ 		s=$status
+ 		if(~ $s *eof) {
+-			echo  QUIT : Leaving... > $netdir/data
++			echo  QUIT : Leaving... >[1=3]
+ 			exit
+ 		}
+ 		msg=()
+ 		out=()
+ 		switch ($cmd(1)) {
+ 		case /!
+-			eval `{mshift $cmd} | while(rc=`{read})echo 'PRIVMSG '^$target^' :' $rc | tee $netdir/data
++			eval `{mshift $cmd} | while(rc=`{read}) echo 'PRIVMSG '^$target^' :' $rc | tee /fd/3
+ 		case /M
+-			msg = (MODE `{mshift $cmd})
++			switch($#cmd){
++			case 1
++				if(! ~ $target '')
++					msg = (TOPIC $target)
++			case 2
++				msg = (TOPIC $cmd(2))
++			case *
++				chan = $cmd(2)
++				topic = $cmd(3-)
++				msg = (TOPIC $chan :^$"topic)
++			}
+ 		case /N
+ 			cmd = `{mshift $cmd}
+ 			if (! ~ $#cmd 0)
+@@ -101,7 +110,7 @@
+ 		case /w
+ 			msg = (WHO `{mshift $cmd})
+ 		case /x
+-			echo  QUIT : Leaving... > $netdir/data
++			echo QUIT : Leaving... >[1=3]
+ 			exit
+ 		case /*
+ 			echo unknown command
+@@ -110,7 +119,7 @@
+ 			out = '('^$target^')	⇐	'^$"cmd
+ 		}
+ 		if (! ~ $#msg 0)
+-			echo $msg > $netdir/data
++			echo $msg >[1=3]
+ 		if (! ~ $#out 0)
+ 			echo `{etime}^' '^$out
+ 	}
+@@ -174,7 +183,7 @@
+ 		case *NOTICE*
+ 			line = `{echo -n $line | notice}
+ 		case *PING*
+-			echo -n $line | sed 's/PING/PONG/' > $netdir/data
++			echo -n $line | sed 's/PING/PONG/' >[1=3]
+ 			line = ()
+ 		case *
+ 			line = `{echo -n $line | numeric}
+@@ -238,16 +247,13 @@
+ 	pass=$userpass(2)
+ }
+ 
+-p='/n/ircrc'
+-bind '#|' $p
++bind '#|' $pipe
+ echo connecting to tcp!$server!$port...
+ if(~ $tls 0){
+-	aux/trampoline tcp!$server!$port <>$p/data1 >[1=0] &
++	aux/trampoline tcp!$server!$port <>$pipe/data1 >[1=0] &
+ }
+ if not {
+-	tlsclient tcp!$server!$port <>$p/data1 >[1=0] &
++	tlsclient tcp!$server!$port <>$pipe/data1 >[1=0] &
+ }
+-netdir=$p
+-cat $netdir/data | tr -d '\x2\x8\xd\x1f' | pretty &
+-work
+-
++<$pipe/data tr -d '\x2\x8\xd\x1f' | pretty >[3]$pipe/data &
++work >[3]$pipe/data
--- /dev/null
+++ b/ircrc-print-log
@@ -1,0 +1,63 @@
+tweak how this prints messages and append to a log.
+assumes jpm's patch applied beforehand.
+
+diff -r ef9b307e9439 rc/bin/ircrc
+--- a/rc/bin/ircrc	Sun Feb 26 00:27:50 2017
++++ b/rc/bin/ircrc	Sun Feb 26 00:28:19 2017
+@@ -26,7 +26,11 @@
+ }
+ 
+ fn etime {
+-	date | awk '{print $4}' | awk -F ':' '{print "[" $1 ":" $2 "]"}'
++	date -t | sed 's/-//g;s/^..(.*)T(..):(..).*/\1:\2\3/'
++}
++
++fn fmt2 {	# this fucking sucks
++	sed 's/^(......:....) (J?O?I?N? *)\(?(#?[^ \)]+)\)? «([^»]+)»/\1 \2\3:\4/'
+ }
+ 
+ fn work {
+@@ -90,7 +94,7 @@
+ 			if (! ~ $#cmd 0) {
+ 				to = $cmd(1)
+ 				cmd = `{mshift $cmd}
+-				out =  '('^$to^')	⇐	'^$"cmd
++				out = $to^' ⇐ '^$"cmd
+ 				msg = 'PRIVMSG '^$to^' :'^$"cmd
+ 			}
+ 		case /n
+@@ -116,19 +120,19 @@
+ 			echo unknown command
+ 		case *
+ 			msg = 'PRIVMSG '^$target^' :'^$"cmd
+-			out = '('^$target^')	⇐	'^$"cmd
++			out = $target^' ⇐ '^$"cmd
+ 		}
+ 		if (! ~ $#msg 0)
+ 			echo $msg >[1=3]
+ 		if (! ~ $#out 0)
+-			echo `{etime}^' '^$out
++			echo `{etime}^' '^$"out | tee -a $home/tmp/irc
+ 	}
+ }
+ 
+ fn misc {
+ 	sed '
+ 		s/^://
+-		s/!~?[a-zA-Z0-9_@\-|.{=\/:]+ /:/' |
++		s/!~?[a-zA-Z0-9_@\-|.{=\/:`\^]+ /:/' |
+ 	awk -F ':' '
+ 	{
+ 		s =  $2 " «" $1 "»\t" $3;
+@@ -189,7 +193,10 @@
+ 			line = `{echo -n $line | numeric}
+ 		}
+ 		if (! ~ $#line 0)
+-			echo `{etime} $line
++			echo `{etime} $line | fmt2
++		msg = `{echo $line | sed '/^(:|'$nick'|\([^\)]+\) «)/!d'}
++		if (! ~ $#msg 0)
++			echo `{etime} $msg | fmt2 >>$home/tmp/irc
+ 	}
+ 	exit
+ }
--- /dev/null
+++ b/kbdfs-fshalt
@@ -1,0 +1,37 @@
+diff -r 70f20d62c396 sys/src/cmd/aux/kbdfs/kbdfs.c
+--- a/sys/src/cmd/aux/kbdfs/kbdfs.c	Wed Jan 08 02:35:01 2020 +0000
++++ b/sys/src/cmd/aux/kbdfs/kbdfs.c	Wed Jan 08 06:23:13 2020 +0100
+@@ -306,6 +306,24 @@
+ }
+ 
+ void
++emergencywarp(void)
++{
++	int fd;
++
++	if(debug)
++		return;
++
++	if(access("#s/cwfs.cmd", AEXIST) == 0 && (fd = eopen("#s/cwfs.cmd", OWRITE)) >= 0){
++		fprint(fd, "halt\n");
++		close(fd);
++	}
++	if(access("#s/hjfs.cmd", AEXIST) == 0 && (fd = eopen("#s/hjfs.cmd", OWRITE)) >= 0){
++		fprint(fd, "halt\n");
++		close(fd);
++	}
++}
++
++void
+ reboot(void)
+ {
+ 	int fd;
+@@ -387,6 +405,8 @@
+ 	if(scan->caps && key.r<='z' && key.r>='a')
+ 		key.r += 'A' - 'a';
+ 
++	if(scan->ctl && scan->altgr && key.r == Kdel)
++		emergencywarp();
+ 	if(scan->ctl && scan->alt && key.r == Kdel)
+ 		reboot();
+ 
--- /dev/null
+++ b/kbmap-col
@@ -1,0 +1,58 @@
+diff -r 49bd5e4c9bde sys/src/cmd/kbmap.c
+--- a/sys/src/cmd/kbmap.c	Wed Feb 07 18:53:08 2018 +0000
++++ b/sys/src/cmd/kbmap.c	Wed Feb 14 15:12:34 2018 +0100
+@@ -14,8 +14,7 @@
+ 
+ KbMap *map;
+ int nmap;
+-Image *lightblue;
+-Image *justblue;
++Image *txt, *sel;
+ 
+ enum {
+ 	PAD = 3,
+@@ -86,14 +85,14 @@
+ drawmap(int i)
+ {
+ 	if(map[i].current)
+-		draw(screen, map[i].r, justblue, nil, ZP);
++		draw(screen, map[i].r, sel, nil, ZP);
+ 	else
+-		draw(screen, map[i].r, lightblue, nil, ZP);
++		draw(screen, map[i].r, display->black, nil, ZP);
+ 
+-	_string(screen, addpt(map[i].r.min, Pt(2,0)), display->black, ZP,
++	_string(screen, addpt(map[i].r.min, Pt(2,0)), txt, ZP,
+ 		font, map[i].name, nil, strlen(map[i].name), 
+ 		map[i].r, nil, ZP, SoverD);
+-	border(screen, map[i].r, 1, display->black, ZP);	
++	border(screen, map[i].r, 1, txt, ZP);	
+ }
+ 
+ void
+@@ -116,7 +115,7 @@
+ {
+ 	int i;
+ 
+-	draw(screen, screen->r, lightblue, nil, ZP);
++	draw(screen, screen->r, display->black, nil, ZP);
+ 	for(i=0; i<nmap; i++)
+ 		drawmap(i);
+ 	flushimage(display, 1);
+@@ -233,12 +232,10 @@
+ 		fprint(2, "kbmap: initdraw failed: %r\n");
+ 		exits("initdraw");
+ 	}
+-	lightblue = allocimagemix(display, DPalebluegreen, DWhite);
+-	if(lightblue == nil)
+-		sysfatal("allocimagemix: %r");
+-	justblue = allocimagemix(display, DBlue, DWhite);
+-	if(justblue == nil)
+-		sysfatal("allocimagemix: %r");
++	txt = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x884400FF);
++	sel = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x440000FF);
++	if(txt == nil || sel == nil)
++		sysfatal("allocimage: %r");
+ 
+ 	eresized(0);
+ 	einit(Emouse|Ekeyboard);
--- /dev/null
+++ b/libdraw-menucol
@@ -1,0 +1,72 @@
+mouse menu color patch
+
+diff -r d4af852fff2d sys/src/libdraw/emenuhit.c
+--- a/sys/src/libdraw/emenuhit.c	Sat Aug 08 09:20:48 2015 +0200
++++ b/sys/src/libdraw/emenuhit.c	Sat Aug 15 02:08:58 2015 +0300
+@@ -27,13 +27,14 @@
+ menucolors(void)
+ {
+ 	/* Main tone is greenish, with negative selection */
+-	back = allocimagemix(display, DPalegreen, DWhite);
+-	high = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DDarkgreen);	/* dark green */
+-	bord = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DMedgreen);	/* not as dark green */
+-	if(back==nil || high==nil || bord==nil)
++	text = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x884400FF);
++	high = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x884400FF);
++	bord = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x440000FF);
++	if(text==nil || high==nil || bord==nil)
+ 		goto Error;
+-	text = display->black;
++	back = display->black;
+ 	htext = back;
++	menutxt = high;
+ 	return;
+ 
+     Error:
+@@ -149,10 +150,7 @@
+ 	if(r.max.y < r.min.y+2)
+ 		r.max.y = r.min.y+2;
+ 	border(screen, r, 1, bord, ZP);
+-	if(menutxt == 0)
+-		menutxt = allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, DDarkgreen);
+-	if(menutxt)
+-		draw(screen, insetrect(r, 1), menutxt, nil, ZP);
++	draw(screen, insetrect(r, 1), menutxt, nil, ZP);
+ }
+ 
+ int
+diff -r d4af852fff2d sys/src/libdraw/menuhit.c
+--- a/sys/src/libdraw/menuhit.c	Sat Aug 08 09:20:48 2015 +0200
++++ b/sys/src/libdraw/menuhit.c	Sat Aug 15 02:08:58 2015 +0300
+@@ -28,13 +28,14 @@
+ menucolors(void)
+ {
+ 	/* Main tone is greenish, with negative selection */
+-	back = allocimagemix(display, DPalegreen, DWhite);
+-	high = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkgreen);	/* dark green */
+-	bord = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DMedgreen);	/* not as dark green */
+-	if(back==nil || high==nil || bord==nil)
++	text = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x884400FF);
++	high = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x884400FF);
++	bord = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x440000FF);
++	if(text==nil || high==nil || bord==nil)
+ 		goto Error;
+-	text = display->black;
++	back = display->black;
+ 	htext = back;
++	menutxt = high;
+ 	return;
+ 
+     Error:
+@@ -146,10 +147,7 @@
+ 	if(r.max.y < r.min.y+2)
+ 		r.max.y = r.min.y+2;
+ 	border(m, r, 1, bord, ZP);
+-	if(menutxt == 0)
+-		menutxt = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DDarkgreen);	/* border color; BUG? */
+-	if(menutxt)
+-		draw(m, insetrect(r, 1), menutxt, nil, ZP);
++	draw(m, insetrect(r, 1), menutxt, nil, ZP);
+ }
+ 
+ int
--- /dev/null
+++ b/man-joy
@@ -1,0 +1,39 @@
+document nusb/joy
+
+diff -r e540b0bb61c8 sys/man/4/nusb
+--- a/sys/man/4/nusb	Sat Oct 03 12:19:31 2015 +0200
++++ b/sys/man/4/nusb	Sun Oct 04 16:10:51 2015 +0200
+@@ -35,6 +35,12 @@
+ ]
+ .I devid
+ .PP
++.B nusb/joy
++[
++.B -d
++]
++.I devid
++.PP
+ .B nusb/serial
+ [
+ .B -d
+@@ -135,6 +141,10 @@
+ Mouse events are sent to
+ .BR /dev/mousein
+ in the same way.
++.SS Joysticks
++.I Joy
++parses data packets from a given endpoint and prints back
++any changes in the device's axes or buttons.
+ .SS Disks
+ .I Disk
+ configures and manages USB mass storage devices. It
+@@ -245,6 +255,9 @@
+ .IR shr (3),
+ .IR nusbrc (8),
+ .IR kbdfs (8)
++.SH HISTORY
++.I Joy
++first appeared in 9front (March, 2014).
+ .SH BUGS
+ The various device drivers are generic USB drivers and
+ may work only for certain devices of each class.
--- /dev/null
+++ b/midi-debug
@@ -1,0 +1,103 @@
+diff -r 02682c97b28d sys/src/games/midi.c
+--- a/sys/src/games/midi.c	Fri Sep 29 21:19:12 2017 +0200
++++ b/sys/src/games/midi.c	Sat Sep 30 14:21:21 2017 +0200
+@@ -13,11 +13,13 @@
+ 
+ typedef struct Tracker Tracker;
+ 
+-int fd, ofd, div, tempo = 500000, ntrack;
++int fd, ofd, div, tempo = 500000, ntrack, debug;
+ uvlong T;
+ int freq[128];
+ uchar out[8192], *outp = out;
+ 
++#define dprint	if(debug)fprint
++
+ void *
+ emallocz(int size)
+ {
+@@ -149,9 +151,10 @@
+ readevent(Tracker *src)
+ {
+ 	uvlong l;
+-	int n,t;
++	int c,t,n,v;
+ 	
+-	l = tconv(getvar(src));
++	c = getvar(src);
++	l = tconv(c);
+ 	run(src->t += l);
+ 	t = get8(src);
+ 	if((t & 0x80) == 0){
+@@ -164,35 +167,46 @@
+ 	switch(t >> 4){
+ 	case 0x8:
+ 		n = get8(src);
+-		get8(src);
++		v = get8(src);
++		dprint(2, "t%zd %d c%d off %d %d\n", src-tr, c, t&15, n, v);
+ 		src->notes[t & 15][n] = 0;
+ 		break;
+ 	case 0x9:
+ 		n = get8(src);
+-		src->notes[t & 15][n] = get8(src);
++		v = get8(src);
++		dprint(2, "t%zd %d c%d on %d %d\n", src-tr, c, t&15, n, v);
++		src->notes[t & 15][n] = v;
+ 		break;
+ 	case 0xB:
+-		get16(src);
++		n = get8(src);
++		v = get8(src);
++		dprint(2, "t%zd %d c%d ctl %d %d\n", src-tr, c, t&15, n, v);
+ 		break;
+ 	case 0xC:
+-		get8(src);
++		n = get8(src);
++		dprint(2, "t%zd %d c%d bank %d\n", src-tr, c, t&15, n);
+ 		break;
+ 	case 0xE:
+-		get16(src);
++		n = get8(src);
++		n = get8(src) << 7 | n & 0x7f;
++		dprint(2, "t%zd %d c%d bend %d\n", src-tr, c, t&15, n);
+ 		break;
+ 	case 0xF:
+ 		t = get8(src);
+ 		n = get8(src);
+ 		switch(t){
+ 		case 0x2F:
++			dprint(2, "t%zd %d eot\n", src-tr, c);
+ 			src->ended = 1;
+ 			break;
+ 		case 0x51:
+ 			tempo = get16(src) << 8;
+ 			tempo |= get8(src);
++			dprint(2, "t%zd %d tempo %d\n", src-tr, c, tempo);
+ 			break;
+ 		case 5:
+-			write(2, src->data, n);
++			if(!debug)
++				write(2, src->data, n);
+ 			skip(src, n);
+ 			break;
+ 		default:
+@@ -217,6 +231,9 @@
+ 	case 'c':
+ 		ofd = 1;
+ 		break;
++	case 'v':
++		debug = 1;
++		break;
+ 	}ARGEND;
+ 	if(*argv != nil)
+ 		fd = open(*argv, OREAD);
+@@ -229,6 +246,8 @@
+ 	get16(nil);
+ 	ntrack = get16(nil);
+ 	div = get16(nil);
++	if(debug)
++		fprint(2, "div %d\n", div);
+ 	tr = emallocz(ntrack * sizeof(*tr));
+ 	for(i = 0; i < ntrack; i++){
+ 		if(get32(nil) != 0x4D54726B)
--- /dev/null
+++ b/midi-dobend
@@ -1,0 +1,217 @@
+midi: implement pitch wheel
+
+this is an aborted attempt to implement pitch bending (event 0xe0).
+
+this maintains a list of active notes, storing note number, amplitude and bent
+frequency; this greatly reduces cpu time spent in run(), also alleviating the
+issue of delays between writes to /dev/audio being too great...
+
+it blows chunks:
+- greatly complexifies midi note handling
+- pitch steps are discrete and too noticeable, actually worsening playback
+  (blatant example: doom e2m2), but don't yet know where T % f >= f/2 is coming
+  from or how to fix this
+
+→ i don't yet know what i'm doing nor how to solve this correctly
+
+diff -r 9c4b9c67ff16 sys/src/games/midi.c
+--- a/sys/src/games/midi.c	Thu Feb 16 20:11:20 2017 +0100
++++ b/sys/src/games/midi.c	Wed Feb 22 01:34:39 2017 +0200
+@@ -3,19 +3,29 @@
+ 
+ enum { SAMPLE = 44100 };
+ 
++typedef struct Note Note;
++typedef struct Tracker Tracker;
++
++struct Note {
++	int nt;
++	Tracker *src;
++	int ch;
++	uchar a;
++	int f;
++	int ½f;
++	Note *n;
++} nl[256], np;
++
+ struct Tracker {
+ 	uchar *data;
+ 	char ended;
+ 	uvlong t;
+-	uchar notes[16][128];
++	double bend[16];
+ 	int cmd;
+ } *tr;
+ 
+-typedef struct Tracker Tracker;
+-
+ int fd, ofd, div, tempo = 500000, ntrack;
+ uvlong T;
+-int freq[128];
+ 
+ void *
+ emallocz(int size)
+@@ -108,32 +118,23 @@
+ }
+ 
+ void
+-run(uvlong n)
++run(uvlong dt)
+ {
+-	int samp, j, k, l, no[128];
++	int samp, l;
+ 	uchar *s;
+-	int t, f;
++	int t;
+ 	short u;
+-	Tracker *x;
+-	
+-	samp = n - T;
++	Note *n;
++
++	samp = dt - T;
+ 	if(samp <= 0)
+ 		return;
+-	memset(no, 0, sizeof no);
+-	for(x = tr; x < tr + ntrack; x++){
+-		if(x->ended)
+-			continue;
+-		for(j = 0; j < 16; j++)
+-			for(k = 0; k < 128; k++)
+-				no[k] += x->notes[j][k];
+-	}
+ 	s = emallocz(samp * 4);
+ 	for(l = 0; l < samp; l++){
+ 		t = 0;
+-		for(k = 0; k < 128; k++){
+-			f = (T % freq[k]) >= freq[k]/2 ? 1 : 0;
+-			t += f * no[k];
+-		}
++		for(n = np.n; n != &np; n = n->n)
++			if(T % n->f >= n->½f)
++				t += n->a;
+ 		u = t*10;
+ 		s[4 * l] = s[4 * l + 2] = u;
+ 		s[4 * l + 1] = s[4 * l + 3] = u >> 8;
+@@ -144,11 +145,70 @@
+ }
+ 
+ void
++bend(Tracker *src, int ch, int b)
++{
++	int f;
++	double d;
++	Note *n;
++
++	d = (b - 8192) / 4095.5;
++	d = pow(2, d / 12);
++	src->bend[ch] = d;
++	for(n = np.n; n != &np; n = n->n){
++		if(n->src != src || n->ch != ch)
++			continue;
++		f = SAMPLE / (440 * d * pow(1.05946, n->nt - 69));
++		n->f = f;
++		n->½f = f / 2;
++	}
++}
++
++void
++pop(Tracker *src, int ch, int nt)
++{
++	Note *n, *p;
++
++	for(p = &np, n = np.n; n != &np; p = n, n = n->n)
++		if(n->src == src && n->ch == ch && n->nt == nt){
++			p->n = n->n;
++			memset(n, 0, sizeof *n);
++			return;
++		}
++}
++
++void
++push(Tracker *src, int ch, int nt, int a)
++{
++	int f;
++	Note *n, *p;
++
++	for(p = &np, n = np.n; n != &np; p = n, n = n->n)
++		if(n->src == src && n->ch == ch && n->nt == nt){
++			n->a = a;
++			return;
++		}
++	for(n = nl; n < nl + nelem(nl); n++)
++		if(n->src == nil)
++			break;
++	if(n == nl + nelem(nl))
++		return;
++	n->src = src;
++	n->ch = ch;
++	n->nt = nt;
++	n->a = a;
++	f = SAMPLE / (440 * src->bend[ch] * pow(1.05946, nt - 69));
++	n->f = f;
++	n->½f = f / 2;
++	p->n = n;
++	n->n = &np;
++}
++
++void
+ readevent(Tracker *src)
+ {
+ 	uvlong l;
+ 	int n,t;
+-	
++
+ 	l = tconv(getvar(src));
+ 	run(src->t += l);
+ 	t = get8(src);
+@@ -161,13 +221,12 @@
+ 		src->cmd = t;
+ 	switch(t >> 4){
+ 	case 0x8:
+-		n = get8(src);
++		pop(src, t & 15, get8(src));
+ 		get8(src);
+-		src->notes[t & 15][n] = 0;
+ 		break;
+ 	case 0x9:
+ 		n = get8(src);
+-		src->notes[t & 15][n] = get8(src);
++		push(src, t & 15, n, get8(src));
+ 		break;
+ 	case 0xB:
+ 		get16(src);
+@@ -175,6 +234,11 @@
+ 	case 0xC:
+ 		get8(src);
+ 		break;
++	case 0xE:
++		n = get8(src) & 0x7f;
++		n = get8(src) << 7 & 0x3f80 | n;
++		bend(src, t & 15, n);
++		break;
+ 	case 0xF:
+ 		t = get8(src);
+ 		n = get8(src);
+@@ -204,7 +268,7 @@
+ void
+ main(int argc, char **argv)
+ {
+-	int i, size;
++	int i, j, size;
+ 	uvlong t, mint;
+ 	Tracker *x, *minx;
+ 
+@@ -228,9 +292,10 @@
+ 		size = get32(nil);
+ 		tr[i].data = emallocz(size);
+ 		read(fd, tr[i].data, size);
++		for(j = 0; j < 16; j++)
++			tr[i].bend[j] = 1.0;
+ 	}
+-	for(i = 0; i < 128; i++)
+-		freq[i] = SAMPLE / (440 * pow(1.05946, i - 69));
++	np.n = &np;
+ 	for(;;){
+ 		minx = nil;
+ 		mint = 0;
--- /dev/null
+++ b/mothra-slmod
@@ -1,0 +1,341 @@
+modified diff of http://9front.org/extra/mothra.black.tgz
+
+diff -r 3f9227bd4f1d sys/src/cmd/mothra/libpanel/draw.c
+--- a/sys/src/cmd/mothra/libpanel/draw.c	Mon Feb 01 22:49:20 2016 +0100
++++ b/sys/src/cmd/mothra/libpanel/draw.c	Tue Feb 02 14:03:58 2016 +0100
+@@ -15,14 +15,16 @@
+ #define	CKBORDER 2	/* space around X inside frame */
+ static int plldepth;
+ static Image *pl_white, *pl_light, *pl_dark, *pl_black, *pl_hilit;
++Image *txtcol;
+ int pl_drawinit(int ldepth){
+ 	plldepth=ldepth;
+-	pl_white=allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xFFFFFFFF);
+-	pl_light=allocimagemix(display, DPalebluegreen, DWhite);
+-	pl_dark =allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue);
+-	pl_black=allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x000000FF);
++	pl_white=display->black;
++	pl_light=display->black;
++	pl_dark=allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x222222FF);
++	pl_black=allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x770000FF);
+ 	pl_hilit=allocimage(display, Rect(0,0,1,1), CHAN1(CAlpha,8), 1, 0x80);
+-	if(pl_white==0 || pl_light==0 || pl_black==0 || pl_dark==0) return 0;
++	txtcol=allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x884400FF);
++	if(pl_white==0 || pl_light==0 || pl_black==0 || pl_dark==0 || txtcol==0) return 0;
+ 	return 1;
+ }
+ void pl_relief(Image *b, Image *ul, Image *lr, Rectangle r, int wid){
+@@ -38,6 +40,13 @@
+ }
+ Rectangle pl_boxoutline(Image *b, Rectangle r, int style, int fill){
+ 	if(plldepth==0) switch(style){
++	case SUP:
++	case TUP:
++		pl_relief(b, pl_white, pl_white, r, BWID);
++		r=insetrect(r, BWID);
++		if(fill) draw(b, r, pl_white, 0, ZP);
++		else border(b, r, SPACE, pl_white, ZP);
++		break;
+ 	case UP:
+ 		pl_relief(b, pl_black, pl_black, r, BWID);
+ 		r=insetrect(r, BWID);
+@@ -68,6 +77,13 @@
+ 		break;
+ 	}
+ 	else switch(style){
++	case SUP:
++	case TUP:
++		pl_relief(b, pl_white, pl_white, r, BWID);
++		r=insetrect(r, BWID);
++		if(fill) draw(b, r, pl_light, 0, ZP);
++		else border(b, r, SPACE, pl_white, ZP);
++		break;
+ 	case UP:
+ 		pl_relief(b, pl_white, pl_black, r, BWID);
+ 		r=insetrect(r, BWID);
+@@ -97,7 +113,10 @@
+ 		else border(b, r, SPACE, pl_white, ZP);
+ 		break;
+ 	}
+-	return insetrect(r, SPACE);
++	switch(style){
++	case SUP: return insetrect(r, SPACE-SPACE);
++	default: return insetrect(r, SPACE);
++	}
+ }
+ Rectangle pl_outline(Image *b, Rectangle r, int style){
+ 	return pl_boxoutline(b, r, style, 0);
+@@ -270,7 +289,7 @@
+ 	draw(b, r, pl_dark, pl_hilit, ZP);
+ }
+ void pl_clr(Image *b, Rectangle r){
+-	draw(b, r, display->white, 0, ZP);
++	draw(b, r, display->black, 0, ZP);
+ }
+ void pl_fill(Image *b, Rectangle r){
+ 	draw(b, r, plldepth==0? pl_white : pl_light, 0, ZP);
+diff -r 3f9227bd4f1d sys/src/cmd/mothra/libpanel/message.c
+--- a/sys/src/cmd/mothra/libpanel/message.c	Mon Feb 01 22:49:20 2016 +0100
++++ b/sys/src/cmd/mothra/libpanel/message.c	Tue Feb 02 14:03:58 2016 +0100
+@@ -29,7 +29,7 @@
+ 			end=s;
+ 		c=*end;
+ 		*end='\0';
+-		string(b, where, display->black, ZP, f, start);
++		string(b, where, txtcol, ZP, f, start);
+ 		*end=c;
+ 		where.y+=font->height;
+ 		s=end;
+diff -r 3f9227bd4f1d sys/src/cmd/mothra/libpanel/panel.h
+--- a/sys/src/cmd/mothra/libpanel/panel.h	Mon Feb 01 22:49:20 2016 +0100
++++ b/sys/src/cmd/mothra/libpanel/panel.h	Tue Feb 02 14:03:58 2016 +0100
+@@ -108,6 +108,7 @@
+ #define PL_STR		4
+ 
+ Panel *plkbfocus;			/* the panel in keyboard focus */
++extern Image *txtcol;
+ 
+ int plinit(int);			/* initialization */
+ void plpack(Panel *, Rectangle);	/* figure out where to put the Panel & children */
+diff -r 3f9227bd4f1d sys/src/cmd/mothra/libpanel/pldefs.h
+--- a/sys/src/cmd/mothra/libpanel/pldefs.h	Mon Feb 01 22:49:20 2016 +0100
++++ b/sys/src/cmd/mothra/libpanel/pldefs.h	Tue Feb 02 14:03:58 2016 +0100
+@@ -17,7 +17,9 @@
+  * States, also styles
+  */
+ enum{
+-	UP,
++	SUP,	// scrollbar
++	TUP,	// textview
++	UP,	// deprecated
+ 	DOWN1,
+ 	DOWN2,
+ 	DOWN3,
+diff -r 3f9227bd4f1d sys/src/cmd/mothra/libpanel/rtext.c
+--- a/sys/src/cmd/mothra/libpanel/rtext.c	Mon Feb 01 22:49:20 2016 +0100
++++ b/sys/src/cmd/mothra/libpanel/rtext.c	Tue Feb 02 14:03:58 2016 +0100
+@@ -11,7 +11,7 @@
+ #include "rtext.h"
+ 
+ #define LEAD	4	/* extra space between lines */
+-#define BORD	2	/* extra border for images */
++#define BORD	0	/* extra border for images */
+ 
+ Rtext *pl_rtnew(Rtext **t, int space, int indent, int voff, Image *b, Panel *p, Font *f, char *s, int flags, void *user){
+ 	Rtext *new;
+@@ -195,14 +195,14 @@
+ 		&& dr.min.x<r.max.x){
+ 			if(t->b){
+ 				draw(b, insetrect(dr, BORD), t->b, 0, t->b->r.min);
+-				if(t->flags&PL_HOT) border(b, dr, 1, display->black, ZP);
++				if(t->flags&PL_HOT) border(b, dr, 1, txtcol, ZP);
+ 				if(t->flags&PL_STR) {
+ 					line(b, Pt(dr.min.x, dr.min.y), Pt(dr.max.x, dr.max.y),
+ 						Endsquare, Endsquare, 0,
+-						display->black, ZP);
++						txtcol, ZP);
+ 					line(b, Pt(dr.min.x, dr.max.y), Pt(dr.max.x, dr.min.y),
+ 						Endsquare, Endsquare, 0,
+-						display->black, ZP);
++						txtcol, ZP);
+ 				}
+ 				if(t->flags&PL_SEL)
+ 					pl_highlight(b, dr);
+@@ -214,7 +214,7 @@
+ 					pl_stuffbitmap(t->p, bb);
+ 			}
+ 			else{
+-				string(b, dr.min, display->black, ZP, t->font, t->text);
++				string(b, dr.min, txtcol, ZP, t->font, t->text);
+ 				if(t->flags&PL_SEL)
+ 					pl_highlight(b, dr);
+ 				if(t->flags&PL_STR){
+@@ -223,7 +223,7 @@
+ 						sp = Pt(dr.min.x, y);
+ 					line(b, sp, Pt(dr.max.x, y),
+ 						Endsquare, Endsquare, 0,
+-						display->black, ZP);
++						txtcol, ZP);
+ 					sp = Pt(dr.max.x, y);
+ 				} else
+ 					sp = ZP;
+@@ -233,7 +233,7 @@
+ 						lp = Pt(dr.min.x, y);
+ 					line(b, lp, Pt(dr.max.x, y),
+ 						Endsquare, Endsquare, 0,
+-						display->black, ZP);
++						txtcol, ZP);
+ 					lp = Pt(dr.max.x, y);
+ 				} else
+ 					lp = ZP;
+diff -r 3f9227bd4f1d sys/src/cmd/mothra/libpanel/scrollbar.c
+--- a/sys/src/cmd/mothra/libpanel/scrollbar.c	Mon Feb 01 22:49:20 2016 +0100
++++ b/sys/src/cmd/mothra/libpanel/scrollbar.c	Tue Feb 02 14:03:58 2016 +0100
+@@ -16,7 +16,7 @@
+ void pl_drawscrollbar(Panel *p){
+ 	Scrollbar *sp;
+ 	sp=p->data;
+-	sp->interior=pl_outline(p->b, p->r, p->state);
++	sp->interior=pl_outline(p->b, p->r, SUP); /* SUP was p->state */
+ 	pl_sliderupd(p->b, sp->interior, sp->dir, sp->lo, sp->hi);
+ }
+ int pl_hitscrollbar(Panel *g, Mouse *m){
+diff -r 3f9227bd4f1d sys/src/cmd/mothra/libpanel/textview.c
+--- a/sys/src/cmd/mothra/libpanel/textview.c	Mon Feb 01 22:49:20 2016 +0100
++++ b/sys/src/cmd/mothra/libpanel/textview.c	Tue Feb 02 14:03:58 2016 +0100
+@@ -47,7 +47,7 @@
+ 	Point size;
+ 
+ 	tp=p->data;
+-	r=pl_outline(p->b, p->r, UP);
++	r=pl_outline(p->b, p->r, TUP);
+ 	twid=r.max.x-r.min.x;
+ 	if(twid!=tp->twid){
+ 		tp->twid=twid;
+@@ -80,7 +80,7 @@
+ 	if(oldhitword==oldhitfirst)
+ 		pl_passon(oldhitword, m);
+ 	if(m->buttons&OUT)
+-		p->state=UP;
++		p->state=PASSIVE;
+ 	else if(m->buttons&7){
+ 		p->state=DOWN;
+ 		tp->buttons=m->buttons;
+@@ -100,7 +100,7 @@
+ 	}
+ 	else{
+ 		if(p->state==DOWN) hitme=1;
+-		p->state=UP;
++		p->state=PASSIVE;
+ 	}
+ 	if(tp->hitfirst!=oldhitfirst || tp->hitword!=oldhitword){
+ 		plrtseltext(tp->text, tp->hitword, tp->hitfirst);
+@@ -214,7 +214,7 @@
+ 	Textview *tp;
+ 	tp=v->data;
+ 	v->flags=flags|LEAF;
+-	v->state=UP;
++	v->state=PASSIVE;
+ 	v->draw=pl_drawtextview;
+ 	v->hit=pl_hittextview;
+ 	v->type=pl_typetextview;
+diff -r 3f9227bd4f1d sys/src/cmd/mothra/libpanel/textwin.c
+--- a/sys/src/cmd/mothra/libpanel/textwin.c	Mon Feb 01 22:49:20 2016 +0100
++++ b/sys/src/cmd/mothra/libpanel/textwin.c	Tue Feb 02 14:03:58 2016 +0100
+@@ -136,7 +136,7 @@
+ 	er=t->text+last;
+ 	for(r=t->text+first,lp=t->loc+(first-t->top);r!=er;r++,lp++){
+ 		if(lp->y+t->hgt>t->r.max.y){
+-			fprint(2, "chr %C, index %ld of %d, loc %d %d, off bottom\n",
++			fprint(2, "chr %C, index %zd of %d, loc %d %d, off bottom\n",
+ 				*r, lp-t->loc, t->bot-t->top, lp->x, lp->y);
+ 			return;
+ 		}
+@@ -155,7 +155,7 @@
+ 		default:
+ 			buf[runetochar(buf, r)]='\0';
+ 	/***/		pl_clr(t->b, Rpt(*lp, addpt(*lp, stringsize(t->font, buf))));
+-			ur=string(t->b, *lp, display->black, ZP, t->font, buf);
++			ur=string(t->b, *lp, txtcol, ZP, t->font, buf);
+ 			break;
+ 		}
+ 		if(lp[1].y!=lp[0].y)
+diff -r 3f9227bd4f1d sys/src/cmd/mothra/mothra.c
+--- a/sys/src/cmd/mothra/mothra.c	Mon Feb 01 22:49:20 2016 +0100
++++ b/sys/src/cmd/mothra/mothra.c	Tue Feb 02 14:03:58 2016 +0100
+@@ -38,17 +38,17 @@
+ 	0x10, 0x08, 0x14, 0x08, 0x14, 0x28, 0x12, 0x28,
+ 	0x0A, 0x50, 0x16, 0x68, 0x20, 0x04, 0x3F, 0xFC,
+ };
+-Cursor confirmcursor={
++Cursor confirmcurs={
+ 	0, 0,
+-	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+-	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+-	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+-	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
++	0x0F, 0xBF, 0x0F, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF,
++	0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE,
++	0xFF, 0xFE, 0xFF, 0xFF, 0x00, 0x03, 0xFF, 0xFF,
++	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC,
+ 
+ 	0x00, 0x0E, 0x07, 0x1F, 0x03, 0x17, 0x73, 0x6F,
+ 	0xFB, 0xCE, 0xDB, 0x8C, 0xDB, 0xC0, 0xFB, 0x6C,
+ 	0x77, 0xFC, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03,
+-	0x94, 0xA6, 0x63, 0x3C, 0x63, 0x18, 0x94, 0x90,
++	0x94, 0xA6, 0x63, 0x3C, 0x63, 0x18, 0x94, 0x90
+ };
+ Cursor readingcurs={
+ 	-10, -3,
+@@ -349,15 +349,15 @@
+ 	plinit(screen->depth);
+ 	if(debug) notify(dienow);
+ 	getfonts();
+-	hrule=allocimage(display, Rect(0, 0, 1, 5), screen->chan, 1, DWhite);
++	hrule=allocimage(display, Rect(0, 0, 2048, 5), screen->chan, 1, DBlack);
+ 	if(hrule==0)
+ 		sysfatal("can't allocimage!");
+-	draw(hrule, Rect(0,1,1,3), display->black, 0, ZP);
+-	linespace=allocimage(display, Rect(0, 0, 1, 5), screen->chan, 1, DWhite);
++	draw(hrule, Rect(0,1,1280,3), txtcol, 0, ZP);
++	linespace=display->black;
+ 	if(linespace==0)
+ 		sysfatal("can't allocimage!");
+-	bullet=allocimage(display, Rect(0,0,25, 8), screen->chan, 0, DWhite);
+-	fillellipse(bullet, Pt(4,4), 3, 3, display->black, ZP);
++	bullet=allocimage(display, Rect(0,0,25, 8), screen->chan, 0, DBlack);
++	fillellipse(bullet, Pt(4,4), 3, 3, txtcol, ZP);
+ 	mkpanels();
+ 	unlockdisplay(display);
+ 	eresized(0);
+@@ -422,11 +422,17 @@
+ 				search();
+ 				break;
+ 			case Kright:
+-				sidescroll(text->size.x/4, 1);
++				scrolltext(text->size.y/24, 1); /* down one line */
+ 				break;
+ 			case Kleft:
++				scrolltext(-text->size.y/24, 1); /* up one line */
++				break;
++			case Kins:
+ 				sidescroll(-text->size.x/4, 1);
+ 				break;
++			case Kdel:
++				sidescroll(text->size.x/4, 1);
++				break;
+ 			}
+ 			break;
+ 		case Emouse:
+@@ -449,6 +455,18 @@
+ 		}
+ 	}
+ }
++Cursor confirmcursor={
++	0, 0,
++	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
++	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
++	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
++	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
++
++	0x00, 0x0E, 0x07, 0x1F, 0x03, 0x17, 0x73, 0x6F,
++	0xFB, 0xCE, 0xDB, 0x8C, 0xDB, 0xC0, 0xFB, 0x6C,
++	0x77, 0xFC, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03,
++	0x94, 0xA6, 0x63, 0x3C, 0x63, 0x18, 0x94, 0x90,
++};
+ int confirm(int b){
+ 	Mouse down, up;
+ 	esetcursor(&confirmcursor);
+diff -r 3f9227bd4f1d sys/src/cmd/mothra/mothra.h
+--- a/sys/src/cmd/mothra/mothra.h	Mon Feb 01 22:49:20 2016 +0100
++++ b/sys/src/cmd/mothra/mothra.h	Tue Feb 02 14:03:58 2016 +0100
+@@ -1,7 +1,7 @@
+ enum{
+ 	NWWW=64,	/* # of pages we hold in the log */
+-	NXPROC=5,	/* # of parallel procs loading the pix */
+-	NPIXMB=8,	/* megabytes of image data to keep arround */
++	NXPROC=16,	/* # of parallel procs loading the pix */
++	NPIXMB=48,	/* megabytes of image data to keep arround */
+ 	NNAME=512,
+ 	NLINE=256,
+ 	NAUTH=128,
--- /dev/null
+++ b/mothra-unifont
@@ -1,0 +1,384 @@
+diff -r b619d1fc23f6 sys/src/cmd/mothra/libpanel/draw.c
+--- a/sys/src/cmd/mothra/libpanel/draw.c	Mon Feb 11 23:43:14 2019 +0100
++++ b/sys/src/cmd/mothra/libpanel/draw.c	Wed Feb 13 15:03:32 2019 +0100
+@@ -15,14 +15,16 @@
+ #define	CKBORDER 2	/* space around X inside frame */
+ static int plldepth;
+ static Image *pl_white, *pl_light, *pl_dark, *pl_black, *pl_hilit;
++Image *txtcol;
+ int pl_drawinit(int ldepth){
+ 	plldepth=ldepth;
+-	pl_white=allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xFFFFFFFF);
+-	pl_light=allocimagemix(display, DPalebluegreen, DWhite);
+-	pl_dark =allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue);
+-	pl_black=allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x000000FF);
++	pl_white=display->black;
++	pl_light=display->black;
++	pl_dark=allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x222222FF);
++	pl_black=allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x770000FF);
+ 	pl_hilit=allocimage(display, Rect(0,0,1,1), CHAN1(CAlpha,8), 1, 0x80);
+-	if(pl_white==0 || pl_light==0 || pl_black==0 || pl_dark==0) return 0;
++	txtcol=allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x884400FF);
++	if(pl_white==0 || pl_light==0 || pl_black==0 || pl_dark==0 || txtcol==0) return 0;
+ 	return 1;
+ }
+ void pl_relief(Image *b, Image *ul, Image *lr, Rectangle r, int wid){
+@@ -38,6 +40,13 @@
+ }
+ Rectangle pl_boxoutline(Image *b, Rectangle r, int style, int fill){
+ 	if(plldepth==0) switch(style){
++	case SUP:
++	case TUP:
++		pl_relief(b, pl_white, pl_white, r, BWID);
++		r=insetrect(r, BWID);
++		if(fill) draw(b, r, pl_white, 0, ZP);
++		else border(b, r, SPACE, pl_white, ZP);
++		break;
+ 	case UP:
+ 		pl_relief(b, pl_black, pl_black, r, BWID);
+ 		r=insetrect(r, BWID);
+@@ -68,6 +77,13 @@
+ 		break;
+ 	}
+ 	else switch(style){
++	case SUP:
++	case TUP:
++		pl_relief(b, pl_white, pl_white, r, BWID);
++		r=insetrect(r, BWID);
++		if(fill) draw(b, r, pl_light, 0, ZP);
++		else border(b, r, SPACE, pl_white, ZP);
++		break;
+ 	case UP:
+ 		pl_relief(b, pl_white, pl_black, r, BWID);
+ 		r=insetrect(r, BWID);
+@@ -97,7 +113,10 @@
+ 		else border(b, r, SPACE, pl_white, ZP);
+ 		break;
+ 	}
+-	return insetrect(r, SPACE);
++	switch(style){
++	case SUP: return insetrect(r, SPACE-SPACE);
++	default: return insetrect(r, SPACE);
++	}
+ }
+ Rectangle pl_outline(Image *b, Rectangle r, int style){
+ 	return pl_boxoutline(b, r, style, 0);
+@@ -269,7 +288,7 @@
+ 	draw(b, r, pl_dark, pl_hilit, ZP);
+ }
+ void pl_clr(Image *b, Rectangle r){
+-	draw(b, r, display->white, 0, ZP);
++	draw(b, r, display->black, 0, ZP);
+ }
+ void pl_fill(Image *b, Rectangle r){
+ 	draw(b, r, plldepth==0? pl_white : pl_light, 0, ZP);
+diff -r b619d1fc23f6 sys/src/cmd/mothra/libpanel/message.c
+--- a/sys/src/cmd/mothra/libpanel/message.c	Mon Feb 11 23:43:14 2019 +0100
++++ b/sys/src/cmd/mothra/libpanel/message.c	Wed Feb 13 15:03:32 2019 +0100
+@@ -29,7 +29,7 @@
+ 			end=s;
+ 		c=*end;
+ 		*end='\0';
+-		string(b, where, display->black, ZP, f, start);
++		string(b, where, txtcol, ZP, f, start);
+ 		*end=c;
+ 		where.y+=font->height;
+ 		s=end;
+diff -r b619d1fc23f6 sys/src/cmd/mothra/libpanel/panel.h
+--- a/sys/src/cmd/mothra/libpanel/panel.h	Mon Feb 11 23:43:14 2019 +0100
++++ b/sys/src/cmd/mothra/libpanel/panel.h	Wed Feb 13 15:03:32 2019 +0100
+@@ -108,6 +108,7 @@
+ #define PL_STR		4
+ 
+ Panel *plkbfocus;			/* the panel in keyboard focus */
++extern Image *txtcol;
+ 
+ int plinit(int);			/* initialization */
+ void plpack(Panel *, Rectangle);	/* figure out where to put the Panel & children */
+diff -r b619d1fc23f6 sys/src/cmd/mothra/libpanel/pldefs.h
+--- a/sys/src/cmd/mothra/libpanel/pldefs.h	Mon Feb 11 23:43:14 2019 +0100
++++ b/sys/src/cmd/mothra/libpanel/pldefs.h	Wed Feb 13 15:03:32 2019 +0100
+@@ -17,7 +17,9 @@
+  * States, also styles
+  */
+ enum{
+-	UP,
++	SUP,	// scrollbar
++	TUP,	// textview
++	UP,	// deprecated
+ 	DOWN1,
+ 	DOWN2,
+ 	DOWN3,
+diff -r b619d1fc23f6 sys/src/cmd/mothra/libpanel/rtext.c
+--- a/sys/src/cmd/mothra/libpanel/rtext.c	Mon Feb 11 23:43:14 2019 +0100
++++ b/sys/src/cmd/mothra/libpanel/rtext.c	Wed Feb 13 15:03:32 2019 +0100
+@@ -11,7 +11,7 @@
+ #include "rtext.h"
+ 
+ #define LEAD	4	/* extra space between lines */
+-#define BORD	2	/* extra border for images */
++#define BORD	0	/* extra border for images */
+ 
+ Rtext *pl_rtnew(Rtext **t, int space, int indent, int voff, Image *b, Panel *p, Font *f, char *s, int flags, void *user){
+ 	Rtext *new;
+@@ -195,14 +195,14 @@
+ 		&& dr.min.x<r.max.x){
+ 			if(t->b){
+ 				draw(b, insetrect(dr, BORD), t->b, 0, t->b->r.min);
+-				if(t->flags&PL_HOT) border(b, dr, 1, display->black, ZP);
++				if(t->flags&PL_HOT) border(b, dr, 1, txtcol, ZP);
+ 				if(t->flags&PL_STR) {
+ 					line(b, Pt(dr.min.x, dr.min.y), Pt(dr.max.x, dr.max.y),
+ 						Endsquare, Endsquare, 0,
+-						display->black, ZP);
++						txtcol, ZP);
+ 					line(b, Pt(dr.min.x, dr.max.y), Pt(dr.max.x, dr.min.y),
+ 						Endsquare, Endsquare, 0,
+-						display->black, ZP);
++						txtcol, ZP);
+ 				}
+ 				if(t->flags&PL_SEL)
+ 					pl_highlight(b, dr);
+@@ -214,7 +214,7 @@
+ 					pl_stuffbitmap(t->p, bb);
+ 			}
+ 			else{
+-				string(b, dr.min, display->black, ZP, t->font, t->text);
++				string(b, dr.min, txtcol, ZP, t->font, t->text);
+ 				if(t->flags&PL_SEL)
+ 					pl_highlight(b, dr);
+ 				if(t->flags&PL_STR){
+@@ -223,7 +223,7 @@
+ 						sp = Pt(dr.min.x, y);
+ 					line(b, sp, Pt(dr.max.x, y),
+ 						Endsquare, Endsquare, 0,
+-						display->black, ZP);
++						txtcol, ZP);
+ 					sp = Pt(dr.max.x, y);
+ 				} else
+ 					sp = ZP;
+@@ -233,7 +233,7 @@
+ 						lp = Pt(dr.min.x, y);
+ 					line(b, lp, Pt(dr.max.x, y),
+ 						Endsquare, Endsquare, 0,
+-						display->black, ZP);
++						txtcol, ZP);
+ 					lp = Pt(dr.max.x, y);
+ 				} else
+ 					lp = ZP;
+diff -r b619d1fc23f6 sys/src/cmd/mothra/libpanel/scrollbar.c
+--- a/sys/src/cmd/mothra/libpanel/scrollbar.c	Mon Feb 11 23:43:14 2019 +0100
++++ b/sys/src/cmd/mothra/libpanel/scrollbar.c	Wed Feb 13 15:03:32 2019 +0100
+@@ -16,7 +16,7 @@
+ void pl_drawscrollbar(Panel *p){
+ 	Scrollbar *sp;
+ 	sp=p->data;
+-	sp->interior=pl_outline(p->b, p->r, p->state);
++	sp->interior=pl_outline(p->b, p->r, SUP); /* SUP was p->state */
+ 	pl_sliderupd(p->b, sp->interior, sp->dir, sp->lo, sp->hi);
+ }
+ int pl_hitscrollbar(Panel *g, Mouse *m){
+diff -r b619d1fc23f6 sys/src/cmd/mothra/libpanel/textview.c
+--- a/sys/src/cmd/mothra/libpanel/textview.c	Mon Feb 11 23:43:14 2019 +0100
++++ b/sys/src/cmd/mothra/libpanel/textview.c	Wed Feb 13 15:03:32 2019 +0100
+@@ -47,7 +47,7 @@
+ 	Point size;
+ 
+ 	tp=p->data;
+-	r=pl_outline(p->b, p->r, UP);
++	r=pl_outline(p->b, p->r, TUP);
+ 	twid=r.max.x-r.min.x;
+ 	if(twid!=tp->twid){
+ 		tp->twid=twid;
+@@ -80,7 +80,7 @@
+ 	if(oldhitword==oldhitfirst)
+ 		pl_passon(oldhitword, m);
+ 	if(m->buttons&OUT)
+-		p->state=UP;
++		p->state=PASSIVE;
+ 	else if(m->buttons&7){
+ 		p->state=DOWN;
+ 		tp->buttons=m->buttons;
+@@ -100,7 +100,7 @@
+ 	}
+ 	else{
+ 		if(p->state==DOWN) hitme=1;
+-		p->state=UP;
++		p->state=PASSIVE;
+ 	}
+ 	if(tp->hitfirst!=oldhitfirst || tp->hitword!=oldhitword){
+ 		plrtseltext(tp->text, tp->hitword, tp->hitfirst);
+@@ -214,7 +214,7 @@
+ 	Textview *tp;
+ 	tp=v->data;
+ 	v->flags=flags|LEAF;
+-	v->state=UP;
++	v->state=PASSIVE;
+ 	v->draw=pl_drawtextview;
+ 	v->hit=pl_hittextview;
+ 	v->type=pl_typetextview;
+diff -r b619d1fc23f6 sys/src/cmd/mothra/libpanel/textwin.c
+--- a/sys/src/cmd/mothra/libpanel/textwin.c	Mon Feb 11 23:43:14 2019 +0100
++++ b/sys/src/cmd/mothra/libpanel/textwin.c	Wed Feb 13 15:03:32 2019 +0100
+@@ -136,7 +136,7 @@
+ 	er=t->text+last;
+ 	for(r=t->text+first,lp=t->loc+(first-t->top);r!=er;r++,lp++){
+ 		if(lp->y+t->hgt>t->r.max.y){
+-			fprint(2, "chr %C, index %ld of %d, loc %d %d, off bottom\n",
++			fprint(2, "chr %C, index %zd of %d, loc %d %d, off bottom\n",
+ 				*r, lp-t->loc, t->bot-t->top, lp->x, lp->y);
+ 			return;
+ 		}
+@@ -155,7 +155,7 @@
+ 		default:
+ 			buf[runetochar(buf, r)]='\0';
+ 	/***/		pl_clr(t->b, Rpt(*lp, addpt(*lp, stringsize(t->font, buf))));
+-			ur=string(t->b, *lp, display->black, ZP, t->font, buf);
++			ur=string(t->b, *lp, txtcol, ZP, t->font, buf);
+ 			break;
+ 		}
+ 		if(lp[1].y!=lp[0].y)
+diff -r b619d1fc23f6 sys/src/cmd/mothra/mothra.c
+--- a/sys/src/cmd/mothra/mothra.c	Mon Feb 11 23:43:14 2019 +0100
++++ b/sys/src/cmd/mothra/mothra.c	Wed Feb 13 15:03:32 2019 +0100
+@@ -39,17 +39,17 @@
+ 	0x10, 0x08, 0x14, 0x08, 0x14, 0x28, 0x12, 0x28,
+ 	0x0A, 0x50, 0x16, 0x68, 0x20, 0x04, 0x3F, 0xFC,
+ };
+-Cursor confirmcursor={
++Cursor confirmcurs={
+ 	0, 0,
+-	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+-	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+-	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+-	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
++	0x0F, 0xBF, 0x0F, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF,
++	0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE,
++	0xFF, 0xFE, 0xFF, 0xFF, 0x00, 0x03, 0xFF, 0xFF,
++	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC,
+ 
+ 	0x00, 0x0E, 0x07, 0x1F, 0x03, 0x17, 0x73, 0x6F,
+ 	0xFB, 0xCE, 0xDB, 0x8C, 0xDB, 0xC0, 0xFB, 0x6C,
+ 	0x77, 0xFC, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03,
+-	0x94, 0xA6, 0x63, 0x3C, 0x63, 0x18, 0x94, 0x90,
++	0x94, 0xA6, 0x63, 0x3C, 0x63, 0x18, 0x94, 0x90
+ };
+ Cursor readingcurs={
+ 	-10, -3,
+@@ -351,15 +351,15 @@
+ 	plinit(screen->depth);
+ 	if(debug) notify(dienow);
+ 	getfonts();
+-	hrule=allocimage(display, Rect(0, 0, 1, 5), screen->chan, 1, DWhite);
++	hrule=allocimage(display, Rect(0, 0, 2048, 5), screen->chan, 1, DBlack);
+ 	if(hrule==0)
+ 		sysfatal("can't allocimage!");
+-	draw(hrule, Rect(0,1,1,3), display->black, 0, ZP);
+-	linespace=allocimage(display, Rect(0, 0, 1, 5), screen->chan, 1, DWhite);
++	draw(hrule, Rect(0,1,1280,3), txtcol, 0, ZP);
++	linespace=display->black;
+ 	if(linespace==0)
+ 		sysfatal("can't allocimage!");
+-	bullet=allocimage(display, Rect(0,0,25, 8), screen->chan, 0, DWhite);
+-	fillellipse(bullet, Pt(4,4), 3, 3, display->black, ZP);
++	bullet=allocimage(display, Rect(0,0,25, 8), screen->chan, 0, DBlack);
++	fillellipse(bullet, Pt(4,4), 3, 3, txtcol, ZP);
+ 	mkpanels();
+ 	unlockdisplay(display);
+ 	eresized(0);
+@@ -423,11 +423,17 @@
+ 				search();
+ 				break;
+ 			case Kright:
+-				sidescroll(text->size.x/4, 1);
++				scrolltext(text->size.y/24, 1); /* down one line */
+ 				break;
+ 			case Kleft:
++				scrolltext(-text->size.y/24, 1); /* up one line */
++				break;
++			case Kins:
+ 				sidescroll(-text->size.x/4, 1);
+ 				break;
++			case Kdel:
++				sidescroll(text->size.x/4, 1);
++				break;
+ 			}
+ 			break;
+ 		case Emouse:
+@@ -450,6 +456,18 @@
+ 		}
+ 	}
+ }
++Cursor confirmcursor={
++	0, 0,
++	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
++	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
++	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
++	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
++
++	0x00, 0x0E, 0x07, 0x1F, 0x03, 0x17, 0x73, 0x6F,
++	0xFB, 0xCE, 0xDB, 0x8C, 0xDB, 0xC0, 0xFB, 0x6C,
++	0x77, 0xFC, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03,
++	0x94, 0xA6, 0x63, 0x3C, 0x63, 0x18, 0x94, 0x90,
++};
+ int confirm(int b){
+ 	Mouse down, up;
+ 	esetcursor(&confirmcursor);
+diff -r b619d1fc23f6 sys/src/cmd/mothra/mothra.h
+--- a/sys/src/cmd/mothra/mothra.h	Mon Feb 11 23:43:14 2019 +0100
++++ b/sys/src/cmd/mothra/mothra.h	Wed Feb 13 15:03:32 2019 +0100
+@@ -1,7 +1,7 @@
+ enum{
+ 	NWWW=64,	/* # of pages we hold in the log */
+-	NXPROC=5,	/* # of parallel procs loading the pix */
+-	NPIXMB=8,	/* megabytes of image data to keep arround */
++	NXPROC=16,	/* # of parallel procs loading the pix */
++	NPIXMB=48,	/* megabytes of image data to keep arround */
+ 	NNAME=512,
+ 	NLINE=256,
+ 	NAUTH=128,
+diff -r b619d1fc23f6 sys/src/cmd/mothra/rdhtml.c
+--- a/sys/src/cmd/mothra/rdhtml.c	Mon Feb 11 23:43:14 2019 +0100
++++ b/sys/src/cmd/mothra/rdhtml.c	Wed Feb 13 15:03:32 2019 +0100
+@@ -13,25 +13,25 @@
+ 	Font *font;
+ 	int space;
+ }fontlist[4][4]={
+-	"dejavusans/unicode.12", 0, 0,
+-	"dejavusans/unicode.12", 0, 0,
+-	"dejavusans/unicode.14", 0, 0,
+-	"dejavusans/unicode.16", 0, 0,
++	"unifont/unifont.12", 0, 0,
++	"unifont/unifont.12", 0, 0,
++	"unifont/unifont.14", 0, 0,
++	"unifont/unifont.16", 0, 0,
+ 
+-	"dejavusansit/unicode.12", 0, 0,
+-	"dejavusansit/unicode.12", 0, 0,
+-	"dejavusansit/unicode.14", 0, 0,
+-	"dejavusansit/unicode.16", 0, 0,
++	"unifont/unifont.12", 0, 0,
++	"unifont/unifont.12", 0, 0,
++	"unifont/unifont.14", 0, 0,
++	"unifont/unifont.16", 0, 0,
+ 
+-	"dejavusansbd/unicode.12", 0, 0,
+-	"dejavusansbd/unicode.12", 0, 0,
+-	"dejavusansbd/unicode.14", 0, 0,
+-	"dejavusansbd/unicode.16", 0, 0,
++	"unifont/unifont.12", 0, 0,
++	"unifont/unifont.12", 0, 0,
++	"unifont/unifont.14", 0, 0,
++	"unifont/unifont.16", 0, 0,
+ 
+-	"terminus/unicode.12", 0, 0,
+-	"terminus/unicode.14", 0, 0,
+-	"terminus/unicode.16", 0, 0,
+-	"terminus/unicode.18", 0, 0,
++	"unifont/unifont.12", 0, 0,
++	"unifont/unifont.14", 0, 0,
++	"unifont/unifont.16", 0, 0,
++	"unifont/unifont.18", 0, 0,
+ };
+ 
+ Font *pl_whichfont(int f, int s, int *space){
--- /dev/null
+++ b/nusb-ure
@@ -1,0 +1,492 @@
+patch for broken fucking ure piece of shit ass fuck
+usb ether card; incomplete since the fucking thing died
+
+diff -r ebaff70ba8fe sys/src/9/boot/nusbrc
+--- a/sys/src/9/boot/nusbrc	Mon May 22 18:33:14 2017 +0200
++++ b/sys/src/9/boot/nusbrc	Sat May 27 11:58:35 2017 +0200
+@@ -23,6 +23,8 @@
+ 			nusb/ether -t aue $etherargs $id
+ 		case 0bda8150
+ 			nusb/ether -t url $etherargs $id
++		case 0bda8151
++			nusb/ether -t ure $etherargs $id
+ 		case 18d14ee3 0bb40003
+ 			nusb/ether -t rndis $etherargs $id
+ 		case *
+diff -r ebaff70ba8fe sys/src/cmd/nusb/ether/ether.c
+--- a/sys/src/cmd/nusb/ether/ether.c	Mon May 22 18:33:14 2017 +0200
++++ b/sys/src/cmd/nusb/ether/ether.c	Sat May 27 11:58:35 2017 +0200
+@@ -808,6 +808,7 @@
+ extern int a88772init(Dev *);
+ extern int smscinit(Dev *);
+ extern int cdcinit(Dev *);
++extern int ureinit(Dev *);
+ extern int urlinit(Dev *);
+ extern int rndisinit(Dev *);
+ 
+@@ -820,6 +821,7 @@
+ 	"a88178",	a88178init,
+ 	"a88772",	a88772init,
+ 	"aue",		aueinit,
++	"ure",		ureinit,
+ 	"url",		urlinit,
+ 	"rndis",	rndisinit,
+ };
+diff -r ebaff70ba8fe sys/src/cmd/nusb/ether/mkfile
+--- a/sys/src/cmd/nusb/ether/mkfile	Mon May 22 18:33:14 2017 +0200
++++ b/sys/src/cmd/nusb/ether/mkfile	Sat May 27 11:58:35 2017 +0200
+@@ -5,7 +5,7 @@
+ 
+ TARG=ether
+ HFILES=
+-OFILES=ether.$O cdc.$O smsc.$O asix.$O aue.$O url.$O rndis.$O
++OFILES=ether.$O cdc.$O smsc.$O asix.$O aue.$O ure.$O url.$O rndis.$O
+ 
+ </sys/src/cmd/mkone
+ 
+diff -r ebaff70ba8fe sys/src/cmd/nusb/ether/ure.c
+--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
++++ b/sys/src/cmd/nusb/ether/ure.c	Sat May 27 11:58:35 2017 +0200
+@@ -0,0 +1,442 @@
++/*
++ * Realtek RTL8152 10/100 USB Ethernet
++ */
++/* FIXME: note on submit: based on url.c and openbsd's ure(4) driver rev 1.3
++ * + changes to nusbrc */
++#include <u.h>
++#include <libc.h>
++#include <thread.h>
++
++#include "usb.h"
++#include "dat.h"
++
++enum {
++	Timeout = 1000,
++	Ver4c00 = 0x4c00,
++	Ver4c10 = 0x4c10,
++
++	/* requests */
++	Rqm = 0x05,		/* request mem */
++	Crm = 1,		/* command read mem */
++	Cwm = 2,		/* command write mem */
++	Cpla = 0x100,		/* indices */
++	Cusb = 0x000,
++
++	/* registers */
++	Idr = 0xc000,		/* ether addr */
++	Rcr = 0xc010,		/* receive configuration */
++		Ab = 1<<3,
++		Am = 1<<2,
++		Apm = 1<<1,
++		Aap = 1<<0,
++	Rfifo0 = 0xc0a0,	/* rx fifo */
++		Thr1n = 0x00080002,	/* thr1 normal */
++	Rfifo1 = 0xc0a4,
++		Thr2fl = 0x00000060,	/* thr2 full speed */
++		Thr2hi = 0x00000038,	/* thr2 high speed */
++	Rfifo2 = 0xc0a8,
++		Thr3fl = 0x00000078,	/* thr3 full speed */
++		Thr3hi = 0x00000048,	/* thr3 high speed */
++	Fmc = 0xc0b4,		/* packet filter */
++		Fcr = 1<<0,
++	Tun = 0xc0bc,		/* teredo configuration */
++		Tusel = 1<<15,	/* select */
++		Tuwak = 0x7f00,	/* wake mask */
++		Turs = 0x00fe,	/* rs event mask */
++		Tunm = Tusel | Tuwak | Turs,
++	Mar0 = 0xcd00,		/* multicast addr */
++	Mar4 = 0xcd04,
++	Bak = 0xd000,		/* backup */
++	Tutm = 0xd2cc,		/* teredo timer */
++	Wow = 0xd2e8,		/* realwow timer */
++	Led = 0xdd92,		/* led control */
++		Ledm = 7<<8,	/* mode mask */
++	Phimr = 0xe022,		/* gphy intr imr */
++		Ilnkch = 1<<3,	/* speed down link change */
++		Irxdv = 1<<2,	/* speed down rxdv */
++		Ispddn = 1<<1,	/* speed down */
++		Ists = 1<<0,	/* status */
++	Mcpwr = 0xe0c0,		/* mac power control */
++		D3clk = 1<<14,
++		Mcuclk = 0x07010f07,
++		Mcuclkm = 0x0f0f0f0f,
++	Wdt6 = 0xe428,
++		Wdtmd = 1<<0,	/* wdt6 set mode */
++	Tcr0 = 0xe610,
++		Fifoau = 1<<7,	/* auto fifo */
++	Tcr1 = 0xe612,
++		Ver = 0x7cf0,	/* version mask */
++	Tfifo = 0xe618,		/* tx fifo */
++		Thrn = 0x00400008,	/* thr normal */
++	Cr = 0xe813,		/* command */
++		Sr = 1<<4,	/* software reset */
++		Re = 1<<3,	/* ethernet receive enable */
++		Te = 1<<2,	/* ethernet transmit enable */
++	Phpwr = 0xe84c,		/* phy power control */
++		Txidl = 1<<7,	/* tx 10m idle */
++		Pfmpwm = 1<<6,
++	Oob = 0xe84f,		/* oob control */
++		Ook = 1<<7,	/* now is oob */
++		Ordy = 1<<1,	/* link list ready */
++	Cpcr = 0xe854,
++		Rxvlan = 1<<6,
++	Msc1 = 0xe85a,		/* misc 1 */
++		Rxdy = 1<<3,
++	Rocp = 0xe86c,		/* ocp gphy base */
++		Aldps = 0x2010,	/* aldps configuration */
++			Aldpsa = Aldps & 0xf000,
++			Aldpsr = Aldps & 0x0fff | 0xb000,
++			Pwsv = 1<<15,	/* power save */
++			Pdnps = 1<<9,
++			Lnk = 1<<8,	/* link enable */
++			Dis = 1<<4,	/* dis sdsave */
++	Sff7 = 0xe8de,			/* sff status 7 */
++		Inll = 1<<15,
++		Borw = 1<<14,
++
++	Ucr = 0xd406,		/* usb control */
++		Noagg = 1<<4,	/* disable rx aggregation */
++	Txag = 0xd40a,		/* tx aggregation */
++		Maxth = 3<<0,	/* max threshold */
++	Urth = 0xd40c,		/* rx buf threshold */
++		Urthhi = 0x7a120180,
++	Pms = 0xd432,		/* pm status */
++		Rsm = 1<<0,	/* resume indicate */
++	Udma = 0xd434,
++		Txadj1 = 1<<8,	/* tx size adjust 1 */
++		Tstoff = 1<<0,	/* test mode disable */
++	Ups = 0xd800,
++		Cut = 1<<8	/* power cut */
++};
++
++static int
++mem(Dev *d, int cmd, int off, int index, uchar *buf, int len)
++{
++	int r, rc;
++
++	if(d == nil)
++		return 0;
++	r = Rvendor | Rdev;
++	if(cmd == Crm)
++		r |= Rd2h;
++	else
++		r |= Rh2d;
++	rc = usbcmd(d, r, Rqm, off, index, buf, len);
++	if(rc < 0){
++		fprint(2, "%s: mem(%d, %#.4x, %#.4x) failed\n",
++			argv0, cmd, off, index);
++	}
++	return rc;
++}
++
++static int
++rr8(Dev *d, int reg, int index)
++{
++	uchar v[4], m;
++
++	m = (reg & 3) << 3;
++	reg &= ~3;
++	PUT4(v, 0);
++	if(mem(d, Crm, reg, index, v, sizeof v) < 0)
++		return 0;
++	fprint(2, "rr8 %ux %ux %ux\n", reg, index, GET4(v) >> m & 0xff);
++	return GET4(v) >> m & 0xff;
++}
++
++static int
++rr16(Dev *d, int reg, int index)
++{
++	uchar v[4], m;
++
++	m = (reg & 2) << 3;
++	reg &= ~3;
++	PUT4(v, 0);
++	if(mem(d, Crm, reg, index, v, sizeof v) < 0)
++		return 0;
++	fprint(2, "rr16 %ux %ux %ux\n", reg, index, GET4(v) >> m & 0xffff);
++	return GET4(v) >> m & 0xffff;
++}
++
++static int
++rr32(Dev *d, int reg, int index)
++{
++	uchar v[4];
++
++	PUT4(v, 0);
++	if(mem(d, Crm, reg, index, v, sizeof v) < 0)
++		return 0;
++	fprint(2, "rr32 %ux %ux %ux\n", reg, index, GET4(v));
++	return GET4(v);
++}
++
++static int
++wr8(Dev *d, int reg, int index, int val)
++{
++	uchar v[4], m;
++	u16int en;
++
++	fprint(2, "wr8 %ux %ux %ux\n", reg, index, val);
++	en = 0x11;
++	m = reg & 3;
++	val &= 0xff;
++	if(reg & 3){
++		en <<= m;
++		val <<= m << 3;
++		reg &= ~3;
++	}
++	PUT4(v, val);
++	if(mem(d, Cwm, reg, index | en, v, sizeof v) < 0)
++		return -1;
++	return 0;
++}
++
++static int
++wr16(Dev *d, int reg, int index, int val)
++{
++	uchar v[4], m;
++	u16int en;
++
++	fprint(2, "wr16 %ux %ux %ux\n", reg, index, val);
++	en = 0x33;
++	m = reg & 2;
++	val &= 0xffff;
++	if(reg & 2){
++		en <<= m;
++		val <<= m << 3;
++		reg &= ~3;
++	}
++	PUT4(v, val);
++	if(mem(d, Cwm, reg, index | en, v, sizeof v) < 0)
++		return -1;
++	return 0;
++}
++
++static int
++wr32(Dev *d, int reg, int index, int val)
++{
++	uchar v[4];
++
++	fprint(2, "wr32 %ux %ux %ux\n", reg, index, val);
++	PUT4(v, val);
++	if(mem(d, Cwm, reg, index | 0xff, v, sizeof v) < 0)
++		return -1;
++	return 0;
++}
++
++static int
++csr8(Dev *d, int reg, int index, int clr, int set)
++{
++	int v;
++
++	v = rr8(d, reg, index) & ~clr | set;
++	return wr8(d, reg, index, v);
++}
++
++static int
++csr16(Dev *d, int reg, int index, int clr, int set)
++{
++	int v;
++
++	v = rr16(d, reg, index) & ~clr | set;
++	return wr16(d, reg, index, v);
++}
++
++static int
++csr32(Dev *d, int reg, int index, int clr, int set)
++{
++	int v;
++
++	v = rr32(d, reg, index) & ~clr | set;
++	return wr32(d, reg, index, v);
++}
++
++static void
++oobwait(Dev *d)
++{
++	int i;
++
++	for(i=0; i<Timeout; i++){
++		if(rr8(d, Oob, Cpla) & Ordy)
++			break;
++		sleep(10);
++	}
++	if(i == Timeout)
++		fprint(2, "%s: timeout waiting for OOB control\n", argv0);
++}
++
++static void
++reset(Dev *d)
++{
++	int i;
++
++	wr8(d, Cr, Cpla, Sr);
++	for(i=0; i<Timeout; i++) {
++		if((rr8(d, Cr, Cpla) & Sr) == 0)
++			break;
++		sleep(10);
++	}
++	if(i >= Timeout)
++		fprint(2, "%s: reset failed\n", argv0);
++	sleep(100);
++}
++
++static int
++phyinit(Dev *d)
++{
++	int r, v;
++	uchar a[8];
++
++	reset(d);
++	v = rr16(d, Tcr1, Cpla) & Ver;
++	if(!setmac){
++		r = v == Ver4c10 ? Bak : Idr;
++		if(mem(d, Crm, r, Cpla, a, sizeof a) < 0)
++			return -1;
++		memcpy(macaddr, a, sizeof macaddr);
++	}
++
++	wr16(d, Rocp, Cpla, Aldpsa);
++	wr16(d, Aldpsr, Cpla, Pwsv | Pdnps | Lnk | Dis);
++	sleep(20);
++
++	if(v == 0x4c00)
++		csr16(d, Led, Cpla, Ledm, 0);
++	csr16(d, Ups, Cusb, Cut, 0);
++	csr16(d, Pms, Cusb, Rsm, 0);
++	csr16(d, Phpwr, Cpla, 0, Txidl | Pfmpwm);
++	csr32(d, Mcpwr, Cpla, Mcuclkm, Mcuclk | D3clk);
++	wr16(d, Phimr, Cpla, Ists | Ispddn | Irxdv | Ilnkch);
++	csr16(d, Ucr, Cusb, 0, Noagg);
++
++	wr16(d, Rocp, Cpla, Aldpsa);
++	wr16(d, Aldpsr, Cpla, Pwsv | Pdnps | Lnk | Dis);
++	sleep(20);
++
++	csr16(d, Msc1, Cpla, 0, Rxdy);
++	csr32(d, Tun, Cpla, Tunm, 0);
++	wr16(d, Wdt6, Cpla, Wdtmd);
++	wr16(d, Wow, Cpla, 0);
++	wr32(d, Tutm, Cpla, 0);
++	csr32(d, Rcr, Cpla, Aap | Apm | Am | Ab, 0);
++	reset(d);
++	wr8(d, Cr, Cpla, 0);
++
++	csr8(d, Oob, Cpla, Ook, 0);
++	csr16(d, Sff7, Cpla, Borw, 0);
++	oobwait(d);
++	csr16(d, Sff7, Cpla, 0, Inll);
++	oobwait(d);
++
++	csr16(d, Cpcr, Cpla, Rxvlan, 0);
++	csr16(d, Tcr0, Cpla, Fifoau, 0);
++	wr32(d, Rfifo0, Cpla, Thr1n);
++	wr32(d, Rfifo1, Cpla, Thr2fl);
++	wr32(d, Rfifo2, Cpla, Thr3fl);
++	wr32(d, Tfifo, Cpla, Thrn);
++	wr8(d, Txag, Cusb, Maxth);
++	wr32(d, Urth, Cusb, Urthhi);
++	wr32(d, Udma, Cusb, Tstoff | Txadj1);
++	reset(d);
++	return 0;
++}
++
++static int
++urereceive(Dev *ep)
++{
++	Block *b;
++	int n;
++
++	b = allocb(Maxpkt+24);
++	if((n = read(ep->dfd, b->wp, b->lim - b->base)) < 0){
++		freeb(b);
++		return -1;
++	}
++	b->wp += n;
++	fprint(2, "urerx %d\n", n);
++	while(BLEN(b) > 24){
++		n = GET4(b->rp) & 0x7fff;
++		b->rp += 24;
++		if(n > BLEN(b))
++			break;
++		etheriq(b, 1);
++		b->rp += n;
++	}
++	freeb(b);
++	return 0;
++}
++
++static void
++uretransmit(Dev *ep, Block *b)
++{
++	int n;
++
++	n = BLEN(b);
++	fprint(2, "uretx %d\n", n);
++	b->rp -= 8;
++	PUT4(b->rp, n + 8 | 3 << 30);
++	PUT4(b->rp+4, 0);
++	write(ep->dfd, b->rp, 8+n);
++	freeb(b);
++	fprint(2, "done\n");
++}
++
++static int
++urepromiscuous(Dev *d, int on)
++{
++	int c, s;
++
++	c = 0;
++	s = 0;
++	if(on)
++		s |= Am | Aap;
++	else{
++		c = Aap;
++		if(nmulti == 0)
++			c |= Am;
++	}
++	return csr16(d, Rcr, Cpla, c, s);
++}
++
++static int
++uremulticast(Dev *d, uchar*, int)
++{
++	int c, s;
++
++	c = 0;
++	s = 0;
++	if(nmulti)
++		s |= Am;
++	else{
++		if(nprom == 0)
++			c = Am;
++	}
++	return csr16(d, Rcr, Cpla, c, s);
++}
++
++int
++ureinit(Dev *d)
++{
++	uchar a[8];
++
++	if(phyinit(d) < 0)
++		return -1;
++	memset(a, 0, sizeof a);
++	memcpy(a, macaddr, sizeof macaddr);
++	if(mem(d, Cwm, Idr, Cpla | 0x3f, a, sizeof a) < 0)
++		return -1;
++
++	csr16(d, Fmc, Cpla, Fcr, 0);
++	csr16(d, Fmc, Cpla, 0, Fcr);
++	csr8(d, Cr, Cpla, 0, Re | Te);
++	csr16(d, Msc1, Cpla, Rxdy, 0);
++	wr32(d, Rcr, Cpla, Apm);
++	wr32(d, Mar0, Cpla, 0);
++	wr32(d, Mar4, Cpla, 0);
++	wr32(d, Rcr, Cpla, Apm | Am);
++
++	epreceive = urereceive;
++	eptransmit = uretransmit;
++	eppromiscuous = urepromiscuous;
++	epmulticast = uremulticast;
++	return 0;
++}
--- /dev/null
+++ b/page-invert
@@ -1,0 +1,73 @@
+this shit sucks. that code doesn't belong there. something in pipeline would be
+better. pico?
+
+diff -r 3cb0cf9ab43a sys/src/cmd/page.c
+--- a/sys/src/cmd/page.c	Mon Feb 08 20:07:56 2016 -0500
++++ b/sys/src/cmd/page.c	Thu Feb 11 16:16:28 2016 +0100
+@@ -33,6 +33,7 @@
+ int imode;
+ int newwin;
+ int rotate;
++int invert;
+ int viewgen;
+ int forward;	/* read ahead direction: >= 0 forwards, < 0 backwards */
+ Point resize, pos;
+@@ -67,6 +68,7 @@
+ 	Cfitheight,
+ 	Crotate90,
+ 	Cupsidedown,
++	Cinv,
+ 	Cdummy1,
+ 	Cnext,
+ 	Cprev,
+@@ -91,6 +93,7 @@
+ 	[Cfitheight]	"fit height",	'h', 0, 0,
+ 	[Crotate90]	"rotate 90",	'r', 0, 0,
+ 	[Cupsidedown]	"upside down",	'u', 0, 0,
++	[Cinv]		"invert",	'i', 0, 0,
+ 	[Cdummy1]	"",		0, 0, 0,
+ 	[Cnext]		"next",		Kright, ' ', '\n', 
+ 	[Cprev]		"prev",		Kleft, Kbs, 0,
+@@ -894,6 +897,23 @@
+ }
+ 
+ void
++inv(Image *i)
++{
++	int n;
++	uchar *buf, *p;
++
++	n = imagesize(i);
++	buf = malloc(n);
++	if(buf == 0)
++		return;
++	unloadimage(i, i->r, buf, n);
++	for(p=buf; p<buf+n; p++)
++		*p = ~*p;
++	loadimage(i, i->r, buf, n);
++	free(buf);
++}
++
++void
+ loadpage(Page *p)
+ {
+ 	int fd;
+@@ -912,6 +932,8 @@
+ 		if(p->image == nil)
+ 			p->open = nil;
+ 		else {
++			if(invert)
++				inv(p->image);
+ 			lockdisplay(display);
+ 			imemsize += imagesize(p->image);
+ 			unlockdisplay(display);
+@@ -1494,6 +1516,9 @@
+ 		resize = subpt(screen->r.max, screen->r.min);
+ 		resize.x = 0;
+ 		goto Unload;
++	case Cinv:
++		invert = !invert;
++		goto Unload;
+ 	case Czoomin:
+ 	case Czoomout:
+ 		if(current == nil || !canqlock(current))
--- /dev/null
+++ b/page-invertone
@@ -1,0 +1,57 @@
+diff -r 813115218568 sys/src/cmd/page.c
+--- a/sys/src/cmd/page.c	Tue Jan 19 13:06:22 2016 +0100
++++ b/sys/src/cmd/page.c	Sat Jan 23 01:35:43 2016 +0100
+@@ -67,6 +67,7 @@
+ 	Cfitheight,
+ 	Crotate90,
+ 	Cupsidedown,
++	Cinv,
+ 	Cdummy1,
+ 	Cnext,
+ 	Cprev,
+@@ -91,6 +92,7 @@
+ 	[Cfitheight]	"fit height",	'h', 0, 0,
+ 	[Crotate90]	"rotate 90",	'r', 0, 0,
+ 	[Cupsidedown]	"upside down",	'u', 0, 0,
++	[Cinv]		"invert",	'i', 0, 0,
+ 	[Cdummy1]	"",		0, 0, 0,
+ 	[Cnext]		"next",		Kright, ' ', '\n', 
+ 	[Cprev]		"prev",		Kleft, Kbs, 0,
+@@ -1375,6 +1377,23 @@
+ }
+ 
+ void
++inv(Image *i)
++{
++	int n;
++	uchar *buf, *p;
++
++	n = imagesize(i);
++	buf = malloc(n);
++	if(buf == 0)
++		return;
++	unloadimage(i, i->r, buf, n);
++	for(p=buf; p<buf+n; p++)
++		*p = ~*p;
++	loadimage(i, i->r, buf, n);
++	free(buf);
++}
++
++void
+ showext(Page *p)
+ {
+ 	char label[64], *argv[4];
+@@ -1494,6 +1513,13 @@
+ 		resize = subpt(screen->r.max, screen->r.min);
+ 		resize.x = 0;
+ 		goto Unload;
++	case Cinv:
++		if(current == nil || !canqlock(current))
++			break;
++		inv(current->image);
++		drawpage(current);
++		qunlock(current);
++		break;
+ 	case Czoomin:
+ 	case Czoomout:
+ 		if(current == nil || !canqlock(current))
--- /dev/null
+++ b/page-tempfix
@@ -1,0 +1,12 @@
+diff -r 2579c34216f2 sys/src/cmd/page.c
+--- a/sys/src/cmd/page.c	Tue Jul 05 23:21:36 2016 -0500
++++ b/sys/src/cmd/page.c	Fri Jul 08 10:02:17 2016 +0200
+@@ -436,7 +436,7 @@
+ 		"/\\<itemref/{"
+ 		"if(match($0, /idref\\=\\\"([^\\\"]+)\\\"/)){"
+ 		"ref=substr($0, RSTART+7, RLENGTH-8);"
+-		"print item[ref]; fflush}}'");
++		"print item[ref]}}'");
+ 	s = strrchr(buf, '/')+1;
+ 	while((n = read(fd, s, e-s)) > 0){
+ 		while(n > 0 && s[n-1] == '\n')
--- /dev/null
+++ b/paint-colpick
@@ -1,0 +1,35 @@
+diff -r eb26bc92b777 sys/src/cmd/paint.c
+--- a/sys/src/cmd/paint.c	Sun Jan 13 17:17:11 2019 +0100
++++ b/sys/src/cmd/paint.c	Sun Jan 20 07:54:13 2019 +0100
+@@ -523,9 +523,12 @@
+ 		return 1;
+ 	}
+ 	if(ptinrect(m.xy, palr)){
++		int i, n;
++		char buf[32];
+ 		Image *col;
+ 
+-		col = pal[(m.xy.x - palr.min.x) * nelem(pal) / Dx(palr)];
++		i = (m.xy.x - palr.min.x) * nelem(pal) / Dx(palr);
++		col = pal[i];
+ 		switch(m.buttons & 7){
+ 		case 1:
+ 			ink = col;
+@@ -536,6 +539,17 @@
+ 			drawpal();
+ 			update(nil);
+ 			break;
++		case 4:
++			buf[0] = 0;
++			if(eenter("Color", buf, sizeof(buf), &m) <= 0)
++				break;
++			n = strtol(buf, nil, 0);
++			freeimage(pal[i]);
++			pal[i] = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1, n << 8 | 0xFF);
++			if(pal[i] == nil)
++				sysfatal("allocimage: %r");
++			drawpal();
++			break;
+ 		}
+ 		return 1;
+ 	}
--- /dev/null
+++ b/paint-reverse
@@ -1,0 +1,14 @@
+diff -r 18ea8aad3bb6 sys/src/cmd/paint.c
+--- a/sys/src/cmd/paint.c	Mon Feb 25 04:36:37 2019 +0100
++++ b/sys/src/cmd/paint.c	Tue Feb 26 18:27:09 2019 +0100
+@@ -625,8 +639,8 @@
+ 		if(pal[i] == nil)
+ 			sysfatal("allocimage: %r");
+ 	}
+-	ink = pal[0];
+-	back = pal[1];
++	ink = pal[1];
++	back = pal[0];
+ 	drawpal();
+ 	center();
+ 
--- /dev/null
+++ b/pc64-nvidia
@@ -1,0 +1,20 @@
+diff -r 2c3766a9188f sys/src/9/pc64/mkfile
+--- a/sys/src/9/pc64/mkfile	Sun May 21 17:27:30 2017 +0200
++++ b/sys/src/9/pc64/mkfile	Mon May 22 08:05:44 2017 +0200
+@@ -84,7 +84,7 @@
+ 
+ # copies generated by the rule below
+ PCHEADERS=wifi.h usbehci.h screen.h etherif.h ethermii.h mp.h io.h ahci.h \
+-	yukdump.h
++	yukdump.h nv_dma.h
+ 
+ REPCH=`{echo $PCHEADERS | sed 's/\.h//g; s/ /|/g'}
+ ^($REPCH)\.h:R:	../pc/\1.h
+@@ -106,6 +106,7 @@
+ etheryuk.$O:			yukdump.h
+ $VGA mouse.$O:			screen.h /sys/include/memdraw.h
+ vgavesa.$O:			/386/include/ureg.h
++vganvidia.$O:			nv_dma.h
+ 
+ mp.$O:				mp.h apbootstrap.h 
+ apic.$O squidboy.$O:		mp.h
--- /dev/null
+++ b/play-dmid-mod
@@ -1,0 +1,43 @@
+diff -r a1433cb28946 rc/bin/play
+--- a/rc/bin/play	Sun Jul 15 06:31:45 2018 +0200
++++ b/rc/bin/play	Sun Jul 15 17:58:38 2018 +0200
+@@ -23,6 +23,13 @@
+ 	cleanup
+ }
+ 
++fn midi {
++	if(test -f /mnt/wad/genmidi)
++		games/dmid | games/opl3
++	if not
++		games/midi -c
++}
++
+ fn play1 {
+ 	if(! ~ $#* 2){
+ 		tmp=(/tmp/play.$pid.$#tmp.tmp $tmp)
+@@ -57,9 +64,11 @@
+ 		case *audio/basic*
+ 			audio/sundec
+ 		case *audio/midi*
+-			games/midi -c
++			midi
+ 		case *audio/mus*
+-			games/mus | games/midi -c
++			games/mus | midi
++		case *audio/mod*
++			mod/decode
+ 		case *pls*
+ 			awk 'BEGIN {FS="="} /^File/{print $2}' | play1 list plain
+ 		case *
+@@ -75,9 +84,9 @@
+ 			case *.au *.AU
+ 				audio/sundec
+ 			case *.mid *.MID
+-				games/midi -c
++				midi
+ 			case *.mus *.MUS
+-				games/mus | games/midi -c
++				games/mus | midi
+ 			case *
+ 				echo $argv0: $1: unknown format: $2 >[1=2]
+ 			}
--- /dev/null
+++ b/png-maxmem
@@ -1,0 +1,14 @@
+wtf?
+
+diff -r 813115218568 sys/src/cmd/jpg/readpng.c
+--- a/sys/src/cmd/jpg/readpng.c	Tue Jan 19 13:06:22 2016 +0100
++++ b/sys/src/cmd/jpg/readpng.c	Sat Jan 23 08:13:54 2016 +0100
+@@ -10,7 +10,7 @@
+ 
+ enum
+ {
+-	IDATSIZE = 8*1024*1024,
++	IDATSIZE = 64*1024*1024,
+ 
+ 	/* filtering algorithms */
+ 	FilterNone =	0,	/* new[x][y] = buf[x][y] */
--- /dev/null
+++ b/png-trns-unfinished
@@ -1,0 +1,87 @@
+png: read mTRS chunks for indexed rgb24 images
+
+FIXME: UNFINISHED, surely breaks with other formats, and code is crap
+- then, don't check against apalsize (which should not exist) but
+  channel/nout/... settings
+- implement support for other modes, if we can find test pictures
+
+diff -r 321912cfedf3 sys/src/cmd/jpg/readpng.c
+--- a/sys/src/cmd/jpg/readpng.c	Sun Jun 25 22:57:47 2017 +0200
++++ b/sys/src/cmd/jpg/readpng.c	Mon Jun 26 23:29:03 2017 +0200
+@@ -46,6 +46,8 @@
+ 	int pass;			/* adam7 pass#; 0 means no adam7 */
+ 	uchar palette[3*256];	/* color palette */
+ 	int palsize;		/* number of palette entries */
++	uchar apalette[256];	/* optional alpha values for palette */
++	int hasapal;
+ };
+ 
+ struct ZlibR
+@@ -142,8 +144,18 @@
+ 			z->w->palsize = 256;
+ 			goto Again;
+ 		}
+-		if(type[0] & PropertyBit)
+-			goto Again;  /* skip auxiliary chunks fornow */
++		if(type[0] & PropertyBit){
++			/* FIXME: non-3 formats are unhandled */
++			if(strcmp(type,"tRNS"))
++				goto Again;  /* skip auxiliary chunks fornow */
++			if(z->w->chandesc != CRGBA32 || z->w->nchan != 1)
++				goto Again;	/* unimplemented */
++			if(n > z->w->palsize)
++				sysfatal("invalid tRNS chunk len %d", n);
++			memcpy(z->w->apalette, z->p, n);
++			z->w->hasapal = 1;
++			goto Again;
++		}
+ 		if(strcmp(type,"IDAT")){
+ 			sysfatal("unrecognized mandatory chunk %s", type);
+ 			goto Again;
+@@ -258,7 +270,7 @@
+ 							j >>= 8-z->bpc;
+ 						if(j >= z->palsize)
+ 							sysfatal("index %d >= palette size %d", j, z->palsize);
+-						pixel[3] = pixel[1];	/* alpha */
++						pixel[3] = z->hasapal ?  z->apalette[j] : pixel[1];	/* alpha */
+ 						pixel[0] = z->palette[3*j];
+ 						pixel[1] = z->palette[3*j+1];
+ 						pixel[2] = z->palette[3*j+2];
+@@ -272,6 +284,12 @@
+ 					case CRGBA32:
+ 					//	print("%.2x%.2x%.2x%.2x ", pixel[0], pixel[1], pixel[2], pixel[3]);
+ 						*w++ += pixel[3];
++						if(z->hasapal){
++							*w++ = pixel[2];
++							*w++ = pixel[1];
++							*w++ = pixel[0];
++							break;
++						}
+ 						*w++ += (pixel[2]*pixel[3])/255;
+ 						*w++ += (pixel[1]*pixel[3])/255;
+ 						*w++ += (pixel[0]*pixel[3])/255;
+@@ -438,9 +456,14 @@
+ 	case 3: /* indexed rgb with PLTE */
+ 		if(bpc != 1 && bpc != 2 && bpc != 4 && bpc != 8)
+ 			sysfatal("invalid indexed rgb bpc %d", bpc);
++		/* FIXME: conditially set CRGBA32? maybe pngmalloc directly to zw buffer, resize it
++		 * as needed, and set it before returning? or perhaps alloc maximal size,
++		 * and if there's no valid tRNS chunk, shrink it before returning image;
++		 * so, alloc max, but use minimal params before inflating... */
++		/* FIXME: support other formats */
+ 		image->nchans = 1;
+-		image->chandesc = CRGB24;
+-		nout = 3;
++		image->chandesc = CRGBA32;
++		nout = 4;
+ 		nchan = 1;
+ 		break;
+ 	case 4:	/* grey+alpha */
+@@ -478,6 +501,7 @@
+ 	zw.ndata = image->chanlen;
+ 	zw.chandesc = image->chandesc;
+ 	zw.noutchan = nout;
++	memset(zw.apalette, 0xff, sizeof zw.apalette);
+ 
+ 	zw.dx = dx;
+ 	zw.dy = dy;
--- /dev/null
+++ b/rio-col
@@ -1,0 +1,46 @@
+rio colors.
+
+diff -r 3cb0cf9ab43a sys/src/cmd/rio/data.c
+--- a/sys/src/cmd/rio/data.c	Mon Feb 08 20:07:56 2016 -0500
++++ b/sys/src/cmd/rio/data.c	Sun Feb 14 06:53:09 2016 +0100
+@@ -175,27 +175,21 @@
+ void
+ iconinit(void)
+ {
+-	background = allocimage(display, Rect(0,0,1,1), RGB24, 1, 0x777777FF);
++	background = display->black;
+ 
+ 	/* greys are multiples of 0x11111100+0xFF, 14* being palest */
+-	cols[BACK] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xFFFFFFFF^reverse);
+-	cols[BORD] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x999999FF^reverse);
+-	cols[TEXT] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x000000FF^reverse);
+-	cols[HTEXT] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x000000FF);
+-	if(!reverse) {
+-		cols[HIGH] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xCCCCCCFF);
+-		titlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DGreygreen);
+-		lighttitlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPalegreygreen);
+-	} else {
+-		cols[HIGH] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPurpleblue);
+-		titlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPurpleblue);
+-		lighttitlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x222222FF);
+-	}
+-	dholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DMedblue);
+-	lightholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DGreyblue);
+-	paleholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DPalegreyblue);
+-	paletextcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x666666FF^reverse);
+-	sizecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DRed);
++	cols[BACK] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DBlack);
++	cols[BORD] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x111111FF);
++	cols[TEXT] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x884400FF);
++	cols[HTEXT] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x884400FF);
++	cols[HIGH] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x440000FF);
++	titlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x440000FF);
++	lighttitlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x111111FF);
++	dholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x666666FF);
++	lightholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x222222FF);
++	paleholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x333333FF);
++	paletextcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x550000FF);
++	sizecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x550000FF);
+ 
+ 	if(reverse == 0)
+ 		holdcol = dholdcol;
--- /dev/null
+++ b/rio-exit
@@ -1,0 +1,16 @@
+rio: always enable kill menu option
+
+diff -r 49bd5e4c9bde sys/src/cmd/rio/rio.c
+--- a/sys/src/cmd/rio/rio.c	Wed Feb 07 18:53:08 2018 +0000
++++ b/sys/src/cmd/rio/rio.c	Thu Feb 15 23:00:00 2018 +0100
+@@ -125,10 +125,6 @@
+ 	Image *i;
+ 	Rectangle r;
+ 
+-	if(strstr(argv[0], ".out") == nil){
+-		menu3str[Exit] = nil;
+-		Hidden--;
+-	}
+ 	initstr = nil;
+ 	kbdin = nil;
+ 	maxtab = 0;
--- /dev/null
+++ b/samterm-cmdrect
@@ -1,0 +1,14 @@
+diff -r 16e46e6fee1e sys/src/cmd/samterm/main.c
+--- a/sys/src/cmd/samterm/main.c	Thu Oct 05 20:33:46 2017 +0200
++++ b/sys/src/cmd/samterm/main.c	Sat Oct 07 06:32:03 2017 +0200
+@@ -42,7 +42,9 @@
+ 	scratch = alloc(100*RUNESIZE);
+ 	nscralloc = 100;
+ 	r = screen->r;
+-	r.max.y = r.min.y+Dy(r)/5;
++	if(Dx(r) > 8*80+FLMARGIN+FLSCROLLWID)
++		r.max.x = r.min.x + 8*80+FLMARGIN+FLSCROLLWID;
++	r.max.y = r.min.y+Dy(r)/6;
+ 	flstart(screen->clipr);
+ 	rinit(&cmd.rasp);
+ 	flnew(&cmd.l[0], gettext, 1, &cmd);
--- /dev/null
+++ b/samterm-col
@@ -1,0 +1,70 @@
+samterm color patch.
+
+diff -r 0dbb01291c38 sys/src/cmd/samterm/flayer.c
+--- a/sys/src/cmd/samterm/flayer.c	Thu Jan 07 15:04:56 2016 -0500
++++ b/sys/src/cmd/samterm/flayer.c	Sun Jan 10 20:31:20 2016 +0100
+@@ -29,18 +29,18 @@
+ 	lDrect = r;
+ 
+ 	/* Main text is yellowish */
+-	maincols[BACK] = allocimagemix(display, DPaleyellow, DWhite);
+-	maincols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow);
+-	maincols[BORD] = allocimage(display, Rect(0,0,2,2), screen->chan, 1, DYellowgreen);
+-	maincols[TEXT] = display->black;
+-	maincols[HTEXT] = display->black;
++	maincols[BACK] = display->black;
++	maincols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x440000FF);
++	maincols[BORD] = allocimage(display, Rect(0,0,2,2), screen->chan, 1, 0x222222FF);
++	maincols[TEXT] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x884400FF);
++	maincols[HTEXT] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x884400FF);
+ 
+ 	/* Command text is blueish */
+-	cmdcols[BACK] = allocimagemix(display, DPalebluegreen, DWhite);
+-	cmdcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreygreen);
+-	cmdcols[BORD] = allocimage(display, Rect(0,0,2,2), screen->chan, 1, DPurpleblue);
+-	cmdcols[TEXT] = display->black;
+-	cmdcols[HTEXT] = display->black;
++	cmdcols[BACK] = display->black;
++	cmdcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x111111FF);
++	cmdcols[BORD] = allocimage(display, Rect(0,0,2,2), screen->chan, 1, 0x440000FF);
++	cmdcols[TEXT] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x770000FF);
++	cmdcols[HTEXT] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x770000FF);
+ }
+ 
+ void
+@@ -89,12 +89,12 @@
+ flclose(Flayer *l)
+ {
+ 	if(l->visible == All)
+-		draw(screen, l->entire, display->white, nil, ZP);
++		draw(screen, l->entire, display->black, nil, ZP);
+ 	else if(l->visible == Some){
+ 		if(l->f.b == 0)
+ 			l->f.b = allocimage(display, l->entire, screen->chan, 0, DNofill);
+ 		if(l->f.b){
+-			draw(l->f.b, l->entire, display->white, nil, ZP);
++			draw(l->f.b, l->entire, display->black, nil, ZP);
+ 			flrefresh(l, l->entire, 0);
+ 		}
+ 	}
+@@ -353,7 +353,7 @@
+ 	if(0 && Dx(dr)==Dx(olDrect) && Dy(dr)==Dy(olDrect))
+ 		move = 1;
+ 	else
+-		draw(screen, lDrect, display->white, nil, ZP);
++		draw(screen, lDrect, display->black, nil, ZP);
+ 	for(i=0; i<nllist; i++){
+ 		l = llist[i];
+ 		l->lastsr = ZR;
+diff -r 0dbb01291c38 sys/src/cmd/samterm/plan9.c
+--- a/sys/src/cmd/samterm/plan9.c	Thu Jan 07 15:04:56 2016 -0500
++++ b/sys/src/cmd/samterm/plan9.c	Sun Jan 10 20:31:20 2016 +0100
+@@ -46,7 +46,7 @@
+ 	if(t != nil)
+ 		maxtab = strtoul(t, nil, 0);
+ 	free(t);
+-	draw(screen, screen->clipr, display->white, nil, ZP);
++	draw(screen, screen->clipr, display->black, nil, ZP);
+ }
+ 
+ int
--- /dev/null
+++ b/samterm-flayers
@@ -1,0 +1,12 @@
+diff -r 12bbf1a60d48 sys/src/cmd/samterm/samterm.h
+--- a/sys/src/cmd/samterm/samterm.h	Thu Jun 01 07:07:55 2017 +0000
++++ b/sys/src/cmd/samterm/samterm.h	Mon Jun 05 15:36:48 2017 +0200
+@@ -3,7 +3,7 @@
+ #define	RUNESIZE	sizeof(Rune)
+ #define	MAXFILES	256
+ #define	READBUFSIZE 8192
+-#define	NL	5
++#define	NL	10
+ 
+ enum{
+ 	Up,
--- /dev/null
+++ b/spewaplay-col
@@ -1,0 +1,75 @@
+diff -Naur a/aplay.c b/aplay.c
+--- a/aplay.c	Sun Feb 14 06:54:35 2016
++++ b/aplay.c	Sun Feb 14 06:54:42 2016
+@@ -85,7 +85,7 @@
+ 	codecargs.pidchan = chancreate(sizeof(ulong), 0);
+ 	waitchan = threadwaitchan();
+ 	initdraw(nil, nil, argv0);
+-	back = allocimagemix(display, DPalebluegreen, DWhite);
++	back = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x441100ff);
+ 	threadcreate(mousethread, &codecargs, STACK);
+ 	kbdargs.exit = chancreate(sizeof(char*), 0);
+ 	kbdargs.codecargs = &codecargs;
+@@ -262,7 +262,8 @@
+ 		r1.max.x = r1.min.x + ((vlong)Dx(r2) * curoff) / maxoff;
+ 	r2.min.x = r1.max.x;
+ 	draw(screen, r1, back, nil, ZP);
+-	draw(screen, r2, display->white, nil, ZP);
++	draw(screen, r2, display->black, nil, ZP);
++	/*
+ 	if(pause)
+ 		strcpy(buf, "pause");
+ 	else
+@@ -273,9 +274,10 @@
+ 	r2 = rectaddpt(r2, subpt(divpt(r1.max, 2), divpt(r2.max, 2)));
+ 	r2 = rectaddpt(r2, screen->r.min);
+ 	r1 = insetrect(r2, -4);
+-	draw(screen, r1, display->white, nil, ZP);
+-	border(screen, insetrect(r1, 1), 2, display->black, ZP);
+-	string(screen, r2.min, display->black, ZP, display->defaultfont, buf);
++	draw(screen, r1, display->black, nil, ZP);
++	border(screen, insetrect(r1, 1), 2, display->white, ZP);
++	string(screen, r2.min, display->white, ZP, display->defaultfont, buf);
++	*/
+ 	flushimage(display, 1);
+ }
+ 
+diff -Naur a/volume.c b/volume.c
+--- a/volume.c	Sun Feb 14 06:55:07 2016
++++ b/volume.c	Sun Feb 14 06:55:10 2016
+@@ -4,8 +4,6 @@
+ #include <event.h>
+ #include <keyboard.h>
+ 
+-char volstr[] = "volume";
+-char mutestr[] = "muted";
+ int volume, muted;
+ Image *back;
+ 
+@@ -21,16 +19,7 @@
+ 	r1.min.y = r1.max.y - ((vlong)Dy(r2) * volume) / 100;
+ 	r2.max.y = r1.min.y;
+ 	draw(screen, r1, back, nil, ZP);
+-	draw(screen, r2, display->white, nil, ZP);
+-	r2.min = ZP;
+-	r2.max = stringsize(display->defaultfont, muted ? mutestr : volstr);
+-	r1 = rectsubpt(screen->r, screen->r.min);
+-	r2 = rectaddpt(r2, subpt(divpt(r1.max, 2), divpt(r2.max, 2)));
+-	r2 = rectaddpt(r2, screen->r.min);
+-	r1 = insetrect(r2, -4);
+-	draw(screen, r1, display->white, nil, ZP);
+-	border(screen, insetrect(r1, 1), 2, display->black, ZP);
+-	string(screen, r2.min, display->black, ZP, display->defaultfont, muted ? mutestr : volstr);
++	draw(screen, r2, display->black, nil, ZP);
+ 	flushimage(display, 1);
+ }
+ 
+@@ -64,7 +53,7 @@
+ 	if(initdraw(0, 0, "volume") < 0)
+ 		sysfatal("initdraw failed: %r");
+ 	einit(Emouse|Ekeyboard);
+-	back = allocimagemix(display, DPalebluegreen, DWhite);
++	back = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x441100ff);
+ 	eresized(0);
+ 	for(;;) switch(event(&e)){
+ 	default:
--- /dev/null
+++ b/spred-col
@@ -1,0 +1,38 @@
+diff -r 4136602b93e9 sys/src/cmd/spred/cmdw.c
+--- a/sys/src/cmd/spred/cmdw.c	Wed Nov 14 11:33:52 2018 -0800
++++ b/sys/src/cmd/spred/cmdw.c	Fri Nov 23 17:25:25 2018 +0100
+@@ -332,9 +332,11 @@
+ 	.rmb = cmdrmb,
+ 	.key = cmdkey,
+ 	.hexcols = {
+-		[BORD] DPurpleblue,
+-		[DISB] 0xCCCCEEFF,
+-		[BACK] 0xCCFFFFFF,
+-		[HIGH] DPalegreygreen
++		[BORD] 0x440000FF,
++		[TEXT] 0x770000FF,
++		[DISB] 0x111111FF,
++		[BACK] 0x000000FF,
++		[HIGH] 0x111111FF,
++		[HTEXT] 0x770000FF,
+ 	}
+ };
+diff -r 4136602b93e9 sys/src/cmd/spred/win.c
+--- a/sys/src/cmd/spred/win.c	Wed Nov 14 11:33:52 2018 -0800
++++ b/sys/src/cmd/spred/win.c	Fri Nov 23 17:25:25 2018 +0100
+@@ -38,6 +38,7 @@
+ 	scr = allocscreen(screen, display->white, 0);
+ 	if(scr == nil)
+ 		sysfatal("allocscreen: %r");
++	draw(screen, screen->r, display->black, nil, ZP);
+ 	for(i = 0; i < NTYPES; i++)
+ 		for(j = 0; j < NCOLS; j++)
+ 			tabs[i]->cols[j] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, tabs[i]->hexcols[j]);
+@@ -254,6 +255,7 @@
+ 	scr = allocscreen(screen, display->white, 0);
+ 	if(scr == nil)
+ 		sysfatal("allocscreen: %r");
++	draw(screen, screen->r, display->black, nil, ZP);
+ 	for(w = wlist.next; w != &wlist; w = w->next){
+ 		r = rectsubpt(w->entire, old.min);
+ 		r.min.x = muldiv(r.min.x, dxn, dxo);
--- /dev/null
+++ b/stats-col
@@ -1,0 +1,89 @@
+diff -r 034743cc4f9f sys/src/cmd/stats.c
+--- a/sys/src/cmd/stats.c	Thu Jun 14 21:54:42 2018 +0200
++++ b/sys/src/cmd/stats.c	Sun Jun 17 07:30:50 2018 +0200
+@@ -208,6 +208,7 @@
+ };
+ 
+ Image	*cols[Ncolor][3];
++Image	*txt;
+ Graph	*graph;
+ Machine	*mach;
+ char	*mysysname;
+@@ -271,30 +272,23 @@
+ }
+ 
+ void
+-mkcol(int i, int c0, int c1, int c2)
++mkcol(int i, int c0, int c1)
+ {
+-	cols[i][0] = allocimagemix(display, c0, DWhite);
+-	cols[i][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, c1);
+-	cols[i][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, c2);
++	cols[i][0] = allocimagemix(display, c0, DBlack);
++	cols[i][1] = allocimagemix(display, 0x773300FF, c1);
++	cols[i][2] = allocimage(display, Rect(0,0,1,1), RGB24, 1, c1);
+ }
+ 
+ void
+ colinit(void)
+ {
+-	/* Peach */
+-	mkcol(0, 0xFFAAAAFF, 0xFFAAAAFF, 0xBB5D5DFF);
+-	/* Aqua */
+-	mkcol(1, DPalebluegreen, DPalegreygreen, DPurpleblue);
+-	/* Yellow */
+-	mkcol(2, DPaleyellow, DDarkyellow, DYellowgreen);
+-	/* Green */
+-	mkcol(3, DPalegreen, DMedgreen, DDarkgreen);
+-	/* Blue */
+-	mkcol(4, 0x00AAFFFF, 0x00AAFFFF, 0x0088CCFF);
+-	/* Grey */
+-	cols[5][0] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xEEEEEEFF);
+-	cols[5][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xCCCCCCFF);
+-	cols[5][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x888888FF);
++	mkcol(0, 0x994400FF, 0x442200FF);
++	mkcol(1, 0x993C00FF, 0x441F00FF);
++	mkcol(2, 0x993300FF, 0x441C00FF);
++	mkcol(3, 0x992200FF, 0x441800FF);
++	mkcol(4, 0x991100FF, 0x441400FF);
++	mkcol(5, 0x990000FF, 0x441100FF);
++	txt = allocimage(display, Rect(0,0,1,1), RGB24, 1, 0x441100FF);
+ }
+ 
+ int
+@@ -437,7 +431,7 @@
+ 		g->overflow = 1;
+ 		draw(g->overtmp, g->overtmp->r, screen, nil, g->overtmp->r.min);
+ 		sprint(buf, "%llud", v);
+-		string(screen, g->overtmp->r.min, display->black, ZP, font, buf);
++		string(screen, g->overtmp->r.min, txt, ZP, font, buf);
+ 	}
+ }
+ 
+@@ -1094,7 +1088,7 @@
+ 	uvlong v, vmax;
+ 	char buf[128], labs[Nlab][Lablen];
+ 
+-	draw(screen, screen->r, display->white, nil, ZP);
++	draw(screen, screen->r, display->black, nil, ZP);
+ 
+ 	/* label left edge */
+ 	x = screen->r.min.x;
+@@ -1123,7 +1117,7 @@
+ 			snprint(buf, sizeof buf, "%.*s(%d)", j, mach[i].shortname, n);
+ 		}else
+ 			snprint(buf, sizeof buf, "%.*s", j, mach[i].shortname);
+-		string(screen, Pt(x+Labspace, screen->r.min.y + Labspace), display->black, ZP, font, buf);
++		string(screen, Pt(x+Labspace, screen->r.min.y + Labspace), txt, ZP, font, buf);
+ 	}
+ 
+ 	maxx = screen->r.max.x;
+@@ -1148,7 +1142,7 @@
+ 					ly = y + (dy*(nlab-k)/(nlab+1));
+ 					draw(screen, Rect(maxx+1, ly, maxx+1+Lx, ly+1), display->black, nil, ZP);
+ 					ly -= font->height/2;
+-					string(screen, Pt(maxx+1+Lx, ly), display->black, ZP, font, labs[k]);
++					string(screen, Pt(maxx+1+Lx, ly), txt, ZP, font, labs[k]);
+ 				}
+ 			}
+ 		}
--- /dev/null
+++ b/statusbar-col
@@ -1,0 +1,16 @@
+diff -r b048db97b5ea sys/src/cmd/aux/statusbar.c
+--- a/sys/src/cmd/aux/statusbar.c	Fri Jan 04 02:51:29 2019 +0100
++++ b/sys/src/cmd/aux/statusbar.c	Mon Jan 07 13:56:09 2019 +0100
+@@ -18,9 +18,9 @@
+ void
+ initcolor(void)
+ {
+-	text = display->black;
+-	light = allocimagemix(display, DPalegreen, DWhite);
+-	dark = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DDarkgreen);
++	text = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x440000FF);
++	light = display->black;
++	dark = text;
+ }
+ 
+ Rectangle rbar;
--- /dev/null
+++ b/statusmsg-col
@@ -1,0 +1,14 @@
+diff -r c392f6b2122c sys/src/cmd/aux/statusmsg.c
+--- a/sys/src/cmd/aux/statusmsg.c	Mon Feb 15 17:30:56 2016 +0100
++++ b/sys/src/cmd/aux/statusmsg.c	Thu Feb 18 06:06:21 2016 +0100
+@@ -19,8 +19,8 @@
+ void
+ initcolor(void)
+ {
+-	text = display->black;
+-	light = allocimagemix(display, DPalegreen, DWhite);
++	text = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x440000FF);
++	light = display->black;
+ }
+ 
+ void
--- /dev/null
+++ b/sudoku-col
@@ -1,0 +1,75 @@
+diff -r 3b33f587ad27 sys/src/games/sudoku/sudoku.c
+--- a/sys/src/games/sudoku/sudoku.c	Wed Oct 03 00:53:10 2018 +0200
++++ b/sys/src/games/sudoku/sudoku.c	Sat Oct 06 16:12:35 2018 +0200
+@@ -14,10 +14,10 @@
+ 
+ Image *background;	/* DPaleyellow */
+ Image *backselect;	/* DPalebluegreen */
+-Image *blink;		/* DDarkyellow */
+ Image *brdr;		/* 0x55555555 */
+ Image *fixed;		/* DBlue */
+ Image *wrong;		/* DRed */
++Image *normal;
+ Image *dig[10];		/* digit masks */
+ 
+ Dir *dir;
+@@ -200,7 +200,7 @@
+ 	int i;
+ 
+ 	for(i = 0; i < Psize; i++) {
+-		drawcell(i / Brdsize, i % Brdsize, brd[i].digit, brd[i].locked ? fixed : display->black);
++		drawcell(i / Brdsize, i % Brdsize, brd[i].digit, brd[i].locked ? fixed : normal);
+ 	}
+ }
+ 
+@@ -214,7 +214,7 @@
+ 			drawcell(i / Brdsize, i % Brdsize, brd[i].digit, fixed);
+ 		else 
+ 			drawcell(i / Brdsize, i % Brdsize, brd[i].digit, 
+-					checkpossible(brd, i / Brdsize, i % Brdsize, brd[i].digit) ? display->black : wrong);
++					checkpossible(brd, i / Brdsize, i % Brdsize, brd[i].digit) ? normal : wrong);
+ 	}
+ }
+ 
+@@ -224,7 +224,7 @@
+ 	Point l1, l2;
+ 	int i;
+ 
+-	draw(screen, screen->r, brdr, nil, ZP);
++	draw(screen, screen->r, display->black, nil, ZP);
+ 	draw(screen, insetrect(screen->r, Border), background, nil, ZP);
+ 	for(i = 0; i < Brdsize; i++) {
+ 		l1 = addpt(screen->r.min, Pt(i*Square, Square));
+@@ -253,7 +253,7 @@
+ 	r = insetrect(r, Border);
+ 	r.max = addpt(r.max, Pt(2, 2));
+ 	draw(screen, rectaddpt(r, screen->r.min), selected ? backselect : background, nil, ZP);
+-	draw(screen, rectaddpt(r, screen->r.min), display->black, dig[digit-1], ZP);
++	draw(screen, rectaddpt(r, screen->r.min), normal, dig[digit-1], ZP);
+ }
+ 
+ void
+@@ -267,17 +267,17 @@
+ 		sysfatal("can't reattach to window");
+ 	
+ 	if(background == nil) 
+-		background = eallocimage(Rect(0, 0, 1, 1), 1, DPaleyellow);
++		background = display->black;
+ 	if(backselect == nil) 
+-		backselect = eallocimage(Rect(0, 0, 1, 1), 1, DPalebluegreen);
+-	if(blink == nil) 
+-		blink = eallocimage(Rect(0, 0, 1, 1), 1, DDarkyellow);
++		backselect = eallocimage(Rect(0, 0, 1, 1), 1, 0x440000FF);
+ 	if(brdr == nil)
+-		brdr = eallocimage(Rect(0, 0, 1, 1), 1, 0x55555555);
++		brdr = eallocimage(Rect(0, 0, 1, 1), 1, 0x222222FF);
+ 	if(fixed == nil)
+-		fixed = eallocimage(Rect(0, 0, 1, 1), 1, DBlue);
++		fixed = eallocimage(Rect(0, 0, 1, 1), 1, 0x550000FF);
+ 	if(wrong == nil)
+ 		wrong = eallocimage(Rect(0, 0, 1, 1), 1, DRed);
++	if(normal == nil)
++		normal = eallocimage(Rect(0, 0, 1, 1), 1, 0x884400FF);
+ 	if(dig[0] == nil) {
+ 		for(i = 0; i < 9; i++) {
+ 			snprint(path, 256, "%s/%d.bit", imgdir, i+1);
--- /dev/null
+++ b/troff-nchars
@@ -1,0 +1,18 @@
+troff: inflate maximum size of character set
+
+this enables one to use fonts like unifont with troff.
+
+don't use fonts like unifont with troff.
+
+diff -r 79938a709ae3 sys/src/cmd/troff/tdef.h
+--- a/sys/src/cmd/troff/tdef.h	Wed Jan 30 18:22:52 2019 +0100
++++ b/sys/src/cmd/troff/tdef.h	Mon Feb 11 10:44:11 2019 +0100
+@@ -190,7 +190,7 @@
+ 		unnamed ones and \N's)
+ 	*/
+ 
+-#define	NCHARS	(8*1024)	/* maximum size of troff character set*/
++#define	NCHARS	(64*1024)	/* maximum size of troff character set*/
+ 
+ 
+ 	/* However for nroff you want only :
--- /dev/null
+++ b/vt-col
@@ -1,0 +1,12 @@
+diff -r eecec6d3b341 sys/src/cmd/vt/main.c
+--- a/sys/src/cmd/vt/main.c	Mon Sep 03 20:54:26 2018 +0200
++++ b/sys/src/cmd/vt/main.c	Mon Sep 03 19:11:39 2018 +0200
+@@ -326,7 +326,7 @@
+ 			rgbahicolors[i]);
+ 	}
+ 	bgcolor = (blkbg? display->black: display->white);
+-	fgcolor = (blkbg? display->white: display->black);
++	fgcolor = (blkbg? allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0x884400FF): display->black);
+ 	bgselected = allocimage(display, Rect(0,0,1,1), CMAP8, 1, blkbg ? 0x333333FF : 0xCCCCCCFF);
+ 	fgselected = allocimage(display, Rect(0,0,1,1), CMAP8, 1, blkbg ? 0xCCCCCCFF : 0x333333FF);;
+ 	resize();