shithub: eesp

Download patch

ref: c70b508504537491dbee49e5c5e1a5938998ac83
parent: 627806e4de1f01a670ab6a3ebb960609cbda7a06
author: sirjofri <sirjofri@sirjofri.de>
date: Mon Jul 22 11:54:21 EDT 2024

adds same-code libenter libeenter and genenter

--- a/libeenter.c
+++ b/libeenter.c
@@ -3,369 +3,88 @@
 #include <draw.h>
 #include <event.h>
 #include <keyboard.h>
+#include "genenter.h"
 
-static void
-sorttick(int *t1, int *t2)
+static int
+eeinitsave(Enterparams *p)
 {
-	int i;
-	if(*t1 <= *t2)
-		return;
-	i = *t1;
-	*t1 = *t2;
-	*t2 = i;
-}
-
-static Point
-drawstring(Image *b, char *buf, Point p, int s, int e, Image *bg, int h)
-{
-	sorttick(&s, &e);
-	p = stringn(b, p, display->black, ZP, font, buf, utfnlen(buf, s));
-	if(s == e){
-		draw(b, Rect(p.x-1, p.y, p.x+2, p.y+3), display->black, nil, ZP);
-		draw(b, Rect(p.x, p.y, p.x+1, p.y+h), display->black, nil, ZP);
-		draw(b, Rect(p.x-1, p.y+h-3, p.x+2, p.y+h), display->black, nil, ZP);
-	} else {
-		p = stringnbg(b, p, display->black, ZP, font, buf+s, utfnlen(buf+s, e-s), bg, ZP);
+	if(p->save)
+		return 0;
+	p->save = allocimage(display, p->r, p->b->chan, 0, DNofill);
+	if(p->save == nil){
+		return 1;
 	}
-	p = string(b, p, display->black, ZP, font, buf+e);
-	return p;
+	draw(p->save, p->r, p->b, nil, p->r.min);
+	return 0;
 }
 
-static void
-enterselection(char *buf, int len, int s, int e, int *sp, int *ep)
-{
-	int l, i;
-	Rune k;
-	sorttick(&s, &e);
-	*sp = *ep = -1;
-	for(i = 0; i < len; i += l){
-		l = chartorune(&k, buf+i);
-		if(*sp >= 0 && i >= e){
-			*ep = i;
-			break;
-		}
-		if(*sp < 0 && i >= s)
-			*sp = i;
-	}
-}
-
 static int
-delsubstring(char *buf, int len, int s, int e, int *nlen)
+eegetevent(Enterparams *p)
 {
-	int l, i, sp, ep;
-	Rune k;
-	if(s == e)
-		return s;
-	if (s < 0)
-		s = 0;
-	enterselection(buf, len, s, e, &sp, &ep);
-	memmove(buf+sp, buf+ep, len - ep);
-	buf[len-ep+sp] = 0;
-	if (nlen)
-		*nlen = sp+len-ep;
-	return sp;
-}
+	Event ev;
+	int i = Ekeyboard;
 
-static int
-entersnarf(char *buf, int len, int s, int e, int *nlen)
-{
-	int fd, sp, ep;
-	fd = open("/dev/snarf", OWRITE|OTRUNC);
-	if(fd < 0){
-		fprint(2, "error: %r\n");
-		return s;
+	if(p->aux)
+		i |= Emouse;
+
+	replclipr(p->b, 0, p->sc);
+	i = eread(i, &ev);
+
+	/* screen might have been resized */
+	if(p->b != screen || !eqrect(screen->clipr, p->sc)){
+		freeimage(p->save);
+		p->save = nil;
 	}
-	enterselection(buf, len, s, e, &sp, &ep);
-	if(sp < 0 || ep < 0){
-		close(fd);
-		return sp < 0 ? ep : sp;
+	p->b = screen;
+	p->sc = p->b->clipr;
+	replclipr(p->b, 0, p->b->r);
+	
+	if(i == Emouse){
+		p->m = ev.mouse;
+		return Gmouse;
 	}
-	pwrite(fd, &buf[sp], ep-sp, 0);
-	close(fd);
-	return delsubstring(buf, len, s, e, nlen);
+	if(i == Ekeyboard){
+		p->k = ev.kbdc;
+		return Gkeyboard;
+	}
+	return -1;
 }
 
 static void
-enterpaste(char *buf, int len, int max, int s, int e, int *nlen, int *ns, int *ne)
+eeloopcleanup(Enterparams *p)
 {
-	char *str;
-	int fd, sp, ep, n, newlen;
-	
-	fd = open("/dev/snarf", OREAD);
-	if(fd < 0){
-		fprint(2, "error: %r\n");
-		return;
+	if(p->save){
+		draw(p->b, p->save->r, p->save, nil, p->save->r.min);
+		freeimage(p->save);
+		p->save = nil;
 	}
-	str = mallocz(max*sizeof(char), 1);
-	n = pread(fd, str, max-1, 0);
-	str[n] = 0;
-	close(fd);
+}
 
-	newlen = len;
-	enterselection(buf, len, s, e, &sp, &ep);
-	s = entersnarf(buf, len, s, e, &newlen);
-
-	memmove(buf+n+s, buf+s, newlen-s);
-	memcpy(buf+s, str, n);
-
-	free(str);
-	if (nlen)
-		*nlen = newlen+n;
-	if (ns)
-		*ns = s;
-	if (ne)
-		*ne = s+n;
+static void
+eecleanup(Enterparams *p)
+{
+	replclipr(p->b, 0, p->sc);
 }
 
 int
 eenter(char *ask, char *buf, int len, Mouse *m)
 {
-	int done, down, tick, n, h, w, l, i;
-	int tick2, mb, ti;
-	Image *b, *save, *backcol, *bordcol;
-	Point p, o, t;
-	Rectangle r, sc;
-	Event ev;
-	Rune k;
+	Enterparams p;
 
-	mb = 0;
-	o = screen->r.min;
-	backcol = allocimagemix(display, DPurpleblue, DWhite);
-	bordcol = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue);
-	if(backcol == nil || bordcol == nil)
-		return -1;
-
 	while(ecankbd())
 		ekbd();
 
-	if(m) o = m->xy;
+	if(m)
+		p.o = m->xy;
 
-	if(buf && len > 0)
-		n = strlen(buf);
-	else {
-		buf = nil;
-		len = 0;
-		n = 0;
-	}
+	p.b = screen;
+	p.sc = p.b->clipr;
+	replclipr(p.b, 0, p.b->r);
 
-	k = -1;
-	tick2 = 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 = drawstring(b, buf, p, tick, tick2, bordcol, h);
-		}
-		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;
-				n = tick2 = tick = 0;
-				buf[n] = 0;
-				break;
-			}
-			if(k == Ksoh || k == Khome){
-				tick2 = tick = 0;
-				continue;
-			}
-			if(k == Kenq || k == Kend){
-				tick2 = tick = n;
-				continue;
-			}
-			if(k == Kright){
-				if(tick2 < n)
-					tick2 = 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){
-						tick2 = 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 && tick2 <= 0)
-					continue;
-				if (tick == tick2)
-					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;
-							tick2 = tick -= l;
-							break;
-						}
-					}
-				else
-					tick = tick2 = delsubstring(buf, n, tick-1, tick2, &n);
-				break;
-			}
-			if(k < 0x20 || k == Kdel || (k & 0xFF00) == KF || (k & 0xFF00) == Spec)
-				continue;
-			if((len-n) <= (l = runelen(k)))
-				continue;
-			tick = delsubstring(buf, n, tick, tick2, &n);
-			memmove(buf+tick+l, buf+tick, n - tick);
-			runetochar(buf+tick, &k);
-			buf[n += l] = 0;
-			tick2 = tick += l;
-			break;
-		case Emouse:
-			*m = ev.mouse;
-			if(mb&1 && !m->buttons&1) {
-				sorttick(&tick, &tick2);
-				mb = m->buttons;
-			}
-			if(m->buttons&1 && mb&6){
-				if (m->buttons != mb)
-					goto Mchecks;
-				continue;
-			}
-			if(m->buttons&1 && mb&6){
-				continue;
-			}
-			if(!ptinrect(m->xy, r)){
-				down = 0;
-				goto nodraw;
-			}
-			if(m->buttons&1){
-				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;
-					}
-					if(mb & 1){
-						tick2 = i;
-					}else
-						tick = tick2 = i;
-				}
-				if(!((m->buttons&2) || (m->buttons&4))){
-					mb = m->buttons;
-					continue;
-				}
-			}
-Mchecks:
-			if(!(mb&2) && (m->buttons&3) == 3){
-				tick = tick2 = entersnarf(buf, n, tick, tick2, &n);
-				mb = m->buttons;
-			}
-			if(!(mb&4) && (m->buttons&5) == 5){
-				enterpaste(buf, n, len, tick, tick2, &n, &tick, &tick2);
-				mb = m->buttons;
-			}
-
-			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;
+	p.aux = m;
+	p.initsave = eeinitsave;
+	p.getevent = eegetevent;
+	p.loopcleanup = eeloopcleanup;
+	p.cleanup = eecleanup;
+	return _genenter(ask, buf, len, &p);
 }
-
--- a/libenter.c
+++ b/libenter.c
@@ -4,10 +4,114 @@
 #include <thread.h>
 #include <mouse.h>
 #include <keyboard.h>
+#include "genenter.h"
 
+typedef struct Enterdata Enterdata;
+struct Enterdata {
+	Mousectl *mc;
+	Keyboardctl *kc;
+	Screen *scr;
+	Alt a[3];
+};
+
+static int
+einitsave(Enterparams *p)
+{
+	Enterdata *d = (Enterdata*)p->aux;
+	if(d->scr){
+		if(p->b == nil)
+			p->b = allocwindow(d->scr, p->r, Refbackup, DWhite);
+		if(p->b == nil)
+			d->scr = nil;
+	}
+	if(d->scr == nil && p->save == nil){
+		if(p->b == nil)
+			p->b = screen;
+		p->save = allocimage(display, p->r, p->b->chan, 0, DNofill);
+		if(p->save == nil)
+			return 1;
+		draw(p->save, p->r, p->b, nil, p->r.min);
+	}
+	return 0;
+}
+
+static int
+egetevent(Enterparams *p)
+{
+	Enterdata *d = (Enterdata*)p->aux;
+	
+	switch(alt(d->a)){
+	case 0:
+		return Gkeyboard;
+	case 1:
+		return Gmouse;
+	}
+	return -1;
+}
+
+static void
+eloopcleanup(Enterparams *p)
+{
+	if(p->b != screen){
+		freeimage(p->b);
+		p->b = nil;
+	} else {
+		draw(p->b, p->save->r, p->save, nil, p->save->r.min);
+		freeimage(p->save);
+		p->save = nil;
+	}
+}
+
+static void
+ecleanup(Enterparams *p)
+{
+	replclipr(screen, 0, p->sc);
+}
+
 int
 enter(char *ask, char *buf, int len, Mousectl *mc, Keyboardctl *kc, Screen *scr)
 {
+	Enterparams p;
+	Enterdata d;
+	int n;
+	
+	p.sc = screen->clipr;
+	replclipr(screen, 0, screen->r);
+	n = 0;
+	if(kc){
+		while(nbrecv(kc->c, nil) == 1)
+			;
+		d.a[n].op = CHANRCV;
+		d.a[n].c = kc->c;
+		d.a[n].v = &p.k;
+		n++;
+	}
+	if(mc){
+		p.o = mc->xy;
+		d.a[n].op = CHANRCV;
+		d.a[n].c = mc->c;
+		d.a[n].v = &p.m;
+		n++;
+	}
+	d.a[n].op = CHANEND;
+	d.a[n].c = nil;
+	d.a[n].v = nil;
+	
+	d.mc = mc;
+	d.kc = kc;
+	d.scr = scr;
+	p.aux = &d;
+	p.initsave = einitsave;
+	p.getevent = egetevent;
+	p.loopcleanup = eloopcleanup;
+	p.cleanup = ecleanup;
+	return _genenter(ask, buf, len, &p);
+}
+
+#ifdef COMMENT
+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;
 	Point p, o, t;
@@ -235,3 +339,4 @@
 
 	return n;
 }
+#endif
--- /dev/null
+++ b/libgenenter.c
@@ -1,0 +1,340 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <keyboard.h>
+#include <mouse.h>
+#include "genenter.h"
+
+static void
+sorttick(int *t1, int *t2)
+{
+	int i;
+	if(*t1 <= *t2)
+		return;
+	i = *t1;
+	*t1 = *t2;
+	*t2 = i;
+}
+
+static Point
+drawstring(Image *b, char *buf, Point p, int s, int e, Image *bg, int h)
+{
+	sorttick(&s, &e);
+	p = stringn(b, p, display->black, ZP, font, buf, utfnlen(buf, s));
+	if(s == e){
+		draw(b, Rect(p.x-1, p.y, p.x+2, p.y+3), display->black, nil, ZP);
+		draw(b, Rect(p.x, p.y, p.x+1, p.y+h), display->black, nil, ZP);
+		draw(b, Rect(p.x-1, p.y+h-3, p.x+2, p.y+h), display->black, nil, ZP);
+	} else {
+		p = stringnbg(b, p, display->black, ZP, font, buf+s, utfnlen(buf+s, e-s), bg, ZP);
+	}
+	p = string(b, p, display->black, ZP, font, buf+e);
+	return p;
+}
+
+static void
+enterselection(char *buf, int len, int s, int e, int *sp, int *ep)
+{
+	int l, i;
+	Rune k;
+	sorttick(&s, &e);
+	*sp = *ep = -1;
+	for(i = 0; i < len; i += l){
+		l = chartorune(&k, buf+i);
+		if(*sp >= 0 && i >= e){
+			*ep = i;
+			break;
+		}
+		if(*sp < 0 && i >= s)
+			*sp = i;
+	}
+}
+
+static int
+delsubstring(char *buf, int len, int s, int e, int *nlen)
+{
+	int sp, ep;
+	if(s == e)
+		return s;
+	if (s < 0)
+		s = 0;
+	enterselection(buf, len, s, e, &sp, &ep);
+	memmove(buf+sp, buf+ep, len - ep);
+	buf[len-ep+sp] = 0;
+	if (nlen)
+		*nlen = sp+len-ep;
+	return sp;
+}
+
+static int
+entersnarf(char *buf, int len, int s, int e, int *nlen)
+{
+	int fd, sp, ep;
+	fd = open("/dev/snarf", OWRITE|OTRUNC);
+	if(fd < 0){
+		fprint(2, "error: %r\n");
+		return s;
+	}
+	enterselection(buf, len, s, e, &sp, &ep);
+	if(sp < 0 || ep < 0){
+		close(fd);
+		return sp < 0 ? ep : sp;
+	}
+	pwrite(fd, &buf[sp], ep-sp, 0);
+	close(fd);
+	return delsubstring(buf, len, s, e, nlen);
+}
+
+static void
+enterpaste(char *buf, int len, int max, int s, int e, int *nlen, int *ns, int *ne)
+{
+	char *str;
+	int fd, sp, ep, n, newlen;
+	
+	fd = open("/dev/snarf", OREAD);
+	if(fd < 0){
+		fprint(2, "error: %r\n");
+		return;
+	}
+	str = mallocz(max*sizeof(char), 1);
+	n = pread(fd, str, max-1, 0);
+	str[n] = 0;
+	close(fd);
+
+	newlen = len;
+	enterselection(buf, len, s, e, &sp, &ep);
+	s = entersnarf(buf, len, s, e, &newlen);
+
+	memmove(buf+n+s, buf+s, newlen-s);
+	memcpy(buf+s, str, n);
+
+	free(str);
+	if (nlen)
+		*nlen = newlen+n;
+	if (ns)
+		*ns = s;
+	if (ne)
+		*ne = s+n;
+}
+
+int
+_genenter(char *ask, char *buf, int len, Enterparams *ps)
+{
+	int done, down, tick, n, h, w, l, i;
+	int tick2, mb;
+	Image *backcol, *bordcol;
+	Point p, t;
+	Rune k;
+	Mouse m;
+
+	mb = 0;
+	if(!ptinrect(ps->o, screen->r))
+		ps->o = screen->r.min;
+	backcol = allocimagemix(display, DPurpleblue, DWhite);
+	bordcol = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue);
+	if(backcol == nil || bordcol == nil)
+		return -1;
+
+	if(buf && len > 0)
+		n = strlen(buf);
+	else {
+		buf = nil;
+		len = 0;
+		n = 0;
+	}
+
+	ps->k = -1;
+	tick2 = tick = n;
+	ps->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);
+		}
+		ps->r = rectaddpt(insetrect(Rpt(ZP, p), -4), ps->o);
+		p.x = 0;
+		ps->r = rectsubpt(ps->r, p);
+
+		p = ZP;
+		if(ps->r.min.x < screen->r.min.x)
+			p.x = screen->r.min.x - ps->r.min.x;
+		if(ps->r.min.y < screen->r.min.y)
+			p.y = screen->r.min.y - ps->r.min.y;
+		ps->r = rectaddpt(ps->r, p);
+		p = ZP;
+		if(ps->r.max.x > screen->r.max.x)
+			p.x = ps->r.max.x - screen->r.max.x;
+		if(ps->r.max.y > screen->r.max.y)
+			p.y = ps->r.max.y - screen->r.max.y;
+		ps->r = rectsubpt(ps->r, p);
+
+		ps->r = insetrect(ps->r, -2);
+		if(ps->initsave)
+			if(ps->initsave(ps)){
+				n = -1;
+				break;
+			}
+		draw(ps->b, ps->r, backcol, nil, ZP);
+		border(ps->b, ps->r, 2, bordcol, ZP);
+		p = addpt(ps->r.min, Pt(6, 6));
+		if(ask && ask[0]){
+			p = string(ps->b, p, bordcol, ZP, font, ask);
+			if(buf) p.x += w;
+		}
+		if(buf){
+			t = p;
+			p = drawstring(ps->b, buf, p, tick, tick2, bordcol, h);
+		}
+		flushimage(display, 1);
+
+nodraw:
+		i = ps->getevent(ps);
+		switch(i){
+		default:
+			done = 1;
+			n = -1;
+			break;
+		case Gkeyboard:
+			k = ps->k;
+			if(buf == nil || k == Keof || k == '\n'){
+				done = 1;
+				break;
+			}
+			if(k == Knack || k == Kesc){
+				done = !n;
+				n = tick2 = tick = 0;
+				buf[n] = 0;
+				break;
+			}
+			if(k == Ksoh || k == Khome){
+				tick2 = tick = 0;
+				continue;
+			}
+			if(k == Kenq || k == Kend){
+				tick2 = tick = n;
+				continue;
+			}
+			if(k == Kright){
+				if(tick2 < n)
+					tick2 = 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){
+						tick2 = 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 && tick2 <= 0)
+					continue;
+				if (tick == tick2)
+					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;
+							tick2 = tick -= l;
+							break;
+						}
+					}
+				else
+					tick = tick2 = delsubstring(buf, n, tick-1, tick2, &n);
+				break;
+			}
+			if(k < 0x20 || k == Kdel || (k & 0xFF00) == KF || (k & 0xFF00) == Spec)
+				continue;
+			if((len-n) <= (l = runelen(k)))
+				continue;
+			tick = delsubstring(buf, n, tick, tick2, &n);
+			memmove(buf+tick+l, buf+tick, n - tick);
+			runetochar(buf+tick, &k);
+			buf[n += l] = 0;
+			tick2 = tick += l;
+			break;
+		case Gmouse:
+			m = ps->m;
+			if(mb&1 && !m.buttons&1) {
+				sorttick(&tick, &tick2);
+				mb = m.buttons;
+			}
+			if(m.buttons&1 && mb&6){
+				if (m.buttons != mb)
+					goto Mchecks;
+				continue;
+			}
+			if(m.buttons&1 && mb&6){
+				continue;
+			}
+			if(!ptinrect(m.xy, ps->r)){
+				down = 0;
+				goto nodraw;
+			}
+			if(m.buttons&1){
+				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;
+					}
+					if(mb & 1){
+						tick2 = i;
+					}else
+						tick = tick2 = i;
+				}
+				if(!((m.buttons&2) || (m.buttons&4))){
+					mb = m.buttons;
+					continue;
+				}
+			}
+Mchecks:
+			if(!(mb&2) && (m.buttons&3) == 3){
+				tick = tick2 = entersnarf(buf, n, tick, tick2, &n);
+				mb = m.buttons;
+			}
+			if(!(mb&4) && (m.buttons&5) == 5){
+				enterpaste(buf, n, len, tick, tick2, &n, &tick, &tick2);
+				mb = m.buttons;
+			}
+
+			done = down;
+			break;
+		}
+		if(ps->loopcleanup)
+			ps->loopcleanup(ps);
+	}
+	if(ps->cleanup)
+		ps->cleanup(ps);
+
+	freeimage(backcol);
+	freeimage(bordcol);
+	flushimage(display, 1);
+
+	return n;
+}
--- /dev/null
+++ b/libgenenter.h
@@ -1,0 +1,22 @@
+typedef struct Enterparams Enterparams;
+struct Enterparams {
+	int (*initsave)(Enterparams*);
+	int (*getevent)(Enterparams*);
+	void (*loopcleanup)(Enterparams*);
+	void (*cleanup)(Enterparams*);
+
+	Rune k;
+	Mouse m;
+	Point o;
+	Rectangle r;
+	Rectangle sc;
+	Image *b;
+	Image *save;
+	void *aux;
+};
+int _genenter(char*, char*, int, Enterparams*);
+
+enum {
+	Gkeyboard = 1,
+	Gmouse = 2,
+};
--- /dev/null
+++ b/libmkfile
@@ -1,0 +1,80 @@
+</$objtype/mkfile
+
+LIB=/$objtype/lib/libdraw.a
+
+OFILES=\
+	alloc.$O\
+	allocimagemix.$O\
+	arith.$O\
+	badrect.$O\
+	bezier.$O\
+	border.$O\
+	buildfont.$O\
+	bytesperline.$O\
+	chan.$O\
+	cloadimage.$O\
+	computil.$O\
+	creadimage.$O\
+	debug.$O\
+	defont.$O\
+	draw.$O\
+	drawrepl.$O\
+	eenter.$O\
+	egetrect.$O\
+	ellipse.$O\
+	emenuhit.$O\
+	enter.$O\
+	event.$O\
+	fmt.$O\
+	font.$O\
+	freesubfont.$O\
+	genenter.$O\
+	getdefont.$O\
+	getrect.$O\
+	getsubfont.$O\
+	icossin.$O\
+	icossin2.$O\
+	init.$O\
+	keyboard.$O\
+	line.$O\
+	menuhit.$O\
+	mkfont.$O\
+	mouse.$O\
+	newwindow.$O\
+	openfont.$O\
+	poly.$O\
+	loadimage.$O\
+	readcolmap.$O\
+	readimage.$O\
+	readsubfont.$O\
+	rectclip.$O\
+	replclipr.$O\
+	rgb.$O\
+	scroll.$O\
+	string.$O\
+	stringbg.$O\
+	stringsubfont.$O\
+	stringwidth.$O\
+	subfont.$O\
+	subfontcache.$O\
+	subfontname.$O\
+	unloadimage.$O\
+	window.$O\
+	writecolmap.$O\
+	writeimage.$O\
+	writesubfont.$O\
+
+HFILES=\
+	/sys/include/draw.h\
+	/sys/include/event.h\
+	/sys/include/mouse.h\
+	/sys/include/keyboard.h\
+	genenter.h
+
+UPDATE=\
+	mkfile\
+	$HFILES\
+	${OFILES:%.$O=%.c}\
+	${LIB:/$objtype/%=/386/%}\
+
+</sys/src/cmd/mksyslib
--- a/mkfile
+++ b/mkfile
@@ -4,15 +4,16 @@
 
 </sys/src/cmd/mkmany
 
-prep:V: libenter.c libeenter.c
+prep:V:
+	cp /sys/src/libdraw/enter.c libenter.c
+	cp /sys/src/libdraw/eenter.c libeenter.c
+	cp /sys/src/libdraw/genenter.h libgenenter.h
+	cp /sys/src/libdraw/genenter.c libgenenter.c
+	cp /sys/src/libdraw/mkfile libmkfile
 
-instlib:V: /sys/src/libdraw/enter.c /sys/src/libdraw/eenter.c
-
-libenter.c:
-	cp /sys/src/libdraw/enter.c $target
-
-libeenter.c:
-	cp /sys/src/libdraw/eenter.c $target
-
-/sys/src/libdraw/%.c: lib%.c
-	cp $prereq $target
+instlib:V:
+	cp libenter.c /sys/src/libdraw/enter.c
+	cp libeenter.c /sys/src/libdraw/eenter.c
+	cp libgenenter.h /sys/src/libdraw/genenter.h
+	cp libgenenter.c /sys/src/libdraw/genenter.c
+	cp libmkfile /sys/src/libdraw/mkfile