summaryrefslogtreecommitdiff
path: root/appl/cmd/lego/clock.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/lego/clock.b')
-rw-r--r--appl/cmd/lego/clock.b214
1 files changed, 214 insertions, 0 deletions
diff --git a/appl/cmd/lego/clock.b b/appl/cmd/lego/clock.b
new file mode 100644
index 00000000..3b3c3e50
--- /dev/null
+++ b/appl/cmd/lego/clock.b
@@ -0,0 +1,214 @@
+implement Clock;
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+ draw: Draw;
+ Point, Rect: import draw;
+
+include "math.m";
+ math: Math;
+ sqrt, atan2, hypot, Degree: import math;
+
+include "tk.m";
+ tk: Tk;
+ top: ref Tk->Toplevel;
+
+include "tkclient.m";
+ tkclient: Tkclient;
+
+Clock: module {
+ init: fn(ctxt: ref Draw->Context, argl: list of string);
+};
+
+cmds := array[] of {
+ "bind . <Configure> {send win resize}",
+ "canvas .face -height 200 -width 200 -bg yellow",
+ "bind .face <ButtonPress> {send ptr %x %y}",
+ "bind .face <ButtonRelease> {send ptr release}",
+ "pack .face -expand yes -fill both",
+ "button .reset -text Reset -command {send win reset}",
+ "pack .reset -after .Wm_t.title -side right -fill y",
+ "pack propagate . no",
+};
+
+init(ctxt: ref Draw->Context, nil: list of string)
+{
+ sys = load Sys Sys->PATH;
+ draw = load Draw Draw->PATH;
+ math = load Math Math->PATH;
+ tk = load Tk Tk->PATH;
+ tkclient = load Tkclient Tkclient->PATH;
+ tkclient->init();
+
+ sys->pctl(Sys->NEWPGRP, nil);
+
+ clockface := sys->open("/chan/clockface", Sys->ORDWR);
+ if (clockface == nil) {
+ sys->print("open /chan/clockface failed: %r\n");
+ raise "fail:clockface";
+ }
+ tock := chan of string;
+ spawn readme(clockface, tock);
+
+ titlech: chan of string;
+ (top, titlech) = tkclient->toplevel(ctxt, "hh:mm", "", Tkclient->Appl);
+ win := chan of string;
+ ptr := chan of string;
+ tk->namechan(top, win, "win");
+ tk->namechan(top, ptr, "ptr");
+ for(i:=0; i<len cmds; i++)
+ tk->cmd(top, cmds[i]);
+ tkclient->onscreen(top, nil);
+ tkclient->startinput(top, "ptr"::nil);
+ drawface();
+ spawn hands(ptr, clockface);
+
+ for (;;) alt {
+ s := <-top.ctxt.kbd =>
+ tk->keyboard(top, s);
+ s := <-top.ctxt.ptr =>
+ tk->pointer(top, *s);
+ s := <-top.ctxt.ctl or
+ s = <-top.wreq or
+ s = <-titlech =>
+ tkclient->wmctl(top, s);
+ msg := <-win =>
+ case msg {
+ "resize" => drawface();
+ "reset" => sys->fprint(clockface, "reset");
+ }
+ nowis := <-tock =>
+ (n, toks) := sys->tokenize(nowis, ":");
+ if (n == 2) {
+ (hour, minute) = (int hd toks, int hd tl toks);
+ setclock();
+ }
+ }
+}
+
+readme(fd: ref Sys->FD, ch: chan of string)
+{
+ buf := array[64] of byte;
+ while ((n := sys->read(fd, buf, len buf)) > 0) {
+ if (buf[n-1] == byte '\n')
+ n--;
+ ch <-= string buf[:n];
+ }
+ ch <-= "99:99";
+}
+
+hour, minute: int;
+center, focus: Point;
+major: int;
+
+Frim: con .98;
+Fminute: con .90;
+Fhour: con .45;
+Fnub: con .05;
+
+hands(ptr: chan of string, fd: ref Sys->FD)
+{
+ for (;;) {
+ pos := <-ptr;
+ p := s2p(pos);
+ hand := "";
+ if (elinside(p, Fnub))
+ hand = nil;
+ else if (elinside(p, Fhour))
+ hand = "hour";
+ else if (elinside(p, Fminute))
+ hand = "minute";
+
+ do {
+ p = s2p(pos).sub(center);
+ angle := int (atan2(real -p.y, real p.x) / Degree);
+ if (hand != nil)
+ tkc(".face itemconfigure "+hand+" -start "+string angle+"; update");
+ case hand {
+ "hour" => hour = ((360+90-angle) / 30) % 12;
+ "minute" => minute = ((360+90-angle) / 6) % 60;
+ }
+ } while ((pos = <-ptr) != "release");
+ if (hand != nil)
+ sys->fprint(fd, "%d:%d\n", hour, minute);
+ }
+}
+
+drawface()
+{
+ elparms();
+ tkc(sys->sprint(".face configure -scrollregion {0 0 %d %d}", 2*center.x, 2*center.y));
+ tkc(".face delete all");
+ tkc(".face create oval "+elrect(Frim)+" -fill fuchsia -outline aqua -width 2");
+ for (a := 0; a < 360; a += 30)
+ tkc(".face create arc "+elrect(Frim)+" -fill aqua -outline aqua -width 2 -extent 1 -start "+string a);
+ tkc(".face create oval "+elrect(Fminute)+" -fill fuchsia -outline fuchsia");
+ tkc(".face create oval "+elrect(Fnub)+" -fill aqua -outline aqua");
+ tkc(".face create arc "+elrect(Fhour)+" -fill aqua -outline aqua -width 6 -extent 1 -tags hour");
+ tkc(".face create arc "+elrect(Fminute)+" -fill aqua -outline aqua -width 2 -extent 1 -tags minute");
+ setclock();
+}
+
+setclock()
+{
+ tkc(".face itemconfigure hour -start "+string (90 - 30*(hour%12) - minute/2));
+ tkc(".face itemconfigure minute -start "+string (90 - 6*minute));
+ tkc(sys->sprint(".Wm_t.title configure -text {%d:%.2d}", (hour+11)%12+1, minute));
+ tkc("update");
+}
+
+elparms()
+{
+ center = (int tkc(".face cget actwidth") / 2, int tkc(".face cget actheight") / 2);
+ dist := center.x*center.x - center.y*center.y;
+ if (dist > 0) {
+ major = 2 * center.x;
+ focus = (int sqrt(real dist), 0);
+ } else {
+ major = 2 * center.y;
+ focus = (0, int sqrt(real -dist));
+ }
+}
+
+elinside(p: Point, frac: real): int
+{
+ foc := mulf(focus, frac);
+ d := dist(p, center.add(foc)) + dist(p, center.sub(foc));
+ return (d < frac * real major);
+}
+
+elrect(frac: real): string
+{
+ inset := mulf(center, 1.-frac);
+ r := Rect(inset, center.mul(2).sub(inset));
+ return sys->sprint("%d %d %d %d", r.min.x, r.min.y, r.max.x, r.max.y);
+}
+
+mulf(p: Point, f: real): Point
+{
+ return (int (f * real p.x), int (f * real p.y));
+}
+
+dist(p, q: Point): real
+{
+ p = p.sub(q);
+ return hypot(real p.x, real p.y);
+}
+
+s2p(s: string): Point
+{
+ (nil, xy) := sys->tokenize(s, " ");
+ if (len xy != 2)
+ return (0, 0);
+ return (int hd xy, int hd tl xy);
+}
+
+tkc(msg: string): string
+{
+ ret := tk->cmd(top, msg);
+ if (ret != nil && ret[0] == '!')
+ sys->print("tk error? %s → %s\n", msg, ret);
+ return ret;
+}