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;}
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;
}