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