Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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
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
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
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
315
lcdstop(void)
316
{
317
	configscreengpio();
318
	screenclockson();
319
 
320
	lcdoff();
321
}
322
 
323
static void
324
lcdinit(void)
325
{
326
	lcdstop();
327
 
328
	dssstart();
329
	configdispc();
330
}
331
 
332
/* Paint the image data with blue pixels */
333
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
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
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
368
swcursoravoid(Rectangle r)
369
{
370
	if(swvisible && rectXrect(r, swrect))
371
		swcursorhide();
372
}
373
 
374
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
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
406
cursoroff(int dolock)
407
{
408
	if (dolock)
409
		lock(&oscreen);
410
	swcursorhide();
411
	if (dolock)
412
		unlock(&oscreen);
413
}
414
 
415
void
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
449
setcursor(Cursor* curs)
450
{
451
	cursoroff(1);
452
	oscreen.Cursor = *curs;
453
	swload(curs);
454
	cursoron(1);
455
}
456
 
457
int
458
swmove(Point p)
459
{
460
	swpt = addpt(p, swoffset);
461
	return 0;
462
}
463
 
464
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
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
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
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*
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
610
getcolor(ulong p, ulong *pr, ulong *pg, ulong *pb)
611
{
612
	USED(p, pr, pg, pb);
613
}
614
 
615
int
616
setcolor(ulong p, ulong r, ulong g, ulong b)
617
{
618
	USED(p, r, g, b);
619
	return 0;
620
}
621
 
622
void
623
blankscreen(int blank)
624
{
625
	if (blank)
626
		lcdon(0);
627
	else {
628
		lcdinit();
629
		lcdon(1);
630
	}
631
}
632
 
633
static void
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
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
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
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
}