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 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 */
blackout_tiles(void)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
check_xinerama_clip(void)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
initialize_xinerama(void)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
initialize_blackouts_and_xinerama(void)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
push_sleep(int n)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 */
push_black_screen(int n)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
refresh_screen(int push)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 */
zero_fb(int x1,int y1,int x2,int y2)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