code: 9ferno

ref: 670f4e90ee6028ffee399e5a96fb5e4cd72fc7bc
dir: /appl/wm/logwindow.b/

View raw version
implement Logwindow;

#
# Copyright © 1999 Vita Nuova Limited.  All rights reserved.
#

include "sys.m";
	sys: Sys;
	stderr: ref Sys->FD;
include "draw.m";
	draw: Draw;
include "tk.m";
	tk: Tk;
	cmd: import tk;
include "tkclient.m";
	tkclient: Tkclient;
include "arg.m";

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

cfg := array[] of {
	"frame .bf",
	"checkbutton .bf.scroll -text Scroll -variable scroll -command {send cmd scroll}",
	".bf.scroll select",
	"checkbutton .bf.popup -text {Pop up} -variable popup -command {send cmd popup}",
	".bf.popup select",
	"pack .bf.scroll .bf.popup -side left",
	"frame .t",
	"scrollbar .t.scroll -command {.t.t yview}",
	"text .t.t -height 7c -yscrollcommand {.t.scroll set}",
	"pack .t.scroll -side left -fill y",
	"pack .t.t -fill both -expand 1",
	"pack .Wm_t -fill x",
	"pack .bf -anchor w",
	"pack .t -fill both -expand 1",
	"pack propagate . 0",
};

eflag := 0;

badmodule(p: string)
{
	sys->fprint(stderr, "logwindow: cannot load %s: %r\n", p);
	raise "fail:bad module";
}

init(ctxt: ref Draw->Context, argv: list of string)
{
	sys = load Sys Sys->PATH;
	stderr = sys->fildes(2);

	tkclient = load Tkclient Tkclient->PATH;
	if (tkclient == nil)
		badmodule(Tkclient->PATH);
	tkclient->init();

	tk = load Tk Tk->PATH;
	if (tk == nil)
		badmodule(Tk->PATH);

	arg := load Arg Arg->PATH;
	if (arg == nil)
		badmodule(Arg->PATH);

	if (ctxt == nil) {
		sys->fprint(stderr, "logwindow: nil Draw->Context\n");
		raise "fail:no draw context";
	}
	gflag := 0;
	title := "Log Window";
	arg->init(argv);
	while ((opt := arg->opt()) != 0) {
		case opt {
		'e' =>
			eflag = 1;
		'g' =>
			gflag = 1;
		* =>
			sys->fprint(stderr, "usage: logwindow [-ge] [title]\n");
			raise "fail:usage";
		}
	}
	argv = arg->argv();
	if (argv != nil)
		title = hd argv;

	if (!gflag)
		sys->pctl(Sys->NEWPGRP, nil);

	(top, wmchan) := tkclient->toplevel(ctxt, "", title, Tkclient->Hide|Tkclient->Resize);
	if (top == nil) {
		sys->fprint(stderr, "logwindow: couldn't make window\n");
		raise "fail: no window";
	}
	cmd(top, ". unmap");

	for (c:=0; c<len cfg; c++)
		tk->cmd(top, cfg[c]);
	if ((err := tk->cmd(top, "variable lasterror")) != nil) {
		sys->fprint(stderr, "logwindow: tk error: %s\n", err);
		raise "fail: tk error";
	}

	logwin(sys->fildes(0), top, wmchan);
}

scrolling := 1;
popup := 1;

logwin(fd: ref Sys->FD, top: ref Tk->Toplevel, wmchan: chan of string)
{
	cmd := chan of string;
	tk->namechan(top, cmd, "cmd");
	raised := 0;
	ichan := chan of int;
	spawn inputmon(fd, top, ichan);
	tkclient->onscreen(top, nil);
	tkclient->startinput(top, "kbd"::"ptr"::nil);
	tkclient->wmctl(top, "task");
	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 = <-wmchan =>
		case s {
		"task" =>
			raised = 0;
		"untask" =>
			raised = 1;
		}
		tkclient->wmctl(top, s);
	e := <-ichan =>
		if (e == 0 && eflag) {
			tkclient->wmctl(top, "exit");
			exit;
		}
		if (!raised && popup)
			tkclient->wmctl(top, "untask");
	msg := <-cmd =>
		case msg {
		"scroll" =>
			scrolling = int tk->cmd(top, "variable scroll");
		"popup" =>
			popup = int tk->cmd(top, "variable popup");
		}
	}
}

inputmon(fd: ref Sys->FD, top: ref Tk->Toplevel, ichan: chan of int)
{
	buf := array[Sys->ATOMICIO] of byte;
	t := 0;
	while ((n := sys->read(fd, buf[t:], len buf-t)) > 0) {
		t += n;
		cl := 0;
		for (i := t - 1; i >= 0; i--) {
			(nil, cl, nil) = sys->byte2char(buf, i);
			if (cl > 0)
				break;
		}
		if (cl == 0)
			continue;
		logmsg(top, ichan, string buf[0:i+cl]);
		buf[0:] = buf[i+cl:t];
		t -= i + cl;
	}
	if (n < 0)
		logmsg(top, ichan, sys->sprint("Input error: %r\n"));
	else
		logmsg(top, ichan, "Got EOF\n");
	if (eflag)
		ichan <-= 0;
}

logmsg(top: ref Tk->Toplevel, ichan: chan of int, m: string)
{
	tk->cmd(top, ".t.t insert end '"+m);
	if (scrolling)
		tk->cmd(top, ".t.t see end");
	tk->cmd(top, "update");
	ichan <-= 1;
}