summaryrefslogtreecommitdiffstats
path: root/x11vnc/xinerama.c
diff options
context:
space:
mode:
Diffstat (limited to 'x11vnc/xinerama.c')
-rw-r--r--x11vnc/xinerama.c409
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;
+ }
+}
+
+