Plan 9 from Bell Labs’s /usr/web/sources/contrib/nemo/octopus/port/live/wpanel.b

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


implement Wpanel;
include "mods.m";
mods, debug, win, tree: import dat;

Menu: import menus;
Cpointer, Tagwid, Taght, Inset, setcursor, drawtag,
getfont, readsnarf, cookclick, Arrow, Drag: import gui;

# Panel polymorphism support.


#  Panels are only drawn due to user interaction or updates made. All that
#  from the o/mero tree. This means that write and update (data/ctl) routines should never draw.
#  They just set the Pdraw flag.
#  Once the whole tree is updated, we call draw for those that must
#  be redrawn.

#  Also, mouse and keyboard handlers are called from within the tree, after
#  synchronizing, thus it's safe to operate on the panel.
#  However:
#  Containers do not keep the panel busy in the mean time, thus they may call
#  tree operations but may have races if not done with care.
#  Atoms keep the panel busy while doing user I/O, thus they may NOT call
#  tree operations (directly) but should not have races.

#  Doing this right yet not creating one process per panel is hard.
#  Perhaps we should have gone for Xfids.

panels:	list of Pimpl;
cc:		chan of (ref Panel, string);
init(d: Livedat, dir: string)
{
	dat = d;
	initmods();
	fd := open(dir, OREAD);
	if (fd == nil)
		error(sprint("can't open %s: %r", dir));
	(dirs, n) := readdir->readall(fd, Readdir->NONE);
	if (n < 0)
		error(sprint("reading %s: %r", dir));
	panels = nil;
	for (i := 0; i < n; i++){
		nm := dirs[i].name;
		l := len nm;
		if (l > 7 && nm[0:3] == "owp" && nm[l-4:] == ".dis"){
			path := dir + "/" + nm;
			if (debug['P'])
				fprint(stderr, "loading %s\n", path);
			m := load Pimpl path;
			if (m == nil)
				fprint(stderr, "%s: %r\n", path);
			else if ((e := m->init(d)) != nil)
				fprint(stderr, "%s: %s\n", path, e);
			else
				panels = m :: panels;
		}
	}
	if (debug['d'] || debug['P'])
		fprint(stderr, "%d panels loaded\n", len panels);
}

screenname(s: string): int
{
	for (i := 0; i < len s; i++)
		if (s[i] == ':')
			return 0;
	return 1;
}

addchild(fp: ref Panel, p: ref Panel)
{
	n := len fp.child;
	child := array[n + 1] of ref Panel;
	child[0:] = fp.child;
	child[n] = p;
	fp.child = child;
	p.parent = fp;
}


nullpanel: Panel;
Panel.new(n: string, fp: ref Panel): ref Panel
{
	pname := n;
	if (screenname(n))
		pname = "row:" + n;
	for (l := panels; l != nil; l = tl l){
		m := hd l;
		for (prefs := m->prefixes; prefs != nil; prefs = tl prefs){
			pl := len hd prefs;
			if (len pname > pl && pname[0:pl]  == hd prefs){
				p := ref nullpanel;
				p.impl = m;
				p.name = n;
				if (fp != nil){
					addchild(fp, p);
					p.path = names->rooted(fp.path, n);
					if(fp.flags&Ptag)
						p.depth = fp.depth + 1;
					else
						p.depth = fp.depth;
				}
				p.rowcol = Qatom;
				p.init();
				p.flags |= Predraw;
				if (debug['P'])
					fprint(stderr, "o/live: new %s\n", p.path);
				return p;
			}
		}
	}
	return nil;
}

Panel.text(p: self ref Panel): string
{
	flags := "-------------";
	if (p.flags&Phide) flags[0] = 'h';
	if (p.flags&Playout) flags[1] = 'l';
	if (p.flags&Pedit) flags[2] = 'e';
	if (p.flags&Ptag) flags[3] = 't';
	if (p.flags&Pmore) flags[4] = 'm';
	if (p.flags&Pdirties) flags[5] = 'd';
	if (p.flags&Pdirty) flags[6] = 'd';
	if (p.flags&Pline) flags[7] = '1';
	if (p.flags&Ptbl) flags[8] = 't';
	if (p.flags&Psync) flags[9] = 'f';
	if (p.flags&Predraw) flags[10] = 'r';
	if (p.flags&Pdead) flags[11]='!';
	if (p.flags&Pbusy) flags[12] = 'b';
	s := sprint("%s row=%d, flags %s %d childs %d shown", p.name, p.rowcol, flags, len p.child, p.nshown);
	return s;
}

escape(s: string): string
{
	for (i := 0; i < len s -1; i++)
		if (s[i] == '\n')
			s[i] = 1;
	if (s[len s -1] != '\n' )
		s[len s] = '\n';
	return s;
}

unescape(s: string): string
{
	for (i := 0; i < len s; i++)
		if (s[i] == 1)
			s[i] = '\n';
	return s;
}

pfsctl(p: ref Panel, s: string)
{
	fname := p.path + "/ctl";
	fd := open(fname, OWRITE);
	if (fd != nil){
		if (debug['E'])
			fprint(stderr, "fsctl: %s: %s\n", p.path, s);
		seek(fd, big 0, 2);
		data := array of byte escape(s);
		if (write(fd, data, len data) != len data && debug['E'])
			fprint(stderr, "fsctl: write: %r\n");
	} else if (debug['E'])
		fprint(stderr, "fsctl: open %s/ctl: %r\n", p.path);
}

fsctlproc()
{
	for(;;){
		(p, s) := <-cc;
		pfsctl(p, s);
	}
}

Panel.fsctl(p: self ref Panel, s: string, async: int)
{
	if (!async){
		pfsctl(p, s);
		return;
	}
	if (cc == nil){
		cc = chan[16] of (ref Panel, string);
		spawn fsctlproc();
	}
	cc <-= (p, s);
}

Panel.init(p: self ref Panel)
{
	if (p.impl == nil)
		panic("nil panel implementation");
	p.impl->pinit(p);
	p.flags |= Predraw;
}

Panel.term(p: self ref Panel)
{
	p.flags |= Pdead;	# safety
	if (debug['P'])
		fprint(stderr, "o/live: gone %s\n", p.path);
	p.impl->pterm(p);
}

# Process control operation, during update.
# Data is guaranteed to be updated. This is the update for ctl.
Panel.ctl(p: self ref Panel, s: string)
{
	p.impl->pctl(p, s);
}

# update panel data, during update
Panel.update(p: self ref Panel, d: array of byte)
{
	p.impl->pupdate(p, d);
}

# For panels that require incremental changes.
# Currently text ins/del events only.
Panel.event(p: self ref Panel, s: string)
{
	p.impl->pevent(p, s);
}

Panel.draw(p: self ref Panel)
{
	if (p.flags&Pshown)
		p.impl->pdraw(p);
}

Panel.mouse(p: self ref Panel, m: ref Cpointer, cm: chan of ref Cpointer)
{
	p.impl->pmouse(p, m, cm);
}

Panel.kbd(p: self ref Panel, r: int)
{
	p.impl->pkbd(p, r);
}

Panel.sync(p: self ref Panel)
{
	p.impl->psync(p);
}

intag(p: ref Panel, xy: Point): int
{
	ht := Taght;
	if (p.flags&Pmore)
		ht *= 2;
	r := Rect(p.rect.min, (p.rect.min.x+Tagwid, p.rect.min.y+Taght));
	return r.contains(xy);
}

nth(l: list of string, n: int): string
{
	for (i := 0; l != nil && i < n; i++)
		l = tl l;
	if (l != nil)
		return hd l;
	return nil;
}


Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.