diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/pc/screen.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/pc/screen.c')
-rwxr-xr-x | sys/src/9/pc/screen.c | 748 |
1 files changed, 748 insertions, 0 deletions
diff --git a/sys/src/9/pc/screen.c b/sys/src/9/pc/screen.c new file mode 100755 index 000000000..d1d4ae729 --- /dev/null +++ b/sys/src/9/pc/screen.c @@ -0,0 +1,748 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" + +#define Image IMAGE +#include <draw.h> +#include <memdraw.h> +#include <cursor.h> +#include "screen.h" + +#define RGB2K(r,g,b) ((156763*(r)+307758*(g)+59769*(b))>>19) + +Point ZP = {0, 0}; + +Rectangle physgscreenr; + +Memdata gscreendata; +Memimage *gscreen; + +VGAscr vgascreen[1]; + +Cursor arrow = { + { -1, -1 }, + { 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04, + 0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04, + 0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40, + }, + { 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0, + 0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8, + 0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8, + 0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00, + }, +}; + +int didswcursorinit; + +static void *softscreen; + +int +screensize(int x, int y, int z, ulong chan) +{ + VGAscr *scr; + void *oldsoft; + + lock(&vgascreenlock); + if(waserror()){ + unlock(&vgascreenlock); + nexterror(); + } + + memimageinit(); + scr = &vgascreen[0]; + oldsoft = softscreen; + + if(scr->paddr == 0){ + int width = (x*z)/BI2WD; + void *p; + + p = xalloc(width*BY2WD*y); + if(p == nil) + error("no memory for vga soft screen"); + gscreendata.bdata = softscreen = p; + if(scr->dev && scr->dev->page){ + scr->vaddr = KADDR(VGAMEM()); + scr->apsize = 1<<16; + } + scr->useflush = 1; + } + else{ + gscreendata.bdata = scr->vaddr; + scr->useflush = scr->dev && scr->dev->flush; + } + + scr->gscreen = nil; + if(gscreen) + freememimage(gscreen); + gscreen = allocmemimaged(Rect(0,0,x,y), chan, &gscreendata); + if(gscreen == nil) + error("no memory for vga memimage"); + vgaimageinit(chan); + + scr->palettedepth = 6; /* default */ + scr->gscreendata = &gscreendata; + scr->memdefont = getmemdefont(); + scr->gscreen = gscreen; + + physgscreenr = gscreen->r; + unlock(&vgascreenlock); + poperror(); + if(oldsoft) + xfree(oldsoft); + + memimagedraw(gscreen, gscreen->r, memblack, ZP, nil, ZP, S); + flushmemscreen(gscreen->r); + + if(didswcursorinit) + swcursorinit(); + drawcmap(); + return 0; +} + +int +screenaperture(int size, int align) +{ + VGAscr *scr; + + scr = &vgascreen[0]; + + if(scr->paddr) /* set up during enable */ + return 0; + + if(size == 0) + return 0; + + if(scr->dev && scr->dev->linear){ + scr->dev->linear(scr, size, align); + return 0; + } + + /* + * Need to allocate some physical address space. + * The driver will tell the card to use it. + */ + size = PGROUND(size); + scr->paddr = upaalloc(size, align); + if(scr->paddr == 0) + return -1; + scr->vaddr = vmap(scr->paddr, size); + if(scr->vaddr == nil) + return -1; + scr->apsize = size; + + return 0; +} + +uchar* +attachscreen(Rectangle* r, ulong* chan, int* d, int* width, int *softscreen) +{ + VGAscr *scr; + + scr = &vgascreen[0]; + if(scr->gscreen == nil || scr->gscreendata == nil) + return nil; + + *r = scr->gscreen->clipr; + *chan = scr->gscreen->chan; + *d = scr->gscreen->depth; + *width = scr->gscreen->width; + *softscreen = scr->useflush; + + return scr->gscreendata->bdata; +} + +/* + * It would be fair to say that this doesn't work for >8-bit screens. + */ +void +flushmemscreen(Rectangle r) +{ + VGAscr *scr; + uchar *sp, *disp, *sdisp, *edisp; + int y, len, incs, off, page; + + scr = &vgascreen[0]; + if(scr->dev && scr->dev->flush){ + scr->dev->flush(scr, r); + return; + } + if(scr->gscreen == nil || scr->useflush == 0) + return; + if(scr->dev == nil || scr->dev->page == nil) + return; + + if(rectclip(&r, scr->gscreen->r) == 0) + return; + + incs = scr->gscreen->width * BY2WD; + + switch(scr->gscreen->depth){ + default: + len = 0; + panic("flushmemscreen: depth\n"); + break; + case 8: + len = Dx(r); + break; + } + if(len < 1) + return; + + off = r.min.y*scr->gscreen->width*BY2WD+(r.min.x*scr->gscreen->depth)/8; + page = off/scr->apsize; + off %= scr->apsize; + disp = scr->vaddr; + sdisp = disp+off; + edisp = disp+scr->apsize; + + off = r.min.y*scr->gscreen->width*BY2WD+(r.min.x*scr->gscreen->depth)/8; + + sp = scr->gscreendata->bdata + off; + + scr->dev->page(scr, page); + for(y = r.min.y; y < r.max.y; y++) { + if(sdisp + incs < edisp) { + memmove(sdisp, sp, len); + sp += incs; + sdisp += incs; + } + else { + off = edisp - sdisp; + page++; + if(off <= len){ + if(off > 0) + memmove(sdisp, sp, off); + scr->dev->page(scr, page); + if(len - off > 0) + memmove(disp, sp+off, len - off); + } + else { + memmove(sdisp, sp, len); + scr->dev->page(scr, page); + } + sp += incs; + sdisp += incs - scr->apsize; + } + } +} + +void +getcolor(ulong p, ulong* pr, ulong* pg, ulong* pb) +{ + VGAscr *scr; + ulong x; + + scr = &vgascreen[0]; + if(scr->gscreen == nil) + return; + + switch(scr->gscreen->depth){ + default: + x = 0x0F; + break; + case 8: + x = 0xFF; + break; + } + p &= x; + + lock(&cursor); + *pr = scr->colormap[p][0]; + *pg = scr->colormap[p][1]; + *pb = scr->colormap[p][2]; + unlock(&cursor); +} + +int +setpalette(ulong p, ulong r, ulong g, ulong b) +{ + VGAscr *scr; + int d; + + scr = &vgascreen[0]; + d = scr->palettedepth; + + lock(&cursor); + scr->colormap[p][0] = r; + scr->colormap[p][1] = g; + scr->colormap[p][2] = b; + vgao(PaddrW, p); + vgao(Pdata, r>>(32-d)); + vgao(Pdata, g>>(32-d)); + vgao(Pdata, b>>(32-d)); + unlock(&cursor); + + return ~0; +} + +/* + * On some video cards (e.g. Mach64), the palette is used as the + * DAC registers for >8-bit modes. We don't want to set them when the user + * is trying to set a colormap and the card is in one of these modes. + */ +int +setcolor(ulong p, ulong r, ulong g, ulong b) +{ + VGAscr *scr; + int x; + + scr = &vgascreen[0]; + if(scr->gscreen == nil) + return 0; + + switch(scr->gscreen->depth){ + case 1: + case 2: + case 4: + x = 0x0F; + break; + case 8: + x = 0xFF; + break; + default: + return 0; + } + p &= x; + + return setpalette(p, r, g, b); +} + +int +cursoron(int dolock) +{ + VGAscr *scr; + int v; + + scr = &vgascreen[0]; + if(scr->cur == nil || scr->cur->move == nil) + return 0; + + if(dolock) + lock(&cursor); + v = scr->cur->move(scr, mousexy()); + if(dolock) + unlock(&cursor); + + return v; +} + +void +cursoroff(int) +{ +} + +void +setcursor(Cursor* curs) +{ + VGAscr *scr; + + scr = &vgascreen[0]; + if(scr->cur == nil || scr->cur->load == nil) + return; + + scr->cur->load(scr, curs); +} + +int hwaccel = 1; +int hwblank = 0; /* turned on by drivers that are known good */ +int panning = 0; + +int +hwdraw(Memdrawparam *par) +{ + VGAscr *scr; + Memimage *dst, *src, *mask; + int m; + + if(hwaccel == 0) + return 0; + + scr = &vgascreen[0]; + if((dst=par->dst) == nil || dst->data == nil) + return 0; + if((src=par->src) == nil || src->data == nil) + return 0; + if((mask=par->mask) == nil || mask->data == nil) + return 0; + + if(scr->cur == &swcursor){ + /* + * always calling swcursorhide here doesn't cure + * leaving cursor tracks nor failing to refresh menus + * with the latest libmemdraw/draw.c. + */ + if(dst->data->bdata == gscreendata.bdata) + swcursoravoid(par->r); + if(src->data->bdata == gscreendata.bdata) + swcursoravoid(par->sr); + if(mask->data->bdata == gscreendata.bdata) + swcursoravoid(par->mr); + } + + if(dst->data->bdata != gscreendata.bdata) + return 0; + + if(scr->fill==nil && scr->scroll==nil) + return 0; + + /* + * If we have an opaque mask and source is one opaque + * pixel we can convert to the destination format and just + * replicate with memset. + */ + m = Simplesrc|Simplemask|Fullmask; + if(scr->fill + && (par->state&m)==m + && ((par->srgba&0xFF) == 0xFF) + && (par->op&S) == S) + return scr->fill(scr, par->r, par->sdval); + + /* + * If no source alpha, an opaque mask, we can just copy the + * source onto the destination. If the channels are the same and + * the source is not replicated, memmove suffices. + */ + m = Simplemask|Fullmask; + if(scr->scroll + && src->data->bdata==dst->data->bdata + && !(src->flags&Falpha) + && (par->state&m)==m + && (par->op&S) == S) + return scr->scroll(scr, par->r, par->sr); + + return 0; +} + +void +blankscreen(int blank) +{ + VGAscr *scr; + + scr = &vgascreen[0]; + if(hwblank){ + if(scr->blank) + scr->blank(scr, blank); + else + vgablank(scr, blank); + } +} + +void +vgalinearpciid(VGAscr *scr, int vid, int did) +{ + Pcidev *p; + + p = nil; + while((p = pcimatch(p, vid, 0)) != nil){ + if(p->ccrb != 3) /* video card */ + continue; + if(did != 0 && p->did != did) + continue; + break; + } + if(p == nil) + error("pci video card not found"); + + scr->pci = p; + vgalinearpci(scr); +} + +void +vgalinearpci(VGAscr *scr) +{ + ulong paddr; + int i, size, best; + Pcidev *p; + + p = scr->pci; + if(p == nil) + return; + + /* + * Scan for largest memory region on card. + * Some S3 cards (e.g. Savage) have enormous + * mmio regions (but even larger frame buffers). + * Some 3dfx cards (e.g., Voodoo3) have mmio + * buffers the same size as the frame buffer, + * but only the frame buffer is marked as + * prefetchable (bar&8). If a card doesn't fit + * into these heuristics, its driver will have to + * call vgalinearaddr directly. + */ + best = -1; + for(i=0; i<nelem(p->mem); i++){ + if(p->mem[i].bar&1) /* not memory */ + continue; + if(p->mem[i].size < 640*480) /* not big enough */ + continue; + if(best==-1 + || p->mem[i].size > p->mem[best].size + || (p->mem[i].size == p->mem[best].size + && (p->mem[i].bar&8) + && !(p->mem[best].bar&8))) + best = i; + } + if(best >= 0){ + paddr = p->mem[best].bar & ~0x0F; + size = p->mem[best].size; + vgalinearaddr(scr, paddr, size); + return; + } + error("no video memory found on pci card"); +} + +void +vgalinearaddr(VGAscr *scr, ulong paddr, int size) +{ + int x, nsize; + ulong npaddr; + + /* + * new approach. instead of trying to resize this + * later, let's assume that we can just allocate the + * entire window to start with. + */ + + if(scr->paddr == paddr && size <= scr->apsize) + return; + + if(scr->paddr){ + /* + * could call vunmap and vmap, + * but worried about dangling pointers in devdraw + */ + error("cannot grow vga frame buffer"); + } + + /* round to page boundary, just in case */ + x = paddr&(BY2PG-1); + npaddr = paddr-x; + nsize = PGROUND(size+x); + + /* + * Don't bother trying to map more than 4000x4000x32 = 64MB. + * We only have a 256MB window. + */ + if(nsize > 64*MB) + nsize = 64*MB; + scr->vaddr = vmap(npaddr, nsize); + if(scr->vaddr == 0) + error("cannot allocate vga frame buffer"); + scr->vaddr = (char*)scr->vaddr+x; + scr->paddr = paddr; + scr->apsize = nsize; + /* let mtrr harmlessly fail on old CPUs, e.g., P54C */ + if(!waserror()){ + mtrr(npaddr, nsize, "wc"); + poperror(); + } +} + + +/* + * Software cursor. + */ +int swvisible; /* is the cursor visible? */ +int swenabled; /* is the cursor supposed to be on the screen? */ +Memimage* swback; /* screen under cursor */ +Memimage* swimg; /* cursor image */ +Memimage* swmask; /* cursor mask */ +Memimage* swimg1; +Memimage* swmask1; + +Point swoffset; +Rectangle swrect; /* screen rectangle in swback */ +Point swpt; /* desired cursor location */ +Point swvispt; /* actual cursor location */ +int swvers; /* incremented each time cursor image changes */ +int swvisvers; /* the version on the screen */ + +/* + * called with drawlock locked for us, most of the time. + * kernel prints at inopportune times might mean we don't + * hold the lock, but memimagedraw is now reentrant so + * that should be okay: worst case we get cursor droppings. + */ +void +swcursorhide(void) +{ + if(swvisible == 0) + return; + if(swback == nil) + return; + swvisible = 0; + memimagedraw(gscreen, swrect, swback, ZP, memopaque, ZP, S); + flushmemscreen(swrect); +} + +void +swcursoravoid(Rectangle r) +{ + if(swvisible && rectXrect(r, swrect)) + swcursorhide(); +} + +void +swcursordraw(void) +{ + if(swvisible) + return; + if(swenabled == 0) + return; + if(swback == nil || swimg1 == nil || swmask1 == nil) + return; + assert(!canqlock(&drawlock)); + swvispt = swpt; + swvisvers = swvers; + swrect = rectaddpt(Rect(0,0,16,16), swvispt); + memimagedraw(swback, swback->r, gscreen, swpt, memopaque, ZP, S); + memimagedraw(gscreen, swrect, swimg1, ZP, swmask1, ZP, SoverD); + flushmemscreen(swrect); + swvisible = 1; +} + +/* + * Need to lock drawlock for ourselves. + */ +void +swenable(VGAscr*) +{ + swenabled = 1; + if(canqlock(&drawlock)){ + swcursordraw(); + qunlock(&drawlock); + } +} + +void +swdisable(VGAscr*) +{ + swenabled = 0; + if(canqlock(&drawlock)){ + swcursorhide(); + qunlock(&drawlock); + } +} + +void +swload(VGAscr*, Cursor *curs) +{ + uchar *ip, *mp; + int i, j, set, clr; + + if(!swimg || !swmask || !swimg1 || !swmask1) + return; + /* + * Build cursor image and mask. + * Image is just the usual cursor image + * but mask is a transparent alpha mask. + * + * The 16x16x8 memimages do not have + * padding at the end of their scan lines. + */ + ip = byteaddr(swimg, ZP); + mp = byteaddr(swmask, ZP); + for(i=0; i<32; i++){ + set = curs->set[i]; + clr = curs->clr[i]; + for(j=0x80; j; j>>=1){ + *ip++ = set&j ? 0x00 : 0xFF; + *mp++ = (clr|set)&j ? 0xFF : 0x00; + } + } + swoffset = curs->offset; + swvers++; + memimagedraw(swimg1, swimg1->r, swimg, ZP, memopaque, ZP, S); + memimagedraw(swmask1, swmask1->r, swmask, ZP, memopaque, ZP, S); +} + +int +swmove(VGAscr*, Point p) +{ + swpt = addpt(p, swoffset); + return 0; +} + +void +swcursorclock(void) +{ + int x; + + if(!swenabled) + return; + if(swvisible && eqpt(swpt, swvispt) && swvers==swvisvers) + return; + + x = splhi(); + if(swenabled) + if(!swvisible || !eqpt(swpt, swvispt) || swvers!=swvisvers) + if(canqlock(&drawlock)){ + swcursorhide(); + swcursordraw(); + qunlock(&drawlock); + } + splx(x); +} + +void +swcursorinit(void) +{ + static int init, warned; + VGAscr *scr; + + didswcursorinit = 1; + if(!init){ + init = 1; + addclock0link(swcursorclock, 10); + } + scr = &vgascreen[0]; + if(scr==nil || scr->gscreen==nil) + return; + + if(scr->dev == nil || scr->dev->linear == nil){ + if(!warned){ + print("cannot use software cursor on non-linear vga screen\n"); + warned = 1; + } + return; + } + + if(swback){ + freememimage(swback); + freememimage(swmask); + freememimage(swmask1); + freememimage(swimg); + freememimage(swimg1); + } + + swback = allocmemimage(Rect(0,0,32,32), gscreen->chan); + swmask = allocmemimage(Rect(0,0,16,16), GREY8); + swmask1 = allocmemimage(Rect(0,0,16,16), GREY1); + swimg = allocmemimage(Rect(0,0,16,16), GREY8); + swimg1 = allocmemimage(Rect(0,0,16,16), GREY1); + if(swback==nil || swmask==nil || swmask1==nil || swimg==nil || swimg1 == nil){ + print("software cursor: allocmemimage fails"); + return; + } + + memfillcolor(swmask, DOpaque); + memfillcolor(swmask1, DOpaque); + memfillcolor(swimg, DBlack); + memfillcolor(swimg1, DBlack); +} + +VGAcur swcursor = +{ + "soft", + swenable, + swdisable, + swload, + swmove, +}; + |