diff options
Diffstat (limited to 'appl/cmd/lego/clock.b')
| -rw-r--r-- | appl/cmd/lego/clock.b | 214 |
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; +} |
