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 
102 		/* take clipping region into account */
103 		x = nfix(x - coff_x, wdpy_x);
104 		X = nfix(X - coff_x, wdpy_x);
105 		y = nfix(y - coff_y, wdpy_y);
106 		Y = nfix(Y - coff_y, wdpy_y);
107 
108 		if (x < 0 || x > dpy_x || y < 0 || y > dpy_y ||
109 		    X < 0 || X > dpy_x || Y < 0 || Y > dpy_y ||
110 		    x == X || y == Y) {
111 			rfbLog("skipping invalid blackout geometry: %s x="
112 			    "%d-%d,y=%d-%d,w=%d,h=%d\n", p, x, X, y, Y, w, h);
113 		} else {
114 			rfbLog("blackout rect: %s: x=%d-%d y=%d-%d\n", p,
115 			    x, X, y, Y);
116 
117 			/*
118 			 * note that the black out is x1 <= x but x < x2
119 			 * for the region. i.e. the x2, y2 are outside
120 			 * by 1 pixel.
121 			 */
122 			blackr[blackouts].x1 = x;
123 			blackr[blackouts].y1 = y;
124 			blackr[blackouts].x2 = X;
125 			blackr[blackouts].y2 = Y;
126 			blackouts++;
127 			if (blackouts >= BLACKR_MAX) {
128 				rfbLog("too many blackouts: %d\n", blackouts);
129 				break;
130 			}
131 		}
132 		p = strtok(NULL, ", \t");
133 	}
134 	free(blist);
135 }
136 
137 /*
138  * Now that all blackout rectangles have been constructed, see what overlap
139  * they have with the tiles in the system.  If a tile is touched by a
140  * blackout, record information.
141  */
blackout_tiles(void)142 static void blackout_tiles(void) {
143 	int tx, ty;
144 	int debug_bo = 0;
145 	if (! blackouts) {
146 		return;
147 	}
148 	if (getenv("DEBUG_BLACKOUT") != NULL) {
149 		debug_bo = 1;
150 	}
151 
152 	/*
153 	 * to simplify things drop down to single copy mode, etc...
154 	 */
155 	single_copytile = 1;
156 	/* loop over all tiles. */
157 	for (ty=0; ty < ntiles_y; ty++) {
158 		for (tx=0; tx < ntiles_x; tx++) {
159 			sraRegionPtr tile_reg, black_reg;
160 			sraRect rect;
161 			sraRectangleIterator *iter;
162 			int n, b, x1, y1, x2, y2, cnt;
163 
164 			/* tile number and coordinates: */
165 			n = tx + ty * ntiles_x;
166 			x1 = tx * tile_x;
167 			y1 = ty * tile_y;
168 			x2 = x1 + tile_x;
169 			y2 = y1 + tile_y;
170 			if (x2 > dpy_x) {
171 				x2 = dpy_x;
172 			}
173 			if (y2 > dpy_y) {
174 				y2 = dpy_y;
175 			}
176 
177 			/* make regions for the tile and the blackouts: */
178 			black_reg = (sraRegionPtr) sraRgnCreate();
179 			tile_reg  = (sraRegionPtr) sraRgnCreateRect(x1, y1,
180 			    x2, y2);
181 
182 			tile_blackout[n].cover = 0;
183 			tile_blackout[n].count = 0;
184 
185 			/* union of blackouts */
186 			for (b=0; b < blackouts; b++) {
187 				sraRegionPtr tmp_reg = (sraRegionPtr)
188 				    sraRgnCreateRect(blackr[b].x1,
189 				    blackr[b].y1, blackr[b].x2, blackr[b].y2);
190 
191 				sraRgnOr(black_reg, tmp_reg);
192 				sraRgnDestroy(tmp_reg);
193 			}
194 
195 			if (! sraRgnAnd(black_reg, tile_reg)) {
196 				/*
197 				 * no intersection for this tile, so we
198 				 * are done.
199 				 */
200 				sraRgnDestroy(black_reg);
201 				sraRgnDestroy(tile_reg);
202 				continue;
203 			}
204 
205 			/*
206 			 * loop over rectangles that make up the blackout
207 			 * region:
208 			 */
209 			cnt = 0;
210 			iter = sraRgnGetIterator(black_reg);
211 			while (sraRgnIteratorNext(iter, &rect)) {
212 
213 				/* make sure x1 < x2 and y1 < y2 */
214 				if (rect.x1 > rect.x2) {
215 					int tmp = rect.x2;
216 					rect.x2 = rect.x1;
217 					rect.x1 = tmp;
218 				}
219 				if (rect.y1 > rect.y2) {
220 					int tmp = rect.y2;
221 					rect.y2 = rect.y1;
222 					rect.y1 = tmp;
223 				}
224 
225 				/* store coordinates */
226 				tile_blackout[n].bo[cnt].x1 = rect.x1;
227 				tile_blackout[n].bo[cnt].y1 = rect.y1;
228 				tile_blackout[n].bo[cnt].x2 = rect.x2;
229 				tile_blackout[n].bo[cnt].y2 = rect.y2;
230 
231 				/* note if the tile is completely obscured */
232 				if (rect.x1 == x1 && rect.y1 == y1 &&
233 				    rect.x2 == x2 && rect.y2 == y2) {
234 					tile_blackout[n].cover = 2;
235 					if (debug_bo) {
236  						fprintf(stderr, "full: %d=%d,%d"
237 						    "  (%d-%d)  (%d-%d)\n",
238 						    n, tx, ty, x1, x2, y1, y2);
239 					}
240 				} else {
241 					tile_blackout[n].cover = 1;
242 					if (debug_bo) {
243 						fprintf(stderr, "part: %d=%d,%d"
244 						    "  (%d-%d)  (%d-%d)\n",
245 						    n, tx, ty, x1, x2, y1, y2);
246 					}
247 				}
248 
249 				if (++cnt >= BO_MAX) {
250 					rfbLog("too many blackout rectangles "
251 					    "for tile %d=%d,%d.\n", n, tx, ty);
252 					break;
253 				}
254 			}
255 			sraRgnReleaseIterator(iter);
256 
257 			sraRgnDestroy(black_reg);
258 			sraRgnDestroy(tile_reg);
259 
260 			tile_blackout[n].count = cnt;
261 			if (debug_bo && cnt > 1) {
262  				rfbLog("warning: multiple region overlaps[%d] "
263 				    "for tile %d=%d,%d.\n", cnt, n, tx, ty);
264 			}
265 		}
266 	}
267 }
268 
269 static int did_xinerama_clip = 0;
270 
check_xinerama_clip(void)271 void check_xinerama_clip(void) {
272 #if HAVE_LIBXINERAMA
273 	int n, k, i, ev, er, juse = -1;
274 	int score[32], is = 0;
275 	XineramaScreenInfo *x;
276 
277 	if (!clip_str || !dpy) {
278 		return;
279 	}
280 	if (sscanf(clip_str, "xinerama%d", &k) == 1) {
281 		;
282 	} else if (sscanf(clip_str, "screen%d", &k) == 1) {
283 		;
284 	} else {
285 		return;
286 	}
287 
288 	free(clip_str);
289 	clip_str = NULL;
290 
291 	if (! XineramaQueryExtension(dpy, &ev, &er)) {
292 		return;
293 	}
294 	if (! XineramaIsActive(dpy)) {
295 		return;
296 	}
297 	x = XineramaQueryScreens(dpy, &n);
298 	if (k < 0 || k >= n) {
299 		XFree_wr(x);
300 		return;
301 	}
302 	for (i=0; i < n; i++) {
303 		score[is++] = nabs(x[i].x_org) + nabs(x[i].y_org);
304 		if (is >= 32) {
305 			break;
306 		}
307 	}
308 	for (i=0; i <= k; i++) {
309 		int j, jmon = 0, mon = -1, mox = -1;
310 		for (j=0; j < is; j++) {
311 			if (mon < 0 || score[j] < mon) {
312 				mon = score[j];
313 				jmon = j;
314 			}
315 			if (mox < 0 || score[j] > mox) {
316 				mox = score[j];
317 			}
318 		}
319 		juse = jmon;
320 		score[juse] = mox+1+i;
321 	}
322 
323 	if (juse >= 0 && juse < n) {
324 		char str[64];
325 		sprintf(str, "%dx%d+%d+%d", x[juse].width, x[juse].height,
326 		    x[juse].x_org, x[juse].y_org);
327 		clip_str = strdup(str);
328 		did_xinerama_clip = 1;
329 	} else {
330 		clip_str = strdup("");
331 	}
332 	XFree_wr(x);
333 	if (!quiet) {
334 		rfbLog("set -clip to '%s' for xinerama%d\n", clip_str, k);
335 	}
336 #endif
337 }
338 
initialize_xinerama(void)339 static void initialize_xinerama (void) {
340 #if !HAVE_LIBXINERAMA
341 	if (!raw_fb_str) {
342 		rfbLog("Xinerama: Library libXinerama is not available to determine\n");
343 		rfbLog("Xinerama: the head geometries, consider using -blackout\n");
344 		rfbLog("Xinerama: if the screen is non-rectangular.\n");
345 	}
346 #else
347 	XineramaScreenInfo *sc, *xineramas;
348 	sraRegionPtr black_region, tmp_region;
349 	sraRectangleIterator *iter;
350 	sraRect rect;
351 	char *bstr, *tstr;
352 	int ev, er, i, n, rcnt;
353 
354 	RAWFB_RET_VOID
355 
356 	X_LOCK;
357 	if (! XineramaQueryExtension(dpy, &ev, &er)) {
358 		if (verbose) {
359 			rfbLog("Xinerama: disabling: display does not support it.\n");
360 		}
361 		xinerama = 0;
362 		xinerama_present = 0;
363 		X_UNLOCK;
364 		return;
365 	}
366 	if (! XineramaIsActive(dpy)) {
367 		/* n.b. change to XineramaActive(dpy, window) someday */
368 		if (verbose) {
369 			rfbLog("Xinerama: disabling: not active on display.\n");
370 		}
371 		xinerama = 0;
372 		xinerama_present = 0;
373 		X_UNLOCK;
374 		return;
375 	}
376 	xinerama_present = 1;
377 	rfbLog("\n");
378 	rfbLog("Xinerama is present and active (e.g. multi-head).\n");
379 
380 	/* n.b. change to XineramaGetData() someday */
381 	xineramas = XineramaQueryScreens(dpy, &n);
382 	rfbLog("Xinerama: number of sub-screens: %d\n", n);
383 
384 	if (! use_xwarppointer && ! got_noxwarppointer && n > 1) {
385 		rfbLog("Xinerama: enabling -xwarppointer mode to try to correct\n");
386 		rfbLog("Xinerama: mouse pointer motion. XTEST+XINERAMA bug.\n");
387 		rfbLog("Xinerama: Use -noxwarppointer to force XTEST.\n");
388 		use_xwarppointer = 1;
389 	}
390 
391 	if (n == 1) {
392 		rfbLog("Xinerama: no blackouts needed (only one sub-screen)\n");
393 		rfbLog("\n");
394 		XFree_wr(xineramas);
395 		X_UNLOCK;
396 		return;		/* must be OK w/o change */
397 	}
398 
399 	black_region = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
400 
401 	sc = xineramas;
402 	for (i=0; i<n; i++) {
403 		int x, y, w, h;
404 
405 		x = sc->x_org;
406 		y = sc->y_org;
407 		w = sc->width;
408 		h = sc->height;
409 
410 		rfbLog("Xinerama: sub-screen[%d]  %dx%d+%d+%d\n", i, w, h, x, y);
411 
412 		tmp_region = sraRgnCreateRect(x, y, x + w, y + h);
413 
414 		sraRgnSubtract(black_region, tmp_region);
415 		sraRgnDestroy(tmp_region);
416 		sc++;
417 	}
418 	XFree_wr(xineramas);
419 	X_UNLOCK;
420 
421 
422 	if (sraRgnEmpty(black_region)) {
423 		rfbLog("Xinerama: no blackouts needed (screen fills"
424 		    " rectangle)\n");
425 		rfbLog("\n");
426 		sraRgnDestroy(black_region);
427 		return;
428 	}
429 	if (did_xinerama_clip) {
430 		rfbLog("Xinerama: no blackouts due to -clip xinerama.\n");
431 		return;
432 	}
433 
434 	/* max len is 10000x10000+10000+10000 (23 chars) per geometry */
435 	rcnt = (int) sraRgnCountRects(black_region);
436 	bstr = (char *) malloc(30 * (rcnt+1));
437 	tstr = (char *) malloc(30);
438 	bstr[0] = '\0';
439 
440 	iter = sraRgnGetIterator(black_region);
441 	while (sraRgnIteratorNext(iter, &rect)) {
442 		int x, y, w, h;
443 
444 		/* make sure x1 < x2 and y1 < y2 */
445 		if (rect.x1 > rect.x2) {
446 			int tmp = rect.x2;
447 			rect.x2 = rect.x1;
448 			rect.x1 = tmp;
449 		}
450 		if (rect.y1 > rect.y2) {
451 			int tmp = rect.y2;
452 			rect.y2 = rect.y1;
453 			rect.y1 = tmp;
454 		}
455 		x = rect.x1;
456 		y = rect.y1;
457 		w = rect.x2 - x;
458 		h = rect.y2 - y;
459 		sprintf(tstr, "%dx%d+%d+%d,", w, h, x, y);
460 		strcat(bstr, tstr);
461 	}
462 	sraRgnReleaseIterator(iter);
463 	initialize_blackouts(bstr);
464 	rfbLog("\n");
465 
466 	free(bstr);
467 	free(tstr);
468 #endif
469 }
470 
initialize_blackouts_and_xinerama(void)471 void initialize_blackouts_and_xinerama(void) {
472 
473 	blackouts = 0;
474 	blackout_ptr = 0;
475 
476 	if (blackout_str != NULL) {
477 		initialize_blackouts(blackout_str);
478 	}
479 	if (xinerama) {
480 		initialize_xinerama();
481 	}
482 	if (blackouts) {
483 		blackout_tiles();
484 		/* schedule a copy_screen(), now is too early. */
485 		do_copy_screen = 1;
486 	}
487 }
488 
push_sleep(int n)489 void push_sleep(int n) {
490 	int i;
491 	for (i=0; i<n; i++) {
492 		rfbPE(-1);
493 		if (i != n-1 && defer_update) {
494 			usleep(defer_update * 1000);
495 		}
496 	}
497 }
498 
499 /*
500  * try to forcefully push a black screen to all connected clients
501  */
push_black_screen(int n)502 void push_black_screen(int n) {
503 	int Lx = dpy_x, Ly = dpy_y;
504 	if (!screen) {
505 		return;
506 	}
507 #ifndef NO_NCACHE
508 	if (ncache > 0) {
509 		Ly = dpy_y * (1+ncache);
510 	}
511 #endif
512 	zero_fb(0, 0, Lx, Ly);
513 	mark_rect_as_modified(0, 0, Lx, Ly, 0);
514 	push_sleep(n);
515 }
516 
refresh_screen(int push)517 void refresh_screen(int push) {
518 	int i;
519 	if (!screen) {
520 		return;
521 	}
522 	mark_rect_as_modified(0, 0, dpy_x, dpy_y, 0);
523 	for (i=0; i<push; i++) {
524 		rfbPE(-1);
525 	}
526 }
527 
528 /*
529  * Fill the framebuffer with zero for the prescribed rectangle
530  */
zero_fb(int x1,int y1,int x2,int y2)531 void zero_fb(int x1, int y1, int x2, int y2) {
532 	int pixelsize = bpp/8;
533 	int line, fill = 0, yfac = 1;
534 	char *dst;
535 
536 #ifndef NO_NCACHE
537 	if (ncache > 0) {
538 		yfac = 1+ncache;
539 		if (ncache_xrootpmap) {
540 			yfac++;
541 		}
542 	}
543 #endif
544 
545 	if (x1 < 0 || x2 <= x1 || x2 > dpy_x) {
546 		return;
547 	}
548 	if (y1 < 0 || y2 <= y1 || y2 > yfac * dpy_y) {
549 		return;
550 	}
551 	if (! main_fb) {
552 		return;
553 	}
554 
555 	dst = main_fb + y1 * main_bytes_per_line + x1 * pixelsize;
556 	line = y1;
557 	while (line++ < y2) {
558 		memset(dst, fill, (size_t) (x2 - x1) * pixelsize);
559 		dst += main_bytes_per_line;
560 	}
561 }
562 
563 
564