#include #include #include #include #include #include #include #include "dat.h" #include "fns.h" Mousectl *mctl; ulong kdown; double Δt; Image *screenb; Memimage *fb; Memimage *pal[NCOLOR]; Sprite bob; Rune keys[Ke] = { [KR↺] 'q', [KR↻] 'e', [K↑] Kup, [K↓] Kdown, [K←] Kleft, [K→] Kright }; Memimage* eallocmemimage(Rectangle r, ulong chan) { Memimage *i; i = allocmemimage(r, chan); if(i == nil) sysfatal("allocmemimage: %r"); memfillcolor(i, DTransparent); return i; } Memimage* rgb(ulong c) { Memimage *i; i = eallocmemimage(Rect(0,0,1,1), screen->chan); i->flags |= Frepl; i->clipr = Rect(-1e6, -1e6, 1e6, 1e6); memfillcolor(i, c); return i; } Memimage* loadsprite(char *path) { Memimage *i; int fd; fd = open(path, OREAD); if(fd < 0) sysfatal("open: %r"); i = readmemimage(fd); if(i == nil) sysfatal("readmemimage: %r"); close(fd); return i; } void initpal(void) { pal[Cmask] = rgb(MASKCOLOR); pal[Cbg] = rgb(DWhite); pal[Cfg] = rgb(DBlack); } void kbdproc(void*) { Rune r, *a; char buf[128], *s; int fd, n; threadsetname("kbdproc"); if((fd = open("/dev/kbd", OREAD)) < 0) sysfatal("kbdproc: %r"); memset(buf, 0, sizeof buf); for(;;){ if(buf[0] != 0){ n = strlen(buf)+1; memmove(buf, buf+n, sizeof(buf)-n); } if(buf[0] == 0){ if((n = read(fd, buf, sizeof(buf)-1)) <= 0) break; buf[n-1] = 0; buf[n] = 0; } if(buf[0] == 'c'){ if(utfrune(buf, Kdel)){ close(fd); threadexitsall(nil); } } if(buf[0] != 'k' && buf[0] != 'K') continue; s = buf+1; kdown = 0; while(*s){ s += chartorune(&r, s); for(a = keys; a < keys+Ke; a++) if(r == *a){ kdown |= 1 << a-keys; break; } } } } enum{ Frameslice = 1000/30 }; void drawproc(void *arg) { Channel *c; vlong t0, t, dt, tt; int fps; c = arg; fps = 0; tt = 0; t0 = nsec(); for(;;){ send(c, nil); t = nsec(); dt = t-t0; fps++; tt += dt; if(tt >= 1e9){ fprint(2, "fps %d\n", fps); tt = fps = 0; } dt /= 1e6; if(dt < Frameslice) sleep(Frameslice - dt); t0 = t; } } void resetfb(void) { freememimage(fb); fb = eallocmemimage(Rect(0,0,Dx(screen->r),Dy(screen->r)), screen->chan); freeimage(screenb); screenb = allocimage(display, Rect(0,0,Dx(screen->r),Dy(screen->r)), screen->chan, 0, DNofill); } double round(double n) { return floor(n + 0.5); } Point rotatept(Point p, double θ, Point c) { Point r; p = subpt(p, c); r.x = round(p.x*cos(θ) - p.y*sin(θ)); r.y = round(p.x*sin(θ) + p.y*cos(θ)); r = addpt(r, c); return r; } void toroidwarp(Point *p) { if(p->x < 0) p->x += fb->r.max.x; if(p->x >= fb->r.max.x) p->x -= fb->r.max.x; if(p->y < 0) p->y += fb->r.max.y; if(p->y >= fb->r.max.y) p->y -= fb->r.max.y; } void redraw(void) { Rectangle r; Point p, dp; memimagedraw(fb, fb->r, pal[Cbg], ZP, nil, ZP, SoverD); for(r = ZR; r.min.y < bob.spr->r.max.y; r.min.y++){ r.min.x = 0; r.max.y = r.min.y+1; for(; r.min.x < bob.spr->r.max.x; r.min.x++){ r.max.x = r.min.x+1; p = rotatept(r.min, bob.roll, divpt(bob.spr->r.max, 2)); if(memcmp(byteaddr(bob.spr, p), byteaddr(pal[Cmask], ZP), bob.spr->depth/8) != 0){ dp = addpt(r.min, bob.p); toroidwarp(&dp); memimagedraw(fb, Rpt(dp,addpt(dp, Pt(1,1))), bob.spr, p, nil, ZP, SoverD); } } } for(r = fb->r; r.min.y < fb->r.max.y; r.min.y++){ r.max.y = r.min.y+1; loadimage(screenb, r, byteaddr(fb, r.min), bytesperline(r, fb->depth)); } lockdisplay(display); draw(screen, screen->r, screenb, nil, ZP); flushimage(display, 1); unlockdisplay(display); } void mouse(void) { } void resize(void) { if(getwindow(display, Refnone) < 0) fprint(2, "can't reattach to window\n"); resetfb(); redraw(); } void usage(void) { fprint(2, "usage: %s\n", argv0); exits("usage"); } void threadmain(int argc, char *argv[]) { vlong t0, t; Channel *drawc; ARGBEGIN{ default: usage(); }ARGEND; if(argc) usage(); if(initdraw(nil, nil, nil) < 0) sysfatal("initdraw: %r"); if(memimageinit() < 0) sysfatal("memimageinit: %r"); if((mctl = initmouse(nil, screen)) == nil) sysfatal("initmouse: %r"); resetfb(); initpal(); bob.spr = loadsprite("assets/bob.pic"); bob.p = divpt(fb->r.max, 2); drawc = chancreate(1, 0); display->locking = 1; unlockdisplay(display); proccreate(kbdproc, nil, mainstacksize); proccreate(drawproc, drawc, mainstacksize); t0 = nsec(); for(;;){ enum {MOUSE, RESIZE, DRAW}; Alt a[] = { {mctl->c, &mctl->Mouse, CHANRCV}, {mctl->resizec, nil, CHANRCV}, {drawc, nil, CHANRCV}, {nil, nil, CHANEND} }; switch(alt(a)){ case MOUSE: mouse(); break; case RESIZE: resize(); break; case DRAW: redraw(); break; } t = nsec(); Δt = (t-t0)/1e9; t0 = t; if(kdown & 1<