/* -- xinerama.c -- */ #include "x11vnc.h" #include "xwrappers.h" #include "blackout_t.h" #include "scan.h" /* * routines related to xinerama and blacking out rectangles */ /* blacked-out region (-blackout, -xinerama) */ #define BLACKR_MAX 100 blackout_t blackr[BLACKR_MAX]; /* hardwired max blackouts */ tile_blackout_t *tile_blackout; int blackouts = 0; void initialize_blackouts_and_xinerama(void); void push_sleep(int n); void push_black_screen(int n); void refresh_screen(int push); void zero_fb(int x1, int y1, int x2, int y2); static void initialize_blackouts(char *list); static void blackout_tiles(void); static void initialize_xinerama (void); /* * Take a comma separated list of geometries: WxH+X+Y and register them as * rectangles to black out from the screen. */ static void initialize_blackouts(char *list) { char *p, *blist = strdup(list); int x, y, X, Y, h, w, t; p = strtok(blist, ", \t"); while (p) { if (!strcmp("noptr", p)) { blackout_ptr = 1; rfbLog("pointer will be blocked from blackout " "regions\n"); p = strtok(NULL, ", \t"); continue; } if (! parse_geom(p, &w, &h, &x, &y, dpy_x, dpy_y)) { if (*p != '\0') { rfbLog("skipping invalid geometry: %s\n", p); } p = strtok(NULL, ", \t"); continue; } w = nabs(w); h = nabs(h); x = nfix(x, dpy_x); y = nfix(y, dpy_y); X = x + w; Y = y + h; X = nfix(X, dpy_x+1); Y = nfix(Y, dpy_y+1); if (x > X) { t = X; X = x; x = t; } if (y > Y) { t = Y; Y = y; y = t; } if (x < 0 || x > dpy_x || y < 0 || y > dpy_y || X < 0 || X > dpy_x || Y < 0 || Y > dpy_y || x == X || y == Y) { rfbLog("skipping invalid blackout geometry: %s x=" "%d-%d,y=%d-%d,w=%d,h=%d\n", p, x, X, y, Y, w, h); } else { rfbLog("blackout rect: %s: x=%d-%d y=%d-%d\n", p, x, X, y, Y); /* * note that the black out is x1 <= x but x < x2 * for the region. i.e. the x2, y2 are outside * by 1 pixel. */ blackr[blackouts].x1 = x; blackr[blackouts].y1 = y; blackr[blackouts].x2 = X; blackr[blackouts].y2 = Y; blackouts++; if (blackouts >= BLACKR_MAX) { rfbLog("too many blackouts: %d\n", blackouts); break; } } p = strtok(NULL, ", \t"); } free(blist); } /* * Now that all blackout rectangles have been constructed, see what overlap * they have with the tiles in the system. If a tile is touched by a * blackout, record information. */ static void blackout_tiles(void) { int tx, ty; int debug_bo = 0; if (! blackouts) { return; } if (getenv("DEBUG_BLACKOUT") != NULL) { debug_bo = 1; } /* * to simplify things drop down to single copy mode, etc... */ single_copytile = 1; /* loop over all tiles. */ for (ty=0; ty < ntiles_y; ty++) { for (tx=0; tx < ntiles_x; tx++) { sraRegionPtr tile_reg, black_reg; sraRect rect; sraRectangleIterator *iter; int n, b, x1, y1, x2, y2, cnt; /* tile number and coordinates: */ n = tx + ty * ntiles_x; x1 = tx * tile_x; y1 = ty * tile_y; x2 = x1 + tile_x; y2 = y1 + tile_y; if (x2 > dpy_x) { x2 = dpy_x; } if (y2 > dpy_y) { y2 = dpy_y; } /* make regions for the tile and the blackouts: */ black_reg = (sraRegionPtr) sraRgnCreate(); tile_reg = (sraRegionPtr) sraRgnCreateRect(x1, y1, x2, y2); tile_blackout[n].cover = 0; tile_blackout[n].count = 0; /* union of blackouts */ for (b=0; b < blackouts; b++) { sraRegionPtr tmp_reg = (sraRegionPtr) sraRgnCreateRect(blackr[b].x1, blackr[b].y1, blackr[b].x2, blackr[b].y2); sraRgnOr(black_reg, tmp_reg); sraRgnDestroy(tmp_reg); } if (! sraRgnAnd(black_reg, tile_reg)) { /* * no intersection for this tile, so we * are done. */ sraRgnDestroy(black_reg); sraRgnDestroy(tile_reg); continue; } /* * loop over rectangles that make up the blackout * region: */ cnt = 0; iter = sraRgnGetIterator(black_reg); while (sraRgnIteratorNext(iter, &rect)) { /* make sure x1 < x2 and y1 < y2 */ if (rect.x1 > rect.x2) { int tmp = rect.x2; rect.x2 = rect.x1; rect.x1 = tmp; } if (rect.y1 > rect.y2) { int tmp = rect.y2; rect.y2 = rect.y1; rect.y1 = tmp; } /* store coordinates */ tile_blackout[n].bo[cnt].x1 = rect.x1; tile_blackout[n].bo[cnt].y1 = rect.y1; tile_blackout[n].bo[cnt].x2 = rect.x2; tile_blackout[n].bo[cnt].y2 = rect.y2; /* note if the tile is completely obscured */ if (rect.x1 == x1 && rect.y1 == y1 && rect.x2 == x2 && rect.y2 == y2) { tile_blackout[n].cover = 2; if (debug_bo) { fprintf(stderr, "full: %d=%d,%d" " (%d-%d) (%d-%d)\n", n, tx, ty, x1, x2, y1, y2); } } else { tile_blackout[n].cover = 1; if (debug_bo) { fprintf(stderr, "part: %d=%d,%d" " (%d-%d) (%d-%d)\n", n, tx, ty, x1, x2, y1, y2); } } if (++cnt >= BO_MAX) { rfbLog("too many blackout rectangles " "for tile %d=%d,%d.\n", n, tx, ty); break; } } sraRgnReleaseIterator(iter); sraRgnDestroy(black_reg); sraRgnDestroy(tile_reg); tile_blackout[n].count = cnt; if (debug_bo && cnt > 1) { rfbLog("warning: multiple region overlaps[%d] " "for tile %d=%d,%d.\n", cnt, n, tx, ty); } } } } static void initialize_xinerama (void) { #if !LIBVNCSERVER_HAVE_LIBXINERAMA rfbLog("Xinerama: Library libXinerama is not available to determine\n"); rfbLog("Xinerama: the head geometries, consider using -blackout\n"); rfbLog("Xinerama: if the screen is non-rectangular.\n"); #else XineramaScreenInfo *sc, *xineramas; sraRegionPtr black_region, tmp_region; sraRectangleIterator *iter; sraRect rect; char *bstr, *tstr; int ev, er, i, n, rcnt; if (raw_fb && ! dpy) return; /* raw_fb hack */ if (! XineramaQueryExtension(dpy, &ev, &er)) { rfbLog("Xinerama: disabling: display does not support it.\n"); xinerama = 0; xinerama_present = 0; return; } if (! XineramaIsActive(dpy)) { /* n.b. change to XineramaActive(dpy, window) someday */ rfbLog("Xinerama: disabling: not active on display.\n"); xinerama = 0; xinerama_present = 0; return; } xinerama_present = 1; /* n.b. change to XineramaGetData() someday */ xineramas = XineramaQueryScreens(dpy, &n); rfbLog("Xinerama: number of sub-screens: %d\n", n); if (n == 1) { rfbLog("Xinerama: no blackouts needed (only one" " sub-screen)\n"); XFree(xineramas); return; /* must be OK w/o change */ } black_region = sraRgnCreateRect(0, 0, dpy_x, dpy_y); sc = xineramas; for (i=0; ix_org; y = sc->y_org; w = sc->width; h = sc->height; tmp_region = sraRgnCreateRect(x, y, x + w, y + h); sraRgnSubtract(black_region, tmp_region); sraRgnDestroy(tmp_region); sc++; } XFree(xineramas); if (sraRgnEmpty(black_region)) { rfbLog("Xinerama: no blackouts needed (screen fills" " rectangle)\n"); sraRgnDestroy(black_region); return; } /* max len is 10000x10000+10000+10000 (23 chars) per geometry */ rcnt = (int) sraRgnCountRects(black_region); bstr = (char *) malloc(30 * (rcnt+1)); tstr = (char *) malloc(30); bstr[0] = '\0'; iter = sraRgnGetIterator(black_region); while (sraRgnIteratorNext(iter, &rect)) { int x, y, w, h; /* make sure x1 < x2 and y1 < y2 */ if (rect.x1 > rect.x2) { int tmp = rect.x2; rect.x2 = rect.x1; rect.x1 = tmp; } if (rect.y1 > rect.y2) { int tmp = rect.y2; rect.y2 = rect.y1; rect.y1 = tmp; } x = rect.x1; y = rect.y1; w = rect.x2 - x; h = rect.y2 - y; sprintf(tstr, "%dx%d+%d+%d,", w, h, x, y); strcat(bstr, tstr); } sraRgnReleaseIterator(iter); initialize_blackouts(bstr); free(bstr); free(tstr); #endif } void initialize_blackouts_and_xinerama(void) { blackouts = 0; blackout_ptr = 0; if (blackout_str != NULL) { initialize_blackouts(blackout_str); } if (xinerama) { initialize_xinerama(); } if (blackouts) { blackout_tiles(); /* schedule a copy_screen(), now is too early. */ do_copy_screen = 1; } } void push_sleep(int n) { int i; for (i=0; i dpy_x) { return; } if (y1 < 0 || y2 <= y1 || y2 > dpy_y) { return; } if (! main_fb) { return; } dst = main_fb + y1 * main_bytes_per_line + x1 * pixelsize; line = y1; while (line++ < y2) { memset(dst, fill, (size_t) (x2 - x1) * pixelsize); dst += main_bytes_per_line; } }