code: 9ferno

ref: 4f618bf2be8c5b8ee401db4e1787c4d41a7c59f8
dir: /appl/spree/clients/gather.b/

View raw version
implement Gather;

include "sys.m";
	sys: Sys;
include "draw.m";
	draw: Draw;
	Point, Rect, Display, Image, Font: import draw;
include "tk.m";
	tk: Tk;
include "tkclient.m";
	tkclient: Tkclient;
include "commandline.m";
	commandline: Commandline;
	Cmdline: import commandline;
include "sh.m";

Gather: module {
	init: fn(ctxt: ref Draw->Context, argv: list of string);
};

CLIENTDIR: con "/dis/spree/clients";

drawctxt: ref Draw->Context;
cliquefd: ref Sys->FD;
stderr: ref Sys->FD;

mnt, dir: string;

init(ctxt: ref Draw->Context, argv: list of string)
{
	sys = load Sys Sys->PATH;
	stderr = sys->fildes(2);
	draw = load Draw Draw->PATH;
	tk = load Tk Tk->PATH;
	tkclient = load Tkclient Tkclient->PATH;
	if (tkclient == nil) {
		sys->fprint(stderr, "gather: cannot load %s: %r\n", Tkclient->PATH);
		raise "fail:bad module";
	}
	tkclient->init();
	commandline = load Commandline Commandline->PATH;
	if(commandline == nil) {
		sys->fprint(stderr, "gather: cannot load %s: %r\n", Commandline->PATH);
		raise "fail:bad module";
	}
	commandline->init();
	drawctxt = ctxt;
	cliquefd = sys->fildes(0);

	if (len argv >= 3) {
		mnt = hd tl argv;
		dir = hd tl tl argv;
	} else
		sys->fprint(stderr, "gather: expected mnt, dir args\n");
	client1();
}

client1()
{
	(win, winctl) := tkclient->toplevel(drawctxt, nil, "Gathering", Tkclient->Appl);
	ech := chan of string;
	tk->namechan(win, ech, "e");
	(chat, chatevent) := Cmdline.new(win, ".chat", nil);
	updatech := chan of string;
	spawn readproc(updatech);

	cmd(win, "button .b -text Start -command {send e start}");
	cmd(win, "pack .b -side top -anchor w");
	cmd(win, "pack .chat -fill both -expand 1");
	cmd(win, "pack propagate . 0");
	cmd(win, "update");
	tkclient->onscreen(win, nil);
	tkclient->startinput(win, "kbd"::"ptr"::nil);
	for (;;) alt {
	s := <-win.ctxt.kbd =>
		tk->keyboard(win, s);
	s := <-win.ctxt.ptr =>
		tk->pointer(win, *s);
	s := <-win.ctxt.ctl or
	s = <-win.wreq or
	s = <-winctl =>
		tkclient->wmctl(win, s);
	line := <-updatech =>
		(n, toks) := sys->tokenize(line, " ");
		if (toks == nil)
			continue;
		case hd toks {
		"clienttype" =>
			chat.addtext("starting " + hd tl toks + " session...\n");
			cmd(win, "update");
			path := CLIENTDIR + "/" + hd tl toks + ".dis";
			mod := load Command path;
			if (mod == nil) {
				chat.addtext(sys->sprint("could not load %s: %r\n", path));
				chat.addtext("bye bye\n");
				cliquefd = nil;
			} else {
				win = nil;
				chat = nil;
				startclient(mod, hd tl toks :: mnt :: dir :: tl tl toks);
				exit;
			}
		"chat" =>
			chat.addtext(hd tl toks + ": " + concat(tl tl toks) + "\n");
		"title" =>
			tkclient->settitle(win, "Gather " + concat(tl toks));
		"join" or
		"leave" or
		"watch" or
		"unwatch" =>
			chat.addtext(line + "\n");
		* =>
			chat.addtext("unknown update: " + line + "\n");
		}
		cmd(win, "update");
	c := <-chatevent =>
		lines := chat.event(c);
		for (; lines != nil; lines = tl lines)
			cliquecmd("chat " + hd lines, chat);
	c := <-ech =>
		cliquecmd(c, chat);
	}
}

cliquecmd(s: string, chat: ref Cmdline)
{
	if (sys->fprint(cliquefd, "%s", s) == -1) {
		chat.addtext(sys->sprint("command failed: %r\n"));
		cmd(chat.top, "update");
	}
}

prefixed(s: string, prefix: string): int
{
	return len s >= len prefix && s[0:len prefix] == prefix;
}

readproc(updatech: chan of string)
{
	buf := array[Sys->ATOMICIO] of byte;
	while ((n := sys->read(cliquefd, buf, Sys->ATOMICIO)) > 0) {
		(nil, lines) := sys->tokenize(string buf[0:n], "\n");
		for (; lines != nil; lines = tl lines) {
			updatech <-= hd lines;
			if (prefixed(hd lines, "clienttype"))
				exit;
		}
	}
	updatech <-= nil;
}

startclient(mod: Command, argv: list of string)
{
	{
		mod->init(drawctxt, argv);
	} exception e {
	"*" =>
		sys->print("client %s broken: %s\n", hd argv, e);
	}
}

cmd(win: ref Tk->Toplevel, s: string): string
{
	r := tk->cmd(win, s);
	if(len r > 0 && r[0] == '!')
		sys->print("error executing '%s': %s\n", s, r[1:]);
	return r;
}

concat(l: list of string): string
{
	if (l == nil)
		return nil;
	s := hd l;
	for (l = tl l; l != nil; l = tl l)
		s += " " + hd l;
	return s;
}