implement Dialupchat; # # Copyright © 2001 Vita Nuova Holdings Limited. All rights reserved. # include "sys.m"; sys: Sys; include "draw.m"; draw: Draw; Point, Rect: import draw; include "tk.m"; tk: Tk; include "wmlib.m"; wmlib: Wmlib; include "translate.m"; translate: Translate; Dict: import translate; dict: ref Dict; Dialupchat: module { init: fn(nil: ref Draw->Context, nil: list of string); }; # Dimension constant for ISP Connect window WIDTH: con 300; HEIGHT: con 58; LightGreen: con "#00FF80"; # colour for successful blob Blobx: con 8; Gapx: con 4; BARW: con (Blobx+Gapx)*10; # Progress bar width BARH: con 18; # Progress bar height DIALQUANTA : con 1000; ICONQUANTA : con 5000; pppquanta := DIALQUANTA; Maxstep: con 9; init(ctxt: ref Draw->Context, args: list of string) { sys = load Sys Sys->PATH; draw = load Draw Draw->PATH; tk = load Tk Tk->PATH; wmlib = load Wmlib Wmlib->PATH; wmlib->init(); translate = load Translate Translate->PATH; if(translate != nil) { translate->init(); dictname := translate->mkdictname("", "pppchat"); dicterr: string; (dict, dicterr) = translate->opendict(dictname); if(dicterr != nil) sys->fprint(sys->fildes(2), "pppchat: can't open %s: %s\n", dictname, dicterr); }else sys->fprint(sys->fildes(2), "pppchat: can't load %s: %r\n", Translate->PATH); tkargs: string; if(args != nil) { tkargs = hd args; args = tl args; } sys->pctl(Sys->NEWPGRP, nil); pppfd := sys->open("/chan/pppctl", Sys->ORDWR); if(pppfd == nil) error(sys->sprint("can't open /chan/pppctl: %r")); (t, wmctl) := wmlib->titlebar(ctxt.screen, tkargs, X("Dialup Connection"), Wmlib->Hide); cmd := chan of string; tk->namechan(t, cmd, "cmd"); pb := Progressbar.mk(t, ".f.prog.c", (BARW, BARH)); config_win := array[] of { "frame .f", "frame .f.prog", "frame .f.b", pb.tkcreate(), "pack .f.prog.c -pady 6 -side top", "label .f.stat -fg blue -text {"+X("Initialising connection...")+"}", "pack .f.stat -side top -fill x -expand 1 -anchor n", "pack .f -side top -expand 1 -padx 5 -pady 3 -fill both -anchor w", "pack .f.prog -side top -expand 1 -fill x", "button .f.b.done -text {"+X("Cancel")+"} -command {send cmd cancel}", "pack .f.b.done -side right -padx 1 -pady 1 -anchor s", "button .f.b.retry -text {"+X("Retry")+"} -command {send cmd retry} -state disabled", "pack .f.b.retry -side left -padx 1 -pady 1 -anchor s", "pack .f.b -side top -expand 1 -fill x", "pack propagate . 0", "update", }; for(i := 0; i < len config_win; i++) tkcmd(t, config_win[i]); connected := 0; winmapped := 1; timecount := 0; xmin := 0; x := 0; turn := 0; pppquanta = DIALQUANTA; ticks := chan of int; spawn ppptimer(ticks); statuslines := chan of (string, string); pids := chan of int; spawn ctlreader(pppfd, pids, statuslines); ctlpid := <-pids; Work: for(;;) alt { s := <-wmctl => if(s == "exit") s = "task"; if(s == "task"){ spawn wmlib->titlectl(t, s); continue; } wmlib->titlectl(t, s); press := <-cmd => case press { "cancel" or "disconnect" => tkcmd(t, sys->sprint(".f.stat configure -text '%s", X("Disconnecting"))); tkcmd(t, "update"); if(sys->fprint(pppfd, "hangup") < 0){ err := sys->sprint("%r"); tkcmd(t, sys->sprint(".f.stat configure -text '%s: %s", X("Error disconnecting"), X(err))); sys->fprint(sys->fildes(2), "pppchat: can't disconnect: %s\n", err); } break Work; "retry" => if(sys->fprint(pppfd, "connect") < 0){ err := sys->sprint("%r"); } } <-ticks => ticks <-= 1; if(!connected){ if(pb != nil){ if((turn ^= 1) == 0) pb.setcolour("white"); else pb.setcolour(LightGreen); } tkcmd(t, "raise .; update"); } (status, err) := <-statuslines => if(status == nil){ status = "0 1 empty status"; if(err != nil) sys->print("pppchat: !%s\n", err); } else sys->print("pppchat: %s\n", status); (nf, flds) := sys->tokenize(status, " \t\n"); # for(i = 0; i < len status; i++) # if(status[i] == ' ' || status[i] == '\t') { # status = status[i+1:]; # break; # } if(nf < 3) break; step := int hd flds; flds = tl flds; nstep := int hd flds; flds = tl flds; if(step < 0) raise "pppchat: bad step"; case hd flds { "error:" => tkcmd(t, ".f.stat configure -fg red -text '"+X(status)); tkcmd(t, ".f.b.retry configure -state normal"); tkcmd(t, "update"); wmlib->unhide(); winmapped = 1; pb.stepto(step, "red"); #break Work; * => pb.setcolour(LightGreen); pb.stepto(step, LightGreen); } turn = 0; statusmsg := X(status); tkcmd(t, ".f.stat configure -text '"+statusmsg); tkcmd(t, "raise .; update"); case hd flds { "up" or "done" => if(!connected){ connected = 1; } pppquanta = ICONQUANTA; # display connection speed if(tl flds != nil) tkcmd(t, ".f.stat configure -text {"+statusmsg+" "+"SPEED"+" hd tl flds}"); else tkcmd(t, ".f.stat configure -text {"+statusmsg+"}"); tkcmd(t, ".f.b.done configure -text Disconnect -command 'send cmd disconnect"); tkcmd(t, "update"); sys->sleep(2000); tkcmd(t, "pack forget .f.prog; update"); spawn wmlib->titlectl(t, "task"); winmapped = 0; } tkcmd(t, "update"); } <-ticks; ticks <-= 0; # stop ppptimer kill(ctlpid); } ppptimer(ticks: chan of int) { do{ sys->sleep(pppquanta); ticks <-= 1; }while(<-ticks); } ctlreader(fd: ref Sys->FD, pidc: chan of int, lines: chan of (string, string)) { pidc <-= sys->pctl(0, nil); buf := array[128] of byte; while((n := sys->read(fd, buf, len buf)) > 0) lines <-= (string buf[0:n], nil); if(n < 0) lines <-= (nil, sys->sprint("%r")); else lines <-= (nil, nil); } Progressbar: adt { t: ref Tk->Toplevel; canvas: string; csize: Point; blobs: list of string; mk: fn(t: ref Tk->Toplevel, canvas: string, csize: Point): ref Progressbar; tkcreate: fn(pb: self ref Progressbar): string; setcolour: fn(pb: self ref Progressbar, c: string); stepto: fn(pb: self ref Progressbar, step: int, col: string); destroy: fn(pb: self ref Progressbar); }; Progressbar.mk(t: ref Tk->Toplevel, canvas: string, csize: Point): ref Progressbar { return ref Progressbar(t, canvas, csize, nil); } Progressbar.tkcreate(pb: self ref Progressbar): string { return sys->sprint("canvas %s -width %d -height %d", pb.canvas, pb.csize.x, pb.csize.y); } Progressbar.setcolour(pb: self ref Progressbar, colour: string) { if(pb.blobs != nil) tkcmd(pb.t, sys->sprint("%s itemconfigure %s -fill %s; update", pb.canvas, hd pb.blobs, colour)); } Progressbar.stepto(pb: self ref Progressbar, step: int, col: string) { for(nblob := len pb.blobs; nblob > step+1; nblob--){ tkcmd(pb.t, sys->sprint("%s delete %s", pb.canvas, hd pb.blobs)); pb.blobs = tl pb.blobs; } if(nblob == step+1) return; p := Point(step*(Blobx+Gapx), 0); r := Rect(p, p.add((Blobx, pb.csize.y-2))); pb.blobs = tkcmd(pb.t, sys->sprint("%s create rectangle %d %d %d %d -fill %s", pb.canvas, r.min.x,r.min.y, r.max.x,r.max.y, col)) :: pb.blobs; } Progressbar.destroy(pb: self ref Progressbar) { tk->cmd(pb.t, "destroy "+pb.canvas); # ignore errors } tkcmd(t: ref Tk->Toplevel, s: string): string { e := tk->cmd(t, s); if(e != nil && e[0] == '!') sys->print("pppchat: tk error: %s [%s]\n", e, s); return e; } kill(pid: int) { if(pid > 0 && (fd := sys->open("/prog/"+string pid+"/ctl", Sys->OWRITE)) != nil) sys->fprint(fd, "kill"); } error(s: string) { sys->fprint(sys->fildes(2), "pppchat: %s\n", s); raise "fail:error"; } X(s: string): string { if(dict != nil) return dict.xlate(s); return s; }