shithub: rcfitness

ref: 2d161e298d7e0605fac08e574f2a03fbd49e58e0
dir: /chrono.c/

View raw version
#include <u.h>
#include <libc.h>
#include <thread.h>
#include <draw.h>
#include <mouse.h>
#include <keyboard.h>

#define HZ2MS(hz)	(1000/(hz))

enum {
	Stop,
	Pause,
	Run,
};

typedef struct Stopwatch Stopwatch;
struct Stopwatch
{
	uvlong elapsed;		/* in ms */
	char hms[4][4];
	int state;

	void (*start)(Stopwatch*);
	void (*stop)(Stopwatch*);
	void (*pause)(Stopwatch*);
	void (*update)(Stopwatch*, uvlong);
	void (*draw)(Stopwatch*, Image*, Point, double);
};

char deffont[] = "/lib/font/bit/lucida/unicode.32.font";

Image *screenb;
Keyboardctl *kc;
Mousectl *mc;
Channel *drawc;
Stopwatch *chrono;

uvlong nanosec(void);

static void
stopwatch_start(Stopwatch *self)
{
	if(self->state == Stop)
		self->elapsed = 0;

	self->state = Run;
}

static void
stopwatch_stop(Stopwatch *self)
{
	if(self->state == Run)
		self->state = Stop;
}

static void
stopwatch_pause(Stopwatch *self)
{
	if(self->state == Run)
		self->state = Pause;
}

static void
stopwatch_update(Stopwatch *self, uvlong dt)
{
	int HMS[4], i;
	double t;

	self->elapsed += dt;
	t = self->elapsed;
	t /= 60*60*1000;	HMS[0] = t;	t -= HMS[0];
	t *= 60;		HMS[1] = t;	t -= HMS[1];
	t *= 60;		HMS[2] = t;	t -= HMS[2];
	t *= 1000;		HMS[3] = t;

	for(i = 0; i < nelem(HMS); i++)
		snprint(self->hms[i], sizeof self->hms[i], i < 3? "%02d": "%03d", HMS[i]);
}

static void
stopwatch_draw(Stopwatch *self, Image *dst, Point dp, double scale)
{
	USED(scale);
	int i;

	for(i = 0; i < nelem(self->hms); i++){
		if(i > 0)
			dp = string(dst, dp, display->white, ZP, font, i < 3? ":": ".");
		dp = string(dst, dp, display->white, ZP, font, self->hms[i]);
	}
}

void
timer(void *arg)
{
	Stopwatch *s;
	uvlong t0, t1;
	uvlong dt;	/* in ms */

	threadsetname("tic-tac");

	s = arg;
	t0 = nanosec();
	for(;;){
		t1 = nanosec();
		dt = (t1 - t0)/1000000ULL;

		if(s->state == Run){
			s->update(s, dt);
			nbsend(drawc, nil);
		}

		t0 = t1;
		sleep(HZ2MS(13));
	}
}

Stopwatch *
mkstopwatch(void)
{
	Stopwatch *s;

	s = malloc(sizeof *s);
	if(s == nil)
		sysfatal("malloc: %r");

	memset(s, 0, sizeof *s);
	s->start = stopwatch_start;
	s->stop = stopwatch_stop;
	s->pause = stopwatch_pause;
	s->update = stopwatch_update;
	s->draw = stopwatch_draw;

	proccreate(timer, s, mainstacksize);

	return s;
}

void
rmstopwatch(Stopwatch *s)
{
	free(s);
}

void
initscreenb(void)
{
	if(screenb != nil)
		freeimage(screenb);

	screenb = allocimage(display, rectsubpt(screen->r, screen->r.min), screen->chan, 0, DNofill);
	if(screenb == nil)
		sysfatal("allocimage: %r");
}

void
redraw(void)
{
	draw(screenb, screenb->r, display->black, nil, ZP);
	chrono->draw(chrono, screenb, Pt(10, 10), 1);
	draw(screen, screen->r, screenb, nil, ZP);
	flushimage(display, 1);
}

void
resize(void)
{
	if(getwindow(display, Refnone) < 0)
		sysfatal("resize failed");

	initscreenb();
	nbsend(drawc, nil);
}

void
mouse(Mousectl *)
{

}

void
key(Rune r)
{
	switch(r){
	case Kdel:
		threadexitsall(nil);
	case Kesc:
		if(chrono->state == Run)
			chrono->pause(chrono);
		else if(chrono->state == Pause)
			chrono->start(chrono);
		break;
	case ' ':
		if(chrono->state == Run)
			chrono->stop(chrono);
		else if(chrono->state == Stop)
			chrono->start(chrono);
		break;
	}
}

void
usage(void)
{
	fprint(2, "usage: %s\n", argv0);
	exits("usage");
}

void
threadmain(int argc, char *argv[])
{
	Rune r;

	ARGBEGIN{
	default: usage();
	}ARGEND;

	if(initdraw(nil, deffont, "chrono") < 0)
		sysfatal("initdraw: %r");
	if((mc = initmouse(nil, screen)) == nil)
		sysfatal("initmouse: %r");
	if((kc = initkeyboard(nil)) == nil)
		sysfatal("initkeyboard: %r");

	initscreenb();
	drawc = chancreate(sizeof(void*), 1);
	nbsend(drawc, nil);
	chrono = mkstopwatch();

	enum { MOUSE, RESIZE, KEYS, DRAW, NONE };
	Alt a[] = {
	 [MOUSE]	{mc->c, &mc->Mouse, CHANRCV},
	 [RESIZE]	{mc->resizec, nil, CHANRCV},
	 [KEYS]		{kc->c, &r, CHANRCV},
	 [DRAW]		{drawc, nil, CHANRCV},
	 [NONE]		{nil, nil, CHANEND}
	};
	for(;;)
		switch(alt(a)){
		case MOUSE:
			mouse(mc);
			break;
		case RESIZE:
			resize();
			break;
		case KEYS:
			key(r);
			break;
		case DRAW:
			redraw();
			break;
		default:
			sysfatal("main loop interrupted");
		}
}