]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/screen.c
Import sources from 2011-03-30 iso image
[plan9front.git] / sys / src / 9 / pc / screen.c
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
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
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*
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
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
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
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
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
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
336 cursoroff(int)
337 {
338 }
339
340 void
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
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
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
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
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
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
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
585 swcursoravoid(Rectangle r)
586 {
587         if(swvisible && rectXrect(r, swrect))
588                 swcursorhide();
589 }
590
591 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
614 swenable(VGAscr*)
615 {
616         swenabled = 1;
617         if(canqlock(&drawlock)){
618                 swcursordraw();
619                 qunlock(&drawlock);
620         }
621 }
622
623 void
624 swdisable(VGAscr*)
625 {
626         swenabled = 0;
627         if(canqlock(&drawlock)){
628                 swcursorhide();
629                 qunlock(&drawlock);
630         }
631 }
632
633 void
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
666 swmove(VGAscr*, Point p)
667 {
668         swpt = addpt(p, swoffset);
669         return 0;
670 }
671
672 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
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