xref: /plan9/sys/src/cmd/vnc/draw.c (revision 6bbfed0d85c6d7248503ef0614d0f1e40438b735)
1 #include "vnc.h"
2 #include "vncv.h"
3 
4 static struct {
5 	char	*name;
6 	int	num;
7 } enctab[] = {
8 	"copyrect",	EncCopyRect,
9 	"corre",	EncCorre,
10 	"hextile",	EncHextile,
11 	"raw",		EncRaw,
12 	"rre",		EncRre,
13 	"mousewarp",	EncMouseWarp,
14 };
15 
16 static	uchar	*pixbuf;
17 static	uchar	*linebuf;
18 static	int	vpixb;
19 static	int	pixb;
20 static	void	(*pixcp)(uchar*, uchar*);
21 
22 static void
vncrdcolor(Vnc * v,uchar * color)23 vncrdcolor(Vnc *v, uchar *color)
24 {
25 	vncrdbytes(v, color, vpixb);
26 
27 	if(cvtpixels)
28 		(*cvtpixels)(color, color, 1);
29 }
30 
31 void
sendencodings(Vnc * v)32 sendencodings(Vnc *v)
33 {
34 	char *f[10];
35 	int enc[10], nenc, i, j, nf;
36 
37 	nf = tokenize(encodings, f, nelem(f));
38 	nenc = 0;
39 	for(i=0; i<nf; i++){
40 		for(j=0; j<nelem(enctab); j++)
41 			if(strcmp(f[i], enctab[j].name) == 0)
42 				break;
43 		if(j == nelem(enctab)){
44 			print("warning: unknown encoding %s\n", f[i]);
45 			continue;
46 		}
47 		enc[nenc++] = enctab[j].num;
48 	}
49 
50 	vnclock(v);
51 	vncwrchar(v, MSetEnc);
52 	vncwrchar(v, 0);
53 	vncwrshort(v, nenc);
54 	for(i=0; i<nenc; i++)
55 		vncwrlong(v, enc[i]);
56 	vncflush(v);
57 	vncunlock(v);
58 }
59 
60 void
requestupdate(Vnc * v,int incremental)61 requestupdate(Vnc *v, int incremental)
62 {
63 	int x, y;
64 
65 	lockdisplay(display);
66 	x = Dx(screen->r);
67 	y = Dy(screen->r);
68 	unlockdisplay(display);
69 	if(x > v->dim.x)
70 		x = v->dim.x;
71 	if(y > v->dim.y)
72 		y = v->dim.y;
73 	vnclock(v);
74 	vncwrchar(v, MFrameReq);
75 	vncwrchar(v, incremental);
76 	vncwrrect(v, Rpt(ZP, Pt(x, y)));
77 	vncflush(v);
78 	vncunlock(v);
79 }
80 
81 static Rectangle
clippixbuf(Rectangle r,int maxx,int maxy)82 clippixbuf(Rectangle r, int maxx, int maxy)
83 {
84 	int y, h, stride1, stride2;
85 
86 	if(r.min.x > maxx || r.min.y > maxy){
87 		r.max.x = 0;
88 		return r;
89 	}
90 	if(r.max.y > maxy)
91 		r.max.y = maxy;
92 	if(r.max.x <= maxx)
93 		return r;
94 
95 	stride2 = Dx(r) * pixb;
96 	r.max.x = maxx;
97 	stride1 = Dx(r) * pixb;
98 	h = Dy(r);
99 	for(y = 0; y < h; y++)
100 		memmove(&pixbuf[y * stride1], &pixbuf[y * stride2], stride1);
101 
102 	return r;
103 }
104 
105 /* must be called with display locked */
106 static void
updatescreen(Rectangle r)107 updatescreen(Rectangle r)
108 {
109 	Image* img;
110 	int b, bb;
111 
112 	lockdisplay(display);
113 	if(r.max.x > Dx(screen->r) || r.max.y > Dy(screen->r)){
114 		r = clippixbuf(r, Dx(screen->r), Dy(screen->r));
115 		if(r.max.x == 0){
116 			unlockdisplay(display);
117 			return;
118 		}
119 	}
120 
121 	/*
122 	 * assume load image fails only because of resize
123 	 */
124 	img = allocimage(display, r, screen->chan, 0, DNofill);
125 	if(img == nil)
126 		sysfatal("updatescreen: %r");
127 	b = Dx(r) * pixb * Dy(r);
128 	bb = loadimage(img, r, pixbuf, b);
129 	if(bb != b && verbose)
130 		fprint(2, "loadimage %d on %R for %R returned %d: %r\n", b, rectaddpt(r, screen->r.min), screen->r, bb);
131 	draw(screen, rectaddpt(r, screen->r.min), img, nil, r.min);
132 	freeimage(img);
133 	unlockdisplay(display);
134 }
135 
136 static void
fillrect(Rectangle r,int stride,uchar * color)137 fillrect(Rectangle r, int stride, uchar *color)
138 {
139 	int x, xe, y, off;
140 
141 	y = r.min.y;
142 	off = y * stride;
143 	for(; y < r.max.y; y++){
144 		xe = off + r.max.x * pixb;
145 		for(x = off + r.min.x * pixb; x < xe; x += pixb)
146 			(*pixcp)(&pixbuf[x], color);
147 		off += stride;
148 	}
149 }
150 
151 static void
loadbuf(Vnc * v,Rectangle r,int stride)152 loadbuf(Vnc *v, Rectangle r, int stride)
153 {
154 	int off, y;
155 
156 	if(cvtpixels){
157 		y = r.min.y;
158 		off = y * stride;
159 		for(; y < r.max.y; y++){
160 			vncrdbytes(v, linebuf, Dx(r) * vpixb);
161 			(*cvtpixels)(&pixbuf[off + r.min.x * pixb], linebuf, Dx(r));
162 			off += stride;
163 		}
164 	}else{
165 		y = r.min.y;
166 		off = y * stride;
167 		for(; y < r.max.y; y++){
168 			vncrdbytes(v, &pixbuf[off + r.min.x * pixb], Dx(r) * pixb);
169 			off += stride;
170 		}
171 	}
172 }
173 
174 static Rectangle
hexrect(ushort u)175 hexrect(ushort u)
176 {
177 	int x, y, w, h;
178 
179 	x = u>>12;
180 	y = (u>>8)&15;
181 	w = ((u>>4)&15)+1;
182 	h = (u&15)+1;
183 
184 	return Rect(x, y, x+w, y+h);
185 }
186 
187 
188 static void
dohextile(Vnc * v,Rectangle r,int stride)189 dohextile(Vnc *v, Rectangle r, int stride)
190 {
191 	ulong bg, fg, c;
192 	int enc, nsub, sx, sy, w, h, th, tw;
193 	Rectangle sr, ssr;
194 
195 	fg = bg = 0;
196 	h = Dy(r);
197 	w = Dx(r);
198 	for(sy = 0; sy < h; sy += HextileDim){
199 		th = h - sy;
200 		if(th > HextileDim)
201 			th = HextileDim;
202 		for(sx = 0; sx < w; sx += HextileDim){
203 			tw = w - sx;
204 			if(tw > HextileDim)
205 				tw = HextileDim;
206 
207 			sr = Rect(sx, sy, sx + tw, sy + th);
208 			enc = vncrdchar(v);
209 			if(enc & HextileRaw){
210 				loadbuf(v, sr, stride);
211 				continue;
212 			}
213 
214 			if(enc & HextileBack)
215 				vncrdcolor(v, (uchar*)&bg);
216 			fillrect(sr, stride, (uchar*)&bg);
217 
218 			if(enc & HextileFore)
219 				vncrdcolor(v, (uchar*)&fg);
220 
221 			if(enc & HextileRects){
222 				nsub = vncrdchar(v);
223 				(*pixcp)((uchar*)&c, (uchar*)&fg);
224 				while(nsub-- > 0){
225 					if(enc & HextileCols)
226 						vncrdcolor(v, (uchar*)&c);
227 					ssr = rectaddpt(hexrect(vncrdshort(v)), sr.min);
228 					fillrect(ssr, stride, (uchar*)&c);
229 				}
230 			}
231 		}
232 	}
233 }
234 
235 static void
dorectangle(Vnc * v)236 dorectangle(Vnc *v)
237 {
238 	ulong type;
239 	long n, stride;
240 	ulong color;
241 	Point p;
242 	Rectangle r, subr, maxr;
243 
244 	r = vncrdrect(v);
245 	if(r.min.x == r.max.x || r.min.y == r.max.y)
246 		return;
247 	if(!rectinrect(r, Rpt(ZP, v->dim)))
248 		sysfatal("bad rectangle from server: %R not in %R", r, Rpt(ZP, v->dim));
249 	stride = Dx(r) * pixb;
250 	type = vncrdlong(v);
251 	switch(type){
252 	default:
253 		sysfatal("bad rectangle encoding from server");
254 		break;
255 	case EncRaw:
256 		loadbuf(v, Rpt(ZP, Pt(Dx(r), Dy(r))), stride);
257 		updatescreen(r);
258 		break;
259 
260 	case EncCopyRect:
261 		p = vncrdpoint(v);
262 		lockdisplay(display);
263 		p = addpt(p, screen->r.min);
264 		r = rectaddpt(r, screen->r.min);
265 		draw(screen, r, screen, nil, p);
266 		unlockdisplay(display);
267 		break;
268 
269 	case EncRre:
270 	case EncCorre:
271 		maxr = Rpt(ZP, Pt(Dx(r), Dy(r)));
272 		n = vncrdlong(v);
273 		vncrdcolor(v, (uchar*)&color);
274 		fillrect(maxr, stride, (uchar*)&color);
275 		while(n-- > 0){
276 			vncrdcolor(v, (uchar*)&color);
277 			if(type == EncRre)
278 				subr = vncrdrect(v);
279 			else
280 				subr = vncrdcorect(v);
281 			if(!rectinrect(subr, maxr))
282 				sysfatal("bad encoding from server");
283 			fillrect(subr, stride, (uchar*)&color);
284 		}
285 		updatescreen(r);
286 		break;
287 
288 	case EncHextile:
289 		dohextile(v, r, stride);
290 		updatescreen(r);
291 		break;
292 
293 	case EncMouseWarp:
294 		mousewarp(r.min);
295 		break;
296 	}
297 }
298 
299 static void
pixcp8(uchar * dst,uchar * src)300 pixcp8(uchar *dst, uchar *src)
301 {
302 	*dst = *src;
303 }
304 
305 static void
pixcp16(uchar * dst,uchar * src)306 pixcp16(uchar *dst, uchar *src)
307 {
308 	*(ushort*)dst = *(ushort*)src;
309 }
310 
311 static void
pixcp32(uchar * dst,uchar * src)312 pixcp32(uchar *dst, uchar *src)
313 {
314 	*(ulong*)dst = *(ulong*)src;
315 }
316 
317 static void
pixcp24(uchar * dst,uchar * src)318 pixcp24(uchar *dst, uchar *src)
319 {
320 	dst[0] = src[0];
321 	dst[1] = src[1];
322 	dst[2] = src[2];
323 }
324 
325 static int
calcpixb(int bpp)326 calcpixb(int bpp)
327 {
328 	if(bpp / 8 * 8 != bpp)
329 		sysfatal("can't handle your screen");
330 	return bpp / 8;
331 }
332 
333 void
readfromserver(Vnc * v)334 readfromserver(Vnc *v)
335 {
336 	uchar type;
337 	uchar junk[100];
338 	long n;
339 
340 	vpixb = calcpixb(v->bpp);
341 	pixb = calcpixb(screen->depth);
342 	switch(pixb){
343 	case 1:
344 		pixcp = pixcp8;
345 		break;
346 	case 2:
347 		pixcp = pixcp16;
348 		break;
349 	case 3:
350 		pixcp = pixcp24;
351 		break;
352 	case 4:
353 		pixcp = pixcp32;
354 		break;
355 	default:
356 		sysfatal("can't handle your screen: bad depth %d", pixb);
357 	}
358 	linebuf = malloc(v->dim.x * vpixb);
359 	pixbuf = malloc(v->dim.x * pixb * v->dim.y);
360 	if(linebuf == nil || pixbuf == nil)
361 		sysfatal("can't allocate pix decompression storage");
362 	for(;;){
363 		type = vncrdchar(v);
364 		switch(type){
365 		default:
366 			sysfatal("bad message from server");
367 			break;
368 		case MFrameUpdate:
369 			vncrdchar(v);
370 			n = vncrdshort(v);
371 			while(n-- > 0)
372 				dorectangle(v);
373 			flushimage(display, 1);
374 			requestupdate(v, 1);
375 			break;
376 
377 		case MSetCmap:
378 			vncrdbytes(v, junk, 3);
379 			n = vncrdshort(v);
380 			vncgobble(v, n*3*2);
381 			break;
382 
383 		case MBell:
384 			break;
385 
386 		case MSAck:
387 			break;
388 
389 		case MSCut:
390 			vncrdbytes(v, junk, 3);
391 			n = vncrdlong(v);
392 			writesnarf(v, n);
393 			break;
394 		}
395 	}
396 }
397