xref: /inferno-os/appl/lib/wmsrv.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1implement Wmsrv;
2
3include "sys.m";
4	sys: Sys;
5include "draw.m";
6	draw: Draw;
7	Display, Image, Point, Rect, Screen, Pointer, Context, Wmcontext: import draw;
8include "wmsrv.m";
9
10zorder: ref Client;		# top of z-order list, linked by znext.
11
12ZR: con Rect((0, 0), (0, 0));
13Iqueue: adt {
14	h, t: list of int;
15	n: int;
16	put:			fn(q: self ref Iqueue, s: int);
17	get:			fn(q: self ref Iqueue): int;
18	peek:		fn(q: self ref Iqueue): int;
19	nonempty:	fn(q: self ref Iqueue): int;
20};
21Squeue: adt {
22	h, t: list of string;
23	n: int;
24	put:			fn(q: self ref Squeue, s: string);
25	get:			fn(q: self ref Squeue): string;
26	peek:		fn(q: self ref Squeue): string;
27	nonempty:	fn(q: self ref Squeue): int;
28};
29# Ptrqueue is the same as the other queues except it merges events
30# that have the same button state.
31Ptrqueue: adt {
32	last: ref Pointer;
33	h, t: list of ref Pointer;
34	put:			fn(q: self ref Ptrqueue, s: ref Pointer);
35	get:			fn(q: self ref Ptrqueue): ref Pointer;
36	peek:		fn(q: self ref Ptrqueue): ref Pointer;
37	nonempty:	fn(q: self ref Ptrqueue): int;
38	flush:		fn(q: self ref Ptrqueue);
39};
40
41init(): 	(chan of (string, chan of (string, ref Wmcontext)),
42		chan of (ref Client, chan of string),
43		chan of (ref Client, array of byte, Sys->Rwrite))
44{
45	sys = load Sys Sys->PATH;
46	draw = load Draw Draw->PATH;
47
48	sys->bind("#s", "/chan", Sys->MBEFORE);
49
50	ctlio := sys->file2chan("/chan", "wmctl");
51	if(ctlio == nil){
52		sys->werrstr(sys->sprint("can't create /chan/wmctl: %r"));
53		return (nil, nil, nil);
54	}
55
56	wmreq := chan of (string, chan of (string, ref Wmcontext));
57	join := chan of (ref Client, chan of string);
58	req := chan of (ref Client, array of byte, Sys->Rwrite);
59	spawn wm(ctlio, wmreq, join, req);
60	return (wmreq, join, req);
61}
62
63wm(ctlio: ref Sys->FileIO,
64			wmreq: chan of (string, chan of (string, ref Wmcontext)),
65			join: chan of (ref Client, chan of string),
66			req: chan of (ref Client, array of byte, Sys->Rwrite))
67{
68	clients: array of ref Client;
69
70	for(;;)alt{
71	(cmd, rc) := <-wmreq =>
72		token := int cmd;
73		for(i := 0; i < len clients; i++)
74			if(clients[i] != nil && clients[i].token == token)
75				break;
76
77		if(i == len clients){
78			spawn senderror(rc, "not found");
79			break;
80		}
81		c := clients[i];
82		if(c.stop != nil){
83			spawn senderror(rc, "already started");
84			break;
85		}
86		ok := chan of string;
87		join <-= (c, ok);
88		if((e := <-ok) != nil){
89			spawn senderror(rc, e);
90			break;
91		}
92		c.stop = chan of int;
93		spawn childminder(c, rc);
94
95	(nil, nbytes, fid, rc) := <-ctlio.read =>
96		if(rc == nil)
97			break;
98		c := findfid(clients, fid);
99		if(c == nil){
100			c = ref Client(
101				chan of int,
102				chan of ref Draw->Pointer,
103				chan of string,
104				nil,
105				0,
106				nil,
107				nil,
108				nil,
109
110				chan of (ref Point, ref Image, chan of int),
111				-1,
112				fid,
113				fid,			# token; XXX could be random integer + fid
114				newwmcontext()
115			);
116			clients = addclient(clients, c);
117		}
118		alt{
119		rc <-= (sys->aprint("%d", c.token), nil) => ;
120		* => ;
121		}
122	(nil, data, fid, wc) := <-ctlio.write =>
123		c := findfid(clients, fid);
124		if(wc != nil){
125			if(c == nil){
126				alt{
127				wc <-= (0, "must read first") => ;
128				* => ;
129				}
130				break;
131			}
132			req <-= (c, data, wc);
133		}else if(c != nil){
134			req <-= (c, nil, nil);
135			delclient(clients, c);
136		}
137	}
138}
139
140# buffer all events between a window manager and
141# a client, so that one recalcitrant child can't
142# clog the whole system.
143childminder(c: ref Client, rc: chan of (string, ref Wmcontext))
144{
145	wmctxt := c.wmctxt;
146
147	dummykbd := chan of int;
148	dummyptr := chan of ref Pointer;
149	dummyimg := chan of ref Image;
150	dummyctl := chan of string;
151
152	kbdq := ref Iqueue;
153	ptrq := ref Ptrqueue;
154	ctlq := ref Squeue;
155
156	Imgnone, Imgsend, Imgsendnil1, Imgsendnil2, Imgorigin: con iota;
157	img, sendimg: ref Image;
158	imgorigin: Point;
159	imgstate := Imgnone;
160
161	# send reply to client, but make sure we don't block.
162Reply:
163	for(;;) alt{
164	rc <-= (nil, ref *wmctxt) =>
165		break Reply;
166	<-c.stop =>
167		exit;
168	key := <-c.kbd =>
169		kbdq.put(key);
170	ptr := <-c.ptr =>
171		ptrq.put(ptr);
172	ctl := <-c.ctl =>
173		ctlq.put(ctl);
174	}
175
176	for(;;){
177		outkbd := dummykbd;
178		key := -1;
179		if(kbdq.nonempty()){
180			key = kbdq.peek();
181			outkbd = wmctxt.kbd;
182		}
183
184		outptr := dummyptr;
185		ptr: ref Pointer;
186		if(ptrq.nonempty()){
187			ptr = ptrq.peek();
188			outptr = wmctxt.ptr;
189		}
190
191		outctl := dummyctl;
192		ctl: string;
193		if(ctlq.nonempty()){
194			ctl = ctlq.peek();
195			outctl = wmctxt.ctl;
196		}
197
198		outimg := dummyimg;
199		case imgstate{
200		Imgsend =>
201			outimg = wmctxt.images;
202			sendimg = img;
203		Imgsendnil1 or
204		Imgsendnil2 or
205		Imgorigin =>
206			outimg = wmctxt.images;
207			sendimg = nil;
208		}
209
210		alt{
211		outkbd <-= key =>
212			kbdq.get();
213		outptr <-= ptr =>
214			ptrq.get();
215		outctl <-= ctl =>
216			ctlq.get();
217		outimg <-= sendimg =>
218			case imgstate{
219			Imgsend =>
220				imgstate = Imgnone;
221				img = sendimg = nil;
222			Imgsendnil1 =>
223				imgstate = Imgsendnil2;
224			Imgsendnil2 =>
225				imgstate = Imgnone;
226			Imgorigin =>
227				if(img.origin(imgorigin, imgorigin) == -1){
228					# XXX what can we do about this? there's no way at the moment
229					# of getting the information about the origin failure back to the wm,
230					# so we end up with an inconsistent window position.
231					# if the window manager blocks while we got the sync from
232					# the client, then a client could block the whole window manager
233					# which is what we're trying to avoid.
234					# but there's no other time we could set the origin of the window,
235					# and not risk mucking up the window contents.
236					# the short answer is that running out of image space is Bad News.
237				}
238				imgstate = Imgsend;
239			}
240
241		# XXX could mark the application as unresponding if any of these queues
242		# start growing too much.
243		ch := <-c.kbd =>
244			kbdq.put(ch);
245		p := <-c.ptr =>
246			if(p == nil)
247				ptrq.flush();
248			else
249				ptrq.put(p);
250		e := <-c.ctl =>
251			ctlq.put(e);
252		(o, i, reply) := <-c.images =>
253			# can't queue multiple image requests.
254			if(imgstate != Imgnone)
255				reply <-= -1;
256			else {
257				# if the origin is being set, then we first send a nil image
258				# to indicate that this is happening, and then the
259				# image itself (reorigined).
260				# if a nil image is being set, then we
261				# send nil twice.
262				if(o != nil){
263					imgorigin = *o;
264					imgstate = Imgorigin;
265					img = i;
266				}else if(i != nil){
267					img = i;
268					imgstate = Imgsend;
269				}else
270					imgstate = Imgsendnil1;
271				reply <-= 0;
272			}
273		<-c.stop =>
274			# XXX do we need to unblock channels, kill, etc.?
275			# we should perhaps drain the ctl output channel here
276			# if possible, exiting if it times out.
277			exit;
278		}
279	}
280}
281
282findfid(clients: array of ref Client, fid: int): ref Client
283{
284	for(i := 0; i < len clients; i++)
285		if(clients[i] != nil && clients[i].fid == fid)
286			return clients[i];
287	return nil;
288}
289
290addclient(clients: array of ref Client, c: ref Client): array of ref Client
291{
292	for(i := 0; i < len clients; i++)
293		if(clients[i] == nil){
294			clients[i] = c;
295			c.id = i;
296			return clients;
297		}
298	nc := array[len clients + 4] of ref Client;
299	nc[0:] = clients;
300	nc[len clients] = c;
301	c.id = len clients;
302	return nc;
303}
304
305delclient(clients: array of ref Client, c: ref Client)
306{
307	clients[c.id] = nil;
308}
309
310senderror(rc: chan of (string, ref Wmcontext), e: string)
311{
312	rc <-= (e, nil);
313}
314
315Client.window(c: self ref Client, tag: string): ref Window
316{
317	for (w := c.wins; w != nil; w = tl w)
318		if((hd w).tag == tag)
319			return hd w;
320	return nil;
321}
322
323Client.image(c: self ref Client, tag: string): ref Draw->Image
324{
325	w := c.window(tag);
326	if(w != nil)
327		return w.img;
328	return nil;
329}
330
331Client.setimage(c: self ref Client, tag: string, img: ref Draw->Image): int
332{
333	# if img is nil, remove window from list.
334	if(img == nil){
335		# usual case:
336		if(c.wins != nil && (hd c.wins).tag == tag){
337			c.wins = tl c.wins;
338			return -1;
339		}
340		nw: list of ref Window;
341		for (w := c.wins; w != nil; w = tl w)
342			if((hd w).tag != tag)
343				nw = hd w :: nw;
344		c.wins = nil;
345		for(; nw != nil; nw = tl nw)
346			c.wins = hd nw :: c.wins;
347		return -1;
348	}
349	for(w := c.wins; w != nil; w = tl w)
350		if((hd w).tag == tag)
351			break;
352	win: ref Window;
353	if(w != nil)
354		win = hd w;
355	else{
356		win = ref Window(tag, ZR, nil);
357		c.wins = win :: c.wins;
358	}
359	win.img = img;
360	win.r = img.r;			# save so clients can set logical origin
361	rc := chan of int;
362	c.images <-= (nil, img, rc);
363	return <-rc;
364}
365
366# tell a client about a window that's moved to screen coord o.
367Client.setorigin(c: self ref Client, tag: string, o: Draw->Point): int
368{
369	w := c.window(tag);
370	if(w == nil)
371		return -1;
372	img := w.img;
373	if(img == nil)
374		return -1;
375	rc := chan of int;
376	c.images <-= (ref o, w.img, rc);
377	if(<-rc != -1){
378		w.r = (o, o.add(img.r.size()));
379		return 0;
380	}
381	return -1;
382}
383
384clientimages(c: ref Client): array of ref Image
385{
386	a := array[len c.wins] of ref Draw->Image;
387	i := 0;
388	for(w := c.wins; w != nil; w = tl w)
389		if((hd w).img != nil)
390			a[i++] = (hd w).img;
391	return a[0:i];
392}
393
394Client.top(c: self ref Client)
395{
396	imgs := clientimages(c);
397	if(len imgs > 0)
398		imgs[0].screen.top(imgs);
399
400	if(zorder == c)
401		return;
402
403	prev: ref Client;
404	for(z := zorder; z != nil; (prev, z) = (z, z.znext))
405		if(z == c)
406			break;
407	if(prev != nil)
408		prev.znext = c.znext;
409	c.znext = zorder;
410	zorder = c;
411}
412
413Client.bottom(c: self ref Client)
414{
415	if(c.znext == nil)
416		return;
417	imgs := clientimages(c);
418	if(len imgs > 0)
419		imgs[0].screen.bottom(imgs);
420	prev: ref Client;
421	for(z := zorder; z != nil; (prev, z) = (z, z.znext))
422		if(z == c)
423			break;
424	if(prev != nil)
425		prev.znext = c.znext;
426	else
427		zorder = c.znext;
428	z = c.znext;
429	c.znext = nil;
430	for(; z != nil; (prev, z) = (z, z.znext))
431		;
432	if(prev != nil)
433		prev.znext = c;
434	else
435		zorder = c;
436}
437
438Client.hide(nil: self ref Client)
439{
440}
441
442Client.unhide(nil: self ref Client)
443{
444}
445
446Client.remove(c: self ref Client)
447{
448	prev: ref Client;
449	for(z := zorder; z != nil; (prev, z) = (z, z.znext))
450		if(z == c)
451			break;
452	if(z == nil)
453		return;
454	if(prev != nil)
455		prev.znext = z.znext;
456	else if(z != nil)
457		zorder = zorder.znext;
458}
459
460find(p: Draw->Point): ref Client
461{
462	for(z := zorder; z != nil; z = z.znext)
463		if(z.contains(p))
464			return z;
465	return nil;
466}
467
468top(): ref Client
469{
470	return zorder;
471}
472
473Client.contains(c: self ref Client, p: Point): int
474{
475	for(w := c.wins; w != nil; w = tl w)
476		if((hd w).r.contains(p))
477			return 1;
478	return 0;
479}
480
481r2s(r: Rect): string
482{
483	return string r.min.x + " " + string r.min.y + " " +
484			string r.max.x + " " + string r.max.y;
485}
486
487newwmcontext(): ref Wmcontext
488{
489	return ref Wmcontext(
490		chan of int,
491		chan of ref Pointer,
492		chan of string,
493		nil,
494		chan of ref Image,
495		nil,
496		nil
497	);
498}
499
500Iqueue.put(q: self ref Iqueue, s: int)
501{
502	q.t = s :: q.t;
503}
504Iqueue.get(q: self ref Iqueue): int
505{
506	s := -1;
507	if(q.h == nil){
508		for(t := q.t; t != nil; t = tl t)
509			q.h = hd t :: q.h;
510		q.t = nil;
511	}
512	if(q.h != nil){
513		s = hd q.h;
514		q.h = tl q.h;
515	}
516	return s;
517}
518Iqueue.peek(q: self ref Iqueue): int
519{
520	s := -1;
521	if (q.h == nil && q.t == nil)
522		return s;
523	s = q.get();
524	q.h = s :: q.h;
525	return s;
526}
527Iqueue.nonempty(q: self ref Iqueue): int
528{
529	return q.h != nil || q.t != nil;
530}
531
532
533Squeue.put(q: self ref Squeue, s: string)
534{
535	q.t = s :: q.t;
536}
537Squeue.get(q: self ref Squeue): string
538{
539	s: string;
540	if(q.h == nil){
541		for(t := q.t; t != nil; t = tl t)
542			q.h = hd t :: q.h;
543		q.t = nil;
544	}
545	if(q.h != nil){
546		s = hd q.h;
547		q.h = tl q.h;
548	}
549	return s;
550}
551Squeue.peek(q: self ref Squeue): string
552{
553	s: string;
554	if (q.h == nil && q.t == nil)
555		return s;
556	s = q.get();
557	q.h = s :: q.h;
558	return s;
559}
560Squeue.nonempty(q: self ref Squeue): int
561{
562	return q.h != nil || q.t != nil;
563}
564
565Ptrqueue.put(q: self ref Ptrqueue, s: ref Pointer)
566{
567	if(q.last != nil && s.buttons == q.last.buttons)
568		*q.last = *s;
569	else{
570		q.t = s :: q.t;
571		q.last = s;
572	}
573}
574Ptrqueue.get(q: self ref Ptrqueue): ref Pointer
575{
576	s: ref Pointer;
577	h := q.h;
578	if(h == nil){
579		for(t := q.t; t != nil; t = tl t)
580			h = hd t :: h;
581		q.t = nil;
582	}
583	if(h != nil){
584		s = hd h;
585		h = tl h;
586		if(h == nil)
587			q.last = nil;
588	}
589	q.h = h;
590	return s;
591}
592Ptrqueue.peek(q: self ref Ptrqueue): ref Pointer
593{
594	s: ref Pointer;
595	if (q.h == nil && q.t == nil)
596		return s;
597	t := q.last;
598	s = q.get();
599	q.h = s :: q.h;
600	q.last = t;
601	return s;
602}
603Ptrqueue.nonempty(q: self ref Ptrqueue): int
604{
605	return q.h != nil || q.t != nil;
606}
607Ptrqueue.flush(q: self ref Ptrqueue)
608{
609	q.h = q.t = nil;
610}
611