aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chrono.c255
-rw-r--r--mkfile10
-rw-r--r--nanosec.c38
3 files changed, 303 insertions, 0 deletions
diff --git a/chrono.c b/chrono.c
new file mode 100644
index 0000000..26750b4
--- /dev/null
+++ b/chrono.c
@@ -0,0 +1,255 @@
+#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");
+ }
+}
diff --git a/mkfile b/mkfile
new file mode 100644
index 0000000..ae67b34
--- /dev/null
+++ b/mkfile
@@ -0,0 +1,10 @@
+</$objtype/mkfile
+
+BIN=/$objtype/bin/games
+TARG=\
+ chrono\
+
+OFILES=\
+ nanosec.$O\
+
+</sys/src/cmd/mkmany
diff --git a/nanosec.c b/nanosec.c
new file mode 100644
index 0000000..1301e9f
--- /dev/null
+++ b/nanosec.c
@@ -0,0 +1,38 @@
+#include <u.h>
+#include <libc.h>
+#include <tos.h>
+
+/*
+ * nsec() is wallclock and can be adjusted by timesync
+ * so need to use cycles() instead, but fall back to
+ * nsec() in case we can't
+ */
+uvlong
+nanosec(void)
+{
+ static uvlong fasthz, xstart;
+ uvlong x, div;
+
+ if(fasthz == ~0ULL)
+ return nsec() - xstart;
+
+ if(fasthz == 0){
+ if(_tos->cyclefreq){
+ cycles(&xstart);
+ fasthz = _tos->cyclefreq;
+ } else {
+ xstart = nsec();
+ fasthz = ~0ULL;
+ fprint(2, "cyclefreq not available, falling back to nsec()\n");
+ fprint(2, "you might want to disable aux/timesync\n");
+ return 0;
+ }
+ }
+ cycles(&x);
+ x -= xstart;
+
+ /* this is ugly */
+ for(div = 1000000000ULL; x < 0x1999999999999999ULL && div > 1 ; div /= 10ULL, x *= 10ULL);
+
+ return x / (fasthz / div);
+}