xref: /inferno-os/appl/wm/drawmux/drawmux.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1implement Drawmux;
2
3include "sys.m";
4include "draw.m";
5include "drawmux.m";
6
7include "drawoffs.m";
8
9sys : Sys;
10draw : Draw;
11
12Display, Point, Rect, Chans : import draw;
13
14Ehungup : con "Hangup";
15
16drawR: Draw->Rect;
17drawchans: Draw->Chans;
18drawop := Draw->SoverD;
19drawfd: ref Sys->FD;
20images: ref Imageset;
21screens: ref Screenset;
22viewers: list of ref Viewer;
23drawlock: chan of chan of int;
24readdata: array of byte;
25nhangups := 0;
26prevnhangups := 0;
27
28init() : (string, ref Draw->Display)
29{
30	sys = load Sys Sys->PATH;
31	draw = load Draw Draw->PATH;
32
33	if (draw == nil)
34		return (sys->sprint("cannot load %s: %r", Draw->PATH), nil);
35	drawlock = chan of chan of int;
36	images = Imageset.new();
37	screens = Screenset. new();
38	res := chan of (string, ref Draw->Display);
39	spawn getdisp(res);
40	r := <- res;
41	return r;
42}
43
44newviewer(fd : ref Sys->FD)
45{
46	reply := array of byte sys->sprint("%.11d %.11d ", drawR.max.x - drawR.min.x, drawR.max.y - drawR.min.y);
47	if (sys->write(fd, reply, len reply) != len reply) {
48#		sys->print("viewer hangup\n");
49		return;
50	}
51
52	buf := array [Sys->ATOMICIO] of byte;
53	n := sys->read(fd, buf, len buf);
54	if (n < 24)
55		return;
56	pubscr := int string buf[0:12];
57	chans := Chans.mk(string buf[12:24]);
58
59	sys->pctl(Sys->FORKNS, nil);
60	sys->mount(fd, nil, "/", Sys->MREPL, nil);
61	cfd := sys->open("/new", Sys->OREAD);
62	sys->read(cfd, buf, len buf);
63	cnum := int string buf[0:12];
64	cdata := sys->sprint("/%d/data", cnum);
65	datafd := sys->open(cdata, Sys->ORDWR);
66
67	if (datafd == nil) {
68#		sys->print("cannot open viewer data file: %r\n");
69		return;
70	}
71	Viewer.new(datafd, pubscr, chans);
72}
73
74getdisp(result : chan of (string, ref Draw->Display))
75{
76	sys->pctl(Sys->FORKNS, nil);
77	sys->bind("#i", "/dev", Sys->MREPL);
78	sys->bind("#s", "/dev/draw", Sys->MBEFORE);
79	newio := sys->file2chan("/dev/draw", "new");
80	if (newio == nil) {
81		result <- = ("cannot create /dev/new file2chan", nil);
82		return;
83	}
84	spawn srvnew(newio);
85	disp := Display.allocate(nil);
86	if (disp == nil) {
87		result <-= (sys->sprint("%r"), nil);
88		return;
89	}
90
91	draw->disp.image.draw(disp.image.r, disp.rgb(0,0,0), nil, Point(0,0));
92	result <- = (nil, disp);
93}
94
95srvnew(newio : ref Sys->FileIO)
96{
97	for (;;) alt {
98	(offset, count, fid, rc) := <- newio.read =>
99		if (rc != nil) {
100			c := chan of (string, ref Sys->FD);
101			fd := sys->open("#i/draw/new", Sys->OREAD);
102			# +1 because of a sprint() nasty in devdraw.c
103			buf := array [(12 * 12)+1] of byte;
104			nn := sys->read(fd, buf, len buf);
105			cnum := int string buf[0:12];
106			drawchans = Chans.mk(string buf[24:36]);
107			# repl is at [36:48]
108			drawR.min.x = int string buf[48:60];
109			drawR.min.y = int string buf[60:72];
110			drawR.max.x = int string buf[72:84];
111			drawR.max.y = int string buf[84:96];
112
113			bwidth := bytesperline(drawR, drawchans);
114			img := ref Image (0, 0, 0, 0, drawchans, 0, drawR, drawR, Draw->Black, nil, drawR.min, bwidth, 0, "");
115			images.add(0, img);
116
117			cdir := sys->sprint("/dev/draw/%d", cnum);
118			dpath := sys->sprint("#i/draw/%d/data", cnum);
119			drawfd = sys->open(dpath, Sys->ORDWR);
120			fd = nil;
121			if (drawfd == nil) {
122				rc <-= (nil, sys->sprint("%r"));
123				return;
124			}
125			sys->bind("#s", cdir, Sys->MBEFORE);
126			drawio := sys->file2chan(cdir, "data");
127			spawn drawclient(drawio);
128			rc <- = (buf, nil);
129			return;
130		}
131	(offset, data, fid, wc) := <- newio.write =>
132		if (wc != nil)
133			writereply(wc, (0, "permission denied"));
134	}
135}
136
137# for simplicity make the file 'exclusive use'
138drawclient(drawio : ref Sys->FileIO)
139{
140	activefid := -1;
141	closecount := 2;
142
143	for (;closecount;) {
144		alt {
145		unlock := <- drawlock =>
146			<- unlock;
147
148		(offset, count, fid, rc) := <- drawio.read =>
149				if (activefid == -1)
150					activefid = fid;
151
152				if (rc == nil) {
153					closecount--;
154					continue;
155				}
156				if (fid != activefid) {
157					rc <-= (nil, "file busy");
158					continue;
159				}
160				if (readdata == nil) {
161					rc <-= (nil, nil);
162					continue;
163				}
164				if (count > len readdata)
165					count = len readdata;
166				rc <- = (readdata[0:count], nil);
167				readdata = nil;
168
169		(offset, data, fid, wc) := <- drawio.write =>
170			if (wc == nil) {
171				closecount--;
172				continue;
173			}
174			writereply(wc, process(data));
175		}
176		if (nhangups != prevnhangups) {
177			ok : list of ref Viewer;
178			for (ok = nil; viewers != nil; viewers = tl viewers) {
179				v := hd viewers;
180				if (!v.hungup)
181					ok = v :: ok;
182				else {
183#					sys->print("shutting down Viewer\n");
184					v.output <- = (nil, nil);
185				}
186			}
187			viewers = ok;
188			prevnhangups = nhangups;
189		}
190	}
191#	sys->print("DRAWIO DONE!\n");
192}
193
194writereply(wc : chan of (int, string), val : (int, string))
195{
196	alt {
197	wc <-= val =>
198		;
199	* =>
200		;
201	}
202}
203
204Image: adt {
205	id: int;
206	refc: int;
207	screenid: int;
208	refresh: int;
209	chans: Draw->Chans;
210	repl: int;
211	R: Draw->Rect;
212	clipR: Draw->Rect;
213	rrggbbaa: int;
214	font: ref Font;
215	lorigin: Draw->Point;
216	bwidth: int;
217	dirty: int;
218	name: string;
219};
220
221Screen: adt {
222	id: int;
223	imageid: int;
224	fillid: int;
225	windows: array of int;
226
227	setz: fn (s: self ref Screen, z: array of int, top: int);
228	addwin: fn (s: self ref Screen, wid: int);
229	delwin: fn (s: self ref Screen, wid: int);
230};
231
232Font: adt {
233	ascent: int;
234	chars: array of ref Fontchar;
235};
236
237Fontchar: adt {
238	srcid: int;
239	R: Draw->Rect;
240	P: Draw->Point;
241	left: int;
242	width: int;
243};
244
245Idpair: adt {
246	key: int;
247	val: int;
248	next: cyclic ref Idpair;
249};
250
251Idmap: adt {
252	buckets: array of ref Idpair;
253
254	new: fn (): ref Idmap;
255	add: fn (m: self ref Idmap, key, val: int);
256	del: fn (m: self ref Idmap, key: int);
257	lookup: fn (m: self ref Idmap, key: int): int;
258};
259
260Imageset: adt {
261	images: array of ref Image;
262	ixmap: ref Idmap;
263	freelist: list of int;
264	new: fn (): ref Imageset;
265	add: fn (s: self ref Imageset, id: int, img: ref Image);
266	del: fn (s: self ref Imageset, id: int);
267	lookup: fn (s: self ref Imageset, id: int): ref Image;
268	findname: fn(s: self ref Imageset, name: string): ref Image;
269};
270
271Screenset: adt {
272	screens: array of ref Screen;
273	ixmap: ref Idmap;
274	freelist: list of int;
275	new: fn (): ref Screenset;
276	add: fn (s: self ref Screenset, scr: ref Screen);
277	del: fn (s: self ref Screenset, id: int);
278	lookup: fn (s: self ref Screenset, id: int): ref Screen;
279};
280
281
282Drawreq: adt {
283	data: array of byte;
284	pick {
285#	a =>	# allocate image
286#		id: int;
287#		screenid: int;
288#		refresh: int;
289#		ldepth: int;
290#		repl: int;
291#		R: Draw->Rect;
292#		clipR: Draw->Rect;
293#		value: int;
294	b =>	# new allocate image
295		id: int;
296		screenid: int;
297		refresh: int;
298		chans: Draw->Chans;
299		repl: int;
300		R: 	Draw->Rect;
301		clipR: Draw->Rect;
302		rrggbbaa: int;
303	A => # allocate screen
304		id: int;
305		imageid: int;
306		fillid: int;
307	c => # set clipr and repl
308		dstid: int;
309		repl: int;
310		clipR: Draw->Rect;
311#	x => # move cursor
312#	C => # set cursor image and hotspot
313#		_: int;
314	d => # general draw op
315		dstid: int;
316		srcid: int;
317		maskid: int;
318	D => # debug mode
319		_: int;
320	e => # draw ellipse
321		dstid: int;
322		srcid: int;
323	f => # free image
324		id: int;
325		img: ref Image;	# helper for Viewers
326	F =>	 # free screen
327		id: int;
328	i => # convert image to font
329		fontid: int;
330		nchars: int;
331		ascent: int;
332	l => # load a char into font
333		fontid: int;
334		srcid: int;
335		index: int;
336		R: Draw->Rect;
337		P: Draw->Point;
338		left: int;
339		width: int;
340	L => # draw line
341		dstid: int;
342		srcid: int;
343	n =>	# attach to named image
344		dstid: int;
345		name: string;
346	N => # name image
347		dstid: int;
348		in: int;
349		name: string;
350	o =>	# set window origins
351		id: int;
352		rmin: Draw->Point;
353		screenrmin: Draw->Point;
354	O => # set next compositing op
355		op: int;
356	p =>	# draw polygon
357		dstid: int;
358		srcid: int;
359	r =>	# read pixels
360		id: int;
361		R: Draw->Rect;
362	s =>	# draw text
363		dstid: int;
364		srcid: int;
365		fontid: int;
366	x => # draw text with bg
367		dstid: int;
368		srcid: int;
369		fontid: int;
370		bgid: int;
371	S =>	# import public screen
372	t =>	# adjust window z order
373		top: int;
374		ids: array of int;
375	v =>	 # flush updates to display
376	y =>	# write pixels
377		id: int;
378		R: Draw->Rect;
379	}
380};
381
382getreq(data : array of byte, ix : int) : (ref Drawreq, string)
383{
384	mlen := 0;
385	err := "short draw message";
386	req : ref Drawreq;
387
388	case int data[ix] {
389	'b' => # alloc image
390		mlen = 1+4+4+1+4+1+(4*4)+(4*4)+4;
391		if (mlen+ix <= len data) {
392			data = data[ix:ix+mlen];
393			r := ref Drawreq.b;
394			r.data = data;
395			r.id = get4(data, OPb_id);
396			r.screenid = get4(data, OPb_screenid);
397			r.refresh = get1(data, OPb_refresh);
398			r.chans = Draw->Chans(get4(data, OPb_chans));
399			r.repl = get1(data, OPb_repl);
400			r.R = getR(data, OPb_R);
401			r.clipR = getR(data, OPb_clipR);
402			r.rrggbbaa = get4(data, OPb_rrggbbaa);
403			req = r;
404		}
405	'A' => # alloc screen
406		mlen = 1+4+4+4+1;
407		if (mlen+ix <= len data) {
408			data = data[ix:ix+mlen];
409			r := ref Drawreq.A;
410			r.data = data;
411			r.id = get4(data, OPA_id);
412			r.imageid = get4(data, OPA_imageid);
413			r.fillid = get4(data, OPA_fillid);
414			req = r;
415		}
416	'c' => # set clipR
417		mlen = 1+4+1+(4*4);
418		if (mlen+ix <= len data) {
419			data = data[ix:ix+mlen];
420			r := ref Drawreq.c;
421			r.data = data;
422			r.dstid = get4(data, OPc_dstid);
423			r.repl = get1(data, OPc_repl);
424			r.clipR = getR(data, OPc_clipR);
425			req = r;
426		}
427	'd' => # draw
428		mlen = 1+4+4+4+(4*4)+(2*4)+(2*4);
429		if (mlen+ix <= len data) {
430			data = data[ix:ix+mlen];
431			r := ref Drawreq.d;
432			r.data = data;
433			r.dstid = get4(data, OPd_dstid);
434			r.srcid = get4(data, OPd_srcid);
435			r.maskid = get4(data, OPd_maskid);
436			req = r;
437		}
438	'D' =>
439		# debug mode
440		mlen = 1+1;
441		if (mlen+ix <= len data) {
442			req = ref Drawreq.v;
443			req.data = data[ix:ix+mlen];
444		}
445	'e' or
446	'E' => # ellipse
447		mlen = 1+4+4+(2*4)+4+4+4+(2*4)+4+4;
448		if (mlen+ix <= len data) {
449			data = data[ix:ix+mlen];
450			r := ref Drawreq.e;
451			r.data = data;
452			r.dstid = get4(data, OPe_dstid);
453			r.srcid = get4(data, OPe_srcid);
454			req = r;
455		}
456	'f' => # free image
457		mlen = 1+4;
458		if (mlen+ix <= len data) {
459			data = data[ix:ix+mlen];
460			r := ref Drawreq.f;
461			r.data = data;
462			r.id = get4(data, OPf_id);
463			req = r;
464		}
465	'F' => # free screen
466		mlen = 1+4;
467		if (mlen+ix <= len data) {
468			data = data[ix:ix+mlen];
469			r := ref Drawreq.f;
470			r.data = data;
471			r.id = get4(data, OPF_id);
472			req = r;
473		}
474	'i' =>	 # alloc font
475		mlen = 1+4+4+1;
476		if (mlen+ix <= len data) {
477			data = data[ix:ix+mlen];
478			r := ref Drawreq.i;
479			r.data = data;
480			r.fontid = get4(data, OPi_fontid);
481			r.nchars = get4(data, OPi_nchars);
482			r.ascent = get1(data, OPi_ascent);
483			req = r;
484		}
485	'l' =>	 # load font char
486		mlen = 1+4+4+2+(4*4)+(2*4)+1+1;
487		if (mlen+ix <= len data) {
488			data = data[ix:ix+mlen];
489			r := ref Drawreq.l;
490			r.data = data;
491			r.fontid = get4(data, OPl_fontid);
492			r.srcid = get4(data, OPl_srcid);
493			r.index = get2(data, OPl_index);
494			r.R = getR(data, OPl_R);
495			r.P = getP(data, OPl_P);
496			r.left = get1(data, OPl_left);
497			r.width = get1(data, OPl_width);
498			req = r;
499		}
500	'L' => # line
501		mlen = 1+4+(2*4)+(2*4)+4+4+4+4+(2*4);
502		if (mlen+ix <= len data) {
503			data = data[ix:ix+mlen];
504			r := ref Drawreq.L;
505			r.data = data;
506			r.dstid = get4(data, OPL_dstid);
507			r.srcid = get4(data, OPL_srcid);
508			req = r;
509		}
510	'n' => # attach to named image
511		mlen = 1+4+1;
512		if (mlen+ix < len data) {
513			mlen += get1(data, ix+OPn_j);
514			if (mlen+ix <= len data) {
515				data = data[ix:ix+mlen];
516				r := ref Drawreq.n;
517				r.data = data;
518				r.dstid = get4(data, OPn_dstid);
519				r.name = string data[OPn_name:];
520				req = r;
521			}
522		}
523	'N' => # name image
524		mlen = 1+4+1+1;
525		if (mlen+ix < len data) {
526			mlen += get1(data, ix+OPN_j);
527			if (mlen+ix <= len data) {
528				data = data[ix:ix+mlen];
529				r := ref Drawreq.N;
530				r.data = data;
531				r.dstid = get4(data, OPN_dstid);
532				r.in = get1(data, OPN_in);
533				r.name = string data[OPN_name:];
534				req = r;
535			}
536		}
537	'o' => # set origins
538		mlen = 1+4+(2*4)+(2*4);
539		if (mlen+ix <= len data) {
540			data = data[ix:ix+mlen];
541			r := ref Drawreq.o;
542			r.data = data;
543			r.id = get4(data, OPo_id);
544			r.rmin = getP(data, OPo_rmin);
545			r.screenrmin = getP(data, OPo_screenrmin);
546			req = r;
547		}
548	'O' => # set next compop
549		mlen = 1+1;
550		if (mlen+ix <= len data) {
551			data = data[ix:ix+mlen];
552			r := ref Drawreq.O;
553			r.data = data;
554			r.op = get1(data, OPO_op);
555			req = r;
556		}
557	'p' or
558	'P' => # polygon
559		mlen = 1+4+2+4+4+4+4+(2*4);
560		if (mlen + ix <= len data) {
561			n := get2(data, ix+OPp_n);
562			nb := coordslen(data, ix+OPp_P0, 2*(n+1));
563			if (nb == -1)
564				err = "bad coords";
565			else {
566				mlen += nb;
567				if (mlen+ix <= len data) {
568					data = data[ix:ix+mlen];
569					r := ref Drawreq.p;
570					r.data = data;
571					r.dstid = get4(data, OPp_dstid);
572					r.srcid = get4(data, OPp_srcid);
573					req = r;
574				}
575			}
576		}
577	'r' =>	 # read pixels
578		mlen = 1+4+(4*4);
579		if (mlen+ix <= len data) {
580			data = data[ix:ix+mlen];
581			r := ref Drawreq.r;
582			r.data = data;
583			r.id = get4(data, OPr_id);
584			r.R = getR(data, OPr_R);
585			req = r;
586		}
587	's' => # text
588		mlen = 1+4+4+4+(2*4)+(4*4)+(2*4)+2;
589		if (ix+mlen <= len data) {
590			ni := get2(data, ix+OPs_ni);
591			mlen += (2*ni);
592			if (mlen+ix <= len data) {
593				data = data[ix:ix+mlen];
594				r := ref Drawreq.s;
595				r.data = data;
596				r.dstid = get4(data, OPs_dstid);
597				r.srcid = get4(data, OPs_srcid);
598				r.fontid = get4(data, OPs_fontid);
599				req = r;
600			}
601		}
602	'x' => # text with bg img
603		mlen = 1+4+4+4+(2*4)+(4*4)+(2*4)+2+4+(2*4);
604		if (ix+mlen <= len data) {
605			ni := get2(data, ix+OPx_ni);
606			mlen += (2*ni);
607			if (mlen+ix <= len data) {
608				data = data[ix:ix+mlen];
609				r := ref Drawreq.x;
610				r.data = data;
611				r.dstid = get4(data, OPx_dstid);
612				r.srcid = get4(data, OPx_srcid);
613				r.fontid = get4(data, OPx_fontid);
614				r.bgid = get4(data, OPx_bgid);
615				req = r;
616			}
617		}
618	'S' => # import public screen
619		mlen = 1+4+4;
620		if (mlen+ix <= len data) {
621			data = data[ix:ix+mlen];
622			req = ref Drawreq.S;
623			req.data = data;
624		}
625	't' => # adjust window z order
626		mlen = 1+1+2;
627		if (ix+mlen<= len data) {
628			nw := get2(data, ix+OPt_nw);
629			mlen += (4*nw);
630			if (mlen+ix <= len data) {
631				data = data[ix:ix+mlen];
632				r := ref Drawreq.t;
633				r.data = data;
634				r.top = get1(data, OPt_top);
635				r.ids = array [nw] of int;
636				for (n := 0; n < nw; n++)
637					r.ids[n] = get4(data, OPt_id + 4*n);
638				req = r;
639			}
640		}
641	'v' => # flush
642		req = ref Drawreq.v;
643		req.data = data[ix:ix+1];
644	'y' or
645	'Y' =>	# write pixels
646		mlen = 1+4+(4*4);
647		if (ix+mlen <= len data) {
648			imgid := get4(data, ix+OPy_id);
649			img := images.lookup(imgid);
650			compd := data[ix] == byte 'Y';
651			r := getR(data, ix+OPy_R);
652			n := imglen(img, data, ix+mlen, r, compd);
653			if (n == -1)
654				err ="bad image data";
655			mlen += n;
656			if (mlen+ix <= len data)
657				req = ref Drawreq.y (data[ix:ix+mlen], imgid, r);
658		}
659	* =>
660		err = "bad draw command";
661	}
662
663	if (req == nil)
664		return (nil, err);
665	return (req, nil);
666}
667
668process(data : array of byte) : (int, string)
669{
670	offset := 0;
671	while (offset < len data) {
672		(req, err) := getreq(data, offset);
673		if (err != nil)
674			return (0, err);
675		offset += len req.data;
676		n := sys->write(drawfd, req.data, len req.data);
677		if (n <= 0)
678			return (n, sys->sprint("[%c] %r", int req.data[0]));
679
680		readn := 0;
681		sendtoviews := 1;
682
683		# actions that must be done before sending to Viewers
684		pick r := req {
685		b =>	# allocate image
686			bwidth := bytesperline(r.R, r.chans);
687			img := ref Image (r.id, 0, r.screenid, r.refresh, r.chans, r.repl, r.R, r.clipR, r.rrggbbaa, nil, r.R.min, bwidth, 0, "");
688			images.add(r.id, img);
689			if (r.screenid != 0) {
690				scr := screens.lookup(r.screenid);
691				scr.addwin(r.id);
692			}
693
694		A =>	# allocate screen
695			scr := ref Screen (r.id, r.imageid, r.fillid, nil);
696			screens.add(scr);
697			# we never allocate public screens on our Viewers
698			put1(r.data, OPA_public, 0);
699			dirty(r.imageid, 0);
700
701		c =>	# set clipr and repl
702			img := images.lookup(r.dstid);
703			img.repl = r.repl;
704			img.clipR = r.clipR;
705
706		d =>	# general draw op
707			dirty(r.dstid, 1);
708			drawop = Draw->SoverD;
709
710		e =>	# draw ellipse
711			dirty(r.dstid, 1);
712			drawop = Draw->SoverD;
713
714		f => # free image
715			# help out Viewers, real work is done later
716			r.img = images.lookup(r.id);
717
718		L =>	# draw line
719			dirty(r.dstid, 1);
720			drawop = Draw->SoverD;
721
722		n =>	# attach to named image
723			img := images.findname(r.name);
724			images.add(r.dstid, img);
725
726		N => # name image
727			img := images.lookup(r.dstid);
728			if (r.in)
729				img.name = r.name;
730			else
731				img.name = nil;
732
733		o => # set image origins
734			img := images.lookup(r.id);
735			deltax := img.lorigin.x - r.rmin.x;
736			deltay := img.lorigin.y - r.rmin.y;
737			w := img.R.max.x - img.R.min.x;
738			h := img.R.max.y - img.R.min.y;
739
740			img.R = Draw->Rect(r.screenrmin, (r.screenrmin.x + w, r.screenrmin.y + h));
741			img.clipR = Draw->Rect((img.clipR.min.x - deltax, img.clipR.min.y - deltay), (img.clipR.max.x - deltax, img.clipR.max.y - deltay));
742			img.lorigin = r.rmin;
743
744		O =>	# set compositing op
745			drawop = r.op;
746
747		p =>	# draw polygon
748			dirty(r.dstid, 1);
749			drawop = Draw->SoverD;
750
751		r => # read pixels
752			img := images.lookup(r.id);
753			bpl := bytesperline(r.R, img.chans);
754			readn = bpl * (r.R.max.y - r.R.min.y);
755
756		s =>	# draw text
757			dirty(r.dstid, 1);
758			drawop = Draw->SoverD;
759
760		x => # draw text with bg
761			dirty(r.dstid, 1);
762			drawop = Draw->SoverD;
763
764		t =>	# adjust window z order
765			if (r.ids != nil) {
766				img := images.lookup(r.ids[0]);
767				scr := screens.lookup(img.screenid);
768				scr.setz(r.ids, r.top);
769			}
770
771		y =>	# write pixels
772			dirty(r.id, 1);
773		}
774
775		if (readn) {
776			rdata := array [readn] of byte;
777			if (sys->read(drawfd, rdata, readn) == readn)
778				readdata = rdata;
779		}
780
781		for (vs := viewers; vs != nil; vs = tl vs) {
782			v := hd vs;
783			v.process(req);
784		}
785
786		# actions that must only be done after sending to Viewers
787		pick r := req {
788		f => # free image
789			img := images.lookup(r.id);
790			if (img.screenid != 0) {
791				scr := screens.lookup(img.screenid);
792				scr.delwin(img.id);
793			}
794			images.del(r.id);
795
796		F =>	# free screen
797			scr := screens.lookup(r.id);
798			for (i := 0; i < len scr.windows; i++) {
799				img := images.lookup(scr.windows[i]);
800				img.screenid = 0;
801			}
802			screens.del(r.id);
803
804		i => # convert image to font
805			img := images.lookup(r.fontid);
806			font := ref Font;
807			font.ascent = r.ascent;
808			font.chars = array[r.nchars] of ref Fontchar;
809			img.font = font;
810
811		l =>	# load a char into font
812			img := images.lookup(r.fontid);
813			font := img.font;
814			fc := ref Fontchar(r.srcid, r.R, r.P, r.left, r.width);
815			font.chars[r.index] = fc;
816		}
817	}
818	return (offset, nil);
819}
820
821coordslen(data : array of byte, ix, n : int) : int
822{
823	start := ix;
824	dlen := len data;
825	if (ix == dlen)
826		return -1;
827	while (ix < dlen && n) {
828		n--;
829		if ((int data[ix++]) & 16r80)
830			ix += 2;
831	}
832	if (n)
833		return -1;
834	return ix - start;
835}
836
837
838imglen(i : ref Image, data : array of byte, ix : int, r : Draw->Rect, comp : int) : int
839{
840	bpl := bytesperline(r, i.chans);
841	if (!comp)
842		return (r.max.y - r.min.y) * bpl;
843	y := r.min.y;
844	lineix := byteaddr(i, r.min);
845	elineix := lineix+bpl;
846	start := ix;
847	eix := len data;
848	for (;;) {
849		if (lineix == elineix) {
850			if (++y == r.max.y)
851				break;
852			lineix = byteaddr(i, Point(r.min.x, y));
853			elineix = lineix+bpl;
854		}
855		if (ix == eix)	# buffer too small
856			return -1;
857		c := int data[ix++];
858		if (c >= 128) {
859			for (cnt := c-128+1; cnt != 0; --cnt) {
860				if (ix == eix)	# buffer too small
861					return -1;
862				if (lineix == elineix)	# phase error
863					return -1;
864				lineix++;
865				ix++;
866			}
867		} else {
868			if (ix == eix)	# short buffer
869				return -1;
870			ix++;
871			for (cnt := (c >> 2)+3; cnt != 0; --cnt) {
872				if (lineix == elineix) # phase error
873					return -1;
874				lineix++;
875			}
876		}
877	}
878	return ix-start;
879}
880
881byteaddr(i: ref Image, p: Point): int
882{
883	x := p.x - i.lorigin.x;
884	y := p.y - i.lorigin.y;
885	bits := i.chans.depth();
886	if (bits == 0)
887		# invalid chans
888		return 0;
889	return (y*i.bwidth)+(x<<3)/bits;
890}
891
892bytesperline(r: Draw->Rect, chans: Draw->Chans): int
893{
894	d := chans.depth();
895	l, t: int;
896
897	if(r.min.x >= 0){
898		l = (r.max.x*d+8-1)/8;
899		l -= (r.min.x*d)/8;
900	}else{			# make positive before divide
901		t = (-r.min.x*d+8-1)/8;
902		l = t+(r.max.x*d+8-1)/8;
903	}
904	return l;
905}
906
907get1(data : array of byte, ix : int) : int
908{
909	return int data[ix];
910}
911
912put1(data : array of byte, ix, val : int)
913{
914	data[ix] = byte val;
915}
916
917get2(data : array of byte, ix : int) : int
918{
919	return int data[ix] | ((int data[ix+1]) << 8);
920}
921
922put2(data : array of byte, ix, val : int)
923{
924	data[ix] = byte val;
925	data[ix+1] = byte (val >> 8);
926}
927
928get4(data : array of byte, ix : int) : int
929{
930	return int data[ix] | ((int data[ix+1]) << 8) | ((int data[ix+2]) << 16) | ((int data[ix+3]) << 24);
931}
932
933put4(data : array of byte, ix, val : int)
934{
935	data[ix] = byte val;
936	data[ix+1] = byte (val >> 8);
937	data[ix+2] = byte (val >> 16);
938	data[ix+3] = byte (val >> 24);
939}
940
941getP(data : array of byte, ix : int) : Draw->Point
942{
943	x := int data[ix] | ((int data[ix+1]) << 8) | ((int data[ix+2]) << 16) | ((int data[ix+3]) << 24);
944	ix += 4;
945	y := int data[ix] | ((int data[ix+1]) << 8) | ((int data[ix+2]) << 16) | ((int data[ix+3]) << 24);
946	return Draw->Point(x, y);
947}
948
949putP(data : array of byte, ix : int, P : Draw->Point)
950{
951	val := P.x;
952	data[ix] = byte val;
953	data[ix+1] = byte (val >> 8);
954	data[ix+2] = byte (val >> 16);
955	data[ix+3] = byte (val >> 24);
956	val = P.y;
957	ix += 4;
958	data[ix] = byte val;
959	data[ix+1] = byte (val >> 8);
960	data[ix+2] = byte (val >> 16);
961	data[ix+3] = byte (val >> 24);
962}
963
964getR(data : array of byte, ix : int) : Draw->Rect
965{
966	minx := int data[ix] | ((int data[ix+1]) << 8) | ((int data[ix+2]) << 16) | ((int data[ix+3]) << 24);
967	ix += 4;
968	miny := int data[ix] | ((int data[ix+1]) << 8) | ((int data[ix+2]) << 16) | ((int data[ix+3]) << 24);
969	ix += 4;
970	maxx :=  int data[ix] | ((int data[ix+1]) << 8) | ((int data[ix+2]) << 16) | ((int data[ix+3]) << 24);
971	ix += 4;
972	maxy :=  int data[ix] | ((int data[ix+1]) << 8) | ((int data[ix+2]) << 16) | ((int data[ix+3]) << 24);
973
974	return Draw->Rect(Draw->Point(minx, miny), Draw->Point(maxx, maxy));
975}
976
977putR(data : array of byte, ix : int , R : Draw->Rect)
978{
979	val := R.min.x;
980	data[ix] = byte val;
981	data[ix+1] = byte (val >> 8);
982	data[ix+2] = byte (val >> 16);
983	data[ix+3] = byte (val >> 24);
984	val = R.min.y;
985	ix += 4;
986	data[ix] = byte val;
987	data[ix+1] = byte (val >> 8);
988	data[ix+2] = byte (val >> 16);
989	data[ix+3] = byte (val >> 24);
990	val = R.max.x;
991	ix += 4;
992	data[ix] = byte val;
993	data[ix+1] = byte (val >> 8);
994	data[ix+2] = byte (val >> 16);
995	data[ix+3] = byte (val >> 24);
996	val = R.max.y;
997	ix += 4;
998	data[ix] = byte val;
999	data[ix+1] = byte (val >> 8);
1000	data[ix+2] = byte (val >> 16);
1001	data[ix+3] = byte (val >> 24);
1002}
1003
1004dirty(id, v : int)
1005{
1006	img := images.lookup(id);
1007	img.dirty = v;
1008}
1009
1010Screen.setz(s : self ref Screen, z : array of int, top : int)
1011{
1012	old := s.windows;
1013	nw := array [len old] of int;
1014	# use a dummy idmap to ensure uniqueness;
1015	ids := Idmap.new();
1016	ix := 0;
1017	if (top) {
1018		for (i := 0; i < len z; i++) {
1019			if (ids.lookup(z[i]) == -1) {
1020				ids.add(z[i], 0);
1021				nw[ix++] = z[i];
1022			}
1023		}
1024	}
1025	for (i := 0; i < len old; i++) {
1026		if (ids.lookup(old[i]) == -1) {
1027			ids.add(old[i], 0);
1028			nw[ix++] = old[i];
1029		}
1030	}
1031	if (!top) {
1032		for (i = 0; i < len z; i++) {
1033			if (ids.lookup(z[i]) == -1) {
1034				ids.add(z[i], 0);
1035				nw[ix++] = z[i];
1036			}
1037		}
1038	}
1039	s.windows = nw;
1040}
1041
1042Screen.addwin(s : self ref Screen, wid : int)
1043{
1044	nw :=  array [len s.windows + 1] of int;
1045	nw[0] = wid;
1046	nw[1:] = s.windows;
1047	s.windows = nw;
1048}
1049
1050Screen.delwin(s : self ref Screen, wid : int)
1051{
1052	if (len s.windows == 1) {
1053		# assert s.windows[0] == wid
1054		s.windows = nil;
1055		return;
1056	}
1057	nw := array [len s.windows - 1] of int;
1058	ix := 0;
1059	for (i := 0; i < len s.windows; i++) {
1060		if (s.windows[i] == wid)
1061			continue;
1062		nw[ix++] = s.windows[i];
1063	}
1064	s.windows = nw;
1065}
1066
1067Idmap.new() : ref Idmap
1068{
1069	m := ref Idmap;
1070	m.buckets = array[256] of ref Idpair;
1071	return m;
1072}
1073
1074Idmap.add(m : self ref Idmap, key, val : int)
1075{
1076	h := key & 16rff;
1077	m.buckets[h] = ref Idpair (key, val, m.buckets[h]);
1078}
1079
1080Idmap.del(m : self ref Idmap, key : int)
1081{
1082	h := key &16rff;
1083	prev := m.buckets[h];
1084	if (prev == nil)
1085		return;
1086	if (prev.key == key) {
1087		m.buckets[h] = m.buckets[h].next;
1088		return;
1089	}
1090	for (idp := prev.next; idp != nil; idp = idp.next) {
1091		if (idp.key == key)
1092			break;
1093		prev = idp;
1094	}
1095	if (idp != nil)
1096		prev.next = idp.next;
1097}
1098
1099Idmap.lookup(m :self ref Idmap, key : int) : int
1100{
1101	h := key &16rff;
1102	for (idp := m.buckets[h]; idp != nil; idp = idp.next) {
1103		if (idp.key == key)
1104			return idp.val;
1105	}
1106	return -1;
1107}
1108
1109Imageset.new() : ref Imageset
1110{
1111	s := ref Imageset;
1112	s.images = array [32] of ref Image;
1113	s.ixmap = Idmap.new();
1114	for (i := 0; i < len s.images; i++)
1115		s.freelist = i :: s.freelist;
1116	return s;
1117}
1118
1119Imageset.add(s: self ref Imageset, id: int, img: ref Image)
1120{
1121	if (s.freelist == nil) {
1122		n := 2 * len s.images;
1123		ni := array [n] of ref Image;
1124		ni[:] = s.images;
1125		for (i := len s.images; i < n; i++)
1126			s.freelist = i :: s.freelist;
1127		s.images = ni;
1128	}
1129	ix := hd s.freelist;
1130	s.freelist = tl s.freelist;
1131	s.images[ix] = img;
1132	s.ixmap.add(id, ix);
1133	img.refc++;
1134}
1135
1136Imageset.del(s: self ref Imageset, id: int)
1137{
1138	ix := s.ixmap.lookup(id);
1139	if (ix == -1)
1140		return;
1141	img := s.images[ix];
1142	if (img != nil)
1143		img.refc--;
1144	s.images[ix] = nil;
1145	s.freelist = ix :: s.freelist;
1146	s.ixmap.del(id);
1147}
1148
1149Imageset.lookup(s : self ref Imageset, id : int ) : ref Image
1150{
1151	ix := s.ixmap.lookup(id);
1152	if (ix == -1)
1153		return nil;
1154	return s.images[ix];
1155}
1156
1157Imageset.findname(s: self ref Imageset, name: string): ref Image
1158{
1159	for (ix := 0; ix < len s.images; ix++) {
1160		img := s.images[ix];
1161		if (img != nil && img.name == name)
1162			return img;
1163	}
1164	return nil;
1165}
1166
1167Screenset.new() : ref Screenset
1168{
1169	s := ref Screenset;
1170	s.screens = array [32] of ref Screen;
1171	s.ixmap = Idmap.new();
1172	for (i := 0; i < len s.screens; i++)
1173		s.freelist = i :: s.freelist;
1174	return s;
1175}
1176
1177Screenset.add(s : self ref Screenset, scr : ref Screen)
1178{
1179	if (s.freelist == nil) {
1180		n := 2 * len s.screens;
1181		ns := array [n] of ref Screen;
1182		ns[:] = s.screens;
1183		for (i := len s.screens; i < n; i++)
1184			s.freelist = i :: s.freelist;
1185		s.screens = ns;
1186	}
1187	ix := hd s.freelist;
1188	s.freelist = tl s.freelist;
1189	s.screens[ix] = scr;
1190	s.ixmap.add(scr.id, ix);
1191}
1192
1193Screenset.del(s : self ref Screenset, id : int)
1194{
1195	ix := s.ixmap.lookup(id);
1196	if (ix == -1)
1197		return;
1198	s.screens[ix] = nil;
1199	s.freelist = ix :: s.freelist;
1200	s.ixmap.del(id);
1201}
1202
1203Screenset.lookup(s : self ref Screenset, id : int ) : ref Screen
1204{
1205	ix := s.ixmap.lookup(id);
1206	if (ix == -1)
1207		return nil;
1208	return s.screens[ix];
1209}
1210
1211
1212Viewer : adt {
1213	imgmap:	ref Idmap;
1214	scrmap:	ref Idmap;
1215	chanmap:	ref Idmap;	# maps to 1 for images that require chan conversion
1216
1217	imageid:	int;
1218	screenid:	int;
1219	whiteid:	int;
1220	hungup:	int;
1221	dchans:	Draw->Chans;	# chans.desc of remote display img
1222
1223	# temporary image for chan conversion
1224	tmpid:	int;
1225	tmpR:	Draw->Rect;
1226
1227	output:	chan of (array of byte, chan of string);
1228
1229	new:		fn(fd: ref Sys->FD, pubscr: int, chans: Draw->Chans): string;
1230	process:	fn(v: self ref Viewer, req: ref Drawreq);
1231	getimg:	fn(v: self ref Viewer, id: int): int;
1232	getscr:	fn(v: self ref Viewer, id, win: int): (int, int);
1233	copyimg:	fn(v: self ref Viewer, img: ref Image, id: int);
1234	chanconv:	fn(v: self ref Viewer, img: ref Image, id: int, r: Rect, ymsg: array of byte);
1235};
1236
1237vwriter(fd : ref Sys->FD, datac : chan of array of byte, nc : chan of string)
1238{
1239	for (;;) {
1240		data := <- datac;
1241		if (data == nil)
1242			return;
1243		n := sys->write(fd, data, len data);
1244		if (n != len data) {
1245#			sys->print("[%c]: %r\n", int data[0]);
1246#			sys->print("[%c] datalen %d got %d error: %r\n", int data[0], len data, n);
1247			nc <-= sys->sprint("%r");
1248		} else {
1249#			sys->print("[%c]", int data[0]);
1250			nc <-= nil;
1251		}
1252	}
1253}
1254
1255vbmsg : adt {
1256	data : array of byte;
1257	rc : chan of string;
1258	next : cyclic ref vbmsg;
1259};
1260
1261vbuffer(v : ref Viewer, fd : ref Sys->FD)
1262{
1263	ioc := v.output;
1264	datac := chan of array of byte;
1265	errc := chan of string;
1266	spawn vwriter(fd, datac, errc);
1267	fd = nil;
1268
1269	msghd : ref vbmsg;
1270	msgtl : ref vbmsg;
1271
1272Loop:
1273	for (;;) alt {
1274	(data, rc) := <- ioc =>
1275		if (data == nil)
1276			break Loop;
1277		if (msgtl != nil) {
1278			if (msgtl != msghd && msgtl.rc == nil && (len msgtl.data + len data) <= Sys->ATOMICIO) {
1279				ndata := array [len msgtl.data + len data] of byte;
1280				ndata[:] = msgtl.data;
1281				ndata[len msgtl.data:] = data;
1282				msgtl.data = ndata;
1283				msgtl.rc = rc;
1284			} else {
1285				msgtl.next = ref vbmsg (data, rc, nil);
1286				msgtl = msgtl.next;
1287			}
1288		} else {
1289			msghd = ref vbmsg (data, rc, nil);
1290			msgtl = msghd;
1291			datac <-= data;
1292		}
1293	err := <- errc =>
1294		if (msghd.rc != nil)
1295			msghd.rc <- = err;
1296		msghd = msghd.next;
1297		if (msghd != nil)
1298			datac <-= msghd.data;
1299		else
1300			msgtl = nil;
1301		if (err == Ehungup) {
1302			nhangups++;
1303			v.hungup = 1;
1304		}
1305	}
1306	# shutdown vwriter (may be blocked sending on errc)
1307	for (;;) alt {
1308	<- errc =>
1309		;
1310	datac <- = nil =>
1311		return;
1312	}
1313}
1314
1315Viewer.new(fd: ref Sys->FD, pubscr: int, chans: Draw->Chans): string
1316{
1317	v := ref Viewer;
1318	v.output = chan of (array of byte, chan of string);
1319	spawn vbuffer(v, fd);
1320
1321	v.imgmap = Idmap.new();
1322	v.scrmap = Idmap.new();
1323	v.chanmap = Idmap.new();
1324	v.imageid = 0;
1325	v.screenid = pubscr;
1326	v.hungup = 0;
1327	v.dchans = chans;
1328	v.tmpid = 0;
1329	v.tmpR = Rect((0,0), (0,0));
1330
1331#D := array[1+1] of byte;
1332#D[0] = byte 'D';
1333#D[1] = byte 1;
1334#v.output <-= (D, nil);
1335
1336	reply := chan of string;
1337	# import remote public screen into our remote draw client
1338	S := array [1+4+4] of byte;
1339	S[0] = byte 'S';
1340	put4(S, OPS_id, pubscr);
1341	put4(S, OPS_chans, chans.desc);
1342	v.output <-= (S, reply);
1343	err := <- reply;
1344	if (err != nil) {
1345		v.output <-= (nil, nil);
1346		return err;
1347	}
1348
1349	# create remote window
1350	dispid := ++v.imageid;
1351	b := array [1+4+4+1+4+1+(4*4)+(4*4)+4] of byte;
1352	b[0] = byte 'b';
1353	put4(b, OPb_id, dispid);
1354	put4(b, OPb_screenid, pubscr);
1355	put1(b, OPb_refresh, 0);
1356	put4(b, OPb_chans, chans.desc);
1357	put1(b, OPb_repl, 0);
1358	putR(b, OPb_R, drawR);
1359	putR(b, OPb_clipR, drawR);
1360	put4(b, OPb_rrggbbaa, Draw->White);
1361	v.output <-= (b, reply);
1362	err = <- reply;
1363	if (err != nil) {
1364		v.output <-= (nil, nil);
1365		return err;
1366	}
1367
1368	# map local display image id to remote window image id
1369	v.imgmap.add(0, dispid);
1370	if (!drawchans.eq(chans))
1371		# writepixels on this image must be chan converted
1372		v.chanmap.add(0, 1);
1373
1374	# create 'white' repl image for use as mask
1375	v.whiteid = ++v.imageid;
1376	put4(b, OPb_id, v.whiteid);
1377	put4(b, OPb_screenid, 0);
1378	put1(b, OPb_refresh, 0);
1379	put4(b, OPb_chans, (Draw->RGBA32).desc);
1380	put1(b, OPb_repl, 1);
1381	putR(b, OPb_R, Rect((0,0), (1,1)));
1382	putR(b, OPb_clipR, Rect((-16r3FFFFFFF, -16r3FFFFFFF), (16r3FFFFFFF, 16r3FFFFFFF)));
1383	put4(b, OPb_rrggbbaa, Draw->White);
1384	v.output <-= (b, reply);
1385	err = <- reply;
1386	if (err != nil) {
1387		v.output <-= (nil, nil);
1388		return err;
1389	}
1390
1391	img := images.lookup(0);
1392	key := chan of int;
1393	drawlock <- = key;
1394	v.copyimg(img, dispid);
1395
1396	O := array [1+1] of byte;
1397	O[0] = byte 'O';
1398	O[1] = byte drawop;
1399	v.output <-= (O, nil);
1400
1401	flush := array [1] of byte;
1402	flush[0] = byte 'v';
1403	v.output <- = (flush, nil);
1404	viewers = v :: viewers;
1405	key <-= 1;
1406	return nil;
1407}
1408
1409Viewer.process(v : self ref Viewer, req : ref Drawreq)
1410{
1411	data := req.data;
1412	pick r := req {
1413	b => # allocate image
1414		imgid := ++v.imageid;
1415		if (r.screenid != 0) {
1416			(scrid, mapchans) := v.getscr(r.screenid, 0);
1417			put4(data, OPb_screenid, scrid);
1418			if (mapchans) {
1419				put4(data, OPb_chans, v.dchans.desc);
1420				v.chanmap.add(r.id, 1);
1421			}
1422		}
1423		v.imgmap.add(r.id, imgid);
1424		put4(data, OPb_id, imgid);
1425
1426	A => # allocate screen
1427		imgid := v.getimg(r.imageid);
1428		put4(data, OPA_fillid, v.getimg(r.fillid));
1429		put4(data, OPA_imageid, imgid);
1430		reply := chan of string;
1431		for (i := 0; i < 25; i++) {
1432			put4(data, OPA_id, ++v.screenid);
1433			v.output <-= (data, reply);
1434			if (<-reply == nil) {
1435				v.scrmap.add(r.id, v.screenid);
1436				return;
1437			}
1438		}
1439		return;
1440
1441	c => # set clipr and repl
1442		put4(data, OPc_dstid, v.getimg(r.dstid));
1443
1444	d =>	 # general draw op
1445		dstid := v.imgmap.lookup(r.dstid);
1446		if (dstid == -1) {
1447			# don't do draw op as getimg() will do a writepixels
1448			v.getimg(r.dstid);
1449			return;
1450		}
1451		put4(data, OPd_maskid, v.getimg(r.maskid));
1452		put4(data, OPd_srcid, v.getimg(r.srcid));
1453		put4(data, OPd_dstid, dstid);
1454
1455	e =>	 # draw ellipse
1456		dstid := v.imgmap.lookup(r.dstid);
1457		if (dstid == -1) {
1458			# don't do draw op as getimg() will do a writepixels
1459			v.getimg(r.dstid);
1460			return;
1461		}
1462		put4(data, OPe_srcid, v.getimg(r.srcid));
1463		put4(data, OPe_dstid, dstid);
1464
1465	f => # free image
1466		id := v.imgmap.lookup(r.img.id);
1467		if (id == -1)
1468			# Viewer has never seen this image - ignore
1469			return;
1470		v.imgmap.del(r.id);
1471		# Viewers alias named images - only delete if last reference
1472		if (r.img.refc > 1)
1473			return;
1474		v.chanmap.del(r.img.id);
1475		put4(data, OPf_id, id);
1476
1477	F => # free screen
1478		id := v.scrmap.lookup(r.id);
1479		scr := screens.lookup(r.id);
1480		# image and fill are free'd separately
1481		#v.imgmap.del(scr.imageid);
1482		#v.imgmap.del(scr.fillid);
1483		if (id == -1)
1484			return;
1485		put4(data, OPF_id, id);
1486
1487	i => # convert image to font
1488		put4(data, OPi_fontid, v.getimg(r.fontid));
1489
1490	l => # load a char into font
1491		put4(data, OPl_srcid, v.getimg(r.srcid));
1492		put4(data, OPl_fontid, v.getimg(r.fontid));
1493
1494	L => # draw line
1495		dstid := v.imgmap.lookup(r.dstid);
1496		if (dstid == -1) {
1497			# don't do draw op as getimg() will do a writepixels
1498			v.getimg(r.dstid);
1499			return;
1500		}
1501		put4(data, OPL_srcid, v.getimg(r.srcid));
1502		put4(data, OPL_dstid, dstid);
1503
1504#	n =>	# attach to named image
1505#	N =>	# name
1506#		Handled by id remapping to avoid clashes in namespace of remote viewers.
1507#		If it is a name we know then the id is remapped within the images Imageset
1508#		Otherwise, there is nothing we can do other than ignore all ops related to the id.
1509
1510	o =>	 # set image origins
1511		id := v.imgmap.lookup(r.id);
1512		if (id == -1)
1513			# Viewer has never seen this image - ignore
1514			return;
1515		put4(data, OPo_id, id);
1516
1517	O =>	# set next compositing op
1518		;
1519
1520	p =>	 # draw polygon
1521		dstid := v.imgmap.lookup(r.dstid);
1522		if (dstid == -1) {
1523			# don't do draw op as getimg() will do a writepixels
1524			v.getimg(r.dstid);
1525			return;
1526		}
1527		put4(data, OPp_srcid, v.getimg(r.srcid));
1528		put4(data, OPp_dstid, dstid);
1529
1530	s => # draw text
1531		dstid := v.imgmap.lookup(r.dstid);
1532		if (dstid == -1) {
1533			# don't do draw op as getimg() will do a writepixels
1534			v.getimg(r.dstid);
1535			return;
1536		}
1537		put4(data, OPs_fontid, v.getimg(r.fontid));
1538		put4(data, OPs_srcid, v.getimg(r.srcid));
1539		put4(data, OPs_dstid, dstid);
1540
1541	x =>	# draw text with bg
1542		dstid := v.imgmap.lookup(r.dstid);
1543		if (dstid == -1) {
1544			# don't do draw op as getimg() will do a writepixels
1545			v.getimg(r.dstid);
1546			return;
1547		}
1548		put4(data, OPx_fontid, v.getimg(r.fontid));
1549		put4(data, OPx_srcid, v.getimg(r.srcid));
1550		put4(data, OPx_bgid, v.getimg(r.bgid));
1551		put4(data, OPx_dstid, dstid);
1552
1553	t => # adjust window z order
1554		for (i := 0; i < len r.ids; i++)
1555			put4(data, OPt_id + 4*i, v.getimg(r.ids[i]));
1556
1557	v => # flush updates to display
1558			;
1559
1560	y => # write pixels
1561		id := v.imgmap.lookup(r.id);
1562		if (id == -1) {
1563			# don't do draw op as getimg() will do a writepixels
1564			v.getimg(r.id);
1565			return;
1566		}
1567		if (!drawchans.eq(v.dchans) && v.chanmap.lookup(r.id) != -1) {
1568			# chans clash
1569			img := images.lookup(r.id);
1570			# copy data as other Viewers may alter contents
1571			copy := (array [len data] of byte)[:] = data;
1572			v.chanconv(img, id, r.R, copy);
1573			return;
1574		}
1575		put4(data, OPy_id, id);
1576
1577	* =>
1578		return;
1579	}
1580	# send out a copy of the data as other Viewers may alter contents
1581	copy := array [len data] of byte;
1582	copy[:] = data;
1583	v.output <-= (copy, nil);
1584}
1585
1586Viewer.getimg(v: self ref Viewer, localid: int) : int
1587{
1588	remid := v.imgmap.lookup(localid);
1589	if (remid != -1)
1590		return remid;
1591
1592	img := images.lookup(localid);
1593	if (img.id != localid) {
1594		# attached via name, see if we have the aliased image
1595		remid = v.imgmap.lookup(img.id);
1596		if (remid != -1) {
1597			# we have it, add mapping to save us this trouble next time
1598			v.imgmap.add(localid, remid);
1599			return remid;
1600		}
1601	}
1602	# is the image a window?
1603	scrid := 0;
1604	mapchans := 0;
1605	if (img.screenid != 0)
1606		(scrid, mapchans) = v.getscr(img.screenid, img.id);
1607
1608	vid := ++v.imageid;
1609	# create the image
1610	# note: clipr for image creation has to be based on screen co-ords
1611	clipR := img.clipR.subpt(img.lorigin);
1612	clipR = clipR.addpt(img.R.min);
1613	b := array [1+4+4+1+4+1+(4*4)+(4*4)+4] of byte;
1614	b[0] = byte 'b';
1615	put4(b, OPb_id, vid);
1616	put4(b, OPb_screenid, scrid);
1617	put1(b, OPb_refresh, 0);
1618	if (mapchans)
1619		put4(b, OPb_chans, v.dchans.desc);
1620	else
1621		put4(b, OPb_chans, img.chans.desc);
1622	put1(b, OPb_repl, img.repl);
1623	putR(b, OPb_R, img.R);
1624	putR(b, OPb_clipR, clipR);
1625	put4(b, OPb_rrggbbaa, img.rrggbbaa);
1626	v.output <-= (b, nil);
1627
1628	v.imgmap.add(img.id, vid);
1629	if (mapchans)
1630		v.chanmap.add(img.id, 1);
1631
1632	# set the origin
1633	if (img.lorigin.x != img.R.min.x || img.lorigin.y != img.R.min.y) {
1634		o := array [1+4+(2*4)+(2*4)] of byte;
1635		o[0] = byte 'o';
1636		put4(o, OPo_id, vid);
1637		putP(o, OPo_rmin, img.lorigin);
1638		putP(o, OPo_screenrmin, img.R.min);
1639		v.output <-= (o, nil);
1640	}
1641
1642	# is the image a font?
1643	if (img.font != nil) {
1644		f := img.font;
1645		i := array [1+4+4+1] of byte;
1646		i[0] = byte 'i';
1647		put4(i, OPi_fontid, vid);
1648		put4(i, OPi_nchars, len f.chars);
1649		put1(i, OPi_ascent, f.ascent);
1650		v.output <-= (i, nil);
1651
1652		for (index := 0; index < len f.chars; index++) {
1653			ch := f.chars[index];
1654			if (ch == nil)
1655				continue;
1656			l := array [1+4+4+2+(4*4)+(2*4)+1+1] of byte;
1657			l[0] = byte 'l';
1658			put4(l, OPl_fontid, vid);
1659			put4(l, OPl_srcid, v.getimg(ch.srcid));
1660			put2(l, OPl_index, index);
1661			putR(l, OPl_R, ch.R);
1662			putP(l, OPl_P, ch.P);
1663			put1(l, OPl_left, ch.left);
1664			put1(l, OPl_width, ch.width);
1665			v.output <-= (l, nil);
1666		}
1667	}
1668
1669	# if 'dirty' then writepixels
1670	if (img.dirty)
1671		v.copyimg(img, vid);
1672
1673	return vid;
1674}
1675
1676Viewer.copyimg(v : self ref Viewer, img : ref Image, id : int)
1677{
1678	dx := img.R.max.x - img.R.min.x;
1679	dy := img.R.max.y - img.R.min.y;
1680	srcR := Rect (img.lorigin, (img.lorigin.x + dx, img.lorigin.y + dy));
1681	bpl := bytesperline(srcR, img.chans);
1682	rlen : con 1+4+(4*4);
1683	ystep := (Sys->ATOMICIO - rlen)/ bpl;
1684	minx := srcR.min.x;
1685	maxx := srcR.max.x;
1686	maxy := srcR.max.y;
1687
1688	chanconv := 0;
1689	if (!drawchans.eq(v.dchans) && v.chanmap.lookup(img.id) != -1)
1690		chanconv = 1;
1691
1692	for (y := img.lorigin.y; y < maxy; y += ystep) {
1693		if (y + ystep > maxy)
1694			ystep = (maxy - y);
1695		R := Draw->Rect((minx, y), (maxx, y+ystep));
1696		r := array [rlen] of byte;
1697		r[0] = byte 'r';
1698		put4(r, OPr_id, img.id);
1699		putR(r, OPr_R, R);
1700		if (sys->write(drawfd, r, len r) != len r)
1701			break;
1702
1703		nb := bpl * ystep;
1704		ymsg := array [1+4+(4*4)+nb] of byte;
1705		ymsg[0] = byte 'y';
1706#		put4(ymsg, OPy_id, id);
1707		putR(ymsg, OPy_R, R);
1708		n := sys->read(drawfd, ymsg[OPy_data:], nb);
1709		if (n != nb)
1710			break;
1711		if (chanconv)
1712			v.chanconv(img, id, R, ymsg);
1713		else {
1714			put4(ymsg, OPy_id, id);
1715			v.output <-= (ymsg, nil);
1716		}
1717	}
1718}
1719
1720Viewer.chanconv(v: self ref Viewer, img: ref Image, id: int, r: Rect, ymsg: array of byte)
1721{
1722	# check origin matches and enough space in conversion image
1723	if (!(img.lorigin.eq(v.tmpR.min) && r.inrect(v.tmpR))) {
1724		# create new tmp image
1725		if (v.tmpid != 0) {
1726			f := array [1+4] of byte;
1727			f[0] = byte 'f';
1728			put4(f, OPf_id, v.tmpid);
1729			v.output <-= (f, nil);
1730		}
1731		v.tmpR = Rect((0,0), (img.R.dx(), img.R.dy())).addpt(img.lorigin);
1732		v.tmpid = ++v.imageid;
1733		b := array [1+4+4+1+4+1+(4*4)+(4*4)+4] of byte;
1734		b[0] = byte 'b';
1735		put4(b, OPb_id, v.tmpid);
1736		put4(b, OPb_screenid, 0);
1737		put1(b, OPb_refresh, 0);
1738		put4(b, OPb_chans, drawchans.desc);
1739		put1(b, OPb_repl, 0);
1740		putR(b, OPb_R, v.tmpR);
1741		putR(b, OPb_clipR, v.tmpR);
1742		put4(b, OPb_rrggbbaa, Draw->Nofill);
1743		v.output <-= (b, nil);
1744	}
1745	# writepixels to conversion image
1746	put4(ymsg, OPy_id, v.tmpid);
1747	v.output <-= (ymsg, nil);
1748
1749	# ensure that drawop is Draw->S
1750	if (drawop != Draw->S) {
1751		O := array [1+1] of byte;
1752		O[0] = byte 'O';
1753		put1(O, OPO_op, Draw->S);
1754		v.output <-= (O, nil);
1755	}
1756	# blit across to real target
1757	d := array [1+4+4+4+(4*4)+(2*4)+(2*4)] of byte;
1758	d[0] = byte 'd';
1759	put4(d, OPd_dstid, id);
1760	put4(d, OPd_srcid, v.tmpid);
1761	put4(d, OPd_maskid, v.whiteid);
1762	putR(d, OPd_R, r);
1763	putP(d, OPd_P0, r.min);
1764	putP(d, OPd_P1, r.min);
1765	v.output <-= (d, nil);
1766
1767	# restore drawop if necessary
1768	if (drawop != Draw->S) {
1769		O := array [1+1] of byte;
1770		O[0] = byte 'O';
1771		put1(O, OPO_op, drawop);
1772		v.output <-= (O, nil);
1773	}
1774}
1775
1776# returns (rid, map)
1777# rid == remote screen id
1778# map indicates that chan mapping is required for windows on this screen
1779
1780Viewer.getscr(v : self ref Viewer, localid, winid : int) : (int, int)
1781{
1782	remid := v.scrmap.lookup(localid);
1783	if (remid != -1) {
1784		if (drawchans.eq(v.dchans))
1785			return (remid, 0);
1786		scr := screens.lookup(localid);
1787		if (v.chanmap.lookup(scr.imageid) == -1)
1788			return (remid, 0);
1789		return (remid, 1);
1790	}
1791
1792	scr := screens.lookup(localid);
1793	imgid := v.getimg(scr.imageid);
1794	fillid := v.getimg(scr.fillid);
1795	A := array [1+4+4+4+1] of byte;
1796	A[0] = byte 'A';
1797	put4(A, OPA_imageid, imgid);
1798	put4(A, OPA_fillid, fillid);
1799	put1(A, OPA_public, 0);
1800
1801	reply := chan of string;
1802	for (i := 0; i < 25; i++) {
1803		put4(A, OPA_id, ++v.screenid);
1804		v.output <-= (A, reply);
1805		if (<-reply != nil)
1806			continue;
1807		v.scrmap.add(localid, v.screenid);
1808		break;
1809	}
1810	# if i == 25 then we have a problem
1811	# ...
1812	if (i == 25) {
1813#		sys->print("failed to create remote screen\n");
1814		return (0, 0);
1815	}
1816
1817	# pre-construct the windows on this screen
1818	for (ix := len scr.windows -1; ix >=0; ix--)
1819		if (scr.windows[ix] != winid)
1820			v.getimg(scr.windows[ix]);
1821
1822	if (drawchans.eq(v.dchans))
1823		return (v.screenid, 0);
1824	if (v.chanmap.lookup(scr.imageid) == -1)
1825		return (v.screenid, 0);
1826	return (v.screenid, 1);
1827}
1828