xref: /inferno-os/appl/cmd/rioimport.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1implement Rioimport;
2include "sys.m";
3	sys: Sys;
4include "draw.m";
5	draw: Draw;
6	Image, Point, Rect, Display, Screen: import draw;
7include "wmsrv.m";
8	wmsrv: Wmsrv;
9include "sh.m";
10	sh: Sh;
11include "string.m";
12	str: String;
13
14Rioimport: module{
15	init: fn(nil: ref Draw->Context, argv: list of string);
16};
17
18Client: adt{
19	ptrstarted:	int;
20	kbdstarted:	int;
21	state:		int;		# Hidden|Current
22	req:		chan of (array of byte, Sys->Rwrite);
23	resize:	chan of ref Riowin;
24	ptr:		chan of ref Draw->Pointer;
25	riowctl:	chan of (ref Riowin, int);
26	wins:	list of ref Riowin;
27	winfd:	ref Sys->FD;
28	sc: 		ref Wmsrv->Client;
29};
30
31Riowin: adt {
32	tag:		string;
33	img:		ref Image;
34	dir:		string;
35	state:	int;
36	ptrpid:	int;
37	kbdpid:	int;
38	ctlpid:	int;
39	ptrfd:	ref Sys->FD;
40	ctlfd:		ref Sys->FD;
41};
42
43Hidden, Current: con 1<<iota;
44Ptrsize: con 1+4*12;		# 'm' plus 4 12-byte decimal integers
45P9PATH: con "/n/local";
46Borderwidth: con 4;		# defined in /sys/include/draw.h
47
48display: ref Display;
49wsysseq := 0;
50screenr := Rect((0, 0), (640, 480));	# no way of getting this reliably from rio
51
52Minwinsize: con Point(100, 42);
53
54init(nil: ref Draw->Context, argv: list of string)
55{
56	sys = load Sys Sys->PATH;
57	draw = load Draw Draw->PATH;
58	sh = load Sh Sh->PATH;
59	sh->initialise();
60	str = load String String->PATH;
61	wmsrv = load Wmsrv Wmsrv->PATH;
62
63	wc := chan of (ref Draw->Context, string);
64	spawn rioproxy(wc);
65	(ctxt, err) := <-wc;
66	if(err != nil){
67		sys->fprint(sys->fildes(2), "rioimport: %s\n", err);
68		raise "fail:no display";
69	}
70	sh->run(ctxt, tl argv);
71}
72
73ebind(a, b: string, flag: int)
74{
75	if(sys->bind(a, b, flag) == -1){
76		sys->fprint(sys->fildes(2), "rioimport: cannot bind %q onto %q: %r\n", a, b);
77		raise "fail:error";
78	}
79}
80
81rioproxy(wc: chan of (ref Draw->Context, string))
82{
83	{
84		rioproxy1(wc);
85	} exception e {
86	"fail:*" =>
87		wc <-= (nil, e[5:]);
88	}
89}
90
91rioproxy1(wc: chan of (ref Draw->Context, string))
92{
93	sys->pctl(Sys->NEWFD, 0 :: 1 :: 2 :: nil);
94
95	ebind("#U*", P9PATH, Sys->MREPL);
96	display = Display.allocate(P9PATH + "/dev");
97	if(display == nil)
98		raise sys->sprint("fail:cannot allocate display: %r");
99
100
101	(wm, join, req) := wmsrv->init();
102	if(wm == nil){
103		wc <-= (nil, sys->sprint("%r"));
104		return;
105	}
106	readscreenr();
107	wc <-= (ref Draw->Context(display, nil, wm), nil);
108
109	sys->pctl(Sys->FORKNS, nil);
110	ebind("#₪", "/srv", Sys->MREPL|Sys->MCREATE);
111	if(sys->bind(P9PATH+"/dev/draw", "/dev/draw", Sys->MREPL) == -1)
112		ebind(P9PATH+"/dev", "/dev", Sys->MAFTER);
113	sh->run(nil, "mount" :: "{mntgen}" :: "/mnt" :: nil);
114
115	clients: array of ref Client;
116	nc := 0;
117	for(;;) alt{
118	(sc, rc) := <-join =>
119		if(nc != 0)
120			rc <-= "only one client available";
121		sync := chan of (ref Client, string);
122		spawn clientproc(sc,sync);
123		(c, err) := <-sync;
124		rc <-= err;
125		if(c != nil){
126			if(sc.id >= len clients)
127				clients = (array[sc.id + 1] of ref Client)[0:] = clients;
128			clients[sc.id] = c;
129		}
130	(sc, data, rc) := <-req =>
131		clients[sc.id].req <-= (data, rc);
132		if(rc == nil)
133			clients[sc.id] = nil;
134	}
135}
136zclient: Client;
137clientproc(sc: ref Wmsrv->Client, rc: chan of (ref Client, string))
138{
139	c := ref zclient;
140	c.req = chan of (array of byte, Sys->Rwrite);
141	c.resize = chan of ref Riowin;
142	c.ptr = chan of ref Draw->Pointer;
143	c.riowctl = chan of (ref Riowin, int);
144	c.sc = sc;
145	rc <-= (c, nil);
146
147loop:
148	for(;;) alt{
149	(data, drc) := <-c.req =>
150		if(drc == nil)
151			break loop;
152		err := handlerequest(c, data);
153		n := len data;
154		if(err != nil)
155			n = -1;
156		alt{
157		drc <-= (n, err) =>;
158		* =>;
159		}
160	p := <-c.ptr =>
161		sc.ptr <-= p;
162	w := <-c.resize =>
163		if((c.state & Hidden) == 0)
164			sc.ctl <-= sys->sprint("!reshape %q -1 0 0 0 0 getwin", w.tag);
165	(w, state) := <-c.riowctl =>
166		if((c.state^state)&Current)
167			sc.ctl <-= "haskbdfocus " + string ((state & Current)!=0);
168		if((c.state^state)&Hidden){
169			s := "unhide";
170			if(state&Hidden)
171				s = "hide";
172			for(wl := c.wins; wl != nil; wl = tl wl){
173				if(hd wl != w)
174					rioctl(hd wl, s);
175				if(c.state&Hidden)
176					sc.ctl <-= sys->sprint("!reshape %q -1 0 0 0 0 getwin", (hd wl).tag);
177			}
178		}
179		c.state = state;
180		w.state = state;
181	}
182	sc.stop <-= 1;
183	for(wl := c.wins; wl != nil; wl = tl wl)
184		delwin(hd wl);
185}
186
187handlerequest(c: ref Client, data: array of byte): string
188{
189	req := string data;
190#sys->print("%d: %s\n", c.sc.id, req);
191	if(req == nil)
192		return "no request";
193	args := str->unquoted(req);
194	n := len args;
195	case hd args {
196	"key" =>
197		return "permission denied";
198	"ptr" =>
199		# ptr x y
200		if(n != 3)
201			return "bad arg count";
202		if(c.ptrstarted == 0)
203			return "pointer not active";
204		for(w := c.wins; w != nil; w = tl w){
205			if((hd w).ptrfd != nil){
206				sys->fprint((hd w).ptrfd, "m%11d %11d", int hd tl args, int hd tl tl args);
207				return nil;
208			}
209		}
210		return "no windows";
211	"start" =>
212		if(n != 2)
213			return "bad arg count";
214		case hd tl args {
215		"ptr" or
216		"mouse" =>
217			if(c.ptrstarted == -1)
218				return "already started";
219			sync := chan of int;
220			for(w := c.wins; w != nil; w = tl w){
221				spawn ptrproc(hd w, c.ptr, c.resize, sync);
222				(hd w).ptrpid = <-sync;
223			}
224			c.ptrstarted = 1;
225			return nil;
226		"kbd" =>
227			if(c.kbdstarted == -1)
228				return "already started";
229			sync := chan of int;
230			for(w := c.wins; w != nil; w = tl w){
231				spawn kbdproc(hd w, c.sc.kbd, sync);
232				(hd w).kbdpid = <-sync;
233			}
234			return nil;
235		* =>
236			return "unknown input source";
237		}
238	"!reshape" =>
239		# reshape tag reqid rect [how]
240		# XXX allow "how" to specify that the origin of the window is never
241		# changed - a new window will be created instead.
242		if(n < 7)
243			return "bad arg count";
244		args = tl args;
245		tag := hd args; args = tl args;
246		args = tl args;		# skip reqid
247		r: Rect;
248		r.min.x = int hd args; args = tl args;
249		r.min.y = int hd args; args = tl args;
250		r.max.x = int hd args; args = tl args;
251		r.max.y = int hd args; args = tl args;
252		if(r.dx() < Minwinsize.x)
253			r.max.x = r.min.x + Minwinsize.x;
254		if(r.dy() < Minwinsize.y)
255			r.max.y = r.min.y + Minwinsize.y;
256
257		spec := "";
258		if(args != nil){
259			case hd args{
260			"onscreen" =>
261				r = fitrect(r, screenr).inset(-Borderwidth);
262				spec = "-r " + r2s(r);
263			"place" =>
264				r = fitrect(r, screenr).inset(-Borderwidth);
265				spec = "-dx " + string r.dx() + " -dy " + string r.dy();
266			"exact" =>
267				spec = "-r " + r2s(r.inset(-Borderwidth));
268			"max" =>
269				r = screenr;			# XXX don't obscure toolbar?
270				spec = "-r " + r2s(r.inset(Borderwidth));
271			"getwin" =>
272				;						# just get the new image
273			* =>
274				return "unkown placement method";
275			}
276		}else
277			spec = "-r " + r2s(r.inset(-Borderwidth));
278		return reshape(c, tag, spec);
279	"delete" =>
280		# delete tag
281		if(tl args == nil)
282			return "tag required";
283		tag := hd tl args;
284		nw: list of ref Riowin;
285		for(w := c.wins; w != nil; w = tl w){
286			if((hd w).tag == tag){
287				delwin(hd w);
288				wmsrv->c.sc.setimage(tag, nil);
289			}else
290				nw = hd w :: nw;
291		}
292		c.wins = nil;
293		for(; nw != nil; nw = tl nw)
294			c.wins = hd nw :: c.wins;
295	"label" =>
296		if(n != 2)
297			return "bad arg count";
298		for(w := c.wins; w != nil; w = tl w)
299			setlabel(hd w, hd tl args);
300	"raise" =>
301		for(w := c.wins; w != nil; w = tl w){
302			rioctl(hd w, "top");
303			if(tl w == nil)
304				rioctl(hd w, "current");
305		}
306	"lower" =>
307		for(w := c.wins; w != nil; w = tl w)
308			rioctl(hd w, "bottom");
309	"task" =>
310		if(n != 2)
311			return "bad arg count";
312		c.state |= Hidden;
313		for(w := c.wins; w != nil; w = tl w){
314			setlabel(hd w, hd tl args);
315			rioctl(hd w, "hide");
316		}
317	"untask" =>
318		wins: list of ref Riowin;
319		for(w := c.wins; w != nil; w = tl w)
320			wins = hd w :: wins;
321		for(; wins != nil; wins = tl wins)
322			rioctl(hd wins, "unhide");
323	"!move" =>
324		# !move tag reqid startx starty
325		if(n != 5)
326			return "bad arg count";
327		args = tl args;
328		tag := hd args; args = tl args;
329		args = tl args;
330		w := wmsrv->c.sc.window(tag);
331		if(w == nil)
332			return "no such tag";
333		return dragwin(c.ptr, c, w, Point(int hd args, int hd tl args));
334	"!size" =>
335		return "nope";
336	"kbdfocus" =>
337		if(n != 2)
338			return "bad arg count";
339		if(int hd tl args){
340			if(c.wins != nil)
341				return rioctl(hd c.wins, "current");
342		}
343		return nil;
344	* =>
345		return "unknown request";
346	}
347	return nil;
348}
349
350dragwin(ptr: chan of ref Draw->Pointer, c: ref Client, w: ref Wmsrv->Window, click: Point): string
351{
352#	if(buttons == 0)
353#		return "too late";
354	p: ref Draw->Pointer;
355	img := w.img.screen.image;
356	r := img.r;
357	off := click.sub(r.min);
358	do{
359		p = <-ptr;
360		img.origin(r.min, p.xy.sub(off));
361	} while (p.buttons != 0);
362	c.sc.ptr <-= p;
363#	buttons = 0;
364	nr: Rect;
365	nr.min = p.xy.sub(off);
366	nr.max = nr.min.add(r.size());
367	if(nr.eq(r))
368		return "not moved";
369	reshape(c, w.tag, "-r " + r2s(nr));
370	return nil;
371}
372
373rioctl(w: ref Riowin, req: string): string
374{
375	if(sys->fprint(w.ctlfd, "%s", req) == -1){
376#sys->print("rioctl fail %s: %s: %r\n", w.dir, req);
377		return sys->sprint("%r");
378}
379#sys->print("rioctl %s: %s\n", w.dir, req);
380	return nil;
381}
382
383reshape(c: ref Client, tag: string, spec: string): string
384{
385	for(wl := c.wins; wl != nil; wl = tl wl)
386		if((hd wl).tag == tag)
387			break;
388	if(wl == nil){
389		(w, e) := newwin(c, tag, spec);
390		if(w == nil){
391sys->print("can't make new win (spec %q): %s\n", spec, e);
392			return e;
393		}
394		c.wins = w :: c.wins;
395		wmsrv->c.sc.setimage(tag, w.img);
396		sync := chan of int;
397		if(c.kbdstarted){
398			spawn kbdproc(w, c.sc.kbd, sync);
399			w.kbdpid = <-sync;
400		}
401		if(c.ptrstarted){
402			spawn ptrproc(w, c.ptr, c.resize, sync);
403			w.ptrpid = <-sync;
404		}
405		return nil;
406	}
407	w := hd wl;
408	if(spec != nil){
409		e := rioctl(w, "resize " + spec);
410		if(e != nil)
411			return e;
412	}
413	getwin(w);
414	if(w.img == nil)
415		return "getwin failed";
416	wmsrv->c.sc.setimage(tag, w.img);
417	return nil;
418}
419
420zriowin: Riowin;
421newwin(c: ref Client, tag, spec: string): (ref Riowin, string)
422{
423	wsys := readfile(P9PATH + "/env/wsys");
424	if(wsys == nil)
425		return (nil, "no $wsys");
426
427	d := "/mnt/"+string wsysseq++;
428	fd := sys->open(wsys, Sys->ORDWR);
429	if(fd == nil)
430		return (nil, sys->sprint("cannot open %q: %r\n", wsys));
431	# XXX this won't multiplex properly - srv9 should export attach files (actually that's what plan 9 should do)
432	if(sys->mount(fd, nil, d, Sys->MREPL, "new "+spec) == -1)
433		return (nil, sys->sprint("mount %q failed: %r", wsys));
434	(ok, nil) := sys->stat(d + "/winname");
435	if(ok == -1)
436		return (nil, "could not make window");
437	w := ref zriowin;
438	w.tag = tag;
439	w.dir = d;
440	getwin(w);
441	w.ctlfd = sys->open(d + "/wctl", Sys->ORDWR);
442	setlabel(w, "inferno "+string sys->pctl(0, nil)+"."+tag);
443	sync := chan of int;
444	spawn ctlproc(w, c.riowctl, sync);
445	w.ctlpid = <-sync;
446	return (w, nil);
447}
448
449setlabel(w: ref Riowin, s: string)
450{
451	fd := sys->open(w.dir + "/label", Sys->OWRITE);
452	if(fd != nil)
453		sys->fprint(fd, "%s", s);
454}
455
456ctlproc(w: ref Riowin, wctl: chan of (ref Riowin, int), sync: chan of int)
457{
458	sync <-= sys->pctl(0, nil);
459	buf := array[1024] of byte;
460	for(;;){
461		n := sys->read(w.ctlfd, buf, len buf);
462		if(n <= 0)
463			break;
464		if(n > 4*12){
465			state := 0;
466			(nil, toks) := sys->tokenize(string buf[4*12:], " ");
467			if(hd toks == "current")
468				state |= Current;
469			if(hd tl toks == "hidden")
470				state |= Hidden;
471			wctl <-= (w, state);
472		}
473	}
474#sys->print("riowctl eof\n");
475}
476
477delwin(w: ref Riowin)
478{
479	sys->unmount(nil, w.dir);
480	kill(w.ptrpid, "kill");
481	kill(w.kbdpid, "kill");
482	kill(w.ctlpid, "kill");
483}
484
485getwin(w: ref Riowin): int
486{
487	s := readfile(w.dir + "/winname");
488#sys->print("getwin %s\n", s);
489	i := display.namedimage(s);
490	if(i == nil)
491		return -1;
492	scr := Screen.allocate(i, display.white, 0);
493	if(scr == nil)
494		return -1;
495	wi := scr.newwindow(i.r.inset(Borderwidth), Draw->Refnone, Draw->Nofill);
496	if(wi == nil)
497		return -1;
498	w.img = wi;
499	return 0;
500}
501
502kbdproc(w: ref Riowin, keys: chan of int, sync: chan of int)
503{
504	sys->pctl(Sys->NEWFD, nil);
505	cctl := sys->open(w.dir + "/consctl", Sys->OWRITE);
506	sys->fprint(cctl, "rawon");
507	fd := sys->open(w.dir + "/cons", Sys->OREAD);
508	if(fd == nil){
509		sync <-= -1;
510		return;
511	}
512	sync <-= sys->pctl(0, nil);
513	buf := array[12] of byte;
514	while((n := sys->read(fd, buf, len buf)) > 0){
515		s := string buf[0:n];
516		for(j := 0; j < len s; j++)
517			keys <-= int s[j];
518	}
519#sys->print("eof on kbdproc\n");
520}
521
522# fit a window rectangle to the available space.
523# try to preserve requested location if possible.
524# make sure that the window is no bigger than
525# the screen, and that its top and left-hand edges
526# will be visible at least.
527fitrect(w, r: Rect): Rect
528{
529	if(w.dx() > r.dx())
530		w.max.x = w.min.x + r.dx();
531	if(w.dy() > r.dy())
532		w.max.y = w.min.y + r.dy();
533	size := w.size();
534	if (w.max.x > r.max.x)
535		(w.min.x, w.max.x) = (r.min.x - size.x, r.max.x - size.x);
536	if (w.max.y > r.max.y)
537		(w.min.y, w.max.y) = (r.min.y - size.y, r.max.y - size.y);
538	if (w.min.x < r.min.x)
539		(w.min.x, w.max.x) = (r.min.x, r.min.x + size.x);
540	if (w.min.y < r.min.y)
541		(w.min.y, w.max.y) = (r.min.y, r.min.y + size.y);
542	return w;
543}
544
545ptrproc(w: ref Riowin, ptr: chan of ref Draw->Pointer, resize: chan of ref Riowin, sync: chan of int)
546{
547	w.ptrfd = sys->open(w.dir + "/mouse", Sys->ORDWR);
548	if(w.ptrfd == nil){
549		sync <-= -1;
550		return;
551	}
552	sync <-= sys->pctl(0, nil);
553
554	b:= array[Ptrsize] of byte;
555	while((n := sys->read(w.ptrfd, b, len b)) > 0){
556		if(n > 0 && int b[0] == 'r'){
557#sys->print("ptrproc got resize: %s\n", string b[0:n]);
558			resize <-= w;
559		}else{
560			p := bytes2ptr(b);
561			if(p != nil)
562				ptr <-= p;
563		}
564	}
565#sys->print("eof on ptrproc\n");
566}
567
568bytes2ptr(b: array of byte): ref Draw->Pointer
569{
570	if(len b < Ptrsize || int b[0] != 'm')
571		return nil;
572	x := int string b[1:13];
573	y := int string b[13:25];
574	but := int string b[25:37];
575	msec := int string b[37:49];
576	return ref Draw->Pointer (but, (x, y), msec);
577}
578
579readfile(f: string): string
580{
581	fd := sys->open(f, sys->OREAD);
582	if(fd == nil)
583		return nil;
584
585	buf := array[8192] of byte;
586	n := sys->read(fd, buf, len buf);
587	if(n < 0)
588		return nil;
589
590	return string buf[0:n];
591}
592
593readscreenr()
594{
595	fd := sys->open(P9PATH + "/dev/screen", Sys->OREAD);
596	if(fd == nil)
597		return ;
598	buf := array[5*12] of byte;
599	n := sys->read(fd, buf, len buf);
600	if(n <= len buf)
601		return;
602	screenr.min.x = int string buf[12:23];
603	screenr.min.y = int string buf[24:35];
604	screenr.max.x = int string buf[36:47];
605	screenr.max.y = int string buf[48:];
606}
607
608r2s(r: Rect): string
609{
610	return string r.min.x + " " + string r.min.y + " " +
611			string r.max.x + " " + string r.max.y;
612}
613
614kill(pid: int, note: string): int
615{
616	fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE);
617	if(fd == nil || sys->fprint(fd, "%s", note) < 0)
618		return -1;
619	return 0;
620}
621