/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #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 #include #include #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, uint32_t chan) { Proc *up = externup(); 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 = malloc(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) free(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 = ROUNDUP(sizeof(size), 4*KiB); scr->paddr = (uint64_t)malloc(size); if(scr->paddr == 0) return -1; scr->vaddr = vmap(scr->paddr, size); if(scr->vaddr == nil) return -1; scr->apsize = size; return 0; } unsigned char* attachscreen(Rectangle* r, uint32_t* 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; unsigned char *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(uint32_t p, uint32_t* pr, uint32_t* pg, uint32_t* pb) { VGAscr *scr; uint32_t 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.l); *pr = scr->colormap[p][0]; *pg = scr->colormap[p][1]; *pb = scr->colormap[p][2]; unlock(&cursor.l); } int setpalette(uint32_t p, uint32_t r, uint32_t g, uint32_t b) { VGAscr *scr; int d; scr = &vgascreen[0]; d = scr->palettedepth; lock(&cursor.l); 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.l); 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(uint32_t p, uint32_t r, uint32_t g, uint32_t 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.l); v = scr->cur->move(scr, mousexy()); if(dolock) unlock(&cursor.l); return v; } void cursoroff(int i) { } 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) { uint32_t 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; imem); 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, uint32_t paddr, int size) { int x, nsize; uint32_t 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&(4*KiB-1); npaddr = paddr-x; nsize = ROUNDUP(size+x, 4*KiB); /* * 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; } /* * 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 *v) { swenabled = 1; if(canqlock(&drawlock)){ swcursordraw(); qunlock(&drawlock); } } void swdisable(VGAscr *v) { swenabled = 0; if(canqlock(&drawlock)){ swcursorhide(); qunlock(&drawlock); } } void swload(VGAscr *v, Cursor *curs) { unsigned char *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 *v, 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; 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); if(!init){ init = 1; addclock0link(swcursorclock, 10); } } VGAcur swcursor = { "soft", swenable, swdisable, swload, swmove, }; // A bit hokey but it saves dumbness in the build tool and other code. VGAcur vgavesacur = { "vesa", swenable, swdisable, swload, swmove, };