xref: /plan9-contrib/sys/src/9/bcm/screen.c (revision 5c47fe09a0cc86dfb02c0ea4a2b6aec7eda2361f)
1 /*
2  * bcm2385 framebuffer
3  */
4 
5 #include "u.h"
6 #include "../port/lib.h"
7 #include "mem.h"
8 #include "dat.h"
9 #include "fns.h"
10 
11 #define	Image	IMAGE
12 #include <draw.h>
13 #include <memdraw.h>
14 #include <cursor.h>
15 #include "screen.h"
16 
17 enum {
18 	Tabstop		= 4,
19 	Scroll		= 8,
20 	Wid		= 1024,
21 	Ht		= 768,
22 	Depth		= 16,
23 };
24 
25 Cursor	arrow = {
26 	{ -1, -1 },
27 	{ 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C,
28 	  0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04,
29 	  0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04,
30 	  0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40,
31 	},
32 	{ 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0,
33 	  0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8,
34 	  0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8,
35 	  0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00,
36 	},
37 };
38 
39 Memimage *gscreen;
40 Lcd	*lcd;
41 
42 static Memdata xgdata;
43 
44 /*static*/ Memimage xgscreen =
45 {
46 	{ 0, 0, Wid, Ht },	/* r */
47 	{ 0, 0, Wid, Ht },	/* clipr */
48 	Depth,			/* depth */
49 	3,			/* nchan */
50 	RGB16,			/* chan */
51 	nil,			/* cmap */
52 	&xgdata,		/* data */
53 	0,			/* zero */
54 	0, 			/* width in words of a single scan line */
55 	0,			/* layer */
56 	0,			/* flags */
57 };
58 
59 static Memimage *conscol;
60 static Memimage *back;
61 static Memsubfont *memdefont;
62 
63 static Lock screenlock;
64 
65 static Point	curpos;
66 static int	h, w;
67 static Rectangle window;
68 
69 static void myscreenputs(char *s, int n);
70 static void screenputc(char *buf);
71 static void screenwin(void);
72 
73 /*
74  * Software cursor.
75  */
76 static int	swvisible;	/* is the cursor visible? */
77 static int	swenabled;	/* is the cursor supposed to be on the screen? */
78 static Memimage *swback;	/* screen under cursor */
79 static Memimage *swimg;		/* cursor image */
80 static Memimage *swmask;	/* cursor mask */
81 static Memimage *swimg1;
82 static Memimage *swmask1;
83 
84 static Point	swoffset;
85 static Rectangle swrect;	/* screen rectangle in swback */
86 static Point	swpt;		/* desired cursor location */
87 static Point	swvispt;	/* actual cursor location */
88 static int	swvers;		/* incremented each time cursor image changes */
89 static int	swvisvers;	/* the version on the screen */
90 
91 /*
92  * called with drawlock locked for us, most of the time.
93  * kernel prints at inopportune times might mean we don't
94  * hold the lock, but memimagedraw is now reentrant so
95  * that should be okay: worst case we get cursor droppings.
96  */
97 static void
swcursorhide(void)98 swcursorhide(void)
99 {
100 	if(swvisible == 0)
101 		return;
102 	if(swback == nil)
103 		return;
104 	swvisible = 0;
105 	memimagedraw(gscreen, swrect, swback, ZP, memopaque, ZP, S);
106 	flushmemscreen(swrect);
107 }
108 
109 static void
swcursoravoid(Rectangle r)110 swcursoravoid(Rectangle r)
111 {
112 	if(swvisible && rectXrect(r, swrect))
113 		swcursorhide();
114 }
115 
116 static void
swcursordraw(void)117 swcursordraw(void)
118 {
119 	int dounlock;
120 
121 	if(swvisible)
122 		return;
123 	if(swenabled == 0)
124 		return;
125 	if(swback == nil || swimg1 == nil || swmask1 == nil)
126 		return;
127 	dounlock = canqlock(&drawlock);
128 	swvispt = swpt;
129 	swvisvers = swvers;
130 	swrect = rectaddpt(Rect(0,0,16,16), swvispt);
131 	memimagedraw(swback, swback->r, gscreen, swpt, memopaque, ZP, S);
132 	memimagedraw(gscreen, swrect, swimg1, ZP, swmask1, ZP, SoverD);
133 	flushmemscreen(swrect);
134 	swvisible = 1;
135 	if(dounlock)
136 		qunlock(&drawlock);
137 }
138 
139 int
cursoron(int dolock)140 cursoron(int dolock)
141 {
142 	int retry;
143 
144 	if (dolock)
145 		lock(&cursor);
146 	if (canqlock(&drawlock)) {
147 		retry = 0;
148 		swcursorhide();
149 		swcursordraw();
150 		qunlock(&drawlock);
151 	} else
152 		retry = 1;
153 	if (dolock)
154 		unlock(&cursor);
155 	return retry;
156 }
157 
158 void
cursoroff(int dolock)159 cursoroff(int dolock)
160 {
161 	if (dolock)
162 		lock(&cursor);
163 	swcursorhide();
164 	if (dolock)
165 		unlock(&cursor);
166 }
167 
168 static void
swload(Cursor * curs)169 swload(Cursor *curs)
170 {
171 	uchar *ip, *mp;
172 	int i, j, set, clr;
173 
174 	if(!swimg || !swmask || !swimg1 || !swmask1)
175 		return;
176 	/*
177 	 * Build cursor image and mask.
178 	 * Image is just the usual cursor image
179 	 * but mask is a transparent alpha mask.
180 	 *
181 	 * The 16x16x8 memimages do not have
182 	 * padding at the end of their scan lines.
183 	 */
184 	ip = byteaddr(swimg, ZP);
185 	mp = byteaddr(swmask, ZP);
186 	for(i=0; i<32; i++){
187 		set = curs->set[i];
188 		clr = curs->clr[i];
189 		for(j=0x80; j; j>>=1){
190 			*ip++ = set&j ? 0x00 : 0xFF;
191 			*mp++ = (clr|set)&j ? 0xFF : 0x00;
192 		}
193 	}
194 	swoffset = curs->offset;
195 	swvers++;
196 	memimagedraw(swimg1,  swimg1->r,  swimg,  ZP, memopaque, ZP, S);
197 	memimagedraw(swmask1, swmask1->r, swmask, ZP, memopaque, ZP, S);
198 }
199 
200 /* called from devmouse */
201 void
setcursor(Cursor * curs)202 setcursor(Cursor* curs)
203 {
204 	cursoroff(0);
205 	swload(curs);
206 	cursoron(0);
207 }
208 
209 static int
swmove(Point p)210 swmove(Point p)
211 {
212 	swpt = addpt(p, swoffset);
213 	return 0;
214 }
215 
216 static void
swcursorclock(void)217 swcursorclock(void)
218 {
219 	int x;
220 
221 	if(!swenabled)
222 		return;
223 	swmove(mousexy());
224 	if(swvisible && eqpt(swpt, swvispt) && swvers==swvisvers)
225 		return;
226 
227 	x = splhi();
228 	if(swenabled)
229 	if(!swvisible || !eqpt(swpt, swvispt) || swvers!=swvisvers)
230 	if(canqlock(&drawlock)){
231 		swcursorhide();
232 		swcursordraw();
233 		qunlock(&drawlock);
234 	}
235 	splx(x);
236 }
237 
238 void
swcursorinit(void)239 swcursorinit(void)
240 {
241 	static int init;
242 
243 	if(!init){
244 		init = 1;
245 		addclock0link(swcursorclock, 10);
246 		swenabled = 1;
247 	}
248 	if(swback){
249 		freememimage(swback);
250 		freememimage(swmask);
251 		freememimage(swmask1);
252 		freememimage(swimg);
253 		freememimage(swimg1);
254 	}
255 
256 	swback  = allocmemimage(Rect(0,0,32,32), gscreen->chan);
257 	swmask  = allocmemimage(Rect(0,0,16,16), GREY8);
258 	swmask1 = allocmemimage(Rect(0,0,16,16), GREY1);
259 	swimg   = allocmemimage(Rect(0,0,16,16), GREY8);
260 	swimg1  = allocmemimage(Rect(0,0,16,16), GREY1);
261 	if(swback==nil || swmask==nil || swmask1==nil || swimg==nil || swimg1 == nil){
262 		print("software cursor: allocmemimage fails\n");
263 		return;
264 	}
265 
266 	memfillcolor(swmask, DOpaque);
267 	memfillcolor(swmask1, DOpaque);
268 	memfillcolor(swimg, DBlack);
269 	memfillcolor(swimg1, DBlack);
270 }
271 
272 int
hwdraw(Memdrawparam * par)273 hwdraw(Memdrawparam *par)
274 {
275 	Memimage *dst, *src, *mask;
276 
277 	if((dst=par->dst) == nil || dst->data == nil)
278 		return 0;
279 	if((src=par->src) == nil || src->data == nil)
280 		return 0;
281 	if((mask=par->mask) == nil || mask->data == nil)
282 		return 0;
283 
284 	if(dst->data->bdata == xgdata.bdata)
285 		swcursoravoid(par->r);
286 	if(src->data->bdata == xgdata.bdata)
287 		swcursoravoid(par->sr);
288 	if(mask->data->bdata == xgdata.bdata)
289 		swcursoravoid(par->mr);
290 
291 	if(lcd)
292 		lcd->draw(par->r);
293 
294 	return 0;
295 }
296 
297 static int
screensize(void)298 screensize(void)
299 {
300 	char *p, buf[32];
301 	char *f[3];
302 	int width, height, depth;
303 
304 	p = getconf("vgasize");
305 	if(p == nil || memccpy(buf, p, '\0', sizeof buf) == nil)
306 		return -1;
307 
308 	if(getfields(buf, f, nelem(f), 0, "x") != nelem(f) ||
309 	    (width = atoi(f[0])) < 16 ||
310 	    (height = atoi(f[1])) <= 0 ||
311 	    (depth = atoi(f[2])) <= 0)
312 		return -1;
313 	xgscreen.r.max = Pt(width, height);
314 	xgscreen.depth = depth;
315 	return 0;
316 }
317 
318 void
screeninit(void)319 screeninit(void)
320 {
321 	uchar *fb;
322 	char *p;
323 	int set, rgbswap;
324 	ulong chan;
325 
326 	set = screensize() == 0;
327 	fb = fbinit(set, &xgscreen.r.max.x, &xgscreen.r.max.y, &xgscreen.depth);
328 	if(fb == nil){
329 		xgscreen.r.max = Pt(640, 480);
330 		xgscreen.depth = 16;
331 		fb = fbinit(set, &xgscreen.r.max.x, &xgscreen.r.max.y, &xgscreen.depth);
332 	}
333 	if(fb == nil){
334 		print("can't initialise %dx%dx%d framebuffer \n",
335 			xgscreen.r.max.x, xgscreen.r.max.y, xgscreen.depth);
336 		return;
337 	}
338 	rgbswap = ((p = getconf("bcm2708_fb.fbswap")) != nil && *p == '1');
339 	xgscreen.clipr = xgscreen.r;
340 	switch(xgscreen.depth){
341 	default:
342 		print("unsupported screen depth %d\n", xgscreen.depth);
343 		xgscreen.depth = 16;
344 		/* fall through */
345 	case 16:
346 		chan = RGB16;
347 		break;
348 	case 24:
349 		chan = rgbswap? RGB24 : BGR24;
350 		break;
351 	case 32:
352 		chan = rgbswap? XRGB32 : XBGR32;
353 		break;
354 	}
355 	memsetchan(&xgscreen, chan);
356 	conf.monitor = 1;
357 	xgdata.bdata = fb;
358 	xgdata.ref = 1;
359 	gscreen = &xgscreen;
360 	gscreen->width = wordsperline(gscreen->r, gscreen->depth);
361 
362 	memimageinit();
363 	memdefont = getmemdefont();
364 	screenwin();
365 	screenputs = myscreenputs;
366 }
367 
368 void
flushmemscreen(Rectangle)369 flushmemscreen(Rectangle)
370 {
371 }
372 
373 uchar*
attachscreen(Rectangle * r,ulong * chan,int * d,int * width,int * softscreen)374 attachscreen(Rectangle *r, ulong *chan, int* d, int *width, int *softscreen)
375 {
376 	*r = gscreen->r;
377 	*d = gscreen->depth;
378 	*chan = gscreen->chan;
379 	*width = gscreen->width;
380 	*softscreen = 0;
381 
382 	return gscreen->data->bdata;
383 }
384 
385 void
getcolor(ulong p,ulong * pr,ulong * pg,ulong * pb)386 getcolor(ulong p, ulong *pr, ulong *pg, ulong *pb)
387 {
388 	USED(p, pr, pg, pb);
389 }
390 
391 int
setcolor(ulong p,ulong r,ulong g,ulong b)392 setcolor(ulong p, ulong r, ulong g, ulong b)
393 {
394 	USED(p, r, g, b);
395 	return 0;
396 }
397 
398 void
blankscreen(int blank)399 blankscreen(int blank)
400 {
401 	fbblank(blank);
402 	if(lcd)
403 		lcd->blank(blank);
404 }
405 
406 static void
myscreenputs(char * s,int n)407 myscreenputs(char *s, int n)
408 {
409 	int i;
410 	Rune r;
411 	char buf[4];
412 
413 	if(!islo()) {
414 		/* don't deadlock trying to print in interrupt */
415 		if(!canlock(&screenlock))
416 			return;
417 	}
418 	else
419 		lock(&screenlock);
420 
421 	while(n > 0){
422 		i = chartorune(&r, s);
423 		if(i == 0){
424 			s++;
425 			--n;
426 			continue;
427 		}
428 		memmove(buf, s, i);
429 		buf[i] = 0;
430 		n -= i;
431 		s += i;
432 		screenputc(buf);
433 	}
434 	unlock(&screenlock);
435 }
436 
437 static void
screenwin(void)438 screenwin(void)
439 {
440 	char *greet;
441 	Memimage *orange;
442 	Point p, q;
443 	Rectangle r;
444 
445 	back = memwhite;
446 	conscol = memblack;
447 
448 	orange = allocmemimage(Rect(0, 0, 1, 1), RGB16);
449 	orange->flags |= Frepl;
450 	orange->clipr = gscreen->r;
451 	orange->data->bdata[0] = 0x40;		/* magic: colour? */
452 	orange->data->bdata[1] = 0xfd;		/* magic: colour? */
453 
454 	w = memdefont->info[' '].width;
455 	h = memdefont->height;
456 
457 	r = insetrect(gscreen->r, 4);
458 
459 	memimagedraw(gscreen, r, memblack, ZP, memopaque, ZP, S);
460 	window = insetrect(r, 4);
461 	memimagedraw(gscreen, window, memwhite, ZP, memopaque, ZP, S);
462 
463 	memimagedraw(gscreen, Rect(window.min.x, window.min.y,
464 		window.max.x, window.min.y + h + 5 + 6), orange, ZP, nil, ZP, S);
465 	freememimage(orange);
466 	window = insetrect(window, 5);
467 
468 	greet = " Plan 9 Console ";
469 	p = addpt(window.min, Pt(10, 0));
470 	q = memsubfontwidth(memdefont, greet);
471 	memimagestring(gscreen, p, conscol, ZP, memdefont, greet);
472 	flushmemscreen(r);
473 	window.min.y += h + 6;
474 	curpos = window.min;
475 	window.max.y = window.min.y + ((window.max.y - window.min.y) / h) * h;
476 }
477 
478 static void
scroll(void)479 scroll(void)
480 {
481 	int o;
482 	Point p;
483 	Rectangle r;
484 
485 	o = Scroll*h;
486 	r = Rpt(window.min, Pt(window.max.x, window.max.y-o));
487 	p = Pt(window.min.x, window.min.y+o);
488 	memimagedraw(gscreen, r, gscreen, p, nil, p, S);
489 	flushmemscreen(r);
490 	if(lcd)
491 		lcd->draw(r);
492 	r = Rpt(Pt(window.min.x, window.max.y-o), window.max);
493 	memimagedraw(gscreen, r, back, ZP, nil, ZP, S);
494 	flushmemscreen(r);
495 	if(lcd)
496 		lcd->draw(r);
497 
498 	curpos.y -= o;
499 }
500 
501 static void
screenputc(char * buf)502 screenputc(char *buf)
503 {
504 	int w;
505 	uint pos;
506 	Point p;
507 	Rectangle r;
508 	static int *xp;
509 	static int xbuf[256];
510 
511 	if (xp < xbuf || xp >= &xbuf[sizeof(xbuf)])
512 		xp = xbuf;
513 
514 	switch (buf[0]) {
515 	case '\n':
516 		if (curpos.y + h >= window.max.y)
517 			scroll();
518 		curpos.y += h;
519 		screenputc("\r");
520 		break;
521 	case '\r':
522 		xp = xbuf;
523 		curpos.x = window.min.x;
524 		break;
525 	case '\t':
526 		p = memsubfontwidth(memdefont, " ");
527 		w = p.x;
528 		if (curpos.x >= window.max.x - Tabstop * w)
529 			screenputc("\n");
530 
531 		pos = (curpos.x - window.min.x) / w;
532 		pos = Tabstop - pos % Tabstop;
533 		*xp++ = curpos.x;
534 		r = Rect(curpos.x, curpos.y, curpos.x + pos * w, curpos.y + h);
535 		memimagedraw(gscreen, r, back, back->r.min, nil, back->r.min, S);
536 		flushmemscreen(r);
537 		curpos.x += pos * w;
538 		break;
539 	case '\b':
540 		if (xp <= xbuf)
541 			break;
542 		xp--;
543 		r = Rect(*xp, curpos.y, curpos.x, curpos.y + h);
544 		memimagedraw(gscreen, r, back, back->r.min, nil, back->r.min, S);
545 		flushmemscreen(r);
546 		curpos.x = *xp;
547 		break;
548 	case '\0':
549 		break;
550 	default:
551 		p = memsubfontwidth(memdefont, buf);
552 		w = p.x;
553 
554 		if (curpos.x >= window.max.x - w)
555 			screenputc("\n");
556 
557 		*xp++ = curpos.x;
558 		r = Rect(curpos.x, curpos.y, curpos.x + w, curpos.y + h);
559 		memimagedraw(gscreen, r, back, back->r.min, nil, back->r.min, S);
560 		memimagestring(gscreen, curpos, conscol, ZP, memdefont, buf);
561 		flushmemscreen(r);
562 		curpos.x += w;
563 		break;
564 	}
565 }
566