ref: 99e6eda7182651155b2baa3957242898fa489f37
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;
Point mapimagesize;
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 > 90 ? 'N' : 'S';
we = gpsloc.lon > 180 ? 'E' : 'W';
snprint(statusline, sizeof statusline, "Loc: %f%c %f%c Z: %d %c %s",
fabs(gpsloc.lon - 180), we,
fabs(gpsloc.lat - 90), 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)
{
return 360 / zoomsquared[currentloc.z];
}
NWindow *mainwindow;
NImage *nimage;
void
lockmapimage(void)
{
qlock(&mapimagelock);
}
void
unlockmapimage(void)
{
qunlock(&mapimagelock);
}
static GPos
gpsoff(void)
{
GPos p;
double x, y;
/* TODO: fix: "zoom point" should be centered properly */
if (gpsloc.lat > 180)
gpsloc.lat = 180;
if (gpsloc.lat < 0)
gpsloc.lat = 0;
if (gpsloc.lon > 360)
gpsloc.lon -= 360;
if (gpsloc.lon < 0)
gpsloc.lon = 360 - gpsloc.lon;
x = mapimagesize.x / 2. / (double)tilesize;
y = mapimagesize.y / 2. / (double)tilesize;
tile2gps(&x, &y, currentloc.z);
p.lon = gpsloc.lon - x;
p.lat = gpsloc.lat - y;
return p;
}
enum {
Cnil,
Credraw,
};
static void
locupdated(void)
{
currentloc = getbundle(gpsoff(), currentloc.z, &drawoffset);
}
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;
nateredraw(0);
}
/* download threads */
void
imageupdated(void)
{
qlock(&shouldredrawl);
shouldredraw++;
qunlock(&shouldredrawl);
}
void
redrawmap(void)
{
requestimage(currentloc, imageupdated);
}
void
initimage(void)
{
Rectangle r;
lockmapimage();
if (mapimage) {
freeimage(mapimage);
mapimage = nil;
}
ncallcalcrect(mainwindow, screen, screen->r);
r.min = ZP;
r.max.x = Dx(nimage->slot.r);
r.max.y = Dy(nimage->slot.r);
mapimagesize = r.max;
mapimage = allocimage(display, r, screen->chan, 0, DWhite);
nimage->image = mapimage;
unlockmapimage();
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();
if (x) *x = deg * m;
if (y) {
v = 1. - fabs(gpsloc.lat - 90) / 90.;
if (v < 0.1)
v = 0.1;
*y = deg * m * v;
}
}
static int
handlekeyboard(int kbdc)
{
double v;
double off = 0.5;
switch (kbdc) {
case Kdel:
case 'q':
exits(nil);
case '.':
return inczoom(+1);
case ',':
return inczoom(-1);
case Kleft:
calcoffset(off, &v, nil);
gpsloc.lon -= v;
goto Loc;
case Kright:
calcoffset(off, &v, nil);
gpsloc.lon += v;
goto Loc;
case Kup:
calcoffset(off, nil, &v);
gpsloc.lat += v;
goto Loc;
case Kdown:
calcoffset(off, nil, &v);
gpsloc.lat -= v;
goto Loc;
}
return Cnil;
Loc:
locupdated();
return Credraw;
}
static void
handlecmd(int cmd)
{
switch (cmd) {
case Credraw:
redrawmap();
return;
case Cnil:
default:
break;
}
}
static void
handleplumb(Plumbmsg* pm)
{
GPos pos;
int zoom;
if (pm->ndata < 0)
goto Out;
if (!parsepos(pm->data, pm->ndata, &pos, &zoom))
goto Out;
gpsloc = pos;
if (zoom >= 0)
currentloc.z = zoom;
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;
}
}
}