ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/cmd/lego/clock.b/
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;
}