forsyth20121205-174063a80cc13 years agoHistory
590 lines · 13.4 KB · Brainfuck
RawDownload
implement Wmlib; ## Copyright © 2003 Vita Nuova Holdings Limited# # basic window manager functionality, used by# tkclient and wmclient to create more usable functionality. include "sys.m";	sys: Sys;include "draw.m";	draw: Draw;	Display, Image, Screen, Rect, Point, Pointer, Wmcontext, Context: import draw;include "wmsrv.m";include "wmlib.m"; Client: adt{	ptrpid:	int;	kbdpid:	int;	ctlpid:	int;	req:		chan of (array of byte, Sys->Rwrite);	dir:		string;	ctlfd:		ref Sys->FD;	winfd:	ref Sys->FD;}; DEVWM: con "/mnt/wm";Ptrsize: con 1+4*12;		# 'm' plus 4 12-byte decimal integers kbdstarted: int;ptrstarted: int;wptr: chan of Point;		# set mouse position (only if we've opened /dev/pointer directly)cswitch: chan of (string, int, chan of string);	# switch cursor images (as for wptr) init(){	sys = load Sys Sys->PATH;	draw = load Draw Draw->PATH;} #	(_screen, dispi) := ctxt.display.getwindow("/dev/winname", nil, nil, 1); XXX corrupts heap... fix it! makedrawcontext(): ref Draw->Context{	display := Display.allocate(nil);	if(display == nil){		sys->fprint(sys->fildes(2), "wmlib: can't allocate Display: %r\n");		raise "fail:no display";	}	return ref Draw->Context(display, nil, nil);} importdrawcontext(devdraw, mntwm: string): (ref Draw->Context, string){	if(mntwm == nil)		mntwm = "/mnt/wm"; 	display := Display.allocate(devdraw);	if(display == nil)		return (nil, sys->sprint("cannot allocate display: %r"));	(ok, nil) := sys->stat(mntwm + "/clone");	if(ok == -1)		return (nil, "cannot find wm namespace");	wc := chan of (ref Draw->Context, string);	spawn wmproxy(display, mntwm, wc);	return <-wc;} # XXX we have no way of knowing when this process should go away...# perhaps a Draw->Context should hold a file descriptor# so that we do.wmproxy(display: ref Display, dir: string, wc: chan of (ref Draw->Context, string)){	wmsrv := load Wmsrv Wmsrv->PATH;	if(wmsrv == nil){		wc <-= (nil, sys->sprint("cannot load %s: %r", Wmsrv->PATH));		return;	}	sys->pctl(Sys->NEWFD, 1 :: 2 :: nil); 	(wm, join, req) := wmsrv->init();	if(wm == nil){		wc <-= (nil, sys->sprint("%r"));		return;	}	wc <-= (ref Draw->Context(display, nil, wm), nil); 	clients: array of ref Client;	for(;;) alt{	(sc, rc) := <-join =>		sync := chan of (ref Client, string);		spawn clientproc(display, sc, dir, sync);		(c, err) := <-sync;		rc <-= err;		if(c != nil){			if(sc.id >= len clients)				clients = (array[sc.id + 1] of ref Client)[0:] = clients;			clients[sc.id] = c;		}	(sc, data, rc) := <-req =>		clients[sc.id].req <-= (data, rc);		if(rc == nil)			clients[sc.id] = nil;	}} zclient: Client;clientproc(display: ref Display, sc: ref Wmsrv->Client, dir: string, rc: chan of (ref Client, string)){	ctlfd := sys->open(dir + "/clone", Sys->ORDWR);	if(ctlfd == nil){		rc <-= (nil, sys->sprint("cannot open %s/clone: %r", dir));		return;	}	buf := array[20] of byte;	n := sys->read(ctlfd, buf, len buf);	if(n <= 0){		rc <-= (nil, "cannot read ctl id");		return;	}	sys->fprint(ctlfd, "fixedorigin");	dir += "/" + string buf[0:n];	c := ref zclient;	c.req = chan of (array of byte, Sys->Rwrite);	c.dir = dir;	c.ctlfd = ctlfd;	if ((c.winfd = sys->open(dir + "/winname", Sys->OREAD)) == nil){		rc <-= (nil, sys->sprint("cannot open %s/winname: %r", dir));		return;	}	rc <-= (c, nil); 	pidc := chan of int;	spawn ctlproc(pidc, ctlfd, sc.ctl);	c.ctlpid = <-pidc;	for(;;) {		(data, drc) := <-c.req;		if(drc == nil)			break;		err := handlerequest(display, c, sc, data);		n = len data;		if(err != nil)			n = -1;		alt{		drc <-= (n, err) =>;		* =>;		}	}	sc.stop <-= 1;	kill(c.kbdpid, "kill");	kill(c.ptrpid, "kill");	kill(c.ctlpid, "kill");	c.ctlfd = nil;	c.winfd = nil;} handlerequest(display: ref Display, c: ref Client, sc: ref Wmsrv->Client, data: array of byte): string{	req := string data;	if(req == nil)		return nil;	(w, e) := qword(req, 0);	case w {	"start" =>		(w, e) = qword(req, e);		case w {		"ptr" or		"mouse" =>			if(c.ptrpid == -1)				return "already started";			fd := sys->open(c.dir + "/pointer", Sys->OREAD);			if(fd == nil)				return sys->sprint("cannot open %s: %r", c.dir + "/pointer");			sync := chan of int;			spawn ptrproc(sync, fd, sc.ptr);			c.ptrpid = <-sync;			return nil;		"kbd" =>			if(c.kbdpid == -1)				return "already started";			sync := chan of (int, string);			spawn kbdproc(sync, c.dir + "/keyboard", sc.kbd);			(pid, err) := <-sync;			c.kbdpid = pid;			return err;		}	} 	if(sys->write(c.ctlfd, data, len data) == -1)		return sys->sprint("%r");	if(req[0] == '!'){		buf := array[100] of byte;		n := sys->read(c.winfd, buf, len buf);		if(n <= 0)			return sys->sprint("read winname: %r");		name := string buf[0:n];		# XXX this is the dodgy bit...		i := display.namedimage(name);		if(i == nil)			return sys->sprint("cannot get image %#q: %r", name);		s := Screen.allocate(i, display.white, 0);		i = s.newwindow(i.r, Draw->Refnone, Draw->Nofill);		rc := chan of int;		sc.images <-= (nil, i, rc);		if(<-rc == -1)			return "image request already in progress";	}	return nil;} connect(ctxt: ref Context): ref Wmcontext{	# don't automatically make a new Draw->Context, 'cos the	# client should be aware that there's no wm so multiple	# windows won't work correctly.	# ... unless there's an exported wm available, of course!	if(ctxt == nil){		sys->fprint(sys->fildes(2), "wmlib: no draw context\n");		raise "fail:error";	}	if(ctxt.wm == nil){		wm := ref Wmcontext(			chan of int,			chan of ref Draw->Pointer,			chan of string,			nil,	# unused			chan of ref Image,			nil,			ctxt		);		return wm;	}	fd := sys->open("/chan/wmctl", Sys->ORDWR);	if(fd == nil){		sys->fprint(sys->fildes(2), "wmlib: cannot open /chan/wmctl: %r\n");		raise "fail:error";	}	buf := array[32] of byte;	n := sys->read(fd, buf, len buf);	if(n < 0){		sys->fprint(sys->fildes(2), "wmlib: cannot get window token: %r\n");		raise "fail:error";	}	reply := chan of (string, ref Wmcontext);	ctxt.wm <-= (string buf[0:n], reply);	(err, wm) := <-reply;	if(err != nil){		sys->fprint(sys->fildes(2), "wmlib: cannot connect: %s\n", err);		raise "fail:" + err;	}	wm.connfd = fd;	wm.ctxt = ctxt;	return wm;} startinput(wm: ref Wmcontext, devs: list of string): string{	for(; devs != nil; devs = tl devs)		wmctl(wm, "start " + hd devs);	return nil;} reshape(wm: ref Wmcontext, name: string, r: Draw->Rect, i: ref Draw->Image, how: string): ref Draw->Image{	if(name == nil)		return nil;	(nil, ni, err) := wmctl(wm, sys->sprint("!reshape %s -1 %d %d %d %d %s", name, r.min.x, r.min.y, r.max.x, r.max.y, how));	if(err == nil)		return ni;	return i;} ## wmctl implements the default window behaviour#wmctl(wm: ref Wmcontext, request: string): (string, ref Image, string){	(w, e) := qword(request, 0);	case w {	"exit" =>		kill(sys->pctl(0, nil), "killgrp");		exit;	* =>		if(wm.connfd != nil){			# standard form for requests: if request starts with '!',			# then the next word gives the tag of the window that the			# request applies to, and a new image is provided.			if(sys->fprint(wm.connfd, "%s", request) == -1){				sys->fprint(sys->fildes(2), "wmlib: wm request '%s' failed\n", request);				return (nil, nil, sys->sprint("%r"));			}			if(request[0] == '!'){				i := <-wm.images;				if(i == nil)					i = <-wm.images;				return (qword(request, e).t0, i, nil);			}			return (nil, nil, nil);		}		# requests we can handle ourselves, if we have to.		case w{		"start" =>			(w, e) = qword(request, e);			case w{			"ptr" or			"mouse" =>				if(!ptrstarted){					fd := sys->open("/dev/pointer", Sys->ORDWR);					if(fd != nil)						wptr = chan of Point;					else						fd = sys->open("/dev/pointer", Sys->OREAD);					if(fd == nil)						return (nil, nil, sys->sprint("cannot open /dev/pointer: %r"));					cfd := sys->open("/dev/cursor", Sys->OWRITE);					if(cfd != nil)						cswitch = chan of (string, int, chan of string);					spawn wptrproc(fd, cfd);					sync := chan of int;					spawn ptrproc(sync, fd, wm.ptr);					<-sync;					ptrstarted = 1;				}			"kbd" =>				if(!kbdstarted){					sync := chan of (int, string);					spawn kbdproc(sync, "/dev/keyboard", wm.kbd);					(nil, err) := <-sync;					if(err != nil)						return (nil, nil, err);					spawn sendreq(wm.ctl, "haskbdfocus 1");					kbdstarted = 1;				}			* =>				return (nil, nil, "unknown input source");			}			return (nil, nil, nil);		"ptr" =>			if(wptr == nil)				return (nil, nil, "cannot change mouse position");			p: Point;			(w, e) = qword(request, e);			p.x = int w;			(w, e) = qword(request, e);			p.y = int w;			wptr <-= p;			return (nil, nil, nil);		"cursor" =>			if(cswitch == nil)				return (nil, nil, "cannot switch cursor");			cswitch <-= (request, e, reply := chan of string);			return (nil, nil, <-reply);		* =>			return (nil, nil, "unknown wmctl request");		}	}} sendreq(c: chan of string, s: string){	c <-= s;} ctlproc(sync: chan of int, fd: ref Sys->FD, ctl: chan of string){	sync <-= sys->pctl(0, nil);	buf := array[4096] of byte;	while((n := sys->read(fd, buf, len buf)) > 0)		ctl <-= string buf[0:n];} kbdproc(sync: chan of (int, string), f: string, keys: chan of int){	sys->pctl(Sys->NEWFD, nil);	fd := sys->open(f, Sys->OREAD);	if(fd == nil){		sync <-= (-1, sys->sprint("cannot open /dev/keyboard: %r"));		return;	}	sync <-= (sys->pctl(0, nil), nil);	buf := array[12] of byte;	while((n := sys->read(fd, buf, len buf)) > 0){		s := string buf[0:n];		for(j := 0; j < len s; j++)			keys <-= int s[j];	}} wptrproc(pfd, cfd: ref Sys->FD){	if(wptr == nil && cswitch == nil)		return;	if(wptr == nil)		wptr = chan of Point;	if(cswitch == nil)		cswitch = chan of (string, int, chan of string);	for(;;)alt{	p := <-wptr =>		sys->fprint(pfd, "m%11d %11d", p.x, p.y);	(c, start, reply) := <-cswitch =>		buf: array of byte;		if(start == len c){			buf = array[0] of byte;		}else{			hot, size: Point;			(w, e) := qword(c, start);			hot.x = int w;			(w, e) = qword(c, e);			hot.y = int w;			(w, e) = qword(c, e);			size.x = int w;			(w, e) = qword(c, e);			size.y = int w;			((d0, d1), nil) := splitqword(c, e);			nb := size.x/8*size.y;			if(d1 - d0 != nb * 2){				reply <-= "inconsistent cursor image data";				break;			}			buf = array[4*4 + nb] of byte;			bplong(buf, 0*4, hot.x);			bplong(buf, 1*4, hot.y);			bplong(buf, 2*4, size.x);			bplong(buf, 3*4, size.y);			j := 4*4;			for(i := d0; i < d1; i += 2)				buf[j++] = byte ((hexc(c[i]) << 4) | hexc(c[i+1]));		}		if(sys->write(cfd, buf, len buf) != len buf)			reply <-= sys->sprint("%r");		else			reply <-= nil;	}} hexc(c: int): int{	if(c >= '0' && c <= '9')		return c - '0';	if(c >= 'a' && c <= 'f')		return c - 'a' + 10;	if(c >= 'A' && c <= 'F')		return c - 'A' + 10;	return 0;} bplong(d: array of byte, o: int, x: int){	d[o] = byte x;	d[o+1] = byte (x >> 8);	d[o+2] = byte (x >> 16);	d[o+3] = byte (x >> 24);} ptrproc(sync: chan of int, fd: ref Sys->FD, ptr: chan of ref Draw->Pointer){	sync <-= sys->pctl(0, nil); 	b:= array[Ptrsize] of byte;	while(sys->read(fd, b, len b) > 0){		p := bytes2ptr(b);		if(p != nil)			ptr <-= p;	}} bytes2ptr(b: array of byte): ref Pointer{	if(len b < Ptrsize || int b[0] != 'm')		return nil;	x := int string b[1:13];	y := int string b[13:25];	but := int string b[25:37];	msec := int string b[37:49];	return ref Pointer (but, (x, y), msec);} snarfbuf: string;		# at least we get *something* when there's no wm. snarfget(): string{	fd := sys->open("/chan/snarf", sys->OREAD);	if(fd == nil)		return snarfbuf; 	buf := array[8192] of byte;	nr := 0;	while ((n := sys->read(fd, buf[nr:], len buf - nr)) > 0) {		nr += n;		if (nr == len buf) {			nbuf := array[len buf * 2] of byte;			nbuf[0:] = buf;			buf = nbuf;		}	}	return string buf[0:nr];} snarfput(buf: string)

Lines 1–500 of 590.