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