ref: a7a315bff61bff58a9c918294f53c1e87d7ae7bb
dir: /sys/src/games/eui.c/
#include <u.h> #include <libc.h> #include <thread.h> #include <draw.h> #include <keyboard.h> #include <mouse.h> #include "eui.h" typedef struct Kfn Kfn; u64int keys, keys2; int trace, paused; int savereq, loadreq; QLock pauselock; int scale, fixscale, warp10; uchar *pic; Rectangle picr; Mousectl *mc; Image *bg; static int profile, framestep; static int vwdx, vwdy, vwbpp; static ulong vwchan; static Image *fb; static Channel *conv, *sync[2]; static uchar *screenconv[2]; static int screenconvi; struct Kfn{ Rune r; int k; char joyk[16]; void(*fn)(void); Kfn *n; }; static Kfn kfn, kkn; static int ax0, ax1; void * emalloc(ulong sz) { void *v; v = mallocz(sz, 1); if(v == nil) sysfatal("malloc: %r"); setmalloctag(v, getcallerpc(&sz)); return v; } static void joyproc(void *) { char *s, *down[9]; static char buf[64]; int n, k, j; Kfn *kp; j = 1; for(;;){ n = read(0, buf, sizeof(buf) - 1); if(n <= 0) sysfatal("read: %r"); buf[n] = 0; n = getfields(buf, down, nelem(down), 1, " "); k = 0; for(n--; n >= 0; n--){ s = down[n]; if(strcmp(s, "joy1") == 0) j = 1; else if(strcmp(s, "joy2") == 0) j = 2; for(kp=kkn.n; kp!=nil; kp=kp->n){ if(strcmp(kp->joyk, s) == 0) k |= kp->k; } } if(j == 2) keys2 = k; else keys = k; } } static void keyproc(void *) { int fd, n, k; static char buf[256]; char *s; Rune r; Kfn *kp; fd = open("/dev/kbd", OREAD); if(fd < 0) sysfatal("open: %r"); for(;;){ if(buf[0] != 0){ n = strlen(buf)+1; memmove(buf, buf+n, sizeof(buf)-n); } if(buf[0] == 0){ n = read(fd, buf, sizeof(buf)-1); if(n <= 0) sysfatal("read /dev/kbd: %r"); buf[n-1] = 0; buf[n] = 0; } if(buf[0] == 'c'){ if(utfrune(buf, Kdel)){ close(fd); threadexitsall(nil); } if(utfrune(buf, KF|5)) savereq = 1; if(utfrune(buf, KF|6)) loadreq = 1; if(utfrune(buf, KF|12)) profile ^= 1; if(utfrune(buf, 't')) trace = !trace; for(kp=kfn.n; kp!=nil; kp=kp->n){ if(utfrune(buf, kp->r)) kp->fn(); } } if(buf[0] != 'k' && buf[0] != 'K') continue; s = buf + 1; k = 0; while(*s != 0){ s += chartorune(&r, s); switch(r){ case Kdel: close(fd); threadexitsall(nil); case Kesc: if(paused) qunlock(&pauselock); else qlock(&pauselock); paused = !paused; break; case KF|1: if(paused){ qunlock(&pauselock); paused=0; } framestep = !framestep; break; case '`': warp10 = !warp10; break; } for(kp=kkn.n; kp!=nil; kp=kp->n){ if(utfrune(buf, kp->r)) k |= kp->k; } } if((k & ax0) == ax0) k &= ~ax0; if((k & ax1) == ax1) k &= ~ax1; keys = k; } } static void timing(void) { static int fcount; static vlong old; static char buf[32]; vlong new; if(++fcount == 60) fcount = 0; else return; new = nsec(); if(new != old) sprint(buf, "%6.2f%%", 1e11 / (new - old)); else buf[0] = 0; draw(screen, rectaddpt(Rect(10, 10, vwdx-40, 30), screen->r.min), bg, nil, ZP); string(screen, addpt(screen->r.min, Pt(10, 10)), display->black, ZP, display->defaultfont, buf); old = nsec(); } static void screeninit(void) { Point p; send(sync[0], nil); if(!fixscale){ scale = Dx(screen->r) / vwdx; if(Dy(screen->r) / vwdy < scale) scale = Dy(screen->r) / vwdy; } if(scale <= 0) scale = 1; else if(scale > 16) scale = 16; p = divpt(addpt(screen->r.min, screen->r.max), 2); picr = Rpt(subpt(p, Pt(scale * vwdx/2, scale * vwdy/2)), addpt(p, Pt(scale * vwdx/2, scale * vwdy/2))); freeimage(fb); fb = allocimage(display, Rect(0, 0, scale * vwdx, scale > 1 ? 1 : scale * vwdy), vwchan, scale > 1, 0); free(pic); pic = emalloc(vwdx * vwdy * vwbpp * scale); free(screenconv[0]); free(screenconv[1]); screenconv[0] = emalloc(vwdx * vwdy * vwbpp * scale); screenconv[1] = emalloc(vwdx * vwdy * vwbpp * scale); draw(screen, screen->r, bg, nil, ZP); recv(sync[1], nil); } void flushmouse(int discard) { Mouse m; if(nbrecvul(mc->resizec) > 0){ send(sync[0], nil); if(getwindow(display, Refnone) < 0) sysfatal("resize failed: %r"); recv(sync[1], nil); screeninit(); } if(discard) while(nbrecv(mc->c, &m) > 0) ; } static void screenproc(void*) { uchar *p; enum { Draw, Sync1, Sync2 }; Alt alts[] = { [Draw] {.c = conv, .v = &p, .op = CHANRCV}, [Sync1] {.c = sync[0], .op = CHANRCV}, [Sync2] {.c = sync[1], .op = CHANNOP}, {.op = CHANEND}, }; for(;;) switch(alt(alts)){ case Draw: if(scale == 1){ loadimage(fb, fb->r, p, vwdx * vwdy * vwbpp); draw(screen, picr, fb, nil, ZP); } else { Rectangle r; uchar *s; int w; s = p; r = picr; w = vwdx * vwbpp * scale; while(r.min.y < picr.max.y){ loadimage(fb, fb->r, s, w); s += w; r.max.y = r.min.y+scale; draw(screen, r, fb, nil, ZP); r.min.y = r.max.y; } } flushimage(display, 1); break; case Sync1: alts[Draw].op = CHANNOP; alts[Sync1].op = CHANNOP; alts[Sync2].op = CHANSND; break; case Sync2: alts[Draw].op = CHANRCV; alts[Sync1].op = CHANRCV; alts[Sync2].op = CHANNOP; break; } } void flushscreen(void) { memmove(screenconv[screenconvi], pic, vwdx * vwdy * vwbpp * scale); if(sendp(conv, screenconv[screenconvi]) > 0) screenconvi = (screenconvi + 1) % 2; if(profile) timing(); } void flushaudio(int (*audioout)(void)) { static vlong old, delta; vlong new, diff; if(audioout == nil || audioout() < 0 && !warp10){ new = nsec(); diff = 0; if(old != 0){ diff = BILLION/60 - (new - old) - delta; if(diff >= MILLION) sleep(diff/MILLION); } old = nsec(); if(diff > 0){ diff = (old - new) - (diff / MILLION) * MILLION; delta += (diff - delta) / 100; } } if(framestep){ paused = 1; qlock(&pauselock); framestep = 0; } } void regkeyfn(Rune r, void (*fn)(void)) { Kfn *kp; for(kp=&kfn; kp->n!=nil; kp=kp->n) ; kp->n = emalloc(sizeof *kp); kp->n->r = r; kp->n->fn = fn; } void regkey(char *joyk, Rune r, int k) { Kfn *kp; for(kp=&kkn; kp->n!=nil; kp=kp->n) ; kp->n = emalloc(sizeof *kp); strncpy(kp->n->joyk, joyk, sizeof(kp->n->joyk)-1); if(strcmp(joyk, "up") == 0 || strcmp(joyk, "down") == 0) ax0 |= k; if(strcmp(joyk, "left") == 0 || strcmp(joyk, "right") == 0) ax1 |= k; kp->n->r = r; kp->n->k = k; } void initemu(int dx, int dy, int bpp, ulong chan, int dokey, void(*kproc)(void*)) { vwdx = dx; vwdy = dy; vwchan = chan; vwbpp = bpp; if(initdraw(nil, nil, nil) < 0) sysfatal("initdraw: %r"); mc = initmouse(nil, screen); if(mc == nil) sysfatal("initmouse: %r"); if(dokey) proccreate(kproc != nil ? kproc : keyproc, nil, mainstacksize); if(kproc == nil) proccreate(joyproc, nil, mainstacksize); bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF); scale = fixscale; conv = chancreate(sizeof(uchar*), 0); sync[0] = chancreate(1, 0); sync[1] = chancreate(1, 0); proccreate(screenproc, nil, mainstacksize); screeninit(); }