--- /sys/src/9k/386/uarti8250.c +++ /sys/src/9k/386/uarti8250.c @@ -801,3 +801,27 @@ i8250console(char* cfg) return uart; } + +void +i8250mouse(char* which, int (*putc)(Queue*, int), int setb1200) +{ + char *p; + int port; + + port = strtol(which, &p, 0); + if(p == which || port < 0 || port > 1) + error(Ebadarg); + uartmouse(&i8250uart[port], putc, setb1200); +} + +void +i8250setmouseputc(char* which, int (*putc)(Queue*, int)) +{ + char *p; + int port; + + port = strtol(which, &p, 0); + if(p == which || port < 0 || port > 1) + error(Ebadarg); + uartsetmouseputc(&i8250uart[port], putc); +} --- /dev/null +++ /sys/src/9k/386/vgavesa.c @@ -0,0 +1,231 @@ +/* + * vga driver using just vesa bios to set up. + * + * note that setting hwaccel to zero will cause cursor ghosts to be + * left behind. hwaccel set non-zero repairs this. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "/386/include/ureg.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" + +#define RMBUF ((void*)(KZERO+0x9000)) /* buffer for user space - known to vga */ +#define BI2WD 32 /* bits per word */ +#define BY2WD 4 /* bytes per word */ +#define ROUND(s, sz) (((s)+(sz-1))&~(sz-1)) + +enum { + Usesoftscreen = 1, +}; + +static void *hardscreen; +static uchar modebuf[0x1000]; + +#define WORD(p) ((p)[0] | ((p)[1]<<8)) +#define LONG(p) ((p)[0] | ((p)[1]<<8) | ((p)[2]<<16) | ((p)[3]<<24)) +#define PWORD(p, v) (p)[0] = (v); (p)[1] = (v)>>8 +#define PLONG(p, v) (p)[0] = (v); (p)[1] = (v)>>8; (p)[2] = (v)>>16; (p)[3] = (v)>>24 + +static uchar* +vbesetup(Ureg *u, int ax) +{ + ulong pa; + + pa = PADDR(RMBUF); + memset(modebuf, 0, sizeof modebuf); + memset(u, 0, sizeof *u); + u->ax = ax; + u->es = (pa>>4)&0xF000; + u->di = pa&0xFFFF; + return modebuf; +} + +static void +vbecall(Ureg *u) +{ + Chan *creg, *cmem; + ulong pa; + + cmem = namec("/dev/realmodemem", Aopen, ORDWR, 0); + if(waserror()){ + cclose(cmem); + nexterror(); + } + creg = namec("/dev/realmode", Aopen, ORDWR, 0); + if(waserror()){ + cclose(creg); + nexterror(); + } + pa = PADDR(RMBUF); + cmem->dev->write(cmem, modebuf, sizeof modebuf, pa); + u->trap = 0x10; + creg->dev->write(creg, u, sizeof *u, 0); + + creg->dev->read(creg, u, sizeof *u, 0); + if((u->ax&0xFFFF) != 0x004F) + error("vesa bios error"); + cmem->dev->read(cmem, modebuf, sizeof modebuf, pa); + + poperror(); + cclose(creg); + poperror(); + cclose(cmem); +} + +static void +vbecheck(void) +{ + Ureg u; + uchar *p; + + p = vbesetup(&u, 0x4F00); + strcpy((char*)p, "VBE2"); + vbecall(&u); + if(memcmp((char*)p, "VESA", 4) != 0) + error("bad vesa signature"); + if(p[5] < 2) + error("bad vesa version"); +} + +static int +vbegetmode(void) +{ + Ureg u; + + vbesetup(&u, 0x4F03); + vbecall(&u); + return u.bx; +} + +static uchar* +vbemodeinfo(int mode) +{ + uchar *p; + Ureg u; + + p = vbesetup(&u, 0x4F01); + u.cx = mode; + vbecall(&u); + return p; +} + +static void +vesalinear(VGAscr *scr, int, int) +{ + int i, mode, size, havesize; + uchar *p; + ulong paddr; + Pcidev *pci; + + if(hardscreen) { + scr->vaddr = 0; + scr->paddr = scr->apsize = 0; + return; + } + + vbecheck(); + mode = vbegetmode(); + /* + * bochs loses the top bits - cannot use this + if((mode&(1<<14)) == 0) + error("not in linear graphics mode"); + */ + mode &= 0x3FFF; + p = vbemodeinfo(mode); + if(!(WORD(p+0) & (1<<4))) + error("not in VESA graphics mode"); + if(!(WORD(p+0) & (1<<7))) + error("not in linear graphics mode"); + + paddr = LONG(p+40); + size = WORD(p+20)*WORD(p+16); + size = ROUNDUP(size, PGSZ); + + /* + * figure out max size of memory so that we have + * enough if the screen is resized. + */ + pci = nil; + havesize = 0; + while(!havesize && (pci = pcimatch(pci, 0, 0)) != nil){ + if(pci->ccrb != Pcibcdisp) + continue; + for(i=0; imem); i++) + if(paddr == (pci->mem[i].bar&~0x0F)){ + if(pci->mem[i].size > size) + size = pci->mem[i].size; + havesize = 1; + break; + } + } + + /* no pci - heuristic guess */ + if (!havesize) + if(size < 4*1024*1024) + size = 4*1024*1024; + else + size = ROUND(size, 1024*1024); + if(size > 16*1024*1024) /* arbitrary */ + size = 16*1024*1024; + + vgalinearaddr(scr, paddr, size); + if(scr->apsize) + addvgaseg("vesascreen", scr->paddr, scr->apsize); + + if(Usesoftscreen){ + hardscreen = scr->vaddr; + scr->vaddr = 0; + scr->paddr = scr->apsize = 0; + } +} + +static void +vesaflush(VGAscr *scr, Rectangle r) +{ + int t, w, wid, off; + ulong *hp, *sp, *esp; + + if(hardscreen == nil) + return; + if(rectclip(&r, scr->gscreen->r) == 0) + return; + sp = (ulong*)(scr->gscreendata->bdata + scr->gscreen->zero); + t = (r.max.x * scr->gscreen->depth + 2*BI2WD-1) / BI2WD; + w = (r.min.x * scr->gscreen->depth) / BI2WD; + w = (t - w) * BY2WD; + wid = scr->gscreen->width; + off = r.min.y * wid + (r.min.x * scr->gscreen->depth) / BI2WD; + + hp = hardscreen; + hp += off; + sp += off; + esp = sp + Dy(r) * wid; + while(sp < esp){ + memmove(hp, sp, w); + hp += wid; + sp += wid; + } +} + +VGAdev vgavesadev = { + "vesa", + 0, + 0, + 0, + vesalinear, + 0, + 0, + 0, + 0, + vesaflush, +}; --- /dev/null +++ /sys/src/9k/k10/devvga.c @@ -0,0 +1,483 @@ +/* + * 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 +#include +#include +#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, +}; + +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, +}; + +static void +vgareset(void) +{ + /* 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"); +} + +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 long +vgastat(Chan* c, uchar* dp, long 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 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 = malloc(READSTR); + if(p == nil) + error(Enomem); + 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); + 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 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(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 CMtype: + for(i = 0; vgadev[i]; i++){ + if(strcmp(cb->f[1], vgadev[i]->name)) + continue; + if(scr->dev && scr->dev->disable) + scr->dev->disable(scr); + scr->dev = vgadev[i]; + if(scr->dev->enable) + scr->dev->enable(scr); + return; + } + break; + + case CMtextmode: + cgainit(); + 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(1); + deletescreenimage(); + if(screensize(x, y, z, chan)) + error(Egreg); + vgascreenwin(scr); + resetscreenimage(); + cursoron(1); + 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 CMdrawinit: + if(scr->gscreen == nil) + error("drawinit: no gscreen"); + if(scr->dev && scr->dev->drawinit) + scr->dev->drawinit(scr); + 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 CMmemset: + memset((void*)strtoul(cb->f[1], 0, 0), atoi(cb->f[2]), atoi(cb->f[3])); + 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, +}; --- /sys/src/9k/k10/fns.h +++ /sys/src/9k/k10/fns.h @@ -41,6 +41,8 @@ void i8042auxenable(void (*)(int, int)); void i8042reset(void); Uart* i8250console(char*); void* i8250alloc(int, int, int); +void i8250mouse(char*, int (*)(Queue*, int), int); +void i8250setmouseputc(char*, int (*)(Queue*, int)); vlong i8254hz(u32int[2][4]); void idlehands(void); void idthandlers(void); --- /dev/null +++ /sys/src/9k/k10/k10f @@ -0,0 +1,200 @@ +dev +dev + root + cons + arch + env + pipe + proc + mnt + srv + dup + rtc + ssl + tls + cap + kprof + acpi + + ether netif + ip arp chandial ip ipv6 ipaux iproute netlog nullmedium pktmedium ptclbsum + + uart + + sd + + draw drawalloc screen vga vgax + mouse mouse + vga + +sd +dev + sdata pci sdscsi + sdiahci pci sdscsi + sdvirtio pci sdscsi + +uart +dev + uarti8250 + uartpci pci + +vga +dev + vgavesa + +ip +dev + tcp + udp + ipifc + icmp + icmp6 + +link +dev + ether8139 pci + ether8169 pci ethermii + ether82557 pci + ether82563 pci + ether82598 pci + etherigbe pci ethermii +# etherm10g pci + ethervirtio pci + ethermedium + loopbackmedium + netdevmedium + +# acpi hpet +# ht + + realmode + +misc +dev + cache + mp apic ioapic pci sipi mpacpi + +# +#boot cpu +# int cpuflag = 1; +#boot cpu boot $3 +# int cpuflag = 1; +# char* bootdisk = "$3"; +#boot rootdir $3 +# char* rootdir = "$3"; +#boot (bboot|romboot|dosboot) +# int cpuflag = 1; +# char* bootprog = $2; +#boot boot $3 +# char* bootdisk = "$3"; +# +boot + tcp + local + +rootdir + bootk10f.out boot + /amd64/bin/auth/factotum factotum + /amd64/bin/ip/ipconfig ipconfig + /amd64/bin/fossil/fossil fossil + /amd64/bin/venti/venti venti + ../root/nvram nvram + +conf + int cpuserver = 0; + +# +#dbgflg +# chan 'c' +# apic 'A' +# acpi 'C' +# hpet 'H' +# ht 'H' +# ioapic 'I' +# mp 'M' +# pci 'P' +# arch 'V' +# +dbgflg + apic 'A' + devacpi 'C' + hpet 'H' + ht 'H' + ioapic 'I' + mp 'M' + arch 'V' + archk10 'V' + sysproc 'E' + main 'x' + mmu 'm' + asm 'a' + sipi 'y' + +amd64 +dev + l32p + l64v + l64idt + l64syscall + l64fpu + l64cpuid + arch + archk10 + asm + cga + crap + fpu + i8254 + i8259 + kbd + main + map + memory + mmu + multiboot + qmalloc + random + syscall + syscallfmt + trap + vsvm + +port + alarm + allocb + chan + dev + devtab + edf + fault + image + latin1 + page + parse + pgrp + portclock + print + proc + ps + qio + qlock + rebootcmd + segment + sysauth + sysfile + sysproc + sysseg + systab + taslock + tod + watermarks + +# +#dir +# pc -.I. +# +dir + 386 + ip + port + +lib + libmemlayer + libmemdraw + libdraw + libip + libsec + libmp + libc --- /dev/null +++ /sys/src/9k/k10/mouse.c @@ -0,0 +1,343 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" + +/* + * mouse types + */ +enum +{ + Mouseother= 0, + Mouseserial= 1, + MousePS2= 2, +}; + +extern int mouseshifted; + +static QLock mousectlqlock; +static int mousetype; +static int intellimouse; +static int packetsize; +static int resolution; +static int accelerated; +static int mousehwaccel; +static char mouseport[5]; + +enum +{ + CMaccelerated, + CMhwaccel, + CMintellimouse, + CMlinear, + CMps2, + CMps2intellimouse, + CMres, + CMreset, + CMserial, +}; + +static Cmdtab mousectlmsg[] = +{ + CMaccelerated, "accelerated", 0, + CMhwaccel, "hwaccel", 2, + CMintellimouse, "intellimouse", 1, + CMlinear, "linear", 1, + CMps2, "ps2", 1, + CMps2intellimouse, "ps2intellimouse", 1, + CMres, "res", 0, + CMreset, "reset", 1, + CMserial, "serial", 0, +}; + +/* + * ps/2 mouse message is three bytes + * + * byte 0 - 0 0 SDY SDX 1 M R L + * byte 1 - DX + * byte 2 - DY + * + * shift & right button is the same as middle button + * + * Intellimouse and AccuPoint with extra buttons deliver + * byte 3 - 00 or 01 or FF according to extra button state. + * extra buttons are mapped in this code to buttons 4 and 5. + * AccuPoint generates repeated events for these buttons; +* it and Intellimouse generate 'down' events only, so + * user-level code is required to generate button 'up' events + * if they are needed by the application. + * Also on laptops with AccuPoint AND external mouse, the + * controller may deliver 3 or 4 bytes according to the type + * of the external mouse; code must adapt. + * + * On the NEC Versa series (and perhaps others?) we seem to + * lose a byte from the packet every once in a while, which + * means we lose where we are in the instruction stream. + * To resynchronize, if we get a byte more than two seconds + * after the previous byte, we assume it's the first in a packet. + */ +static void +ps2mouseputc(int c, int shift) +{ + static short msg[4]; + static int nb; + static uchar b[] = {0, 1, 4, 5, 2, 3, 6, 7, 0, 1, 2, 3, 2, 3, 6, 7 }; + static ulong lasttick; + ulong m; + int buttons, dx, dy; + + /* + * non-ps2 keyboards might not set shift + * but still set mouseshifted. + */ + shift |= mouseshifted; + /* + * Resynchronize in stream with timing; see comment above. + */ + m = sys->ticks; + if(TK2SEC(m - lasttick) > 2) + nb = 0; + lasttick = m; + + /* + * check byte 0 for consistency + */ + if(nb==0 && (c&0xc8)!=0x08) + if(intellimouse && (c==0x00 || c==0x01 || c==0xFF)){ + /* last byte of 4-byte packet */ + packetsize = 4; + return; + } + + msg[nb] = c; + if(++nb == packetsize){ + nb = 0; + if(msg[0] & 0x10) + msg[1] |= 0xFF00; + if(msg[0] & 0x20) + msg[2] |= 0xFF00; + + buttons = b[(msg[0]&7) | (shift ? 8 : 0)]; + if(intellimouse && packetsize==4){ + if((msg[3]&0xc8) == 0x08){ + /* first byte of 3-byte packet */ + packetsize = 3; + msg[0] = msg[3]; + nb = 1; + /* fall through to emit previous packet */ + }else{ + /* The AccuPoint on the Toshiba 34[48]0CT + * encodes extra buttons as 4 and 5. They repeat + * and don't release, however, so user-level + * timing code is required. Furthermore, + * intellimice with 3buttons + scroll give a + * two's complement number in the lower 4 bits + * (bit 4 is sign extension) that describes + * the amount the scroll wheel has moved during + * the last sample. Here we use only the sign to + * decide whether the wheel is moving up or down + * and generate a single button 4 or 5 click + * accordingly. + */ + if((msg[3] >> 3) & 1) + buttons |= 1<<3; + else if(msg[3] & 0x7) + buttons |= 1<<4; + } + } + dx = msg[1]; + dy = -msg[2]; + mousetrack(dx, dy, buttons, TK2MS(sys->ticks)); + } + return; +} + +/* + * set up a ps2 mouse + */ +static void +ps2mouse(void) +{ + if(mousetype == MousePS2) + return; + + i8042auxenable(ps2mouseputc); + /* make mouse streaming, enabled */ + i8042auxcmd(0xEA); + i8042auxcmd(0xF4); + + mousetype = MousePS2; + packetsize = 3; + mousehwaccel = 1; +} + +/* + * The PS/2 Trackpoint multiplexor on the IBM Thinkpad T23 ignores + * acceleration commands. It is supposed to pass them on + * to the attached device, but my Logitech mouse is simply + * not behaving any differently. For such devices, we allow + * the user to use "hwaccel off" to tell us to back off to + * software acceleration even if we're using the PS/2 port. + * (Serial mice are always software accelerated.) + * For more information on the Thinkpad multiplexor, see + * http://wwwcssrv.almaden.ibm.com/trackpoint/ + */ +static void +setaccelerated(int x) +{ + accelerated = x; + if(mousehwaccel){ + switch(mousetype){ + case MousePS2: + i8042auxcmd(0xE7); + return; + } + } + mouseaccelerate(x); +} + +static void +setlinear(void) +{ + accelerated = 0; + if(mousehwaccel){ + switch(mousetype){ + case MousePS2: + i8042auxcmd(0xE6); + return; + } + } + mouseaccelerate(0); +} + +static void +setres(int n) +{ + resolution = n; + switch(mousetype){ + case MousePS2: + i8042auxcmd(0xE8); + i8042auxcmd(n); + break; + } +} + +static void +setintellimouse(void) +{ + intellimouse = 1; + packetsize = 4; + switch(mousetype){ + case MousePS2: + i8042auxcmd(0xF3); /* set sample */ + i8042auxcmd(0xC8); + i8042auxcmd(0xF3); /* set sample */ + i8042auxcmd(0x64); + i8042auxcmd(0xF3); /* set sample */ + i8042auxcmd(0x50); + break; + case Mouseserial: + i8250setmouseputc(mouseport, m5mouseputc); + break; + } +} + +static void +resetmouse(void) +{ + packetsize = 3; + switch(mousetype){ + case MousePS2: + i8042auxcmd(0xF6); + i8042auxcmd(0xEA); /* streaming */ + i8042auxcmd(0xE8); /* set resolution */ + i8042auxcmd(3); + i8042auxcmd(0xF4); /* enabled */ + break; + } +} + +void +mousectl(Cmdbuf *cb) +{ + Cmdtab *ct; + + qlock(&mousectlqlock); + if(waserror()){ + qunlock(&mousectlqlock); + nexterror(); + } + + ct = lookupcmd(cb, mousectlmsg, nelem(mousectlmsg)); + switch(ct->index){ + case CMaccelerated: + setaccelerated(cb->nf == 1 ? 1 : atoi(cb->f[1])); + break; + case CMintellimouse: + setintellimouse(); + break; + case CMlinear: + setlinear(); + break; + case CMps2: + intellimouse = 0; + ps2mouse(); + break; + case CMps2intellimouse: + ps2mouse(); + setintellimouse(); + break; + case CMres: + if(cb->nf >= 2) + setres(atoi(cb->f[1])); + else + setres(1); + break; + case CMreset: + resetmouse(); + if(accelerated) + setaccelerated(accelerated); + if(resolution) + setres(resolution); + if(intellimouse) + setintellimouse(); + break; + case CMserial: + if(mousetype == Mouseserial) + error(Emouseset); + + if(cb->nf > 2){ + if(strcmp(cb->f[2], "M") == 0) + i8250mouse(cb->f[1], m3mouseputc, 0); + else if(strcmp(cb->f[2], "MI") == 0) + i8250mouse(cb->f[1], m5mouseputc, 0); + else + i8250mouse(cb->f[1], mouseputc, cb->nf == 1); + } else + i8250mouse(cb->f[1], mouseputc, cb->nf == 1); + + mousetype = Mouseserial; + strncpy(mouseport, cb->f[1], sizeof(mouseport)-1); + packetsize = 3; + break; + case CMhwaccel: + if(strcmp(cb->f[1], "on")==0) + mousehwaccel = 1; + else if(strcmp(cb->f[1], "off")==0) + mousehwaccel = 0; + else + cmderror(cb, "bad mouse control message"); + } + + qunlock(&mousectlqlock); + poperror(); +} --- /dev/null +++ /sys/src/9k/k10/realmode.c @@ -0,0 +1,51 @@ +#include "u.h" +#include "tos.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 LORMBUF (0x9000) + +static long +rmemrw(int isr, void *a, long n, vlong off) +{ + if(off < 0 || n < 0) + error("bad offset/count"); + if(isr){ + if(off >= MiB) + return 0; + if(off+n >= MiB) + n = MiB - off; + memmove(a, KADDR((uintptr)off), n); + }else{ + /* realmode buf page ok, allow vga framebuf's access */ + if(off >= MiB || off+n > MiB || + (off < LORMBUF || off+n > LORMBUF+4*KiB) && + (off < 0xA0000 || off+n > 0xB0000+0x10000)) + error("bad offset/count in write"); + memmove(KADDR((uintptr)off), a, n); + } + return n; +} + +static long +rmemread(Chan*, void *a, long n, vlong off) +{ + return rmemrw(1, a, n, off); +} + +static long +rmemwrite(Chan*, void *a, long n, vlong off) +{ + return rmemrw(0, a, n, off); +} + +void +realmodelink(void) +{ + addarchfile("realmodemem", 0660, rmemread, rmemwrite); +} --- /sys/src/9k/k10/root/cpu.c +++ /sys/src/9k/k10/root/cpu.c @@ -173,7 +173,7 @@ main(int argc, char **argv) /* start up a process to pass along notes */ lclnoteproc(data); - /* + /* * Wait for the other end to execute and start our file service * of /mnt/term */ @@ -264,7 +264,7 @@ old9p(int fd) close(fd); close(p[0]); } - return p[1]; + return p[1]; } /* Invoked with stdin, stdout and stderr connected to the network connection */ @@ -412,7 +412,7 @@ readstr(int fd, char *str, int len) while(len) { n = read(fd, str, 1); - if(n < 0) + if(n < 0) return -1; if(*str == '\0') return 0; @@ -582,7 +582,7 @@ chuid(char* to) werrstr("open #ยค/caphash: %r"); return -1; } - + cap = smprint("%s@Why can't we all just get along?", to); p = strrchr(cap, '@'); hmac_sha1((uchar*)cap, p-cap, (uchar*)p+1, strlen(p+1), hash, nil); @@ -759,7 +759,7 @@ rmtnoteproc(void) syslog(0, "cpu", "cpu -R: can't open %s", rmtnotefile); _exits(0); } - + for(;;){ n = read(fd, buf, sizeof(buf)-1); if(n <= 0){ --- /dev/null +++ /sys/src/9k/k10/screen.c @@ -0,0 +1,746 @@ +#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 BI2WD 32 /* bits per word */ +#define BY2WD 4 /* bytes per word */ + +#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 = 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(size, 4*KiB); + scr->paddr = (uintptr)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; +} + +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"); + 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; 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, 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&(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*MiB) + nsize = 64*MiB; + 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*) +{ + 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, +}; + --- /dev/null +++ /sys/src/9k/k10/screen.h @@ -0,0 +1,177 @@ +typedef struct Cursor Cursor; +typedef struct Cursorinfo Cursorinfo; +struct Cursorinfo { + Cursor; + Lock; +}; + +/* devmouse.c */ +extern void mousetrack(int, int, int, int); +extern Point mousexy(void); + +extern void mouseaccelerate(int); +extern int m3mouseputc(Queue*, int); +extern int m5mouseputc(Queue*, int); +extern int mouseputc(Queue*, int); + +extern Cursorinfo cursor; +extern Cursor arrow; + +/* + * Generic VGA registers. + */ +enum { + MiscW = 0x03C2, /* Miscellaneous Output (W) */ + MiscR = 0x03CC, /* Miscellaneous Output (R) */ + Status0 = 0x03C2, /* Input status 0 (R) */ + Status1 = 0x03DA, /* Input Status 1 (R) */ + FeatureR = 0x03CA, /* Feature Control (R) */ + FeatureW = 0x03DA, /* Feature Control (W) */ + + Seqx = 0x03C4, /* Sequencer Index, Data at Seqx+1 */ + Crtx = 0x03D4, /* CRT Controller Index, Data at Crtx+1 */ + Grx = 0x03CE, /* Graphics Controller Index, Data at Grx+1 */ + Attrx = 0x03C0, /* Attribute Controller Index and Data */ + + PaddrW = 0x03C8, /* Palette Address Register, write */ + Pdata = 0x03C9, /* Palette Data Register */ + Pixmask = 0x03C6, /* Pixel Mask Register */ + PaddrR = 0x03C7, /* Palette Address Register, read */ + Pstatus = 0x03C7, /* DAC Status (RO) */ + + Pcolours = 256, /* Palette */ + Pred = 0, + Pgreen = 1, + Pblue = 2, + + Pblack = 0x00, + Pwhite = 0xFF, +}; + +#define VGAMEM() 0xA0000 +#define vgai(port) inb(port) +#define vgao(port, data) outb(port, data) + +extern int vgaxi(long, uchar); +extern int vgaxo(long, uchar, uchar); + +/* + */ +typedef struct VGAdev VGAdev; +typedef struct VGAcur VGAcur; +typedef struct VGAscr VGAscr; + +struct VGAdev { + char* name; + + void (*enable)(VGAscr*); + void (*disable)(VGAscr*); + void (*page)(VGAscr*, int); + void (*linear)(VGAscr*, int, int); + void (*drawinit)(VGAscr*); + int (*fill)(VGAscr*, Rectangle, ulong); + void (*ovlctl)(VGAscr*, Chan*, void*, int); + int (*ovlwrite)(VGAscr*, void*, int, vlong); + void (*flush)(VGAscr*, Rectangle); +}; + +struct VGAcur { + char* name; + + void (*enable)(VGAscr*); + void (*disable)(VGAscr*); + void (*load)(VGAscr*, Cursor*); + int (*move)(VGAscr*, Point); + + int doespanning; +}; + +/* + */ +struct VGAscr { + Lock devlock; + VGAdev* dev; + Pcidev* pci; + + VGAcur* cur; + ulong storage; + Cursor; + + int useflush; + + ulong paddr; /* frame buffer */ + void* vaddr; + int apsize; + + ulong io; /* device specific registers */ + ulong *mmio; + + ulong colormap[Pcolours][3]; + int palettedepth; + + Memimage* gscreen; + Memdata* gscreendata; + Memsubfont* memdefont; + + int (*fill)(VGAscr*, Rectangle, ulong); + int (*scroll)(VGAscr*, Rectangle, Rectangle); + void (*blank)(VGAscr*, int); + ulong id; /* internal identifier for driver use */ + int isblank; + int overlayinit; +}; + +extern VGAscr vgascreen[]; + +enum { + Backgnd = 0, /* black */ +}; + +/* mouse.c */ +extern void mousectl(Cmdbuf*); +extern void mouseresize(void); + +/* screen.c */ +extern int hwaccel; /* use hw acceleration; default on */ +extern int hwblank; /* use hw blanking; default on */ +extern int panning; /* use virtual screen panning; default off */ +extern void addvgaseg(char*, ulong, ulong); +extern uchar* attachscreen(Rectangle*, ulong*, int*, int*, int*); +extern void flushmemscreen(Rectangle); +extern int cursoron(int); +extern void cursoroff(int); +extern void setcursor(Cursor*); +extern int screensize(int, int, int, ulong); +extern int screenaperture(int, int); +extern Rectangle physgscreenr; /* actual monitor size */ +extern void blankscreen(int); + +extern VGAcur swcursor; +extern void swcursorinit(void); +extern void swcursorhide(void); +extern void swcursoravoid(Rectangle); +extern void swcursorunhide(void); + +/* devdraw.c */ +extern void deletescreenimage(void); +extern void resetscreenimage(void); +extern int drawhasclients(void); +extern ulong blanktime; +extern void setscreenimageclipr(Rectangle); +extern void drawflush(void); +extern int drawidletime(void); +extern QLock drawlock; + +/* vga.c */ +extern void vgascreenwin(VGAscr*); +extern void vgaimageinit(ulong); +extern void vgalinearpciid(VGAscr*, int, int); +extern void vgalinearpci(VGAscr*); +extern void vgalinearaddr(VGAscr*, ulong, int); + +extern void drawblankscreen(int); +extern void vgablank(VGAscr*, int); + +extern Lock vgascreenlock; + +#define ishwimage(i) (vgascreen[0].gscreendata && (i)->data->bdata == vgascreen[0].gscreendata->bdata) --- /dev/null +++ /sys/src/9k/k10/vga.c @@ -0,0 +1,263 @@ +#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 +#include +#include +#include "screen.h" + +static Memimage* back; +static Memimage *conscol; + +static Point curpos; +static Rectangle window; +static int *xp; +static int xbuf[256]; +Lock vgascreenlock; +int drawdebug; + +void +vgaimageinit(ulong chan) +{ + if(back == nil){ + back = allocmemimage(Rect(0,0,1,1), chan); /* RSC BUG */ + if(back == nil) + panic("back alloc"); /* RSC BUG */ + back->flags |= Frepl; + back->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF); + memfillcolor(back, DBlack); + } + + if(conscol == nil){ + conscol = allocmemimage(Rect(0,0,1,1), chan); /* RSC BUG */ + if(conscol == nil) + panic("conscol alloc"); /* RSC BUG */ + conscol->flags |= Frepl; + conscol->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF); + memfillcolor(conscol, DWhite); + } +} + +static void +vgascroll(VGAscr* scr) +{ + int h, o; + Point p; + Rectangle r; + + h = scr->memdefont->height; + o = 8*h; + r = Rpt(window.min, Pt(window.max.x, window.max.y-o)); + p = Pt(window.min.x, window.min.y+o); + memimagedraw(scr->gscreen, r, scr->gscreen, p, nil, p, S); + r = Rpt(Pt(window.min.x, window.max.y-o), window.max); + memimagedraw(scr->gscreen, r, back, ZP, nil, ZP, S); + + curpos.y -= o; +} + +static void +vgascreenputc(VGAscr* scr, char* buf, Rectangle *flushr) +{ + Point p; + int h, w, pos; + Rectangle r; + +// drawdebug = 1; + if(xp < xbuf || xp >= &xbuf[sizeof(xbuf)]) + xp = xbuf; + + h = scr->memdefont->height; + switch(buf[0]){ + + case '\n': + if(curpos.y+h >= window.max.y){ + vgascroll(scr); + *flushr = window; + } + curpos.y += h; + vgascreenputc(scr, "\r", flushr); + break; + + case '\r': + xp = xbuf; + curpos.x = window.min.x; + break; + + case '\t': + p = memsubfontwidth(scr->memdefont, " "); + w = p.x; + if(curpos.x >= window.max.x-4*w) + vgascreenputc(scr, "\n", flushr); + + pos = (curpos.x-window.min.x)/w; + pos = 4-(pos%4); + *xp++ = curpos.x; + r = Rect(curpos.x, curpos.y, curpos.x+pos*w, curpos.y + h); + memimagedraw(scr->gscreen, r, back, back->r.min, nil, back->r.min, S); + curpos.x += pos*w; + break; + + case '\b': + if(xp <= xbuf) + break; + xp--; + r = Rect(*xp, curpos.y, curpos.x, curpos.y+h); + memimagedraw(scr->gscreen, r, back, back->r.min, nil, ZP, S); + combinerect(flushr, r); + curpos.x = *xp; + break; + + case '\0': + break; + + default: + p = memsubfontwidth(scr->memdefont, buf); + w = p.x; + + if(curpos.x >= window.max.x-w) + vgascreenputc(scr, "\n", flushr); + + *xp++ = curpos.x; + r = Rect(curpos.x, curpos.y, curpos.x+w, curpos.y+h); + memimagedraw(scr->gscreen, r, back, back->r.min, nil, back->r.min, S); + memimagestring(scr->gscreen, curpos, conscol, ZP, scr->memdefont, buf); + combinerect(flushr, r); + curpos.x += w; + } +// drawdebug = 0; +} + +static void +vgascreenputs(char* s, int n) +{ + int i, gotdraw; + Rune r; + char buf[4]; + VGAscr *scr; + Rectangle flushr; + + scr = &vgascreen[0]; + + if(!islo()){ + /* + * Don't deadlock trying to + * print in an interrupt. + */ + if(!canlock(&vgascreenlock)) + return; + } + else + lock(&vgascreenlock); + + /* + * Be nice to hold this, but not going to deadlock + * waiting for it. Just try and see. + */ + gotdraw = canqlock(&drawlock); + + flushr = Rect(10000, 10000, -10000, -10000); + + while(n > 0){ + i = chartorune(&r, s); + if(i == 0){ + s++; + --n; + continue; + } + memmove(buf, s, i); + buf[i] = 0; + n -= i; + s += i; + vgascreenputc(scr, buf, &flushr); + } + flushmemscreen(flushr); + + if(gotdraw) + qunlock(&drawlock); + unlock(&vgascreenlock); +} + +void +vgascreenwin(VGAscr* scr) +{ + int h, w; + + h = scr->memdefont->height; + w = scr->memdefont->info[' '].width; + + window = insetrect(scr->gscreen->r, 48); + window.max.x = window.min.x+((window.max.x-window.min.x)/w)*w; + window.max.y = window.min.y+((window.max.y-window.min.y)/h)*h; + curpos = window.min; + + consputs = vgascreenputs; +} + +/* + * Supposedly this is the way to turn DPMS + * monitors off using just the VGA registers. + * Unfortunately, it seems to mess up the video mode + * on the cards I've tried. + */ +void +vgablank(VGAscr*, int blank) +{ + uchar seq1, crtc17; + + if(blank) { + seq1 = 0x00; + crtc17 = 0x80; + } else { + seq1 = 0x20; + crtc17 = 0x00; + } + + outs(Seqx, 0x0100); /* synchronous reset */ + seq1 |= vgaxi(Seqx, 1) & ~0x20; + vgaxo(Seqx, 1, seq1); + crtc17 |= vgaxi(Crtx, 0x17) & ~0x80; + delay(10); + vgaxo(Crtx, 0x17, crtc17); + outs(Crtx, 0x0300); /* end synchronous reset */ +} + +void +addvgaseg(char *name, ulong pa, ulong size) +{ + Physseg seg; + + memset(&seg, 0, sizeof seg); + seg.attr = SG_PHYSICAL; + seg.name = name; + seg.pa = pa; + seg.size = size; + addphysseg(&seg); +} + +void +cornerstring(char *s) +{ + int h, w; + VGAscr *scr; + Rectangle r; + Point p; + + scr = &vgascreen[0]; + if(scr->vaddr == nil || consputs != vgascreenputs) + return; + p = memsubfontwidth(scr->memdefont, s); + w = p.x; + h = scr->memdefont->height; + + r = Rect(0, 0, w, h); + memimagedraw(scr->gscreen, r, back, back->r.min, nil, back->r.min, S); + memimagestring(scr->gscreen, r.min, conscol, ZP, scr->memdefont, s); +// flushmemscreen(r); +} --- /dev/null +++ /sys/src/9k/k10/vgax.c @@ -0,0 +1,103 @@ +#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 +#include +#include +#include "screen.h" + +static Lock vgaxlock; /* access to index registers */ + +int +vgaxi(long port, uchar index) +{ + uchar data; + + ilock(&vgaxlock); + switch(port){ + + case Seqx: + case Crtx: + case Grx: + outb(port, index); + data = inb(port+1); + break; + + case Attrx: + /* + * Allow processor access to the colour + * palette registers. Writes to Attrx must + * be preceded by a read from Status1 to + * initialise the register to point to the + * index register and not the data register. + * Processor access is allowed by turning + * off bit 0x20. + */ + inb(Status1); + if(index < 0x10){ + outb(Attrx, index); + data = inb(Attrx+1); + inb(Status1); + outb(Attrx, 0x20|index); + } + else{ + outb(Attrx, 0x20|index); + data = inb(Attrx+1); + } + break; + + default: + iunlock(&vgaxlock); + return -1; + } + iunlock(&vgaxlock); + + return data & 0xFF; +} + +int +vgaxo(long port, uchar index, uchar data) +{ + ilock(&vgaxlock); + switch(port){ + + case Seqx: + case Crtx: + case Grx: + /* + * We could use an outport here, but some chips + * (e.g. 86C928) have trouble with that for some + * registers. + */ + outb(port, index); + outb(port+1, data); + break; + + case Attrx: + inb(Status1); + if(index < 0x10){ + outb(Attrx, index); + outb(Attrx, data); + inb(Status1); + outb(Attrx, 0x20|index); + } + else{ + outb(Attrx, 0x20|index); + outb(Attrx, data); + } + break; + + default: + iunlock(&vgaxlock); + return -1; + } + iunlock(&vgaxlock); + + return 0; +} --- /sys/src/9k/mk/parse +++ /sys/src/9k/mk/parse @@ -349,6 +349,27 @@ function mkdevc( a, d, i, m, n, s, t, u, name, cname){ s = s "PhysUart* physuart[] = {\n" t "\tnil,\n};\n\n"; } + if("vga" in d && "vga" in section){ + s = s "#define Image IMAGE\n" + s = s "#include \n" + s = s "#include \n" + s = s "#include \n" + s = s "#include \"screen.h\"\n" + + t = ""; + for(i = 1; i < section["vga"]; i++){ + split(line["vga", i], a); + s = s "extern VGAdev " a[1]"dev;\n"; + t = t "\t&" a[1] "dev,\n"; + } + s = s "VGAdev *vgadev[] = {\n" t "\tnil,\n};\n\n" + + t = " &swcursor, \n" + s = s "extern VGAcur swcursor;\n"; + s = s "VGAcur *vgacur[] = {\n" t "\tnil,\n};\n\n" + + } + t = ""; n = 0; if("physseg" in section){ --- /dev/null +++ /sys/src/9k/port/devdraw.c @@ -0,0 +1,2218 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#define Image IMAGE +#include +#include +#include +#include +#include "screen.h" + +enum +{ + Qtopdir = 0, + Qnew, + Qwinname, + Q3rd, + Q2nd, + Qcolormap, + Qctl, + Qdata, + Qrefresh, +}; + +/* + * Qid path is: + * 4 bits of file type (qids above) + * 24 bits of mux slot number +1; 0 means not attached to client + */ +#define QSHIFT 4 /* location in qid of client # */ + +#define QID(q) ((((ulong)(q).path)&0x0000000F)>>0) +#define CLIENTPATH(q) ((((ulong)q)&0x7FFFFFF0)>>QSHIFT) +#define CLIENT(q) CLIENTPATH((q).path) + +#define NHASH (1<<5) +#define HASHMASK (NHASH-1) +#define IOUNIT (64*1024) + +typedef struct Client Client; +typedef struct Draw Draw; +typedef struct DImage DImage; +typedef struct DScreen DScreen; +typedef struct CScreen CScreen; +typedef struct FChar FChar; +typedef struct Refresh Refresh; +typedef struct Refx Refx; +typedef struct DName DName; + +ulong blanktime = 30; /* in minutes; a half hour */ + +struct Draw +{ + int clientid; + int nclient; + Client** client; + int nname; + DName* name; + int vers; + int softscreen; + int blanked; /* screen turned off */ + ulong blanktime; /* time of last operation */ + ulong savemap[3*256]; +}; + +struct Client +{ + Ref r; + DImage* dimage[NHASH]; + CScreen* cscreen; + Refresh* refresh; + Rendez refrend; + uchar* readdata; + int nreaddata; + int busy; + int clientid; + int slot; + int refreshme; + int infoid; + int op; +}; + +struct Refresh +{ + DImage* dimage; + Rectangle r; + Refresh* next; +}; + +struct Refx +{ + Client* client; + DImage* dimage; +}; + +struct DName +{ + char *name; + Client *client; + DImage* dimage; + int vers; +}; + +struct FChar +{ + int minx; /* left edge of bits */ + int maxx; /* right edge of bits */ + uchar miny; /* first non-zero scan-line */ + uchar maxy; /* last non-zero scan-line + 1 */ + schar left; /* offset of baseline */ + uchar width; /* width of baseline */ +}; + +/* + * Reference counts in DImages: + * one per open by original client + * one per screen image or fill + * one per image derived from this one by name + */ +struct DImage +{ + int id; + int ref; + char *name; + int vers; + Memimage* image; + int ascent; + int nfchar; + FChar* fchar; + DScreen* dscreen; /* 0 if not a window */ + DImage* fromname; /* image this one is derived from, by name */ + DImage* next; +}; + +struct CScreen +{ + DScreen* dscreen; + CScreen* next; +}; + +struct DScreen +{ + int id; + int public; + int ref; + DImage *dimage; + DImage *dfill; + Memscreen* screen; + Client* owner; + DScreen* next; +}; + +static Draw sdraw; + QLock drawlock; + +static Memimage *screenimage; +static DImage* screendimage; +static char screenname[40]; +static int screennameid; + +static Rectangle flushrect; +static int waste; +static DScreen* dscreen; +extern void flushmemscreen(Rectangle); + void drawmesg(Client*, void*, int); + void drawuninstall(Client*, int); + void drawfreedimage(DImage*); + Client* drawclientofpath(ulong); + DImage* allocdimage(Memimage*); + +static char Enodrawimage[] = "unknown id for draw image"; +static char Enodrawscreen[] = "unknown id for draw screen"; +static char Eshortdraw[] = "short draw message"; +static char Eshortread[] = "draw read too short"; +static char Eimageexists[] = "image id in use"; +static char Escreenexists[] = "screen id in use"; +static char Edrawmem[] = "image memory allocation failed"; +static char Ereadoutside[] = "readimage outside image"; +static char Ewriteoutside[] = "writeimage outside image"; +static char Enotfont[] = "image not a font"; +static char Eindex[] = "character index out of range"; +static char Enoclient[] = "no such draw client"; +static char Edepth[] = "image has bad depth"; +static char Enameused[] = "image name in use"; +static char Enoname[] = "no image with that name"; +static char Eoldname[] = "named image no longer valid"; +static char Enamed[] = "image already has name"; +static char Ewrongname[] = "wrong name for image"; + +static void +dlock(void) +{ + qlock(&drawlock); +} + +static int +candlock(void) +{ + return canqlock(&drawlock); +} + +static void +dunlock(void) +{ + qunlock(&drawlock); +} + +static int +drawgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp) +{ + int t; + Qid q; + ulong path; + Client *cl; + + q.vers = 0; + + if(s == DEVDOTDOT){ + switch(QID(c->qid)){ + case Qtopdir: + case Q2nd: + mkqid(&q, Qtopdir, 0, QTDIR); + devdir(c, q, "#i", 0, eve, 0500, dp); + break; + case Q3rd: + cl = drawclientofpath(c->qid.path); + if(cl == nil) + strncpy(up->genbuf, "??", sizeof up->genbuf); + else + snprint(up->genbuf, sizeof up->genbuf, + "%d", cl->clientid); + mkqid(&q, Q2nd, 0, QTDIR); + devdir(c, q, up->genbuf, 0, eve, 0500, dp); + break; + default: + panic("drawwalk %llux", c->qid.path); + } + return 1; + } + + /* + * Top level directory contains the name of the device. + */ + t = QID(c->qid); + if(t == Qtopdir){ + switch(s){ + case 0: + mkqid(&q, Q2nd, 0, QTDIR); + devdir(c, q, "draw", 0, eve, 0555, dp); + break; + case 1: + mkqid(&q, Qwinname, 0, QTFILE); + devdir(c, q, "winname", 0, eve, 0444, dp); + break; + default: + return -1; + } + return 1; + } + + if(t == Qwinname){ + mkqid(&q, Qwinname, 0, QTFILE); + devdir(c, q, "winname", 0, eve, 0444, dp); + return 1; + } + + /* + * Second level contains "new" plus all the clients. + */ + if(t == Q2nd || t == Qnew){ + if(s == 0){ + mkqid(&q, Qnew, 0, QTFILE); + devdir(c, q, "new", 0, eve, 0666, dp); + } + else if(s <= sdraw.nclient){ + cl = sdraw.client[s-1]; + if(cl == 0) + return 0; + snprint(up->genbuf, sizeof up->genbuf, "%d", + cl->clientid); + mkqid(&q, (s<genbuf, 0, eve, 0555, dp); + return 1; + } + else + return -1; + return 1; + } + + /* + * Third level. + */ + path = c->qid.path&~((1<qid.vers; + q.type = QTFILE; + switch(s){ + case 0: + q.path = path|Qcolormap; + devdir(c, q, "colormap", 0, eve, 0600, dp); + break; + case 1: + q.path = path|Qctl; + devdir(c, q, "ctl", 0, eve, 0600, dp); + break; + case 2: + q.path = path|Qdata; + devdir(c, q, "data", 0, eve, 0600, dp); + break; + case 3: + q.path = path|Qrefresh; + devdir(c, q, "refresh", 0, eve, 0400, dp); + break; + default: + return -1; + } + return 1; +} + +static +int +drawrefactive(void *a) +{ + Client *c; + + c = a; + return c->refreshme || c->refresh!=0; +} + +static +void +drawrefreshscreen(DImage *l, Client *client) +{ + while(l != nil && l->dscreen == nil) + l = l->fromname; + if(l != nil && l->dscreen->owner != client) + l->dscreen->owner->refreshme = 1; +} + +static +void +drawrefresh(Memimage*, Rectangle r, void *v) +{ + Refx *x; + DImage *d; + Client *c; + Refresh *ref; + + if(v == 0) + return; + x = v; + c = x->client; + d = x->dimage; + for(ref=c->refresh; ref; ref=ref->next) + if(ref->dimage == d){ + combinerect(&ref->r, r); + return; + } + ref = malloc(sizeof(Refresh)); + if(ref){ + ref->dimage = d; + ref->r = r; + ref->next = c->refresh; + c->refresh = ref; + } +} + +static void +addflush(Rectangle r) +{ + int abb, ar, anbb; + Rectangle nbb; + + if(sdraw.softscreen==0 || !rectclip(&r, screenimage->r)) + return; + + if(flushrect.min.x >= flushrect.max.x){ + flushrect = r; + waste = 0; + return; + } + nbb = flushrect; + combinerect(&nbb, r); + ar = Dx(r)*Dy(r); + abb = Dx(flushrect)*Dy(flushrect); + anbb = Dx(nbb)*Dy(nbb); + /* + * Area of new waste is area of new bb minus area of old bb, + * less the area of the new segment, which we assume is not waste. + * This could be negative, but that's OK. + */ + waste += anbb-abb - ar; + if(waste < 0) + waste = 0; + /* + * absorb if: + * total area is small + * waste is less than half total area + * rectangles touch + */ + if(anbb<=1024 || waste*2layer; + if(l == nil) + return; + do{ + if(l->screen->image->data != screenimage->data) + return; + r = rectaddpt(r, l->delta); + l = l->screen->image->layer; + }while(l); + addflush(r); +} + +void +drawflush(void) +{ + if(flushrect.min.x < flushrect.max.x) + flushmemscreen(flushrect); + flushrect = Rect(10000, 10000, -10000, -10000); +} + +static +int +drawcmp(char *a, char *b, int n) +{ + if(strlen(a) != n) + return 1; + return memcmp(a, b, n); +} + +DName* +drawlookupname(int n, char *str) +{ + DName *name, *ename; + + name = sdraw.name; + ename = &name[sdraw.nname]; + for(; namename, str, n) == 0) + return name; + return 0; +} + +int +drawgoodname(DImage *d) +{ + DName *n; + + /* if window, validate the screen's own images */ + if(d->dscreen) + if(drawgoodname(d->dscreen->dimage) == 0 + || drawgoodname(d->dscreen->dfill) == 0) + return 0; + if(d->name == nil) + return 1; + n = drawlookupname(strlen(d->name), d->name); + if(n==nil || n->vers!=d->vers) + return 0; + return 1; +} + +DImage* +drawlookup(Client *client, int id, int checkname) +{ + DImage *d; + + d = client->dimage[id&HASHMASK]; + while(d){ + if(d->id == id){ + if(checkname && !drawgoodname(d)) + error(Eoldname); + return d; + } + d = d->next; + } + return 0; +} + +DScreen* +drawlookupdscreen(int id) +{ + DScreen *s; + + s = dscreen; + while(s){ + if(s->id == id) + return s; + s = s->next; + } + return 0; +} + +DScreen* +drawlookupscreen(Client *client, int id, CScreen **cs) +{ + CScreen *s; + + s = client->cscreen; + while(s){ + if(s->dscreen->id == id){ + *cs = s; + return s->dscreen; + } + s = s->next; + } + error(Enodrawscreen); + return 0; +} + +DImage* +allocdimage(Memimage *i) +{ + DImage *d; + + d = malloc(sizeof(DImage)); + if(d == 0) + return 0; + d->ref = 1; + d->name = 0; + d->vers = 0; + d->image = i; + d->nfchar = 0; + d->fchar = 0; + d->fromname = 0; + return d; +} + +Memimage* +drawinstall(Client *client, int id, Memimage *i, DScreen *dscreen) +{ + DImage *d; + + d = allocdimage(i); + if(d == 0) + return 0; + d->id = id; + d->dscreen = dscreen; + d->next = client->dimage[id&HASHMASK]; + client->dimage[id&HASHMASK] = d; + return i; +} + +Memscreen* +drawinstallscreen(Client *client, DScreen *d, int id, DImage *dimage, DImage *dfill, int public) +{ + Memscreen *s; + CScreen *c; + + c = malloc(sizeof(CScreen)); + if(dimage && dimage->image && dimage->image->chan == 0) + panic("bad image %p in drawinstallscreen", dimage->image); + + if(c == 0) + return 0; + if(d == 0){ + d = malloc(sizeof(DScreen)); + if(d == 0){ + free(c); + return 0; + } + s = malloc(sizeof(Memscreen)); + if(s == 0){ + free(c); + free(d); + return 0; + } + s->frontmost = 0; + s->rearmost = 0; + d->dimage = dimage; + if(dimage){ + s->image = dimage->image; + dimage->ref++; + } + d->dfill = dfill; + if(dfill){ + s->fill = dfill->image; + dfill->ref++; + } + d->ref = 0; + d->id = id; + d->screen = s; + d->public = public; + d->next = dscreen; + d->owner = client; + dscreen = d; + } + c->dscreen = d; + d->ref++; + c->next = client->cscreen; + client->cscreen = c; + return d->screen; +} + +void +drawdelname(DName *name) +{ + int i; + + i = name-sdraw.name; + memmove(name, name+1, (sdraw.nname-(i+1))*sizeof(DName)); + sdraw.nname--; +} + +void +drawfreedscreen(DScreen *this) +{ + DScreen *ds, *next; + + this->ref--; + if(this->ref < 0) + print("negative ref in drawfreedscreen\n"); + if(this->ref > 0) + return; + ds = dscreen; + if(ds == this){ + dscreen = this->next; + goto Found; + } + while(next = ds->next){ /* assign = */ + if(next == this){ + ds->next = this->next; + goto Found; + } + ds = next; + } + error(Enodrawimage); + + Found: + if(this->dimage) + drawfreedimage(this->dimage); + if(this->dfill) + drawfreedimage(this->dfill); + free(this->screen); + free(this); +} + +void +drawfreedimage(DImage *dimage) +{ + int i; + Memimage *l; + DScreen *ds; + + dimage->ref--; + if(dimage->ref < 0) + print("negative ref in drawfreedimage\n"); + if(dimage->ref > 0) + return; + + /* any names? */ + for(i=0; ifromname){ /* acquired by name; owned by someone else*/ + drawfreedimage(dimage->fromname); + goto Return; + } +// if(dimage->image == screenimage) /* don't free the display */ +// goto Return; + ds = dimage->dscreen; + if(ds){ + l = dimage->image; + if(l->data == screenimage->data) + addflush(l->layer->screenr); + if(l->layer->refreshfn == drawrefresh) /* else true owner will clean up */ + free(l->layer->refreshptr); + l->layer->refreshptr = nil; + if(drawgoodname(dimage)) + memldelete(l); + else + memlfree(l); + drawfreedscreen(ds); + }else + freememimage(dimage->image); + Return: + free(dimage->fchar); + free(dimage); +} + +void +drawuninstallscreen(Client *client, CScreen *this) +{ + CScreen *cs, *next; + + cs = client->cscreen; + if(cs == this){ + client->cscreen = this->next; + drawfreedscreen(this->dscreen); + free(this); + return; + } + while(next = cs->next){ /* assign = */ + if(next == this){ + cs->next = this->next; + drawfreedscreen(this->dscreen); + free(this); + return; + } + cs = next; + } +} + +void +drawuninstall(Client *client, int id) +{ + DImage *d, *next; + + d = client->dimage[id&HASHMASK]; + if(d == 0) + error(Enodrawimage); + if(d->id == id){ + client->dimage[id&HASHMASK] = d->next; + drawfreedimage(d); + return; + } + while(next = d->next){ /* assign = */ + if(next->id == id){ + d->next = next->next; + drawfreedimage(next); + return; + } + d = next; + } + error(Enodrawimage); +} + +void +drawaddname(Client *client, DImage *di, int n, char *str) +{ + DName *name, *ename, *new, *t; + + name = sdraw.name; + ename = &name[sdraw.nname]; + for(; namename, str, n) == 0) + error(Enameused); + t = smalloc((sdraw.nname+1)*sizeof(DName)); + memmove(t, sdraw.name, sdraw.nname*sizeof(DName)); + free(sdraw.name); + sdraw.name = t; + new = &sdraw.name[sdraw.nname++]; + new->name = smalloc(n+1); + memmove(new->name, str, n); + new->name[n] = 0; + new->dimage = di; + new->client = client; + new->vers = ++sdraw.vers; +} + +Client* +drawnewclient(void) +{ + Client *cl, **cp; + int i; + + for(i=0; islot = i; + cl->clientid = ++sdraw.clientid; + cl->op = SoverD; + sdraw.client[i] = cl; + return cl; +} + +static int +drawclientop(Client *cl) +{ + int op; + + op = cl->op; + cl->op = SoverD; + return op; +} + +int +drawhasclients(void) +{ + /* + * if draw has ever been used, we can't resize the frame buffer, + * even if all clients have exited (nclients is cumulative); it's too + * hard to make work. + */ + return sdraw.nclient != 0; +} + +Client* +drawclientofpath(ulong path) +{ + Client *cl; + int slot; + + slot = CLIENTPATH(path); + if(slot == 0) + return nil; + cl = sdraw.client[slot-1]; + if(cl==0 || cl->clientid==0) + return nil; + return cl; +} + + +Client* +drawclient(Chan *c) +{ + Client *client; + + client = drawclientofpath(c->qid.path); + if(client == nil) + error(Enoclient); + return client; +} + +Memimage* +drawimage(Client *client, uchar *a) +{ + DImage *d; + + d = drawlookup(client, BGLONG(a), 1); + if(d == nil) + error(Enodrawimage); + return d->image; +} + +void +drawrectangle(Rectangle *r, uchar *a) +{ + r->min.x = BGLONG(a+0*4); + r->min.y = BGLONG(a+1*4); + r->max.x = BGLONG(a+2*4); + r->max.y = BGLONG(a+3*4); +} + +void +drawpoint(Point *p, uchar *a) +{ + p->x = BGLONG(a+0*4); + p->y = BGLONG(a+1*4); +} + +Point +drawchar(Memimage *dst, Memimage *rdst, Point p, Memimage *src, Point *sp, DImage *font, int index, int op) +{ + FChar *fc; + Rectangle r; + Point sp1; + static Memimage *tmp; + + fc = &font->fchar[index]; + r.min.x = p.x+fc->left; + r.min.y = p.y-(font->ascent-fc->miny); + r.max.x = r.min.x+(fc->maxx-fc->minx); + r.max.y = r.min.y+(fc->maxy-fc->miny); + sp1.x = sp->x+fc->left; + sp1.y = sp->y+fc->miny; + + /* + * If we're drawing greyscale fonts onto a VGA screen, + * it's very costly to read the screen memory to do the + * alpha blending inside memdraw. If this is really a stringbg, + * then rdst is the bg image (in main memory) which we can + * refer to for the underlying dst pixels instead of reading dst + * directly. + */ + if(ishwimage(dst) && !ishwimage(rdst) && font->image->depth > 1){ + if(tmp == nil || tmp->chan != dst->chan || Dx(tmp->r) < Dx(r) || Dy(tmp->r) < Dy(r)){ + if(tmp) + freememimage(tmp); + tmp = allocmemimage(Rect(0,0,Dx(r),Dy(r)), dst->chan); + if(tmp == nil) + goto fallback; + } + memdraw(tmp, Rect(0,0,Dx(r),Dy(r)), rdst, r.min, memopaque, ZP, S); + memdraw(tmp, Rect(0,0,Dx(r),Dy(r)), src, sp1, font->image, Pt(fc->minx, fc->miny), op); + memdraw(dst, r, tmp, ZP, memopaque, ZP, S); + }else{ + fallback: + memdraw(dst, r, src, sp1, font->image, Pt(fc->minx, fc->miny), op); + } + + p.x += fc->width; + sp->x += fc->width; + return p; +} + +static DImage* +makescreenimage(void) +{ + int width, depth; + ulong chan; + DImage *di; + Memdata *md; + Memimage *i; + Rectangle r; + + md = malloc(sizeof *md); + if(md == nil) + return nil; + md->allocd = 1; + md->base = nil; + md->bdata = attachscreen(&r, &chan, &depth, &width, &sdraw.softscreen); + if(md->bdata == nil){ + free(md); + return nil; + } + md->ref = 1; + i = allocmemimaged(r, chan, md); + if(i == nil){ + free(md); + return nil; + } + i->width = width; + i->clipr = r; + + di = allocdimage(i); + if(di == nil){ + freememimage(i); /* frees md */ + return nil; + } + if(!waserror()){ + snprint(screenname, sizeof screenname, "noborder.screen.%d", ++screennameid); + drawaddname(nil, di, strlen(screenname), screenname); + poperror(); + } + return di; +} + +static int +initscreenimage(void) +{ + if(screenimage != nil) + return 1; + + screendimage = makescreenimage(); + if(screendimage == nil) + return 0; + screenimage = screendimage->image; +// iprint("initscreenimage %p %p\n", screendimage, screenimage); + mouseresize(); + return 1; +} + +void +deletescreenimage(void) +{ + dlock(); + if(screenimage){ + /* will be freed via screendimage; disable */ + screenimage->clipr = ZR; + screenimage = nil; + } + if(screendimage){ + drawfreedimage(screendimage); + screendimage = nil; + } + dunlock(); +} + +void +resetscreenimage(void) +{ + dlock(); + initscreenimage(); + dunlock(); +} + +static Chan* +drawattach(char *spec) +{ + dlock(); + if(!initscreenimage()){ + dunlock(); + error("no frame buffer"); + } + dunlock(); + return devattach('i', spec); +} + +static Walkqid* +drawwalk(Chan *c, Chan *nc, char **name, int nname) +{ + if(screenimage == nil) + error("no frame buffer"); + return devwalk(c, nc, name, nname, 0, 0, drawgen); +} + +static long +drawstat(Chan *c, uchar *db, long n) +{ + return devstat(c, db, n, 0, 0, drawgen); +} + +static Chan* +drawopen(Chan *c, int omode) +{ + Client *cl; + DName *dn; + DImage *di; + + if(c->qid.type & QTDIR){ + c = devopen(c, omode, 0, 0, drawgen); + c->iounit = IOUNIT; + } + + dlock(); + if(waserror()){ + dunlock(); + nexterror(); + } + + if(QID(c->qid) == Qnew){ + cl = drawnewclient(); + if(cl == 0) + error(Enodev); + c->qid.path = Qctl|((cl->slot+1)<qid)){ + case Qwinname: + break; + + case Qnew: + break; + + case Qctl: + cl = drawclient(c); + if(cl->busy) + error(Einuse); + cl->busy = 1; + flushrect = Rect(10000, 10000, -10000, -10000); + dn = drawlookupname(strlen(screenname), screenname); + if(dn == 0) + error("draw: cannot happen 2"); + if(drawinstall(cl, 0, dn->dimage->image, 0) == 0) + error(Edrawmem); + di = drawlookup(cl, 0, 0); + if(di == 0) + error("draw: cannot happen 1"); + di->vers = dn->vers; + di->name = smalloc(strlen(screenname)+1); + strcpy(di->name, screenname); + di->fromname = dn->dimage; + di->fromname->ref++; + incref(&cl->r); + break; + + case Qcolormap: + case Qdata: + case Qrefresh: + cl = drawclient(c); + incref(&cl->r); + break; + } + dunlock(); + poperror(); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + c->iounit = IOUNIT; + return c; +} + +static void +drawclose(Chan *c) +{ + int i; + DImage *d, **dp; + Client *cl; + Refresh *r; + + if(QID(c->qid) < Qcolormap) /* Qtopdir, Qnew, Q3rd, Q2nd have no client */ + return; + dlock(); + if(waserror()){ + dunlock(); + nexterror(); + } + + cl = drawclient(c); + if(QID(c->qid) == Qctl) + cl->busy = 0; + if((c->flag&COPEN) && (decref(&cl->r)==0)){ + while(r = cl->refresh){ /* assign = */ + cl->refresh = r->next; + free(r); + } + /* free names */ + for(i=0; icscreen) + drawuninstallscreen(cl, cl->cscreen); + /* all screens are freed, so now we can free images */ + dp = cl->dimage; + for(i=0; inext; + drawfreedimage(d); + } + dp++; + } + sdraw.client[cl->slot] = 0; + drawflush(); /* to erase visible, now dead windows */ + free(cl); + } + dunlock(); + poperror(); +} + +long +drawread(Chan *c, void *a, long n, vlong off) +{ + int index, m; + ulong red, green, blue; + Client *cl; + uchar *p; + Refresh *r; + DImage *di; + Memimage *i; + ulong offset = off; + char buf[16]; + + if(c->qid.type & QTDIR) + return devdirread(c, a, n, 0, 0, drawgen); + if(QID(c->qid) == Qwinname) + return readstr(off, a, n, screenname); + + cl = drawclient(c); + dlock(); + if(waserror()){ + dunlock(); + nexterror(); + } + switch(QID(c->qid)){ + case Qctl: + if(n < 12*12) + error(Eshortread); + if(cl->infoid < 0) + error(Enodrawimage); + if(cl->infoid == 0){ + i = screenimage; + if(i == nil) + error(Enodrawimage); + }else{ + di = drawlookup(cl, cl->infoid, 1); + if(di == nil) + error(Enodrawimage); + i = di->image; + } + n = snprint(a, n, + "%11d %11d %11s %11d %11d %11d %11d %11d %11d %11d %11d %11d ", + cl->clientid, cl->infoid, chantostr(buf, i->chan), + (i->flags&Frepl)==Frepl, + i->r.min.x, i->r.min.y, i->r.max.x, i->r.max.y, + i->clipr.min.x, i->clipr.min.y, i->clipr.max.x, + i->clipr.max.y); + cl->infoid = -1; + break; + + case Qcolormap: + drawactive(1); /* to restore map from backup */ + p = malloc(4*12*256+1); + if(p == 0) + error(Enomem); + m = 0; + for(index = 0; index < 256; index++){ + getcolor(index, &red, &green, &blue); + m += snprint((char*)p+m, 4*12*256+1 - m, + "%11d %11lud %11lud %11lud\n", index, + red>>24, green>>24, blue>>24); + } + n = readstr(offset, a, n, (char*)p); + free(p); + break; + + case Qdata: + if(cl->readdata == nil) + error("no draw data"); + if(n < cl->nreaddata) + error(Eshortread); + n = cl->nreaddata; + memmove(a, cl->readdata, cl->nreaddata); + free(cl->readdata); + cl->readdata = nil; + break; + + case Qrefresh: + if(n < 5*4) + error(Ebadarg); + for(;;){ + if(cl->refreshme || cl->refresh) + break; + dunlock(); + if(waserror()){ + dlock(); /* restore lock for waserror() above */ + nexterror(); + } + sleep(&cl->refrend, drawrefactive, cl); + poperror(); + dlock(); + } + p = a; + while(cl->refresh && n>=5*4){ + r = cl->refresh; + BPLONG(p+0*4, r->dimage->id); + BPLONG(p+1*4, r->r.min.x); + BPLONG(p+2*4, r->r.min.y); + BPLONG(p+3*4, r->r.max.x); + BPLONG(p+4*4, r->r.max.y); + cl->refresh = r->next; + free(r); + p += 5*4; + n -= 5*4; + } + cl->refreshme = 0; + n = p-(uchar*)a; + break; + } + dunlock(); + poperror(); + return n; +} + +void +drawwakeall(void) +{ + Client *cl; + int i; + + for(i=0; irefreshme || cl->refresh)) + wakeup(&cl->refrend); + } +} + +static long +drawwrite(Chan *c, void *a, long n, vlong) +{ + char buf[128], *fields[4], *q; + Client *cl; + int i, m, red, green, blue, x; + + if(c->qid.type & QTDIR) + error(Eisdir); + cl = drawclient(c); + dlock(); + if(waserror()){ + drawwakeall(); + dunlock(); + nexterror(); + } + switch(QID(c->qid)){ + case Qctl: + if(n != 4) + error("unknown draw control request"); + cl->infoid = BGLONG((uchar*)a); + break; + + case Qcolormap: + drawactive(1); /* to restore map from backup */ + m = n; + n = 0; + while(m > 0){ + x = m; + if(x > sizeof(buf)-1) + x = sizeof(buf)-1; + q = memccpy(buf, a, '\n', x); + if(q == 0) + break; + i = q-buf; + n += i; + a = (char*)a + i; + m -= i; + *q = 0; + if(tokenize(buf, fields, nelem(fields)) != 4) + error(Ebadarg); + i = strtoul(fields[0], 0, 0); + red = strtoul(fields[1], 0, 0); + green = strtoul(fields[2], 0, 0); + blue = strtoul(fields[3], &q, 0); + if(fields[3] == q) + error(Ebadarg); + if(red>255 || green>255 || blue>255 || i<0 || i>255) + error(Ebadarg); + red |= red<<8; + red |= red<<16; + green |= green<<8; + green |= green<<16; + blue |= blue<<8; + blue |= blue<<16; + setcolor(i, red, green, blue); + } + break; + + case Qdata: + drawmesg(cl, a, n); + drawwakeall(); + break; + + default: + error(Ebadusefd); + } + dunlock(); + poperror(); + return n; +} + +uchar* +drawcoord(uchar *p, uchar *maxp, int oldx, int *newx) +{ + int b, x; + + if(p >= maxp) + error(Eshortdraw); + b = *p++; + x = b & 0x7F; + if(b & 0x80){ + if(p+1 >= maxp) + error(Eshortdraw); + x |= *p++ << 7; + x |= *p++ << 15; + if(x & (1<<22)) + x |= ~0<<23; + }else{ + if(b & 0x40) + x |= ~0<<7; + x += oldx; + } + *newx = x; + return p; +} + +static void +printmesg(char *fmt, uchar *a, int plsprnt) +{ + char buf[256]; + char *p, *q; + int s, left; + + if(1|| plsprnt==0){ + SET(s,q,p); + USED(fmt, a, buf, p, q, s); + return; + } + q = buf; + *q++ = *a++; + for(p=fmt; *p; p++){ + left = sizeof buf - 2 - (q - buf); /* 2 for \n\0 */ + switch(*p){ + case 'l': + q += snprint(q, left, " %ld", (long)BGLONG(a)); + a += 4; + break; + case 'L': + q += snprint(q, left, " %.8lux", (ulong)BGLONG(a)); + a += 4; + break; + case 'R': + q += snprint(q, left, " [%d %d %d %d]", BGLONG(a), + BGLONG(a+4), BGLONG(a+8), BGLONG(a+12)); + a += 16; + break; + case 'P': + q += snprint(q, left, " [%d %d]", BGLONG(a), BGLONG(a+4)); + a += 8; + break; + case 'b': + q += snprint(q, left, " %d", *a++); + break; + case 's': + q += snprint(q, left, " %d", BGSHORT(a)); + a += 2; + break; + case 'S': + q += snprint(q, left, " %.4ux", BGSHORT(a)); + a += 2; + break; + } + } + *q++ = '\n'; + *q = 0; + iprint("%.*s", (int)(q-buf), buf); +} + +void +drawmesg(Client *client, void *av, int n) +{ + int c, repl, m, y, dstid, scrnid, ni, ci, j, nw, e0, e1, op, ox, oy, oesize, esize, doflush; + uchar *u, *a, refresh; + char *fmt; + ulong value, chan; + Rectangle r, clipr; + Point p, q, *pp, sp; + Memimage *i, *bg, *dst, *src, *mask; + Memimage *l, **lp; + Memscreen *scrn; + DImage *font, *ll, *di, *ddst, *dsrc; + DName *dn; + DScreen *dscrn; + FChar *fc; + Refx *refx; + CScreen *cs; + Refreshfn reffn; + + a = av; + m = 0; + fmt = nil; + if(waserror()){ + if(fmt) printmesg(fmt, a, 1); + /* iprint("error: %s\n", up->errstr); */ + nexterror(); + } + while((n-=m) > 0){ + USED(fmt); + a += m; + switch(*a){ + default: + error("bad draw command"); + /* new allocate: 'b' id[4] screenid[4] refresh[1] chan[4] repl[1] R[4*4] clipR[4*4] rrggbbaa[4] */ + case 'b': + printmesg(fmt="LLbLbRRL", a, 0); + m = 1+4+4+1+4+1+4*4+4*4+4; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + scrnid = BGSHORT(a+5); + refresh = a[9]; + chan = BGLONG(a+10); + repl = a[14]; + drawrectangle(&r, a+15); + drawrectangle(&clipr, a+31); + value = BGLONG(a+47); + if(drawlookup(client, dstid, 0)) + error(Eimageexists); + if(scrnid){ + dscrn = drawlookupscreen(client, scrnid, &cs); + scrn = dscrn->screen; + if(repl || chan!=scrn->image->chan) + error("image parameters incompatible with screen"); + reffn = nil; + switch(refresh){ + case Refbackup: + break; + case Refnone: + reffn = memlnorefresh; + break; + case Refmesg: + reffn = drawrefresh; + break; + default: + error("unknown refresh method"); + } + l = memlalloc(scrn, r, reffn, 0, value); + if(l == 0) + error(Edrawmem); + addflush(l->layer->screenr); + l->clipr = clipr; + rectclip(&l->clipr, r); + if(drawinstall(client, dstid, l, dscrn) == 0){ + memldelete(l); + error(Edrawmem); + } + dscrn->ref++; + if(reffn){ + refx = nil; + if(reffn == drawrefresh){ + refx = malloc(sizeof(Refx)); + if(refx == 0){ + drawuninstall(client, dstid); + error(Edrawmem); + } + refx->client = client; + refx->dimage = drawlookup(client, dstid, 1); + } + memlsetrefresh(l, reffn, refx); + } + continue; + } + i = allocmemimage(r, chan); + if(i == 0) + error(Edrawmem); + if(repl) + i->flags |= Frepl; + i->clipr = clipr; + if(!repl) + rectclip(&i->clipr, r); + if(drawinstall(client, dstid, i, 0) == 0){ + freememimage(i); + error(Edrawmem); + } + memfillcolor(i, value); + continue; + + /* allocate screen: 'A' id[4] imageid[4] fillid[4] public[1] */ + case 'A': + printmesg(fmt="LLLb", a, 1); + m = 1+4+4+4+1; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + if(dstid == 0) + error(Ebadarg); + if(drawlookupdscreen(dstid)) + error(Escreenexists); + ddst = drawlookup(client, BGLONG(a+5), 1); + dsrc = drawlookup(client, BGLONG(a+9), 1); + if(ddst==0 || dsrc==0) + error(Enodrawimage); + if(drawinstallscreen(client, 0, dstid, ddst, dsrc, a[13]) == 0) + error(Edrawmem); + continue; + + /* set repl and clip: 'c' dstid[4] repl[1] clipR[4*4] */ + case 'c': + printmesg(fmt="LbR", a, 0); + m = 1+4+1+4*4; + if(n < m) + error(Eshortdraw); + ddst = drawlookup(client, BGLONG(a+1), 1); + if(ddst == nil) + error(Enodrawimage); + if(ddst->name) + error("cannot change repl/clipr of shared image"); + dst = ddst->image; + if(a[5]) + dst->flags |= Frepl; + drawrectangle(&dst->clipr, a+6); + continue; + + /* draw: 'd' dstid[4] srcid[4] maskid[4] R[4*4] P[2*4] P[2*4] */ + case 'd': + printmesg(fmt="LLLRPP", a, 0); + m = 1+4+4+4+4*4+2*4+2*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + dstid = BGLONG(a+1); + src = drawimage(client, a+5); + mask = drawimage(client, a+9); + drawrectangle(&r, a+13); + drawpoint(&p, a+29); + drawpoint(&q, a+37); + op = drawclientop(client); + memdraw(dst, r, src, p, mask, q, op); + dstflush(dstid, dst, r); + continue; + + /* toggle debugging: 'D' val[1] */ + case 'D': + printmesg(fmt="b", a, 0); + m = 1+1; + if(n < m) + error(Eshortdraw); + drawdebug = a[1]; + continue; + + /* ellipse: 'e' dstid[4] srcid[4] center[2*4] a[4] b[4] thick[4] sp[2*4] alpha[4] phi[4]*/ + case 'e': + case 'E': + printmesg(fmt="LLPlllPll", a, 0); + m = 1+4+4+2*4+4+4+4+2*4+2*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + dstid = BGLONG(a+1); + src = drawimage(client, a+5); + drawpoint(&p, a+9); + e0 = BGLONG(a+17); + e1 = BGLONG(a+21); + if(e0<0 || e1<0) + error("invalid ellipse semidiameter"); + j = BGLONG(a+25); + if(j < 0) + error("negative ellipse thickness"); + drawpoint(&sp, a+29); + c = j; + if(*a == 'E') + c = -1; + ox = BGLONG(a+37); + oy = BGLONG(a+41); + op = drawclientop(client); + /* high bit indicates arc angles are present */ + if(ox & (1<<31)){ + if((ox & (1<<30)) == 0) + ox &= ~(1<<31); + memarc(dst, p, e0, e1, c, src, sp, ox, oy, op); + }else + memellipse(dst, p, e0, e1, c, src, sp, op); + dstflush(dstid, dst, Rect(p.x-e0-j, p.y-e1-j, p.x+e0+j+1, p.y+e1+j+1)); + continue; + + /* free: 'f' id[4] */ + case 'f': + printmesg(fmt="L", a, 1); + m = 1+4; + if(n < m) + error(Eshortdraw); + ll = drawlookup(client, BGLONG(a+1), 0); + if(ll && ll->dscreen && ll->dscreen->owner != client) + ll->dscreen->owner->refreshme = 1; + drawuninstall(client, BGLONG(a+1)); + continue; + + /* free screen: 'F' id[4] */ + case 'F': + printmesg(fmt="L", a, 1); + m = 1+4; + if(n < m) + error(Eshortdraw); + drawlookupscreen(client, BGLONG(a+1), &cs); + drawuninstallscreen(client, cs); + continue; + + /* initialize font: 'i' fontid[4] nchars[4] ascent[1] */ + case 'i': + printmesg(fmt="Llb", a, 1); + m = 1+4+4+1; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + if(dstid == 0) + error("cannot use display as font"); + font = drawlookup(client, dstid, 1); + if(font == 0) + error(Enodrawimage); + if(font->image->layer) + error("cannot use window as font"); + ni = BGLONG(a+5); + if(ni<=0 || ni>4096) + error("bad font size (4096 chars max)"); + free(font->fchar); /* should we complain if non-zero? */ + font->fchar = malloc(ni*sizeof(FChar)); + if(font->fchar == 0) + error("no memory for font"); + memset(font->fchar, 0, ni*sizeof(FChar)); + font->nfchar = ni; + font->ascent = a[9]; + continue; + + /* load character: 'l' fontid[4] srcid[4] index[2] R[4*4] P[2*4] left[1] width[1] */ + case 'l': + printmesg(fmt="LLSRPbb", a, 0); + m = 1+4+4+2+4*4+2*4+1+1; + if(n < m) + error(Eshortdraw); + font = drawlookup(client, BGLONG(a+1), 1); + if(font == 0) + error(Enodrawimage); + if(font->nfchar == 0) + error(Enotfont); + src = drawimage(client, a+5); + ci = BGSHORT(a+9); + if(ci >= font->nfchar) + error(Eindex); + drawrectangle(&r, a+11); + drawpoint(&p, a+27); + memdraw(font->image, r, src, p, memopaque, p, S); + fc = &font->fchar[ci]; + fc->minx = r.min.x; + fc->maxx = r.max.x; + fc->miny = r.min.y; + fc->maxy = r.max.y; + fc->left = a[35]; + fc->width = a[36]; + continue; + + /* draw line: 'L' dstid[4] p0[2*4] p1[2*4] end0[4] end1[4] radius[4] srcid[4] sp[2*4] */ + case 'L': + printmesg(fmt="LPPlllLP", a, 0); + m = 1+4+2*4+2*4+4+4+4+4+2*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + dstid = BGLONG(a+1); + drawpoint(&p, a+5); + drawpoint(&q, a+13); + e0 = BGLONG(a+21); + e1 = BGLONG(a+25); + j = BGLONG(a+29); + if(j < 0) + error("negative line width"); + src = drawimage(client, a+33); + drawpoint(&sp, a+37); + op = drawclientop(client); + memline(dst, p, q, e0, e1, j, src, sp, op); + /* avoid memlinebbox if possible */ + if(dstid==0 || dst->layer!=nil){ + /* BUG: this is terribly inefficient: update maximal containing rect*/ + r = memlinebbox(p, q, e0, e1, j); + dstflush(dstid, dst, insetrect(r, -(1+1+j))); + } + continue; + + /* create image mask: 'm' newid[4] id[4] */ +/* + * + case 'm': + printmesg("LL", a, 0); + m = 4+4; + if(n < m) + error(Eshortdraw); + break; + * + */ + + /* attach to a named image: 'n' dstid[4] j[1] name[j] */ + case 'n': + printmesg(fmt="Lz", a, 0); + m = 1+4+1; + if(n < m) + error(Eshortdraw); + j = a[5]; + if(j == 0) /* give me a non-empty name please */ + error(Eshortdraw); + m += j; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + if(drawlookup(client, dstid, 0)) + error(Eimageexists); + dn = drawlookupname(j, (char*)a+6); + if(dn == nil) + error(Enoname); + if(drawinstall(client, dstid, dn->dimage->image, 0) == 0) + error(Edrawmem); + di = drawlookup(client, dstid, 0); + if(di == 0) + error("draw: cannot happen"); + di->vers = dn->vers; + di->name = smalloc(j+1); + di->fromname = dn->dimage; + di->fromname->ref++; + memmove(di->name, a+6, j); + di->name[j] = 0; + client->infoid = dstid; + continue; + + /* name an image: 'N' dstid[4] in[1] j[1] name[j] */ + case 'N': + printmesg(fmt="Lbz", a, 0); + m = 1+4+1+1; + if(n < m) + error(Eshortdraw); + c = a[5]; + j = a[6]; + if(j == 0) /* give me a non-empty name please */ + error(Eshortdraw); + m += j; + if(n < m) + error(Eshortdraw); + di = drawlookup(client, BGLONG(a+1), 0); + if(di == 0) + error(Enodrawimage); + if(di->name) + error(Enamed); + if(c) + drawaddname(client, di, j, (char*)a+7); + else{ + dn = drawlookupname(j, (char*)a+7); + if(dn == nil) + error(Enoname); + if(dn->dimage != di) + error(Ewrongname); + drawdelname(dn); + } + continue; + + /* position window: 'o' id[4] r.min [2*4] screenr.min [2*4] */ + case 'o': + printmesg(fmt="LPP", a, 0); + m = 1+4+2*4+2*4; + if(n < m) + error(Eshortdraw); + dst = drawimage(client, a+1); + if(dst->layer){ + drawpoint(&p, a+5); + drawpoint(&q, a+13); + r = dst->layer->screenr; + ni = memlorigin(dst, p, q); + if(ni < 0) + error("image origin failed"); + if(ni > 0){ + addflush(r); + addflush(dst->layer->screenr); + ll = drawlookup(client, BGLONG(a+1), 1); + drawrefreshscreen(ll, client); + } + } + continue; + + /* set compositing operator for next draw operation: 'O' op */ + case 'O': + printmesg(fmt="b", a, 0); + m = 1+1; + if(n < m) + error(Eshortdraw); + client->op = a[1]; + continue; + + /* filled polygon: 'P' dstid[4] n[2] wind[4] ignore[2*4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */ + /* polygon: 'p' dstid[4] n[2] end0[4] end1[4] radius[4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */ + case 'p': + case 'P': + printmesg(fmt="LslllLPP", a, 0); + m = 1+4+2+4+4+4+4+2*4; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + dst = drawimage(client, a+1); + ni = BGSHORT(a+5); + if(ni < 0) + error("negative count in polygon"); + e0 = BGLONG(a+7); + e1 = BGLONG(a+11); + j = 0; + if(*a == 'p'){ + j = BGLONG(a+15); + if(j < 0) + error("negative polygon line width"); + } + src = drawimage(client, a+19); + drawpoint(&sp, a+23); + drawpoint(&p, a+31); + ni++; + pp = malloc(ni*sizeof(Point)); + if(pp == nil) + error(Enomem); + doflush = 0; + if(dstid==0 || (dst->layer && dst->layer->screen->image->data == screenimage->data)) + doflush = 1; /* simplify test in loop */ + ox = oy = 0; + esize = 0; + u = a+m; + for(y=0; y esize) + esize = c; + } + if(y == ni-1){ + c = memlineendsize(e1); + if(c > esize) + esize = c; + } + } + if(*a=='P' && e0!=1 && e0 !=~0) + r = dst->clipr; + else if(y > 0){ + r = Rect(q.x-oesize, q.y-oesize, q.x+oesize+1, q.y+oesize+1); + combinerect(&r, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1)); + } + if(rectclip(&r, dst->clipr)) /* should perhaps be an arg to dstflush */ + dstflush(dstid, dst, r); + } + pp[y] = p; + } + if(y == 1) + dstflush(dstid, dst, Rect(p.x-esize, p.y-esize, p.x+esize+1, p.y+esize+1)); + op = drawclientop(client); + if(*a == 'p') + mempoly(dst, pp, ni, e0, e1, j, src, sp, op); + else + memfillpoly(dst, pp, ni, e0, src, sp, op); + free(pp); + m = u-a; + continue; + + /* read: 'r' id[4] R[4*4] */ + case 'r': + printmesg(fmt="LR", a, 0); + m = 1+4+4*4; + if(n < m) + error(Eshortdraw); + i = drawimage(client, a+1); + drawrectangle(&r, a+5); + if(!rectinrect(r, i->r)) + error(Ereadoutside); + c = bytesperline(r, i->depth); + c *= Dy(r); + free(client->readdata); + client->readdata = mallocz(c, 0); + if(client->readdata == nil) + error("readimage malloc failed"); + client->nreaddata = memunload(i, r, client->readdata, c); + if(client->nreaddata < 0){ + free(client->readdata); + client->readdata = nil; + error("bad readimage call"); + } + continue; + + /* string: 's' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] ni*(index[2]) */ + /* stringbg: 'x' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] bgid[4] bgpt[2*4] ni*(index[2]) */ + case 's': + case 'x': + printmesg(fmt="LLLPRPs", a, 0); + m = 1+4+4+4+2*4+4*4+2*4+2; + if(*a == 'x') + m += 4+2*4; + if(n < m) + error(Eshortdraw); + + dst = drawimage(client, a+1); + dstid = BGLONG(a+1); + src = drawimage(client, a+5); + font = drawlookup(client, BGLONG(a+9), 1); + if(font == 0) + error(Enodrawimage); + if(font->nfchar == 0) + error(Enotfont); + drawpoint(&p, a+13); + drawrectangle(&r, a+21); + drawpoint(&sp, a+37); + ni = BGSHORT(a+45); + u = a+m; + m += ni*2; + if(n < m) + error(Eshortdraw); + clipr = dst->clipr; + dst->clipr = r; + op = drawclientop(client); + bg = dst; + if(*a == 'x'){ + /* paint background */ + bg = drawimage(client, a+47); + drawpoint(&q, a+51); + r.min.x = p.x; + r.min.y = p.y-font->ascent; + r.max.x = p.x; + r.max.y = r.min.y+Dy(font->image->r); + j = ni; + while(--j >= 0){ + ci = BGSHORT(u); + if(ci<0 || ci>=font->nfchar){ + dst->clipr = clipr; + error(Eindex); + } + r.max.x += font->fchar[ci].width; + u += 2; + } + memdraw(dst, r, bg, q, memopaque, ZP, op); + u -= 2*ni; + } + q = p; + while(--ni >= 0){ + ci = BGSHORT(u); + if(ci<0 || ci>=font->nfchar){ + dst->clipr = clipr; + error(Eindex); + } + q = drawchar(dst, bg, q, src, &sp, font, ci, op); + u += 2; + } + dst->clipr = clipr; + p.y -= font->ascent; + dstflush(dstid, dst, Rect(p.x, p.y, q.x, p.y+Dy(font->image->r))); + continue; + + /* use public screen: 'S' id[4] chan[4] */ + case 'S': + printmesg(fmt="Ll", a, 0); + m = 1+4+4; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + if(dstid == 0) + error(Ebadarg); + dscrn = drawlookupdscreen(dstid); + if(dscrn==0 || (dscrn->public==0 && dscrn->owner!=client)) + error(Enodrawscreen); + if(dscrn->screen->image->chan != BGLONG(a+5)) + error("inconsistent chan"); + if(drawinstallscreen(client, dscrn, 0, 0, 0, 0) == 0) + error(Edrawmem); + continue; + + /* top or bottom windows: 't' top[1] nw[2] n*id[4] */ + case 't': + printmesg(fmt="bsL", a, 0); + m = 1+1+2; + if(n < m) + error(Eshortdraw); + nw = BGSHORT(a+2); + if(nw < 0) + error(Ebadarg); + if(nw == 0) + continue; + m += nw*4; + if(n < m) + error(Eshortdraw); + lp = malloc(nw*sizeof(Memimage*)); + if(lp == 0) + error(Enomem); + if(waserror()){ + free(lp); + nexterror(); + } + for(j=0; jlayer == 0) + error("images are not windows"); + for(j=1; jlayer->screen != lp[0]->layer->screen) + error("images not on same screen"); + if(a[1]) + memltofrontn(lp, nw); + else + memltorearn(lp, nw); + if(lp[0]->layer->screen->image->data == screenimage->data) + for(j=0; jlayer->screenr); + ll = drawlookup(client, BGLONG(a+1+1+2), 1); + drawrefreshscreen(ll, client); + poperror(); + free(lp); + continue; + + /* visible: 'v' */ + case 'v': + printmesg(fmt="", a, 0); + m = 1; + drawflush(); + continue; + + /* write: 'y' id[4] R[4*4] data[x*1] */ + /* write from compressed data: 'Y' id[4] R[4*4] data[x*1] */ + case 'y': + case 'Y': + printmesg(fmt="LR", a, 0); + // iprint("load %c\n", *a); + m = 1+4+4*4; + if(n < m) + error(Eshortdraw); + dstid = BGLONG(a+1); + dst = drawimage(client, a+1); + drawrectangle(&r, a+5); + if(!rectinrect(r, dst->r)) + error(Ewriteoutside); + y = memload(dst, r, a+m, n-m, *a=='Y'); + if(y < 0) + error("bad writeimage call"); + dstflush(dstid, dst, r); + m += y; + continue; + } + } + poperror(); +} + +Dev drawdevtab = { + 'i', + "draw", + + devreset, + devinit, + devshutdown, + drawattach, + drawwalk, + drawstat, + drawopen, + devcreate, + drawclose, + drawread, + devbread, + drawwrite, + devbwrite, + devremove, + devwstat, +}; + +/* + * On 8 bit displays, load the default color map + */ +void +drawcmap(void) +{ + int r, g, b, cr, cg, cb, v; + int num, den; + int i, j; + + drawactive(1); /* to restore map from backup */ + for(r=0,i=0; r!=4; r++) + for(v=0; v!=4; v++,i+=16){ + for(g=0,j=v-r; g!=4; g++) + for(b=0;b!=4;b++,j++){ + den = r; + if(g > den) + den = g; + if(b > den) + den = b; + if(den == 0) /* divide check -- pick grey shades */ + cr = cg = cb = v*17; + else{ + num = 17*(4*den+v); + cr = r*num/den; + cg = g*num/den; + cb = b*num/den; + } + setcolor(i+(j&15), + cr*0x01010101, cg*0x01010101, cb*0x01010101); + } + } +} + +void +drawblankscreen(int blank) +{ + int i, nc; + ulong *p; + + if(blank == sdraw.blanked) + return; + if(!candlock()) + return; + if(screenimage == nil){ + dunlock(); + return; + } + p = sdraw.savemap; + nc = screenimage->depth > 8 ? 256 : 1<depth; + + /* + * blankscreen uses the hardware to blank the screen + * when possible. to help in cases when it is not possible, + * we set the color map to be all black. + */ + if(blank == 0){ /* turn screen on */ + for(i=0; iticks; + }else{ + if(blanktime && sdraw.blanktime && TK2SEC(sys->ticks - sdraw.blanktime)/60 >= blanktime) + drawblankscreen(1); + } +} + +int +drawidletime(void) +{ + return TK2SEC(sys->ticks - sdraw.blanktime)/60; +} --- /dev/null +++ /sys/src/9k/port/devmouse.c @@ -0,0 +1,767 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#define Image IMAGE +#include +#include +#include +#include "screen.h" + +enum { + ScrollUp = 0x08, + ScrollDown = 0x10, + ScrollLeft = 0x20, + ScrollRight = 0x40, +}; + +typedef struct Mouseinfo Mouseinfo; +typedef struct Mousestate Mousestate; + +struct Mousestate +{ + Point xy; /* mouse.xy */ + int buttons; /* mouse.buttons */ + ulong counter; /* increments every update */ + ulong msec; /* time of last event */ +}; + +struct Mouseinfo +{ + Lock; + Mousestate; + int dx; + int dy; + int track; /* dx & dy updated */ + int redraw; /* update cursor on screen */ + ulong lastcounter; /* value when /dev/mouse read */ + ulong lastresize; + ulong resize; + Rendez r; + Ref; + QLock; + int open; + int acceleration; + int maxacc; + Mousestate queue[16]; /* circular buffer of click events */ + int ri; /* read index into queue */ + int wi; /* write index into queue */ + uchar qfull; /* queue is full */ +}; + +enum +{ + CMbuttonmap, + CMscrollswap, + CMswap, + CMwildcard, +}; + +static Cmdtab mousectlmsg[] = +{ + CMbuttonmap, "buttonmap", 0, + CMscrollswap, "scrollswap", 0, + CMswap, "swap", 1, + CMwildcard, "*", 0, +}; + +Mouseinfo mouse; +Cursorinfo cursor; +int mouseshifted; +int kbdbuttons; +void (*kbdmouse)(int); +Cursor curs; + +void Cursortocursor(Cursor*); +int mousechanged(void*); + +static void mouseclock(void); + +enum{ + Qdir, + Qcursor, + Qmouse, + Qmousein, + Qmousectl, +}; + +static Dirtab mousedir[]={ + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "cursor", {Qcursor}, 0, 0666, + "mouse", {Qmouse}, 0, 0666, + "mousein", {Qmousein}, 0, 0220, + "mousectl", {Qmousectl}, 0, 0220, +}; + +static uchar buttonmap[8] = { + 0, 1, 2, 3, 4, 5, 6, 7, +}; +static int mouseswap; +static int scrollswap; +static ulong mousetime; + +extern Memimage* gscreen; +extern ulong kerndate; + +static void +mousereset(void) +{ + curs = arrow; + Cursortocursor(&arrow); + /* redraw cursor about 30 times per second */ + addclock0link(mouseclock, 33); +} + +static void +mousefromkbd(int buttons) +{ + kbdbuttons = buttons; + mousetrack(0, 0, 0, TK2MS(sys->ticks)); +} + +static int +mousedevgen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp) +{ + int rc; + + rc = devgen(c, name, tab, ntab, i, dp); + if(rc != -1) + dp->atime = mousetime; + return rc; +} + +static void +mouseinit(void) +{ + curs = arrow; + Cursortocursor(&arrow); + cursoron(1); + kbdmouse = mousefromkbd; + mousetime = seconds(); +} + +static Chan* +mouseattach(char *spec) +{ + return devattach('m', spec); +} + +static Walkqid* +mousewalk(Chan *c, Chan *nc, char **name, int nname) +{ + Walkqid *wq; + + /* + * We use devgen() and not mousedevgen() here + * see "Ugly problem" in dev.c/devwalk() + */ + wq = devwalk(c, nc, name, nname, mousedir, nelem(mousedir), devgen); + if(wq != nil && wq->clone != c && wq->clone != nil && (wq->clone->qid.type&QTDIR)==0) + incref(&mouse); + return wq; +} + +long int +mousestat(Chan *c, uchar *db, long n) +{ + return devstat(c, db, n, mousedir, nelem(mousedir), mousedevgen); +} + +static Chan* +mouseopen(Chan *c, int omode) +{ + switch((ulong)c->qid.path){ + case Qdir: + if(omode != OREAD) + error(Eperm); + break; + case Qmouse: + lock(&mouse); + if(mouse.open){ + unlock(&mouse); + error(Einuse); + } + mouse.open = 1; + mouse.ref++; + mouse.lastresize = mouse.resize; + unlock(&mouse); + break; + case Qmousein: + if(!iseve()) + error(Eperm); + break; + default: + incref(&mouse); + } + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static void +mousecreate(Chan*, char*, int, int) +{ + error(Eperm); +} + +static void +mouseclose(Chan *c) +{ + if((c->qid.type&QTDIR)==0 && (c->flag&COPEN)){ + if(c->qid.path == Qmousein) + return; + lock(&mouse); + if(c->qid.path == Qmouse) + mouse.open = 0; + if(--mouse.ref == 0){ + cursoroff(1); + curs = arrow; + Cursortocursor(&arrow); + cursoron(1); + } + unlock(&mouse); + } +} + + +static long +mouseread(Chan *c, void *va, long n, vlong off) +{ + char buf[1+4*12+1]; + uchar *p; + ulong offset = off; + Mousestate m; + int b; + + p = va; + switch((ulong)c->qid.path){ + case Qdir: + return devdirread(c, va, n, mousedir, nelem(mousedir), mousedevgen); + + case Qcursor: + if(offset != 0) + return 0; + if(n < 2*4+2*2*16) + error(Eshort); + n = 2*4+2*2*16; + lock(&cursor); + BPLONG(p+0, curs.offset.x); + BPLONG(p+4, curs.offset.y); + memmove(p+8, curs.clr, 2*16); + memmove(p+40, curs.set, 2*16); + unlock(&cursor); + return n; + + case Qmouse: + while(mousechanged(0) == 0) + sleep(&mouse.r, mousechanged, 0); + + mouse.qfull = 0; + mousetime = seconds(); + + /* + * No lock of the indices is necessary here, because ri is only + * updated by us, and there is only one mouse reader + * at a time. I suppose that more than one process + * could try to read the fd at one time, but such behavior + * is degenerate and already violates the calling + * conventions for sleep above. + */ + if(mouse.ri != mouse.wi) { + m = mouse.queue[mouse.ri]; + if(++mouse.ri == nelem(mouse.queue)) + mouse.ri = 0; + } else { + while(!canlock(&cursor)) + tsleep(&up->sleep, return0, 0, TK2MS(1)); + + m = mouse.Mousestate; + unlock(&cursor); + } + + b = buttonmap[m.buttons&7]; + /* put buttons 4 and 5 back in */ + b |= m.buttons & (3<<3); + if (scrollswap) + if (b == 8) + b = 16; + else if (b == 16) + b = 8; + snprint(buf, sizeof buf, "m%11d %11d %11d %11lud ", + m.xy.x, m.xy.y, + b, + m.msec); + mouse.lastcounter = m.counter; + if(n > 1+4*12) + n = 1+4*12; + if(mouse.lastresize != mouse.resize){ + mouse.lastresize = mouse.resize; + buf[0] = 'r'; + } + memmove(va, buf, n); + return n; + } + return 0; +} + +static void +setbuttonmap(char* map) +{ + int i, x, one, two, three; + + one = two = three = 0; + for(i = 0; i < 3; i++){ + if(map[i] == 0) + error(Ebadarg); + if(map[i] == '1'){ + if(one) + error(Ebadarg); + one = 1<qid.path){ + case Qdir: + error(Eisdir); + + case Qcursor: + cursoroff(1); + if(n < 2*4+2*2*16){ + curs = arrow; + Cursortocursor(&arrow); + }else{ + n = 2*4+2*2*16; + curs.offset.x = BGLONG(p+0); + curs.offset.y = BGLONG(p+4); + memmove(curs.clr, p+8, 2*16); + memmove(curs.set, p+40, 2*16); + Cursortocursor(&curs); + } + qlock(&mouse); + mouse.redraw = 1; + mouseclock(); + qunlock(&mouse); + cursoron(1); + return n; + + case Qmousectl: + cb = parsecmd(va, n); + if(waserror()){ + free(cb); + nexterror(); + } + + ct = lookupcmd(cb, mousectlmsg, nelem(mousectlmsg)); + + switch(ct->index){ + case CMswap: + if(mouseswap) + setbuttonmap("123"); + else + setbuttonmap("321"); + mouseswap ^= 1; + break; + + case CMscrollswap: + scrollswap ^= 1; + break; + + case CMbuttonmap: + if(cb->nf == 1) + setbuttonmap("123"); + else + setbuttonmap(cb->f[1]); + break; + + case CMwildcard: + mousectl(cb); + break; + } + + free(cb); + poperror(); + return n; + + case Qmousein: + if(n > sizeof buf-1) + n = sizeof buf -1; + memmove(buf, va, n); + buf[n] = 0; + p = 0; + pt.x = strtol(buf+1, &p, 0); + if(p == 0) + error(Eshort); + pt.y = strtol(p, &p, 0); + if(p == 0) + error(Eshort); + b = strtol(p, &p, 0); + msec = strtol(p, &p, 0); + if(msec == 0) + msec = TK2MS(sys->ticks); + mousetrack(pt.x, pt.y, b, msec); + return n; + + case Qmouse: + if(n > sizeof buf-1) + n = sizeof buf -1; + memmove(buf, va, n); + buf[n] = 0; + p = 0; + pt.x = strtoul(buf+1, &p, 0); + if(p == 0) + error(Eshort); + pt.y = strtoul(p, 0, 0); + qlock(&mouse); + if(ptinrect(pt, gscreen->r)){ + mouse.xy = pt; + mouse.redraw = 1; + mouse.track = 1; + mouseclock(); + } + qunlock(&mouse); + return n; + } + + error(Egreg); + return -1; +} + +Dev mousedevtab = { + 'm', + "mouse", + + mousereset, + mouseinit, + devshutdown, + mouseattach, + mousewalk, + mousestat, + mouseopen, + mousecreate, + mouseclose, + mouseread, + devbread, + mousewrite, + devbwrite, + devremove, + devwstat, +}; + +void +Cursortocursor(Cursor *c) +{ + lock(&cursor); + memmove(&cursor.Cursor, c, sizeof(Cursor)); + setcursor(c); + unlock(&cursor); +} + + +/* + * called by the clock routine to redraw the cursor + */ +static void +mouseclock(void) +{ + if(mouse.track){ + mousetrack(mouse.dx, mouse.dy, mouse.buttons, TK2MS(sys->ticks)); + mouse.track = 0; + mouse.dx = 0; + mouse.dy = 0; + } + if(mouse.redraw && canlock(&cursor)){ + mouse.redraw = 0; + cursoroff(0); + mouse.redraw = cursoron(0); + unlock(&cursor); + } + drawactive(0); +} + +static int +scale(int x) +{ + int sign = 1; + + if(x < 0){ + sign = -1; + x = -x; + } + switch(x){ + case 0: + case 1: + case 2: + case 3: + break; + case 4: + x = 6 + (mouse.acceleration>>2); + break; + case 5: + x = 9 + (mouse.acceleration>>1); + break; + default: + x *= mouse.maxacc; + break; + } + return sign*x; +} + +/* + * called at interrupt level to update the structure and + * awaken any waiting procs. + */ +void +mousetrack(int dx, int dy, int b, int msec) +{ + int x, y, lastb; + + if(gscreen==nil) + return; + + if(mouse.acceleration){ + dx = scale(dx); + dy = scale(dy); + } + x = mouse.xy.x + dx; + if(x < gscreen->clipr.min.x) + x = gscreen->clipr.min.x; + if(x >= gscreen->clipr.max.x) + x = gscreen->clipr.max.x; + y = mouse.xy.y + dy; + if(y < gscreen->clipr.min.y) + y = gscreen->clipr.min.y; + if(y >= gscreen->clipr.max.y) + y = gscreen->clipr.max.y; + + lastb = mouse.buttons; + mouse.xy = Pt(x, y); + mouse.buttons = b|kbdbuttons; + mouse.redraw = 1; + mouse.counter++; + mouse.msec = msec; + + /* + * if the queue fills, we discard the entire queue and don't + * queue any more events until a reader polls the mouse. + */ + if(!mouse.qfull && lastb != b) { /* add to ring */ + mouse.queue[mouse.wi] = mouse.Mousestate; + if(++mouse.wi == nelem(mouse.queue)) + mouse.wi = 0; + if(mouse.wi == mouse.ri) + mouse.qfull = 1; + } + wakeup(&mouse.r); + drawactive(1); +} + +/* + * microsoft 3 button, 7 bit bytes + * + * byte 0 - 1 L R Y7 Y6 X7 X6 + * byte 1 - 0 X5 X4 X3 X2 X1 X0 + * byte 2 - 0 Y5 Y4 Y3 Y2 Y1 Y0 + * byte 3 - 0 M x x x x x (optional) + * + * shift & right button is the same as middle button (for 2 button mice) + */ +int +m3mouseputc(Queue*, int c) +{ + static uchar msg[3]; + static int nb; + static int middle; + static uchar b[] = { 0, 4, 1, 5, 0, 2, 1, 3 }; + short x; + int dx, dy, newbuttons; + static ulong lasttick; + ulong m; + + /* Resynchronize in stream with timing. */ + m = sys->ticks; + if(TK2SEC(m - lasttick) > 2) + nb = 0; + lasttick = m; + + if(nb==0){ + /* + * an extra byte comes for middle button motion. + * only two possible values for the extra byte. + */ + if(c == 0x00 || c == 0x20){ + /* an extra byte gets sent for the middle button */ + middle = (c&0x20) ? 2 : 0; + newbuttons = (mouse.buttons & ~2) | middle; + mousetrack(0, 0, newbuttons, TK2MS(sys->ticks)); + return 0; + } + } + msg[nb] = c; + if(++nb == 3){ + nb = 0; + newbuttons = middle | b[(msg[0]>>4)&3 | (mouseshifted ? 4 : 0)]; + x = (msg[0]&0x3)<<14; + dx = (x>>8) | msg[1]; + x = (msg[0]&0xc)<<12; + dy = (x>>8) | msg[2]; + mousetrack(dx, dy, newbuttons, TK2MS(sys->ticks)); + } + return 0; +} + +/* + * microsoft intellimouse 3 buttons + scroll + * byte 0 - 1 L R Y7 Y6 X7 X6 + * byte 1 - 0 X5 X4 X3 X2 X1 X0 + * byte 2 - 0 Y5 Y4 Y3 Y2 Y1 Y0 + * byte 3 - 0 0 M % % % % + * + * %: 0xf => U , 0x1 => D + * + * L: left + * R: right + * U: up + * D: down + */ +int +m5mouseputc(Queue*, int c) +{ + static uchar msg[3]; + static int nb; + static ulong lasttick; + ulong m; + + /* Resynchronize in stream with timing. */ + m = sys->ticks; + if(TK2SEC(m - lasttick) > 2) + nb = 0; + lasttick = m; + + msg[nb++] = c & 0x7f; + if (nb == 4) { + schar dx,dy,newbuttons; + dx = msg[1] | (msg[0] & 0x3) << 6; + dy = msg[2] | (msg[0] & 0xc) << 4; + newbuttons = + (msg[0] & 0x10) >> (mouseshifted ? 3 : 2) + | (msg[0] & 0x20) >> 5 + | ( msg[3] == 0x10 ? 0x02 : + msg[3] == 0x0f ? ScrollUp : + msg[3] == 0x01 ? ScrollDown : 0 ); + mousetrack(dx, dy, newbuttons, TK2MS(sys->ticks)); + nb = 0; + } + return 0; +} + +/* + * Logitech 5 byte packed binary mouse format, 8 bit bytes + * + * shift & right button is the same as middle button (for 2 button mice) + */ +int +mouseputc(Queue*, int c) +{ + static short msg[5]; + static int nb; + static uchar b[] = {0, 4, 2, 6, 1, 5, 3, 7, 0, 2, 2, 6, 1, 3, 3, 7}; + int dx, dy, newbuttons; + static ulong lasttick; + ulong m; + + /* Resynchronize in stream with timing. */ + m = sys->ticks; + if(TK2SEC(m - lasttick) > 2) + nb = 0; + lasttick = m; + + if((c&0xF0) == 0x80) + nb=0; + msg[nb] = c; + if(c & 0x80) + msg[nb] |= ~0xFF; /* sign extend */ + if(++nb == 5){ + newbuttons = b[((msg[0]&7)^7) | (mouseshifted ? 8 : 0)]; + dx = msg[1]+msg[3]; + dy = -(msg[2]+msg[4]); + mousetrack(dx, dy, newbuttons, TK2MS(sys->ticks)); + nb = 0; + } + return 0; +} + +int +mousechanged(void*) +{ + return mouse.lastcounter != mouse.counter || + mouse.lastresize != mouse.resize; +} + +Point +mousexy(void) +{ + return mouse.xy; +} + +void +mouseaccelerate(int x) +{ + mouse.acceleration = x; + if(mouse.acceleration < 3) + mouse.maxacc = 2; + else + mouse.maxacc = mouse.acceleration; +} + +/* + * notify reader that screen has been resized + */ +void +mouseresize(void) +{ + mouse.resize++; + wakeup(&mouse.r); +} + --- /sys/src/9k/port/devuart.c +++ /sys/src/9k/port/devuart.c @@ -159,6 +159,33 @@ uartconsole(int i, char *cmd) return p; } +void +uartmouse(Uart* p, int (*putc)(Queue*, int), int setb1200) +{ + qlock(p); + if(p->opens++ == 0 && uartenable(p) == nil){ + qunlock(p); + error(Enodev); + } + if(setb1200) + uartctl(p, "b1200"); + p->putc = putc; + p->special = 1; + qunlock(p); +} + +void +uartsetmouseputc(Uart* p, int (*putc)(Queue*, int)) +{ + qlock(p); + if(p->opens == 0 || p->special == 0){ + qunlock(p); + error(Enodev); + } + p->putc = putc; + qunlock(p); +} + static void uartsetlength(int i) { --- /dev/null +++ /sys/src/9k/port/drawalloc.c @@ -0,0 +1,129 @@ +#include +#include +#include +#include + +void +memdrawallocinit(void) +{ +} + +void +memimagemove(void *from, void *to) +{ + Memdata *md; + + md = *(Memdata**)to; + if(md->base != from){ + print("compacted data not right: #%p\n", md->base); + abort(); + } + md->base = to; + + /* if allocmemimage changes this must change too */ + md->bdata = (uchar*)md->base+sizeof(Memdata*)+sizeof(uintptr); +} + +Memimage* +allocmemimaged(Rectangle r, ulong chan, Memdata *md) +{ + int d; + ulong l; + Memimage *i; + + if(Dx(r) <= 0 || Dy(r) <= 0){ + werrstr("bad rectangle %R", r); + return nil; + } + if((d = chantodepth(chan)) == 0) { + werrstr("bad channel descriptor %.8lux", chan); + return nil; + } + + l = wordsperline(r, d); + + i = mallocz(sizeof(Memimage), 1); + if(i == nil) + return nil; + + i->data = md; + i->zero = sizeof(ulong)*l*r.min.y; + + if(r.min.x >= 0) + i->zero += (r.min.x*d)/8; + else + i->zero -= (-r.min.x*d+7)/8; + i->zero = -i->zero; + i->width = l; + i->r = r; + i->clipr = r; + i->flags = 0; + i->layer = nil; + i->cmap = memdefcmap; + if(memsetchan(i, chan) < 0){ + free(i); + return nil; + } + return i; +} + +Memimage* +allocmemimage(Rectangle r, ulong chan) +{ + int d; + uchar *p; + ulong l, nw; + Memdata *md; + Memimage *i; + + if((d = chantodepth(chan)) == 0) { + werrstr("bad channel descriptor %.8lux", chan); + return nil; + } + + l = wordsperline(r, d); + nw = l*Dy(r); + md = malloc(sizeof(Memdata)); + if(md == nil) + return nil; + + md->ref = 1; + md->base = malloc(sizeof(Memdata*)+sizeof(uintptr)+nw*sizeof(ulong)); + if(md->base == nil){ + free(md); + return nil; + } + + p = (uchar*)md->base; + *(Memdata**)p = md; + p += sizeof(Memdata*); + + *(uintptr*)p = getcallerpc(&r); + p += sizeof(uintptr); + + /* if this changes, memimagemove must change too */ + md->bdata = p; + md->allocd = 1; + + i = allocmemimaged(r, chan, md); + if(i == nil){ + free(md->base); + free(md); + return nil; + } + md->imref = i; + return i; +} + +void +freememimage(Memimage *i) +{ + if(i == nil) + return; + if(i->data->ref-- == 1 && i->data->allocd){ + if(i->data->base) + free(i->data->base); + free(i->data); + } + free(i); +} --- /sys/src/9k/port/error.h +++ /sys/src/9k/port/error.h @@ -38,6 +38,7 @@ extern char Econinuse[]; /* connection in use */ extern char Eintr[]; /* interrupted */ extern char Enomem[]; /* kernel allocate failed */ extern char Esoverlap[]; /* segments overlap */ +extern char Emouseset[]; /* mouse type already set */ extern char Eshort[]; /* i/o count too small */ extern char Egreg[]; /* jmk added reentrancy for threads */ extern char Ebadspec[]; /* bad attach specifier */ --- /sys/src/9k/port/portfns.h +++ /sys/src/9k/port/portfns.h @@ -329,6 +329,8 @@ Uart* uartconsole(int, char*); int uartctl(Uart*, char*); int uartgetc(void); void uartkick(void*); +void uartmouse(Uart*, int (*)(Queue*, int), int); +void uartsetmouseputc(Uart*, int (*)(Queue*, int)); void uartputc(int); void uartputs(char*, int); void uartrecv(Uart*, char);