shithub: map

ref: 99e6eda7182651155b2baa3957242898fa489f37
dir: /map.c/

View raw version
#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;
		}
	}
}