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