ref: ccd7f9f1597e56d121e1d05cf6af86f688ef2c48
dir: /appl/wm/stopwatch.b/
implement WmStopWatch; include "sys.m"; sys: Sys; include "draw.m"; draw: Draw; include "tk.m"; tk: Tk; include "tkclient.m"; tkclient: Tkclient; include "daytime.m"; daytime: Daytime; WmStopWatch: module { init: fn(ctxt: ref Draw->Context, argv: list of string); }; t: ref Tk->Toplevel; cmd: chan of string; tpid: int; hr, min, sec: int; sw_cfg := array[] of { "frame .f", "button .f.b1 -text Start -command {send cmd start}", "button .f.b2 -text Stop -command {send cmd stop}", "button .f.b3 -text Reset -command {send cmd reset}", "pack .f.b1 .f.b2 .f.b3 -side left -fill x -expand 1", "frame .ft", "label .ft.d -label {0:00:00}", "pack .ft.d -expand 1", "frame .fs1", "button .fs1.s -text Time1 -command {send cmd s1}", "label .fs1.l -label {0:00:00}", "pack .fs1.s .fs1.l -side left -expand 1", "frame .fs2", "button .fs2.s -text Time2 -command {send cmd s2}", "label .fs2.l -label {0:00:00}", "pack .fs2.s .fs2.l -side left -expand 1", "frame .fs3", "button .fs3.s -text Time3 -command {send cmd s3}", "label .fs3.l -label {0:00:00}", "pack .fs3.s .fs3.l -side left -expand 1", "pack .Wm_t -fill x", "pack .f .ft .fs1 .fs2 .fs3", "pack propagate . 0", "update", }; init(ctxt: ref Draw->Context, nil: list of string) { sys = load Sys Sys->PATH; if (ctxt == nil) { sys->fprint(sys->fildes(2), "stopwatch: no window context\n"); raise "fail:bad context"; } draw = load Draw Draw->PATH; tk = load Tk Tk->PATH; tkclient= load Tkclient Tkclient->PATH; daytime = load Daytime Daytime->PATH; if(draw==nil || tk==nil || tkclient==nil || daytime==nil){ sys->fprint(sys->fildes(2), "stopwatch: couldn't load modules\n"); return; } tkclient->init(); menubut := chan of string; (t, menubut) = tkclient->toplevel(ctxt, "-borderwidth 2 -relief raised", "StopWatch", Tkclient->Appl); hr = 0; min = 0; sec = 0; cmd = chan of string; tk->namechan(t, cmd, "cmd"); for (c:=0; c<len sw_cfg; c++) tk->cmd(t, sw_cfg[c]); tkclient->onscreen(t, nil); tkclient->startinput(t, "kbd"::"ptr"::nil); tpid = 0; # keep the timerloop in a separate thread, # so that wm events don't hold up the ticker # i.e., titlebar click&hold would otherwise # 'pause' the timer since the tick would not # be processed. pid := chan of int; spawn timerloop(pid); looppid := <- pid; for(;;) alt { s := <-t.ctxt.kbd => tk->keyboard(t, s); s := <-t.ctxt.ptr => tk->pointer(t, *s); s := <-t.ctxt.ctl or s = <-t.wreq => tkclient->wmctl(t, s); menu := <-menubut => if(menu == "exit") { if(tpid) kill(tpid); kill(looppid); return; } tkclient->wmctl(t, menu); } } timerloop(pid: chan of int) { pid <- = sys->pctl(0, nil); tick := chan of int; s: string; for(;;) alt { c := <-cmd => if(c == "stop"){ if(tpid != 0){ kill(tpid); tpid = 0; } } else if(c == "reset"){ hr = min = sec = 0; s = sys->sprint("%d:%2.2d:%2.2d", hr, min, sec); tk->cmd(t, ".ft.d configure -label {"+s+"};update"); } else if(c == "start"){ if(tpid == 0){ spawn timer(tick); tpid = <- tick; } } else if(c == "s1" || c == "s2" || c == "s3"){ s = sys->sprint("%d:%2.2d:%2.2d", hr, min, sec); tk->cmd(t, ".f"+c+".l configure -label {"+s+"};update"); } <-tick => sec++; if(sec>=60){ sec = 0; min++; if(min>=60){ min = 0; hr++; } } s = sys->sprint("%d:%2.2d:%2.2d", hr, min, sec); tk->cmd(t, ".ft.d configure -label {"+s+"};update"); } } timer(c: chan of int) { pid := sys->pctl(0, nil); for(;;) { c <-= pid; sys->sleep(1000); } } kill(pid: int) { fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE); if(fd != nil) sys->fprint(fd, "kill"); }