static int copy_all_tile_runs(void) {
	int x, y, n, m, i;
	int diffs = 0, ct;
	int in_run = 0, run = 0;
	int ntave = 0, ntcnt = 0;

	if (unixpw_in_progress) return 0;

	for (y=0; y < ntiles_y; y++) {
		for (x=0; x < ntiles_x + 1; x++) {
			n = x + y * ntiles_x;

			if (x != ntiles_x && tile_has_diff[n]) {
				in_run = 1;
				run++;
			} else {
				if (! in_run) {
					in_run = 0;
					run = 0;
					continue;
				}
				ct = copy_tiles(x - run, y, run);
				if (ct < 0) return ct;	/* fatal */

				ntcnt++;
				ntave += run;
				diffs += run;

				/* neighboring tile downward: */
				for (i=1; i <= run; i++) {
					if ((y+1) < ntiles_y
					    && tile_region[n-i].bot_diff) {
						m = (x-i) + (y+1) * ntiles_x;
						if (! tile_has_diff[m]) {
							tile_has_diff[m] = 2;
						}
					}
				}

				/* neighboring tile to right: */
				if (((x-1)+1) < ntiles_x
				    && tile_region[n-1].right_diff) {
					m = ((x-1)+1) + y * ntiles_x;
					if (! tile_has_diff[m]) {
						tile_has_diff[m] = 2;
					}

					/* note that this starts a new run */
					in_run = 1;
					run = 1;
				} else {
					in_run = 0;
					run = 0;
				}
			}
		}
		/*
		 * Could some activity go here, to emulate threaded
		 * behavior by servicing some libvncserver tasks?
		 */
	}
	return diffs;
}


static void snap_vcsa_rawfb(void) {
	int n;
	char *dst;
	char buf[32];
	int i, len, del;
	unsigned char rows, cols, xpos, ypos;
	static int prev_rows = -1, prev_cols = -1;
	static unsigned char prev_xpos = -1, prev_ypos = -1;
	static char *vcsabuf  = NULL;
	static char *vcsabuf0 = NULL;
	static unsigned int color_tab[16];
	static int Cw = 8, Ch = 16;
	static int db = -1, first = 1;
	int created = 0;
	rfbScreenInfo s;
	rfbScreenInfoPtr fake_screen = &s;
	int Bpp = raw_fb_native_bpp / 8;

	if (db < 0) {
		if (getenv("X11VNC_DEBUG_VCSA")) {
			db = atoi(getenv("X11VNC_DEBUG_VCSA"));
		} else {
			db = 0;
		}
	}

	if (first) {
		unsigned int rm = raw_fb_native_red_mask;
		unsigned int gm = raw_fb_native_green_mask;
		unsigned int bm = raw_fb_native_blue_mask;
		unsigned int rs = raw_fb_native_red_shift;
		unsigned int gs = raw_fb_native_green_shift;
		unsigned int bs = raw_fb_native_blue_shift;
		unsigned int rx = raw_fb_native_red_max;
		unsigned int gx = raw_fb_native_green_max;
		unsigned int bx = raw_fb_native_blue_max;

		for (i=0; i < 16; i++) {
			int r = console_cmap[3*i+0];
			int g = console_cmap[3*i+1];
			int b = console_cmap[3*i+2];
			r = rx * r / 255;
			g = gx * g / 255;
			b = bx * b / 255;
			color_tab[i] = (r << rs) | (g << gs) | (b << bs);
			if (db) fprintf(stderr, "cmap[%02d] 0x%08x  %04d %04d %04d\n", i, color_tab[i], r, g, b);
			if (i != 0 && getenv("RAWFB_VCSA_BW")) {
				color_tab[i] = rm | gm | bm;
			}
		}
	}
	first = 0;

	lseek(raw_fb_fd, 0, SEEK_SET);
	len = 4;
	del = 0;
	memset(buf, 0, sizeof(buf));
	while (len > 0) {
		n = read(raw_fb_fd, buf + del, len);
		if (n > 0) {
			del += n;
			len -= n;
		} else if (n == 0) {
			break;
		} else if (errno != EINTR && errno != EAGAIN) {
			break;
		}
	}

	rows = (unsigned char) buf[0];
	cols = (unsigned char) buf[1];
	xpos = (unsigned char) buf[2];
	ypos = (unsigned char) buf[3];

	if (db) fprintf(stderr, "rows=%d cols=%d xpos=%d ypos=%d Bpp=%d\n", rows, cols, xpos, ypos, Bpp);
	if (rows == 0 || cols == 0) {
		usleep(100 * 1000);
		return;
	}

	if (vcsabuf == NULL || prev_rows != rows || prev_cols != cols) {
		if (vcsabuf) {
			free(vcsabuf);
			free(vcsabuf0);
		}
		vcsabuf  = (char *) calloc(2 * rows * cols, 1);
		vcsabuf0 = (char *) calloc(2 * rows * cols, 1);
		created = 1;

		if (prev_rows != -1 && prev_cols != -1) {
			do_new_fb(1);
		}

		prev_rows = rows;
		prev_cols = cols;
	}

	if (!rfbEndianTest) {
		unsigned char tc = rows;
		rows = cols;
		cols = tc;

		tc = xpos;
		xpos = ypos;
		ypos = tc;
	}

	len = 2 * rows * cols;
	del = 0;
	memset(vcsabuf, 0, len);
	while (len > 0) {
		n = read(raw_fb_fd, vcsabuf + del, len);
		if (n > 0) {
			del += n;
			len -= n;
		} else if (n == 0) {
			break;
		} else if (errno != EINTR && errno != EAGAIN) {
			break;
		}
	}

	fake_screen->frameBuffer = snap->data;
	fake_screen->paddedWidthInBytes = snap->bytes_per_line;
	fake_screen->serverFormat.bitsPerPixel = raw_fb_native_bpp;
	fake_screen->width = snap->width;
	fake_screen->height = snap->height;

	for (i=0; i < rows * cols; i++) {
		int ix, iy, x, y, w, h;
		unsigned char chr = 0;
		unsigned char attr;
		unsigned int fore, back;
		unsigned short *usp;
		unsigned int *uip;
		chr  = (unsigned char) vcsabuf[2*i];
		attr = vcsabuf[2*i+1];

		iy = i / cols;
		ix = i - iy * cols;

		if (ix == prev_xpos && iy == prev_ypos) {
			;
		} else if (ix == xpos && iy == ypos) {
			;
		} else if (!created && chr == vcsabuf0[2*i] && attr == vcsabuf0[2*i+1]) {
			continue;
		}

		if (!rfbEndianTest) {
			unsigned char tc = chr;
			chr = attr;
			attr = tc;
		}

		y = iy * Ch;
		x = ix * Cw;
		dst = snap->data + y * snap->bytes_per_line + x * Bpp;

		fore = color_tab[attr & 0xf];
		back = color_tab[(attr >> 4) & 0x7];

		if (ix == xpos && iy == ypos) {
			unsigned int ti = fore;
			fore = back;
			back = ti;
		}

		for (h = 0; h < Ch; h++) {
			if (Bpp == 1) {
				memset(dst, back, Cw);
			} else if (Bpp == 2) {
				for (w = 0; w < Cw; w++) {
					usp = (unsigned short *) (dst + w*Bpp);
					*usp = (unsigned short) back;
				}
			} else if (Bpp == 4) {
				for (w = 0; w < Cw; w++) {
					uip = (unsigned int *) (dst + w*Bpp);
					*uip = (unsigned int) back;
				}
			}
			dst += snap->bytes_per_line;
		}
		rfbDrawChar(fake_screen, &default8x16Font, x, y + Ch, chr, fore);
	}
	memcpy(vcsabuf0, vcsabuf, 2 * rows * cols);
	prev_xpos = xpos;
	prev_ypos = ypos;
}

scan: limit access to shared memory segments to current user

void initialize_tiles(void);

void free_tiles(void);

void shm_delete(XShmSegmentInfo *shm);

void shm_clean(XShmSegmentInfo *shm, XImage *xim);

void initialize_polling_images(void);

void scale_rect(double factor_x, double factor_y, int blend, int interpolate, int Bpp,
    char *src_fb, int src_bytes_per_line, char *dst_fb, int dst_bytes_per_line,
    int Nx, int Ny, int nx, int ny, int X1, int Y1, int X2, int Y2, int mark);

void scale_and_mark_rect(int X1, int Y1, int X2, int Y2, int mark);

void mark_rect_as_modified(int x1, int y1, int x2, int y2, int force);

int copy_screen(void);

int copy_snap(void);

void nap_sleep(int ms, int split);

void set_offset(void);

int scan_for_updates(int count_only);

void rotate_curs(char *dst_0, char *src_0, int Dx, int Dy, int Bpp);

void rotate_coords(int x, int y, int *xo, int *yo, int dxi, int dyi);

void rotate_coords_inverse(int x, int y, int *xo, int *yo, int dxi, int dyi);

static void set_fs_factor(int max);

static char *flip_ximage_byte_order(XImage *xim);

static int shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h,
    char *name);

static void create_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void extend_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void save_hint(hint_t hint, int loc);

static void hint_updates(void);

static void mark_hint(hint_t hint);

static int copy_tiles(int tx, int ty, int nt);

static int copy_all_tiles(void);

static int copy_all_tile_runs(void);

static int copy_tiles_backward_pass(void);

static int copy_tiles_additional_pass(void);

static int gap_try(int x, int y, int *run, int *saw, int along_x);

static int fill_tile_gaps(void);

static int island_try(int x, int y, int u, int v, int *run);

static int grow_islands(void);

static void blackout_regions(void);

static void nap_set(int tile_cnt);

static void nap_check(int tile_cnt);

static void ping_clients(int tile_cnt);

static int blackout_line_skip(int n, int x, int y, int rescan,
    int *tile_count);

static int blackout_line_cmpskip(int n, int x, int y, char *dst, char *src,
    int w, int pixelsize);

static int scan_display(int ystart, int rescan);

static hint_t *hint_list;

int nap_ok = 0;

static int nap_diff_count = 0;

static int scan_count = 0;

static int scan_in_progress = 0;

typedef struct tile_change_region {
	/* start and end lines, along y, of the changed area inside a tile. */
	unsigned short first_line, last_line;
	short first_x, last_x;
	/* info about differences along edges. */
	unsigned short left_diff, right_diff;
	unsigned short top_diff,  bot_diff;
} region_t;

static region_t *tile_region;

static int fs_factor = 0;

#define CEIL(x)  ( (double) ((int) (x)) == (x) ? \
	(double) ((int) (x)) : (double) ((int) (x) + 1) )

#define FLOOR(x) ( (double) ((int) (x)) )


static int gap_try(int x, int y, int *run, int *saw, int along_x) {
	int n, m, i, xt, yt, ct;

	n = x + y * ntiles_x;

	if (! tile_has_diff[n]) {
		if (*saw) {
			(*run)++;	/* extend the gap run. */
		}
		return 0;
	}
	if (! *saw || *run == 0 || *run > gaps_fill) {
		*run = 0;		/* unacceptable run. */
		*saw = 1;
		return 0;
	}

	for (i=1; i <= *run; i++) {	/* iterate thru the run. */
		if (along_x) {
			xt = x - i;
			yt = y;
		} else {
			xt = x;
			yt = y - i;
		}

		m = xt + yt * ntiles_x;
		if (tile_tried[m]) {	/* do not repeat tiles */
			continue;
		}

		ct = copy_tiles(xt, yt, 1);
		if (ct < 0) return ct;	/* fatal */
	}
	*run = 0;
	*saw = 1;
	return 1;
}

void scale_rect(double factor_x, double factor_y, int blend, int interpolate, int Bpp,
    char *src_fb, int src_bytes_per_line, char *dst_fb, int dst_bytes_per_line,
    int Nx, int Ny, int nx, int ny, int X1, int Y1, int X2, int Y2, int mark);

void scale_and_mark_rect(int X1, int Y1, int X2, int Y2, int mark);

void mark_rect_as_modified(int x1, int y1, int x2, int y2, int force);

int copy_screen(void);

int copy_snap(void);

void nap_sleep(int ms, int split);

int scan_for_updates(int count_only);

void rotate_curs(char *dst_0, char *src_0, int Dx, int Dy, int Bpp);

void rotate_coords(int x, int y, int *xo, int *yo, int dxi, int dyi);

void rotate_coords_inverse(int x, int y, int *xo, int *yo, int dxi, int dyi);

static void set_fs_factor(int max);

static char *flip_ximage_byte_order(XImage *xim);

static int shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h,
    char *name);

static void create_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void extend_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void save_hint(hint_t hint, int loc);

static void hint_updates(void);

static void mark_hint(hint_t hint);

static int copy_tiles(int tx, int ty, int nt);

static int copy_all_tiles(void);

static int copy_all_tile_runs(void);

static int copy_tiles_backward_pass(void);

static int copy_tiles_additional_pass(void);

static int gap_try(int x, int y, int *run, int *saw, int along_x);

static int fill_tile_gaps(void);

static int island_try(int x, int y, int u, int v, int *run);

static int grow_islands(void);

static void blackout_regions(void);

static void nap_set(int tile_cnt);

static void nap_check(int tile_cnt);

static void ping_clients(int tile_cnt);

static int blackout_line_skip(int n, int x, int y, int rescan,
    int *tile_count);

static int blackout_line_cmpskip(int n, int x, int y, char *dst, char *src,
    int w, int pixelsize);

static int scan_display(int ystart, int rescan);

static hint_t *hint_list;

int nap_ok = 0;

static int nap_diff_count = 0;

static int scan_count = 0;

static int scan_in_progress = 0;

typedef struct tile_change_region {
	/* start and end lines, along y, of the changed area inside a tile. */
	unsigned short first_line, last_line;
	short first_x, last_x;
	/* info about differences along edges. */
	unsigned short left_diff, right_diff;
	unsigned short top_diff,  bot_diff;
} region_t;

static region_t *tile_region;

static int fs_factor = 0;

#define CEIL(x)  ( (double) ((int) (x)) == (x) ? \
	(double) ((int) (x)) : (double) ((int) (x) + 1) )

#define FLOOR(x) ( (double) ((int) (x)) )


static void set_fs_factor(int max) {
	int f, fac = 1, n = dpy_y;

	fs_factor = 0;
	if ((bpp/8) * dpy_x * dpy_y <= max)  {
		fs_factor = 1;
		return;
	}
	for (f=2; f <= 101; f++) {
		while (n % f == 0) {
			n = n / f;
			fac = fac * f;
			if ( (bpp/8) * dpy_x * (dpy_y/fac) <= max )  {
				fs_factor = fac;
				return;
			}
		}
	}
}

void initialize_tiles(void);

void free_tiles(void);

void shm_delete(XShmSegmentInfo *shm);

void shm_clean(XShmSegmentInfo *shm, XImage *xim);

void initialize_polling_images(void);

void scale_rect(double factor_x, double factor_y, int blend, int interpolate, int Bpp,
    char *src_fb, int src_bytes_per_line, char *dst_fb, int dst_bytes_per_line,
    int Nx, int Ny, int nx, int ny, int X1, int Y1, int X2, int Y2, int mark);

void scale_and_mark_rect(int X1, int Y1, int X2, int Y2, int mark);

void mark_rect_as_modified(int x1, int y1, int x2, int y2, int force);

int copy_screen(void);

int copy_snap(void);

void nap_sleep(int ms, int split);

void set_offset(void);

int scan_for_updates(int count_only);

void rotate_curs(char *dst_0, char *src_0, int Dx, int Dy, int Bpp);

void rotate_coords(int x, int y, int *xo, int *yo, int dxi, int dyi);

void rotate_coords_inverse(int x, int y, int *xo, int *yo, int dxi, int dyi);

static void set_fs_factor(int max);

static char *flip_ximage_byte_order(XImage *xim);

static int shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h,
    char *name);

static void create_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void extend_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void save_hint(hint_t hint, int loc);

static void hint_updates(void);

static void mark_hint(hint_t hint);

static int copy_tiles(int tx, int ty, int nt);

static int copy_all_tiles(void);

static int copy_all_tile_runs(void);

static int copy_tiles_backward_pass(void);

static int copy_tiles_additional_pass(void);

static int gap_try(int x, int y, int *run, int *saw, int along_x);

static int fill_tile_gaps(void);

static int island_try(int x, int y, int u, int v, int *run);

static int grow_islands(void);

static void blackout_regions(void);

static void nap_set(int tile_cnt);

static void nap_check(int tile_cnt);

static void ping_clients(int tile_cnt);

static int blackout_line_skip(int n, int x, int y, int rescan,
    int *tile_count);

static int blackout_line_cmpskip(int n, int x, int y, char *dst, char *src,
    int w, int pixelsize);

static int scan_display(int ystart, int rescan);

static hint_t *hint_list;

int nap_ok = 0;

static int nap_diff_count = 0;

static int scan_count = 0;

static int scan_in_progress = 0;

static region_t *tile_region;

static int fs_factor = 0;

#define CEIL(x)  ( (double) ((int) (x)) == (x) ? \
	(double) ((int) (x)) : (double) ((int) (x) + 1) )

#define FLOOR(x) ( (double) ((int) (x)) )

static int *first_line = NULL, *last_line = NULL;

static unsigned short *left_diff = NULL, *right_diff = NULL;


static int shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h,
    char *name) {

	XImage *xim;
	static int reported_flip = 0;
	int db = 0;

	shm->shmid = -1;
	shm->shmaddr = (char *) -1;
	*ximg_ptr = NULL;

	if (nofb) {
		return 1;
	}

	X_LOCK;

	if (! using_shm || xform24to32 || raw_fb) {
		/* we only need the XImage created */
		xim = XCreateImage_wr(dpy, default_visual, depth, ZPixmap,
		    0, NULL, w, h, raw_fb ? 32 : BitmapPad(dpy), 0);

		X_UNLOCK;

		if (xim == NULL) {
			rfbErr("XCreateImage(%s) failed.\n", name);
			if (quiet) {
				fprintf(stderr, "XCreateImage(%s) failed.\n",
				    name);
			}
			return 0;
		}
		if (db) fprintf(stderr, "shm_create simple %d %d\t%p %s\n", w, h, (void *)xim, name);
		xim->data = (char *) malloc(xim->bytes_per_line * xim->height);
		if (xim->data == NULL) {
			rfbErr("XCreateImage(%s) data malloc failed.\n", name);
			if (quiet) {
				fprintf(stderr, "XCreateImage(%s) data malloc"
				    " failed.\n", name);
			}
			return 0;
		}
		if (flip_byte_order) {
			char *order = flip_ximage_byte_order(xim);
			if (! reported_flip && ! quiet) {
				rfbLog("Changing XImage byte order"
				    " to %s\n", order);
				reported_flip = 1;
			}
		}

		*ximg_ptr = xim;
		return 1;
	}

	if (! dpy) {
		X_UNLOCK;
		return 0;
	}

	xim = XShmCreateImage_wr(dpy, default_visual, depth, ZPixmap, NULL,
	    shm, w, h);

	if (xim == NULL) {
		rfbErr("XShmCreateImage(%s) failed.\n", name);
		if (quiet) {
			fprintf(stderr, "XShmCreateImage(%s) failed.\n", name);
		}
		X_UNLOCK;
		return 0;
	}

	*ximg_ptr = xim;

#if HAVE_XSHM
	shm->shmid = shmget(IPC_PRIVATE,
	    xim->bytes_per_line * xim->height, IPC_CREAT | 0600);

	if (shm->shmid == -1) {
		rfbErr("shmget(%s) failed.\n", name);
		rfbLogPerror("shmget");

		XDestroyImage(xim);
		*ximg_ptr = NULL;

		X_UNLOCK;
		return 0;
	}

	shm->shmaddr = xim->data = (char *) shmat(shm->shmid, 0, 0);

	if (shm->shmaddr == (char *)-1) {
		rfbErr("shmat(%s) failed.\n", name);
		rfbLogPerror("shmat");

		XDestroyImage(xim);
		*ximg_ptr = NULL;

		shmctl(shm->shmid, IPC_RMID, 0);
		shm->shmid = -1;

		X_UNLOCK;
		return 0;
	}

	shm->readOnly = False;

	if (! XShmAttach_wr(dpy, shm)) {
		rfbErr("XShmAttach(%s) failed.\n", name);
		XDestroyImage(xim);
		*ximg_ptr = NULL;

		shmdt(shm->shmaddr);
		shm->shmaddr = (char *) -1;

		shmctl(shm->shmid, IPC_RMID, 0);
		shm->shmid = -1;

		X_UNLOCK;
		return 0;
	}
#endif

	X_UNLOCK;
	return 1;
}


void initialize_polling_images(void) {
	int i, MB = 1024 * 1024;

	/* set all shm areas to "none" before trying to create any */
	scanline_shm.shmid	= -1;
	scanline_shm.shmaddr	= (char *) -1;
	scanline		= NULL;
	fullscreen_shm.shmid	= -1;
	fullscreen_shm.shmaddr	= (char *) -1;
	fullscreen		= NULL;
	snaprect_shm.shmid	= -1;
	snaprect_shm.shmaddr	= (char *) -1;
	snaprect		= NULL;
	for (i=1; i<=ntiles_x; i++) {
		tile_row_shm[i].shmid	= -1;
		tile_row_shm[i].shmaddr	= (char *) -1;
		tile_row[i]		= NULL;
	}

	/* the scanline (e.g. 1280x1) shared memory area image: */

	if (! shm_create(&scanline_shm, &scanline, dpy_x, 1, "scanline")) {
		clean_up_exit(1);
	}

	/*
	 * the fullscreen (e.g. 1280x1024/fs_factor) shared memory area image:
	 * (we cut down the size of the shm area to try avoid and shm segment
	 * limits, e.g. the default 1MB on Solaris)
	 */
	if (UT.sysname && strstr(UT.sysname, "Linux")) {
		set_fs_factor(10 * MB);
	} else {
		set_fs_factor(1 * MB);
	}
	if (fs_frac >= 1.0) {
		fs_frac = 1.1;
		fs_factor = 0;
	}
	if (! fs_factor) {
		rfbLog("warning: fullscreen updates are disabled.\n");
	} else {
		if (! shm_create(&fullscreen_shm, &fullscreen, dpy_x,
		    dpy_y/fs_factor, "fullscreen")) {
			clean_up_exit(1);
		}
	}
	if (use_snapfb) {
		if (! fs_factor) {
			rfbLog("warning: disabling -snapfb mode.\n");
			use_snapfb = 0;
		} else if (! shm_create(&snaprect_shm, &snaprect, dpy_x,
		    dpy_y/fs_factor, "snaprect")) {
			clean_up_exit(1);
		}
	}

	/*
	 * for copy_tiles we need a lot of shared memory areas, one for
	 * each possible run length of changed tiles.  32 for 1024x768
	 * and 40 for 1280x1024, etc.
	 */

	tile_shm_count = 0;
	for (i=1; i<=ntiles_x; i++) {
		if (! shm_create(&tile_row_shm[i], &tile_row[i], tile_x * i,
		    tile_y, "tile_row")) {
			if (i == 1) {
				clean_up_exit(1);
			}
			rfbLog("shm: Error creating shared memory tile-row for"
			    " len=%d,\n", i);
			rfbLog("shm: reverting to -onetile mode. If this"
			    " problem persists\n");
			rfbLog("shm: try using the -onetile or -noshm options"
			    " to limit\n");
			rfbLog("shm: shared memory usage, or run ipcrm(1)"
			    " to manually\n");
			rfbLog("shm: delete unattached shm segments.\n");
			single_copytile_count = i;
			single_copytile = 1;
		}
		tile_shm_count++;
		if (single_copytile && i >= 1) {
			/* only need 1x1 tiles */
			break;
		}
	}
	if (verbose) {
		if (using_shm && ! xform24to32) {
			rfbLog("created %d tile_row shm polling images.\n",
			    tile_shm_count);
		} else {
			rfbLog("created %d tile_row polling images.\n",
			    tile_shm_count);
		}
	}
}

void initialize_tiles(void);

void free_tiles(void);

void shm_delete(XShmSegmentInfo *shm);

void shm_clean(XShmSegmentInfo *shm, XImage *xim);

void initialize_polling_images(void);

void scale_rect(double factor_x, double factor_y, int blend, int interpolate, int Bpp,
    char *src_fb, int src_bytes_per_line, char *dst_fb, int dst_bytes_per_line,
    int Nx, int Ny, int nx, int ny, int X1, int Y1, int X2, int Y2, int mark);

void scale_and_mark_rect(int X1, int Y1, int X2, int Y2, int mark);

void mark_rect_as_modified(int x1, int y1, int x2, int y2, int force);

int copy_screen(void);

int copy_snap(void);

void nap_sleep(int ms, int split);

void set_offset(void);

int scan_for_updates(int count_only);

void rotate_curs(char *dst_0, char *src_0, int Dx, int Dy, int Bpp);

void rotate_coords(int x, int y, int *xo, int *yo, int dxi, int dyi);

void rotate_coords_inverse(int x, int y, int *xo, int *yo, int dxi, int dyi);

static void set_fs_factor(int max);

static char *flip_ximage_byte_order(XImage *xim);

static int shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h,
    char *name);

static void create_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void extend_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void save_hint(hint_t hint, int loc);

static void hint_updates(void);

static void mark_hint(hint_t hint);

static int copy_tiles(int tx, int ty, int nt);

static int copy_all_tiles(void);

static int copy_all_tile_runs(void);

static int copy_tiles_backward_pass(void);

static int copy_tiles_additional_pass(void);

static int gap_try(int x, int y, int *run, int *saw, int along_x);

static int fill_tile_gaps(void);

static int island_try(int x, int y, int u, int v, int *run);

static int grow_islands(void);

static void blackout_regions(void);

static void nap_set(int tile_cnt);

static void nap_check(int tile_cnt);

static void ping_clients(int tile_cnt);

static int blackout_line_skip(int n, int x, int y, int rescan,
    int *tile_count);

static int blackout_line_cmpskip(int n, int x, int y, char *dst, char *src,
    int w, int pixelsize);

static int scan_display(int ystart, int rescan);

int nap_ok = 0;

static int nap_diff_count = 0;

static int scan_count = 0;

static int scan_in_progress = 0;

typedef struct tile_change_region {
	/* start and end lines, along y, of the changed area inside a tile. */
	unsigned short first_line, last_line;
	short first_x, last_x;
	/* info about differences along edges. */
	unsigned short left_diff, right_diff;
	unsigned short top_diff,  bot_diff;
} region_t;

static int fs_factor = 0;

#define CEIL(x)  ( (double) ((int) (x)) == (x) ? \
	(double) ((int) (x)) : (double) ((int) (x) + 1) )

#define FLOOR(x) ( (double) ((int) (x)) )


void rotate_curs(char *dst_0, char *src_0, int Dx, int Dy, int Bpp) {
	int x, y, xn, yn;
	char *src, *dst;
	int fbl, rbl;

	if (! rotating) {
		return;
	}

	fbl = Dx * Bpp;
	if (rotating_same) {
		rbl = Dx * Bpp;
	} else {
		rbl = Dy * Bpp;
	}

	if (rotating == ROTATE_X) {
		for (y = 0; y < Dy; y++)  {
			for (x = 0; x < Dx; x++)  {
				xn = Dx - x - 1;
				yn = y;
				ROT_COPY
if (0) fprintf(stderr, "rcurs: %d %d  %d %d\n", x, y, xn, yn);
			}
		}
	} else if (rotating == ROTATE_Y) {
		for (y = 0; y < Dy; y++)  {
			for (x = 0; x < Dx; x++)  {
				xn = x;
				yn = Dy - y - 1;
				ROT_COPY
			}
		}
	} else if (rotating == ROTATE_XY) {
		for (y = 0; y < Dy; y++)  {
			for (x = 0; x < Dx; x++)  {
				xn = Dx - x - 1;
				yn = Dy - y - 1;
				ROT_COPY
			}
		}
	} else if (rotating == ROTATE_90) {
		for (y = 0; y < Dy; y++)  {
			for (x = 0; x < Dx; x++)  {
				xn = Dy - y - 1;
				yn = x;
				ROT_COPY
			}
		}
	} else if (rotating == ROTATE_90X) {
		for (y = 0; y < Dy; y++)  {
			for (x = 0; x < Dx; x++)  {
				xn = y;
				yn = x;
				ROT_COPY
			}
		}
	} else if (rotating == ROTATE_90Y) {
		for (y = 0; y < Dy; y++)  {
			for (x = 0; x < Dx; x++)  {
				xn = Dy - y - 1;
				yn = Dx - x - 1;
				ROT_COPY
			}
		}
	} else if (rotating == ROTATE_270) {
		for (y = 0; y < Dy; y++)  {
			for (x = 0; x < Dx; x++)  {
				xn = y;
				yn = Dx - x - 1;
				ROT_COPY
			}
		}
	}
}

void initialize_tiles(void);

void free_tiles(void);

void shm_delete(XShmSegmentInfo *shm);

void shm_clean(XShmSegmentInfo *shm, XImage *xim);

void initialize_polling_images(void);

void scale_rect(double factor_x, double factor_y, int blend, int interpolate, int Bpp,
    char *src_fb, int src_bytes_per_line, char *dst_fb, int dst_bytes_per_line,
    int Nx, int Ny, int nx, int ny, int X1, int Y1, int X2, int Y2, int mark);

void scale_and_mark_rect(int X1, int Y1, int X2, int Y2, int mark);

void mark_rect_as_modified(int x1, int y1, int x2, int y2, int force);

int copy_screen(void);

int copy_snap(void);

void nap_sleep(int ms, int split);

void set_offset(void);

int scan_for_updates(int count_only);

void rotate_curs(char *dst_0, char *src_0, int Dx, int Dy, int Bpp);

void rotate_coords(int x, int y, int *xo, int *yo, int dxi, int dyi);

void rotate_coords_inverse(int x, int y, int *xo, int *yo, int dxi, int dyi);

static void set_fs_factor(int max);

static char *flip_ximage_byte_order(XImage *xim);

static int shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h,
    char *name);

static void create_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void extend_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void save_hint(hint_t hint, int loc);

static void hint_updates(void);

static void mark_hint(hint_t hint);

static int copy_tiles(int tx, int ty, int nt);

static int copy_all_tiles(void);

static int copy_all_tile_runs(void);

static int copy_tiles_backward_pass(void);

static int copy_tiles_additional_pass(void);

static int gap_try(int x, int y, int *run, int *saw, int along_x);

static int fill_tile_gaps(void);

static int island_try(int x, int y, int u, int v, int *run);

static int grow_islands(void);

static void blackout_regions(void);

static void nap_set(int tile_cnt);

static void nap_check(int tile_cnt);

static void ping_clients(int tile_cnt);

static int blackout_line_skip(int n, int x, int y, int rescan,
    int *tile_count);

static int blackout_line_cmpskip(int n, int x, int y, char *dst, char *src,
    int w, int pixelsize);

static int scan_display(int ystart, int rescan);

int nap_ok = 0;

static int nap_diff_count = 0;

static int scan_count = 0;

static int scan_in_progress = 0;

static int fs_factor = 0;

#define CEIL(x)  ( (double) ((int) (x)) == (x) ? \
	(double) ((int) (x)) : (double) ((int) (x) + 1) )

#define FLOOR(x) ( (double) ((int) (x)) )


int copy_snap(void) {
	int db = 1;
	char *fbp;
	int i, y, block_size;
	double dt;
	static int first = 1, snapcnt = 0;

	if (raw_fb_str) {
		int read_all_at_once = 1;
		double start = dnow();
		if (rawfb_reset < 0) {
			if (getenv("SNAPFB_RAWFB_RESET")) {
				rawfb_reset = 1;
			} else {
				rawfb_reset = 0;
			}
		}
		if (snap_fb == NULL || snap == NULL) {
			rfbLog("copy_snap: rawfb mode and null snap fb\n");
			clean_up_exit(1);
		}
		if (rawfb_reset) {
			initialize_raw_fb(1);
		}
		if (raw_fb_bytes_per_line != snap->bytes_per_line) {
			read_all_at_once = 0;
		}
		if (raw_fb_full_str && strstr(raw_fb_full_str, "/dev/vcsa")) {
			snap_vcsa_rawfb();
		} else if (read_all_at_once) {
			snap_all_rawfb();
		} else {
			/* this goes line by line, XXX not working for video */
			copy_raw_fb(snap, 0, 0, dpy_x, dpy_y);
		}
if (db && snapcnt++ < 5) rfbLog("rawfb copy_snap took: %.5f secs\n", dnow() - start);

		return 0;
	}

	if (! fs_factor) {
		return 0;
	}

	if (! snap_fb || ! snap || ! snaprect) {
		return 0;
	}
	block_size = ((dpy_y/fs_factor) * snap->bytes_per_line);

	fbp = snap_fb;
	y = 0;

	dtime0(&dt);
	X_LOCK;

	/* screen may be too big for 1 shm area, so broken into fs_factor */
	for (i=0; i < fs_factor; i++) {
		XRANDR_SET_TRAP_RET(-1, "copy_snap-set");
		copy_image(snaprect, 0, y, 0, 0);
		XRANDR_CHK_TRAP_RET(-1, "copy_snap-chk");

		memcpy(fbp, snaprect->data, (size_t) block_size);

		y += dpy_y / fs_factor;
		fbp += block_size;
	}

	X_UNLOCK;

	dt = dtime(&dt);
	if (first) {
		rfbLog("copy_snap: time for -snapfb snapshot: %.3f sec\n", dt);
		first = 0;
	}

	return 0;
}


static void nap_check(int tile_cnt) {
	time_t now;

	nap_diff_count += tile_cnt;

	if (! take_naps) {
		return;
	}

	now = time(NULL);

	if (screen_blank > 0) {
		int dt_ev, dt_fbu;
		static int ms = 0;
		if (ms == 0) {
			ms = 2000;
			if (getenv("X11VNC_SB_FACTOR")) {
				ms = ms * atof(getenv("X11VNC_SB_FACTOR"));
			}
			if (ms <= 0) {
				ms = 2000;
			}
		}

		/* if no activity, pause here for a second or so. */
		dt_ev  = (int) (now - last_event);
		dt_fbu = (int) (now - last_fb_bytes_sent);
		if (dt_fbu > screen_blank) {
			/* sleep longer for no fb requests */
			if (debug_tiles > 1) {
				fprintf(stderr, "screen blank sleep1: %d ms / 16, load: %s\n", 2 * ms, get_load());
			}
			nap_sleep(2 * ms, 16);
			return;
		}
		if (dt_ev > screen_blank) {
			if (debug_tiles > 1) {
				fprintf(stderr, "screen blank sleep2: %d ms / 8, load: %s\n", ms, get_load());
			}
			nap_sleep(ms, 8);
			return;
		}
	}
	if (naptile && nap_ok && tile_cnt < naptile) {
		int ms = napfac * waitms;
		ms = ms > napmax ? napmax : ms;
		if (now - last_input <= 3) {
			nap_ok = 0;
		} else if (now - last_local_input <= 3) {
			nap_ok = 0;
		} else {
			if (debug_tiles > 1) {
				fprintf(stderr, "nap_check sleep: %d ms / 1, load: %s\n", ms, get_load());
			}
			nap_sleep(ms, 1);
		}
	}
}

void initialize_tiles(void);

void free_tiles(void);

void shm_delete(XShmSegmentInfo *shm);

void shm_clean(XShmSegmentInfo *shm, XImage *xim);

void initialize_polling_images(void);

void scale_rect(double factor_x, double factor_y, int blend, int interpolate, int Bpp,
    char *src_fb, int src_bytes_per_line, char *dst_fb, int dst_bytes_per_line,
    int Nx, int Ny, int nx, int ny, int X1, int Y1, int X2, int Y2, int mark);

void scale_and_mark_rect(int X1, int Y1, int X2, int Y2, int mark);

void mark_rect_as_modified(int x1, int y1, int x2, int y2, int force);

int copy_screen(void);

int copy_snap(void);

void nap_sleep(int ms, int split);

void set_offset(void);

int scan_for_updates(int count_only);

void rotate_curs(char *dst_0, char *src_0, int Dx, int Dy, int Bpp);

void rotate_coords(int x, int y, int *xo, int *yo, int dxi, int dyi);

void rotate_coords_inverse(int x, int y, int *xo, int *yo, int dxi, int dyi);

static void set_fs_factor(int max);

static char *flip_ximage_byte_order(XImage *xim);

static int shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h,
    char *name);

static void create_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void extend_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void save_hint(hint_t hint, int loc);

static void hint_updates(void);

static void mark_hint(hint_t hint);

static int copy_tiles(int tx, int ty, int nt);

static int copy_all_tiles(void);

static int copy_all_tile_runs(void);

static int copy_tiles_backward_pass(void);

static int copy_tiles_additional_pass(void);

static int gap_try(int x, int y, int *run, int *saw, int along_x);

static int fill_tile_gaps(void);

static int island_try(int x, int y, int u, int v, int *run);

static int grow_islands(void);

static void blackout_regions(void);

static void nap_set(int tile_cnt);

static void nap_check(int tile_cnt);

static void ping_clients(int tile_cnt);

static int blackout_line_skip(int n, int x, int y, int rescan,
    int *tile_count);

static int blackout_line_cmpskip(int n, int x, int y, char *dst, char *src,
    int w, int pixelsize);

static int scan_display(int ystart, int rescan);

static hint_t *hint_list;

int nap_ok = 0;

static int nap_diff_count = 0;

static int scan_count = 0;

static int scan_in_progress = 0;

static region_t *tile_region;

static int fs_factor = 0;

#define CEIL(x)  ( (double) ((int) (x)) == (x) ? \
	(double) ((int) (x)) : (double) ((int) (x) + 1) )

#define FLOOR(x) ( (double) ((int) (x)) )


void rotate_coords(int x, int y, int *xo, int *yo, int dxi, int dyi) {
	int xi = x, yi = y;
	int Dx, Dy;

	if (dxi >= 0) {
		Dx = dxi;
		Dy = dyi;
	} else if (scaling) {
		Dx = scaled_x;
		Dy = scaled_y;
	} else {
		Dx = dpy_x;
		Dy = dpy_y;
	}

	/* ncache?? */

	if (rotating == ROTATE_NONE) {
		*xo = xi;
		*yo = yi;
	} else if (rotating == ROTATE_X) {
		*xo = Dx - xi - 1;
		*yo = yi;
	} else if (rotating == ROTATE_Y) {
		*xo = xi;
		*yo = Dy - yi - 1;
	} else if (rotating == ROTATE_XY) {
		*xo = Dx - xi - 1;
		*yo = Dy - yi - 1;
	} else if (rotating == ROTATE_90) {
		*xo = Dy - yi - 1;
		*yo = xi;
	} else if (rotating == ROTATE_90X) {
		*xo = yi;
		*yo = xi;
	} else if (rotating == ROTATE_90Y) {
		*xo = Dy - yi - 1;
		*yo = Dx - xi - 1;
	} else if (rotating == ROTATE_270) {
		*xo = yi;
		*yo = Dx - xi - 1;
	}
}

void initialize_tiles(void);

void free_tiles(void);

void shm_delete(XShmSegmentInfo *shm);

void shm_clean(XShmSegmentInfo *shm, XImage *xim);

void initialize_polling_images(void);

void scale_rect(double factor_x, double factor_y, int blend, int interpolate, int Bpp,
    char *src_fb, int src_bytes_per_line, char *dst_fb, int dst_bytes_per_line,
    int Nx, int Ny, int nx, int ny, int X1, int Y1, int X2, int Y2, int mark);

void scale_and_mark_rect(int X1, int Y1, int X2, int Y2, int mark);

void mark_rect_as_modified(int x1, int y1, int x2, int y2, int force);

int copy_screen(void);

int copy_snap(void);

void nap_sleep(int ms, int split);

void set_offset(void);

int scan_for_updates(int count_only);

void rotate_curs(char *dst_0, char *src_0, int Dx, int Dy, int Bpp);

void rotate_coords(int x, int y, int *xo, int *yo, int dxi, int dyi);

void rotate_coords_inverse(int x, int y, int *xo, int *yo, int dxi, int dyi);

static void set_fs_factor(int max);

static int shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h,
    char *name);

static void create_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void extend_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void save_hint(hint_t hint, int loc);

static void hint_updates(void);

static void mark_hint(hint_t hint);

static int copy_tiles(int tx, int ty, int nt);

static int copy_all_tiles(void);

static int copy_all_tile_runs(void);

static int copy_tiles_backward_pass(void);

static int copy_tiles_additional_pass(void);

static int gap_try(int x, int y, int *run, int *saw, int along_x);

static int fill_tile_gaps(void);

static int island_try(int x, int y, int u, int v, int *run);

static int grow_islands(void);

static void blackout_regions(void);

static void nap_set(int tile_cnt);

static void nap_check(int tile_cnt);

static void ping_clients(int tile_cnt);

static int blackout_line_skip(int n, int x, int y, int rescan,
    int *tile_count);

static int blackout_line_cmpskip(int n, int x, int y, char *dst, char *src,
    int w, int pixelsize);

static int scan_display(int ystart, int rescan);

int nap_ok = 0;

static int nap_diff_count = 0;

static int scan_count = 0;

static int scan_in_progress = 0;

static int fs_factor = 0;

#define CEIL(x)  ( (double) ((int) (x)) == (x) ? \
	(double) ((int) (x)) : (double) ((int) (x) + 1) )

#define FLOOR(x) ( (double) ((int) (x)) )


static int blackout_line_cmpskip(int n, int x, int y, char *dst, char *src,
    int w, int pixelsize) {

	int i, x1, y1, x2, y2, b, hit = 0;
	int beg = -1, end = -1;

	if (tile_blackout[n].cover == 0) {
		return 0;	/* 0 means do not skip it. */
	} else if (tile_blackout[n].cover == 2) {
		return 1;	/* 1 means skip it. */
	}

	/* tile has partial coverage: */

	for (i=0; i < w * pixelsize; i++)  {
		if (*(dst+i) != *(src+i)) {
			beg = i/pixelsize;	/* beginning difference */
			break;
		}
	}
	for (i = w * pixelsize - 1; i >= 0; i--)  {
		if (*(dst+i) != *(src+i)) {
			end = i/pixelsize;	/* ending difference */
			break;
		}
	}
	if (beg < 0 || end < 0) {
		/* problem finding range... */
		return 0;
	}

	/* loop over blackout rectangles: */
	for (b=0; b < tile_blackout[n].count; b++) {

		/* y in full display space: */
		y1 = tile_blackout[n].bo[b].y1;
		y2 = tile_blackout[n].bo[b].y2;

		/* x relative to tile origin: */
		x1 = tile_blackout[n].bo[b].x1 - x;
		x2 = tile_blackout[n].bo[b].x2 - x;

		if (y1 > y || y >= y2) {
			continue;
		}
		if (x1 <= beg && end <= x2) {
			hit = 1;
			break;
		}
	}
	if (hit) {
		return 1;
	} else {
		return 0;
	}
}


static void ping_clients(int tile_cnt) {
	static time_t last_send = 0;
	time_t now = time(NULL);

	if (rfbMaxClientWait < 20000) {
		rfbMaxClientWait = 20000;
		rfbLog("reset rfbMaxClientWait to %d msec.\n",
		    rfbMaxClientWait);
	}
	if (tile_cnt > 0) {
		last_send = now;
	} else if (tile_cnt < 0) {
		/* negative tile_cnt is -ping case */
		if (now >= last_send - tile_cnt) {
			mark_rect_as_modified(0, 0, 1, 1, 1);
			last_send = now;
		}
	} else if (now - last_send > 5) {
		/* Send small heartbeat to client */
		mark_rect_as_modified(0, 0, 1, 1, 1);
		last_send = now;
	}
}


void free_tiles(void) {
	if (tile_has_diff) {
		free(tile_has_diff);
		tile_has_diff = NULL;
	}
	if (tile_has_xdamage_diff) {
		free(tile_has_xdamage_diff);
		tile_has_xdamage_diff = NULL;
	}
	if (tile_row_has_xdamage_diff) {
		free(tile_row_has_xdamage_diff);
		tile_row_has_xdamage_diff = NULL;
	}
	if (tile_tried) {
		free(tile_tried);
		tile_tried = NULL;
	}
	if (tile_copied) {
		free(tile_copied);
		tile_copied = NULL;
	}
	if (tile_blackout) {
		free(tile_blackout);
		tile_blackout = NULL;
	}
	if (tile_region) {
		free(tile_region);
		tile_region = NULL;
	}
	if (tile_row) {
		free(tile_row);
		tile_row = NULL;
	}
	if (tile_row_shm) {
		free(tile_row_shm);
		tile_row_shm = NULL;
	}
	if (hint_list) {
		free(hint_list);
		hint_list = NULL;
	}
}

void initialize_tiles(void);

void free_tiles(void);

void shm_delete(XShmSegmentInfo *shm);

void shm_clean(XShmSegmentInfo *shm, XImage *xim);

void initialize_polling_images(void);

void scale_rect(double factor_x, double factor_y, int blend, int interpolate, int Bpp,
    char *src_fb, int src_bytes_per_line, char *dst_fb, int dst_bytes_per_line,
    int Nx, int Ny, int nx, int ny, int X1, int Y1, int X2, int Y2, int mark);

void scale_and_mark_rect(int X1, int Y1, int X2, int Y2, int mark);

void mark_rect_as_modified(int x1, int y1, int x2, int y2, int force);

int copy_screen(void);

int copy_snap(void);

void nap_sleep(int ms, int split);

void set_offset(void);

void rotate_curs(char *dst_0, char *src_0, int Dx, int Dy, int Bpp);

void rotate_coords(int x, int y, int *xo, int *yo, int dxi, int dyi);

void rotate_coords_inverse(int x, int y, int *xo, int *yo, int dxi, int dyi);

static void set_fs_factor(int max);

static void create_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void extend_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void save_hint(hint_t hint, int loc);

static void hint_updates(void);

static void mark_hint(hint_t hint);

static int copy_all_tiles(void);

static int copy_all_tile_runs(void);

static int copy_tiles_backward_pass(void);

static int copy_tiles_additional_pass(void);

static int fill_tile_gaps(void);

static int grow_islands(void);

static void blackout_regions(void);

static void nap_set(int tile_cnt);

static void nap_check(int tile_cnt);

static void ping_clients(int tile_cnt);

static hint_t *hint_list;

static region_t *tile_region;

static int *first_line = NULL, *last_line = NULL;

static unsigned short *left_diff = NULL, *right_diff = NULL;


void rotate_fb(int x1, int y1, int x2, int y2) {
	int x, y, xn, yn, r_x1, r_y1, r_x2, r_y2, Bpp = bpp/8;
	int fbl = rfb_bytes_per_line;
	int rbl = rot_bytes_per_line;
	int Dx, Dy;
	char *src, *dst;
	char *src_0 = rfb_fb;
	char *dst_0 = rot_fb;

	if (! rotating || ! rot_fb) {
		return;
	}

	if (scaling) {
		Dx = scaled_x;
		Dy = scaled_y;
	} else {
		Dx = dpy_x;
		Dy = dpy_y;
	}
	rotate_coords(x1, y1, &r_x1, &r_y1, -1, -1);
	rotate_coords(x2, y2, &r_x2, &r_y2, -1, -1);

	dst = rot_fb;

	if (rotating == ROTATE_X) {
		for (y = y1; y < y2; y++)  {
			for (x = x1; x < x2; x++)  {
				xn = Dx - x - 1;
				yn = y;
				ROT_COPY
			}
		}
	} else if (rotating == ROTATE_Y) {
		for (y = y1; y < y2; y++)  {
			for (x = x1; x < x2; x++)  {
				xn = x;
				yn = Dy - y - 1;
				ROT_COPY
			}
		}
	} else if (rotating == ROTATE_XY) {
		for (y = y1; y < y2; y++)  {
			for (x = x1; x < x2; x++)  {
				xn = Dx - x - 1;
				yn = Dy - y - 1;
				ROT_COPY
			}
		}
	} else if (rotating == ROTATE_90) {
		for (y = y1; y < y2; y++)  {
			for (x = x1; x < x2; x++)  {
				xn = Dy - y - 1;
				yn = x;
				ROT_COPY
			}
		}
	} else if (rotating == ROTATE_90X) {
		for (y = y1; y < y2; y++)  {
			for (x = x1; x < x2; x++)  {
				xn = y;
				yn = x;
				ROT_COPY
			}
		}
	} else if (rotating == ROTATE_90Y) {
		for (y = y1; y < y2; y++)  {
			for (x = x1; x < x2; x++)  {
				xn = Dy - y - 1;
				yn = Dx - x - 1;
				ROT_COPY
			}
		}
	} else if (rotating == ROTATE_270) {
		for (y = y1; y < y2; y++)  {
			for (x = x1; x < x2; x++)  {
				xn = y;
				yn = Dx - x - 1;
				ROT_COPY
			}
		}
	}
}


static void snap_all_rawfb(void) {
	int pixelsize = bpp/8;
	int n, sz;
	char *dst;
	static char *unclipped_dst = NULL;
	static int unclipped_len = 0;

	dst = snap->data;

	if (xform24to32 && bpp == 32) {
		pixelsize = 3;
	}
	sz = dpy_y * snap->bytes_per_line;

	if (wdpy_x > dpy_x || wdpy_y > dpy_y) {
		sz = wdpy_x * wdpy_y * pixelsize;
		if (sz > unclipped_len || unclipped_dst == NULL) {
			if (unclipped_dst) {
				free(unclipped_dst);
			}
			unclipped_dst = (char *) malloc(sz+4);
			unclipped_len = sz;
		}
		dst = unclipped_dst;
	}

	if (! raw_fb_seek) {
		memcpy(dst, raw_fb_addr + raw_fb_offset, sz);

	} else {
		int len = sz, del = 0;
		off_t off = (off_t) raw_fb_offset;

		lseek(raw_fb_fd, off, SEEK_SET);
		while (len > 0) {
			n = read(raw_fb_fd, dst + del, len);
			if (n > 0) {
				del += n;
				len -= n;
			} else if (n == 0) {
				break;
			} else if (errno != EINTR && errno != EAGAIN) {
				break;
			}
		}
	}

	if (dst == unclipped_dst) {
		char *src;
		int h;
		int x = off_x + coff_x;
		int y = off_y + coff_y;

		src = unclipped_dst + y * wdpy_x * pixelsize +
		    x * pixelsize;
		dst = snap->data;

		for (h = 0; h < dpy_y; h++) {
			memcpy(dst, src, dpy_x * pixelsize);
			src += wdpy_x * pixelsize;
			dst += snap->bytes_per_line;
		}
	}
}


static char *flip_ximage_byte_order(XImage *xim) {
	char *order;
	if (xim->byte_order == LSBFirst) {
		order = "MSBFirst";
		xim->byte_order = MSBFirst;
		xim->bitmap_bit_order = MSBFirst;
	} else {
		order = "LSBFirst";
		xim->byte_order = LSBFirst;
		xim->bitmap_bit_order = LSBFirst;
	}
	return order;
}

void shm_clean(XShmSegmentInfo *shm, XImage *xim);

void scale_rect(double factor_x, double factor_y, int blend, int interpolate, int Bpp,
    char *src_fb, int src_bytes_per_line, char *dst_fb, int dst_bytes_per_line,
    int Nx, int Ny, int nx, int ny, int X1, int Y1, int X2, int Y2, int mark);

void rotate_curs(char *dst_0, char *src_0, int Dx, int Dy, int Bpp);

static void set_fs_factor(int max);

static char *flip_ximage_byte_order(XImage *xim);

static int shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h,
    char *name);

static void create_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void extend_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void save_hint(hint_t hint, int loc);

static void hint_updates(void);

static void mark_hint(hint_t hint);

static int copy_tiles(int tx, int ty, int nt);

static int copy_all_tiles(void);

static int copy_all_tile_runs(void);

static int copy_tiles_backward_pass(void);

static int copy_tiles_additional_pass(void);

static int gap_try(int x, int y, int *run, int *saw, int along_x);

static int fill_tile_gaps(void);

static int island_try(int x, int y, int u, int v, int *run);

static int grow_islands(void);

static void blackout_regions(void);

static void nap_set(int tile_cnt);

static void nap_check(int tile_cnt);

static void ping_clients(int tile_cnt);

static int blackout_line_skip(int n, int x, int y, int rescan,
    int *tile_count);

static int blackout_line_cmpskip(int n, int x, int y, char *dst, char *src,
    int w, int pixelsize);

static int scan_display(int ystart, int rescan);

static hint_t *hint_list;

static int nap_diff_count = 0;

static int scan_count = 0;

static int scan_in_progress = 0;

static region_t *tile_region;

static int fs_factor = 0;

#define ROT_COPY \
	src = src_0 + fbl*y  + Bpp*x;  \
	dst = dst_0 + rbl*yn + Bpp*xn; \
	if (Bpp == 1) { \
		*(dst) = *(src);	 \
	} else if (Bpp == 2) { \
		*(dst+0) = *(src+0);	 \
		*(dst+1) = *(src+1);	 \
	} else if (Bpp == 3) { \
		*(dst+0) = *(src+0);	 \
		*(dst+1) = *(src+1);	 \
		*(dst+2) = *(src+2);	 \
	} else if (Bpp == 4) { \
		*(dst+0) = *(src+0);	 \
		*(dst+1) = *(src+1);	 \
		*(dst+2) = *(src+2);	 \
		*(dst+3) = *(src+3);	 \
	}

static int *first_line = NULL, *last_line = NULL;

static unsigned short *left_diff = NULL, *right_diff = NULL;


void shm_delete(XShmSegmentInfo *shm) {
#if HAVE_XSHM
	if (getenv("X11VNC_SHM_DEBUG")) fprintf(stderr, "shm_delete:    %p\n", (void *) shm);
	if (shm != NULL && shm->shmaddr != (char *) -1) {
		shmdt(shm->shmaddr);
	}
	if (shm != NULL && shm->shmid != -1) {
		shmctl(shm->shmid, IPC_RMID, 0);
	}
	if (shm != NULL) {
		shm->shmaddr = (char *) -1;
		shm->shmid = -1;
	}
#else
	if (!shm) {}
#endif
}

void initialize_tiles(void);

void free_tiles(void);

void shm_delete(XShmSegmentInfo *shm);

void shm_clean(XShmSegmentInfo *shm, XImage *xim);

void initialize_polling_images(void);

void scale_rect(double factor_x, double factor_y, int blend, int interpolate, int Bpp,
    char *src_fb, int src_bytes_per_line, char *dst_fb, int dst_bytes_per_line,
    int Nx, int Ny, int nx, int ny, int X1, int Y1, int X2, int Y2, int mark);

void scale_and_mark_rect(int X1, int Y1, int X2, int Y2, int mark);

void mark_rect_as_modified(int x1, int y1, int x2, int y2, int force);

int copy_screen(void);

int copy_snap(void);

void nap_sleep(int ms, int split);

void set_offset(void);

void rotate_curs(char *dst_0, char *src_0, int Dx, int Dy, int Bpp);

void rotate_coords(int x, int y, int *xo, int *yo, int dxi, int dyi);

void rotate_coords_inverse(int x, int y, int *xo, int *yo, int dxi, int dyi);

static void set_fs_factor(int max);

static char *flip_ximage_byte_order(XImage *xim);

static int shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h,
    char *name);

static void create_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void extend_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void save_hint(hint_t hint, int loc);

static void hint_updates(void);

static void mark_hint(hint_t hint);

static int copy_all_tiles(void);

static int copy_all_tile_runs(void);

static int copy_tiles_backward_pass(void);

static int copy_tiles_additional_pass(void);

static int fill_tile_gaps(void);

static int grow_islands(void);

static void blackout_regions(void);

static void nap_set(int tile_cnt);

static void nap_check(int tile_cnt);

static void ping_clients(int tile_cnt);

static int blackout_line_cmpskip(int n, int x, int y, char *dst, char *src,
    int w, int pixelsize);

#define ROT_COPY \
	src = src_0 + fbl*y  + Bpp*x;  \
	dst = dst_0 + rbl*yn + Bpp*xn; \
	if (Bpp == 1) { \
		*(dst) = *(src);	 \
	} else if (Bpp == 2) { \
		*(dst+0) = *(src+0);	 \
		*(dst+1) = *(src+1);	 \
	} else if (Bpp == 3) { \
		*(dst+0) = *(src+0);	 \
		*(dst+1) = *(src+1);	 \
		*(dst+2) = *(src+2);	 \
	} else if (Bpp == 4) { \
		*(dst+0) = *(src+0);	 \
		*(dst+1) = *(src+1);	 \
		*(dst+2) = *(src+2);	 \
		*(dst+3) = *(src+3);	 \
	}

static int *first_line = NULL, *last_line = NULL;

static unsigned short *left_diff = NULL, *right_diff = NULL;


static int grow_islands(void) {
	int x, y, n, run;
	int diffs = 0, ct;

	/*
	 * n.b. the way we scan here should keep an extension going,
	 * and so also fill in gaps effectively...
	 */

	/* left to right: */
	for (y=0; y < ntiles_y; y++) {
		run = 0;
		for (x=0; x <= ntiles_x - 2; x++) {
			ct = island_try(x, y, x+1, y, &run);
			if (ct < 0) return ct;	/* fatal */
		}
	}
	/* right to left: */
	for (y=0; y < ntiles_y; y++) {
		run = 0;
		for (x = ntiles_x - 1; x >= 1; x--) {
			ct = island_try(x, y, x-1, y, &run);
			if (ct < 0) return ct;	/* fatal */
		}
	}
	for (n=0; n < ntiles; n++) {
		if (tile_has_diff[n]) {
			diffs++;
		}
	}
	return diffs;
}


int copy_screen(void) {
	char *fbp;
	int i, y, block_size;

	if (! fs_factor) {
		return 0;
	}
	if (debug_tiles) fprintf(stderr, "copy_screen\n");

	if (unixpw_in_progress) return 0;

	if (! main_fb) {
		return 0;
	}

	block_size = ((dpy_y/fs_factor) * main_bytes_per_line);

	fbp = main_fb;
	y = 0;

	X_LOCK;

	/* screen may be too big for 1 shm area, so broken into fs_factor */
	for (i=0; i < fs_factor; i++) {
		XRANDR_SET_TRAP_RET(-1, "copy_screen-set");
		copy_image(fullscreen, 0, y, 0, 0);
		XRANDR_CHK_TRAP_RET(-1, "copy_screen-chk");

		memcpy(fbp, fullscreen->data, (size_t) block_size);

		y += dpy_y / fs_factor;
		fbp += block_size;
	}

	X_UNLOCK;

	if (blackouts) {
		blackout_regions();
	}

	mark_rect_as_modified(0, 0, dpy_x, dpy_y, 0);
	return 0;
}

void initialize_tiles(void);

void free_tiles(void);

void shm_delete(XShmSegmentInfo *shm);

void shm_clean(XShmSegmentInfo *shm, XImage *xim);

void initialize_polling_images(void);

void scale_rect(double factor_x, double factor_y, int blend, int interpolate, int Bpp,
    char *src_fb, int src_bytes_per_line, char *dst_fb, int dst_bytes_per_line,
    int Nx, int Ny, int nx, int ny, int X1, int Y1, int X2, int Y2, int mark);

void scale_and_mark_rect(int X1, int Y1, int X2, int Y2, int mark);

void mark_rect_as_modified(int x1, int y1, int x2, int y2, int force);

int copy_screen(void);

int copy_snap(void);

void nap_sleep(int ms, int split);

void set_offset(void);

int scan_for_updates(int count_only);

void rotate_curs(char *dst_0, char *src_0, int Dx, int Dy, int Bpp);

void rotate_coords(int x, int y, int *xo, int *yo, int dxi, int dyi);

void rotate_coords_inverse(int x, int y, int *xo, int *yo, int dxi, int dyi);

static void set_fs_factor(int max);

static char *flip_ximage_byte_order(XImage *xim);

static int shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h,
    char *name);

static void create_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void extend_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void save_hint(hint_t hint, int loc);

static void hint_updates(void);

static void mark_hint(hint_t hint);

static int copy_tiles(int tx, int ty, int nt);

static int copy_all_tiles(void);

static int copy_all_tile_runs(void);

static int copy_tiles_backward_pass(void);

static int copy_tiles_additional_pass(void);

static int gap_try(int x, int y, int *run, int *saw, int along_x);

static int fill_tile_gaps(void);

static int island_try(int x, int y, int u, int v, int *run);

static int grow_islands(void);

static void blackout_regions(void);

static void nap_set(int tile_cnt);

static void nap_check(int tile_cnt);

static void ping_clients(int tile_cnt);

static int blackout_line_skip(int n, int x, int y, int rescan,
    int *tile_count);

static int blackout_line_cmpskip(int n, int x, int y, char *dst, char *src,
    int w, int pixelsize);

static int scan_display(int ystart, int rescan);

int nap_ok = 0;

static int nap_diff_count = 0;

static int scan_count = 0;

static int scan_in_progress = 0;

typedef struct tile_change_region {
	/* start and end lines, along y, of the changed area inside a tile. */
	unsigned short first_line, last_line;
	short first_x, last_x;
	/* info about differences along edges. */
	unsigned short left_diff, right_diff;
	unsigned short top_diff,  bot_diff;
} region_t;

static int fs_factor = 0;

#define CEIL(x)  ( (double) ((int) (x)) == (x) ? \
	(double) ((int) (x)) : (double) ((int) (x) + 1) )

#define FLOOR(x) ( (double) ((int) (x)) )

static int *first_line = NULL, *last_line = NULL;


void mark_rect_as_modified(int x1, int y1, int x2, int y2, int force) {

	if (damage_time != 0) {
		/*
		 * This is not XDAMAGE, rather a hack for testing
		 * where we allow the framebuffer to be corrupted for
		 * damage_delay seconds.
		 */
		int debug = 0;
		if (time(NULL) > damage_time + damage_delay) {
			if (! quiet) {
				rfbLog("damaging turned off.\n");
			}
			damage_time = 0;
			damage_delay = 0;
		} else {
			if (debug) {
				rfbLog("damaging viewer fb by not marking "
				    "rect: %d,%d,%d,%d\n", x1, y1, x2, y2);
			}
			return;
		}
	}

	if (rfb_fb == main_fb || force) {
		mark_wrapper(x1, y1, x2, y2);
		return;
	}

	if (cmap8to24) {
		bpp8to24(x1, y1, x2, y2);
	}

	if (scaling) {
		scale_and_mark_rect(x1, y1, x2, y2, 1);
	} else {
		mark_wrapper(x1, y1, x2, y2);
	}
}

void initialize_tiles(void);

void free_tiles(void);

void shm_delete(XShmSegmentInfo *shm);

void shm_clean(XShmSegmentInfo *shm, XImage *xim);

void initialize_polling_images(void);

void scale_rect(double factor_x, double factor_y, int blend, int interpolate, int Bpp,
    char *src_fb, int src_bytes_per_line, char *dst_fb, int dst_bytes_per_line,
    int Nx, int Ny, int nx, int ny, int X1, int Y1, int X2, int Y2, int mark);

void scale_and_mark_rect(int X1, int Y1, int X2, int Y2, int mark);

void mark_rect_as_modified(int x1, int y1, int x2, int y2, int force);

int copy_screen(void);

int copy_snap(void);

void nap_sleep(int ms, int split);

void set_offset(void);

int scan_for_updates(int count_only);

void rotate_curs(char *dst_0, char *src_0, int Dx, int Dy, int Bpp);

void rotate_coords(int x, int y, int *xo, int *yo, int dxi, int dyi);

void rotate_coords_inverse(int x, int y, int *xo, int *yo, int dxi, int dyi);

static void set_fs_factor(int max);

static int shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h,
    char *name);

static void create_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void extend_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void save_hint(hint_t hint, int loc);

static void hint_updates(void);

static void mark_hint(hint_t hint);

static int copy_tiles(int tx, int ty, int nt);

static int copy_all_tiles(void);

static int copy_all_tile_runs(void);

static int copy_tiles_backward_pass(void);

static int copy_tiles_additional_pass(void);

static int gap_try(int x, int y, int *run, int *saw, int along_x);

static int fill_tile_gaps(void);

static int island_try(int x, int y, int u, int v, int *run);

static int grow_islands(void);

static void blackout_regions(void);

static void nap_set(int tile_cnt);

static void nap_check(int tile_cnt);

static void ping_clients(int tile_cnt);

static int blackout_line_skip(int n, int x, int y, int rescan,
    int *tile_count);

static int blackout_line_cmpskip(int n, int x, int y, char *dst, char *src,
    int w, int pixelsize);

static int scan_display(int ystart, int rescan);

int nap_ok = 0;

static int nap_diff_count = 0;

static int scan_count = 0;

static int scan_in_progress = 0;

typedef struct tile_change_region {
	/* start and end lines, along y, of the changed area inside a tile. */
	unsigned short first_line, last_line;
	short first_x, last_x;
	/* info about differences along edges. */
	unsigned short left_diff, right_diff;
	unsigned short top_diff,  bot_diff;
} region_t;

static int fs_factor = 0;

#define CEIL(x)  ( (double) ((int) (x)) == (x) ? \
	(double) ((int) (x)) : (double) ((int) (x) + 1) )

#define FLOOR(x) ( (double) ((int) (x)) )


void set_offset(void) {
	Window w;
	if (! subwin) {
		return;
	}
	X_LOCK;
	xtranslate(window, rootwin, 0, 0, &off_x, &off_y, &w, 0);
	X_UNLOCK;
}

void initialize_tiles(void);

void free_tiles(void);

void shm_delete(XShmSegmentInfo *shm);

void shm_clean(XShmSegmentInfo *shm, XImage *xim);

void initialize_polling_images(void);

void scale_rect(double factor_x, double factor_y, int blend, int interpolate, int Bpp,
    char *src_fb, int src_bytes_per_line, char *dst_fb, int dst_bytes_per_line,
    int Nx, int Ny, int nx, int ny, int X1, int Y1, int X2, int Y2, int mark);

void scale_and_mark_rect(int X1, int Y1, int X2, int Y2, int mark);

void mark_rect_as_modified(int x1, int y1, int x2, int y2, int force);

int copy_screen(void);

int copy_snap(void);

void nap_sleep(int ms, int split);

void set_offset(void);

void rotate_curs(char *dst_0, char *src_0, int Dx, int Dy, int Bpp);

void rotate_coords(int x, int y, int *xo, int *yo, int dxi, int dyi);

void rotate_coords_inverse(int x, int y, int *xo, int *yo, int dxi, int dyi);

static void set_fs_factor(int max);

static void create_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void extend_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void save_hint(hint_t hint, int loc);

static void hint_updates(void);

static void mark_hint(hint_t hint);

static int copy_all_tiles(void);

static int copy_all_tile_runs(void);

static int copy_tiles_backward_pass(void);

static int copy_tiles_additional_pass(void);

static int fill_tile_gaps(void);

static int grow_islands(void);

static void blackout_regions(void);

static void nap_set(int tile_cnt);

static void nap_check(int tile_cnt);

static void ping_clients(int tile_cnt);


static void print_tiles(void) {
	/* hack for viewing tile diffs on the screen. */
	static char *prev = NULL;
	int n, x, y, ms = 1500;

	ms = 1;

	if (! prev) {
		prev = (char *) malloc((size_t) ntiles);
		for (n=0; n < ntiles; n++) {
			prev[n] = 0;
		}
	}
	fprintf(stderr, "   ");
	for (x=0; x < ntiles_x; x++) {
		fprintf(stderr, "%1d", x % 10);
	}
	fprintf(stderr, "\n");
	n = 0;
	for (y=0; y < ntiles_y; y++) {
		fprintf(stderr, "%2d ", y);
		for (x=0; x < ntiles_x; x++) {
			if (tile_has_diff[n]) {
				fprintf(stderr, "X");
			} else if (prev[n]) {
				fprintf(stderr, "o");
			} else {
				fprintf(stderr, ".");
			}
			n++;
		}
		fprintf(stderr, "\n");
	}
	for (n=0; n < ntiles; n++) {
		prev[n] = tile_has_diff[n];
	}
	usleep(ms * 1000);
}


static int blackout_line_skip(int n, int x, int y, int rescan,
    int *tile_count) {

	if (tile_blackout[n].cover == 2) {
		tile_has_diff[n] = 0;
		return 1;	/* skip it */

	} else if (tile_blackout[n].cover == 1) {
		int w, x1, y1, x2, y2, b, hit = 0;
		if (x + NSCAN > dpy_x) {
			w = dpy_x - x;
		} else {
			w = NSCAN;
		}

		for (b=0; b < tile_blackout[n].count; b++) {

			/* n.b. these coords are in full display space: */
			x1 = tile_blackout[n].bo[b].x1;
			x2 = tile_blackout[n].bo[b].x2;
			y1 = tile_blackout[n].bo[b].y1;
			y2 = tile_blackout[n].bo[b].y2;

			if (x2 - x1 < w) {
				/* need to cover full width */
				continue;
			}
			if (y1 <= y && y < y2) {
				hit = 1;
				break;
			}
		}
		if (hit) {
			if (! rescan) {
				tile_has_diff[n] = 0;
			} else {
				*tile_count += tile_has_diff[n];
			}
			return 1;	/* skip */
		}
	}
	return 0;	/* do not skip */
}

void scale_rect(double factor_x, double factor_y, int blend, int interpolate, int Bpp,
    char *src_fb, int src_bytes_per_line, char *dst_fb, int dst_bytes_per_line,
    int Nx, int Ny, int nx, int ny, int X1, int Y1, int X2, int Y2, int mark);

void scale_and_mark_rect(int X1, int Y1, int X2, int Y2, int mark);

void mark_rect_as_modified(int x1, int y1, int x2, int y2, int force);

int copy_screen(void);

int copy_snap(void);

void nap_sleep(int ms, int split);

int scan_for_updates(int count_only);

void rotate_curs(char *dst_0, char *src_0, int Dx, int Dy, int Bpp);

void rotate_coords(int x, int y, int *xo, int *yo, int dxi, int dyi);

void rotate_coords_inverse(int x, int y, int *xo, int *yo, int dxi, int dyi);

static void set_fs_factor(int max);

static char *flip_ximage_byte_order(XImage *xim);

static int shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h,
    char *name);

static void create_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void extend_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void save_hint(hint_t hint, int loc);

static void hint_updates(void);

static void mark_hint(hint_t hint);

static int copy_tiles(int tx, int ty, int nt);

static int copy_all_tiles(void);

static int copy_all_tile_runs(void);

static int copy_tiles_backward_pass(void);

static int copy_tiles_additional_pass(void);

static int gap_try(int x, int y, int *run, int *saw, int along_x);

static int fill_tile_gaps(void);

static int island_try(int x, int y, int u, int v, int *run);

static int grow_islands(void);

static void blackout_regions(void);

static void nap_set(int tile_cnt);

static void nap_check(int tile_cnt);

static void ping_clients(int tile_cnt);

static int blackout_line_skip(int n, int x, int y, int rescan,
    int *tile_count);

static int blackout_line_cmpskip(int n, int x, int y, char *dst, char *src,
    int w, int pixelsize);

static int scan_display(int ystart, int rescan);

static hint_t *hint_list;

int nap_ok = 0;

static int nap_diff_count = 0;

static int scan_count = 0;

static int scan_in_progress = 0;

static region_t *tile_region;

static int fs_factor = 0;

#define CEIL(x)  ( (double) ((int) (x)) == (x) ? \
	(double) ((int) (x)) : (double) ((int) (x) + 1) )

#define FLOOR(x) ( (double) ((int) (x)) )


static int copy_tiles(int tx, int ty, int nt) {
	int x, y, line;
	int size_x, size_y, width1, width2;
	int off, len, n, dw, dx, t;
	int w1, w2, dx1, dx2;	/* tmps for normal and short tiles */
	int pixelsize = bpp/8;
	int first_min, last_max;
	int first_x = -1, last_x = -1;
	static int prev_ntiles_x = -1;

	char *src, *dst, *s_src, *s_dst, *m_src, *m_dst;
	char *h_src, *h_dst;
	if (unixpw_in_progress) return 0;

	if (ntiles_x != prev_ntiles_x && first_line != NULL) {
		free(first_line);	first_line = NULL;
		free(last_line);	last_line = NULL;
		free(left_diff);	left_diff = NULL;
		free(right_diff);	right_diff = NULL;
	}

	if (first_line == NULL) {
		/* allocate arrays first time in. */
		int n = ntiles_x + 1;
		rfbLog("copy_tiles: allocating first_line at size %d\n", n);
		first_line = (int *) malloc((size_t) (n * sizeof(int)));
		last_line  = (int *) malloc((size_t) (n * sizeof(int)));
		left_diff  = (unsigned short *)
			malloc((size_t) (n * sizeof(unsigned short)));
		right_diff = (unsigned short *)
			malloc((size_t) (n * sizeof(unsigned short)));
	}
	prev_ntiles_x = ntiles_x;

	x = tx * tile_x;
	y = ty * tile_y;

	size_x = dpy_x - x;
	if ( size_x > tile_x * nt ) {
		size_x = tile_x * nt;
		width1 = tile_x;
		width2 = tile_x;
	} else {
		/* short tile */
		width1 = tile_x;	/* internal tile */
		width2 = size_x - (nt - 1) * tile_x;	/* right hand tile */
	}

	size_y = dpy_y - y;
	if ( size_y > tile_y ) {
		size_y = tile_y;
	}

	n = tx + ty * ntiles_x;		/* number of the first tile */

	if (blackouts && tile_blackout[n].cover == 2) {
		/*
		 * If there are blackouts and this tile is completely covered
		 * no need to poll screen or do anything else..
		 * n.b. we are in single copy_tile mode: nt=1
		 */
		tile_has_diff[n] = 0;
		return(0);
	}

	X_LOCK;
	XRANDR_SET_TRAP_RET(-1, "copy_tile-set");
	/* read in the whole tile run at once: */
	copy_image(tile_row[nt], x, y, size_x, size_y);
	XRANDR_CHK_TRAP_RET(-1, "copy_tile-chk");

	X_UNLOCK;

	if (blackouts && tile_blackout[n].cover == 1) {
		/*
		 * If there are blackouts and this tile is partially covered
		 * we should re-black-out the portion.
		 * n.b. we are in single copy_tile mode: nt=1
		 */
		int x1, x2, y1, y2, b;
		int w, s, fill = 0;

		for (b=0; b < tile_blackout[n].count; b++) {
			char *b_dst = tile_row[nt]->data;

			x1 = tile_blackout[n].bo[b].x1 - x;
			y1 = tile_blackout[n].bo[b].y1 - y;
			x2 = tile_blackout[n].bo[b].x2 - x;
			y2 = tile_blackout[n].bo[b].y2 - y;

			w = (x2 - x1) * pixelsize;
			s = x1 * pixelsize;

			for (line = 0; line < size_y; line++) {
				if (y1 <= line && line < y2) {
					memset(b_dst + s, fill, (size_t) w);
				}
				b_dst += tile_row[nt]->bytes_per_line;
			}
		}
	}

	src = tile_row[nt]->data;
	dst = main_fb + y * main_bytes_per_line + x * pixelsize;

	s_src = src;
	s_dst = dst;

	for (t=1; t <= nt; t++) {
		first_line[t] = -1;
	}

	/* find the first line with difference: */
	w1 = width1 * pixelsize;
	w2 = width2 * pixelsize;

	/* foreach line: */
	for (line = 0; line < size_y; line++) {
		/* foreach horizontal tile: */
		for (t=1; t <= nt; t++) {
			if (first_line[t] != -1) {
				continue;
			}

			off = (t-1) * w1;
			if (t == nt) {
				len = w2;	/* possible short tile */
			} else {
				len = w1;
			}

			if (memcmp(s_dst + off, s_src + off, len)) {
				first_line[t] = line;
			}
		}
		s_src += tile_row[nt]->bytes_per_line;
		s_dst += main_bytes_per_line;
	}

	/* see if there were any differences for any tile: */
	first_min = -1;
	for (t=1; t <= nt; t++) {
		tile_tried[n+(t-1)] = 1;
		if (first_line[t] != -1) {
			if (first_min == -1 || first_line[t] < first_min) {
				first_min = first_line[t];
			}
		}
	}
	if (first_min == -1) {
		/* no tile has a difference, note this and get out: */
		for (t=1; t <= nt; t++) {
			tile_has_diff[n+(t-1)] = 0;
		}
		return(0);
	} else {
		/*
		 * at least one tile has a difference.  make sure info
		 * is recorded (e.g. sometimes we guess tiles and they
		 * came in with tile_has_diff 0)
		 */
		for (t=1; t <= nt; t++) {
			if (first_line[t] == -1) {
				tile_has_diff[n+(t-1)] = 0;
			} else {
				tile_has_diff[n+(t-1)] = 1;
			}
		}
	}

	m_src = src + (tile_row[nt]->bytes_per_line * size_y);
	m_dst = dst + (main_bytes_per_line * size_y);

	for (t=1; t <= nt; t++) {
		last_line[t] = first_line[t];
	}

	/* find the last line with difference: */
	w1 = width1 * pixelsize;
	w2 = width2 * pixelsize;

	/* foreach line: */
	for (line = size_y - 1; line > first_min; line--) {

		m_src -= tile_row[nt]->bytes_per_line;
		m_dst -= main_bytes_per_line;

		/* foreach tile: */
		for (t=1; t <= nt; t++) {
			if (first_line[t] == -1
			    || last_line[t] != first_line[t]) {
				/* tile has no changes or already done */
				continue;
			}

			off = (t-1) * w1;
			if (t == nt) {
				len = w2;	/* possible short tile */
			} else {
				len = w1;
			}
			if (memcmp(m_dst + off, m_src + off, len)) {
				last_line[t] = line;
			}
		}
	}

	/*
	 * determine the farthest down last changed line
	 * will be used below to limit our memcpy() to the framebuffer.
	 */
	last_max = -1;
	for (t=1; t <= nt; t++) {
		if (first_line[t] == -1) {
			continue;
		}
		if (last_max == -1 || last_line[t] > last_max) {
			last_max = last_line[t];
		}
	}

	/* look for differences on left and right hand edges: */
	for (t=1; t <= nt; t++) {
		left_diff[t] = 0;
		right_diff[t] = 0;
	}

	h_src = src;
	h_dst = dst;

	w1 = width1 * pixelsize;
	w2 = width2 * pixelsize;

	dx1 = (width1 - tile_fuzz) * pixelsize;
	dx2 = (width2 - tile_fuzz) * pixelsize;
	dw = tile_fuzz * pixelsize;

	/* foreach line: */
	for (line = 0; line < size_y; line++) {
		/* foreach tile: */
		for (t=1; t <= nt; t++) {
			if (first_line[t] == -1) {
				/* tile has no changes at all */
				continue;
			}

			off = (t-1) * w1;
			if (t == nt) {
				dx = dx2;	/* possible short tile */
				if (dx <= 0) {
					break;
				}
			} else {
				dx = dx1;
			}

			if (! left_diff[t] && memcmp(h_dst + off,
			    h_src + off, dw)) {
				left_diff[t] = 1;
			}
			if (! right_diff[t] && memcmp(h_dst + off + dx,
			    h_src + off + dx, dw) ) {
				right_diff[t] = 1;
			}
		}
		h_src += tile_row[nt]->bytes_per_line;
		h_dst += main_bytes_per_line;
	}

	/* now finally copy the difference to the rfb framebuffer: */
	s_src = src + tile_row[nt]->bytes_per_line * first_min;
	s_dst = dst + main_bytes_per_line * first_min;

	for (line = first_min; line <= last_max; line++) {
		/* for I/O speed we do not do this tile by tile */
		memcpy(s_dst, s_src, size_x * pixelsize);
		if (nt == 1) {
			/*
			 * optimization for tall skinny lines, e.g. wm
			 * frame. try to find first_x and last_x to limit
			 * the size of the hint.  could help for a slow
			 * link.  Unfortunately we spent a lot of time
			 * reading in the many tiles.
			 *
			 * BTW, we like to think the above memcpy leaves
			 * the data we use below in the cache... (but
			 * it could be two 128 byte segments at 32bpp)
			 * so this inner loop is not as bad as it seems.
			 */
			int k, kx;
			kx = pixelsize;
			for (k=0; k<size_x; k++) {
				if (memcmp(s_dst + k*kx, s_src + k*kx, kx))  {
					if (first_x == -1 || k < first_x) {
						first_x = k;
					}
					if (last_x == -1 || k > last_x) {
						last_x = k;
					}
				}
			}
		}
		s_src += tile_row[nt]->bytes_per_line;
		s_dst += main_bytes_per_line;
	}

	/* record all the info in the region array for this tile: */
	for (t=1; t <= nt; t++) {
		int s = t - 1;

		if (first_line[t] == -1) {
			/* tile unchanged */
			continue;
		}
		tile_region[n+s].first_line = first_line[t];
		tile_region[n+s].last_line  = last_line[t];

		tile_region[n+s].first_x = first_x;
		tile_region[n+s].last_x  = last_x;

		tile_region[n+s].top_diff = 0;
		tile_region[n+s].bot_diff = 0;
		if ( first_line[t] < tile_fuzz ) {
			tile_region[n+s].top_diff = 1;
		}
		if ( last_line[t] > (size_y - 1) - tile_fuzz ) {
			tile_region[n+s].bot_diff = 1;
		}

		tile_region[n+s].left_diff  = left_diff[t];
		tile_region[n+s].right_diff = right_diff[t];

		tile_copied[n+s] = 1;
	}

	return(1);
}


static void mark_hint(hint_t hint) {
	int x = hint.x;
	int y = hint.y;
	int w = hint.w;
	int h = hint.h;

	mark_rect_as_modified(x, y, x + w, y + h, 0);
}


static void hint_updates(void) {
	hint_t hint;
	int x, y, i, n, ty, th, tx, tw;
	int hint_count = 0, in_run = 0;

	hint.x = hint.y = hint.w = hint.h = 0;

	for (y=0; y < ntiles_y; y++) {
		for (x=0; x < ntiles_x; x++) {
			n = x + y * ntiles_x;

			if (tile_has_diff[n]) {
				ty = tile_region[n].first_line;
				th = tile_region[n].last_line - ty + 1;

				tx = tile_region[n].first_x;
				tw = tile_region[n].last_x - tx + 1;
				if (tx < 0) {
					tx = 0;
					tw = tile_x;
				}

				if (! in_run) {
					create_tile_hint( x * tile_x + tx,
					    y * tile_y + ty, tw, th, &hint);
					in_run = 1;
				} else {
					extend_tile_hint( x * tile_x + tx,
					    y * tile_y + ty, tw, th, &hint);
				}
			} else {
				if (in_run) {
					/* end of a row run of altered tiles: */
					save_hint(hint, hint_count++);
					in_run = 0;
				}
			}
		}
		if (in_run) {	/* save the last row run */
			save_hint(hint, hint_count++);
			in_run = 0;
		}
	}

	for (i=0; i < hint_count; i++) {
		/* pass update info to vnc: */
		mark_hint(hint_list[i]);
	}
}


static void save_hint(hint_t hint, int loc) {
	/* simply copy it to the global array for later use. */
	hint_list[loc].x = hint.x;
	hint_list[loc].y = hint.y;
	hint_list[loc].w = hint.w;
	hint_list[loc].h = hint.h;
}


void shm_clean(XShmSegmentInfo *shm, XImage *xim) {
	int db = 0;

	if (db) fprintf(stderr, "shm_clean: called:  %p\n", (void *)xim);
	X_LOCK;
#if HAVE_XSHM
	if (shm != NULL && shm->shmid != -1 && dpy) {
		if (db) fprintf(stderr, "shm_clean: XShmDetach_wr\n");
		XShmDetach_wr(dpy, shm);
	}
#endif
	if (xim != NULL) {
		if (! raw_fb_back_to_X) {	/* raw_fb hack */
			if (xim->bitmap_unit != -1) {
				if (db) fprintf(stderr, "shm_clean: XDestroyImage  %p\n", (void *)xim);
				XDestroyImage(xim);
			} else {
				if (xim->data) {
					if (db) fprintf(stderr, "shm_clean: free xim->data  %p %p\n", (void *)xim, (void *)(xim->data));
					free(xim->data);
					xim->data = NULL;
				}
			}
		}
		xim = NULL;
	}
	X_UNLOCK;

	shm_delete(shm);
}


static int island_try(int x, int y, int u, int v, int *run) {
	int n, m, ct;

	n = x + y * ntiles_x;
	m = u + v * ntiles_x;

	if (tile_has_diff[n]) {
		(*run)++;
	} else {
		*run = 0;
	}

	if (tile_has_diff[n] && ! tile_has_diff[m]) {
		/* found a discontinuity */

		if (tile_tried[m]) {
			return 0;
		} else if (*run < grow_fill) {
			return 0;
		}

		ct = copy_tiles(u, v, 1);
		if (ct < 0) return ct;	/* fatal */
	}
	return 1;
}


static void nap_set(int tile_cnt) {
	int nap_in = nap_ok;
	time_t now = time(NULL);

	if (scan_count == 0) {
		/* roll up check for all NSCAN scans */
		nap_ok = 0;
		if (naptile && nap_diff_count < 2 * NSCAN * naptile) {
			/* "2" is a fudge to permit a bit of bg drawing */
			nap_ok = 1;
		}
		nap_diff_count = 0;
	}
	if (nap_ok && ! nap_in && use_xdamage) {
		if (XD_skip > 0.8 * XD_tot) 	{
			/* X DAMAGE is keeping load low, so skip nap */
			nap_ok = 0;
		}
	}
	if (! nap_ok && client_count) {
		if(now > last_fb_bytes_sent + no_fbu_blank) {
			if (debug_tiles > 1) {
				fprintf(stderr, "nap_set: nap_ok=1: now: %d last: %d\n",
				    (int) now, (int) last_fb_bytes_sent);
			}
			nap_ok = 1;
		}
	}

	if (show_cursor) {
		/* kludge for the up to 4 tiles the mouse patch could occupy */
		if ( tile_cnt > 4) {
			last_event = now;
		}
	} else if (tile_cnt != 0) {
		last_event = now;
	}
}


void initialize_tiles(void) {

	ntiles_x = (dpy_x - 1)/tile_x + 1;
	ntiles_y = (dpy_y - 1)/tile_y + 1;
	ntiles = ntiles_x * ntiles_y;

	tile_has_diff = (unsigned char *)
		calloc((size_t) (ntiles * sizeof(unsigned char)), 1);
	tile_has_xdamage_diff = (unsigned char *)
		calloc((size_t) (ntiles * sizeof(unsigned char)), 1);
	tile_row_has_xdamage_diff = (unsigned char *)
		calloc((size_t) (ntiles_y * sizeof(unsigned char)), 1);
	tile_tried    = (unsigned char *)
		calloc((size_t) (ntiles * sizeof(unsigned char)), 1);
	tile_copied   = (unsigned char *)
		calloc((size_t) (ntiles * sizeof(unsigned char)), 1);
	tile_blackout    = (tile_blackout_t *)
		calloc((size_t) (ntiles * sizeof(tile_blackout_t)), 1);
	tile_region = (region_t *) calloc((size_t) (ntiles * sizeof(region_t)), 1);

	tile_row = (XImage **)
		calloc((size_t) ((ntiles_x + 1) * sizeof(XImage *)), 1);
	tile_row_shm = (XShmSegmentInfo *)
		calloc((size_t) ((ntiles_x + 1) * sizeof(XShmSegmentInfo)), 1);

	/* there will never be more hints than tiles: */
	hint_list = (hint_t *) calloc((size_t) (ntiles * sizeof(hint_t)), 1);
}

void initialize_tiles(void);

void free_tiles(void);

void shm_delete(XShmSegmentInfo *shm);

void shm_clean(XShmSegmentInfo *shm, XImage *xim);

void initialize_polling_images(void);

void scale_rect(double factor_x, double factor_y, int blend, int interpolate, int Bpp,
    char *src_fb, int src_bytes_per_line, char *dst_fb, int dst_bytes_per_line,
    int Nx, int Ny, int nx, int ny, int X1, int Y1, int X2, int Y2, int mark);

void scale_and_mark_rect(int X1, int Y1, int X2, int Y2, int mark);

void mark_rect_as_modified(int x1, int y1, int x2, int y2, int force);

int copy_screen(void);

int copy_snap(void);

void nap_sleep(int ms, int split);

void set_offset(void);

void rotate_curs(char *dst_0, char *src_0, int Dx, int Dy, int Bpp);

void rotate_coords(int x, int y, int *xo, int *yo, int dxi, int dyi);

void rotate_coords_inverse(int x, int y, int *xo, int *yo, int dxi, int dyi);

static void set_fs_factor(int max);

static char *flip_ximage_byte_order(XImage *xim);

static int shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h,
    char *name);

static void create_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void extend_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void save_hint(hint_t hint, int loc);

static void hint_updates(void);

static void mark_hint(hint_t hint);

static int copy_all_tiles(void);

static int copy_all_tile_runs(void);

static int copy_tiles_backward_pass(void);

static int copy_tiles_additional_pass(void);

static int fill_tile_gaps(void);

static int grow_islands(void);

static void blackout_regions(void);

static void nap_set(int tile_cnt);

static void nap_check(int tile_cnt);

static void ping_clients(int tile_cnt);

static int blackout_line_cmpskip(int n, int x, int y, char *dst, char *src,
    int w, int pixelsize);

static hint_t *hint_list;

typedef struct tile_change_region {
	/* start and end lines, along y, of the changed area inside a tile. */
	unsigned short first_line, last_line;
	short first_x, last_x;
	/* info about differences along edges. */
	unsigned short left_diff, right_diff;
	unsigned short top_diff,  bot_diff;
} region_t;

static region_t *tile_region;

static unsigned short *left_diff = NULL, *right_diff = NULL;


void mark_wrapper(int x1, int y1, int x2, int y2) {
	int t, r_x1 = x1, r_y1 = y1, r_x2 = x2, r_y2 = y2;

	if (rotating) {
		/* well we hope rot_fb will always be the last one... */
		rotate_coords(x1, y1, &r_x1, &r_y1, -1, -1);
		rotate_coords(x2, y2, &r_x2, &r_y2, -1, -1);
		rotate_fb(x1, y1, x2, y2);
		if (r_x1 > r_x2) {
			t = r_x1;
			r_x1 = r_x2;
			r_x2 = t;
		}
		if (r_y1 > r_y2) {
			t = r_y1;
			r_y1 = r_y2;
			r_y2 = t;
		}
		/* painting errors  */
		r_x1--;
		r_x2++;
		r_y1--;
		r_y2++;
	}
	rfbMarkRectAsModified(screen, r_x1, r_y1, r_x2, r_y2);
}

void initialize_tiles(void);

void free_tiles(void);

void shm_delete(XShmSegmentInfo *shm);

void shm_clean(XShmSegmentInfo *shm, XImage *xim);

void initialize_polling_images(void);

void scale_rect(double factor_x, double factor_y, int blend, int interpolate, int Bpp,
    char *src_fb, int src_bytes_per_line, char *dst_fb, int dst_bytes_per_line,
    int Nx, int Ny, int nx, int ny, int X1, int Y1, int X2, int Y2, int mark);

void scale_and_mark_rect(int X1, int Y1, int X2, int Y2, int mark);

void mark_rect_as_modified(int x1, int y1, int x2, int y2, int force);

int copy_screen(void);

int copy_snap(void);

void nap_sleep(int ms, int split);

void set_offset(void);

int scan_for_updates(int count_only);

void rotate_curs(char *dst_0, char *src_0, int Dx, int Dy, int Bpp);

void rotate_coords(int x, int y, int *xo, int *yo, int dxi, int dyi);

void rotate_coords_inverse(int x, int y, int *xo, int *yo, int dxi, int dyi);

static void set_fs_factor(int max);

static int shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h,
    char *name);

static void create_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void extend_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void save_hint(hint_t hint, int loc);

static void hint_updates(void);

static void mark_hint(hint_t hint);

static int copy_tiles(int tx, int ty, int nt);

static int copy_all_tiles(void);

static int copy_all_tile_runs(void);

static int copy_tiles_backward_pass(void);

static int copy_tiles_additional_pass(void);

static int gap_try(int x, int y, int *run, int *saw, int along_x);

static int fill_tile_gaps(void);

static int island_try(int x, int y, int u, int v, int *run);

static int grow_islands(void);

static void blackout_regions(void);

static void nap_set(int tile_cnt);

static void nap_check(int tile_cnt);

static void ping_clients(int tile_cnt);

static int blackout_line_skip(int n, int x, int y, int rescan,
    int *tile_count);

static int blackout_line_cmpskip(int n, int x, int y, char *dst, char *src,
    int w, int pixelsize);

static int scan_display(int ystart, int rescan);

int nap_ok = 0;

static int nap_diff_count = 0;

static int scan_count = 0;

static int scan_in_progress = 0;

typedef struct tile_change_region {
	/* start and end lines, along y, of the changed area inside a tile. */
	unsigned short first_line, last_line;
	short first_x, last_x;
	/* info about differences along edges. */
	unsigned short left_diff, right_diff;
	unsigned short top_diff,  bot_diff;
} region_t;

static int fs_factor = 0;

#define CEIL(x)  ( (double) ((int) (x)) == (x) ? \
	(double) ((int) (x)) : (double) ((int) (x) + 1) )

#define FLOOR(x) ( (double) ((int) (x)) )

static int *first_line = NULL, *last_line = NULL;

static int xd_samples = 0, xd_misses = 0, xd_do_check = 0;


static void blackout_regions(void) {
	int i;
	for (i=0; i < blackouts; i++) {
		zero_fb(blackr[i].x1, blackr[i].y1, blackr[i].x2, blackr[i].y2);
	}
}


static void create_tile_hint(int x, int y, int tw, int th, hint_t *hint) {
	int w = dpy_x - x;
	int h = dpy_y - y;

	if (w > tw) {
		w = tw;
	}
	if (h > th) {
		h = th;
	}

	hint->x = x;
	hint->y = y;
	hint->w = w;
	hint->h = h;
}


void nap_sleep(int ms, int split) {
	int i, input = got_user_input;
	int gd = got_local_pointer_input;

	for (i=0; i<split; i++) {
		usleep(ms * 1000 / split);
		if (! use_threads && i != split - 1) {
			rfbPE(-1);
		}
		if (input != got_user_input) {
			break;
		}
		if (gd != got_local_pointer_input) {
			break;
		}
	}
}

void initialize_tiles(void);

void free_tiles(void);

void shm_delete(XShmSegmentInfo *shm);

void shm_clean(XShmSegmentInfo *shm, XImage *xim);

void initialize_polling_images(void);

void scale_rect(double factor_x, double factor_y, int blend, int interpolate, int Bpp,
    char *src_fb, int src_bytes_per_line, char *dst_fb, int dst_bytes_per_line,
    int Nx, int Ny, int nx, int ny, int X1, int Y1, int X2, int Y2, int mark);

void scale_and_mark_rect(int X1, int Y1, int X2, int Y2, int mark);

void mark_rect_as_modified(int x1, int y1, int x2, int y2, int force);

int copy_screen(void);

int copy_snap(void);

void nap_sleep(int ms, int split);

void set_offset(void);

int scan_for_updates(int count_only);

void rotate_curs(char *dst_0, char *src_0, int Dx, int Dy, int Bpp);

void rotate_coords(int x, int y, int *xo, int *yo, int dxi, int dyi);

void rotate_coords_inverse(int x, int y, int *xo, int *yo, int dxi, int dyi);

static void set_fs_factor(int max);

static int shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h,
    char *name);

static void create_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void extend_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void save_hint(hint_t hint, int loc);

static void hint_updates(void);

static void mark_hint(hint_t hint);

static int copy_tiles(int tx, int ty, int nt);

static int copy_all_tiles(void);

static int copy_all_tile_runs(void);

static int copy_tiles_backward_pass(void);

static int copy_tiles_additional_pass(void);

static int gap_try(int x, int y, int *run, int *saw, int along_x);

static int fill_tile_gaps(void);

static int island_try(int x, int y, int u, int v, int *run);

static int grow_islands(void);

static void blackout_regions(void);

static void nap_set(int tile_cnt);

static void nap_check(int tile_cnt);

static void ping_clients(int tile_cnt);

static int blackout_line_skip(int n, int x, int y, int rescan,
    int *tile_count);

static int blackout_line_cmpskip(int n, int x, int y, char *dst, char *src,
    int w, int pixelsize);

static int scan_display(int ystart, int rescan);

int nap_ok = 0;

static int nap_diff_count = 0;

static int scan_count = 0;

static int scan_in_progress = 0;

static int fs_factor = 0;

#define CEIL(x)  ( (double) ((int) (x)) == (x) ? \
	(double) ((int) (x)) : (double) ((int) (x) + 1) )

#define FLOOR(x) ( (double) ((int) (x)) )

static int *first_line = NULL, *last_line = NULL;

static int xd_samples = 0, xd_misses = 0, xd_do_check = 0;


static int copy_tiles_backward_pass(void) {
	int x, y, n, m;
	int diffs = 0, ct;

	if (unixpw_in_progress) return 0;

	for (y = ntiles_y - 1; y >= 0; y--) {
	    for (x = ntiles_x - 1; x >= 0; x--) {
		n = x + y * ntiles_x;		/* number of this tile */

		if (! tile_has_diff[n]) {
			continue;
		}

		m = x + (y-1) * ntiles_x;	/* neighboring tile upward */

		if (y >= 1 && ! tile_has_diff[m] && tile_region[n].top_diff) {
			if (! tile_tried[m]) {
				tile_has_diff[m] = 2;
				ct = copy_tiles(x, y-1, 1);
				if (ct < 0) return ct;	/* fatal */
			}
		}

		m = (x-1) + y * ntiles_x;	/* neighboring tile to left */

		if (x >= 1 && ! tile_has_diff[m] && tile_region[n].left_diff) {
			if (! tile_tried[m]) {
				tile_has_diff[m] = 2;
				ct = copy_tiles(x-1, y, 1);
				if (ct < 0) return ct;	/* fatal */
			}
		}
	    }
	}
	for (n=0; n < ntiles; n++) {
		if (tile_has_diff[n]) {
			diffs++;
		}
	}
	return diffs;
}

void initialize_tiles(void);

void free_tiles(void);

void shm_delete(XShmSegmentInfo *shm);

void shm_clean(XShmSegmentInfo *shm, XImage *xim);

void initialize_polling_images(void);

void scale_rect(double factor_x, double factor_y, int blend, int interpolate, int Bpp,
    char *src_fb, int src_bytes_per_line, char *dst_fb, int dst_bytes_per_line,
    int Nx, int Ny, int nx, int ny, int X1, int Y1, int X2, int Y2, int mark);

void scale_and_mark_rect(int X1, int Y1, int X2, int Y2, int mark);

void mark_rect_as_modified(int x1, int y1, int x2, int y2, int force);

int copy_screen(void);

int copy_snap(void);

void nap_sleep(int ms, int split);

void set_offset(void);

int scan_for_updates(int count_only);

void rotate_curs(char *dst_0, char *src_0, int Dx, int Dy, int Bpp);

void rotate_coords(int x, int y, int *xo, int *yo, int dxi, int dyi);

void rotate_coords_inverse(int x, int y, int *xo, int *yo, int dxi, int dyi);

static void set_fs_factor(int max);

static char *flip_ximage_byte_order(XImage *xim);

static int shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h,
    char *name);

static void create_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void extend_tile_hint(int x, int y, int tw, int th, hint_t *hint);

static void save_hint(hint_t hint, int loc);

static void hint_updates(void);

static void mark_hint(hint_t hint);

static int copy_tiles(int tx, int ty, int nt);

static int copy_all_tiles(void);

static int copy_all_tile_runs(void);

static int copy_tiles_backward_pass(void);

static int copy_tiles_additional_pass(void);

static int gap_try(int x, int y, int *run, int *saw, int along_x);

static int fill_tile_gaps(void);

static int island_try(int x, int y, int u, int v, int *run);

static int grow_islands(void);

static void blackout_regions(void);

static void nap_set(int tile_cnt);

static void nap_check(int tile_cnt);

static void ping_clients(int tile_cnt);

static int blackout_line_skip(int n, int x, int y, int rescan,
    int *tile_count);

static int blackout_line_cmpskip(int n, int x, int y, char *dst, char *src,
    int w, int pixelsize);

static int scan_display(int ystart, int rescan);

static hint_t *hint_list;

int nap_ok = 0;

static int nap_diff_count = 0;

static int scan_count = 0;

static int scan_in_progress = 0;

typedef struct tile_change_region {
	/* start and end lines, along y, of the changed area inside a tile. */
	unsigned short first_line, last_line;
	short first_x, last_x;
	/* info about differences along edges. */
	unsigned short left_diff, right_diff;
	unsigned short top_diff,  bot_diff;
} region_t;

static region_t *tile_region;

static int fs_factor = 0;

#define CEIL(x)  ( (double) ((int) (x)) == (x) ? \
	(double) ((int) (x)) : (double) ((int) (x) + 1) )

#define FLOOR(x) ( (double) ((int) (x)) )


void msetCommand(client *c) {
    msetGenericCommand(c,0);
}

Fix integer overflow in STRALGO LCS (CVE-2021-29477)

An integer overflow bug in Redis version 6.0 or newer could be exploited using
the STRALGO LCS command to corrupt the heap and potentially result with remote
code execution.

(cherry picked from commit f0c5f920d0f88bd8aa376a2c05af4902789d1ef9)

int getGenericCommand(client *c);

void stralgoLCS(client *c);


void setCommand(client *c) {
    int j;
    robj *expire = NULL;
    int unit = UNIT_SECONDS;
    int flags = OBJ_SET_NO_FLAGS;

    for (j = 3; j < c->argc; j++) {
        char *a = c->argv[j]->ptr;
        robj *next = (j == c->argc-1) ? NULL : c->argv[j+1];

        if ((a[0] == 'n' || a[0] == 'N') &&
            (a[1] == 'x' || a[1] == 'X') && a[2] == '\0' &&
            !(flags & OBJ_SET_XX))
        {
            flags |= OBJ_SET_NX;
        } else if ((a[0] == 'x' || a[0] == 'X') &&
                   (a[1] == 'x' || a[1] == 'X') && a[2] == '\0' &&
                   !(flags & OBJ_SET_NX))
        {
            flags |= OBJ_SET_XX;
        } else if (!strcasecmp(c->argv[j]->ptr,"KEEPTTL") &&
                   !(flags & OBJ_SET_EX) && !(flags & OBJ_SET_PX))
        {
            flags |= OBJ_SET_KEEPTTL;
        } else if ((a[0] == 'e' || a[0] == 'E') &&
                   (a[1] == 'x' || a[1] == 'X') && a[2] == '\0' &&
                   !(flags & OBJ_SET_KEEPTTL) &&
                   !(flags & OBJ_SET_PX) && next)
        {
            flags |= OBJ_SET_EX;
            unit = UNIT_SECONDS;
            expire = next;
            j++;
        } else if ((a[0] == 'p' || a[0] == 'P') &&
                   (a[1] == 'x' || a[1] == 'X') && a[2] == '\0' &&
                   !(flags & OBJ_SET_KEEPTTL) &&
                   !(flags & OBJ_SET_EX) && next)
        {
            flags |= OBJ_SET_PX;
            unit = UNIT_MILLISECONDS;
            expire = next;
            j++;
        } else {
            addReply(c,shared.syntaxerr);
            return;
        }
    }

    c->argv[2] = tryObjectEncoding(c->argv[2]);
    setGenericCommand(c,flags,c->argv[1],c->argv[2],expire,unit,NULL,NULL);
}

#define OBJ_SET_NO_FLAGS 0

#define OBJ_SET_NX (1<<0)          /* Set if key not exists. */

#define OBJ_SET_XX (1<<1)          /* Set if key exists. */

#define OBJ_SET_EX (1<<2)          /* Set if time in seconds is given */

#define OBJ_SET_PX (1<<3)          /* Set if time in ms in given */

#define OBJ_SET_KEEPTTL (1<<4)     /* Set and keep the ttl */

void stralgoLCS(client *c);


void decrbyCommand(client *c) {
    long long incr;

    if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != C_OK) return;
    incrDecrCommand(c,-incr);
}


void incrbyCommand(client *c) {
    long long incr;

    if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != C_OK) return;
    incrDecrCommand(c,incr);
}


void incrDecrCommand(client *c, long long incr) {
    long long value, oldvalue;
    robj *o, *new;

    o = lookupKeyWrite(c->db,c->argv[1]);
    if (o != NULL && checkType(c,o,OBJ_STRING)) return;
    if (getLongLongFromObjectOrReply(c,o,&value,NULL) != C_OK) return;

    oldvalue = value;
    if ((incr < 0 && oldvalue < 0 && incr < (LLONG_MIN-oldvalue)) ||
        (incr > 0 && oldvalue > 0 && incr > (LLONG_MAX-oldvalue))) {
        addReplyError(c,"increment or decrement would overflow");
        return;
    }
    value += incr;

    if (o && o->refcount == 1 && o->encoding == OBJ_ENCODING_INT &&
        (value < 0 || value >= OBJ_SHARED_INTEGERS) &&
        value >= LONG_MIN && value <= LONG_MAX)
    {
        new = o;
        o->ptr = (void*)((long)value);
    } else {
        new = createStringObjectFromLongLongForValue(value);
        if (o) {
            dbOverwrite(c->db,c->argv[1],new);
        } else {
            dbAdd(c->db,c->argv[1],new);
        }
    }
    signalModifiedKey(c,c->db,c->argv[1]);
    notifyKeyspaceEvent(NOTIFY_STRING,"incrby",c->argv[1],c->db->id);
    server.dirty++;
    addReply(c,shared.colon);
    addReply(c,new);
    addReply(c,shared.crlf);
}


void msetGenericCommand(client *c, int nx) {
    int j;

    if ((c->argc % 2) == 0) {
        addReplyError(c,"wrong number of arguments for MSET");
        return;
    }

    /* Handle the NX flag. The MSETNX semantic is to return zero and don't
     * set anything if at least one key already exists. */
    if (nx) {
        for (j = 1; j < c->argc; j += 2) {
            if (lookupKeyWrite(c->db,c->argv[j]) != NULL) {
                addReply(c, shared.czero);
                return;
            }
        }
    }

    for (j = 1; j < c->argc; j += 2) {
        c->argv[j+1] = tryObjectEncoding(c->argv[j+1]);
        setKey(c,c->db,c->argv[j],c->argv[j+1]);
        notifyKeyspaceEvent(NOTIFY_STRING,"set",c->argv[j],c->db->id);
    }
    server.dirty += (c->argc-1)/2;
    addReply(c, nx ? shared.cone : shared.ok);
}

int getGenericCommand(client *c);

#define OBJ_SET_NX (1<<0)          /* Set if key not exists. */

#define OBJ_SET_XX (1<<1)          /* Set if key exists. */

#define OBJ_KEEPTTL (1<<4)         /* Set and keep the ttl */

#define OBJ_SET_GET (1<<5)         /* Set if want to get key before set */

#define OBJ_PERSIST (1<<8)         /* Set if we need to remove the ttl */

void stralgoLCS(client *c);


void incrCommand(client *c) {
    incrDecrCommand(c,1);
}


void strlenCommand(client *c) {
    robj *o;
    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
        checkType(c,o,OBJ_STRING)) return;
    addReplyLongLong(c,stringObjectLen(o));
}


void getsetCommand(client *c) {
    if (getGenericCommand(c) == C_ERR) return;
    c->argv[2] = tryObjectEncoding(c->argv[2]);
    setKey(c,c->db,c->argv[1],c->argv[2]);
    notifyKeyspaceEvent(NOTIFY_STRING,"set",c->argv[1],c->db->id);
    server.dirty++;
}


static int checkStringLength(client *c, long long size) {
    if (!(c->flags & CLIENT_MASTER) && size > server.proto_max_bulk_len) {
        addReplyError(c,"string exceeds maximum allowed size (proto-max-bulk-len)");
        return C_ERR;
    }
    return C_OK;
}


void stralgoCommand(client *c) {
    /* Select the algorithm. */
    if (!strcasecmp(c->argv[1]->ptr,"lcs")) {
        stralgoLCS(c);
    } else {
        addReply(c,shared.syntaxerr);
    }
}

#define OBJ_SET_KEEPTTL (1<<4)     /* Set and keep the ttl */

void stralgoLCS(client *c);


int getGenericCommand(client *c) {
    robj *o;

    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.null[c->resp])) == NULL)
        return C_OK;

    if (o->type != OBJ_STRING) {
        addReply(c,shared.wrongtypeerr);
        return C_ERR;
    } else {
        addReplyBulk(c,o);
        return C_OK;
    }
}