Home | History | Annotate | Download | only in x11vnc
      1 /*
      2    Copyright (C) 2002-2010 Karl J. Runge <runge (at) 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  */
     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  */
    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 
    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 
    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 
    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 
    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  */
    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 
    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  */
    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