shithub: scripts

Download patch

ref: a4c1f8359d8e4aadf881585ecb137cf6d4904ce2
author: kitzman <[email protected]>
date: Fri Nov 17 11:15:00 EST 2023

first commit

diff: cannot open b/bin/rc//null: file does not exist: 'b/bin/rc//null' diff: cannot open b/bin//null: file does not exist: 'b/bin//null' diff: cannot open b/lib/pico//null: file does not exist: 'b/lib/pico//null' diff: cannot open b/lib/theme//null: file does not exist: 'b/lib/theme//null' diff: cannot open b/lib//null: file does not exist: 'b/lib//null' diff: cannot open b/patches//null: file does not exist: 'b/patches//null'
--- /dev/null
+++ b/bin/rc/chatcat
@@ -1,0 +1,6 @@
+#!/bin/rc
+chan=$1
+label $chan
+echo JOIN $nick to chat >>/n/chat.9p.zone/$chan
+cat /n/chat.9p.zone/$chan &
+while() cat | sed '1s/^/'$nick' → /' >>/n/chat.9p.zone/$chan
--- /dev/null
+++ b/bin/rc/gemhist
@@ -1,0 +1,6 @@
+#!/bin/rc
+
+while(){
+	syscall -o read 0 buf 512 </mnt/plumb/gemini >[2]/dev/null | tail -1
+	echo ''
+}
--- /dev/null
+++ b/bin/rc/gfetch
@@ -1,0 +1,80 @@
+#!/bin/rc
+# Warning, parts of this script have been STOLEN!
+
+# TODO:
+# fs usage
+# kernel
+
+fn storage {
+	disks = `{ls -d /dev/sd* | uniq}
+	anydisk = 'false'
+	for(i in $disks){
+		if(test -f $i/data){
+			full = `{ls -l $i/data}
+			disk = `{echo $full | awk -F '/' '{print $3}'}
+			size = `{echo $full | awk '{print $6}'}
+			echo $disk^': '^`{echo $size^' / 1024 / 1024 /1024' | bc}^GB
+			anydisk = 'true'
+		}
+	}
+	if(~ $anydisk -c 'false')
+		echo Running in the diskless
+}
+
+fn fs {
+	if(ls /srv | grep -s '*.cmd')
+		ls /srv/*.cmd | sed 's/.cmd//g' | sed 's/\/srv\///g'
+	if(test -f /srv/fscons)
+		echo fossil
+	# Logic from storage to scrape for venti arenas
+	v='false'
+	disks = `{ls -d /dev/sd* | uniq}
+	for(i in $disks){
+		if(ls $i | grep -s arena)
+			v='true'
+	}
+	if(~ $v -c 'true')
+		echo venti
+}
+
+fn cpuid {
+	if(test -f /bin/aux/cpuid)
+		aux/cpuid >[2]/dev/null | grep procname | sed 's/.*procname//'
+}
+
+os=`{
+	if(grep -s 'zrv' /dev/drivers)
+		echo 9ants
+	if not if(grep -s 'vmx' /dev/drivers)
+		echo 9front
+	if not
+		echo Bell Labs
+}
+
+# fs and storage require #S bound on /dev 
+bind -b '#S' /dev
+
+arch=`{echo $cputype}
+shell=/bin/rc
+uptime=`{uptime| sed 's/.*up//; s/..........$//'}
+scr=`{dd -count 1 < /dev/screen|[2];}
+scr=($scr(4) x $scr(5))
+ram=(`{tr / ' '</dev/swap})
+free=`{echo $ram(1)'/1024^2'|bc}
+used=`{echo '('$ram(3)'*('$ram(7)^+$ram(5)^'))/1024^2'|bc}
+cpu=`{cpuid}
+files=`{fs}
+strg=`{storage}
+
+cat <<EOF
+             $user@$sysname
+    (\(\     -----------
+   j". ..    os: Plan 9 from $os/$arch
+   (  . .)   shell: $shell
+   |   ° ¡   uptime: $uptime
+   ¿     ;   ram: $used/$free MiB
+   c?".UJ    cpu: $cputype $cpu
+             resolution: $scr
+             fs: $files
+             $strg
+EOF
--- /dev/null
+++ b/bin/rc/gridup
@@ -1,0 +1,15 @@
+#!/bin/rc
+
+srv tcp!registry.9p.zone!6675 registry /n/registry
+srv tcp!chat.9p.zone!9990 9p.zone.chat /n/chat.9p.zone
+# srv tcp!oat.nine.sirjofri.de!564 sirjofripub /n/sirjofripub
+srv -c tcp!9p.zone!9991 9p.zone.disk /n/griddisk
+
+fn clean {
+	echo $1 | sed 's/.*!([^!]+)!.*/\1/'
+}
+
+for(s in /n/registry/tcp*){
+	s=`{basename $s}
+	srv $s `{clean $s} /n/^`{clean $s} &
+}
--- /dev/null
+++ b/bin/rc/inferno
@@ -1,0 +1,40 @@
+#!/bin/rc
+
+rfork ne
+
+ROOT=/sys/inferno
+
+main=4M
+image=8M
+heap=8M
+
+# font=/fonts/pelm/unicode.8.font
+font=/fonts/lucidasans/typeunicode.7.font
+
+if(! ~ $#* 0 && ! ~ $#* 3) {
+	echo 'usage: inferno [main image heap]'
+	exit
+}
+
+if(~ $#* 3) {
+	main=$*(1)
+	image=$*(2)
+	heap=$*(3)
+}
+
+if (! test -d /sys/inferno/usr) {
+	echo '/sys/inferno does not exist'
+	exit
+}
+
+unionfs -m /n/iusr /usr $ROOT/usr
+bind -c /n/iusr $ROOT/usr
+bind -c $ROOT/usr/$user/lib/keydb.inferno \
+		$ROOT/keydb
+bind -c $home/lib/ndb.inferno /sys/inferno/lib/ndb
+# bind -a /net /n/inferno/net
+# bind -a /dev /n/inferno/dev
+
+cd /sys/inferno
+Plan9/arm/bin/emu '-pmain='$main '-pimage='$image '-pheap='$heap \
+	-g800x1024 -c1 -f$font
--- /dev/null
+++ b/bin/rc/librarysrv
@@ -1,0 +1,59 @@
+#!/bin/rc
+
+librarysrv=/n/santiago/srv/library
+libraryrmtpt=/n/libraryroot
+librarymtpt=/n/library
+cachemtpt=/n/ramfs.library
+
+cmd=$1
+switch($cmd) {
+case start
+	echo 'starting cached library'
+case halt
+	echo 'halting cached library'
+	for(mtpt in $librarymtpt $libraryrmtpt $cachemtpt) {
+		echo 'unmounting' $mtpt
+		unmount $mtpt
+	}
+
+	for(srvf in /srv/library.cached /srv/ramfs.library) {
+		echo 'removing' $srvf
+		rm $srvf
+	}
+
+	echo 'done halting; you may kill ramfs and cfs now'
+	exit
+case *
+	echo 'usage: librarysrv start|halt'
+	exit
+}
+
+#cachesize=8192		# 32
+cachesize=16384		# 64M
+
+if(! test -f $librarysrv) {
+	echo 'library server' $librarysrv 'not found'
+	exit
+}
+
+if(! test -f /srv/ramfs.library) {
+	echo 'running ramfs'
+	ramfs -S ramfs.library -m $cachemtpt
+}
+if not {
+	mount /srv/ramfs.library $cachemtpt
+}
+
+if(! test -f $cachemtpt^'/cache') {
+	echo 'creating cache:' $cachesize 'blocks'
+	dd -if /dev/zero -of $cachemtpt^'/cache' -bs 4096 -count $cachesize
+}
+
+echo 'running cfs'
+cfs -S -n -r -f $cachemtpt^'/cache' -F $librarysrv $libraryrmtpt
+
+mkdir /tmp/library
+
+libraryc=`{ls `{ls $libraryrmtpt | tail -n 1} | tail -n 1}
+
+unionfs -m $librarymtpt -s library.cached /tmp/library $libraryc $librarymtpt
--- /dev/null
+++ b/bin/rc/themeswitch
@@ -1,0 +1,39 @@
+#!/bin/rc
+
+switch($themespec) {
+case green
+	font=/lib/font/bit/uni-vga/u_vga16.font
+	# font=/lib/font/bit/zevv-peep/zevv-peep-14.font
+	# font=/lib/font/bit/spleen/spleen.12.font
+	# font=/lib/font/bit/tamzen/tamzen14.font
+	themepath=$home/lib/theme/green.theme
+case blit
+	font=/lib/font/bit/fixed/unicode.9x18B.font
+	font=/lib/font/bit/fixed/unicode.9x15B.font
+	themepath=$home/lib/theme/blit.theme
+case orange
+	font=/lib/font/bit/zevv-peep/zevv-peep-14.font
+	font=/lib/font/bit/tamzen/tamzen16.font
+	themepath=$home/lib/theme/orange.theme
+case gruvbox
+	font=/lib/font/bit/uni-vga/u_vga16.font
+	themepath=$home/lib/theme/gruvbox.theme
+case gruvboxl
+	font=/lib/font/bit/uni-vga/u_vga16.font
+	themepath=$home/lib/theme/gruvboxl.theme
+case white
+	font=/lib/font/bit/lucm/unicode.9.font
+	font=/lib/font/bit/lucidasans/typeunicode.7.font
+	# font=/lib/font/bit/pelm/unicode.8.font
+	themepath=$home/lib/theme/white.theme
+case greeng
+	font=/lib/font/bit/zevv-peep/zevv-peep-16.font
+	themepath=$home/lib/theme/greengold.theme
+case nord
+	font=/lib/font/bit/pelm/unicode.8.font
+	themepath=$home/lib/theme/nord.theme
+}
+
+if(test -f /dev/theme) {
+	cat $themepath >/dev/theme
+}
--- /dev/null
+++ b/bin/rc/txcheck
@@ -1,0 +1,81 @@
+#!/bin/rc
+
+flagfmt='r procroot, u unit, m maxsize, D delay'
+args='txpid txfd'
+
+# variables and functions
+
+txpid=()
+txfd=()
+
+pidfile=()
+delay=()
+fdpath=()
+maxsz=()
+unitfrac=1
+
+fn usage {
+	aux/usage
+	exit usage
+}
+
+fn fatal {
+	echo $* >[1=2]
+	exit $"*
+}
+
+fn isnum {
+	echo $1 | grep -s '^[0-9]+$'
+}
+
+fn isfloat {
+	echo $1 | grep -s '^[0-9]+\.[0-9]+$'
+}
+
+# argument parsing
+
+if(! eval `{aux/getflags -r /proc -u b -D 3 $*})
+	usage
+
+txpid=$1
+txfd=$2
+
+if(~ $#txpid 0 || ~ $#txfd 0)
+	usage
+
+# util and sanity checks
+
+fdpath=$flagr^'/'^$txpid^'/fd'
+if(! test -r $fdpath)
+	fatal 'unable to read' $fdpath
+
+switch($flagu) {
+case b or B
+	unitfrac='1'
+case kb or KB
+	unitfrac='1024'
+case mb or MB
+	unitfrac='1024 * 1024'
+case *
+	if(! isnum $flagu)
+		fatal 'unknown unit or not a number'
+	unitfrac=$flagu
+}
+
+if(! isnum $flagD)
+	fatal 'delay is not a number'
+delay=$flagD
+
+if(! isfloat $flagm)
+	fatal 'maximum size is not a float'
+maxsz=$flagm
+
+pidfile=/tmp/txcheck.$txpid
+touch $pidfile
+
+while(test -f $pidfile) {
+	fdamt=`{cat $fdpath | grep '^  '^$txfd | awk '{print $9}'}
+	amt=`{echo 'scale = 2;' $fdamt '/ (' $unitfrac ')' | bc}
+	echo ($amt $maxsz)
+	sleep $delay
+}
--- /dev/null
+++ b/bin/rc/vtb
@@ -1,0 +1,6 @@
+#!/bin/rc
+
+# vtfont=/lib/font/bit/fixed/unicode.9x18B.font
+vtfont=/lib/font/bit/fixed/unicode.9x15B.font
+
+exec vt.green -bcx -f $vtfont $*
--- /dev/null
+++ b/lib/pico/gruvboxl.pico
@@ -1,0 +1,4 @@
+!f somefile? img
+!s 1920 1080
+
+z == 0 && sqrt(pow(x - 344, 2) + pow(y - 764, 2)) < 192 ? 142 : z == 1 && sqrt(pow(x - 344, 2) + pow(y - 764, 2)) < 192 ? 192 : z == 2 && sqrt(pow(x - 344, 2) + pow(y - 764, 2)) < 192 ? 124 : z == 0 && sqrt(pow(x - 760, 2) + pow(y - 764, 2)) < 192 ? 131 : z == 1 && sqrt(pow(x - 760, 2) + pow(y - 764, 2)) < 192 ? 165 : z == 2 && sqrt(pow(x - 760, 2) + pow(y - 764, 2)) < 192 ? 152 : z == 0 && sqrt(pow(x - 1176, 2) + pow(y - 764, 2)) < 192 ? 250 : z == 1 && sqrt(pow(x - 1176, 2) + pow(y - 764, 2)) < 192 ? 189 : z == 2 && sqrt(pow(x - 1176, 2) + pow(y - 764, 2)) < 192 ? 47 : z == 0 && sqrt(pow(x - 1592, 2) + pow(y - 764, 2)) < 192 ? 251 : z == 1 && sqrt(pow(x - 1592, 2) + pow(y - 764, 2)) < 192 ? 73 : z == 2 && sqrt(pow(x - 1592, 2) + pow(y - 764, 2)) < 192 ? 52 : z == 0 && sqrt(pow(x - 344, 2) + pow(y - 316, 2)) < 192 ? 251 : z == 1 && sqrt(pow(x - 344, 2) + pow(y - 316, 2)) < 192 ? 73 : z == 2 && sqrt(pow(x - 344, 2) + pow(y - 316, 2)) < 192 ? 52 : z == 0 && sqrt(pow(x - 760, 2) + pow(y - 316, 2)) < 192 ? 250 : z == 1 && sqrt(pow(x - 760, 2) + pow(y - 316, 2)) < 192 ? 189 : z == 2 && sqrt(pow(x - 760, 2) + pow(y - 316, 2)) < 192 ? 47 : z == 0 && sqrt(pow(x - 1176, 2) + pow(y - 316, 2)) < 192 ? 131 : z == 1 && sqrt(pow(x - 1176, 2) + pow(y - 316, 2)) < 192 ? 165 : z == 2 && sqrt(pow(x - 1176, 2) + pow(y - 316, 2)) < 192 ? 152 : z == 0 && sqrt(pow(x - 1592, 2) + pow(y - 316, 2)) < 192 ? 142 : z == 1 && sqrt(pow(x - 1592, 2) + pow(y - 316, 2)) < 192 ? 192 : z == 2 && sqrt(pow(x - 1592, 2) + pow(y - 316, 2)) < 192 ? 124 : z == 0 ? 235 : z == 1 ? 219 : z == 2 ? 178 : 0
--- /dev/null
+++ b/lib/theme/black.theme
@@ -1,0 +1,20 @@
+rioback 101010
+back    000000
+high    bdae93
+border  1d2021
+text    ffffff
+htext   1d2021
+title   d65d0e
+ltitle  1d2021
+hold    458588
+lhold   83a598
+palehold        83a598
+paletext        ffffff
+size    cc241d
+menubar 8b8d8d
+menuback        000000
+menuhigh        fbf1c7
+menubord        d65d0e
+menutext        ffffff
+menuhtext       000000
+
--- /dev/null
+++ b/lib/theme/blit.theme
@@ -1,0 +1,19 @@
+rioback	00100c
+back	000000
+high	00ee1a
+border	d0d0d0
+text	00ee1a
+htext	000000
+title	00ee1a
+ltitle	00551a
+hold	ee0000
+lhold	991000
+palehold	991000
+paletext	00ee1a
+size	f1f1f1
+menubar	448844
+menuback	000000
+menuhigh	ffffff
+menubord	00ee1a
+menutext	00ee1a
+menuhtext	000000
--- /dev/null
+++ b/lib/theme/green.theme
@@ -1,0 +1,19 @@
+rioback	/usr/kitzman/lib/wallpapers/green2.wallpaper
+back	000000
+high	ffffff
+border	d0d0d0
+text	f1f1f1
+htext	000000
+title	00ee1a
+ltitle	00551a
+hold	005dbb
+lhold	000099
+palehold	0943ff
+paletext	6f6f6f
+size	f1f1f1
+menubar	448844
+menuback	000000
+menuhigh	ffffff
+menubord	00ee1a
+menutext	f1f1f1
+menuhtext	000000
--- /dev/null
+++ b/lib/theme/greengold.theme
@@ -1,0 +1,19 @@
+rioback	/usr/kitzman/lib/wallpapers/green1.wallpaper
+back	0A2018
+high	ffffff
+border	d0d0d0
+text	f1f1f1
+htext	000000
+title	FF7030
+ltitle	E8A060
+hold	C81018
+lhold	804040
+palehold	804040
+paletext	6f6f6f
+size	1b5dbb
+menubar	448844
+menuback	0A2018
+menuhigh	ffffff
+menubord	FF7838
+menutext	f1f1f1
+menuhtext	000000
--- /dev/null
+++ b/lib/theme/gruvbox.theme
@@ -1,0 +1,19 @@
+rioback /usr/kitzman/lib/wallpapers/gruvbox.wallpaper
+back	1d2021
+high	ebdbb2
+border	3c3836
+text	fbf1c7
+htext	000000
+title	8ec07c
+ltitle	83a598
+hold	fb4934
+lhold	fabd2f
+palehold	fabd2f
+paletext	665c54
+size	d3869b
+menubar	000000
+menuback	1d2021
+menuhigh	000000
+menubord	8fbcbb
+menutext	fbf1c7
+menuhtext	ffffff
--- /dev/null
+++ b/lib/theme/gruvboxl.theme
@@ -1,0 +1,19 @@
+rioback	/usr/kitzman/lib/wallpapers/gruvboxl.wallpaper
+back	ebdbb2
+high	1d2021
+border	3c3836
+text	1d2021
+htext	ebdbb2
+title	8ec07c
+ltitle	83a598
+hold	fb4934
+lhold	fabd2f
+palehold	fabd2f
+paletext	665c54
+size	d3869b
+menubar	000000
+menuback	ebdbb2
+menuhigh	000000
+menubord	8fbcbb
+menutext	1d2021
+menuhtext	ffffff
--- /dev/null
+++ b/lib/theme/nord.theme
@@ -1,0 +1,19 @@
+rioback	/usr/kitzman/lib/wallpapers/nord.wallpaper
+back	2e3440
+high	d8dee9
+border	222222
+text	eceff4
+htext	000000
+title	8fbcbb
+ltitle	4c566a
+hold	bf616a
+lhold	d08770
+palehold	d08770
+paletext	838c9e
+size	ebcb8b
+menubar	000000
+menuback	2e3440
+menuhigh	000000
+menubord	8fbcbb
+menutext	d8dee9
+menuhtext	ffffff
--- /dev/null
+++ b/lib/theme/orange.theme
@@ -1,0 +1,19 @@
+rioback	1d2021
+back	1d2021
+high	ebdbb2
+border	222222
+text	fbf1c7
+htext	000000
+title	fb4934
+ltitle	fabd2f
+hold	8ec07c
+lhold	83a598
+palehold	83a598
+paletext	665c54
+size	d3869b
+menubar	000000
+menuback	1d2021
+menuhigh	000000
+menubord	fb4934
+menutext	fbf1c7
+menuhtext	ffffff
--- /dev/null
+++ b/lib/theme/plateau.theme
@@ -1,0 +1,16 @@
+back 1b1818
+high e7dfdf
+border 292424
+text f4ecec
+htext 1b1818
+title 5485b6
+ltitle 7272ca
+hold ca4949
+lhold a06e3b
+palehold a06e3b
+paletext 655d5d
+size 8464c4
+menuback 1b1818
+menubord 5485b6
+menutext f4ecec
+rioback 1b1818
--- /dev/null
+++ b/lib/theme/white.theme
@@ -1,0 +1,19 @@
+rioback		/usr/kitzman/lib/wallpapers/white.wallpaper
+back		ffffff
+high		88ccaa
+border		222222
+text		000000
+htext		000000
+title		555555
+ltitle		888888
+hold		007dee
+lhold		557dee
+palehold	4998dd
+paletext	555555
+size		ff0000
+menubar		000000
+menuback	ffffff
+menuhigh	000000
+menubord	555555
+menutext	000000
+menuhtext	ffffff
--- /dev/null
+++ b/patches/faces.patch
@@ -1,0 +1,195 @@
+diff a/main.c b/main.c
+60a61
+> Image	*txtc;		/* text color */
+81a83,179
+> static char *
+> readall(int f, int *osz)
+> {
+> 	int bufsz, sz, n;
+> 	char *s;
+> 
+> 	bufsz = 1023;
+> 	s = nil;
+> 	for(sz = 0;; sz += n){
+> 		if(bufsz-sz < 1024){
+> 			bufsz *= 2;
+> 			s = realloc(s, bufsz);
+> 		}
+> 		if((n = readn(f, s+sz, bufsz-sz-1)) < 1)
+> 			break;
+> 	}
+> 	if(n < 0 || sz < 1){
+> 		free(s);
+> 		return nil;
+> 	}
+> 	s[sz] = 0;
+> 	*osz = sz;
+> 
+> 	return s;
+> }
+> 
+> enum {
+> 	Colrioback,
+> 
+> 	/* the following group has to be in order, they are used by libframe */
+> 	Colback,
+> 	Colhigh,
+> 	Colbord,
+> 	Coltext,
+> 	Colhtext,
+> 
+> 	Coltitle,
+> 	Colltitle,
+> 	Colhold,
+> 	Collhold,
+> 	Colpalehold,
+> 	Colpaletext,
+> 	Colsize,
+> 
+> 	/* menuhit */
+> 	Colmenubar,
+> 	Colmenuback,
+> 	Colmenuhigh,
+> 	Colmenubord,
+> 	Colmenutext,
+> 	Colmenuhtext,
+> 
+> 	Numcolors
+> };
+> 
+> typedef struct Color Color;
+> 
+> struct Color {
+> 	char *id;
+> 	union {
+> 		u32int rgb;
+> 		char *path;
+> 	};
+> 	int flags;
+> };
+> 
+> static Color theme[Numcolors] = {
+> 	[Colrioback]   = {"rioback",   {0x777777}, 0},
+> 	[Colback]      = {"back",      {0xffffff}, 0},
+> 	[Colhigh]      = {"high",      {0xcccccc}, 0},
+> 	[Colbord]      = {"border",    {0x999999}, 0},
+> 	[Coltext]      = {"text",      {DBlack>>8}, 0},
+> 	[Colhtext]     = {"htext",     {DBlack>>8}, 0},
+> 	[Coltitle]     = {"title",     {DGreygreen>>8}, 0},
+> 	[Colltitle]    = {"ltitle",    {DPalegreygreen>>8}, 0},
+> 	[Colhold]      = {"hold",      {DMedblue>>8}, 0},
+> 	[Collhold]     = {"lhold",     {DGreyblue>>8}, 0},
+> 	[Colpalehold]  = {"palehold",  {DPalegreyblue>>8}, 0},
+> 	[Colpaletext]  = {"paletext",  {0x666666}, 0},
+> 	[Colsize]      = {"size",      {DRed>>8}, 0},
+> 	[Colmenubar]   = {"menubar",   {DDarkgreen>>8}, 1},
+> 	[Colmenuback]  = {"menuback",  {0xeaffea}, 1},
+> 	[Colmenuhigh]  = {"menuhigh",  {DDarkgreen>>8}, 1},
+> 	[Colmenubord]  = {"menubord",  {DMedgreen>>8}, 1},
+> 	[Colmenutext]  = {"menutext",  {DBlack>>8}, 1},
+> 	[Colmenuhtext] = {"menuhtext", {0xeaffea}, 1},
+> };
+> 
+> u32int
+> color2rgb(Color c, int scale)
+> {
+> 	return ((((c.rgb >> 16) & 0xff) / scale) << 24) |
+> 			((((c.rgb >> 8) & 0xff) / scale) << 16) |
+> 			(((c.rgb & 0xff) / scale) << 8) |
+> 			0xff;
+> }
+> 
+82a181,228
+> themeload(char *s, int n)
+> {
+> 	int i;
+> 	char *t, *a[2], *e;
+> 	Image *newc;
+> 	u32int rgb;
+> 
+> 	if((t = malloc(n+1)) == nil)
+> 		return;
+> 	memmove(t, s, n);
+> 	t[n] = 0;
+> 
+> 	for(s = t; s != nil && *s; s = e){
+> 		if((e = strchr(s, '\n')) != nil)
+> 			*e++ = 0;
+> 		if(tokenize(s, a, 2) == 2){
+> 			for(i = 0; i < nelem(theme); i++) {
+> 				if(strcmp(theme[i].id, a[0]) == 0) {
+> 					rgb = strtoul(a[1], nil, 16);
+> 					if((newc = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1, rgb<<8 | 0xff)) != nil) {
+> 						theme[i].rgb = rgb;
+> 					}
+> 					break;
+> 				}
+> 			}
+> 		}
+> 	}
+> 	free(t);
+> }
+> 
+> char *
+> themestring(int *n)
+> {
+> 	char *s, *t, *e;
+> 	int i;
+> 
+> 	if((t = malloc(512)) != nil){
+> 		s = t;
+> 		e = s+512;
+> 		for(i = 0; i < nelem(theme); i++)
+> 			s = seprint(s, e, "%s\t%06ux\n", theme[i].id, theme[i].rgb);
+> 		*n = s - t;
+> 	}
+> 
+> 	return t;
+> }
+> 
+> void
+99a246,256
+> 	// load theme
+> 	int sz, f;
+> 	char *s;
+> 
+> 	if((f = open("/dev/theme", OREAD|OCEXEC)) >= 0){
+> 		if((s = readall(f, &sz)) != nil)
+> 			themeload(s, sz);
+> 		free(s);
+> 		close(f);
+> 	}
+> 
+101,104c258,263
+< 	bgrnd = allocimagemix(display, DPalebluegreen, DWhite);
+< 	blue = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x008888FF);	/* blue-green */
+< 	left = allocimage(display, leftright, GREY1, 0, DWhite);
+< 	right = allocimage(display, leftright, GREY1, 0, DWhite);
+---
+> 	bgrnd = allocimagemix(display, color2rgb(theme[Colback], 1), color2rgb(theme[Colback], 1));
+> 	blue = allocimage(display, Rect(0,0,1,1), screen->chan, color2rgb(theme[Colltitle], 1), color2rgb(theme[Coltext], 1));	/* blue-green */
+> 	left = allocimage(display, leftright, GREY1, color2rgb(theme[Coltext], 1), color2rgb(theme[Coltext], 1));
+> 	right = allocimage(display, leftright, GREY1, color2rgb(theme[Coltext], 1), color2rgb(theme[Coltext], 1));
+> 	txtc = allocimage(display, leftright, GREY1, color2rgb(theme[Colback], 1), color2rgb(theme[Coltext], 1));
+> 
+142c301
+< 	string(screen, r.min, display->black, ZP, datefont, date);
+---
+> 	string(screen, r.min, txtc, ZP, datefont, date);
+263c422
+< 	center(mediumfont, r.min, f->str[Suser], display->black);
+---
+> 	center(mediumfont, r.min, f->str[Suser], txtc);
+266c425
+< 	center(mediumfont, r.min, tstr, display->black);
+---
+> 	center(mediumfont, r.min, tstr, txtc);
+272c431
+< 		center(tinyfont, r.min, f->str[Sdomain], display->black);
+---
+> 		center(tinyfont, r.min, f->str[Sdomain], bgrnd);
+diff a/mkfile b/mkfile
+6c6
+< TARG=faces
+---
+> TARG=facest
--- /dev/null
+++ b/patches/stats.patch
@@ -1,0 +1,459 @@
+9a10,154
+> static char *
+> readall(int f, int *osz)
+> {
+> 	int bufsz, sz, n;
+> 	char *s;
+> 
+> 	bufsz = 1023;
+> 	s = nil;
+> 	for(sz = 0;; sz += n){
+> 		if(bufsz-sz < 1024){
+> 			bufsz *= 2;
+> 			s = realloc(s, bufsz);
+> 		}
+> 		if((n = readn(f, s+sz, bufsz-sz-1)) < 1)
+> 			break;
+> 	}
+> 	if(n < 0 || sz < 1){
+> 		free(s);
+> 		return nil;
+> 	}
+> 	s[sz] = 0;
+> 	*osz = sz;
+> 
+> 	return s;
+> }
+> 
+> enum {
+> 	Colrioback,
+> 
+> 	/* the following group has to be in order, they are used by libframe */
+> 	Colback,
+> 	Colhigh,
+> 	Colbord,
+> 	Coltext,
+> 	Colhtext,
+> 
+> 	Coltitle,
+> 	Colltitle,
+> 	Colhold,
+> 	Collhold,
+> 	Colpalehold,
+> 	Colpaletext,
+> 	Colsize,
+> 
+> 	/* menuhit */
+> 	Colmenubar,
+> 	Colmenuback,
+> 	Colmenuhigh,
+> 	Colmenubord,
+> 	Colmenutext,
+> 	Colmenuhtext,
+> 
+> 	Numcolors
+> };
+> 
+> typedef struct Color Color;
+> 
+> struct Color {
+> 	char *id;
+> 	union {
+> 		u32int rgb;
+> 		char *path;
+> 	};
+> 	int flags;
+> };
+> 
+> static Color theme[Numcolors] = {
+> 	[Colrioback]   = {"rioback",   {0x777777}, 0},
+> 	[Colback]      = {"back",      {0xffffff}, 0},
+> 	[Colhigh]      = {"high",      {0xcccccc}, 0},
+> 	[Colbord]      = {"border",    {0x999999}, 0},
+> 	[Coltext]      = {"text",      {DBlack>>8}, 0},
+> 	[Colhtext]     = {"htext",     {DBlack>>8}, 0},
+> 	[Coltitle]     = {"title",     {DGreygreen>>8}, 0},
+> 	[Colltitle]    = {"ltitle",    {DPalegreygreen>>8}, 0},
+> 	[Colhold]      = {"hold",      {DMedblue>>8}, 0},
+> 	[Collhold]     = {"lhold",     {DGreyblue>>8}, 0},
+> 	[Colpalehold]  = {"palehold",  {DPalegreyblue>>8}, 0},
+> 	[Colpaletext]  = {"paletext",  {0x666666}, 0},
+> 	[Colsize]      = {"size",      {DRed>>8}, 0},
+> 	[Colmenubar]   = {"menubar",   {DDarkgreen>>8}, 1},
+> 	[Colmenuback]  = {"menuback",  {0xeaffea}, 1},
+> 	[Colmenuhigh]  = {"menuhigh",  {DDarkgreen>>8}, 1},
+> 	[Colmenubord]  = {"menubord",  {DMedgreen>>8}, 1},
+> 	[Colmenutext]  = {"menutext",  {DBlack>>8}, 1},
+> 	[Colmenuhtext] = {"menuhtext", {0xeaffea}, 1},
+> };
+> 
+> u32int
+> color2rgb(Color c, int scale)
+> {
+> 	return ((((c.rgb >> 16) & 0xff) / scale) << 24) |
+> 			((((c.rgb >> 8) & 0xff) / scale) << 16) |
+> 			(((c.rgb & 0xff) / scale) << 8) |
+> 			0xff;
+> }
+> 
+> void
+> themeload(char *s, int n)
+> {
+> 	int i;
+> 	char *t, *a[2], *e;
+> 	Image *newc;
+> 	u32int rgb;
+> 
+> 	if((t = malloc(n+1)) == nil)
+> 		return;
+> 	memmove(t, s, n);
+> 	t[n] = 0;
+> 
+> 	for(s = t; s != nil && *s; s = e){
+> 		if((e = strchr(s, '\n')) != nil)
+> 			*e++ = 0;
+> 		if(tokenize(s, a, 2) == 2){
+> 			for(i = 0; i < nelem(theme); i++) {
+> 				if(strcmp(theme[i].id, a[0]) == 0) {
+> 					rgb = strtoul(a[1], nil, 16);
+> 					if((newc = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1, rgb<<8 | 0xff)) != nil) {
+> 						theme[i].rgb = rgb;
+> 					}
+> 					break;
+> 				}
+> 			}
+> 		}
+> 	}
+> 	free(t);
+> }
+> 
+> char *
+> themestring(int *n)
+> {
+> 	char *s, *t, *e;
+> 	int i;
+> 
+> 	if((t = malloc(512)) != nil){
+> 		s = t;
+> 		e = s+512;
+> 		for(i = 0; i < nelem(theme); i++)
+> 			s = seprint(s, e, "%s\t%06ux\n", theme[i].id, theme[i].rgb);
+> 		*n = s - t;
+> 	}
+> 
+> 	return t;
+> }
+> 
+29c174
+< 	/* /dev/swap */
+---
+> 	/* old /dev/swap */
+34,35c179
+< 	Reclaim,
+< 	Maxreclaim,
+---
+> 
+52d195
+< 
+74c217
+< 	uvlong		devswap[10];
+---
+> 	uvlong		devswap[8];
+125d267
+< 	Mreclaim,
+150d291
+< 	"add  reclaim ",
+174d314
+< 	reclaimval(Machine*, uvlong*, uvlong*, int),
+200d339
+< 	reclaimval,
+214c353
+< char	argchars[] = "8bcdeEfiIkmlnprstwz";
+---
+> char	argchars[] = "8bcdeEfiIkmlnpstwz";
+222,224c361
+< int	sleeptime = 1000;
+< int	batteryperiod = 1000;
+< int	tempperiod = 1000;
+---
+> int 	sleeptime = 1000;
+227a365,366
+> Image	*stext;
+> 
+278c417
+< 	cols[i][0] = allocimagemix(display, c0, DWhite);
+---
+> 	cols[i][0] = allocimagemix(display, c0, DBlack);
+287c426
+< 	mkcol(0, 0xFFAAAAFF, 0xFFAAAAFF, 0xBB5D5DFF);
+---
+> 	mkcol(0, color2rgb(theme[Coltitle], 1), color2rgb(theme[Colhold], 1), color2rgb(theme[Collhold], 1));
+289c428
+< 	mkcol(1, DPalebluegreen, DPalegreygreen, DPurpleblue);
+---
+> 	mkcol(1, color2rgb(theme[Coltitle], 1), color2rgb(theme[Colhold], 1), color2rgb(theme[Collhold], 1));
+291c430
+< 	mkcol(2, DPaleyellow, DDarkyellow, DYellowgreen);
+---
+> 	mkcol(2, color2rgb(theme[Coltitle], 1), color2rgb(theme[Colhold], 1), color2rgb(theme[Collhold], 1));
+293c432
+< 	mkcol(3, DPalegreen, DMedgreen, DDarkgreen);
+---
+> 	mkcol(3, color2rgb(theme[Coltitle], 1), color2rgb(theme[Colhold], 1), color2rgb(theme[Collhold], 1));
+295c434
+< 	mkcol(4, 0x00AAFFFF, 0x00AAFFFF, 0x0088CCFF);
+---
+> 	mkcol(4, color2rgb(theme[Coltitle], 1), color2rgb(theme[Colhold], 1), color2rgb(theme[Collhold], 1));
+297,299c436,438
+< 	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);
+---
+> 	cols[5][0] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, color2rgb(theme[Colhigh], 1));
+> 	cols[5][1] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, color2rgb(theme[Coltext], 1));
+> 	cols[5][2] = allocimage(display, Rect(0,0,1,1), CMAP8, 1, color2rgb(theme[Collhold], 1));
+342c481
+< 		runestring(screen, p, display->black, ZP, font, r);
+---
+> 		runestring(screen, p, stext, ZP, font, r);
+442c581
+< 		string(screen, g->overtmp->r.min, display->black, ZP, font, buf);
+---
+> 		string(screen, g->overtmp->r.min, stext, ZP, font, buf);
+476,477d614
+< 	static int xxx = 0;
+< 
+481a619,622
+> 		a[0] = a[3];
+> 		a[1] = a[4];
+> 		a[2] = a[5];
+> 		a[3] = a[6];
+483,499c624,625
+< 		a[Mem] = a[3];
+< 		a[Maxmem] = a[4];
+< 		a[Swap] = a[5];
+< 		a[Maxswap] = a[6];
+< 
+< 		a[Reclaim] = 0;
+< 		a[Maxreclaim] = 0;
+< 		if(m->bufp = strstr(m->buf, "reclaim")){
+< 			while(m->bufp > m->buf && m->bufp[-1] != '\n')
+< 				m->bufp--;
+< 			a[Reclaim] = strtoull(m->bufp, &m->bufp, 10);
+< 			while(*m->bufp++ == '/')
+< 				a[Maxreclaim] = strtoull(m->bufp, &m->bufp, 10);
+< 		}
+< 
+< 		a[Kern] = 0;
+< 		a[Maxkern] = 0;
+---
+> 		a[4] = 0;
+> 		a[5] = 0;
+503c629
+< 			a[Kern] = strtoull(m->bufp, &m->bufp, 10);
+---
+> 			a[4] = strtoull(m->bufp, &m->bufp, 10);
+505c631
+< 				a[Maxkern] = strtoull(m->bufp, &m->bufp, 10);
+---
+> 				a[5] = strtoull(m->bufp, &m->bufp, 10);
+508,509c634,635
+< 		a[Draw] = 0;
+< 		a[Maxdraw] = 0;
+---
+> 		a[6] = 0;
+> 		a[7] = 0;
+513c639
+< 			a[Draw] = strtoull(m->bufp, &m->bufp, 10);
+---
+> 			a[6] = strtoull(m->bufp, &m->bufp, 10);
+515c641
+< 				a[Maxdraw] = strtoull(m->bufp, &m->bufp, 10);
+---
+> 				a[7] = strtoull(m->bufp, &m->bufp, 10);
+521,527c647,650
+< 	a[Reclaim] = 0;
+< 	a[Maxreclaim] = 0;
+< 	a[Kern] = 0;
+< 	a[Maxkern] = 0;
+< 	a[Draw] = 0;
+< 	a[Maxdraw] = 0;
+< 
+---
+> 	a[4] = 0;
+> 	a[5] = 0;
+> 	a[6] = 0;
+> 	a[7] = 0;
+625c748
+< 		snprint(buf, sizeof buf, "%s/mnt/pm/battery", mpt);
+---
+> 		snprint(buf, sizeof buf, "%s/mnt/acpi/battery", mpt);
+630d752
+< 		batteryperiod = 10000;
+642,643c764
+< 		tempperiod = 5000;
+< 		snprint(buf, sizeof buf, "%s/mnt/pm/cputemp", mpt);
+---
+> 		snprint(buf, sizeof buf, "%s/mnt/acpi/cputemp", mpt);
+665c786
+< 	return init | present[Mmem] | present[Mswap] | present[Mreclaim] | present[Mkern] | present[Mdraw];
+---
+> 	return init | present[Mmem] | present[Mswap] | present[Mkern] | present[Mdraw];
+686,693c807
+< 	static uint step = 0;
+< 
+< 	if(++step*sleeptime >= batteryperiod){
+< 		step = 0;
+< 		return init | present[Mbattery];
+< 	}
+< 
+< 	return 0;
+---
+> 	return init | present[Mbattery];
+705,712c819
+< 	static uint step = 0;
+< 
+< 	if(++step*sleeptime >= tempperiod){
+< 		step = 0;
+< 		return init | present[Mtemp];
+< 	}
+< 
+< 	return 0;
+---
+> 	return init | present[Mtemp];
+758,763c865,868
+< 	if(needbattery(init)){
+< 		if(loadbuf(m, &m->batteryfd) && readnums(m, nelem(m->batterystats), a, 0))
+< 			memmove(m->batterystats, a, sizeof(m->batterystats));
+< 		else if(loadbuf(m, &m->bitsybatfd) && readnums(m, 1, a, 0))
+< 			memmove(m->batterystats, a, sizeof(m->batterystats));
+< 	}
+---
+> 	if(needbattery(init) && loadbuf(m, &m->batteryfd) && readnums(m, nelem(m->batterystats), a, 0))
+> 		memmove(m->batterystats, a, sizeof(m->batterystats));
+> 	if(needbattery(init) && loadbuf(m, &m->bitsybatfd) && readnums(m, 1, a, 0))
+> 		memmove(m->batterystats, a, sizeof(m->batterystats));
+792,800d896
+< reclaimval(Machine *m, uvlong *v, uvlong *vmax, int)
+< {
+< 	*v = m->devswap[Reclaim];
+< 	*vmax = m->devswap[Maxreclaim];
+< 	if(*vmax == 0)
+< 		*vmax = 1;
+< }
+< 
+< void
+899c995
+< etherval(Machine *m, uvlong *v, uvlong *vmax, int)
+---
+> etherval(Machine *m, uvlong *v, uvlong *vmax, int init)
+902c998,1000
+< 	*vmax = sleeptime;
+---
+> 	*vmax = sleeptime*m->nproc;
+> 	if(init)
+> 		*vmax = sleeptime;
+906c1004
+< etherinval(Machine *m, uvlong *v, uvlong *vmax, int)
+---
+> etherinval(Machine *m, uvlong *v, uvlong *vmax, int init)
+909c1007,1009
+< 	*vmax = sleeptime;
+---
+> 	*vmax = sleeptime*m->nproc;
+> 	if(init)
+> 		*vmax = sleeptime;
+913c1013
+< etheroutval(Machine *m, uvlong *v, uvlong *vmax, int)
+---
+> etheroutval(Machine *m, uvlong *v, uvlong *vmax, int init)
+916c1016,1018
+< 	*vmax = sleeptime;
+---
+> 	*vmax = sleeptime*m->nproc;
+> 	if(init)
+> 		*vmax = sleeptime;
+920c1022
+< ethererrval(Machine *m, uvlong *v, uvlong *vmax, int)
+---
+> ethererrval(Machine *m, uvlong *v, uvlong *vmax, int init)
+926,927c1028,1031
+< 		*v += m->netetherstats[i]-m->prevetherstats[i];
+< 	*vmax = (sleeptime/1000)*10;
+---
+> 		*v += m->netetherstats[i];
+> 	*vmax = (sleeptime/1000)*10*m->nproc;
+> 	if(init)
+> 		*vmax = (sleeptime/1000)*10;
+962c1066
+< 	*vmax = 100;
+---
+> 	*vmax = sleeptime;
+967c1071
+< 		*v = l;
+---
+> 		*v = (l-20)*27;
+1109c1213
+< 	draw(screen, screen->r, display->white, nil, ZP);
+---
+> 	draw(screen, screen->r, display->black, nil, ZP);
+1119c1223
+< 		draw(screen, Rect(x, y-1, screen->r.max.x, y), display->black, nil, ZP);
+---
+> 		draw(screen, Rect(x, y-1, screen->r.max.x, y), stext, nil, ZP);
+1128c1232
+< 		draw(screen, Rect(x-1, starty-1, x, screen->r.max.y), display->black, nil, ZP);
+---
+> 		draw(screen, Rect(x-1, starty-1, x, screen->r.max.y), stext, nil, ZP);
+1138c1242
+< 		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), stext, ZP, font, buf);
+1146c1250
+< 		if(wid < dx-10){
+---
+> 		if(wid < (maxx-startx)-30){
+1149c1253
+< 			draw(screen, Rect(maxx, starty, maxx+1, screen->r.max.y), display->black, nil, ZP);
+---
+> 			draw(screen, Rect(maxx, starty, maxx+1, screen->r.max.y), stext, nil, ZP);
+1161c1265
+< 					draw(screen, Rect(maxx+1, ly, maxx+1+Lx, ly+1), display->black, nil, ZP);
+---
+> 					draw(screen, Rect(maxx+1, ly, maxx+1+Lx, ly+1), stext, nil, ZP);
+1163c1267
+< 					string(screen, Pt(maxx+1+Lx, ly), display->black, ZP, font, labs[k]);
+---
+> 					string(screen, Pt(maxx+1+Lx, ly), stext, ZP, font, labs[k]);
+1171,1173c1275,1277
+< 		machr = Rect(startx+i*dx, starty, startx+(i+1)*dx - 1, screen->r.max.y);
+< 		if(i == nmach-1)
+< 			machr.max.x = maxx;
+---
+> 		machr = Rect(startx+i*dx, starty, maxx, screen->r.max.y);
+> 		if(i < nmach-1)
+> 			machr.max.x = startx+(i+1)*dx - 1;
+1199,1200d1302
+< 			if(vmax == 0)
+< 				vmax = 1;
+1375,1377d1476
+< 	case 'r':
+< 		addgraph(Mreclaim);
+< 		break;
+1412a1512,1524
+> 
+> 	// load theme
+> 	int sz, f;
+> 	char *s;
+> 
+> 	if((f = open("/dev/theme", OREAD|OCEXEC)) >= 0){
+> 		if((s = readall(f, &sz)) != nil)
+> 			themeload(s, sz);
+> 		free(s);
+> 		close(f);
+> 	}
+> 
+> 	stext = allocimage(display, Rect(0,0,1,1), CMAP8, 1, color2rgb(theme[Coltitle], 1));
+1429,1430d1540
+< 			if(vmax == 0)
+< 				vmax = 1;
--- /dev/null
+++ b/patches/vtg.patch
@@ -1,0 +1,23 @@
+diff a/main.c b/main.c
+326a327,328
+> 	ulong MyGreen = 0x00ee1aff;
+> 
+328c330
+< 	green = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DGreen);
+---
+> 	green = allocimage(display, Rect(0,0,1,1), screen->chan, 1, MyGreen);
+338,341c340,343
+< 	bgcolor = (blkbg? display->black: display->white);
+< 	fgcolor = (blkbg? display->white: 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);
+---
+> 	bgcolor = (blkbg? display->black: display->black);
+> 	fgcolor = (blkbg? green: green);
+> 	bgselected = allocimage(display, Rect(0,0,1,1), CMAP8, 1, blkbg ? MyGreen : MyGreen);
+> 	fgselected = allocimage(display, Rect(0,0,1,1), CMAP8, 1, blkbg ? DBlack : DBlack);
+diff a/mkfile b/mkfile
+3c3
+< TARG=vt
+---
+> TARG=vtg
--- /dev/null
+++ b/patches/winwatch.patch
@@ -1,0 +1,184 @@
+8a9,34
+> static char *
+> readall(int f, int *osz)
+> {
+> 	int bufsz, sz, n;
+> 	char *s;
+> 
+> 	bufsz = 1023;
+> 	s = nil;
+> 	for(sz = 0;; sz += n){
+> 		if(bufsz-sz < 1024){
+> 			bufsz *= 2;
+> 			s = realloc(s, bufsz);
+> 		}
+> 		if((n = readn(f, s+sz, bufsz-sz-1)) < 1)
+> 			break;
+> 	}
+> 	if(n < 0 || sz < 1){
+> 		free(s);
+> 		return nil;
+> 	}
+> 	s[sz] = 0;
+> 	*osz = sz;
+> 
+> 	return s;
+> }
+> 
+9a36,106
+> 	Colrioback,
+> 
+> 	/* the following group has to be in order, they are used by libframe */
+> 	Colback,
+> 	Colhigh,
+> 	Colbord,
+> 	Coltext,
+> 	Colhtext,
+> 
+> 	Coltitle,
+> 	Colltitle,
+> 	Colhold,
+> 	Collhold,
+> 	Colpalehold,
+> 	Colpaletext,
+> 	Colsize,
+> 
+> 	/* menuhit */
+> 	Colmenubar,
+> 	Colmenuback,
+> 	Colmenuhigh,
+> 	Colmenubord,
+> 	Colmenutext,
+> 	Colmenuhtext,
+> 
+> 	Numcolors
+> };
+> 
+> typedef struct Color Color;
+> 
+> struct Color {
+> 	char *id;
+> 	union {
+> 		u32int rgb;
+> 		char *path;
+> 	};
+> 	int flags;
+> };
+> 
+> static Color theme[Numcolors] = {
+> 	[Colrioback]   = {"rioback",   {0x777777}, 0},
+> 	[Colback]      = {"back",      {0xffffff}, 0},
+> 	[Colhigh]      = {"high",      {0xcccccc}, 0},
+> 	[Colbord]      = {"border",    {0x999999}, 0},
+> 	[Coltext]      = {"text",      {DBlack>>8}, 0},
+> 	[Colhtext]     = {"htext",     {DBlack>>8}, 0},
+> 	[Coltitle]     = {"title",     {DGreygreen>>8}, 0},
+> 	[Colltitle]    = {"ltitle",    {DPalegreygreen>>8}, 0},
+> 	[Colhold]      = {"hold",      {DMedblue>>8}, 0},
+> 	[Collhold]     = {"lhold",     {DGreyblue>>8}, 0},
+> 	[Colpalehold]  = {"palehold",  {DPalegreyblue>>8}, 0},
+> 	[Colpaletext]  = {"paletext",  {0x666666}, 0},
+> 	[Colsize]      = {"size",      {DRed>>8}, 0},
+> 	[Colmenubar]   = {"menubar",   {DDarkgreen>>8}, 1},
+> 	[Colmenuback]  = {"menuback",  {0xeaffea}, 1},
+> 	[Colmenuhigh]  = {"menuhigh",  {DDarkgreen>>8}, 1},
+> 	[Colmenubord]  = {"menubord",  {DMedgreen>>8}, 1},
+> 	[Colmenutext]  = {"menutext",  {DBlack>>8}, 1},
+> 	[Colmenuhtext] = {"menuhtext", {0xeaffea}, 1},
+> };
+> 
+> u32int
+> color2rgb(Color c, int scale)
+> {
+> 	return ((((c.rgb >> 16) & 0xff) / scale) << 24) |
+> 			((((c.rgb >> 8) & 0xff) / scale) << 16) |
+> 			(((c.rgb & 0xff) / scale) << 8) |
+> 			0xff;
+> }
+> 
+> enum {
+30c127
+< Image *statecol[4];
+---
+> Image *statecol[5];
+178c275
+< 	_string(screen, addpt(win[i].r.min, Pt(2,0)), display->black, ZP,
+---
+> 	_string(screen, addpt(win[i].r.min, Pt(2,0)), statecol[4], ZP,
+181c278
+< 	border(screen, win[i].r, 1, display->black, ZP);	
+---
+> 	border(screen, win[i].r, 1, statecol[4], ZP);	
+316a414,461
+> themeload(char *s, int n)
+> {
+> 	int i;
+> 	char *t, *a[2], *e;
+> 	Image *newc;
+> 	u32int rgb;
+> 
+> 	if((t = malloc(n+1)) == nil)
+> 		return;
+> 	memmove(t, s, n);
+> 	t[n] = 0;
+> 
+> 	for(s = t; s != nil && *s; s = e){
+> 		if((e = strchr(s, '\n')) != nil)
+> 			*e++ = 0;
+> 		if(tokenize(s, a, 2) == 2){
+> 			for(i = 0; i < nelem(theme); i++) {
+> 				if(strcmp(theme[i].id, a[0]) == 0) {
+> 					rgb = strtoul(a[1], nil, 16);
+> 					if((newc = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1, rgb<<8 | 0xff)) != nil) {
+> 						theme[i].rgb = rgb;
+> 					}
+> 					break;
+> 				}
+> 			}
+> 		}
+> 	}
+> 	free(t);
+> }
+> 
+> char *
+> themestring(int *n)
+> {
+> 	char *s, *t, *e;
+> 	int i;
+> 
+> 	if((t = malloc(512)) != nil){
+> 		s = t;
+> 		e = s+512;
+> 		for(i = 0; i < nelem(theme); i++)
+> 			s = seprint(s, e, "%s\t%06ux\n", theme[i].id, theme[i].rgb);
+> 		*n = s - t;
+> 	}
+> 
+> 	return t;
+> }
+> 
+> void
+342d486
+< 	lightblue = allocimagemix(display, DPalebluegreen, DWhite);
+344c488,501
+< 	statecol[0] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xCCCCCCFF);
+---
+> 	// load theme
+> 	int sz, f;
+> 	char *s;
+> 
+> 	if((f = open("/dev/theme", OREAD|OCEXEC)) >= 0){
+> 		if((s = readall(f, &sz)) != nil)
+> 			themeload(s, sz);
+> 		free(s);
+> 		close(f);
+> 	}
+> 
+> 	lightblue = allocimagemix(display, color2rgb(theme[Colback], 1), color2rgb(theme[Colback], 1));
+> 
+> 	statecol[0] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, color2rgb(theme[Colhold], 2));
+347c504,505
+< 	statecol[3] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreygreen);
+---
+> 	statecol[3] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, color2rgb(theme[Colltitle], 1));
+> 	statecol[4] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, color2rgb(theme[Colmenubord], 1));