1 /*
2    Copyright (C) 2002-2010 Karl J. Runge <runge@karlrunge.com>
3    All rights reserved.
4 
5 This file is part of x11vnc.
6 
7 x11vnc is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or (at
10 your option) any later version.
11 
12 x11vnc is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with x11vnc; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
20 or see <http://www.gnu.org/licenses/>.
21 
22 In addition, as a special exception, Karl J. Runge
23 gives permission to link the code of its release of x11vnc with the
24 OpenSSL project's "OpenSSL" library (or with modified versions of it
25 that use the same license as the "OpenSSL" library), and distribute
26 the linked executables.  You must obey the GNU General Public License
27 in all respects for all of the code used other than "OpenSSL".  If you
28 modify this file, you may extend this exception to your version of the
29 file, but you are not obligated to do so.  If you do not wish to do
30 so, delete this exception statement from your version.
31 */
32 
33 /* -- xinerama.c -- */
34 
35 #include "x11vnc.h"
36 #include "xwrappers.h"
37 #include "blackout_t.h"
38 #include "scan.h"
39 
40 /*
41  * routines related to xinerama and blacking out rectangles
42  */
43 
44 /* blacked-out region (-blackout, -xinerama) */
45 
46 #define BLACKR_MAX 100
47 blackout_t blackr[BLACKR_MAX];	/* hardwired max blackouts */
48 tile_blackout_t *tile_blackout;
49 int blackouts = 0;
50 
51 void initialize_blackouts_and_xinerama(void);
52 void push_sleep(int n);
53 void push_black_screen(int n);
54 void refresh_screen(int push);
55 void zero_fb(int x1, int y1, int x2, int y2);
56 
57 
58 static void initialize_blackouts(char *list);
59 static void blackout_tiles(void);
60 static void initialize_xinerama (void);
61 
62 
63 /*
64  * Take a comma separated list of geometries: WxH+X+Y and register them as
65  * rectangles to black out from the screen.
66  */
initialize_blackouts(char * list)67 static void initialize_blackouts(char *list) {
68 	char *p, *blist = strdup(list);
69 	int x, y, X, Y, h, w, t;
70 
71 	p = strtok(blist, ", \t");
72 	while (p) {
73 		if (!strcmp("noptr", p)) {
74 			blackout_ptr = 1;
75 			rfbLog("pointer will be blocked from blackout "
76 			    "regions\n");
77 			p = strtok(NULL, ", \t");
78 			continue;
79 		}
80 		if (! parse_geom(p, &w, &h, &x, &y, dpy_x, dpy_y)) {
81 			if (*p != '\0') {
82 				rfbLog("skipping invalid geometry: %s\n", p);
83 			}
84 			p = strtok(NULL, ", \t");
85 			continue;
86 		}
87 		w = nabs(w);
88 		h = nabs(h);
89 		x = nfix(x, dpy_x);
90 		y = nfix(y, dpy_y);
91 		X = x + w;
92 		Y = y + h;
93 		X = nfix(X, dpy_x+1);
94 		Y = nfix(Y, dpy_y+1);
95 		if (x > X) {
96 			t = X; X = x; x = t;
97 		}
98 		if (y > Y) {
99 			t = Y; Y = y; y = t;
100 		}
101 		if (x < 0 || x > dpy_x || y < 0 || y > dpy_y ||
102 		    X < 0 || X > dpy_x || Y < 0 || Y > dpy_y ||
103 		    x == X || y == Y) {
104 			rfbLog("skipping invalid blackout geometry: %s x="
105 			    "%d-%d,y=%d-%d,w=%d,h=%d\n", p, x, X, y, Y, w, h);
106 		} else {
107 			rfbLog("blackout rect: %s: x=%d-%d y=%d-%d\n", p,
108 			    x, X, y, Y);
109 
110 			/*
111 			 * note that the black out is x1 <= x but x < x2
112 			 * for the region. i.e. the x2, y2 are outside
113 			 * by 1 pixel.
114 			 */
115 			blackr[blackouts].x1 = x;
116 			blackr[blackouts].y1 = y;
117 			blackr[blackouts].x2 = X;
118 			blackr[blackouts].y2 = Y;
119 			blackouts++;
120 			if (blackouts >= BLACKR_MAX) {
121 				rfbLog("too many blackouts: %d\n", blackouts);
122 				break;
123 			}
124 		}
125 		p = strtok(NULL, ", \t");
126 	}
127 	free(blist);
128 }
129 
130 /*
131  * Now that all blackout rectangles have been constructed, see what overlap
132  * they have with the tiles in the system.  If a tile is touched by a
133  * blackout, record information.
134  */
blackout_tiles(void)135 static void blackout_tiles(void) {
136 	int tx, ty;
137 	int debug_bo = 0;
138 	if (! blackouts) {
139 		return;
140 	}
141 	if (getenv("DEBUG_BLACKOUT") != NULL) {
142 		debug_bo = 1;
143 	}
144 
145 	/*
146 	 * to simplify things drop down to single copy mode, etc...
147 	 */
148 	single_copytile = 1;
149 	/* loop over all tiles. */
150 	for (ty=0; ty < ntiles_y; ty++) {
151 		for (tx=0; tx < ntiles_x; tx++) {
152 			sraRegionPtr tile_reg, black_reg;
153 			sraRect rect;
154 			sraRectangleIterator *iter;
155 			int n, b, x1, y1, x2, y2, cnt;
156 
157 			/* tile number and coordinates: */
158 			n = tx + ty * ntiles_x;
159 			x1 = tx * tile_x;
160 			y1 = ty * tile_y;
161 			x2 = x1 + tile_x;
162 			y2 = y1 + tile_y;
163 			if (x2 > dpy_x) {
164 				x2 = dpy_x;
165 			}
166 			if (y2 > dpy_y) {
167 				y2 = dpy_y;
168 			}
169 
170 			/* make regions for the tile and the blackouts: */
171 			black_reg = (sraRegionPtr) sraRgnCreate();
172 			tile_reg  = (sraRegionPtr) sraRgnCreateRect(x1, y1,
173 			    x2, y2);
174 
175 			tile_blackout[n].cover = 0;
176 			tile_blackout[n].count = 0;
177 
178 			/* union of blackouts */
179 			for (b=0; b < blackouts; b++) {
180 				sraRegionPtr tmp_reg = (sraRegionPtr)
181 				    sraRgnCreateRect(blackr[b].x1,
182 				    blackr[b].y1, blackr[b].x2, blackr[b].y2);
183 
184 				sraRgnOr(black_reg, tmp_reg);
185 				sraRgnDestroy(tmp_reg);
186 			}
187 
188 			if (! sraRgnAnd(black_reg, tile_reg)) {
189 				/*
190 				 * no intersection for this tile, so we
191 				 * are done.
192 				 */
193 				sraRgnDestroy(black_reg);
194 				sraRgnDestroy(tile_reg);
195 				continue;
196 			}
197 
198 			/*
199 			 * loop over rectangles that make up the blackout
200 			 * region:
201 			 */
202 			cnt = 0;
203 			iter = sraRgnGetIterator(black_reg);
204 			while (sraRgnIteratorNext(iter, &rect)) {
205 
206 				/* make sure x1 < x2 and y1 < y2 */
207 				if (rect.x1 > rect.x2) {
208 					int tmp = rect.x2;
209 					rect.x2 = rect.x1;
210 					rect.x1 = tmp;
211 				}
212 				if (rect.y1 > rect.y2) {
213 					int tmp = rect.y2;
214 					rect.y2 = rect.y1;
215 					rect.y1 = tmp;
216 				}
217 
218 				/* store coordinates */
219 				tile_blackout[n].bo[cnt].x1 = rect.x1;
220 				tile_blackout[n].bo[cnt].y1 = rect.y1;
221 				tile_blackout[n].bo[cnt].x2 = rect.x2;
222 				tile_blackout[n].bo[cnt].y2 = rect.y2;
223 
224 				/* note if the tile is completely obscured */
225 				if (rect.x1 == x1 && rect.y1 == y1 &&
226 				    rect.x2 == x2 && rect.y2 == y2) {
227 					tile_blackout[n].cover = 2;
228 					if (debug_bo) {
229  						fprintf(stderr, "full: %d=%d,%d"
230 						    "  (%d-%d)  (%d-%d)\n",
231 						    n, tx, ty, x1, x2, y1, y2);
232 					}
233 				} else {
234 					tile_blackout[n].cover = 1;
235 					if (debug_bo) {
236 						fprintf(stderr, "part: %d=%d,%d"
237 						    "  (%d-%d)  (%d-%d)\n",
238 						    n, tx, ty, x1, x2, y1, y2);
239 					}
240 				}
241 
242 				if (++cnt >= BO_MAX) {
243 					rfbLog("too many blackout rectangles "
244 					    "for tile %d=%d,%d.\n", n, tx, ty);
245 					break;
246 				}
247 			}
248 			sraRgnReleaseIterator(iter);
249 
250 			sraRgnDestroy(black_reg);
251 			sraRgnDestroy(tile_reg);
252 
253 			tile_blackout[n].count = cnt;
254 			if (debug_bo && cnt > 1) {
255  				rfbLog("warning: multiple region overlaps[%d] "
256 				    "for tile %d=%d,%d.\n", cnt, n, tx, ty);
257 			}
258 		}
259 	}
260 }
261 
262 static int did_xinerama_clip = 0;
263 
check_xinerama_clip(void)264 void check_xinerama_clip(void) {
265 #if LIBVNCSERVER_HAVE_LIBXINERAMA
266 	int n, k, i, ev, er, juse = -1;
267 	int score[32], is = 0;
268 	XineramaScreenInfo *x;
269 
270 	if (!clip_str || !dpy) {
271 		return;
272 	}
273 	if (sscanf(clip_str, "xinerama%d", &k) == 1) {
274 		;
275 	} else if (sscanf(clip_str, "screen%d", &k) == 1) {
276 		;
277 	} else {
278 		return;
279 	}
280 
281 	free(clip_str);
282 	clip_str = NULL;
283 
284 	if (! XineramaQueryExtension(dpy, &ev, &er)) {
285 		return;
286 	}
287 	if (! XineramaIsActive(dpy)) {
288 		return;
289 	}
290 	x = XineramaQueryScreens(dpy, &n);
291 	if (k < 0 || k >= n) {
292 		XFree_wr(x);
293 		return;
294 	}
295 	for (i=0; i < n; i++) {
296 		score[is++] = nabs(x[i].x_org) + nabs(x[i].y_org);
297 		if (is >= 32) {
298 			break;
299 		}
300 	}
301 	for (i=0; i <= k; i++) {
302 		int j, jmon = 0, mon = -1, mox = -1;
303 		for (j=0; j < is; j++) {
304 			if (mon < 0 || score[j] < mon) {
305 				mon = score[j];
306 				jmon = j;
307 			}
308 			if (mox < 0 || score[j] > mox) {
309 				mox = score[j];
310 			}
311 		}
312 		juse = jmon;
313 		score[juse] = mox+1+i;
314 	}
315 
316 	if (juse >= 0 && juse < n) {
317 		char str[64];
318 		sprintf(str, "%dx%d+%d+%d", x[juse].width, x[juse].height,
319 		    x[juse].x_org, x[juse].y_org);
320 		clip_str = strdup(str);
321 		did_xinerama_clip = 1;
322 	} else {
323 		clip_str = strdup("");
324 	}
325 	XFree_wr(x);
326 	if (!quiet) {
327 		rfbLog("set -clip to '%s' for xinerama%d\n", clip_str, k);
328 	}
329 #endif
330 }
331 
initialize_xinerama(void)332 static void initialize_xinerama (void) {
333 #if !LIBVNCSERVER_HAVE_LIBXINERAMA
334 	if (!raw_fb_str) {
335 		rfbLog("Xinerama: Library libXinerama is not available to determine\n");
336 		rfbLog("Xinerama: the head geometries, consider using -blackout\n");
337 		rfbLog("Xinerama: if the screen is non-rectangular.\n");
338 	}
339 #else
340 	XineramaScreenInfo *sc, *xineramas;
341 	sraRegionPtr black_region, tmp_region;
342 	sraRectangleIterator *iter;
343 	sraRect rect;
344 	char *bstr, *tstr;
345 	int ev, er, i, n, rcnt;
346 
347 	RAWFB_RET_VOID
348 
349 	X_LOCK;
350 	if (! XineramaQueryExtension(dpy, &ev, &er)) {
351 		if (verbose) {
352 			rfbLog("Xinerama: disabling: display does not support it.\n");
353 		}
354 		xinerama = 0;
355 		xinerama_present = 0;
356 		X_UNLOCK;
357 		return;
358 	}
359 	if (! XineramaIsActive(dpy)) {
360 		/* n.b. change to XineramaActive(dpy, window) someday */
361 		if (verbose) {
362 			rfbLog("Xinerama: disabling: not active on display.\n");
363 		}
364 		xinerama = 0;
365 		xinerama_present = 0;
366 		X_UNLOCK;
367 		return;
368 	}
369 	xinerama_present = 1;
370 	rfbLog("\n");
371 	rfbLog("Xinerama is present and active (e.g. multi-head).\n");
372 
373 	/* n.b. change to XineramaGetData() someday */
374 	xineramas = XineramaQueryScreens(dpy, &n);
375 	rfbLog("Xinerama: number of sub-screens: %d\n", n);
376 
377 	if (! use_xwarppointer && ! got_noxwarppointer && n > 1) {
378 		rfbLog("Xinerama: enabling -xwarppointer mode to try to correct\n");
379 		rfbLog("Xinerama: mouse pointer motion. XTEST+XINERAMA bug.\n");
380 		rfbLog("Xinerama: Use -noxwarppointer to force XTEST.\n");
381 		use_xwarppointer = 1;
382 	}
383 
384 	if (n == 1) {
385 		rfbLog("Xinerama: no blackouts needed (only one sub-screen)\n");
386 		rfbLog("\n");
387 		XFree_wr(xineramas);
388 		X_UNLOCK;
389 		return;		/* must be OK w/o change */
390 	}
391 
392 	black_region = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
393 
394 	sc = xineramas;
395 	for (i=0; i<n; i++) {
396 		int x, y, w, h;
397 
398 		x = sc->x_org;
399 		y = sc->y_org;
400 		w = sc->width;
401 		h = sc->height;
402 
403 		rfbLog("Xinerama: sub-screen[%d]  %dx%d+%d+%d\n", i, w, h, x, y);
404 
405 		tmp_region = sraRgnCreateRect(x, y, x + w, y + h);
406 
407 		sraRgnSubtract(black_region, tmp_region);
408 		sraRgnDestroy(tmp_region);
409 		sc++;
410 	}
411 	XFree_wr(xineramas);
412 	X_UNLOCK;
413 
414 
415 	if (sraRgnEmpty(black_region)) {
416 		rfbLog("Xinerama: no blackouts needed (screen fills"
417 		    " rectangle)\n");
418 		rfbLog("\n");
419 		sraRgnDestroy(black_region);
420 		return;
421 	}
422 	if (did_xinerama_clip) {
423 		rfbLog("Xinerama: no blackouts due to -clip xinerama.\n");
424 		return;
425 	}
426 
427 	/* max len is 10000x10000+10000+10000 (23 chars) per geometry */
428 	rcnt = (int) sraRgnCountRects(black_region);
429 	bstr = (char *) malloc(30 * (rcnt+1));
430 	tstr = (char *) malloc(30);
431 	bstr[0] = '\0';
432 
433 	iter = sraRgnGetIterator(black_region);
434 	while (sraRgnIteratorNext(iter, &rect)) {
435 		int x, y, w, h;
436 
437 		/* make sure x1 < x2 and y1 < y2 */
438 		if (rect.x1 > rect.x2) {
439 			int tmp = rect.x2;
440 			rect.x2 = rect.x1;
441 			rect.x1 = tmp;
442 		}
443 		if (rect.y1 > rect.y2) {
444 			int tmp = rect.y2;
445 			rect.y2 = rect.y1;
446 			rect.y1 = tmp;
447 		}
448 		x = rect.x1;
449 		y = rect.y1;
450 		w = rect.x2 - x;
451 		h = rect.y2 - y;
452 		sprintf(tstr, "%dx%d+%d+%d,", w, h, x, y);
453 		strcat(bstr, tstr);
454 	}
455 	sraRgnReleaseIterator(iter);
456 	initialize_blackouts(bstr);
457 	rfbLog("\n");
458 
459 	free(bstr);
460 	free(tstr);
461 #endif
462 }
463 
initialize_blackouts_and_xinerama(void)464 void initialize_blackouts_and_xinerama(void) {
465 
466 	blackouts = 0;
467 	blackout_ptr = 0;
468 
469 	if (blackout_str != NULL) {
470 		initialize_blackouts(blackout_str);
471 	}
472 	if (xinerama) {
473 		initialize_xinerama();
474 	}
475 	if (blackouts) {
476 		blackout_tiles();
477 		/* schedule a copy_screen(), now is too early. */
478 		do_copy_screen = 1;
479 	}
480 }
481 
push_sleep(int n)482 void push_sleep(int n) {
483 	int i;
484 	for (i=0; i<n; i++) {
485 		rfbPE(-1);
486 		if (i != n-1 && defer_update) {
487 			usleep(defer_update * 1000);
488 		}
489 	}
490 }
491 
492 /*
493  * try to forcefully push a black screen to all connected clients
494  */
push_black_screen(int n)495 void push_black_screen(int n) {
496 	int Lx = dpy_x, Ly = dpy_y;
497 	if (!screen) {
498 		return;
499 	}
500 #ifndef NO_NCACHE
501 	if (ncache > 0) {
502 		Ly = dpy_y * (1+ncache);
503 	}
504 #endif
505 	zero_fb(0, 0, Lx, Ly);
506 	mark_rect_as_modified(0, 0, Lx, Ly, 0);
507 	push_sleep(n);
508 }
509 
refresh_screen(int push)510 void refresh_screen(int push) {
511 	int i;
512 	if (!screen) {
513 		return;
514 	}
515 	mark_rect_as_modified(0, 0, dpy_x, dpy_y, 0);
516 	for (i=0; i<push; i++) {
517 		rfbPE(-1);
518 	}
519 }
520 
521 /*
522  * Fill the framebuffer with zero for the prescribed rectangle
523  */
zero_fb(int x1,int y1,int x2,int y2)524 void zero_fb(int x1, int y1, int x2, int y2) {
525 	int pixelsize = bpp/8;
526 	int line, fill = 0, yfac = 1;
527 	char *dst;
528 
529 #ifndef NO_NCACHE
530 	if (ncache > 0) {
531 		yfac = 1+ncache;
532 		if (ncache_xrootpmap) {
533 			yfac++;
534 		}
535 	}
536 #endif
537 
538 	if (x1 < 0 || x2 <= x1 || x2 > dpy_x) {
539 		return;
540 	}
541 	if (y1 < 0 || y2 <= y1 || y2 > yfac * dpy_y) {
542 		return;
543 	}
544 	if (! main_fb) {
545 		return;
546 	}
547 
548 	dst = main_fb + y1 * main_bytes_per_line + x1 * pixelsize;
549 	line = y1;
550 	while (line++ < y2) {
551 		memset(dst, fill, (size_t) (x2 - x1) * pixelsize);
552 		dst += main_bytes_per_line;
553 	}
554 }
555 
556 
557