shithub: fork

Download patch

ref: b85e023d0fb10b704d116742bfaf251ceca42ca9
parent: 14cac15cc44e62351d644e8fb31dc600fa25ee14
author: qwx <qwx@sciops.net>
date: Wed Jan 7 05:32:46 EST 2026

libdraw: sync with 9front

--- a/sys/src/libdraw/eenter.c
+++ /dev/null
@@ -1,241 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <draw.h>
-#include <event.h>
-#include <keyboard.h>
-
-int
-eenter(char *ask, char *buf, int len, Mouse *m)
-{
-	int done, down, tick, n, h, w, l, i;
-	Image *b, *save, *backcol, *bordcol, *txtcol;
-	Point p, o, t;
-	Rectangle r, sc;
-	Event ev;
-	Rune k;
-
-	o = screen->r.min;
-
-	enum{
-		Cback,
-		Cbord,
-		Ctext,
-		Ncols,
-	};
-	Theme th[Ncols] = {
-		[Cback] { "menuback",	0xEAFFEAFF },
-		[Cbord]	{ "menubord",	DMedgreen },
-		[Ctext]	{ "menutext",	DBlack },
-	};
-	readtheme(th, nelem(th), nil);
-	backcol = allocimage(display, Rect(0,0,1,1), screen->chan, 1, th[Cback].c);
-	bordcol = allocimage(display, Rect(0,0,1,1), screen->chan, 1, th[Cbord].c);
-	txtcol = allocimage(display, Rect(0,0,1,1), screen->chan, 1, th[Ctext].c);
-	if(backcol == nil || bordcol == nil || txtcol == nil)
-		return -1;
-
-	while(ecankbd())
-		ekbd();
-
-	if(m) o = m->xy;
-
-	if(buf && len > 0)
-		n = strlen(buf);
-	else {
-		buf = nil;
-		len = 0;
-		n = 0;
-	}
-
-	k = -1;
-	tick = n;
-	save = nil;
-	done = down = 0;
-
-	p = stringsize(font, " ");
-	h = p.y;
-	w = p.x;
-
-	b = screen;
-	sc = b->clipr;
-	replclipr(b, 0, b->r);
-
-	while(!done){
-		p = stringsize(font, buf ? buf : "");
-		if(ask && ask[0]){
-			if(buf) p.x += w;
-			p.x += stringwidth(font, ask);
-		}
-		r = rectaddpt(insetrect(Rpt(ZP, p), -4), o);
-		p.x = 0;
-		r = rectsubpt(r, p);
-
-		p = ZP;
-		if(r.min.x < screen->r.min.x)
-			p.x = screen->r.min.x - r.min.x;
-		if(r.min.y < screen->r.min.y)
-			p.y = screen->r.min.y - r.min.y;
-		r = rectaddpt(r, p);
-		p = ZP;
-		if(r.max.x > screen->r.max.x)
-			p.x = r.max.x - screen->r.max.x;
-		if(r.max.y > screen->r.max.y)
-			p.y = r.max.y - screen->r.max.y;
-		r = rectsubpt(r, p);
-
-		r = insetrect(r, -2);
-		if(save == nil){
-			save = allocimage(display, r, b->chan, 0, DNofill);
-			if(save == nil){
-				n = -1;
-				break;
-			}
-			draw(save, r, b, nil, r.min);
-		}
-		draw(b, r, backcol, nil, ZP);
-		border(b, r, 2, bordcol, ZP);
-		p = addpt(r.min, Pt(6, 6));
-		if(ask && ask[0]){
-			p = string(b, p, bordcol, ZP, font, ask);
-			if(buf) p.x += w;
-		}
-		if(buf){
-			t = p;
-			p = stringn(b, p, txtcol, ZP, font, buf, utfnlen(buf, tick));
-			draw(b, Rect(p.x-1, p.y, p.x+2, p.y+3), txtcol, nil, ZP);
-			draw(b, Rect(p.x, p.y, p.x+1, p.y+h), txtcol, nil, ZP);
-			draw(b, Rect(p.x-1, p.y+h-3, p.x+2, p.y+h), txtcol, nil, ZP);
-			p = string(b, p, txtcol, ZP, font, buf+tick);
-		}
-		flushimage(display, 1);
-
-nodraw:
-		i = Ekeyboard;
-		if(m != nil)
-			i |= Emouse;
-
-		replclipr(b, 0, sc);
-		i = eread(i, &ev);
-
-		/* screen might have been resized */
-		if(b != screen || !eqrect(screen->clipr, sc)){
-			freeimage(save);
-			save = nil;
-		}
-		b = screen;
-		sc = b->clipr;
-		replclipr(b, 0, b->r);
-
-		switch(i){
-		default:
-			done = 1;
-			n = -1;
-			break;
-		case Ekeyboard:
-			k = ev.kbdc;
-			if(buf == nil || k == Keof || k == '\n'){
-				done = 1;
-				break;
-			}
-			if(k == Knack || k == Kesc){
-				done = !n;
-				buf[n = tick = 0] = 0;
-				break;
-			}
-			if(k == Ksoh || k == Khome){
-				tick = 0;
-				continue;
-			}
-			if(k == Kenq || k == Kend){
-				tick = n;
-				continue;
-			}
-			if(k == Kright){
-				if(tick < n)
-					tick += chartorune(&k, buf+tick);
-				continue;
-			}
-			if(k == Kleft){
-				for(i = 0; i < n; i += l){
-					l = chartorune(&k, buf+i);
-					if(i+l >= tick){
-						tick = i;
-						break;
-					}
-				}
-				continue;
-			}
-			if(k == Ketb){
-				l = tick;
-				while(tick > 0){
-					tick--;
-					if(tick == 0 ||
-						strchr(" !\"#$%&'()*+,-./:;<=>?@`[\\]^{|}~", buf[tick-1]))
-						break;
-				}
-				memmove(buf+tick, buf+l, n-l);
-				buf[n -= l-tick] = 0;
-				break;
-			}
-			if(k == Kbs){
-				if(tick <= 0)
-					continue;
-				for(i = 0; i < n; i += l){
-					l = chartorune(&k, buf+i);
-					if(i+l >= tick){
-						memmove(buf+i, buf+i+l, n - (i+l));
-						buf[n -= l] = 0;
-						tick -= l;
-						break;
-					}
-				}
-				break;
-			}
-			if(k < 0x20 || k == Kdel || (k & 0xFF00) == KF || (k & 0xFF00) == Spec)
-				continue;
-			if((len-n) <= (l = runelen(k)))
-				continue;
-			memmove(buf+tick+l, buf+tick, n - tick);
-			runetochar(buf+tick, &k);
-			buf[n += l] = 0;
-			tick += l;
-			break;
-		case Emouse:
-			*m = ev.mouse;
-			if(!ptinrect(m->xy, r)){
-				down = 0;
-				goto nodraw;
-			}
-			if(m->buttons & 7){
-				down = 1;
-				if(buf && m->xy.x >= (t.x - w)){
-					down = 0;
-					for(i = 0; i < n; i += l){
-						l = chartorune(&k, buf+i);
-						t.x += stringnwidth(font, buf+i, 1);
-						if(t.x > m->xy.x)
-							break;
-					}
-					tick = i;
-				}
-				continue;
-			}
-			done = down;
-			break;
-		}
-		if(save){
-			draw(b, save->r, save, nil, save->r.min);
-			freeimage(save);
-			save = nil;
-		}
-	}
-
-	replclipr(b, 0, sc);
-
-	freeimage(backcol);
-	freeimage(bordcol);
-	flushimage(display, 1);
-
-	return n;
-}
-
--- a/sys/src/libdraw/emenuhit.c
+++ /dev/null
@@ -1,291 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <draw.h>
-#include <event.h>
-
-enum
-{
-	Margin = 4,		/* outside to text */
-	Border = 2,		/* outside to selection boxes */
-	Blackborder = 2,	/* width of outlining border */
-	Vspacing = 2,		/* extra spacing between lines of text */
-	Maxunscroll = 25,	/* maximum #entries before scrolling turns on */
-	Nscroll = 20,		/* number entries in scrolling part */
-	Scrollwid = 14,		/* width of scroll bar */
-	Gap = 4,			/* between text and scroll bar */
-};
-
-static	Image	*menutxt;
-static	Image	*back;
-static	Image	*high;
-static	Image	*bord;
-static	Image	*text;
-static	Image	*htext;
-
-static
-void
-menucolors(void)
-{
-	enum{
-		Cback,
-		Chigh,
-		Cbord,
-		Ctext,
-		Chtext,
-		Cmtext,
-		Ncols,
-	};
-	Theme th[Ncols] = {
-		[Cback] { "menuback",	0xEAFFEAFF },
-		[Chigh] { "menuhigh",	DDarkgreen },
-		[Cbord]	{ "menubord",	DMedgreen },
-		[Ctext]	{ "menutext",	DBlack },
-		[Chtext]{ "menuhtext",	0xEAFFEAFF },
-		[Cmtext]{ "menubar",	DDarkgreen },
-	};
-
-	readtheme(th, nelem(th), nil);
-	back = allocimage(display, Rect(0,0,1,1), screen->chan, 1, th[Cback].c);
-	high = allocimage(display, Rect(0,0,1,1), screen->chan, 1, th[Chigh].c);
-	bord = allocimage(display, Rect(0,0,1,1), screen->chan, 1, th[Cbord].c);
-	text = allocimage(display, Rect(0,0,1,1), screen->chan, 1, th[Ctext].c);
-	htext = allocimage(display, Rect(0,0,1,1), screen->chan, 1, th[Chtext].c);
-	menutxt = allocimage(display, Rect(0,0,1,1), screen->chan, 1, th[Cmtext].c);
-	if(back == nil || high == nil || bord == nil
-	|| text == nil || htext == nil || menutxt == nil)
-		goto Error;
-	return;
-
-    Error:
-	freeimage(back);
-	freeimage(high);
-	freeimage(bord);
-	freeimage(menutxt);
-	freeimage(htext);
-	freeimage(text);
-	back = display->white;
-	high = display->black;
-	bord = display->black;
-	text = display->black;
-	htext = display->white;
-	menutxt = display->black;
-}
-
-/*
- * r is a rectangle holding the text elements.
- * return the rectangle, including its black edge, holding element i.
- */
-static Rectangle
-menurect(Rectangle r, int i)
-{
-	if(i < 0)
-		return Rect(0, 0, 0, 0);
-	r.min.y += (font->height+Vspacing)*i;
-	r.max.y = r.min.y+font->height+Vspacing;
-	return insetrect(r, Border-Margin);
-}
-
-/*
- * r is a rectangle holding the text elements.
- * return the element number containing p.
- */
-static int
-menusel(Rectangle r, Point p)
-{
-	if(!ptinrect(p, r))
-		return -1;
-	return (p.y-r.min.y)/(font->height+Vspacing);
-}
-
-static
-void
-paintitem(Menu *menu, Rectangle textr, int off, int i, int highlight, Image *save, Image *restore)
-{
-	char *item;
-	Rectangle r;
-	Point pt;
-
-	if(i < 0)
-		return;
-	r = menurect(textr, i);
-	if(restore){
-		draw(screen, r, restore, nil, restore->r.min);
-		return;
-	}
-	if(save)
-		draw(save, save->r, screen, nil, r.min);
-	item = menu->item? menu->item[i+off] : (*menu->gen)(i+off);
-	pt.x = (textr.min.x+textr.max.x-stringwidth(font, item))/2;
-	pt.y = textr.min.y+i*(font->height+Vspacing);
-	draw(screen, r, highlight? high : back, nil, pt);
-	string(screen, pt, highlight? htext : text, pt, font, item);
-}
-
-/*
- * menur is a rectangle holding all the highlightable text elements.
- * track mouse while inside the box, return what's selected when button
- * is raised, -1 as soon as it leaves box.
- * invariant: nothing is highlighted on entry or exit.
- */
-static int
-menuscan(Menu *menu, int but, Mouse *m, Rectangle textr, int off, int lasti, Image *save)
-{
-	int i;
-
-	paintitem(menu, textr, off, lasti, 1, save, nil);
-	flushimage(display, 1);
-	*m = emouse();
-	while(m->buttons & (1<<(but-1))){
-		flushimage(display, 1);
-		*m = emouse();
-		i = menusel(textr, m->xy);
-		if(i != -1 && i == lasti)
-			continue;
-		paintitem(menu, textr, off, lasti, 0, nil, save);
-		if(i == -1)
-			return i;
-		lasti = i;
-		paintitem(menu, textr, off, lasti, 1, save, nil);
-	}
-	return lasti;
-}
-
-static void
-menupaint(Menu *menu, Rectangle textr, int off, int nitemdrawn)
-{
-	int i;
-
-	draw(screen, insetrect(textr, Border-Margin), back, nil, ZP);
-	for(i = 0; i<nitemdrawn; i++)
-		paintitem(menu, textr, off, i, 0, nil, nil);
-}
-
-static void
-menuscrollpaint(Rectangle scrollr, int off, int nitem, int nitemdrawn)
-{
-	Rectangle r;
-
-	draw(screen, scrollr, back, nil, ZP);
-	r.min.x = scrollr.min.x;
-	r.max.x = scrollr.max.x;
-	r.min.y = scrollr.min.y + (Dy(scrollr)*off)/nitem;
-	r.max.y = scrollr.min.y + (Dy(scrollr)*(off+nitemdrawn))/nitem;
-	if(r.max.y < r.min.y+2)
-		r.max.y = r.min.y+2;
-	border(screen, r, 1, bord, ZP);
-	draw(screen, insetrect(r, 1), menutxt, nil, ZP);
-}
-
-int
-emenuhit(int but, Mouse *m, Menu *menu)
-{
-	int i, nitem, nitemdrawn, maxwid, lasti, off, noff, wid, screenitem;
-	int scrolling;
-	Rectangle r, menur, sc, textr, scrollr;
-	Image *b, *save;
-	Point pt;
-	char *item;
-
-	if(back == nil)
-		menucolors();
-	sc = screen->clipr;
-	replclipr(screen, 0, screen->r);
-	maxwid = 0;
-	for(nitem = 0;
-	    item = menu->item? menu->item[nitem] : (*menu->gen)(nitem);
-	    nitem++){
-		i = stringwidth(font, item);
-		if(i > maxwid)
-			maxwid = i;
-	}
-	if(menu->lasthit<0 || menu->lasthit>=nitem)
-		menu->lasthit = 0;
-	screenitem = (Dy(screen->r)-10)/(font->height+Vspacing);
-	if(nitem>Maxunscroll || nitem>screenitem){
-		scrolling = 1;
-		nitemdrawn = Nscroll;
-		if(nitemdrawn > screenitem)
-			nitemdrawn = screenitem;
-		wid = maxwid + Gap + Scrollwid;
-		off = menu->lasthit - nitemdrawn/2;
-		if(off < 0)
-			off = 0;
-		if(off > nitem-nitemdrawn)
-			off = nitem-nitemdrawn;
-		lasti = menu->lasthit-off;
-	}else{
-		scrolling = 0;
-		nitemdrawn = nitem;
-		wid = maxwid;
-		off = 0;
-		lasti = menu->lasthit;
-	}
-	r = insetrect(Rect(0, 0, wid, nitemdrawn*(font->height+Vspacing)), -Margin);
-	r = rectsubpt(r, Pt(wid/2, lasti*(font->height+Vspacing)+font->height/2));
-	r = rectaddpt(r, m->xy);
-	pt = ZP;
-	if(r.max.x>screen->r.max.x)
-		pt.x = screen->r.max.x-r.max.x;
-	if(r.max.y>screen->r.max.y)
-		pt.y = screen->r.max.y-r.max.y;
-	if(r.min.x<screen->r.min.x)
-		pt.x = screen->r.min.x-r.min.x;
-	if(r.min.y<screen->r.min.y)
-		pt.y = screen->r.min.y-r.min.y;
-	menur = rectaddpt(r, pt);
-	textr.max.x = menur.max.x-Margin;
-	textr.min.x = textr.max.x-maxwid;
-	textr.min.y = menur.min.y+Margin;
-	textr.max.y = textr.min.y + nitemdrawn*(font->height+Vspacing);
-	if(scrolling){
-		scrollr = insetrect(menur, Border);
-		scrollr.max.x = scrollr.min.x+Scrollwid;
-	}else
-		scrollr = Rect(0, 0, 0, 0);
-
-	b = allocimage(display, menur, screen->chan, 0, 0);
-	if(b == 0)
-		b = screen;
-	draw(b, menur, screen, nil, menur.min);
-	draw(screen, menur, back, nil, ZP);
-	border(screen, menur, Blackborder, bord, ZP);
-	save = allocimage(display, menurect(textr, 0), screen->chan, 0, -1);
-	r = menurect(textr, lasti);
-	if(pt.x || pt.y)
-		emoveto(divpt(addpt(r.min, r.max), 2));
-	menupaint(menu, textr, off, nitemdrawn);
-	if(scrolling)
-		menuscrollpaint(scrollr, off, nitem, nitemdrawn);
-	while(m->buttons & (1<<(but-1))){
-		lasti = menuscan(menu, but, m, textr, off, lasti, save);
-		if(lasti >= 0)
-			break;
-		while(!ptinrect(m->xy, textr) && (m->buttons & (1<<(but-1)))){
-			if(scrolling && ptinrect(m->xy, scrollr)){
-				noff = ((m->xy.y-scrollr.min.y)*nitem)/Dy(scrollr);
-				noff -= nitemdrawn/2;
-				if(noff < 0)
-					noff = 0;
-				if(noff > nitem-nitemdrawn)
-					noff = nitem-nitemdrawn;
-				if(noff != off){
-					off = noff;
-					menupaint(menu, textr, off, nitemdrawn);
-					menuscrollpaint(scrollr, off, nitem, nitemdrawn);
-				}
-			}
-			flushimage(display, 1);
-			*m = emouse();
-		}
-	}
-	draw(screen, menur, b, nil, menur.min);
-	if(b != screen)
-		freeimage(b);
-	freeimage(save);
-	replclipr(screen, 0, sc);
-	if(lasti >= 0){
-		menu->lasthit = lasti+off;
-		return menu->lasthit;
-	}
-	return -1;
-}
--- a/sys/src/libdraw/enter.c
+++ /dev/null
@@ -1,251 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <draw.h>
-#include <thread.h>
-#include <mouse.h>
-#include <keyboard.h>
-
-int
-enter(char *ask, char *buf, int len, Mousectl *mc, Keyboardctl *kc, Screen *scr)
-{
-	int done, down, tick, n, h, w, l, i;
-	Image *b, *save, *backcol, *bordcol, *txtcol;
-	Point p, o, t;
-	Rectangle r, sc;
-	Alt a[3];
-	Mouse m;
-	Rune k;
-
-	o = screen->r.min;
-
-	enum{
-		Cback,
-		Cbord,
-		Ctext,
-		Ncols,
-	};
-	Theme th[Ncols] = {
-		[Cback] { "menuback",	0xEAFFEAFF },
-		[Cbord]	{ "menubord",	DMedgreen },
-		[Ctext]	{ "menutext",	DBlack },
-	};
-	readtheme(th, nelem(th), nil);
-	backcol = allocimage(display, Rect(0,0,1,1), screen->chan, 1, th[Cback].c);
-	bordcol = allocimage(display, Rect(0,0,1,1), screen->chan, 1, th[Cbord].c);
-	txtcol = allocimage(display, Rect(0,0,1,1), screen->chan, 1, th[Ctext].c);
-	if(backcol == nil || bordcol == nil || txtcol == nil)
-		return -1;
-
-	sc = screen->clipr;
-	replclipr(screen, 0, screen->r);
-
-	n = 0;
-	if(kc){
-		while(nbrecv(kc->c, nil) == 1)
-			;
-		a[n].op = CHANRCV;
-		a[n].c = kc->c;
-		a[n].v = &k;
-		n++;
-	}
-	if(mc){
-		o = mc->xy;
-		a[n].op = CHANRCV;
-		a[n].c = mc->c;
-		a[n].v = &m;
-		n++;
-	}
-	a[n].op = CHANEND;
-	a[n].c = nil;
-	a[n].v = nil;
-
-	if(buf && len > 0)
-		n = strlen(buf);
-	else {
-		buf = nil;
-		len = 0;
-		n = 0;
-	}
-
-	k = -1;
-	b = nil;
-	tick = n;
-	save = nil;
-	done = down = 0;
-
-	p = stringsize(font, " ");
-	h = p.y;
-	w = p.x;
-
-	while(!done){
-		p = stringsize(font, buf ? buf : "");
-		if(ask && ask[0]){
-			if(buf) p.x += w;
-			p.x += stringwidth(font, ask);
-		}
-		r = rectaddpt(insetrect(Rpt(ZP, p), -4), o);
-		p.x = 0;
-		r = rectsubpt(r, p);
-
-		p = ZP;
-		if(r.min.x < screen->r.min.x)
-			p.x = screen->r.min.x - r.min.x;
-		if(r.min.y < screen->r.min.y)
-			p.y = screen->r.min.y - r.min.y;
-		r = rectaddpt(r, p);
-		p = ZP;
-		if(r.max.x > screen->r.max.x)
-			p.x = r.max.x - screen->r.max.x;
-		if(r.max.y > screen->r.max.y)
-			p.y = r.max.y - screen->r.max.y;
-		r = rectsubpt(r, p);
-
-		r = insetrect(r, -2);
-		if(scr){
-			if(b == nil)
-				b = allocwindow(scr, r, Refbackup, DWhite);
-			if(b == nil)
-				scr = nil;
-		}
-		if(scr == nil && save == nil){
-			if(b == nil)
-				b = screen;
-			save = allocimage(display, r, b->chan, 0, DNofill);
-			if(save == nil){
-				n = -1;
-				break;
-			}
-			draw(save, r, b, nil, r.min);
-		}
-		draw(b, r, backcol, nil, ZP);
-		border(b, r, 2, bordcol, ZP);
-		p = addpt(r.min, Pt(6, 6));
-		if(ask && ask[0]){
-			p = string(b, p, bordcol, ZP, font, ask);
-			if(buf) p.x += w;
-		}
-		if(buf){
-			t = p;
-			p = stringn(b, p, txtcol, ZP, font, buf, utfnlen(buf, tick));
-			draw(b, Rect(p.x-1, p.y, p.x+2, p.y+3), txtcol, nil, ZP);
-			draw(b, Rect(p.x, p.y, p.x+1, p.y+h), txtcol, nil, ZP);
-			draw(b, Rect(p.x-1, p.y+h-3, p.x+2, p.y+h), txtcol, nil, ZP);
-			p = string(b, p, txtcol, ZP, font, buf+tick);
-		}
-		flushimage(display, 1);
-
-nodraw:
-		switch(alt(a)){
-		case -1:
-			done = 1;
-			n = -1;
-			break;
-		case 0:
-			if(buf == nil || k == Keof || k == '\n'){
-				done = 1;
-				break;
-			}
-			if(k == Knack || k == Kesc){
-				done = !n;
-				buf[n = tick = 0] = 0;
-				break;
-			}
-			if(k == Ksoh || k == Khome){
-				tick = 0;
-				continue;
-			}
-			if(k == Kenq || k == Kend){
-				tick = n;
-				continue;
-			}
-			if(k == Kright){
-				if(tick < n)
-					tick += chartorune(&k, buf+tick);
-				continue;
-			}
-			if(k == Kleft){
-				for(i = 0; i < n; i += l){
-					l = chartorune(&k, buf+i);
-					if(i+l >= tick){
-						tick = i;
-						break;
-					}
-				}
-				continue;
-			}
-			if(k == Ketb){
-				l = tick;
-				while(tick > 0){
-					tick--;
-					if(tick == 0 ||
-						strchr(" !\"#$%&'()*+,-./:;<=>?@`[\\]^{|}~", buf[tick-1]))
-						break;
-				}
-				memmove(buf+tick, buf+l, n-l);
-				buf[n -= l-tick] = 0;
-				break;
-			}
-			if(k == Kbs){
-				if(tick <= 0)
-					continue;
-				for(i = 0; i < n; i += l){
-					l = chartorune(&k, buf+i);
-					if(i+l >= tick){
-						memmove(buf+i, buf+i+l, n - (i+l));
-						buf[n -= l] = 0;
-						tick -= l;
-						break;
-					}
-				}
-				break;
-			}
-			if(k < 0x20 || k == Kdel || (k & 0xFF00) == KF || (k & 0xFF00) == Spec)
-				continue;
-			if((len-n) <= (l = runelen(k)))
-				continue;
-			memmove(buf+tick+l, buf+tick, n - tick);
-			runetochar(buf+tick, &k);
-			buf[n += l] = 0;
-			tick += l;
-			break;
-		case 1:
-			if(!ptinrect(m.xy, r)){
-				down = 0;
-				goto nodraw;
-			}
-			if(m.buttons & 7){
-				down = 1;
-				if(buf && m.xy.x >= (t.x - w)){
-					down = 0;
-					for(i = 0; i < n; i += l){
-						l = chartorune(&k, buf+i);
-						t.x += stringnwidth(font, buf+i, 1);
-						if(t.x > m.xy.x)
-							break;
-					}
-					tick = i;
-				}
-				continue;
-			}
-			done = down;
-			break;
-		}
-
-		if(b != screen) {
-			freeimage(b);
-			b = nil;
-		} else {
-			draw(b, save->r, save, nil, save->r.min);
-			freeimage(save);
-			save = nil;
-		}
-	}
-
-	replclipr(screen, 0, sc);
-
-	freeimage(backcol);
-	freeimage(bordcol);
-	flushimage(display, 1);
-
-	return n;
-}
--- /dev/null
+++ b/sys/src/libdraw/genenter.c
@@ -1,0 +1,227 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <mouse.h>
+#include <keyboard.h>
+
+int
+genenter(char *ask, char *buf, int len, Mouse *m, void *c, int (*_input)(Mouse*, void*, Rune*), Screen *scr)
+{
+	int done, down, tick, n, h, w, l, i;
+	Image *b, *save, *backcol, *bordcol, *txtcol;
+	Point p, o, t;
+	Rectangle r, sc;
+	Rune k;
+
+	enum{
+		Cback,
+		Cbord,
+		Ctext,
+		Ncols,
+	};
+	Theme th[Ncols] = {
+		[Cback] { "menuback",	0xEAFFEAFF },
+		[Cbord]	{ "menubord",	DMedgreen },
+		[Ctext]	{ "menutext",	DBlack },
+	};
+	readtheme(th, nelem(th), nil);
+	backcol = allocimage(display, Rect(0,0,1,1), screen->chan, 1, th[Cback].c);
+	bordcol = allocimage(display, Rect(0,0,1,1), screen->chan, 1, th[Cbord].c);
+	txtcol = allocimage(display, Rect(0,0,1,1), screen->chan, 1, th[Ctext].c);
+	if(backcol == nil || bordcol == nil || txtcol == nil)
+ 		return -1;
+
+	sc = screen->clipr;
+	replclipr(screen, 0, screen->r);
+
+	if(buf && len > 0)
+		n = strlen(buf);
+	else {
+		buf = nil;
+		len = 0;
+		n = 0;
+	}
+
+	k = -1;
+	b = nil;
+	tick = n;
+	save = nil;
+	done = down = 0;
+
+	p = stringsize(font, " ");
+	h = p.y;
+	w = p.x;
+
+	o = m->xy;
+	while(!done){
+		p = stringsize(font, buf ? buf : "");
+		if(ask && ask[0]){
+			if(buf) p.x += w;
+			p.x += stringwidth(font, ask);
+		}
+		r = rectaddpt(insetrect(Rpt(ZP, p), -4), o);
+		p.x = 0;
+		r = rectsubpt(r, p);
+
+		p = ZP;
+		if(r.min.x < screen->r.min.x)
+			p.x = screen->r.min.x - r.min.x;
+		if(r.min.y < screen->r.min.y)
+			p.y = screen->r.min.y - r.min.y;
+		r = rectaddpt(r, p);
+		p = ZP;
+		if(r.max.x > screen->r.max.x)
+			p.x = r.max.x - screen->r.max.x;
+		if(r.max.y > screen->r.max.y)
+			p.y = r.max.y - screen->r.max.y;
+		r = rectsubpt(r, p);
+
+		r = insetrect(r, -2);
+		if(scr){
+			if(b == nil)
+				b = allocwindow(scr, r, Refbackup, DWhite);
+			if(b == nil)
+				scr = nil;
+		}
+		if(scr == nil && save == nil){
+			if(b == nil)
+				b = screen;
+			save = allocimage(display, r, b->chan, 0, DNofill);
+			if(save == nil){
+				n = -1;
+				break;
+			}
+			draw(save, r, b, nil, r.min);
+		}
+		draw(b, r, backcol, nil, ZP);
+		border(b, r, 2, bordcol, ZP);
+		p = addpt(r.min, Pt(6, 6));
+		if(ask && ask[0]){
+			p = string(b, p, bordcol, ZP, font, ask);
+			if(buf) p.x += w;
+		}
+		if(buf){
+			t = p;
+			p = stringn(b, p, txtcol, ZP, font, buf, utfnlen(buf, tick));
+			draw(b, Rect(p.x-1, p.y, p.x+2, p.y+3), txtcol, nil, ZP);
+			draw(b, Rect(p.x, p.y, p.x+1, p.y+h), txtcol, nil, ZP);
+			draw(b, Rect(p.x-1, p.y+h-3, p.x+2, p.y+h), txtcol, nil, ZP);
+			p = string(b, p, txtcol, ZP, font, buf+tick);
+		}
+		flushimage(display, 1);
+
+nodraw:
+		switch((*_input)(m, c, &k)){
+		case -1:
+			done = 1;
+			n = -1;
+			break;
+		case 1:
+			if(buf == nil || k == Keof || k == '\n'){
+				done = 1;
+				break;
+			}
+			if(k == Knack || k == Kesc){
+				done = !n;
+				buf[n = tick = 0] = 0;
+				break;
+			}
+			if(k == Ksoh || k == Khome){
+				tick = 0;
+				continue;
+			}
+			if(k == Kenq || k == Kend){
+				tick = n;
+				continue;
+			}
+			if(k == Kright){
+				if(tick < n)
+					tick += chartorune(&k, buf+tick);
+				continue;
+			}
+			if(k == Kleft){
+				for(i = 0; i < n; i += l){
+					l = chartorune(&k, buf+i);
+					if(i+l >= tick){
+						tick = i;
+						break;
+					}
+				}
+				continue;
+			}
+			if(k == Ketb){
+				l = tick;
+				while(tick > 0){
+					tick--;
+					if(tick == 0 ||
+						strchr(" !\"#$%&'()*+,-./:;<=>?@`[\\]^{|}~", buf[tick-1]))
+						break;
+				}
+				memmove(buf+tick, buf+l, n-l);
+				buf[n -= l-tick] = 0;
+				break;
+			}
+			if(k == Kbs){
+				if(tick <= 0)
+					continue;
+				for(i = 0; i < n; i += l){
+					l = chartorune(&k, buf+i);
+					if(i+l >= tick){
+						memmove(buf+i, buf+i+l, n - (i+l));
+						buf[n -= l] = 0;
+						tick -= l;
+						break;
+					}
+				}
+				break;
+			}
+			if(k < 0x20 || k == Kdel || (k & 0xFF00) == KF || (k & 0xFF00) == Spec)
+				continue;
+			if((len-n) <= (l = runelen(k)))
+				continue;
+			memmove(buf+tick+l, buf+tick, n - tick);
+			runetochar(buf+tick, &k);
+			buf[n += l] = 0;
+			tick += l;
+			break;
+		case 0:
+			if(!ptinrect(m->xy, r)){
+				down = 0;
+				goto nodraw;
+			}
+			if(m->buttons & 7){
+				down = 1;
+				if(buf && m->xy.x >= (t.x - w)){
+					down = 0;
+					for(i = 0; i < n; i += l){
+						l = chartorune(&k, buf+i);
+						t.x += stringnwidth(font, buf+i, 1);
+						if(t.x > m->xy.x)
+							break;
+					}
+					tick = i;
+				}
+				continue;
+			}
+			done = down;
+			break;
+		}
+
+		if(b != screen) {
+			freeimage(b);
+			b = nil;
+		} else {
+			draw(b, save->r, save, nil, save->r.min);
+			freeimage(save);
+			save = nil;
+		}
+	}
+
+	replclipr(screen, 0, sc);
+
+	freeimage(backcol);
+	freeimage(bordcol);
+	flushimage(display, 1);
+
+	return n;
+}
--- /dev/null
+++ b/sys/src/libdraw/genmenuhit.c
@@ -1,0 +1,297 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <mouse.h>
+
+enum
+{
+	Margin = 4,		/* outside to text */
+	Border = 2,		/* outside to selection boxes */
+	Blackborder = 2,	/* width of outlining border */
+	Vspacing = 2,		/* extra spacing between lines of text */
+	Maxunscroll = 25,	/* maximum #entries before scrolling turns on */
+	Nscroll = 20,		/* number entries in scrolling part */
+	Scrollwid = 14,		/* width of scroll bar */
+	Gap = 4,			/* between text and scroll bar */
+};
+
+static	Image	*menutxt;
+static	Image	*back;
+static	Image	*high;
+static	Image	*bord;
+static	Image	*text;
+static	Image	*htext;
+
+static void
+menucolors(void)
+{
+	enum{
+		Cback,
+		Chigh,
+		Cbord,
+		Ctext,
+		Chtext,
+		Cmtext,
+		Ncols,
+	};
+	Theme th[Ncols] = {
+		[Cback] { "menuback",	0xEAFFEAFF },
+		[Chigh] { "menuhigh",	DDarkgreen },
+		[Cbord]	{ "menubord",	DMedgreen },
+		[Ctext]	{ "menutext",	DBlack },
+		[Chtext]{ "menuhtext",	0xEAFFEAFF },
+		[Cmtext]{ "menubar",	DDarkgreen },
+	};
+
+	readtheme(th, nelem(th), nil);
+	back = allocimage(display, Rect(0,0,1,1), screen->chan, 1, th[Cback].c);
+	high = allocimage(display, Rect(0,0,1,1), screen->chan, 1, th[Chigh].c);
+	bord = allocimage(display, Rect(0,0,1,1), screen->chan, 1, th[Cbord].c);
+	text = allocimage(display, Rect(0,0,1,1), screen->chan, 1, th[Ctext].c);
+	htext = allocimage(display, Rect(0,0,1,1), screen->chan, 1, th[Chtext].c);
+	menutxt = allocimage(display, Rect(0,0,1,1), screen->chan, 1, th[Cmtext].c);
+	if(back == nil || high == nil || bord == nil
+	|| text == nil || htext == nil || menutxt == nil)
+ 		goto Error;
+	return;
+
+    Error:
+	freeimage(menutxt);
+	freeimage(back);
+	freeimage(high);
+	freeimage(bord);
+	freeimage(menutxt);
+	freeimage(htext);
+	freeimage(text);
+	menutxt = display->black;
+	back = display->white;
+	high = display->black;
+	bord = display->black;
+	text = display->black;
+	htext = display->white;
+	menutxt = display->black;
+}
+
+/*
+ * r is a rectangle holding the text elements.
+ * return the rectangle, including its black edge, holding element i.
+ */
+static Rectangle
+menurect(Rectangle r, int i)
+{
+	if(i < 0)
+		return Rect(0, 0, 0, 0);
+	r.min.y += (font->height+Vspacing)*i;
+	r.max.y = r.min.y+font->height+Vspacing;
+	return insetrect(r, Border-Margin);
+}
+
+/*
+ * r is a rectangle holding the text elements.
+ * return the element number containing p.
+ */
+static int
+menusel(Rectangle r, Point p)
+{
+	if(!ptinrect(p, r))
+		return -1;
+	return (p.y-r.min.y)/(font->height+Vspacing);
+}
+
+static void
+paintitem(Image *m, Menu *menu, Rectangle textr, int off, int i, int highlight, Image *save, Image *restore)
+{
+	char *item;
+	Rectangle r;
+	Point pt;
+
+	if(i < 0)
+		return;
+	r = menurect(textr, i);
+	if(restore){
+		draw(m, r, restore, nil, restore->r.min);
+		return;
+	}
+	if(save)
+		draw(save, save->r, m, nil, r.min);
+	item = menu->item? menu->item[i+off] : (*menu->gen)(i+off);
+	pt.x = (textr.min.x+textr.max.x-stringwidth(font, item))/2;
+	pt.y = textr.min.y+i*(font->height+Vspacing);
+	draw(m, r, highlight? high : back, nil, pt);
+	string(m, pt, highlight? htext : text, pt, font, item);
+}
+
+/*
+ * menur is a rectangle holding all the highlightable text elements.
+ * track mouse while inside the box, return what's selected when button
+ * is raised, -1 as soon as it leaves box.
+ * invariant: nothing is highlighted on entry or exit.
+ */
+static int
+menuscan(Image *b, Menu *menu, int but, Mouse *m, void (*_readmouse)(Mouse*), Rectangle textr, int off, int lasti, Image *save)
+{
+	int i;
+
+	paintitem(b, menu, textr, off, lasti, 1, save, nil);
+	for((*_readmouse)(m); m->buttons & (1<<(but-1)); (*_readmouse)(m)){
+		i = menusel(textr, m->xy);
+		if(i != -1 && i == lasti)
+			continue;
+		paintitem(b, menu, textr, off, lasti, 0, nil, save);
+		if(i == -1)
+			return i;
+		lasti = i;
+		paintitem(b, menu, textr, off, lasti, 1, save, nil);
+	}
+	return lasti;
+}
+
+static void
+menupaint(Image *b, Menu *menu, Rectangle textr, int off, int nitemdrawn)
+{
+	int i;
+
+	draw(b, insetrect(textr, Border-Margin), back, nil, ZP);
+	for(i = 0; i<nitemdrawn; i++)
+		paintitem(b, menu, textr, off, i, 0, nil, nil);
+}
+
+static void
+menuscrollpaint(Image *b, Rectangle scrollr, int off, int nitem, int nitemdrawn)
+{
+	Rectangle r;
+
+	draw(b, scrollr, back, nil, ZP);
+	r.min.x = scrollr.min.x;
+	r.max.x = scrollr.max.x;
+	r.min.y = scrollr.min.y + (Dy(scrollr)*off)/nitem;
+	r.max.y = scrollr.min.y + (Dy(scrollr)*(off+nitemdrawn))/nitem;
+	if(r.max.y < r.min.y+2)
+		r.max.y = r.min.y+2;
+	border(b, r, 1, bord, ZP);
+	draw(b, insetrect(r, 1), menutxt, nil, ZP);
+}
+
+int
+genmenuhit(int but, Mouse *m, void (*_readmouse)(Mouse*), void (*_moveto)(Mouse*, Point), Menu *menu, Screen *scr)
+{
+	int i, nitem, nitemdrawn, maxwid, lasti, off, noff, wid, screenitem;
+	int scrolling;
+	Rectangle r, menur, sc, textr, scrollr;
+	Image *b, *save, *backup;
+	Point pt;
+	char *item;
+
+	if(back == nil)
+		menucolors();
+	sc = screen->clipr;
+	replclipr(screen, 0, screen->r);
+	maxwid = 0;
+	for(nitem = 0;
+	    item = menu->item? menu->item[nitem] : (*menu->gen)(nitem);
+	    nitem++){
+		i = stringwidth(font, item);
+		if(i > maxwid)
+			maxwid = i;
+	}
+	if(menu->lasthit<0 || menu->lasthit>=nitem)
+		menu->lasthit = 0;
+	screenitem = (Dy(screen->r)-10)/(font->height+Vspacing);
+	if(nitem>Maxunscroll || nitem>screenitem){
+		scrolling = 1;
+		nitemdrawn = Nscroll;
+		if(nitemdrawn > screenitem)
+			nitemdrawn = screenitem;
+		wid = maxwid + Gap + Scrollwid;
+		off = menu->lasthit - nitemdrawn/2;
+		if(off < 0)
+			off = 0;
+		if(off > nitem-nitemdrawn)
+			off = nitem-nitemdrawn;
+		lasti = menu->lasthit-off;
+	}else{
+		scrolling = 0;
+		nitemdrawn = nitem;
+		wid = maxwid;
+		off = 0;
+		lasti = menu->lasthit;
+	}
+	r = insetrect(Rect(0, 0, wid, nitemdrawn*(font->height+Vspacing)), -Margin);
+	r = rectsubpt(r, Pt(wid/2, lasti*(font->height+Vspacing)+font->height/2));
+	r = rectaddpt(r, m->xy);
+	pt = ZP;
+	if(r.max.x>screen->r.max.x)
+		pt.x = screen->r.max.x-r.max.x;
+	if(r.max.y>screen->r.max.y)
+		pt.y = screen->r.max.y-r.max.y;
+	if(r.min.x<screen->r.min.x)
+		pt.x = screen->r.min.x-r.min.x;
+	if(r.min.y<screen->r.min.y)
+		pt.y = screen->r.min.y-r.min.y;
+	menur = rectaddpt(r, pt);
+	textr.max.x = menur.max.x-Margin;
+	textr.min.x = textr.max.x-maxwid;
+	textr.min.y = menur.min.y+Margin;
+	textr.max.y = textr.min.y + nitemdrawn*(font->height+Vspacing);
+	if(scrolling){
+		scrollr = insetrect(menur, Border);
+		scrollr.max.x = scrollr.min.x+Scrollwid;
+	}else
+		scrollr = Rect(0, 0, 0, 0);
+
+	if(scr){
+		b = allocwindow(scr, menur, Refbackup, DWhite);
+		if(b == nil)
+			b = screen;
+		backup = nil;
+	}else{
+		b = screen;
+		backup = allocimage(display, menur, screen->chan, 0, -1);
+		if(backup)
+			draw(backup, menur, screen, nil, menur.min);
+	}
+	draw(b, menur, back, nil, ZP);
+	border(b, menur, Blackborder, bord, ZP);
+	save = allocimage(display, menurect(textr, 0), screen->chan, 0, -1);
+	r = menurect(textr, lasti);
+	if(pt.x || pt.y)
+		(*_moveto)(m, divpt(addpt(r.min, r.max), 2));
+	menupaint(b, menu, textr, off, nitemdrawn);
+	if(scrolling)
+		menuscrollpaint(b, scrollr, off, nitem, nitemdrawn);
+	while(m->buttons & (1<<(but-1))){
+		lasti = menuscan(b, menu, but, m, _readmouse, textr, off, lasti, save);
+		if(lasti >= 0)
+			break;
+		while(!ptinrect(m->xy, textr) && (m->buttons & (1<<(but-1)))){
+			if(scrolling && ptinrect(m->xy, scrollr)){
+				noff = ((m->xy.y-scrollr.min.y)*nitem)/Dy(scrollr);
+				noff -= nitemdrawn/2;
+				if(noff < 0)
+					noff = 0;
+				if(noff > nitem-nitemdrawn)
+					noff = nitem-nitemdrawn;
+				if(noff != off){
+					off = noff;
+					menupaint(b, menu, textr, off, nitemdrawn);
+					menuscrollpaint(b, scrollr, off, nitem, nitemdrawn);
+				}
+			}
+			(*_readmouse)(m);
+		}
+	}
+	if(b != screen)
+		freeimage(b);
+	if(backup){
+		draw(screen, menur, backup, nil, menur.min);
+		freeimage(backup);
+	}
+	freeimage(save);
+	replclipr(screen, 0, sc);
+	flushimage(display, 1);
+	if(lasti >= 0){
+		menu->lasthit = lasti+off;
+		return menu->lasthit;
+	}
+	return -1;
+}
--- a/sys/src/libdraw/menuhit.c
+++ /dev/null
@@ -1,298 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <draw.h>
-#include <thread.h>
-#include <mouse.h>
-
-enum
-{
-	Margin = 4,		/* outside to text */
-	Border = 2,		/* outside to selection boxes */
-	Blackborder = 2,	/* width of outlining border */
-	Vspacing = 2,		/* extra spacing between lines of text */
-	Maxunscroll = 25,	/* maximum #entries before scrolling turns on */
-	Nscroll = 20,		/* number entries in scrolling part */
-	Scrollwid = 14,		/* width of scroll bar */
-	Gap = 4,			/* between text and scroll bar */
-};
-
-static	Image	*menutxt;
-static	Image	*back;
-static	Image	*high;
-static	Image	*bord;
-static	Image	*text;
-static	Image	*htext;
-
-static
-void
-menucolors(void)
-{
-	enum{
-		Cback,
-		Chigh,
-		Cbord,
-		Ctext,
-		Chtext,
-		Cmtext,
-		Ncols,
-	};
-	Theme th[Ncols] = {
-		[Cback] { "menuback",	0xEAFFEAFF },
-		[Chigh] { "menuhigh",	DDarkgreen },
-		[Cbord]	{ "menubord",	DMedgreen },
-		[Ctext]	{ "menutext",	DBlack },
-		[Chtext]{ "menuhtext",	0xEAFFEAFF },
-		[Cmtext]{ "menubar",	DDarkgreen },
-	};
-
-	readtheme(th, nelem(th), nil);
-	back = allocimage(display, Rect(0,0,1,1), screen->chan, 1, th[Cback].c);
-	high = allocimage(display, Rect(0,0,1,1), screen->chan, 1, th[Chigh].c);
-	bord = allocimage(display, Rect(0,0,1,1), screen->chan, 1, th[Cbord].c);
-	text = allocimage(display, Rect(0,0,1,1), screen->chan, 1, th[Ctext].c);
-	htext = allocimage(display, Rect(0,0,1,1), screen->chan, 1, th[Chtext].c);
-	menutxt = allocimage(display, Rect(0,0,1,1), screen->chan, 1, th[Cmtext].c);
-	if(back == nil || high == nil || bord == nil
-	|| text == nil || htext == nil || menutxt == nil)
-		goto Error;
-	return;
-
-    Error:
-	freeimage(back);
-	freeimage(high);
-	freeimage(bord);
-	freeimage(menutxt);
-	freeimage(text);
-	freeimage(htext);
-	back = display->white;
-	high = display->black;
-	bord = display->black;
-	text = display->black;
-	htext = display->white;
-	menutxt = display->black;
-}
-
-/*
- * r is a rectangle holding the text elements.
- * return the rectangle, including its black edge, holding element i.
- */
-static Rectangle
-menurect(Rectangle r, int i)
-{
-	if(i < 0)
-		return Rect(0, 0, 0, 0);
-	r.min.y += (font->height+Vspacing)*i;
-	r.max.y = r.min.y+font->height+Vspacing;
-	return insetrect(r, Border-Margin);
-}
-
-/*
- * r is a rectangle holding the text elements.
- * return the element number containing p.
- */
-static int
-menusel(Rectangle r, Point p)
-{
-	if(!ptinrect(p, r))
-		return -1;
-	return (p.y-r.min.y)/(font->height+Vspacing);
-}
-
-static
-void
-paintitem(Image *m, Menu *menu, Rectangle textr, int off, int i, int highlight, Image *save, Image *restore)
-{
-	char *item;
-	Rectangle r;
-	Point pt;
-
-	if(i < 0)
-		return;
-	r = menurect(textr, i);
-	if(restore){
-		draw(m, r, restore, nil, restore->r.min);
-		return;
-	}
-	if(save)
-		draw(save, save->r, m, nil, r.min);
-	item = menu->item? menu->item[i+off] : (*menu->gen)(i+off);
-	pt.x = (textr.min.x+textr.max.x-stringwidth(font, item))/2;
-	pt.y = textr.min.y+i*(font->height+Vspacing);
-	draw(m, r, highlight? high : back, nil, pt);
-	string(m, pt, highlight? htext : text, pt, font, item);
-}
-
-/*
- * menur is a rectangle holding all the highlightable text elements.
- * track mouse while inside the box, return what's selected when button
- * is raised, -1 as soon as it leaves box.
- * invariant: nothing is highlighted on entry or exit.
- */
-static int
-menuscan(Image *m, Menu *menu, int but, Mousectl *mc, Rectangle textr, int off, int lasti, Image *save)
-{
-	int i;
-
-	paintitem(m, menu, textr, off, lasti, 1, save, nil);
-	for(readmouse(mc); mc->buttons & (1<<(but-1)); readmouse(mc)){
-		i = menusel(textr, mc->xy);
-		if(i != -1 && i == lasti)
-			continue;
-		paintitem(m, menu, textr, off, lasti, 0, nil, save);
-		if(i == -1)
-			return i;
-		lasti = i;
-		paintitem(m, menu, textr, off, lasti, 1, save, nil);
-	}
-	return lasti;
-}
-
-static void
-menupaint(Image *m, Menu *menu, Rectangle textr, int off, int nitemdrawn)
-{
-	int i;
-
-	draw(m, insetrect(textr, Border-Margin), back, nil, ZP);
-	for(i = 0; i<nitemdrawn; i++)
-		paintitem(m, menu, textr, off, i, 0, nil, nil);
-}
-
-static void
-menuscrollpaint(Image *m, Rectangle scrollr, int off, int nitem, int nitemdrawn)
-{
-	Rectangle r;
-
-	draw(m, scrollr, back, nil, ZP);
-	r.min.x = scrollr.min.x;
-	r.max.x = scrollr.max.x;
-	r.min.y = scrollr.min.y + (Dy(scrollr)*off)/nitem;
-	r.max.y = scrollr.min.y + (Dy(scrollr)*(off+nitemdrawn))/nitem;
-	if(r.max.y < r.min.y+2)
-		r.max.y = r.min.y+2;
-	border(m, r, 1, bord, ZP);
-	draw(m, insetrect(r, 1), menutxt, nil, ZP);
-}
-
-int
-menuhit(int but, Mousectl *mc, Menu *menu, Screen *scr)
-{
-	int i, nitem, nitemdrawn, maxwid, lasti, off, noff, wid, screenitem;
-	int scrolling;
-	Rectangle r, menur, sc, textr, scrollr;
-	Image *b, *save, *backup;
-	Point pt;
-	char *item;
-
-	if(back == nil)
-		menucolors();
-	sc = screen->clipr;
-	replclipr(screen, 0, screen->r);
-	maxwid = 0;
-	for(nitem = 0;
-	    item = menu->item? menu->item[nitem] : (*menu->gen)(nitem);
-	    nitem++){
-		i = stringwidth(font, item);
-		if(i > maxwid)
-			maxwid = i;
-	}
-	if(menu->lasthit<0 || menu->lasthit>=nitem)
-		menu->lasthit = 0;
-	screenitem = (Dy(screen->r)-10)/(font->height+Vspacing);
-	if(nitem>Maxunscroll || nitem>screenitem){
-		scrolling = 1;
-		nitemdrawn = Nscroll;
-		if(nitemdrawn > screenitem)
-			nitemdrawn = screenitem;
-		wid = maxwid + Gap + Scrollwid;
-		off = menu->lasthit - nitemdrawn/2;
-		if(off < 0)
-			off = 0;
-		if(off > nitem-nitemdrawn)
-			off = nitem-nitemdrawn;
-		lasti = menu->lasthit-off;
-	}else{
-		scrolling = 0;
-		nitemdrawn = nitem;
-		wid = maxwid;
-		off = 0;
-		lasti = menu->lasthit;
-	}
-	r = insetrect(Rect(0, 0, wid, nitemdrawn*(font->height+Vspacing)), -Margin);
-	r = rectsubpt(r, Pt(wid/2, lasti*(font->height+Vspacing)+font->height/2));
-	r = rectaddpt(r, mc->xy);
-	pt = ZP;
-	if(r.max.x>screen->r.max.x)
-		pt.x = screen->r.max.x-r.max.x;
-	if(r.max.y>screen->r.max.y)
-		pt.y = screen->r.max.y-r.max.y;
-	if(r.min.x<screen->r.min.x)
-		pt.x = screen->r.min.x-r.min.x;
-	if(r.min.y<screen->r.min.y)
-		pt.y = screen->r.min.y-r.min.y;
-	menur = rectaddpt(r, pt);
-	textr.max.x = menur.max.x-Margin;
-	textr.min.x = textr.max.x-maxwid;
-	textr.min.y = menur.min.y+Margin;
-	textr.max.y = textr.min.y + nitemdrawn*(font->height+Vspacing);
-	if(scrolling){
-		scrollr = insetrect(menur, Border);
-		scrollr.max.x = scrollr.min.x+Scrollwid;
-	}else
-		scrollr = Rect(0, 0, 0, 0);
-
-	if(scr){
-		b = allocwindow(scr, menur, Refbackup, DWhite);
-		if(b == nil)
-			b = screen;
-		backup = nil;
-	}else{
-		b = screen;
-		backup = allocimage(display, menur, screen->chan, 0, -1);
-		if(backup)
-			draw(backup, menur, screen, nil, menur.min);
-	}
-	draw(b, menur, back, nil, ZP);
-	border(b, menur, Blackborder, bord, ZP);
-	save = allocimage(display, menurect(textr, 0), screen->chan, 0, -1);
-	r = menurect(textr, lasti);
-	if(pt.x || pt.y)
-		moveto(mc, divpt(addpt(r.min, r.max), 2));
-	menupaint(b, menu, textr, off, nitemdrawn);
-	if(scrolling)
-		menuscrollpaint(b, scrollr, off, nitem, nitemdrawn);
-	while(mc->buttons & (1<<(but-1))){
-		lasti = menuscan(b, menu, but, mc, textr, off, lasti, save);
-		if(lasti >= 0)
-			break;
-		while(!ptinrect(mc->xy, textr) && (mc->buttons & (1<<(but-1)))){
-			if(scrolling && ptinrect(mc->xy, scrollr)){
-				noff = ((mc->xy.y-scrollr.min.y)*nitem)/Dy(scrollr);
-				noff -= nitemdrawn/2;
-				if(noff < 0)
-					noff = 0;
-				if(noff > nitem-nitemdrawn)
-					noff = nitem-nitemdrawn;
-				if(noff != off){
-					off = noff;
-					menupaint(b, menu, textr, off, nitemdrawn);
-					menuscrollpaint(b, scrollr, off, nitem, nitemdrawn);
-				}
-			}
-			readmouse(mc);
-		}
-	}
-	if(b != screen)
-		freeimage(b);
-	if(backup){
-		draw(screen, menur, backup, nil, menur.min);
-		freeimage(backup);
-	}
-	freeimage(save);
-	replclipr(screen, 0, sc);
-	flushimage(display, 1);
-	if(lasti >= 0){
-		menu->lasthit = lasti+off;
-		return menu->lasthit;
-	}
-	return -1;
-}
--- a/sys/src/libdraw/mkfile
+++ b/sys/src/libdraw/mkfile
@@ -28,6 +28,8 @@
 	fmt.$O\
 	font.$O\
 	freesubfont.$O\
+	genenter.$O\
+	genmenuhit.$O\
 	getdefont.$O\
 	getrect.$O\
 	getsubfont.$O\
--