diff options
Diffstat (limited to 'x11vnc/xinerama.c')
-rw-r--r-- | x11vnc/xinerama.c | 409 |
1 files changed, 409 insertions, 0 deletions
diff --git a/x11vnc/xinerama.c b/x11vnc/xinerama.c new file mode 100644 index 0000000..975298b --- /dev/null +++ b/x11vnc/xinerama.c @@ -0,0 +1,409 @@ +/* -- 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; i<n; i++) { + int x, y, w, h; + + x = sc->x_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); + } + 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<n; i++) { + rfbPE(-1); + if (i != n-1 && defer_update) { + usleep(defer_update * 1000); + } + } +} + +/* + * try to forcefully push a black screen to all connected clients + */ +void push_black_screen(int n) { + if (!screen) { + return; + } + zero_fb(0, 0, dpy_x, dpy_y); + mark_rect_as_modified(0, 0, dpy_x, dpy_y, 0); + push_sleep(n); +} + +void refresh_screen(int push) { + int i; + if (!screen) { + return; + } + mark_rect_as_modified(0, 0, dpy_x, dpy_y, 0); + for (i=0; i<push; i++) { + rfbPE(-1); + } +} + +/* + * Fill the framebuffer with zero for the prescribed rectangle + */ +void zero_fb(int x1, int y1, int x2, int y2) { + int pixelsize = bpp/8; + int line, fill = 0; + char *dst; + + if (x1 < 0 || x2 <= x1 || x2 > 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; + } +} + + |