San Mehat | a430b2b | 2014-09-23 08:30:51 -0700 | [diff] [blame] | 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 | */ |
| 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 | |