xref: /plan9-contrib/sys/src/9k/k10/screen.c (revision 45e6af3b6d7025ef7184352bb3f6852edd8de07e)
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "io.h"
7 #include "ureg.h"
8 #include "../port/error.h"
9 
10 #define	Image	IMAGE
11 #include <draw.h>
12 #include <memdraw.h>
13 #include <cursor.h>
14 #include "screen.h"
15 
16 #define	BI2WD		32			/* bits per word */
17 #define	BY2WD		4			/* bytes per word */
18 
19 #define RGB2K(r,g,b)	((156763*(r)+307758*(g)+59769*(b))>>19)
20 
21 Point ZP = {0, 0};
22 
23 Rectangle physgscreenr;
24 
25 Memdata gscreendata;
26 Memimage *gscreen;
27 
28 VGAscr vgascreen[1];
29 
30 Cursor	arrow = {
31 	{ -1, -1 },
32 	{ 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C,
33 	  0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04,
34 	  0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04,
35 	  0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40,
36 	},
37 	{ 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0,
38 	  0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8,
39 	  0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8,
40 	  0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00,
41 	},
42 };
43 
44 int didswcursorinit;
45 
46 static void *softscreen;
47 
48 int
screensize(int x,int y,int z,ulong chan)49 screensize(int x, int y, int z, ulong chan)
50 {
51 	VGAscr *scr;
52 	void *oldsoft;
53 
54 	lock(&vgascreenlock);
55 	if(waserror()){
56 		unlock(&vgascreenlock);
57 		nexterror();
58 	}
59 
60 	memimageinit();
61 	scr = &vgascreen[0];
62 	oldsoft = softscreen;
63 
64 	if(scr->paddr == 0){
65 		int width = (x*z)/BI2WD;
66 		void *p;
67 
68 		p = malloc(width*BY2WD*y);
69 		if(p == nil)
70 			error("no memory for vga soft screen");
71 		gscreendata.bdata = softscreen = p;
72 		if(scr->dev && scr->dev->page){
73 			scr->vaddr = KADDR(VGAMEM());
74 			scr->apsize = 1<<16;
75 		}
76 		scr->useflush = 1;
77 	}
78 	else{
79 		gscreendata.bdata = scr->vaddr;
80 		scr->useflush = scr->dev && scr->dev->flush;
81 	}
82 
83 	scr->gscreen = nil;
84 	if(gscreen)
85 		freememimage(gscreen);
86 	gscreen = allocmemimaged(Rect(0,0,x,y), chan, &gscreendata);
87 	if(gscreen == nil)
88 		error("no memory for vga memimage");
89 	vgaimageinit(chan);
90 
91 	scr->palettedepth = 6;	/* default */
92 	scr->gscreendata = &gscreendata;
93 	scr->memdefont = getmemdefont();
94 	scr->gscreen = gscreen;
95 
96 	physgscreenr = gscreen->r;
97 	unlock(&vgascreenlock);
98 	poperror();
99 	if(oldsoft)
100 		free(oldsoft);
101 
102 	memimagedraw(gscreen, gscreen->r, memblack, ZP, nil, ZP, S);
103 	flushmemscreen(gscreen->r);
104 
105 	if(didswcursorinit)
106 		swcursorinit();
107 	drawcmap();
108 	return 0;
109 }
110 
111 int
screenaperture(int size,int align)112 screenaperture(int size, int align)
113 {
114 	VGAscr *scr;
115 
116 	scr = &vgascreen[0];
117 
118 	if(scr->paddr)	/* set up during enable */
119 		return 0;
120 
121 	if(size == 0)
122 		return 0;
123 
124 	if(scr->dev && scr->dev->linear){
125 		scr->dev->linear(scr, size, align);
126 		return 0;
127 	}
128 
129 	/*
130 	 * Need to allocate some physical address space.
131 	 * The driver will tell the card to use it.
132 	 */
133 	size = ROUNDUP(size, 4*KiB);
134 	scr->paddr = (uintptr)malloc(size);
135 	if(scr->paddr == 0)
136 		return -1;
137 	scr->vaddr = vmap(scr->paddr, size);
138 	if(scr->vaddr == nil)
139 		return -1;
140 	scr->apsize = size;
141 
142 	return 0;
143 }
144 
145 uchar*
attachscreen(Rectangle * r,ulong * chan,int * d,int * width,int * softscreen)146 attachscreen(Rectangle* r, ulong* chan, int* d, int* width, int *softscreen)
147 {
148 	VGAscr *scr;
149 
150 	scr = &vgascreen[0];
151 	if(scr->gscreen == nil || scr->gscreendata == nil)
152 		return nil;
153 
154 	*r = scr->gscreen->clipr;
155 	*chan = scr->gscreen->chan;
156 	*d = scr->gscreen->depth;
157 	*width = scr->gscreen->width;
158 	*softscreen = scr->useflush;
159 
160 	return scr->gscreendata->bdata;
161 }
162 
163 /*
164  * It would be fair to say that this doesn't work for >8-bit screens.
165  */
166 void
flushmemscreen(Rectangle r)167 flushmemscreen(Rectangle r)
168 {
169 	VGAscr *scr;
170 	uchar *sp, *disp, *sdisp, *edisp;
171 	int y, len, incs, off, page;
172 
173 	scr = &vgascreen[0];
174 	if(scr->dev && scr->dev->flush){
175 		scr->dev->flush(scr, r);
176 		return;
177 	}
178 	if(scr->gscreen == nil || scr->useflush == 0)
179 		return;
180 	if(scr->dev == nil || scr->dev->page == nil)
181 		return;
182 
183 	if(rectclip(&r, scr->gscreen->r) == 0)
184 		return;
185 
186 	incs = scr->gscreen->width * BY2WD;
187 
188 	switch(scr->gscreen->depth){
189 	default:
190 		len = 0;
191 		panic("flushmemscreen: depth");
192 		break;
193 	case 8:
194 		len = Dx(r);
195 		break;
196 	}
197 	if(len < 1)
198 		return;
199 
200 	off = r.min.y*scr->gscreen->width*BY2WD+(r.min.x*scr->gscreen->depth)/8;
201 	page = off/scr->apsize;
202 	off %= scr->apsize;
203 	disp = scr->vaddr;
204 	sdisp = disp+off;
205 	edisp = disp+scr->apsize;
206 
207 	off = r.min.y*scr->gscreen->width*BY2WD+(r.min.x*scr->gscreen->depth)/8;
208 
209 	sp = scr->gscreendata->bdata + off;
210 
211 	scr->dev->page(scr, page);
212 	for(y = r.min.y; y < r.max.y; y++) {
213 		if(sdisp + incs < edisp) {
214 			memmove(sdisp, sp, len);
215 			sp += incs;
216 			sdisp += incs;
217 		}
218 		else {
219 			off = edisp - sdisp;
220 			page++;
221 			if(off <= len){
222 				if(off > 0)
223 					memmove(sdisp, sp, off);
224 				scr->dev->page(scr, page);
225 				if(len - off > 0)
226 					memmove(disp, sp+off, len - off);
227 			}
228 			else {
229 				memmove(sdisp, sp, len);
230 				scr->dev->page(scr, page);
231 			}
232 			sp += incs;
233 			sdisp += incs - scr->apsize;
234 		}
235 	}
236 }
237 
238 void
getcolor(ulong p,ulong * pr,ulong * pg,ulong * pb)239 getcolor(ulong p, ulong* pr, ulong* pg, ulong* pb)
240 {
241 	VGAscr *scr;
242 	ulong x;
243 
244 	scr = &vgascreen[0];
245 	if(scr->gscreen == nil)
246 		return;
247 
248 	switch(scr->gscreen->depth){
249 	default:
250 		x = 0x0F;
251 		break;
252 	case 8:
253 		x = 0xFF;
254 		break;
255 	}
256 	p &= x;
257 
258 	lock(&cursor);
259 	*pr = scr->colormap[p][0];
260 	*pg = scr->colormap[p][1];
261 	*pb = scr->colormap[p][2];
262 	unlock(&cursor);
263 }
264 
265 int
setpalette(ulong p,ulong r,ulong g,ulong b)266 setpalette(ulong p, ulong r, ulong g, ulong b)
267 {
268 	VGAscr *scr;
269 	int d;
270 
271 	scr = &vgascreen[0];
272 	d = scr->palettedepth;
273 
274 	lock(&cursor);
275 	scr->colormap[p][0] = r;
276 	scr->colormap[p][1] = g;
277 	scr->colormap[p][2] = b;
278 	vgao(PaddrW, p);
279 	vgao(Pdata, r>>(32-d));
280 	vgao(Pdata, g>>(32-d));
281 	vgao(Pdata, b>>(32-d));
282 	unlock(&cursor);
283 
284 	return ~0;
285 }
286 
287 /*
288  * On some video cards (e.g. Mach64), the palette is used as the
289  * DAC registers for >8-bit modes.  We don't want to set them when the user
290  * is trying to set a colormap and the card is in one of these modes.
291  */
292 int
setcolor(ulong p,ulong r,ulong g,ulong b)293 setcolor(ulong p, ulong r, ulong g, ulong b)
294 {
295 	VGAscr *scr;
296 	int x;
297 
298 	scr = &vgascreen[0];
299 	if(scr->gscreen == nil)
300 		return 0;
301 
302 	switch(scr->gscreen->depth){
303 	case 1:
304 	case 2:
305 	case 4:
306 		x = 0x0F;
307 		break;
308 	case 8:
309 		x = 0xFF;
310 		break;
311 	default:
312 		return 0;
313 	}
314 	p &= x;
315 
316 	return setpalette(p, r, g, b);
317 }
318 
319 int
cursoron(int dolock)320 cursoron(int dolock)
321 {
322 	VGAscr *scr;
323 	int v;
324 
325 	scr = &vgascreen[0];
326 	if(scr->cur == nil || scr->cur->move == nil)
327 		return 0;
328 
329 	if(dolock)
330 		lock(&cursor);
331 	v = scr->cur->move(scr, mousexy());
332 	if(dolock)
333 		unlock(&cursor);
334 
335 	return v;
336 }
337 
338 void
cursoroff(int)339 cursoroff(int)
340 {
341 }
342 
343 void
setcursor(Cursor * curs)344 setcursor(Cursor* curs)
345 {
346 	VGAscr *scr;
347 
348 	scr = &vgascreen[0];
349 	if(scr->cur == nil || scr->cur->load == nil)
350 		return;
351 
352 	scr->cur->load(scr, curs);
353 }
354 
355 int hwaccel = 1;
356 int hwblank = 0;	/* turned on by drivers that are known good */
357 int panning = 0;
358 
359 int
hwdraw(Memdrawparam * par)360 hwdraw(Memdrawparam *par)
361 {
362 	VGAscr *scr;
363 	Memimage *dst, *src, *mask;
364 	int m;
365 
366 	if(hwaccel == 0)
367 		return 0;
368 
369 	scr = &vgascreen[0];
370 	if((dst=par->dst) == nil || dst->data == nil)
371 		return 0;
372 	if((src=par->src) == nil || src->data == nil)
373 		return 0;
374 	if((mask=par->mask) == nil || mask->data == nil)
375 		return 0;
376 
377 	if(scr->cur == &swcursor){
378 		/*
379 		 * always calling swcursorhide here doesn't cure
380 		 * leaving cursor tracks nor failing to refresh menus
381 		 * with the latest libmemdraw/draw.c.
382 		 */
383 		if(dst->data->bdata == gscreendata.bdata)
384 			swcursoravoid(par->r);
385 		if(src->data->bdata == gscreendata.bdata)
386 			swcursoravoid(par->sr);
387 		if(mask->data->bdata == gscreendata.bdata)
388 			swcursoravoid(par->mr);
389 	}
390 
391 	if(dst->data->bdata != gscreendata.bdata)
392 		return 0;
393 
394 	if(scr->fill==nil && scr->scroll==nil)
395 		return 0;
396 
397 	/*
398 	 * If we have an opaque mask and source is one opaque
399 	 * pixel we can convert to the destination format and just
400 	 * replicate with memset.
401 	 */
402 	m = Simplesrc|Simplemask|Fullmask;
403 	if(scr->fill
404 	&& (par->state&m)==m
405 	&& ((par->srgba&0xFF) == 0xFF)
406 	&& (par->op&S) == S)
407 		return scr->fill(scr, par->r, par->sdval);
408 
409 	/*
410 	 * If no source alpha, an opaque mask, we can just copy the
411 	 * source onto the destination.  If the channels are the same and
412 	 * the source is not replicated, memmove suffices.
413 	 */
414 	m = Simplemask|Fullmask;
415 	if(scr->scroll
416 	&& src->data->bdata==dst->data->bdata
417 	&& !(src->flags&Falpha)
418 	&& (par->state&m)==m
419 	&& (par->op&S) == S)
420 		return scr->scroll(scr, par->r, par->sr);
421 
422 	return 0;
423 }
424 
425 void
blankscreen(int blank)426 blankscreen(int blank)
427 {
428 	VGAscr *scr;
429 
430 	scr = &vgascreen[0];
431 	if(hwblank){
432 		if(scr->blank)
433 			scr->blank(scr, blank);
434 		else
435 			vgablank(scr, blank);
436 	}
437 }
438 
439 void
vgalinearpciid(VGAscr * scr,int vid,int did)440 vgalinearpciid(VGAscr *scr, int vid, int did)
441 {
442 	Pcidev *p;
443 
444 	p = nil;
445 	while((p = pcimatch(p, vid, 0)) != nil){
446 		if(p->ccrb != 3)	/* video card */
447 			continue;
448 		if(did != 0 && p->did != did)
449 			continue;
450 		break;
451 	}
452 	if(p == nil)
453 		error("pci video card not found");
454 
455 	scr->pci = p;
456 	vgalinearpci(scr);
457 }
458 
459 void
vgalinearpci(VGAscr * scr)460 vgalinearpci(VGAscr *scr)
461 {
462 	ulong paddr;
463 	int i, size, best;
464 	Pcidev *p;
465 
466 	p = scr->pci;
467 	if(p == nil)
468 		return;
469 
470 	/*
471 	 * Scan for largest memory region on card.
472 	 * Some S3 cards (e.g. Savage) have enormous
473 	 * mmio regions (but even larger frame buffers).
474 	 * Some 3dfx cards (e.g., Voodoo3) have mmio
475 	 * buffers the same size as the frame buffer,
476 	 * but only the frame buffer is marked as
477 	 * prefetchable (bar&8).  If a card doesn't fit
478 	 * into these heuristics, its driver will have to
479 	 * call vgalinearaddr directly.
480 	 */
481 	best = -1;
482 	for(i=0; i<nelem(p->mem); i++){
483 		if(p->mem[i].bar&1)	/* not memory */
484 			continue;
485 		if(p->mem[i].size < 640*480)	/* not big enough */
486 			continue;
487 		if(best==-1
488 		|| p->mem[i].size > p->mem[best].size
489 		|| (p->mem[i].size == p->mem[best].size
490 		  && (p->mem[i].bar&8)
491 		  && !(p->mem[best].bar&8)))
492 			best = i;
493 	}
494 	if(best >= 0){
495 		paddr = p->mem[best].bar & ~0x0F;
496 		size = p->mem[best].size;
497 		vgalinearaddr(scr, paddr, size);
498 		return;
499 	}
500 	error("no video memory found on pci card");
501 }
502 
503 void
vgalinearaddr(VGAscr * scr,ulong paddr,int size)504 vgalinearaddr(VGAscr *scr, ulong paddr, int size)
505 {
506 	int x, nsize;
507 	ulong npaddr;
508 
509 	/*
510 	 * new approach.  instead of trying to resize this
511 	 * later, let's assume that we can just allocate the
512 	 * entire window to start with.
513 	 */
514 
515 	if(scr->paddr == paddr && size <= scr->apsize)
516 		return;
517 
518 	if(scr->paddr){
519 		/*
520 		 * could call vunmap and vmap,
521 		 * but worried about dangling pointers in devdraw
522 		 */
523 		error("cannot grow vga frame buffer");
524 	}
525 
526 	/* round to page boundary, just in case */
527 	x = paddr&(4*KiB-1);
528 	npaddr = paddr-x;
529 	nsize = ROUNDUP(size+x, 4*KiB);
530 
531 	/*
532 	 * Don't bother trying to map more than 4000x4000x32 = 64MB.
533 	 * We only have a 256MB window.
534 	 */
535 	if(nsize > 64*MiB)
536 		nsize = 64*MiB;
537 	scr->vaddr = vmap(npaddr, nsize);
538 	if(scr->vaddr == 0)
539 		error("cannot allocate vga frame buffer");
540 	scr->vaddr = (char*)scr->vaddr+x;
541 	scr->paddr = paddr;
542 	scr->apsize = nsize;
543 }
544 
545 
546 /*
547  * Software cursor.
548  */
549 int	swvisible;	/* is the cursor visible? */
550 int	swenabled;	/* is the cursor supposed to be on the screen? */
551 Memimage*	swback;	/* screen under cursor */
552 Memimage*	swimg;	/* cursor image */
553 Memimage*	swmask;	/* cursor mask */
554 Memimage*	swimg1;
555 Memimage*	swmask1;
556 
557 Point	swoffset;
558 Rectangle	swrect;	/* screen rectangle in swback */
559 Point	swpt;	/* desired cursor location */
560 Point	swvispt;	/* actual cursor location */
561 int	swvers;	/* incremented each time cursor image changes */
562 int	swvisvers;	/* the version on the screen */
563 
564 /*
565  * called with drawlock locked for us, most of the time.
566  * kernel prints at inopportune times might mean we don't
567  * hold the lock, but memimagedraw is now reentrant so
568  * that should be okay: worst case we get cursor droppings.
569  */
570 void
swcursorhide(void)571 swcursorhide(void)
572 {
573 	if(swvisible == 0)
574 		return;
575 	if(swback == nil)
576 		return;
577 	swvisible = 0;
578 	memimagedraw(gscreen, swrect, swback, ZP, memopaque, ZP, S);
579 	flushmemscreen(swrect);
580 }
581 
582 void
swcursoravoid(Rectangle r)583 swcursoravoid(Rectangle r)
584 {
585 	if(swvisible && rectXrect(r, swrect))
586 		swcursorhide();
587 }
588 
589 void
swcursordraw(void)590 swcursordraw(void)
591 {
592 	if(swvisible)
593 		return;
594 	if(swenabled == 0)
595 		return;
596 	if(swback == nil || swimg1 == nil || swmask1 == nil)
597 		return;
598 	assert(!canqlock(&drawlock));
599 	swvispt = swpt;
600 	swvisvers = swvers;
601 	swrect = rectaddpt(Rect(0,0,16,16), swvispt);
602 	memimagedraw(swback, swback->r, gscreen, swpt, memopaque, ZP, S);
603 	memimagedraw(gscreen, swrect, swimg1, ZP, swmask1, ZP, SoverD);
604 	flushmemscreen(swrect);
605 	swvisible = 1;
606 }
607 
608 /*
609  * Need to lock drawlock for ourselves.
610  */
611 void
swenable(VGAscr *)612 swenable(VGAscr*)
613 {
614 	swenabled = 1;
615 	if(canqlock(&drawlock)){
616 		swcursordraw();
617 		qunlock(&drawlock);
618 	}
619 }
620 
621 void
swdisable(VGAscr *)622 swdisable(VGAscr*)
623 {
624 	swenabled = 0;
625 	if(canqlock(&drawlock)){
626 		swcursorhide();
627 		qunlock(&drawlock);
628 	}
629 }
630 
631 void
swload(VGAscr *,Cursor * curs)632 swload(VGAscr*, Cursor *curs)
633 {
634 	uchar *ip, *mp;
635 	int i, j, set, clr;
636 
637 	if(!swimg || !swmask || !swimg1 || !swmask1)
638 		return;
639 	/*
640 	 * Build cursor image and mask.
641 	 * Image is just the usual cursor image
642 	 * but mask is a transparent alpha mask.
643 	 *
644 	 * The 16x16x8 memimages do not have
645 	 * padding at the end of their scan lines.
646 	 */
647 	ip = byteaddr(swimg, ZP);
648 	mp = byteaddr(swmask, ZP);
649 	for(i=0; i<32; i++){
650 		set = curs->set[i];
651 		clr = curs->clr[i];
652 		for(j=0x80; j; j>>=1){
653 			*ip++ = set&j ? 0x00 : 0xFF;
654 			*mp++ = (clr|set)&j ? 0xFF : 0x00;
655 		}
656 	}
657 	swoffset = curs->offset;
658 	swvers++;
659 	memimagedraw(swimg1, swimg1->r, swimg, ZP, memopaque, ZP, S);
660 	memimagedraw(swmask1, swmask1->r, swmask, ZP, memopaque, ZP, S);
661 }
662 
663 int
swmove(VGAscr *,Point p)664 swmove(VGAscr*, Point p)
665 {
666 	swpt = addpt(p, swoffset);
667 	return 0;
668 }
669 
670 void
swcursorclock(void)671 swcursorclock(void)
672 {
673 	int x;
674 
675 	if(!swenabled)
676 		return;
677 	if(swvisible && eqpt(swpt, swvispt) && swvers==swvisvers)
678 		return;
679 
680 	x = splhi();
681 	if(swenabled)
682 	if(!swvisible || !eqpt(swpt, swvispt) || swvers!=swvisvers)
683 	if(canqlock(&drawlock)){
684 		swcursorhide();
685 		swcursordraw();
686 		qunlock(&drawlock);
687 	}
688 	splx(x);
689 }
690 
691 void
swcursorinit(void)692 swcursorinit(void)
693 {
694 	static int init, warned;
695 	VGAscr *scr;
696 
697 	didswcursorinit = 1;
698 	if(!init){
699 		init = 1;
700 		addclock0link(swcursorclock, 10);
701 	}
702 	scr = &vgascreen[0];
703 	if(scr==nil || scr->gscreen==nil)
704 		return;
705 
706 	if(scr->dev == nil || scr->dev->linear == nil){
707 		if(!warned){
708 			print("cannot use software cursor on non-linear vga screen\n");
709 			warned = 1;
710 		}
711 		return;
712 	}
713 
714 	if(swback){
715 		freememimage(swback);
716 		freememimage(swmask);
717 		freememimage(swmask1);
718 		freememimage(swimg);
719 		freememimage(swimg1);
720 	}
721 
722 	swback = allocmemimage(Rect(0,0,32,32), gscreen->chan);
723 	swmask = allocmemimage(Rect(0,0,16,16), GREY8);
724 	swmask1 = allocmemimage(Rect(0,0,16,16), GREY1);
725 	swimg = allocmemimage(Rect(0,0,16,16), GREY8);
726 	swimg1 = allocmemimage(Rect(0,0,16,16), GREY1);
727 	if(swback==nil || swmask==nil || swmask1==nil || swimg==nil || swimg1 == nil){
728 		print("software cursor: allocmemimage fails");
729 		return;
730 	}
731 
732 	memfillcolor(swmask, DOpaque);
733 	memfillcolor(swmask1, DOpaque);
734 	memfillcolor(swimg, DBlack);
735 	memfillcolor(swimg1, DBlack);
736 }
737 
738 VGAcur swcursor =
739 {
740 	"soft",
741 	swenable,
742 	swdisable,
743 	swload,
744 	swmove,
745 };
746 
747