xref: /plan9/sys/src/9/omap/screen.c (revision 5efba40621eb0e8502497b8c98fd368f3bc4d354)
1 /*
2  * ti omap35 display subsystem (dss)
3  *
4  * can handle 2ⁿ bits per pixel for 0 < n ≤ 4, and 12 and 24 bits.
5  * can handle	1024×768 at 60 Hz with pixel clock of 63.5 MHz
6  *		1280×800 at 59.91 Hz with pixel clock of 71 MHz
7  *		1400×1050 lcd at 50 MHz with pixel clock of 75 MHz
8  * has 256 24-bit entries in RGB palette
9  */
10 #include "u.h"
11 #include "../port/lib.h"
12 #include "mem.h"
13 #include "dat.h"
14 #include "fns.h"
15 #include "io.h"
16 #include "ureg.h"
17 #include "../port/error.h"
18 
19 #define	Image	IMAGE
20 #include <draw.h>
21 #include <memdraw.h>
22 #include <cursor.h>
23 #include "screen.h"
24 // #include "gamma.h"
25 
26 enum {
27 	Tabstop	= 4,		/* should be 8 */
28 	Scroll	= 8,		/* lines to scroll at one time */
29 	/*
30 	 * screen settings for Wid and Ht, should a bit more dynamic?
31 	 * http://www.epanorama.net/faq/vga2rgb/calc.html
32 	 * used to calculate settings.
33 	 */
34 
35 //	Hbp     = (248-1) << 20,
36 //	Hfp     = (48-1) << 8,
37 //	Hsw     = 112-1,
38 
39 //	Vbp     = 38 << 20,
40 //	Vfp     = 1 << 8,
41 //	Vsw     = 3,
42 
43 	Tft	= 0x60,
44 
45 	Loadmode = 2 << 1,
46 	Fifosize = 0x400,
47 
48 	/* dispc sysconfig */
49 	Midlemode	= 2 << 12,
50 	Sidlemode	= 2 << 3,
51 	EnableWakeup	= 1 << 2,
52 	Autoidle	= 1 << 0,
53 
54 	/* dispc pool_freq */
55 	Ipc		= 1 << 14,
56 	Ihs		= 1 << 13,
57 	Ivs		= 1 << 12,
58 	Acb		= 0x28,
59 
60 	/* gfx attribs */
61 	Burstsize	= 2 << 6,
62 	Format		= 6 << 1,
63 	Gfxenable	= 1 << 0,
64 
65 	/* dispc control */
66 	Gpout1		= 1 << 16,
67 	Gpout0		= 1 << 15,
68 	Tftdata		= 3 << 8,
69 	Digital		= 1 << 6,
70 	Lcd		= 1 << 5,
71 	Stntft		= 1 << 3,
72 	Digitalen	= 1 << 1,
73 //	Lcden		= 1 << 0,	/* unused */
74 };
75 
76 typedef struct Dispcregs Dispc;
77 typedef struct Dssregs Dss;
78 typedef struct Ioregs Ioregs;
79 
80 struct Ioregs {				/* common registers, 68 (0x44) bytes */
81 	ulong 	rev;
82 	uchar	_pad0[0x10-0x4];
83 	ulong	sysconf;
84 	ulong	sysstat;
85 	ulong	irqstat1;
86 
87 	/* Dispc only regs */
88 	ulong	irqen1;
89 	ulong	wkupen;
90 	ulong	_pad1;
91 	ulong	irqsts2;
92 	ulong	irqen2;
93 	ulong	_pad2[4];
94 
95 	ulong	ctrl;
96 };
97 
98 struct Dssregs {			/* display subsys at 0x48050000 */
99 	Ioregs;
100 	ulong	sdicrtl;
101 	ulong	pllcrtl;
102 	uchar	_pad3[0x5c-0x4c];
103 	ulong	sdistat;
104 };
105 
106 struct Dispcregs {			/* display ctlr at 0x48050400 */
107 	Ioregs;
108 	ulong	config;
109 	ulong	_pad3;
110 	ulong 	defaultcolor[2];
111 	ulong	transcolor[2];
112 	ulong	linestat;
113 	ulong	linenum;
114 	ulong	timing_h;
115 	ulong	timing_v;
116 	ulong	pol_req;
117 	ulong	divisor;
118 	ulong	alpha;
119 	ulong	digsize;
120 	ulong	lcdsize;
121 
122 	ulong	base[2];	/* should allocate both to avoid dithering */
123 	ulong	pos;
124 	ulong	size;
125 	ulong	_pad4[4];
126 	ulong	attrib;
127 	ulong	fifothr;
128 	ulong	fifosize;
129 	ulong	rowinc;
130 	ulong	pixelinc;
131 	ulong	winskip;
132 	ulong	palette;		/* gfx_table_ba */
133 	uchar	_pad5[0x5d4 - 0x4bc];
134 
135 	ulong	datacycle[3];
136 	uchar	_pad5[0x620 - 0x5e0];
137 
138 	ulong	cprcoefr;
139 	ulong	cprcoefg;
140 	ulong	cprcoefb;
141 	ulong	preload;
142 };
143 
144 int	drawdebug;
145 Point	ZP = {0, 0};
146 Cursor	arrow = {
147 	{ -1, -1 },
148 	{ 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C,
149 	  0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04,
150 	  0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04,
151 	  0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40,
152 	},
153 	{ 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0,
154 	  0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8,
155 	  0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8,
156 	  0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00,
157 	},
158 };
159 
160 OScreen oscreen;
161 Settings settings[] = {
162 [Res800x600]   {  800,  600, 60, RGB16,  40000,	 88, 40, 128,	23, 1, 5, },
163 [Res1024x768]  { 1024,  768, 60, RGB16,  65000,	160, 24, 136,	29, 3, 7, },
164 [Res1280x1024] { 1280, 1024, 60, RGB16, 108000,	248, 48, 112,	38, 1, 4, },
165 [Res1400x1050] { 1400, 1050, 50, RGB16, 108000, 248, 48, 112,	38, 1, 4, }, // TODO
166 };
167 Omap3fb *framebuf;
168 Memimage *gscreen;
169 
170 static Memdata xgdata;
171 
172 static Memimage xgscreen =
173 {
174 	{ 0, 0, Wid, Ht },	/* r */
175 	{ 0, 0, Wid, Ht },	/* clipr */
176 	Depth,			/* depth */
177 	3,			/* nchan */
178 	RGB16,			/* chan */
179 	nil,			/* cmap */
180 	&xgdata,		/* data */
181 	0,			/* zero */
182 	Wid*(Depth/BI2BY)/BY2WD, /* width in words of a single scan line */
183 	0,			/* layer */
184 	0,			/* flags */
185 };
186 
187 static Memimage *conscol;
188 static Memimage *back;
189 
190 static Memsubfont *memdefont;
191 
192 static Lock screenlock;
193 
194 static Point	curpos;
195 static int	h, w;
196 static int	landscape = 0;	/* screen orientation, default is 0: portrait */
197 static ushort	*vscreen;	/* virtual screen */
198 static Rectangle window;
199 
200 static Dispc *dispc = (Dispc *)PHYSDISPC;
201 static Dss *dss	= (Dss *)PHYSDSS;
202 
203 static	void	omapscreenputs(char *s, int n);
204 static	ulong	rep(ulong, int);
205 static	void	screenputc(char *buf);
206 static	void	screenwin(void);
207 
208 /*
209  * Software cursor.
210  */
211 int	swvisible;	/* is the cursor visible? */
212 int	swenabled;	/* is the cursor supposed to be on the screen? */
213 Memimage*	swback;	/* screen under cursor */
214 Memimage*	swimg;	/* cursor image */
215 Memimage*	swmask;	/* cursor mask */
216 Memimage*	swimg1;
217 Memimage*	swmask1;
218 
219 Point	swoffset;
220 Rectangle	swrect;	/* screen rectangle in swback */
221 Point	swpt;	/* desired cursor location */
222 Point	swvispt;	/* actual cursor location */
223 int	swvers;	/* incremented each time cursor image changes */
224 int	swvisvers;	/* the version on the screen */
225 
226 static void
lcdoff(void)227 lcdoff(void)
228 {
229 	dispc->ctrl &= ~1;		/* disable the lcd */
230 	coherence();
231 
232 	dispc->irqstat1 |= 1;		/* set framedone */
233 	coherence();
234 
235 	/* the lcd never comes ready, so don't bother with this */
236 #ifdef notdef
237 	/* spin until the frame is complete, but not forever */
238 	for(cnt = 50; !(dispc->irqstat1 & 1) && cnt-- > 0; )
239 		delay(10);
240 #endif
241 	delay(20);			/* worst case for 1 frame, 50Hz */
242 }
243 
244 static void
dssstart(void)245 dssstart(void)
246 {
247 	/* should reset the dss system */
248 	dss->sysconf |= 1;
249 	coherence();
250 }
251 
252 /* see spruf98i §15.6.7.4.2 */
253 static void
configdispc(void)254 configdispc(void)
255 {
256 	Settings *sp;
257 
258 	sp = oscreen.settings;
259 	dss->ctrl &= 0x78;		/* choose dss clock */
260 	dispc->sysconf = Midlemode | Sidlemode | EnableWakeup | Autoidle;
261 	dispc->config = Loadmode;
262 	coherence();
263 
264 	/* pll */
265 	dispc->defaultcolor[0] = 0;	/* set background color to black? */
266 	dispc->defaultcolor[1] = 0;
267 	dispc->transcolor[0] = 0;	/* set transparency to full */
268 	dispc->transcolor[1] = 0;
269 
270 	dispc->timing_h = (sp->hbp-1) << 20 | (sp->hfp-1) << 8 |
271 			(sp->hsw-1);
272 	dispc->timing_v = sp->vbp << 20 | sp->vfp << 8 |
273 			(sp->vsw-1);
274 
275 	dispc->pol_req = Ipc | Ihs | Ivs | Acb;
276 	dispc->divisor = 1 << 16 | HOWMANY(432000, sp->pixelclock);
277 
278 	dispc->lcdsize = (sp->ht - 1) << 16 | (sp->wid - 1);
279 	coherence();
280 
281 	dispc->base[0] = PADDR(framebuf->pixel);
282 	dispc->base[1] = PADDR(framebuf->pixel);
283 
284 	dispc->pos = 0;			/* place screen in the left corner */
285 	/* use the whole screen */
286 	dispc->size = (sp->ht - 1) << 16 | (sp->wid - 1);
287 
288 	/* what mode does plan 9 use for fb? */
289 	dispc->attrib = Burstsize | Format | Gfxenable;
290 
291 	dispc->preload = Tft;
292 	dispc->fifosize = Fifosize;
293 	/* 1008 is max for our Burstsize */
294 	dispc->fifothr = (Fifosize - 1) << 16 | (1008 - 1);
295 
296 	/* 1 byte is one pixel (not true, we use 2 bytes per pixel) */
297 	dispc->rowinc = 1;
298 	dispc->pixelinc = 1;
299 	dispc->winskip = 0;		/* don't skip anything */
300 	coherence();
301 
302 	// dispc->palette = PADDR(framebuf->palette);
303 }
304 
305 static void
lcdon(int enable)306 lcdon(int enable)
307 {
308 	dispc->ctrl = Gpout1 | Gpout0 | Tftdata | Digital | Lcd | Stntft |
309 		Digitalen | enable;
310 	coherence();
311 	delay(10);
312 }
313 
314 static void
lcdstop(void)315 lcdstop(void)
316 {
317 	configscreengpio();
318 	screenclockson();
319 
320 	lcdoff();
321 }
322 
323 static void
lcdinit(void)324 lcdinit(void)
325 {
326 	lcdstop();
327 
328 	dssstart();
329 	configdispc();
330 }
331 
332 /* Paint the image data with blue pixels */
333 void
screentest(void)334 screentest(void)
335 {
336 	int i;
337 
338 	for (i = nelem(framebuf->pixel) - 1; i >= 0; i--)
339 		framebuf->pixel[i] = 0x1f;			/* blue */
340 //	memset(framebuf->pixel, ~0, sizeof framebuf->pixel);	/* white */
341 }
342 
343 void
screenpower(int on)344 screenpower(int on)
345 {
346 	blankscreen(on == 0);
347 }
348 
349 /*
350  * called with drawlock locked for us, most of the time.
351  * kernel prints at inopportune times might mean we don't
352  * hold the lock, but memimagedraw is now reentrant so
353  * that should be okay: worst case we get cursor droppings.
354  */
355 void
swcursorhide(void)356 swcursorhide(void)
357 {
358 	if(swvisible == 0)
359 		return;
360 	if(swback == nil)
361 		return;
362 	swvisible = 0;
363 	memimagedraw(gscreen, swrect, swback, ZP, memopaque, ZP, S);
364 	flushmemscreen(swrect);
365 }
366 
367 void
swcursoravoid(Rectangle r)368 swcursoravoid(Rectangle r)
369 {
370 	if(swvisible && rectXrect(r, swrect))
371 		swcursorhide();
372 }
373 
374 void
swcursordraw(void)375 swcursordraw(void)
376 {
377 	if(swvisible)
378 		return;
379 	if(swenabled == 0)
380 		return;
381 	if(swback == nil || swimg1 == nil || swmask1 == nil)
382 		return;
383 //	assert(!canqlock(&drawlock));		// assertion fails on omap
384 	swvispt = swpt;
385 	swvisvers = swvers;
386 	swrect = rectaddpt(Rect(0,0,16,16), swvispt);
387 	memimagedraw(swback, swback->r, gscreen, swpt, memopaque, ZP, S);
388 	memimagedraw(gscreen, swrect, swimg1, ZP, swmask1, ZP, SoverD);
389 	flushmemscreen(swrect);
390 	swvisible = 1;
391 }
392 
393 int
cursoron(int dolock)394 cursoron(int dolock)
395 {
396 	if (dolock)
397 		lock(&oscreen);
398 	cursoroff(0);
399 	swcursordraw();
400 	if (dolock)
401 		unlock(&oscreen);
402 	return 0;
403 }
404 
405 void
cursoroff(int dolock)406 cursoroff(int dolock)
407 {
408 	if (dolock)
409 		lock(&oscreen);
410 	swcursorhide();
411 	if (dolock)
412 		unlock(&oscreen);
413 }
414 
415 void
swload(Cursor * curs)416 swload(Cursor *curs)
417 {
418 	uchar *ip, *mp;
419 	int i, j, set, clr;
420 
421 	if(!swimg || !swmask || !swimg1 || !swmask1)
422 		return;
423 	/*
424 	 * Build cursor image and mask.
425 	 * Image is just the usual cursor image
426 	 * but mask is a transparent alpha mask.
427 	 *
428 	 * The 16x16x8 memimages do not have
429 	 * padding at the end of their scan lines.
430 	 */
431 	ip = byteaddr(swimg, ZP);
432 	mp = byteaddr(swmask, ZP);
433 	for(i=0; i<32; i++){
434 		set = curs->set[i];
435 		clr = curs->clr[i];
436 		for(j=0x80; j; j>>=1){
437 			*ip++ = set&j ? 0x00 : 0xFF;
438 			*mp++ = (clr|set)&j ? 0xFF : 0x00;
439 		}
440 	}
441 	swoffset = curs->offset;
442 	swvers++;
443 	memimagedraw(swimg1,  swimg1->r,  swimg,  ZP, memopaque, ZP, S);
444 	memimagedraw(swmask1, swmask1->r, swmask, ZP, memopaque, ZP, S);
445 }
446 
447 /* called from devmouse */
448 void
setcursor(Cursor * curs)449 setcursor(Cursor* curs)
450 {
451 	cursoroff(1);
452 	oscreen.Cursor = *curs;
453 	swload(curs);
454 	cursoron(1);
455 }
456 
457 int
swmove(Point p)458 swmove(Point p)
459 {
460 	swpt = addpt(p, swoffset);
461 	return 0;
462 }
463 
464 void
swcursorclock(void)465 swcursorclock(void)
466 {
467 	int x;
468 
469 	if(!swenabled)
470 		return;
471 	swmove(mousexy());
472 	if(swvisible && eqpt(swpt, swvispt) && swvers==swvisvers)
473 		return;
474 
475 	x = splhi();
476 	if(swenabled)
477 	if(!swvisible || !eqpt(swpt, swvispt) || swvers!=swvisvers)
478 	if(canqlock(&drawlock)){
479 		swcursorhide();
480 		swcursordraw();
481 		qunlock(&drawlock);
482 	}
483 	splx(x);
484 }
485 
486 void
swcursorinit(void)487 swcursorinit(void)
488 {
489 	static int init;
490 
491 	if(!init){
492 		init = 1;
493 		addclock0link(swcursorclock, 10);
494 	}
495 	if(swback){
496 		freememimage(swback);
497 		freememimage(swmask);
498 		freememimage(swmask1);
499 		freememimage(swimg);
500 		freememimage(swimg1);
501 	}
502 
503 	swback  = allocmemimage(Rect(0,0,32,32), gscreen->chan);
504 	swmask  = allocmemimage(Rect(0,0,16,16), GREY8);
505 	swmask1 = allocmemimage(Rect(0,0,16,16), GREY1);
506 	swimg   = allocmemimage(Rect(0,0,16,16), GREY8);
507 	swimg1  = allocmemimage(Rect(0,0,16,16), GREY1);
508 	if(swback==nil || swmask==nil || swmask1==nil || swimg==nil || swimg1 == nil){
509 		print("software cursor: allocmemimage fails\n");
510 		return;
511 	}
512 
513 	memfillcolor(swmask, DOpaque);
514 	memfillcolor(swmask1, DOpaque);
515 	memfillcolor(swimg, DBlack);
516 	memfillcolor(swimg1, DBlack);
517 }
518 
519 /* called from main and possibly later from devdss to change resolution */
520 void
screeninit(void)521 screeninit(void)
522 {
523 	static int first = 1;
524 
525 	if (first) {
526 		iprint("screeninit...");
527 		oscreen.settings = &settings[Res1280x1024];
528 
529 		lcdstop();
530 		if (framebuf)
531 			free(framebuf);
532 		/* mode is 16*32 = 512 */
533 		framebuf = xspanalloc(sizeof *framebuf, 16*32, 0);
534 	}
535 
536 	lcdinit();
537 	lcdon(1);
538 	if (first) {
539 		memimageinit();
540 		memdefont = getmemdefont();
541 		screentest();
542 	}
543 
544 	xgdata.ref = 1;
545 	xgdata.bdata = (uchar *)framebuf->pixel;
546 
547 	gscreen = &xgscreen;
548 	gscreen->r = Rect(0, 0, Wid, Ht);
549 	gscreen->clipr = gscreen->r;
550 	/* width, in words, of a single scan line */
551 	gscreen->width = Wid * (Depth / BI2BY) / BY2WD;
552 	flushmemscreen(gscreen->r);
553 
554 	blanktime = 3;				/* minutes */
555 
556 	if (first) {
557 		iprint("on: blue for 3 seconds...");
558 		delay(3*1000);
559 		iprint("\n");
560 
561 		screenwin();		/* draw border & top orange bar */
562 		screenputs = omapscreenputs;
563 		iprint("screen: frame buffer at %#p for %dx%d\n",
564 			framebuf, oscreen.settings->wid, oscreen.settings->ht);
565 
566 		swenabled = 1;
567 		swcursorinit();		/* needs gscreen set */
568 		setcursor(&arrow);
569 
570 		first = 0;
571 	}
572 }
573 
574 /* flushmemscreen should change buffer? */
575 void
flushmemscreen(Rectangle r)576 flushmemscreen(Rectangle r)
577 {
578 	ulong start, end;
579 
580 	if (r.min.x < 0)
581 		r.min.x = 0;
582 	if (r.max.x > Wid)
583 		r.max.x = Wid;
584 	if (r.min.y < 0)
585 		r.min.y = 0;
586 	if (r.max.y > Ht)
587 		r.max.y = Ht;
588 	if (rectclip(&r, gscreen->r) == 0)
589 		return;
590 	start = (ulong)&framebuf->pixel[r.min.y*Wid + r.min.x];
591 	end   = (ulong)&framebuf->pixel[(r.max.y - 1)*Wid + r.max.x -1];
592 	cachedwbse((ulong *)start, end - start);
593 }
594 
595 /*
596  * export screen to devdraw
597  */
598 uchar*
attachscreen(Rectangle * r,ulong * chan,int * d,int * width,int * softscreen)599 attachscreen(Rectangle *r, ulong *chan, int *d, int *width, int *softscreen)
600 {
601 	*r = gscreen->r;
602 	*d = gscreen->depth;
603 	*chan = gscreen->chan;
604 	*width = gscreen->width;
605 	*softscreen = (landscape == 0);
606 	return (uchar *)gscreen->data->bdata;
607 }
608 
609 void
getcolor(ulong p,ulong * pr,ulong * pg,ulong * pb)610 getcolor(ulong p, ulong *pr, ulong *pg, ulong *pb)
611 {
612 	USED(p, pr, pg, pb);
613 }
614 
615 int
setcolor(ulong p,ulong r,ulong g,ulong b)616 setcolor(ulong p, ulong r, ulong g, ulong b)
617 {
618 	USED(p, r, g, b);
619 	return 0;
620 }
621 
622 void
blankscreen(int blank)623 blankscreen(int blank)
624 {
625 	if (blank)
626 		lcdon(0);
627 	else {
628 		lcdinit();
629 		lcdon(1);
630 	}
631 }
632 
633 static void
omapscreenputs(char * s,int n)634 omapscreenputs(char *s, int n)
635 {
636 	int i;
637 	Rune r;
638 	char buf[4];
639 
640 	if (!islo()) {
641 		/* don't deadlock trying to print in interrupt */
642 		if (!canlock(&screenlock))
643 			return;			/* discard s */
644 	} else
645 		lock(&screenlock);
646 
647 	while (n > 0) {
648 		i = chartorune(&r, s);
649 		if (i == 0) {
650 			s++;
651 			--n;
652 			continue;
653 		}
654 		memmove(buf, s, i);
655 		buf[i] = 0;
656 		n -= i;
657 		s += i;
658 		screenputc(buf);
659 	}
660 	unlock(&screenlock);
661 }
662 
663 static void
screenwin(void)664 screenwin(void)
665 {
666 	char *greet;
667 	Memimage *orange;
668 	Point p, q;
669 	Rectangle r;
670 
671 	memsetchan(gscreen, RGB16);
672 
673 	back = memwhite;
674 	conscol = memblack;
675 
676 	orange = allocmemimage(Rect(0, 0, 1, 1), RGB16);
677 	orange->flags |= Frepl;
678 	orange->clipr = gscreen->r;
679 	orange->data->bdata[0] = 0x40;		/* magic: colour? */
680 	orange->data->bdata[1] = 0xfd;		/* magic: colour? */
681 
682 	w = memdefont->info[' '].width;
683 	h = memdefont->height;
684 
685 	r = insetrect(gscreen->r, 4);
686 
687 	memimagedraw(gscreen, r, memblack, ZP, memopaque, ZP, S);
688 	window = insetrect(r, 4);
689 	memimagedraw(gscreen, window, memwhite, ZP, memopaque, ZP, S);
690 
691 	memimagedraw(gscreen, Rect(window.min.x, window.min.y,
692 		window.max.x, window.min.y + h + 5 + 6), orange, ZP, nil, ZP, S);
693 	freememimage(orange);
694 	window = insetrect(window, 5);
695 
696 	greet = " Plan 9 Console ";
697 	p = addpt(window.min, Pt(10, 0));
698 	q = memsubfontwidth(memdefont, greet);
699 	memimagestring(gscreen, p, conscol, ZP, memdefont, greet);
700 	flushmemscreen(r);
701 	window.min.y += h + 6;
702 	curpos = window.min;
703 	window.max.y = window.min.y + ((window.max.y - window.min.y) / h) * h;
704 }
705 
706 static void
scroll(void)707 scroll(void)
708 {
709 	int o;
710 	Point p;
711 	Rectangle r;
712 
713 	/* move window contents up Scroll text lines */
714 	o = Scroll * h;
715 	r = Rpt(window.min, Pt(window.max.x, window.max.y - o));
716 	p = Pt(window.min.x, window.min.y + o);
717 	memimagedraw(gscreen, r, gscreen, p, nil, p, S);
718 	flushmemscreen(r);
719 
720 	/* clear the bottom Scroll text lines */
721 	r = Rpt(Pt(window.min.x, window.max.y - o), window.max);
722 	memimagedraw(gscreen, r, back, ZP, nil, ZP, S);
723 	flushmemscreen(r);
724 
725 	curpos.y -= o;
726 }
727 
728 static void
screenputc(char * buf)729 screenputc(char *buf)
730 {
731 	int w;
732 	uint pos;
733 	Point p;
734 	Rectangle r;
735 	static int *xp;
736 	static int xbuf[256];
737 
738 	if (xp < xbuf || xp >= &xbuf[sizeof(xbuf)])
739 		xp = xbuf;
740 
741 	switch (buf[0]) {
742 	case '\n':
743 		if (curpos.y + h >= window.max.y)
744 			scroll();
745 		curpos.y += h;
746 		screenputc("\r");
747 		break;
748 	case '\r':
749 		xp = xbuf;
750 		curpos.x = window.min.x;
751 		break;
752 	case '\t':
753 		p = memsubfontwidth(memdefont, " ");
754 		w = p.x;
755 		if (curpos.x >= window.max.x - Tabstop * w)
756 			screenputc("\n");
757 
758 		pos = (curpos.x - window.min.x) / w;
759 		pos = Tabstop - pos % Tabstop;
760 		*xp++ = curpos.x;
761 		r = Rect(curpos.x, curpos.y, curpos.x + pos * w, curpos.y + h);
762 		memimagedraw(gscreen, r, back, back->r.min, nil, back->r.min, S);
763 		flushmemscreen(r);
764 		curpos.x += pos * w;
765 		break;
766 	case '\b':
767 		if (xp <= xbuf)
768 			break;
769 		xp--;
770 		r = Rect(*xp, curpos.y, curpos.x, curpos.y + h);
771 		memimagedraw(gscreen, r, back, back->r.min, nil, back->r.min, S);
772 		flushmemscreen(r);
773 		curpos.x = *xp;
774 		break;
775 	case '\0':
776 		break;
777 	default:
778 		p = memsubfontwidth(memdefont, buf);
779 		w = p.x;
780 
781 		if (curpos.x >= window.max.x - w)
782 			screenputc("\n");
783 
784 		*xp++ = curpos.x;
785 		r = Rect(curpos.x, curpos.y, curpos.x + w, curpos.y + h);
786 		memimagedraw(gscreen, r, back, back->r.min, nil, back->r.min, S);
787 		memimagestring(gscreen, curpos, conscol, ZP, memdefont, buf);
788 		flushmemscreen(r);
789 		curpos.x += w;
790 	}
791 }
792