plan9fox/sys/src/9/pc/devvga.c
cinap_lenrek 3045d63969 reduce software cursor flickering
the software cursor starts flickering and reacts bumby if a process
spends most of its time with drawlock acquired because the timer interrupt
thats supposed to redraw the cursor fails to acquire the lock at the time
the timer fires.

instead of trying to draw the cursor on the screen from a timer interrupt
30 times per second, devmouse now creates a process calling cursoron() and
cursoroff() when the cursor needs to be redrawn. this allows the swcursor
to schedule a redraw while holding the drawlock in swcursoravoid() and
cursoron()/cursoroff() are now able to wait for a qlock (drawlock) because
they get called from process context.

the overall responsiveness is also improved with this change as the cursor
redraw rate isnt limited to 30 times a second anymore.
2013-04-14 16:28:54 +02:00

551 lines
10 KiB
C

/*
* VGA controller
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"
#define Image IMAGE
#include <draw.h>
#include <memdraw.h>
#include <cursor.h>
#include "screen.h"
enum {
Qdir,
Qvgabios,
Qvgactl,
Qvgaovl,
Qvgaovlctl,
};
static Dirtab vgadir[] = {
".", { Qdir, 0, QTDIR }, 0, 0550,
"vgabios", { Qvgabios, 0 }, 0x100000, 0440,
"vgactl", { Qvgactl, 0 }, 0, 0660,
"vgaovl", { Qvgaovl, 0 }, 0, 0660,
"vgaovlctl", { Qvgaovlctl, 0 }, 0, 0660,
};
enum {
CMactualsize,
CMblank,
CMblanktime,
CMdrawinit,
CMhwaccel,
CMhwblank,
CMhwgc,
CMlinear,
CMpalettedepth,
CMpanning,
CMsize,
CMtextmode,
CMtype,
CMunblank,
CMsoftscreen,
CMpcidev,
};
static Cmdtab vgactlmsg[] = {
CMactualsize, "actualsize", 2,
CMblank, "blank", 1,
CMblanktime, "blanktime", 2,
CMdrawinit, "drawinit", 1,
CMhwaccel, "hwaccel", 2,
CMhwblank, "hwblank", 2,
CMhwgc, "hwgc", 2,
CMlinear, "linear", 0,
CMpalettedepth, "palettedepth", 2,
CMpanning, "panning", 2,
CMsize, "size", 3,
CMtextmode, "textmode", 1,
CMtype, "type", 2,
CMunblank, "unblank", 1,
CMsoftscreen, "softscreen", 2,
CMpcidev, "pcidev", 2,
};
static void
vgareset(void)
{
Pcidev *pci;
VGAscr *scr;
/* reserve the 'standard' vga registers */
if(ioalloc(0x2b0, 0x2df-0x2b0+1, 0, "vga") < 0)
panic("vga ports already allocated");
if(ioalloc(0x3c0, 0x3da-0x3c0+1, 0, "vga") < 0)
panic("vga ports already allocated");
/* find graphics card pci device */
scr = &vgascreen[0];
scr->pci = pci = nil;
while((pci = pcimatch(pci, 0, 0)) != nil){
if(pci->ccrb == Pcibcdisp){
scr->pci = pci;
break;
}
}
conf.monitor = 1;
}
static Chan*
vgaattach(char* spec)
{
if(*spec && strcmp(spec, "0"))
error(Eio);
return devattach('v', spec);
}
Walkqid*
vgawalk(Chan* c, Chan *nc, char** name, int nname)
{
return devwalk(c, nc, name, nname, vgadir, nelem(vgadir), devgen);
}
static int
vgastat(Chan* c, uchar* dp, int n)
{
return devstat(c, dp, n, vgadir, nelem(vgadir), devgen);
}
static Chan*
vgaopen(Chan* c, int omode)
{
VGAscr *scr;
static char *openctl = "openctl\n";
scr = &vgascreen[0];
if ((ulong)c->qid.path == Qvgaovlctl) {
if (scr->dev && scr->dev->ovlctl)
scr->dev->ovlctl(scr, c, openctl, strlen(openctl));
else
error(Enonexist);
}
return devopen(c, omode, vgadir, nelem(vgadir), devgen);
}
static void
vgaclose(Chan* c)
{
VGAscr *scr;
static char *closectl = "closectl\n";
scr = &vgascreen[0];
if((ulong)c->qid.path == Qvgaovlctl)
if(scr->dev && scr->dev->ovlctl){
if(waserror()){
print("ovlctl error: %s\n", up->errstr);
return;
}
scr->dev->ovlctl(scr, c, closectl, strlen(closectl));
poperror();
}
}
static void
checkport(int start, int end)
{
/* standard vga regs are OK */
if(start >= 0x2b0 && end <= 0x2df+1)
return;
if(start >= 0x3c0 && end <= 0x3da+1)
return;
if(iounused(start, end))
return;
error(Eperm);
}
static long
vgaread(Chan* c, void* a, long n, vlong off)
{
int len;
char *p, *s;
VGAscr *scr;
ulong offset = off;
char chbuf[30];
switch((ulong)c->qid.path){
case Qdir:
return devdirread(c, a, n, vgadir, nelem(vgadir), devgen);
case Qvgabios:
if(offset >= 0x100000)
return 0;
if(offset+n >= 0x100000)
n = 0x100000 - offset;
memmove(a, (uchar*)kaddr(0)+offset, n);
return n;
case Qvgactl:
scr = &vgascreen[0];
p = smalloc(READSTR);
if(waserror()){
free(p);
nexterror();
}
len = 0;
if(scr->dev)
s = scr->dev->name;
else
s = "cga";
len += snprint(p+len, READSTR-len, "type %s\n", s);
if(scr->gscreen) {
len += snprint(p+len, READSTR-len, "size %dx%dx%d %s\n",
scr->gscreen->r.max.x, scr->gscreen->r.max.y,
scr->gscreen->depth, chantostr(chbuf, scr->gscreen->chan));
if(Dx(scr->gscreen->r) != Dx(physgscreenr)
|| Dy(scr->gscreen->r) != Dy(physgscreenr))
len += snprint(p+len, READSTR-len, "actualsize %dx%d\n",
physgscreenr.max.x, physgscreenr.max.y);
}
len += snprint(p+len, READSTR-len, "blank time %lud idle %d state %s\n",
blanktime, drawidletime(), scr->isblank ? "off" : "on");
len += snprint(p+len, READSTR-len, "hwaccel %s\n", hwaccel ? "on" : "off");
len += snprint(p+len, READSTR-len, "hwblank %s\n", hwblank ? "on" : "off");
len += snprint(p+len, READSTR-len, "panning %s\n", panning ? "on" : "off");
len += snprint(p+len, READSTR-len, "addr p 0x%lux v 0x%p size 0x%ux\n", scr->paddr, scr->vaddr, scr->apsize);
len += snprint(p+len, READSTR-len, "softscreen %s\n", scr->softscreen ? "on" : "off");
USED(len);
n = readstr(offset, a, n, p);
poperror();
free(p);
return n;
case Qvgaovl:
case Qvgaovlctl:
error(Ebadusefd);
break;
default:
error(Egreg);
break;
}
return 0;
}
static char Ebusy[] = "vga already configured";
static void
vgactl(Cmdbuf *cb)
{
int align, i, size, x, y, z;
char *chanstr, *p;
ulong chan;
Cmdtab *ct;
VGAscr *scr;
extern VGAdev *vgadev[];
extern VGAcur *vgacur[];
scr = &vgascreen[0];
ct = lookupcmd(cb, vgactlmsg, nelem(vgactlmsg));
switch(ct->index){
case CMhwgc:
if(scr->gscreen == nil)
error("hwgc: no gscreen");
if(strcmp(cb->f[1], "off") == 0){
lock(&cursor);
if(scr->cur){
if(scr->cur->disable)
scr->cur->disable(scr);
scr->cur = nil;
}
unlock(&cursor);
return;
}
if(strcmp(cb->f[1], "soft") == 0){
lock(&cursor);
swcursorinit();
if(scr->cur && scr->cur->disable)
scr->cur->disable(scr);
scr->cur = &swcursor;
if(scr->cur->enable)
scr->cur->enable(scr);
unlock(&cursor);
return;
}
for(i = 0; vgacur[i]; i++){
if(strcmp(cb->f[1], vgacur[i]->name))
continue;
lock(&cursor);
if(scr->cur && scr->cur->disable)
scr->cur->disable(scr);
scr->cur = vgacur[i];
if(scr->cur->enable)
scr->cur->enable(scr);
unlock(&cursor);
return;
}
break;
case CMpcidev:
if(cb->nf == 2){
Pcidev *p;
if((p = pcimatchtbdf(strtoul(cb->f[1], 0, 16))) != nil)
scr->pci = p;
} else
error(Ebadarg);
return;
case CMtype:
for(i = 0; vgadev[i]; i++){
if(strcmp(cb->f[1], vgadev[i]->name))
continue;
if(scr->dev){
if(scr->dev->disable)
scr->dev->disable(scr);
scr->fill = nil;
scr->scroll = nil;
scr->blank = nil;
}
scr->dev = vgadev[i];
if(scr->dev->enable)
scr->dev->enable(scr);
return;
}
break;
case CMtextmode:
screeninit();
return;
case CMsize:
x = strtoul(cb->f[1], &p, 0);
if(x == 0 || x > 10240)
error(Ebadarg);
if(*p)
p++;
y = strtoul(p, &p, 0);
if(y == 0 || y > 10240)
error(Ebadarg);
if(*p)
p++;
z = strtoul(p, &p, 0);
chanstr = cb->f[2];
if((chan = strtochan(chanstr)) == 0)
error("bad channel");
if(chantodepth(chan) != z)
error("depth, channel do not match");
cursoroff();
deletescreenimage();
if(screensize(x, y, z, chan))
error(Egreg);
return;
case CMactualsize:
if(scr->gscreen == nil)
error("set the screen size first");
x = strtoul(cb->f[1], &p, 0);
if(x == 0 || x > 2048)
error(Ebadarg);
if(*p)
p++;
y = strtoul(p, nil, 0);
if(y == 0 || y > 2048)
error(Ebadarg);
if(x > scr->gscreen->r.max.x || y > scr->gscreen->r.max.y)
error("physical screen bigger than virtual");
physgscreenr = Rect(0,0,x,y);
scr->gscreen->clipr = physgscreenr;
return;
case CMpalettedepth:
x = strtoul(cb->f[1], &p, 0);
if(x != 8 && x != 6)
error(Ebadarg);
scr->palettedepth = x;
return;
case CMsoftscreen:
if(strcmp(cb->f[1], "on") == 0)
scr->softscreen = 1;
else if(strcmp(cb->f[1], "off") == 0)
scr->softscreen = 0;
else
break;
if(scr->gscreen == nil)
return;
x = scr->gscreen->r.max.x;
y = scr->gscreen->r.max.y;
z = scr->gscreen->depth;
chan = scr->gscreen->chan;
cursoroff();
deletescreenimage();
if(screensize(x, y, z, chan))
error(Egreg);
/* no break */
case CMdrawinit:
if(scr->gscreen == nil)
error("drawinit: no gscreen");
if(scr->dev && scr->dev->drawinit)
scr->dev->drawinit(scr);
hwblank = scr->blank != nil;
hwaccel = !scr->softscreen && (scr->scroll || scr->fill);
vgascreenwin(scr);
resetscreenimage();
cursoron();
return;
case CMlinear:
if(cb->nf!=2 && cb->nf!=3)
error(Ebadarg);
size = strtoul(cb->f[1], 0, 0);
if(cb->nf == 2)
align = 0;
else
align = strtoul(cb->f[2], 0, 0);
if(screenaperture(size, align) < 0)
error("not enough free address space");
return;
case CMblank:
drawblankscreen(1);
return;
case CMunblank:
drawblankscreen(0);
return;
case CMblanktime:
blanktime = strtoul(cb->f[1], 0, 0);
return;
case CMpanning:
if(strcmp(cb->f[1], "on") == 0){
if(scr == nil || scr->cur == nil)
error("set screen first");
if(!scr->cur->doespanning)
error("panning not supported");
scr->gscreen->clipr = scr->gscreen->r;
panning = 1;
}
else if(strcmp(cb->f[1], "off") == 0){
scr->gscreen->clipr = physgscreenr;
panning = 0;
}else
break;
return;
case CMhwaccel:
if(strcmp(cb->f[1], "on") == 0)
hwaccel = 1;
else if(strcmp(cb->f[1], "off") == 0)
hwaccel = 0;
else
break;
return;
case CMhwblank:
if(strcmp(cb->f[1], "on") == 0)
hwblank = 1;
else if(strcmp(cb->f[1], "off") == 0)
hwblank = 0;
else
break;
return;
}
cmderror(cb, "bad VGA control message");
}
char Enooverlay[] = "No overlay support";
static long
vgawrite(Chan* c, void* a, long n, vlong off)
{
ulong offset = off;
Cmdbuf *cb;
VGAscr *scr;
switch((ulong)c->qid.path){
case Qdir:
error(Eperm);
case Qvgactl:
if(offset || n >= READSTR)
error(Ebadarg);
cb = parsecmd(a, n);
if(waserror()){
free(cb);
nexterror();
}
vgactl(cb);
poperror();
free(cb);
return n;
case Qvgaovl:
scr = &vgascreen[0];
if (scr->dev == nil || scr->dev->ovlwrite == nil) {
error(Enooverlay);
break;
}
return scr->dev->ovlwrite(scr, a, n, off);
case Qvgaovlctl:
scr = &vgascreen[0];
if (scr->dev == nil || scr->dev->ovlctl == nil) {
error(Enooverlay);
break;
}
scr->dev->ovlctl(scr, c, a, n);
return n;
default:
error(Egreg);
break;
}
return 0;
}
Dev vgadevtab = {
'v',
"vga",
vgareset,
devinit,
devshutdown,
vgaattach,
vgawalk,
vgastat,
vgaopen,
devcreate,
vgaclose,
vgaread,
devbread,
vgawrite,
devbwrite,
devremove,
devwstat,
};