ref: a0578c7de4163344d936ea78191a836556495bba
dir: /map.c/
#include <u.h> #include <libc.h> #include <draw.h> #include <event.h> #include <plumb.h> #include <keyboard.h> #include <nate/nate.h> #include <nate/n_window.h> #include <nate/n_hbox.h> #include <nate/n_vbox.h> #include <nate/n_box.h> #include <nate/n_label.h> #include <nate/n_button.h> #include <nate/n_image.h> #include "dat.h" #include "fns.h" void debugprint(char *fmt, ...) { va_list args; if (!debug) return; va_start(args, fmt); vfprint(2, fmt, args); va_end(args); } static void runchecks(void) { if (access("/mnt/map/0", AEXIST) < 0) sysfatal("/mnt/map does not exist: %r"); } GPos gpsloc; GBundle currentloc; Point drawoffset; int maxzoom = 8; int zoom = 2; int tilesize = 256; int needsrequestimage = 0; Point mapimagesize; Point viewsize; Image *mapimage; QLock mapimagelock; char *copyright; char statusline[1024]; static void handlecmd(int cmd); static char* getstatusline(void) { char ns; char we; ns = gpsloc.lat > 0 ? 'N' : 'S'; we = gpsloc.lon > 0 ? 'E' : 'W'; snprint(statusline, sizeof statusline, "Loc: %f%c %f%c Z: %d %c %s", fabs(gpsloc.lon), we, fabs(gpsloc.lat), ns, currentloc.z, copyright ? ':' : ' ', copyright ? copyright : ""); return statusline; } ulong *zoomsquared; static void initmapfs(void) { int fd, n; char buf[32]; Dir *dir; fd = open("/mnt/map/ctl", OREAD); if (fd < 0) sysfatal("mapfs error: open ctl: %r"); n = read(fd, buf, sizeof(buf) - 1); if (n < 0) sysfatal("mapfs error: read ctl: %r"); close(fd); buf[n] = 0; maxzoom = atoi(buf); fd = open("/mnt/map/copyright", OREAD); if (fd >= 0) { dir = dirfstat(fd); n = dir->length; copyright = mallocz(n + 1, 1); free(dir); n = read(fd, copyright, n); if (n < 0) sysfatal("mapfs error: read copyright: %r"); close(fd); } zoomsquared = mallocz(sizeof(ulong) * (maxzoom+1), 1); for (int i = 0; i <= maxzoom; i++) { zoomsquared[i] = 1; for (int j = 0; j <= i; j++) zoomsquared[i] *= 2.; } } static double gettiledeg(void) { double x = 360. / zoomsquared[currentloc.z]; if (x < 0.1) x = 0.1; return x; } NWindow *mainwindow; NImage *nimage; void lockmapimage(void) { qlock(&mapimagelock); } void unlockmapimage(void) { qunlock(&mapimagelock); } static GPos gpsoff(void) { if (gpsloc.lat > 90.) gpsloc.lat = 90.; if (gpsloc.lat < -90.) gpsloc.lat = -90.; if (gpsloc.lon > 180.) gpsloc.lon -= 180.; if (gpsloc.lon < -180.) gpsloc.lon = 360. - gpsloc.lon; return gpsloc; } enum { Cnil, Credraw, }; static int locequals(GBundle *A, GBundle *B) { return A->x == B->x && A->y == B->y && A->z == B->z; } static void locupdated(void) { GBundle newloc; Point off, o, oo; oo.x = viewsize.x/2.; oo.y = viewsize.y/2.; off.x = oo.x/tilesize + 1; off.y = oo.y/tilesize + 1; newloc = getbundle(gpsoff(), currentloc.z, &drawoffset); newloc.x -= off.x; newloc.y -= off.y; debugprint("location updated: %f,%f\n", gpsloc.lon, gpsloc.lat); if (!locequals(¤tloc, &newloc)) { currentloc = newloc; needsrequestimage++; debugprint(" with requestimage"); } debugprint("\n"); o = mulpt(off, tilesize); o = subpt(o, oo); o = addpt(o, drawoffset); nimage->offset = drawoffset = o; } int debug; int shouldredraw = 0; QLock shouldredrawl; /* main thread */ void checkcondredraw(void) { int b; qlock(&shouldredrawl); b = shouldredraw; shouldredraw = 0; qunlock(&shouldredrawl); if (!b) return; debugprint("redraw\n"); nateredraw(0); } /* download threads */ void imageupdated(void) { qlock(&shouldredrawl); shouldredraw++; qunlock(&shouldredrawl); } void redrawmap(void) { if (needsrequestimage) { requestimage(currentloc, imageupdated); return; } needsrequestimage = 0; imageupdated(); } void initimage(void) { Rectangle r; lockmapimage(); if (mapimage) { freeimage(mapimage); mapimage = nil; } ncallcalcrect(mainwindow, screen, screen->r); r.min = ZP; viewsize.x = Dx(nimage->slot.r); viewsize.y = Dy(nimage->slot.r); r.max.x = (viewsize.x/tilesize + 2) * tilesize; r.max.y = (viewsize.y/tilesize + 2) * tilesize; mapimagesize = r.max; mapimage = allocimage(display, r, screen->chan, 0, DWhite); nimage->image = mapimage; unlockmapimage(); locupdated(); debugprint("initimage: %R %p\n", r, mapimage); } void eresized(int new) { if (new && getwindow(display, Refnone) < 0) sysfatal("%r"); initimage(); redrawmap(); nateredraw(0); } int exitclicked(Mouse, Nelem *, void*) { exits(nil); } static void traperrout(void) { int p[2]; pipe(p); dup(p[0], 2); int fd = create("/srv/map.errout", OWRITE|ORCLOSE, 0666); if (fd < 0) sysfatal("create: %r"); fprint(fd, "%d\n", p[1]); close(p[1]); } static int inczoom(int n) { int cz = zoom; cz += n; if (cz > maxzoom) cz = maxzoom; if (cz < 0) cz = 0; currentloc.z = cz; cz = currentloc.z == zoom ? Cnil : Credraw; zoom = currentloc.z; locupdated(); return cz; } static void calcoffset(double m, double *x, double *y) { double v; double deg = gettiledeg(); fprint(2, "deg: %f\n", deg); if (x) *x = deg * m; if (y) { v = 1. - fabs(gpsloc.lat) / 90.; if (v < 0.1) v = 0.1; *y = deg * m * v; } } static int handlekeyboard(int kbdc) { double v; double off = 0.5; int ret = Cnil; USED(ret); /* paranoia */ switch (kbdc) { case Kdel: case 'q': exits(nil); case '.': ret = inczoom(+1); break; case ',': ret = inczoom(-1); break; case Kleft: calcoffset(off, &v, nil); gpsloc.lon -= v; ret = Credraw; break; case Kright: calcoffset(off, &v, nil); gpsloc.lon += v; ret = Credraw; break; case Kup: calcoffset(off, nil, &v); gpsloc.lat += v; ret = Credraw; break; case Kdown: calcoffset(off, nil, &v); gpsloc.lat -= v; ret = Credraw; break; default: return Cnil; } if (ret == Credraw) { locupdated(); return Credraw; } return ret; } static void handlecmd(int cmd) { switch (cmd) { case Credraw: redrawmap(); return; case Cnil: default: break; } } static void handleplumb(Plumbmsg* pm) { GPos pos; int z; if (pm->ndata < 0) goto Out; if (!parsepos(pm->data, pm->ndata, &pos, &zoom)) goto Out; gpsloc = pos; z = zoom; if (z >= 0 && z <= maxzoom) currentloc.z = zoom = z; locupdated(); handlecmd(Credraw); Out: plumbfree(pm); } enum { Etimer = 4, Eplumb = 8, }; void main(int argc, char **argv) { Event ev; int cmd; ARGBEGIN{ case 'd': debug++; }ARGEND; runchecks(); if (debug == 1) traperrout(); initmapfs(); if (initdraw(nil, nil, "map") < 0) sysfatal("%r"); nateborders = 0; natetracedraw = 0; if (debug) natedebugfd = 2; einit(Emouse|Ekeyboard); etimer(Etimer, 100); eplumb(Eplumb, "map"); nateinit(); NAssign(NWindowAccessors, &mainwindow, New_Window(nil)) ->MakeRoot() ->Slot(NSlot(), New_VBox(nil) ->Slot(NSlotx(TOPLEFT, FILLX), New_HBox("menu") ->Slot(NSlotx(LEFT, 0), New_Button("bexit") ->Border(1, display->black) ->Label("Exit") ->OnClick(exitclicked, nil) ) ->Slot(NSlotx(LEFT, 0), New_Button("btest") ->Border(1, display->black) ->Label("Test") ) ) ->Slot(NSlot(), New_Box(nil) ->Border(1, display->black) ->Slot(NSlot(), NAssign(NImageAccessors, &nimage, New_Image("map")) ) ) ->Slot(NSlotx(LEFT, FILLX), New_Box("status") ->Border(1, display->black) ->Padding(NMargin2(5, 5)) ->Slot(NSlot(), New_Label(nil) ->LabelFunc(getstatusline) ->Align(LEFT) ) ) ); initimage(); gpsloc = getlocation(); currentloc.z = zoom; locupdated(); handlecmd(Credraw); imageupdated(); for (;;) { switch (event(&ev)) { case Etimer: checkcondredraw(); break; case Eplumb: handleplumb(ev.v); break; case Emouse: natemouseevent(ev.mouse); break; case Ekeyboard: cmd = handlekeyboard(ev.kbdc); handlecmd(cmd); break; } } }