xref: /plan9/sys/src/cmd/vnc/screen.c (revision f8e525ac91e3a7fae3f104837a69862dc348aa81)
19a747e4fSDavid du Colombier #include	<u.h>
29a747e4fSDavid du Colombier #include	<libc.h>
39a747e4fSDavid du Colombier #include	"compat.h"
49a747e4fSDavid du Colombier #include	"kbd.h"
59a747e4fSDavid du Colombier #include	"error.h"
69a747e4fSDavid du Colombier 
79a747e4fSDavid du Colombier #define	Image	IMAGE
89a747e4fSDavid du Colombier #include	<draw.h>
99a747e4fSDavid du Colombier #include	<memdraw.h>
109a747e4fSDavid du Colombier #include	<cursor.h>
119a747e4fSDavid du Colombier #include	"screen.h"
129a747e4fSDavid du Colombier 
139a747e4fSDavid du Colombier enum
149a747e4fSDavid du Colombier {
159a747e4fSDavid du Colombier 	CURSORDIM = 16
169a747e4fSDavid du Colombier };
179a747e4fSDavid du Colombier 
189a747e4fSDavid du Colombier Memimage	*gscreen;
199a747e4fSDavid du Colombier Point		ZP;
209a747e4fSDavid du Colombier int		cursorver;
219a747e4fSDavid du Colombier Point		cursorpos;
229a747e4fSDavid du Colombier 
239a747e4fSDavid du Colombier static Memimage		*back;
249a747e4fSDavid du Colombier static Memimage		*conscol;
259a747e4fSDavid du Colombier static Memimage		*curscol;
269a747e4fSDavid du Colombier static Point		curpos;
279a747e4fSDavid du Colombier static Memsubfont	*memdefont;
289a747e4fSDavid du Colombier static Rectangle	flushr;
299a747e4fSDavid du Colombier static Rectangle	window;
309a747e4fSDavid du Colombier static int		h;
319a747e4fSDavid du Colombier static int		w;
329a747e4fSDavid du Colombier 
339a747e4fSDavid du Colombier static Rectangle	cursorr;
349a747e4fSDavid du Colombier static Point		offscreen;
359a747e4fSDavid du Colombier static uchar		cursset[CURSORDIM*CURSORDIM/8];
369a747e4fSDavid du Colombier static uchar		cursclr[CURSORDIM*CURSORDIM/8];
379a747e4fSDavid du Colombier static int		cursdrawvers = -1;
389a747e4fSDavid du Colombier static Memimage		*cursorset;
399a747e4fSDavid du Colombier static Memimage		*cursorclear;
409a747e4fSDavid du Colombier static Cursor		screencursor;
419a747e4fSDavid du Colombier 
429a747e4fSDavid du Colombier Cursor	arrow = {
439a747e4fSDavid du Colombier 	{ -1, -1 },
449a747e4fSDavid du Colombier 	{ 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C,
459a747e4fSDavid du Colombier 	  0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04,
469a747e4fSDavid du Colombier 	  0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04,
479a747e4fSDavid du Colombier 	  0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40,
489a747e4fSDavid du Colombier 	},
499a747e4fSDavid du Colombier 	{ 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0,
509a747e4fSDavid du Colombier 	  0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8,
519a747e4fSDavid du Colombier 	  0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8,
529a747e4fSDavid du Colombier 	  0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00,
539a747e4fSDavid du Colombier 	},
549a747e4fSDavid du Colombier };
559a747e4fSDavid du Colombier 
569a747e4fSDavid du Colombier void
screeninit(int x,int y,char * chanstr)579a747e4fSDavid du Colombier screeninit(int x, int y, char *chanstr)
589a747e4fSDavid du Colombier {
599a747e4fSDavid du Colombier 	Point p, q;
609a747e4fSDavid du Colombier 	char *greet;
61*f8e525acSDavid du Colombier 	char buf[128];
629a747e4fSDavid du Colombier 	Memimage *grey;
639a747e4fSDavid du Colombier 	Rectangle r;
649a747e4fSDavid du Colombier 	int chan;
659a747e4fSDavid du Colombier 
669a747e4fSDavid du Colombier 	cursorver = 0;
679a747e4fSDavid du Colombier 
689a747e4fSDavid du Colombier 	memimageinit();
699a747e4fSDavid du Colombier 	chan = strtochan(chanstr);
709a747e4fSDavid du Colombier 	if(chan == 0)
719a747e4fSDavid du Colombier 		error("bad screen channel string");
729a747e4fSDavid du Colombier 
739a747e4fSDavid du Colombier 	r = Rect(0, 0, x, y);
749a747e4fSDavid du Colombier 	gscreen = allocmemimage(r, chan);
75*f8e525acSDavid du Colombier 	if(gscreen == nil){
76*f8e525acSDavid du Colombier 		snprint(buf, sizeof buf, "can't allocate screen image: %r");
77*f8e525acSDavid du Colombier 		error(buf);
78*f8e525acSDavid du Colombier 	}
799a747e4fSDavid du Colombier 
809a747e4fSDavid du Colombier 	offscreen = Pt(x + 100, y + 100);
819a747e4fSDavid du Colombier 	cursorr = Rect(0, 0, CURSORDIM, CURSORDIM);
829a747e4fSDavid du Colombier 	cursorset = allocmemimage(cursorr, GREY8);
839a747e4fSDavid du Colombier 	cursorclear = allocmemimage(cursorr, GREY1);
849a747e4fSDavid du Colombier 	if(cursorset == nil || cursorclear == nil){
859a747e4fSDavid du Colombier 		freememimage(gscreen);
869a747e4fSDavid du Colombier 		freememimage(cursorset);
879a747e4fSDavid du Colombier 		freememimage(cursorclear);
889a747e4fSDavid du Colombier 		gscreen = nil;
899a747e4fSDavid du Colombier 		cursorset = nil;
909a747e4fSDavid du Colombier 		cursorclear = nil;
91*f8e525acSDavid du Colombier 		snprint(buf, sizeof buf, "can't allocate cursor images: %r");
92*f8e525acSDavid du Colombier 		error(buf);
939a747e4fSDavid du Colombier 	}
949a747e4fSDavid du Colombier 
959a747e4fSDavid du Colombier 	drawlock();
969a747e4fSDavid du Colombier 
979a747e4fSDavid du Colombier 	/*
989a747e4fSDavid du Colombier 	 * set up goo for screenputs
999a747e4fSDavid du Colombier 	 */
1009a747e4fSDavid du Colombier 	memdefont = getmemdefont();
1019a747e4fSDavid du Colombier 
1029a747e4fSDavid du Colombier 	back = memwhite;
1039a747e4fSDavid du Colombier 	conscol = memblack;
1049a747e4fSDavid du Colombier 
1059a747e4fSDavid du Colombier 	/* a lot of work to get a grey color */
1069a747e4fSDavid du Colombier 	curscol = allocmemimage(Rect(0,0,1,1), RGBA32);
1079a747e4fSDavid du Colombier 	curscol->flags |= Frepl;
1089a747e4fSDavid du Colombier 	curscol->clipr = gscreen->r;
1099a747e4fSDavid du Colombier 	memfillcolor(curscol, 0xff0000ff);
1109a747e4fSDavid du Colombier 
1119a747e4fSDavid du Colombier 	memfillcolor(gscreen, 0x444488FF);
1129a747e4fSDavid du Colombier 
1139a747e4fSDavid du Colombier 	w = memdefont->info[' '].width;
1149a747e4fSDavid du Colombier 	h = memdefont->height;
1159a747e4fSDavid du Colombier 
1169a747e4fSDavid du Colombier 	window.min = addpt(gscreen->r.min, Pt(20,20));
1179a747e4fSDavid du Colombier 	window.max.x = window.min.x + Dx(gscreen->r)*3/4-40;
1189a747e4fSDavid du Colombier 	window.max.y = window.min.y + Dy(gscreen->r)*3/4-100;
1199a747e4fSDavid du Colombier 
1206a9fc400SDavid du Colombier 	memimagedraw(gscreen, window, memblack, ZP, memopaque, ZP, S);
1219a747e4fSDavid du Colombier 	window = insetrect(window, 4);
1226a9fc400SDavid du Colombier 	memimagedraw(gscreen, window, memwhite, ZP, memopaque, ZP, S);
1239a747e4fSDavid du Colombier 
1249a747e4fSDavid du Colombier 	/* a lot of work to get a grey color */
1259a747e4fSDavid du Colombier 	grey = allocmemimage(Rect(0,0,1,1), CMAP8);
1269a747e4fSDavid du Colombier 	grey->flags |= Frepl;
1279a747e4fSDavid du Colombier 	grey->clipr = gscreen->r;
1289a747e4fSDavid du Colombier 	memfillcolor(grey, 0xAAAAAAFF);
1299a747e4fSDavid du Colombier 	memimagedraw(gscreen, Rect(window.min.x, window.min.y,
1306a9fc400SDavid du Colombier 			window.max.x, window.min.y+h+5+6), grey, ZP, nil, ZP, S);
1319a747e4fSDavid du Colombier 	freememimage(grey);
1329a747e4fSDavid du Colombier 	window = insetrect(window, 5);
1339a747e4fSDavid du Colombier 
1349a747e4fSDavid du Colombier 	greet = " Plan 9 Console ";
1359a747e4fSDavid du Colombier 	p = addpt(window.min, Pt(10, 0));
1369a747e4fSDavid du Colombier 	q = memsubfontwidth(memdefont, greet);
1379a747e4fSDavid du Colombier 	memimagestring(gscreen, p, conscol, ZP, memdefont, greet);
1389a747e4fSDavid du Colombier 	window.min.y += h+6;
1399a747e4fSDavid du Colombier 	curpos = window.min;
1409a747e4fSDavid du Colombier 	window.max.y = window.min.y+((window.max.y-window.min.y)/h)*h;
1419a747e4fSDavid du Colombier 	flushmemscreen(gscreen->r);
1429a747e4fSDavid du Colombier 
1439a747e4fSDavid du Colombier 	drawunlock();
1449a747e4fSDavid du Colombier 
1459a747e4fSDavid du Colombier 	setcursor(&arrow);
1469a747e4fSDavid du Colombier }
1479a747e4fSDavid du Colombier 
1489a747e4fSDavid du Colombier uchar*
attachscreen(Rectangle * r,ulong * chan,int * d,int * width,int * softscreen)1499a747e4fSDavid du Colombier attachscreen(Rectangle* r, ulong* chan, int* d, int* width, int *softscreen)
1509a747e4fSDavid du Colombier {
1519a747e4fSDavid du Colombier 	*r = gscreen->r;
1529a747e4fSDavid du Colombier 	*d = gscreen->depth;
1539a747e4fSDavid du Colombier 	*chan = gscreen->chan;
1549a747e4fSDavid du Colombier 	*width = gscreen->width;
1559a747e4fSDavid du Colombier 	*softscreen = 1;
1569a747e4fSDavid du Colombier 
1579a747e4fSDavid du Colombier 	return gscreen->data->bdata;
1589a747e4fSDavid du Colombier }
1599a747e4fSDavid du Colombier 
1609a747e4fSDavid du Colombier void
getcolor(ulong,ulong * pr,ulong * pg,ulong * pb)1619a747e4fSDavid du Colombier getcolor(ulong , ulong* pr, ulong* pg, ulong* pb)
1629a747e4fSDavid du Colombier {
1639a747e4fSDavid du Colombier 	*pr = 0;
1649a747e4fSDavid du Colombier 	*pg = 0;
1659a747e4fSDavid du Colombier 	*pb = 0;
1669a747e4fSDavid du Colombier }
1679a747e4fSDavid du Colombier 
1689a747e4fSDavid du Colombier int
setcolor(ulong,ulong,ulong,ulong)1699a747e4fSDavid du Colombier setcolor(ulong , ulong , ulong , ulong )
1709a747e4fSDavid du Colombier {
1719a747e4fSDavid du Colombier 	return 0;
1729a747e4fSDavid du Colombier }
1739a747e4fSDavid du Colombier 
1749a747e4fSDavid du Colombier /*
1759a747e4fSDavid du Colombier  * called with cursor unlocked, drawlock locked
1769a747e4fSDavid du Colombier  */
1779a747e4fSDavid du Colombier void
cursordraw(Memimage * dst,Rectangle r)1789a747e4fSDavid du Colombier cursordraw(Memimage *dst, Rectangle r)
1799a747e4fSDavid du Colombier {
1809a747e4fSDavid du Colombier 	static uchar set[CURSORDIM*CURSORDIM], clr[CURSORDIM*CURSORDIM/8];
1819a747e4fSDavid du Colombier 	static int ver = -1;
1829a747e4fSDavid du Colombier 	int i, j, n;
1839a747e4fSDavid du Colombier 
1849a747e4fSDavid du Colombier 	lock(&cursor);
1859a747e4fSDavid du Colombier 	if(ver != cursorver){
1869a747e4fSDavid du Colombier 		n = 0;
1879a747e4fSDavid du Colombier 		for(i = 0; i < CURSORDIM*CURSORDIM/8; i += CURSORDIM/8){
1889a747e4fSDavid du Colombier 			for(j = 0; j < CURSORDIM; j++){
1899a747e4fSDavid du Colombier 				if(cursset[i + (j >> 3)] & (1 << (7 - (j & 7))))
1909a747e4fSDavid du Colombier 					set[n] = 0xaa;
1919a747e4fSDavid du Colombier 				else
1929a747e4fSDavid du Colombier 					set[n] = 0;
1939a747e4fSDavid du Colombier 				n++;
1949a747e4fSDavid du Colombier 			}
1959a747e4fSDavid du Colombier 		}
1969a747e4fSDavid du Colombier 		memmove(clr, cursclr, CURSORDIM*CURSORDIM/8);
1979a747e4fSDavid du Colombier 		ver = cursorver;
1989a747e4fSDavid du Colombier 		unlock(&cursor);
1999a747e4fSDavid du Colombier 		loadmemimage(cursorset, cursorr, set, CURSORDIM*CURSORDIM);
2009a747e4fSDavid du Colombier 		loadmemimage(cursorclear, cursorr, clr, CURSORDIM*CURSORDIM/8);
2019a747e4fSDavid du Colombier 	}else
2029a747e4fSDavid du Colombier 		unlock(&cursor);
203106486e8SDavid du Colombier 	memimagedraw(dst, r, memwhite, ZP, cursorclear, ZP, SoverD);
204106486e8SDavid du Colombier 	memimagedraw(dst, r, curscol, ZP, cursorset, ZP, SoverD);
2059a747e4fSDavid du Colombier }
2069a747e4fSDavid du Colombier 
2079a747e4fSDavid du Colombier /*
208*f8e525acSDavid du Colombier  * called with cursor locked, drawlock possibly unlocked
2099a747e4fSDavid du Colombier  */
2109a747e4fSDavid du Colombier Rectangle
cursorrect(void)2119a747e4fSDavid du Colombier cursorrect(void)
2129a747e4fSDavid du Colombier {
2139a747e4fSDavid du Colombier 	Rectangle r;
2149a747e4fSDavid du Colombier 
2159a747e4fSDavid du Colombier 	r.min.x = cursorpos.x + cursor.offset.x;
2169a747e4fSDavid du Colombier 	r.min.y = cursorpos.y + cursor.offset.y;
2179a747e4fSDavid du Colombier 	r.max.x = r.min.x + CURSORDIM;
2189a747e4fSDavid du Colombier 	r.max.y = r.min.y + CURSORDIM;
2199a747e4fSDavid du Colombier 	return r;
2209a747e4fSDavid du Colombier }
2219a747e4fSDavid du Colombier 
2229a747e4fSDavid du Colombier /*
223*f8e525acSDavid du Colombier  * called with cursor locked, drawlock possibly unlocked
2249a747e4fSDavid du Colombier  */
2259a747e4fSDavid du Colombier void
setcursor(Cursor * curs)2269a747e4fSDavid du Colombier setcursor(Cursor* curs)
2279a747e4fSDavid du Colombier {
2289a747e4fSDavid du Colombier 	cursorver++;
2299a747e4fSDavid du Colombier 	memmove(cursset, curs->set, CURSORDIM*CURSORDIM/8);
2309a747e4fSDavid du Colombier 	memmove(cursclr, curs->clr, CURSORDIM*CURSORDIM/8);
2319a747e4fSDavid du Colombier }
2329a747e4fSDavid du Colombier 
2339a747e4fSDavid du Colombier int
cursoron(int dolock)2349a747e4fSDavid du Colombier cursoron(int dolock)
2359a747e4fSDavid du Colombier {
2369a747e4fSDavid du Colombier 	if(dolock)
2379a747e4fSDavid du Colombier 		lock(&cursor);
2389a747e4fSDavid du Colombier 	cursorpos = mousexy();
2399a747e4fSDavid du Colombier 	if(dolock)
2409a747e4fSDavid du Colombier 		unlock(&cursor);
2419a747e4fSDavid du Colombier 
2429a747e4fSDavid du Colombier 	return 0;
2439a747e4fSDavid du Colombier }
2449a747e4fSDavid du Colombier 
2459a747e4fSDavid du Colombier void
cursoroff(int dolock)2469a747e4fSDavid du Colombier cursoroff(int dolock)
2479a747e4fSDavid du Colombier {
2489a747e4fSDavid du Colombier 	if(dolock)
2499a747e4fSDavid du Colombier 		lock(&cursor);
2509a747e4fSDavid du Colombier 	cursorpos = offscreen;
2519a747e4fSDavid du Colombier 	if(dolock)
2529a747e4fSDavid du Colombier 		unlock(&cursor);
2539a747e4fSDavid du Colombier }
2549a747e4fSDavid du Colombier 
2559a747e4fSDavid du Colombier void
blankscreen(int blank)2569a747e4fSDavid du Colombier blankscreen(int blank)
2579a747e4fSDavid du Colombier {
2589a747e4fSDavid du Colombier 	USED(blank);
2599a747e4fSDavid du Colombier }
2609a747e4fSDavid du Colombier 
2619a747e4fSDavid du Colombier static void
screenflush(void)2629a747e4fSDavid du Colombier screenflush(void)
2639a747e4fSDavid du Colombier {
2649a747e4fSDavid du Colombier 	flushmemscreen(flushr);
2659a747e4fSDavid du Colombier 	flushr = Rect(10000, 10000, -10000, -10000);
2669a747e4fSDavid du Colombier }
2679a747e4fSDavid du Colombier 
2689a747e4fSDavid du Colombier static void
addflush(Rectangle r)2699a747e4fSDavid du Colombier addflush(Rectangle r)
2709a747e4fSDavid du Colombier {
2719a747e4fSDavid du Colombier 	if(flushr.min.x >= flushr.max.x)
2729a747e4fSDavid du Colombier 		flushr = r;
2739a747e4fSDavid du Colombier 	else
2749a747e4fSDavid du Colombier 		combinerect(&flushr, r);
2759a747e4fSDavid du Colombier }
2769a747e4fSDavid du Colombier 
2779a747e4fSDavid du Colombier static void
scroll(void)2789a747e4fSDavid du Colombier scroll(void)
2799a747e4fSDavid du Colombier {
2809a747e4fSDavid du Colombier 	int o;
2819a747e4fSDavid du Colombier 	Point p;
2829a747e4fSDavid du Colombier 	Rectangle r;
2839a747e4fSDavid du Colombier 
2849a747e4fSDavid du Colombier 	o = 8*h;
2859a747e4fSDavid du Colombier 	r = Rpt(window.min, Pt(window.max.x, window.max.y-o));
2869a747e4fSDavid du Colombier 	p = Pt(window.min.x, window.min.y+o);
2876a9fc400SDavid du Colombier 	memimagedraw(gscreen, r, gscreen, p, nil, p, S);
2889a747e4fSDavid du Colombier 	r = Rpt(Pt(window.min.x, window.max.y-o), window.max);
2896a9fc400SDavid du Colombier 	memimagedraw(gscreen, r, back, ZP, nil, ZP, S);
2909a747e4fSDavid du Colombier 	flushmemscreen(gscreen->r);
2919a747e4fSDavid du Colombier 
2929a747e4fSDavid du Colombier 	curpos.y -= o;
2939a747e4fSDavid du Colombier }
2949a747e4fSDavid du Colombier 
2959a747e4fSDavid du Colombier static void
screenputc(char * buf)2969a747e4fSDavid du Colombier screenputc(char *buf)
2979a747e4fSDavid du Colombier {
2989a747e4fSDavid du Colombier 	Point p;
2999a747e4fSDavid du Colombier 	int w, pos;
3009a747e4fSDavid du Colombier 	Rectangle r;
3019a747e4fSDavid du Colombier 	static int *xp;
3029a747e4fSDavid du Colombier 	static int xbuf[256];
3039a747e4fSDavid du Colombier 
3049a747e4fSDavid du Colombier 	if(xp < xbuf || xp >= &xbuf[sizeof(xbuf)])
3059a747e4fSDavid du Colombier 		xp = xbuf;
3069a747e4fSDavid du Colombier 
3079a747e4fSDavid du Colombier 	switch(buf[0]){
3089a747e4fSDavid du Colombier 	case '\n':
3099a747e4fSDavid du Colombier 		if(curpos.y+h >= window.max.y)
3109a747e4fSDavid du Colombier 			scroll();
3119a747e4fSDavid du Colombier 		curpos.y += h;
3129a747e4fSDavid du Colombier 		screenputc("\r");
3139a747e4fSDavid du Colombier 		break;
3149a747e4fSDavid du Colombier 	case '\r':
3159a747e4fSDavid du Colombier 		xp = xbuf;
3169a747e4fSDavid du Colombier 		curpos.x = window.min.x;
3179a747e4fSDavid du Colombier 		break;
3189a747e4fSDavid du Colombier 	case '\t':
3199a747e4fSDavid du Colombier 		p = memsubfontwidth(memdefont, " ");
3209a747e4fSDavid du Colombier 		w = p.x;
3219a747e4fSDavid du Colombier 		*xp++ = curpos.x;
3229a747e4fSDavid du Colombier 		pos = (curpos.x-window.min.x)/w;
3239a747e4fSDavid du Colombier 		pos = 8-(pos%8);
3249a747e4fSDavid du Colombier 		r = Rect(curpos.x, curpos.y, curpos.x+pos*w, curpos.y + h);
3256a9fc400SDavid du Colombier 		memimagedraw(gscreen, r, back, back->r.min, memopaque, ZP, S);
3269a747e4fSDavid du Colombier 		addflush(r);
3279a747e4fSDavid du Colombier 		curpos.x += pos*w;
3289a747e4fSDavid du Colombier 		break;
3299a747e4fSDavid du Colombier 	case '\b':
3309a747e4fSDavid du Colombier 		if(xp <= xbuf)
3319a747e4fSDavid du Colombier 			break;
3329a747e4fSDavid du Colombier 		xp--;
3339a747e4fSDavid du Colombier 		r = Rect(*xp, curpos.y, curpos.x, curpos.y + h);
3346a9fc400SDavid du Colombier 		memimagedraw(gscreen, r, back, back->r.min, memopaque, ZP, S);
3359a747e4fSDavid du Colombier 		addflush(r);
3369a747e4fSDavid du Colombier 		curpos.x = *xp;
3379a747e4fSDavid du Colombier 		break;
3389a747e4fSDavid du Colombier 	default:
3399a747e4fSDavid du Colombier 		p = memsubfontwidth(memdefont, buf);
3409a747e4fSDavid du Colombier 		w = p.x;
3419a747e4fSDavid du Colombier 
3429a747e4fSDavid du Colombier 		if(curpos.x >= window.max.x-w)
3439a747e4fSDavid du Colombier 			screenputc("\n");
3449a747e4fSDavid du Colombier 
3459a747e4fSDavid du Colombier 		*xp++ = curpos.x;
3469a747e4fSDavid du Colombier 		r = Rect(curpos.x, curpos.y, curpos.x+w, curpos.y + h);
3476a9fc400SDavid du Colombier 		memimagedraw(gscreen, r, back, back->r.min, memopaque, ZP, S);
3489a747e4fSDavid du Colombier 		memimagestring(gscreen, curpos, conscol, ZP, memdefont, buf);
3499a747e4fSDavid du Colombier 		addflush(r);
3509a747e4fSDavid du Colombier 		curpos.x += w;
3519a747e4fSDavid du Colombier 	}
3529a747e4fSDavid du Colombier }
3539a747e4fSDavid du Colombier 
3549a747e4fSDavid du Colombier void
screenputs(char * s,int n)3559a747e4fSDavid du Colombier screenputs(char *s, int n)
3569a747e4fSDavid du Colombier {
3579a747e4fSDavid du Colombier 	int i;
3589a747e4fSDavid du Colombier 	Rune r;
3599a747e4fSDavid du Colombier 	char buf[4];
3609a747e4fSDavid du Colombier 
3619a747e4fSDavid du Colombier 	drawlock();
3629a747e4fSDavid du Colombier 	while(n > 0){
3639a747e4fSDavid du Colombier 		i = chartorune(&r, s);
3649a747e4fSDavid du Colombier 		if(i == 0){
3659a747e4fSDavid du Colombier 			s++;
3669a747e4fSDavid du Colombier 			--n;
3679a747e4fSDavid du Colombier 			continue;
3689a747e4fSDavid du Colombier 		}
3699a747e4fSDavid du Colombier 		memmove(buf, s, i);
3709a747e4fSDavid du Colombier 		buf[i] = 0;
3719a747e4fSDavid du Colombier 		n -= i;
3729a747e4fSDavid du Colombier 		s += i;
3739a747e4fSDavid du Colombier 		screenputc(buf);
3749a747e4fSDavid du Colombier 	}
3759a747e4fSDavid du Colombier 	screenflush();
3769a747e4fSDavid du Colombier 	drawunlock();
3779a747e4fSDavid du Colombier }
378