code: 9ferno

ref: 44ce0097b612a1fefd754065bdf8d9d2e5ef60c8
dir: /appl/wm/stopwatch.b/

View raw version
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");
}