/*
   Copyright (C) 2002-2009 Karl J. Runge <runge@karlrunge.com> 
   All rights reserved.

This file is part of x11vnc.

x11vnc is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at
your option) any later version.

x11vnc is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with x11vnc; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
or see <http://www.gnu.org/licenses/>.

In addition, as a special exception, Karl J. Runge
gives permission to link the code of its release of x11vnc with the
OpenSSL project's "OpenSSL" library (or with modified versions of it
that use the same license as the "OpenSSL" library), and distribute
the linked executables.  You must obey the GNU General Public License
in all respects for all of the code used other than "OpenSSL".  If you
modify this file, you may extend this exception to your version of the
file, but you are not obligated to do so.  If you do not wish to do
so, delete this exception statement from your version.
*/

/* -- screen.c -- */

#include "x11vnc.h"
#include "xevents.h"
#include "xwrappers.h"
#include "xinerama.h"
#include "xdamage.h"
#include "win_utils.h"
#include "cleanup.h"
#include "userinput.h"
#include "scan.h"
#include "user.h"
#include "rates.h"
#include "pointer.h"
#include "keyboard.h"
#include "cursor.h"
#include "connections.h"
#include "remote.h"
#include "unixpw.h"
#include "sslcmds.h"
#include "sslhelper.h"
#include "v4l.h"
#include "linuxfb.h"
#include "macosx.h"
#include "macosxCG.h"
#include "avahi.h"
#include "solid.h"
#include "inet.h"
#include "xrandr.h"
#include "xrecord.h"
#include "pm.h"

#include <rfb/rfbclient.h>

void set_greyscale_colormap(void);
void set_hi240_colormap(void);
void unset_colormap(void);
void set_colormap(int reset);
void set_nofb_params(int restore);
void set_raw_fb_params(int restore);
void do_new_fb(int reset_mem);
void free_old_fb(void);
void check_padded_fb(void);
void install_padded_fb(char *geom);
XImage *initialize_xdisplay_fb(void);
void parse_scale_string(char *str, double *factor_x, double *factor_y, int *scaling, int *blend,
    int *nomult4, int *pad, int *interpolate, int *numer, int *denom, int w_in, int h_in);
int parse_rotate_string(char *str, int *mode);
int scale_round(int len, double fac);
void initialize_screen(int *argc, char **argv, XImage *fb);
void set_vnc_desktop_name(void);
void announce(int lport, int ssl, char *iface);

char *vnc_reflect_guess(char *str, char **raw_fb_addr);
void vnc_reflect_process_client(void);
rfbBool vnc_reflect_send_pointer(int x, int y, int mask);
rfbBool vnc_reflect_send_key(uint32_t key, rfbBool down);
rfbBool vnc_reflect_send_cuttext(char *str, int len);

static void debug_colormap(XImage *fb);
static void set_visual(char *str);
static void nofb_hook(rfbClientPtr cl);
static void remove_fake_fb(void);
static void install_fake_fb(int w, int h, int bpp);
static void initialize_snap_fb(void);
XImage *initialize_raw_fb(int);
static void initialize_clipshift(void);
static int wait_until_mapped(Window win);
static void setup_scaling(int *width_in, int *height_in);

static void check_filexfer(void);
static void record_last_fb_update(void);
static void check_cursor_changes(void);
static int choose_delay(double dt);

int rawfb_reset = -1;
int rawfb_dev_video = 0;
int rawfb_vnc_reflect = 0;

/*
 * X11 and rfb display/screen related routines
 */

/*
 * Some handling of 8bpp PseudoColor colormaps.  Called for initializing
 * the clients and dynamically if -flashcmap is specified.
 */
#define NCOLOR 256

/* this is only for rawfb */
void set_greyscale_colormap(void) {
	int i;
	if (! screen) {
		return;
	}
	if (screen->colourMap.data.shorts) {
		free(screen->colourMap.data.shorts);
		screen->colourMap.data.shorts = NULL;
	}

if (0) fprintf(stderr, "set_greyscale_colormap: %s\n", raw_fb_pixfmt);
	screen->colourMap.count = NCOLOR;
	screen->serverFormat.trueColour = FALSE;
	screen->colourMap.is16 = TRUE;
	screen->colourMap.data.shorts = (unsigned short *)
		malloc(3*sizeof(unsigned short) * NCOLOR);

	for(i = 0; i < NCOLOR; i++) {
		unsigned short lvl = i * 256;

		screen->colourMap.data.shorts[i*3+0] = lvl;
		screen->colourMap.data.shorts[i*3+1] = lvl;
		screen->colourMap.data.shorts[i*3+2] = lvl;
	}

	rfbSetClientColourMaps(screen, 0, NCOLOR);
}

/* this is specific to bttv rf tuner card */
void set_hi240_colormap(void) {
	int i;
	if (! screen) {
		return;
	}
if (0) fprintf(stderr, "set_hi240_colormap: %s\n", raw_fb_pixfmt);
	if (screen->colourMap.data.shorts) {
		free(screen->colourMap.data.shorts);
		screen->colourMap.data.shorts = NULL;
	}

	screen->colourMap.count = 256;
	screen->serverFormat.trueColour = FALSE;
	screen->colourMap.is16 = TRUE;
	screen->colourMap.data.shorts = (unsigned short *)
		calloc(3*sizeof(unsigned short) * 256, 1);

	for(i = 0; i < 225; i++) {
		int r, g, b;

		r = ( (i/5) % 5 ) * 255.0 / 4 + 0.5;
		g = ( (i/25)    ) * 255.0 / 8 + 0.5;
		b = ( i % 5     ) * 255.0 / 4 + 0.5;

		screen->colourMap.data.shorts[(i+16)*3+0] = (unsigned short) (r * 256);
		screen->colourMap.data.shorts[(i+16)*3+1] = (unsigned short) (g * 256);
		screen->colourMap.data.shorts[(i+16)*3+2] = (unsigned short) (b * 256);
	}

	rfbSetClientColourMaps(screen, 0, 256);
}

/* this is only for rawfb */
void unset_colormap(void) {
	if (! screen) {
		return;
	}
	if (screen->colourMap.data.shorts) {
		free(screen->colourMap.data.shorts);
		screen->colourMap.data.shorts = NULL;
	}
	screen->serverFormat.trueColour = TRUE;
if (0) fprintf(stderr, "unset_colormap: %s\n", raw_fb_pixfmt);
}

/* this is X11 case */
void set_colormap(int reset) {

#if NO_X11
	if (!reset) {}
	return;
#else
	static int init = 1;
	static XColor *color = NULL, *prev = NULL;
	static int ncolor = 0;
	Colormap cmap;
	Visual *vis;
	int i, ncells, diffs = 0;

	if (reset) {
		init = 1;
		ncolor = 0;
		if (screen->colourMap.data.shorts) {
			free(screen->colourMap.data.shorts);
			screen->colourMap.data.shorts = NULL;
		}
		if (color) {
			free(color);
			color = NULL;
		}
		if (prev) {
			free(prev);
			prev = NULL;
		}
	}

	if (init) {
		if (depth > 16) {
			ncolor = NCOLOR;
		} else if (depth > 8) {
			ncolor = 1 << depth;
		} else {
			ncolor = NCOLOR;
		}
		screen->colourMap.count = ncolor;
		screen->serverFormat.trueColour = FALSE;
		screen->colourMap.is16 = TRUE;
		screen->colourMap.data.shorts = (unsigned short *)
			malloc(3*sizeof(unsigned short) * ncolor);
	}
	if (color == NULL) {
		color = (XColor *) calloc(ncolor * sizeof(XColor), 1);
		prev  = (XColor *) calloc(ncolor * sizeof(XColor), 1);
	}

	for (i=0; i < ncolor; i++) {
		prev[i].red   = color[i].red;
		prev[i].green = color[i].green;
		prev[i].blue  = color[i].blue;
	}

	RAWFB_RET_VOID

	X_LOCK;

	cmap = DefaultColormap(dpy, scr);
	ncells = CellsOfScreen(ScreenOfDisplay(dpy, scr));
	vis = default_visual;

	if (subwin) {
		XWindowAttributes attr;

		if (XGetWindowAttributes(dpy, window, &attr)) {
			cmap = attr.colormap;
			vis = attr.visual;
			ncells = vis->map_entries;
		}
	}

	if (ncells != ncolor) {
		if (! shift_cmap) {
			screen->colourMap.count = ncells;
		}
	}
	if (init && ! quiet) {
		rfbLog("set_colormap: number of cells: %d, "
		    "ncolor(%d) is %d.\n", ncells, depth, ncolor);
	}

	if (flash_cmap && ! init) {
		XWindowAttributes attr;
		Window c;
		int tries = 0;

		c = window;
		while (c && tries++ < 16) {
			c = query_pointer(c);
			if (valid_window(c, &attr, 0)) {
				if (attr.colormap && attr.map_installed) {
					cmap = attr.colormap;
					vis = attr.visual;
					ncells = vis->map_entries;
					break;
				}
			} else {
				break;
			}
		}
	}
	if (ncells > ncolor && ! quiet) {
		rfbLog("set_colormap: big problem: ncells=%d > %d\n",
		    ncells, ncolor);
	}

	if (vis->class == TrueColor || vis->class == DirectColor) {
		/*
		 * Kludge to make 8bpp TrueColor & DirectColor be like
		 * the StaticColor map.  The ncells = 8 is "8 per subfield"
		 * mentioned in xdpyinfo.  Looks OK... perhaps fortuitously.
		 */
		if (ncells == 8 && ! shift_cmap) {
			ncells = ncolor;
		}
	}

	for (i=0; i < ncells; i++) {
		color[i].pixel = i;
		color[i].pad = 0;
	}

	XQueryColors(dpy, cmap, color, ncells);

	X_UNLOCK;

	for(i = ncells - 1; i >= 0; i--) {
		int k = i + shift_cmap;

		screen->colourMap.data.shorts[i*3+0] = color[i].red;
		screen->colourMap.data.shorts[i*3+1] = color[i].green;
		screen->colourMap.data.shorts[i*3+2] = color[i].blue;

		if (prev[i].red   != color[i].red ||
		    prev[i].green != color[i].green || 
		    prev[i].blue  != color[i].blue ) {
			diffs++;
		}

		if (shift_cmap && k >= 0 && k < ncolor) {
			/* kludge to copy the colors to higher pixel values */
			screen->colourMap.data.shorts[k*3+0] = color[i].red;
			screen->colourMap.data.shorts[k*3+1] = color[i].green;
			screen->colourMap.data.shorts[k*3+2] = color[i].blue;
		}
	}

	if (diffs && ! init) {
		if (! all_clients_initialized()) {
			rfbLog("set_colormap: warning: sending cmap "
			    "with uninitialized clients.\n");
		}
		if (shift_cmap) {
			rfbSetClientColourMaps(screen, 0, ncolor);
		} else {
			rfbSetClientColourMaps(screen, 0, ncells);
		}
	}

	init = 0;
#endif	/* NO_X11 */
}

static void debug_colormap(XImage *fb) {
	static int debug_cmap = -1;
	int i, k, *histo;
	int ncolor;

	if (debug_cmap < 0) {
		if (getenv("DEBUG_CMAP") != NULL) {
			debug_cmap = 1;
		} else {
			debug_cmap = 0;
		}
	}
	if (! debug_cmap) {
		return;
	}
	if (! fb) {
		return;
	}
	if (fb->bits_per_pixel > 16) {
		return;
	}
	ncolor = screen->colourMap.count;
	histo = (int *) calloc(ncolor * sizeof(int), 1);

	for (i=0; i < ncolor; i++) {
		histo[i] = 0;
	}
	for (k = 0; k < fb->width * fb->height; k++) {
		unsigned char n;
		char c = *(fb->data + k);

		n = (unsigned char) c;
		histo[n]++;
	}
	fprintf(stderr, "\nColormap histogram for current screen contents:\n");
	for (i=0; i < ncolor; i++) {
		unsigned short r = screen->colourMap.data.shorts[i*3+0];
		unsigned short g = screen->colourMap.data.shorts[i*3+1];
		unsigned short b = screen->colourMap.data.shorts[i*3+2];

		fprintf(stderr, "   %03d: %7d %04x/%04x/%04x", i, histo[i],
		    r, g, b);
		if ((i+1) % 2 == 0)  {
			fprintf(stderr, "\n");
		}
	}
	free(histo);
	fprintf(stderr, "\n");
}

/*
 * Experimental mode to force the visual of the window instead of querying
 * it.  Used for testing, overriding some rare cases (win2vnc), and for
 * -overlay .  Input string can be a decimal or 0x hex or something like
 * TrueColor or TrueColor:24 to force a depth as well.
 *
 * visual_id and possibly visual_depth are set.
 */
static void set_visual(char *str) {
#if NO_X11
	RAWFB_RET_VOID
	if (!str) {}
	return;
#else
	int vis, vdepth, defdepth;
	XVisualInfo vinfo;
	char *p, *vstring = strdup(str);

	RAWFB_RET_VOID

	defdepth = DefaultDepth(dpy, scr);
	visual_id = (VisualID) 0;
	visual_depth = 0;

	if (!strcmp(vstring, "ignore") || !strcmp(vstring, "default")
	    || !strcmp(vstring, "")) {
		free(vstring);
		return;
	}

	/* set visual depth */
	if ((p = strchr(vstring, ':')) != NULL) {
		visual_depth = atoi(p+1);
		*p = '\0';
		vdepth = visual_depth;
	} else {
		vdepth = defdepth; 
	}
	if (! quiet) {
		fprintf(stderr, "\nVisual Info:\n");
		fprintf(stderr, " set_visual(\"%s\")\n", str);
		fprintf(stderr, " visual_depth: %d\n", vdepth);
	}

	/* set visual id number */
	if (strcmp(vstring, "StaticGray") == 0) {
		vis = StaticGray;
	} else if (strcmp(vstring, "GrayScale") == 0) {
		vis = GrayScale;
	} else if (strcmp(vstring, "StaticColor") == 0) {
		vis = StaticColor;
	} else if (strcmp(vstring, "PseudoColor") == 0) {
		vis = PseudoColor;
	} else if (strcmp(vstring, "TrueColor") == 0) {
		vis = TrueColor;
	} else if (strcmp(vstring, "DirectColor") == 0) {
		vis = DirectColor;
	} else {
		unsigned int v_in;
		if (sscanf(vstring, "0x%x", &v_in) != 1) {
			if (sscanf(vstring, "%u", &v_in) == 1) {
				visual_id = (VisualID) v_in;
				return;
			}
			rfbLogEnable(1);
			rfbLog("invalid -visual arg: %s\n", vstring);
			X_UNLOCK;
			clean_up_exit(1);
		}
		visual_id = (VisualID) v_in;
		free(vstring);
		return;
	}

	if (! quiet) fprintf(stderr, " visual: %d\n", vis);
	if (XMatchVisualInfo(dpy, scr, visual_depth, vis, &vinfo)) {
		;
	} else if (XMatchVisualInfo(dpy, scr, defdepth, vis, &vinfo)) {
		;
	} else {
		rfbLogEnable(1);
		rfbLog("could not find visual: %s\n", vstring);
		X_UNLOCK;
		clean_up_exit(1);
	}
	free(vstring);

	/* set numerical visual id. */
	visual_id = vinfo.visualid;
#endif	/* NO_X11 */
}

void set_nofb_params(int restore) {
	static int first = 1;
	static int save[100];
	static char *scroll = NULL;
	int i = 0;

	if (first) {
		first = 0;
		save[i++] = use_xfixes;
		save[i++] = use_xdamage;
		save[i++] = use_xrecord;
		save[i++] = wireframe;
		save[i++] = use_solid_bg;
		save[i++] = overlay;
		save[i++] = overlay_cursor;
		save[i++] = using_shm;
		save[i++] = single_copytile;
		save[i++] = take_naps;
		save[i++] = measure_speeds;
		save[i++] = grab_buster;
		save[i++] = show_cursor;
		save[i++] = cursor_shape_updates;
		save[i++] = cursor_pos_updates;
		save[i++] = ncache;

		scroll = scroll_copyrect;
	}
	if (restore) {
		i = 0;
		use_xfixes            = save[i++];
		use_xdamage           = save[i++];
		use_xrecord           = save[i++];
		wireframe             = save[i++];
		use_solid_bg          = save[i++];
		overlay               = save[i++];
		overlay_cursor        = save[i++];
		using_shm             = save[i++];
		single_copytile       = save[i++];
		take_naps             = save[i++];
		measure_speeds        = save[i++];
		grab_buster           = save[i++];
		show_cursor           = save[i++];
		cursor_shape_updates  = save[i++];
		cursor_pos_updates    = save[i++];
		ncache                = save[i++];

		scroll_copyrect = scroll;

		if (cursor_shape_updates) {
			restore_cursor_shape_updates(screen);
		}
		initialize_cursors_mode();

		return;
	}

	use_xfixes = 0;
	use_xdamage = 0;
	use_xrecord = 0;
	wireframe = 0;

	use_solid_bg = 0;
	overlay = 0;
	overlay_cursor = 0;

	using_shm = 0;
	single_copytile = 1;

	take_naps = 0;
	measure_speeds = 0;

	/* got_grab_buster? */
	grab_buster = 0;

	show_cursor = 0;
	show_multiple_cursors = 0;
	cursor_shape_updates = 0;
	if (! got_cursorpos) {
		cursor_pos_updates = 0;
	}

	ncache = 0;

	scroll_copyrect = "never";

	if (! quiet) {
		rfbLog("disabling: xfixes, xdamage, solid, overlay, shm,\n");
		rfbLog("  wireframe, scrollcopyrect, ncache,\n");
		rfbLog("  noonetile, nap, cursor, %scursorshape\n",
		    got_cursorpos ? "" : "cursorpos, " );
		rfbLog("  in -nofb mode.\n");
	}
}

static char *raw_fb_orig_dpy = NULL;

void set_raw_fb_params(int restore) {
	static int first = 1;
	static int vo0, us0, sm0, ws0, wp0, wc0, wb0, na0, tn0;  
	static int xr0, xrm0, sb0, re0;
	static char *mc0;

	/*
	 * set turn off a bunch of parameters not compatible with 
	 * -rawfb mode: 1) ignoring the X server 2) ignoring user input. 
	 */
	
	if (first) {
		/* at least save the initial settings... */
		vo0 = view_only;
		ws0 = watch_selection;
		wp0 = watch_primary;
		wc0 = watch_clipboard;
		wb0 = watch_bell;
		na0 = no_autorepeat;
		sb0 = use_solid_bg;

		us0 = use_snapfb;
		sm0 = using_shm;
		tn0 = take_naps;
		xr0 = xrandr;
		xrm0 = xrandr_maybe;
		re0 = noxrecord;
		mc0 = multiple_cursors_mode;

		first = 0;
	}

	if (restore) {
		view_only = vo0;
		watch_selection = ws0;
		watch_primary = wp0;
		watch_clipboard = wc0;
		watch_bell = wb0;
		no_autorepeat = na0;
		use_solid_bg = sb0;

		use_snapfb = us0;
		using_shm = sm0;
		take_naps = tn0;
		xrandr = xr0;
		xrandr_maybe = xrm0;
		noxrecord = re0;
		multiple_cursors_mode = mc0;

		if (! dpy && raw_fb_orig_dpy) {
			dpy = XOpenDisplay_wr(raw_fb_orig_dpy);
			if (dpy) {
				if (! quiet) rfbLog("reopened DISPLAY: %s\n",
				    raw_fb_orig_dpy);
				scr = DefaultScreen(dpy);
				rootwin = RootWindow(dpy, scr);
				check_xevents(1);
			} else {
				if (! quiet) rfbLog("WARNING: failed to reopen "
				    "DISPLAY: %s\n", raw_fb_orig_dpy);
			}
		}
		return;
	}

	if (verbose) {
		rfbLog("set_raw_fb_params: modifying settings for "
		    "-rawfb mode.\n");
	}

	if (got_noviewonly) {
		/*
		 * The user input parameters are not unset under
		 * -noviewonly... this usage should be very rare
		 * (i.e. rawfb but also send user input to the X
		 * display, most likely using /dev/fb0 for some reason...)
		 */
		if (verbose) {
		   rfbLog("rawfb: -noviewonly mode: still sending mouse and\n");
		   rfbLog("rawfb:   keyboard input to the X DISPLAY!!\n");
		}
	} else {
		/* Normal case: */
#if 0
		if (! view_only && ! pipeinput_str) {
			if (! quiet) rfbLog("  rawfb: setting view_only\n");
			view_only = 1;
		}
#endif
		if (raw_fb_str && strstr(raw_fb_str, "vnc") == raw_fb_str) {
			;
		} else if (watch_selection) {
			if (verbose) rfbLog("  rawfb: turning off "
			    "watch_selection\n");
			watch_selection = 0;
		}
		if (watch_primary) {
			if (verbose) rfbLog("  rawfb: turning off "
			    "watch_primary\n");
			watch_primary = 0;
		}
		if (watch_clipboard) {
			if (verbose) rfbLog("  rawfb: turning off "
			    "watch_clipboard\n");
			watch_clipboard = 0;
		}
		if (watch_bell) {
			if (verbose) rfbLog("  rawfb: turning off watch_bell\n");
			watch_bell = 0;
		}
		if (no_autorepeat) {
			if (verbose) rfbLog("  rawfb: turning off "
			    "no_autorepeat\n");
			no_autorepeat = 0;
		}
		if (use_solid_bg) {
			if (verbose) rfbLog("  rawfb: turning off "
			    "use_solid_bg\n");
			use_solid_bg = 0;
		}
#ifndef MACOSX
		if (raw_fb_str && strstr(raw_fb_str, "vnc") == raw_fb_str) {
			;
		} else {
			multiple_cursors_mode = strdup("arrow");
		}
#endif
	}
	if (using_shm) {
		if (verbose) rfbLog("  rawfb: turning off using_shm\n");
		using_shm = 0;
	}
	if (take_naps) {
		if (verbose) rfbLog("  rawfb: turning off take_naps\n");
		take_naps = 0;
	}
	if (xrandr) {
		if (verbose) rfbLog("  rawfb: turning off xrandr\n");
		xrandr = 0;
	}
	if (xrandr_maybe) {
		if (verbose) rfbLog("  rawfb: turning off xrandr_maybe\n");
		xrandr_maybe = 0;
	}
	if (! noxrecord) {
		if (verbose) rfbLog("  rawfb: turning off xrecord\n");
		noxrecord = 1;
	}
}

/*
 * Presumably under -nofb the clients will never request the framebuffer.
 * However, we have gotten such a request... so let's just give them
 * the current view on the display.  n.b. x2vnc and perhaps win2vnc
 * requests a 1x1 pixel for some workaround so sadly this evidently
 * nearly always happens.
 */
static void nofb_hook(rfbClientPtr cl) {
	XImage *fb;
	XImage raw;

	rfbLog("framebuffer requested in -nofb mode by client %s\n", cl->host);
	/* ignore xrandr */

	if (raw_fb && ! dpy) {
		fb = &raw;
		fb->data = (char *)malloc(32);
	} else {
		int use_real_ximage = 0;
		if (use_real_ximage) {
			fb = XGetImage_wr(dpy, window, 0, 0, dpy_x, dpy_y, AllPlanes, ZPixmap);
		} else {
			fb = &raw;
			fb->data = (char *) calloc(dpy_x*dpy_y*bpp/8, 1);
		}
	}
	main_fb = fb->data;
	rfb_fb = main_fb;
	screen->frameBuffer = rfb_fb;
	screen->displayHook = NULL;
}

void free_old_fb(void) {
	char *fbs[16];
	int i, j, nfb = 0, db = 0; 

	fbs[nfb++] = main_fb;		main_fb = NULL;
	fbs[nfb++] = rfb_fb;		rfb_fb = NULL;
	fbs[nfb++] = cmap8to24_fb;	cmap8to24_fb = NULL;
	fbs[nfb++] = snap_fb;		snap_fb = NULL;
	fbs[nfb++] = rot_fb;		rot_fb = NULL;
	fbs[nfb++] = raw_fb;		raw_fb = NULL;

	for (i=0; i < nfb; i++) {
		char *fb = fbs[i];
		int freeit = 1;
		if (! fb || fb < (char *) 0x10) {
			continue;
		}
		for (j=0; j < i; j++) {
			if (fb == fbs[j]) {
				freeit = 0;
				break;
			}
		}
		if (freeit) {
			if (db) fprintf(stderr, "free: %i %p\n", i, fb);
			free(fb);
		} else {
			if (db) fprintf(stderr, "skip: %i %p\n", i, fb);
		}
	}
}

void do_new_fb(int reset_mem) {
	XImage *fb;

	/* for threaded we really should lock libvncserver out. */
	if (use_threads) {
		rfbLog("warning: changing framebuffers while threaded may\n");
		rfbLog(" not work, do not use -threads if problems arise.\n");
	}

	if (reset_mem == 1) {
		/* reset_mem == 2 is a hack for changing users... */
		clean_shm(0);
		free_tiles();
	}

	free_old_fb();

	fb = initialize_xdisplay_fb();

	initialize_screen(NULL, NULL, fb);

	if (reset_mem) {
		initialize_tiles();
		initialize_blackouts_and_xinerama();
		initialize_polling_images();
	}
	if (ncache) {
		check_ncache(1, 0);
	}
}

static void remove_fake_fb(void) {
	if (! screen) {
		return;
	}
	rfbLog("removing fake fb: 0x%x\n", fake_fb);

	do_new_fb(1);

	/*
	 * fake_fb is freed in do_new_fb(), but we set to NULL here to
	 * indicate it is gone.
	 */
	fake_fb = NULL;
}

static void install_fake_fb(int w, int h, int bpp) {
	int bpc;
	if (! screen) {
		return;
	}
	if (fake_fb) {
		free(fake_fb);
	}
	fake_fb = (char *) calloc(w*h*bpp/8, 1);
	if (! fake_fb) {
		rfbLog("could not create fake fb: %dx%d %d\n", w, h, bpp);
		return;
	}
	bpc = guess_bits_per_color(bpp);
	rfbLog("installing fake fb: %dx%d %d\n", w, h, bpp);
	rfbLog("rfbNewFramebuffer(0x%x, 0x%x, %d, %d, %d, %d, %d)\n",
	    screen, fake_fb, w, h, bpc, 1, bpp/8);

	rfbNewFramebuffer(screen, fake_fb, w, h, bpc, 1, bpp/8);
}

void check_padded_fb(void) {
	if (! fake_fb) {
		return;
	}
	if (unixpw_in_progress) return;

	if (time(NULL) > pad_geometry_time+1 && all_clients_initialized()) {
		remove_fake_fb();
	}
}

void install_padded_fb(char *geom) {
	int w, h;
	int ok = 1;
	if (! geom || *geom == '\0') {
		ok = 0;
	} else if (sscanf(geom, "%dx%d", &w, &h) != 2)  {
		ok = 0;
	}
	w = nabs(w);
	h = nabs(h);

	if (w < 5) w = 5;
	if (h < 5) h = 5;

	if (!ok) {
		rfbLog("skipping invalid pad geometry: '%s'\n", NONUL(geom));
		return;
	}
	install_fake_fb(w, h, bpp);
	pad_geometry_time = time(NULL);
}

static void initialize_snap_fb(void) {
	RAWFB_RET_VOID
	if (snap_fb) {
		free(snap_fb);
	}
	snap = XGetImage_wr(dpy, window, 0, 0, dpy_x, dpy_y, AllPlanes,
	    ZPixmap);
	snap_fb = snap->data;
}

static rfbClient* client = NULL;

void vnc_reflect_bell(rfbClient *cl) {
	if (cl) {}
	if (sound_bell) {
		if (unixpw_in_progress) {
			return;
		}
		if (! all_clients_initialized()) {
			rfbLog("vnc_reflect_bell: not sending bell: "
			    "uninitialized clients\n");
		} else {
			if (screen && client_count) {
				rfbSendBell(screen);
			}
		}
	}
}

void vnc_reflect_recv_cuttext(rfbClient *cl, const char *str, int len) {
	if (cl) {}
	if (unixpw_in_progress) {
		return;
	}
	if (! watch_selection) {
		return;
	}
	if (! all_clients_initialized()) {
		rfbLog("vnc_reflect_recv_cuttext: no send: uninitialized clients\n");
		return; /* some clients initializing, cannot send */ 
	}
	rfbSendServerCutText(screen, (char *)str, len);
}

void vnc_reflect_got_update(rfbClient *cl, int x, int y, int w, int h) {
	if (cl) {}
	if (use_xdamage) {
		static int first = 1;
		if (first) {
			collect_non_X_xdamage(-1, -1, -1, -1, 0);
			first = 0;
		}
		collect_non_X_xdamage(x, y, w, h, 1);
	}
}

void vnc_reflect_got_cursorshape(rfbClient *cl, int xhot, int yhot, int width, int height, int bytesPerPixel) {
	static int serial = 1;
	int i, j;
	char *pixels = NULL;
	unsigned long r, g, b;
	unsigned int ui = 0;
	unsigned long red_mask, green_mask, blue_mask;

	if (cl) {}
	if (unixpw_in_progress) {
		return;
	}
	if (! all_clients_initialized()) {
		rfbLog("vnc_reflect_got_copyshape: no send: uninitialized clients\n");
		return; /* some clients initializing, cannot send */ 
	}
	if (! client->rcSource) {
		return;
	}
	if (bytesPerPixel != 1 && bytesPerPixel != 2 && bytesPerPixel != 4) {
		return;
	}

	red_mask   = (client->format.redMax   << client->format.redShift);
	green_mask = (client->format.greenMax << client->format.greenShift);
	blue_mask  = (client->format.blueMax  << client->format.blueShift);

	pixels = (char *)malloc(4*width*height);
	for (j=0; j<height; j++) {
		for (i=0; i<width; i++) {
			unsigned int* uip;
			unsigned char* uic;
			int m;
			if (bytesPerPixel == 1) {
				unsigned char* p = (unsigned char *) client->rcSource;
				ui = (unsigned long) *(p + j * width + i);
			} else if (bytesPerPixel == 2) {
				unsigned short* p = (unsigned short *) client->rcSource;
				ui = (unsigned long) *(p + j * width + i);
			} else if (bytesPerPixel == 4) {
				unsigned int* p = (unsigned int *) client->rcSource;
				ui = (unsigned long) *(p + j * width + i);
			}
			r = (red_mask   & ui) >> client->format.redShift;
			g = (green_mask & ui) >> client->format.greenShift;
			b = (blue_mask  & ui) >> client->format.blueShift;

			r = (255 * r) / client->format.redMax;
			g = (255 * g) / client->format.greenMax;
			b = (255 * b) / client->format.blueMax;

			ui = (r << 16 | g << 8 | b << 0) ;

			uic = (unsigned char *)client->rcMask;
			m = (int) *(uic + j * width + i);
			if (m) {
				ui |= 0xff000000;
			}
			uip = (unsigned int *)pixels;
			*(uip + j * width + i) = ui;
		}
	}

	store_cursor(serial++, (unsigned long*) pixels, width, height, 32, xhot, yhot);
	free(pixels);
	set_cursor(cursor_x, cursor_y, get_which_cursor());
}

static void from_libvncclient_CopyRectangleFromRectangle(rfbClient* client, int src_x, int src_y, int w, int h, int dest_x, int dest_y) {
  int i,j;

#define COPY_RECT_FROM_RECT(BPP) \
  { \
    uint##BPP##_t* _buffer=((uint##BPP##_t*)client->frameBuffer)+(src_y-dest_y)*client->width+src_x-dest_x; \
    if (dest_y < src_y) { \
      for(j = dest_y*client->width; j < (dest_y+h)*client->width; j += client->width) { \
        if (dest_x < src_x) { \
          for(i = dest_x; i < dest_x+w; i++) { \
            ((uint##BPP##_t*)client->frameBuffer)[j+i]=_buffer[j+i]; \
          } \
        } else { \
          for(i = dest_x+w-1; i >= dest_x; i--) { \
            ((uint##BPP##_t*)client->frameBuffer)[j+i]=_buffer[j+i]; \
          } \
        } \
      } \
    } else { \
      for(j = (dest_y+h-1)*client->width; j >= dest_y*client->width; j-=client->width) { \
        if (dest_x < src_x) { \
          for(i = dest_x; i < dest_x+w; i++) { \
            ((uint##BPP##_t*)client->frameBuffer)[j+i]=_buffer[j+i]; \
          } \
        } else { \
          for(i = dest_x+w-1; i >= dest_x; i--) { \
            ((uint##BPP##_t*)client->frameBuffer)[j+i]=_buffer[j+i]; \
          } \
        } \
      } \
    } \
  }

  switch(client->format.bitsPerPixel) {
  case  8: COPY_RECT_FROM_RECT(8);  break;
  case 16: COPY_RECT_FROM_RECT(16); break;
  case 32: COPY_RECT_FROM_RECT(32); break;
  default:
    rfbClientLog("Unsupported bitsPerPixel: %d\n",client->format.bitsPerPixel);
  }
}

void vnc_reflect_got_copyrect(rfbClient *cl, int src_x, int src_y, int w, int h, int dest_x, int dest_y) {
	sraRegionPtr reg;
	int dx, dy, rc = -1;
	static int last_dx = 0, last_dy = 0;
	if (cl) {}
	if (unixpw_in_progress) {
		return;
	}
	if (! all_clients_initialized()) {
		rfbLog("vnc_reflect_got_copyrect: no send: uninitialized clients\n");
		return; /* some clients initializing, cannot send */ 
	}
	dx = dest_x - src_x;
	dy = dest_y - src_y;
	if (dx != last_dx || dy != last_dy) {
		rc = fb_push_wait(0.05, FB_COPY|FB_MOD);
	}
	if (0) fprintf(stderr, "vnc_reflect_got_copyrect: %03dx%03d+%03d+%03d   %3d %3d  rc=%d\n", dest_x, dest_y, w, h, dx, dy, rc);
	reg = sraRgnCreateRect(dest_x, dest_y, dest_x + w, dest_y + h);
	do_copyregion(reg, dx, dy, 0);
	sraRgnDestroy(reg);

	last_dx = dx;
	last_dy = dy;

	from_libvncclient_CopyRectangleFromRectangle(cl, src_x, src_y, w, h, dest_x, dest_y);
}

rfbBool vnc_reflect_resize(rfbClient *cl)  {
	static int first = 1;
	if(cl->frameBuffer) {
		free(cl->frameBuffer);
	}
	cl->frameBuffer= malloc(cl->width * cl->height * cl->format.bitsPerPixel/8);
	if (!first) {
		do_new_fb(1);
	}
	first = 0;
	return cl->frameBuffer ? TRUE : FALSE;
}

char *vnc_reflect_guess(char *str, char **raw_fb_addr) {

	static int first = 1;
	char *hp = str + strlen("vnc:");
	char *at = NULL;
	int argc = 0, i;
	char *argv[16];
	char str2[256];
	char *str0 = strdup(str);

	if (client == NULL) {
		client = rfbGetClient(8, 3, 4);
	}

	rfbLog("rawfb: %s\n", str);

	at = strchr(hp, '@');
	if (at) {
		*at = '\0';
		at++;
	}

	client->appData.useRemoteCursor = TRUE;
	client->Bell = vnc_reflect_bell;
	client->GotXCutText = vnc_reflect_recv_cuttext;
	client->GotCopyRect = vnc_reflect_got_copyrect;
	client->GotCursorShape = vnc_reflect_got_cursorshape;
	client->MallocFrameBuffer = vnc_reflect_resize;
	client->canHandleNewFBSize = TRUE;
	client->GotFrameBufferUpdate = vnc_reflect_got_update;

	if (first) {
		argv[argc++] = "x11vnc_rawfb_vnc";
		if (strstr(hp, "listen") == hp) {
			char *q = strchr(hp, ':');
			argv[argc++] = strdup("-listen");
			if (q) {
				client->listenPort = atoi(q+1);
			} else {
				client->listenPort = LISTEN_PORT_OFFSET;
			}
		} else {
			argv[argc++] = strdup(hp);
		}

		if (! rfbInitClient(client, &argc, argv)) {
			rfbLog("vnc_reflector failed for: %s\n", str0);
			clean_up_exit(1);
		}
	}

	if (at) {
		sprintf(str2, "map:/dev/null@%s", at);
	} else {
		unsigned long red_mask, green_mask, blue_mask;
		red_mask   = (client->format.redMax   << client->format.redShift);
		green_mask = (client->format.greenMax << client->format.greenShift);
		blue_mask  = (client->format.blueMax  << client->format.blueShift);
		sprintf(str2, "map:/dev/null@%dx%dx%d:0x%lx/0x%lx/0x%lx",
		    client->width, client->height, client->format.bitsPerPixel,
		    red_mask, green_mask, blue_mask);
	}
	*raw_fb_addr = (char *) client->frameBuffer;
	free(str0);

	if (first) {
		setup_cursors_and_push();

		for (i=0; i<10; i++) {
			vnc_reflect_process_client();
		}
	}
	first = 0;

	return strdup(str2);
}

rfbBool vnc_reflect_send_pointer(int x, int y, int mask) {
	int rc;
	if (mask >= 0) {
		got_user_input++;
		got_pointer_input++;
		last_pointer_time = time(NULL);
	}

	if (cursor_x != x || cursor_y != y) {
		last_pointer_motion_time = dnow();
	}

	cursor_x = x;
	cursor_y = y;

	/* record the x, y position for the rfb screen as well. */
	cursor_position(x, y);

	/* change the cursor shape if necessary */
	rc = set_cursor(x, y, get_which_cursor());
	cursor_changes += rc;

	return SendPointerEvent(client, x, y, mask);
}

rfbBool vnc_reflect_send_key(uint32_t key, rfbBool down) {
	return SendKeyEvent(client, key, down);
}

rfbBool vnc_reflect_send_cuttext(char *str, int len) {
	return SendClientCutText(client, str, len);
}

void vnc_reflect_process_client(void) {
	int num;
	if (client == NULL) {
		return;
	}
	num = WaitForMessage(client, 1000);
	if (num > 0) {
		if (!HandleRFBServerMessage(client)) {
			rfbLog("vnc_reflect_process_client: read failure to server\n");
			shut_down = 1;
		}
	}
}

void linux_dev_fb_msg(char* q) {
	if (strstr(q, "/dev/fb") && strstr(UT.sysname, "Linux")) {
		rfbLog("\n");
		rfbLog("On Linux you may need to load a kernel module to enable\n");
		rfbLog("the framebuffer device /dev/fb*; e.g.:\n");
		rfbLog("   vga=0x303 (and others) kernel boot parameter\n");
		rfbLog("   modprobe uvesafb\n");
		rfbLog("   modprobe radeonfb (card specific)\n");
		rfbLog("   modprobe nvidiafb (card specific, others)\n");
		rfbLog("   modprobe vesafb (?)\n");
		rfbLog("   modprobe vga16fb\n");
		rfbLog("\n");
		rfbLog("You may also need root permission to open /dev/fb*\n");
		rfbLog("and/or /dev/tty*.\n");
		rfbLog("\n");
	}
}

#define RAWFB_MMAP 1
#define RAWFB_FILE 2
#define RAWFB_SHM  3

XImage *initialize_raw_fb(int reset) {
	char *str, *q;
	int w, h, b, shmid = 0;
	unsigned long rm = 0, gm = 0, bm = 0, tm;
	static XImage ximage_struct;	/* n.b.: not (XImage *) */
	static XImage ximage_struct_snap;
	int closedpy = 1, i, m, db = 0;
	int do_macosx = 0;
	int do_reflect = 0;
	char *unlink_me = NULL;

	static char *last_file = NULL;
	static int last_mode = 0;

	if (reset && last_mode) {
		int fd;
		if (last_mode != RAWFB_MMAP && last_mode != RAWFB_FILE) {
			return NULL;
		}
		if (last_mode == RAWFB_MMAP) {
			munmap(raw_fb_addr, raw_fb_mmap);
		}
		if (raw_fb_fd >= 0) {
			close(raw_fb_fd);
		}
		raw_fb_fd = -1;
if (db) fprintf(stderr, "initialize_raw_fb reset\n");
			
		fd = -1;
		if (rawfb_dev_video) {
			fd = open(last_file, O_RDWR);
		}
		if (fd < 0) {
			fd = open(last_file, O_RDONLY);
		}
		if (fd < 0) {
			rfbLogEnable(1);
			rfbLog("failed to rawfb file: %s\n", last_file);
			rfbLogPerror("open");
			clean_up_exit(1);
		}
		raw_fb_fd = fd;
		if (last_mode == RAWFB_MMAP) {
			raw_fb_addr = mmap(0, raw_fb_mmap, PROT_READ,
			    MAP_SHARED, fd, 0);

			if (raw_fb_addr == MAP_FAILED || raw_fb_addr == NULL) {
				rfbLogEnable(1);
				rfbLog("failed to mmap file: %s\n", last_file);
				rfbLog("   raw_fb_addr: %p\n", raw_fb_addr);
				rfbLogPerror("mmap");
				clean_up_exit(1);
			}
		}
		return NULL;
	}

#ifdef MACOSX
	if (raw_fb_addr != NULL && macosx_console && raw_fb_addr == macosx_get_fb_addr()) {
		raw_fb_addr = NULL;
	}
#endif
	
	if (raw_fb_addr || raw_fb_seek) {
		if (raw_fb_shm) {
			shmdt(raw_fb_addr);
#if LIBVNCSERVER_HAVE_MMAP
		} else if (raw_fb_mmap) {
			munmap(raw_fb_addr, raw_fb_mmap);
			if (raw_fb_fd >= 0) {
				close(raw_fb_fd);
			}
			raw_fb_fd = -1;
#endif
		} else if (raw_fb_seek) {
			if (raw_fb_fd >= 0) {
				close(raw_fb_fd);
			}
			raw_fb_fd = -1;
		}
		raw_fb_addr = NULL;
		raw_fb_mmap = 0;
		raw_fb_seek = 0;
	}
	if (! raw_fb_str) {
		return NULL;
	}

	/* testing aliases */
	if (!strcasecmp(raw_fb_str, "NULL") || !strcasecmp(raw_fb_str, "ZERO")
	    || !strcasecmp(raw_fb_str, "NONE")) {
		raw_fb_str = strdup("map:/dev/zero@640x480x32");
	} else if (!strcasecmp(raw_fb_str, "NULLBIG") || !strcasecmp(raw_fb_str, "NONEBIG")) {
		raw_fb_str = strdup("map:/dev/zero@1024x768x32");
	}
	if (!strcasecmp(raw_fb_str, "RAND")) {
		raw_fb_str = strdup("file:/dev/urandom@128x128x16");
	} else if (!strcasecmp(raw_fb_str, "RANDBIG")) {
		raw_fb_str = strdup("file:/dev/urandom@640x480x16");
	} else if (!strcasecmp(raw_fb_str, "RANDHUGE")) {
		raw_fb_str = strdup("file:/dev/urandom@1024x768x16");
	}
	if (strstr(raw_fb_str, "solid=") == raw_fb_str) {
		char *n = raw_fb_str + strlen("solid=");
		char tmp[] = "/tmp/solid.XXXXXX";
		char str[100];
		unsigned int vals[1024], val;
		int x, y, fd, w = 1024, h = 768;
		if (strstr(n, "0x")) {
			if (sscanf(n, "0x%x", &val) != 1) {
				val = 0;
			}
		}
		if (val == 0) {
			val = get_pixel(n);
		}
		if (val == 0) {
			val = 0xFF00FF;
		}
		fd = mkstemp(tmp);
		for (y = 0; y < h; y++) {
			for (x = 0; x < w; x++) {
				vals[x] = val;
			}
			write(fd, (char *)vals, 4 * w);
		}
		close(fd);
		fd = open(tmp, O_WRONLY);
		unlink_me = strdup(tmp);
		sprintf(str, "map:%s@%dx%dx32", tmp, w, h);
		raw_fb_str = strdup(str);
	} else if (strstr(raw_fb_str, "swirl") == raw_fb_str) {
		char tmp[] = "/tmp/solid.XXXXXX";
		char str[100];
		unsigned int val[1024];
		unsigned int c1, c2, c3, c4;
		int x, y, fd, w = 1024, h = 768;
		fd = mkstemp(tmp);
		for (y = 0; y < h; y++) {
			for (x = 0; x < w; x++) {
				c1 = 0;
				c2 = ((x+y)*128)/(w+h);
				c3 = (x*128)/w;
				c4 = (y*256)/h;
				val[x] = (c1 << 24) | (c2 << 16) | (c3 << 8) | (c4 << 0);
			}
			write(fd, (char *)val, 4 * w);
		}
		close(fd);
		fd = open(tmp, O_WRONLY);
		unlink_me = strdup(tmp);
		sprintf(str, "map:%s@%dx%dx32", tmp, w, h);
		raw_fb_str = strdup(str);
	}


	if ( (q = strstr(raw_fb_str, "setup:")) == raw_fb_str) {
		FILE *pipe;
		char line[1024], *t;

		set_child_info();
		q += strlen("setup:");
		/* rawfb-setup */
		if (no_external_cmds || !cmd_ok("rawfb-setup")) {
			rfbLogEnable(1);
			rfbLog("cannot run external commands in -nocmds "
			    "mode:\n");
			rfbLog("   \"%s\"\n", q);
			rfbLog("   exiting.\n");
			clean_up_exit(1);
		}
		rfbLog("running command to setup rawfb: %s\n", q);
		close_exec_fds();
		pipe = popen(q, "r");
		if (! pipe) {
			rfbLogEnable(1);
			rfbLog("popen of setup command failed.\n");
			rfbLogPerror("popen");
			clean_up_exit(1);
		}
		line[0] = '\0';
		if (fgets(line, 1024, pipe) == NULL) {
			rfbLogEnable(1);
			rfbLog("read of setup command failed.\n");
			clean_up_exit(1);
		}
		pclose(pipe);
		str = strdup(line);
		t = str;
		while (*t != '\0') {
			if (*t == '\n') {
				*t = '\0';
			}
			t++;
		}
		rfbLog("setup command returned: %s\n", str);

	} else {
		str = strdup(raw_fb_str);
	}
	if (str[0] == '+') {
		char *t = strdup(str+1);
		free(str);
		str = t;
		closedpy = 0;
		if (! window) {
			window = rootwin;
		}
	}

	raw_fb_shm = 0;
	raw_fb_mmap = 0;
	raw_fb_seek = 0;
	raw_fb_fd = -1;
	raw_fb_addr = NULL;
	raw_fb_offset = 0;
	raw_fb_bytes_per_line = 0;
	rawfb_vnc_reflect = 0;

	last_mode = 0;
	if (last_file) {
		free(last_file);
		last_file = NULL;
	}
	if (strstr(str, "Video") == str) {
		if (pipeinput_str != NULL) {
			free(pipeinput_str);
		}
		pipeinput_str = strdup("VID");
		initialize_pipeinput();
		str[0] = 'v';
	}

	if (strstr(str, "video") == str || strstr(str, "/dev/video") == str) {
		char *str2 = v4l_guess(str, &raw_fb_fd);
		if (str2 == NULL) {
			rfbLog("v4l_guess failed for: %s\n", str);
			clean_up_exit(1);
		}
		str = str2;
		rfbLog("v4l_guess returned: %s\n", str);
		rawfb_dev_video = 1;
	} else if (strstr(str, "dev/video")) {
		rawfb_dev_video = 1;
	} else if (strstr(str, "console") == str || strstr(str, "fb") == str ||
	    strstr(str, "/dev/fb") == str || strstr(str, "vt") == str) {
		char *str2 = console_guess(str, &raw_fb_fd);
		if (str2 == NULL) {
			rfbLog("console_guess failed for: %s\n", str);
			clean_up_exit(1);
		}
		str = str2;
		rfbLog("console_guess returned: %s\n", str);
	} else if (strstr(str, "vnc:") == str) {
		char *str2 = vnc_reflect_guess(str, &raw_fb_addr);

		rawfb_vnc_reflect = 1;
		do_reflect = 1;

		str = str2;
		rfbLog("vnc_reflector set rawfb str to: %s\n", str);
		if (pipeinput_str == NULL) {
			pipeinput_str = strdup("VNC");
		}
		initialize_pipeinput();
	}

	if (closedpy && !view_only && got_noviewonly) {
		rfbLog("not closing X DISPLAY under -noviewonly option.\n");
		closedpy = 0;
		if (! window) {
			window = rootwin;
		}
	}

	if (! raw_fb_orig_dpy && dpy) {
		raw_fb_orig_dpy = strdup(DisplayString(dpy));
	}
#ifndef BOLDLY_CLOSE_DISPLAY
#define BOLDLY_CLOSE_DISPLAY 1
#endif
#if BOLDLY_CLOSE_DISPLAY
	if (closedpy) {
		if (dpy) {
			rfbLog("closing X DISPLAY: %s in rawfb mode.\n",
			    DisplayString(dpy));
			XCloseDisplay_wr(dpy);	/* yow! */
		}
		dpy = NULL;
	}
#endif

	/*
	 * -rawfb shm:163938442@640x480x32:ff/ff00/ff0000+3000
	 * -rawfb map:/path/to/file@640x480x32:ff/ff00/ff0000
	 * -rawfb file:/path/to/file@640x480x32:ff/ff00/ff0000
	 */

	if (raw_fb_full_str) {
		free(raw_fb_full_str);
	}
	raw_fb_full_str = strdup(str);


	/* +O offset */
	if ((q = strrchr(str, '+')) != NULL) {
		if (sscanf(q, "+%d", &raw_fb_offset) == 1) {
			*q = '\0';
		} else {
			raw_fb_offset = 0;
		}
	}
	/* :R/G/B masks */
	if ((q = strrchr(str, ':')) != NULL) {
		if (sscanf(q, ":%lx/%lx/%lx", &rm, &gm, &bm) == 3) {
			*q = '\0';
		} else if (sscanf(q, ":0x%lx/0x%lx/0x%lx", &rm, &gm, &bm)== 3) {
			*q = '\0';
		} else if (sscanf(q, ":%lu/%lu/%lu", &rm, &gm, &bm) == 3) {
			*q = '\0';
		} else {
			rm = 0;
			gm = 0;
			bm = 0;
		}
	}
	if ((q = strrchr(str, '@')) == NULL) {
		rfbLogEnable(1);
		rfbLog("invalid rawfb str: %s\n", str);
		clean_up_exit(1);
	}

	if (strrchr(q, '-')) {
		char *q2 = strrchr(q, '-');
		raw_fb_bytes_per_line = atoi(q2+1);
		*q2 = '\0';
	}
	/* @WxHxB */
	if (sscanf(q, "@%dx%dx%d", &w, &h, &b) != 3) {
		rfbLogEnable(1);
		rfbLog("invalid rawfb str: %s\n", str);
		clean_up_exit(1);
	}
	*q = '\0';

	if (rm == 0 && gm == 0 && bm == 0) {
		/* guess masks... */
		if (b == 24 || b == 32) {
			rm = 0xff0000;
			gm = 0x00ff00;
			bm = 0x0000ff;
		} else if (b == 16) {
			rm = 0xf800;
			gm = 0x07e0;
			bm = 0x001f;
		} else if (b == 8) {
			rm = 0x07;
			gm = 0x38;
			bm = 0xc0;
		}
	}
	/* we can fake -flipbyteorder to some degree... */
	if (flip_byte_order) {
		if (b == 24 || b == 32) {
			tm = rm;
			rm = bm;
			bm = tm;
		} else if (b == 16) {
			unsigned short s1, s2;
			s1 = (unsigned short) rm;
			s2 = ((0xff & s1) << 8) | ((0xff00 & s1) >> 8);
			rm = (unsigned long) s2;
			s1 = (unsigned short) gm;
			s2 = ((0xff & s1) << 8) | ((0xff00 & s1) >> 8);
			gm = (unsigned long) s2;
			s1 = (unsigned short) bm;
			s2 = ((0xff & s1) << 8) | ((0xff00 & s1) >> 8);
			bm = (unsigned long) s2;
		}
	}

	/* native fb stuff for bpp < 8 only */
	raw_fb_native_bpp = b;
	raw_fb_native_red_mask = rm;
	raw_fb_native_green_mask = gm;
	raw_fb_native_blue_mask = bm;
	raw_fb_native_red_shift = 100;
	raw_fb_native_green_shift = 100;
	raw_fb_native_blue_shift = 100;
	raw_fb_native_red_max = 1;
	raw_fb_native_green_max = 1;
	raw_fb_native_blue_max = 1;
	m = 1;
	for (i=0; i<32; i++)  {
		if (raw_fb_native_red_mask & m) {
			if (raw_fb_native_red_shift == 100) {
				raw_fb_native_red_shift = i;
			}
			raw_fb_native_red_max *= 2;
		}
		if (raw_fb_native_green_mask & m) {
			if (raw_fb_native_green_shift == 100) {
				raw_fb_native_green_shift = i;
			}
			raw_fb_native_green_max *= 2;
		}
		if (raw_fb_native_blue_mask & m) {
			if (raw_fb_native_blue_shift == 100) {
				raw_fb_native_blue_shift = i;
			}
			raw_fb_native_blue_max *= 2;
		}
		m = m << 1;
	}
	raw_fb_native_red_max -= 1;
	raw_fb_native_green_max -= 1;
	raw_fb_native_blue_max -= 1;

	if (b < 8) {
		/* e.g. VGA16 */
		rfbLog("raw_fb_native_bpp: %d 0x%02lx 0x%02lx 0x%02lx %d/%d/%d %d/%d/%d\n", raw_fb_native_bpp,
		    raw_fb_native_red_mask, raw_fb_native_green_mask, raw_fb_native_blue_mask,
		    raw_fb_native_red_max, raw_fb_native_green_max, raw_fb_native_blue_max,
		    raw_fb_native_red_shift, raw_fb_native_green_shift, raw_fb_native_blue_shift);
		raw_fb_expand_bytes = 1;
		b = 8;
		rm = 0x07;
		gm = 0x38;
		bm = 0xc0;
	}
	/* end of stuff for bpp < 8 */

	dpy_x = wdpy_x = w;
	dpy_y = wdpy_y = h;
	off_x = 0;
	off_y = 0;

	if (rawfb_dev_video) {
		if (b == 24) {
			rfbLog("enabling -24to32 for 24bpp video\n");
			xform24to32 = 1;
		} else {
			if (xform24to32) {
				rfbLog("disabling -24to32 for 24bpp video\n");
			}
			xform24to32 = 0;
		}
	}

	if (xform24to32) {
		if (b != 24) {
			rfbLog("warning: -24to32 mode and bpp=%d\n", b);
		}
		b = 32;
	}
	if (strstr(str, "snap:") == str) {
		use_snapfb = 1;
		str[0] = 'f'; str[1] = 'i'; str[2] = 'l'; str[3] = 'e';
	}

	if (strstr(str, "shm:") != str && strstr(str, "mmap:") != str &&
	    strstr(str, "map:") != str && strstr(str, "file:") != str) {
		/* hmmm, not following directions, see if map: applies */
		struct stat sbuf;
		if (stat(str, &sbuf) == 0) {
			char *new;
			int len = strlen("map:") + strlen(str) + 1;
			rfbLog("no type prefix: %s\n", raw_fb_str);
			rfbLog("  but file exists, so assuming: map:%s\n",
			    raw_fb_str);
			new = (char *) malloc(len);
			strcpy(new, "map:");
			strcat(new, str);
			free(str);
			str = new;
		}
	}

	if (sscanf(str, "shm:%d", &shmid) == 1) {
		/* shm:N */
#if LIBVNCSERVER_HAVE_XSHM || LIBVNCSERVER_HAVE_SHMAT
		raw_fb_addr = (char *) shmat(shmid, 0, SHM_RDONLY);
		if (! raw_fb_addr) {
			rfbLogEnable(1);
			rfbLog("failed to attach to shm: %d, %s\n", shmid, str);
			rfbLogPerror("shmat");
			clean_up_exit(1);
		}
		raw_fb_shm = 1;
		rfbLog("rawfb: shm: %d W: %d H: %d B: %d addr: %p\n",
		    shmid, w, h, b, raw_fb_addr);
		last_mode = RAWFB_SHM;
#else
		rfbLogEnable(1);
		rfbLog("x11vnc was compiled without shm support.\n");
		rfbLogPerror("shmat");
		clean_up_exit(1);
#endif
	} else if (strstr(str, "map:") == str || strstr(str, "mmap:") == str
	    || strstr(str, "file:") == str) {
		/* map:/path/... or file:/path  */
		int fd, do_mmap = 1, size;
		struct stat sbuf;

		if (*str == 'f') {
			do_mmap = 0;
		}
		q = strchr(str, ':');
		q++;

		macosx_console = 0;
		if (strstr(q, "macosx:") == q) {
			/* mmap:macosx:/dev/null@... */
			q += strlen("macosx:");			
			do_macosx = 1;
			do_mmap = 0;
			macosx_console = 1;
		}

		last_file = strdup(q);

		fd = raw_fb_fd;
		if (fd < 0 && rawfb_dev_video) {
			fd = open(q, O_RDWR);
		}
		if (fd < 0) {
			fd = open(q, O_RDONLY);
		}
		if (fd < 0) {
			rfbLogEnable(1);
			rfbLog("failed to open file: %s, %s\n", q, str);
			rfbLogPerror("open");
			linux_dev_fb_msg(q);
			clean_up_exit(1);
		}
		raw_fb_fd = fd;

		if (raw_fb_native_bpp < 8) {
			size = w*h*raw_fb_native_bpp/8 + raw_fb_offset;
		} else if (xform24to32) {
			size = w*h*24/8 + raw_fb_offset;
		} else {
			size = w*h*b/8 + raw_fb_offset;
		}
		if (fstat(fd, &sbuf) == 0) {
			if (S_ISREG(sbuf.st_mode)) {
				if (0) size = sbuf.st_size;
			} else {
				rfbLog("raw fb is non-regular file: %s\n", q);
			}
		}

		if (do_macosx) {
			raw_fb_addr = macosx_get_fb_addr();
			raw_fb_mmap = size;
			rfbLog("rawfb: macosx fb: %s\n", q);
			rfbLog("   w: %d h: %d b: %d addr: %p sz: %d\n", w, h,
			    b, raw_fb_addr, size);
			last_mode = 0;
		} else if (do_reflect) {
			raw_fb_mmap = size;
			rfbLog("rawfb: vnc fb: %s\n", q);
			rfbLog("   w: %d h: %d b: %d addr: %p sz: %d\n", w, h,
			    b, raw_fb_addr, size);
			last_mode = 0;

		} else if (do_mmap) {
#if LIBVNCSERVER_HAVE_MMAP
			raw_fb_addr = mmap(0, size, PROT_READ, MAP_SHARED,
			    fd, 0);

			if (raw_fb_addr == MAP_FAILED || raw_fb_addr == NULL) {
				rfbLogEnable(1);
				rfbLog("failed to mmap file: %s, %s\n", q, str);
				rfbLog("   raw_fb_addr: %p\n", raw_fb_addr);
				rfbLogPerror("mmap");

				raw_fb_addr = NULL;
				rfbLog("mmap(2) failed, trying slower lseek(2)\n");
				raw_fb_seek = size;
				last_mode = RAWFB_FILE;

			} else {
				raw_fb_mmap = size;

				rfbLog("rawfb: mmap file: %s\n", q);
				rfbLog("   w: %d h: %d b: %d addr: %p sz: %d\n", w, h,
				    b, raw_fb_addr, size);
				last_mode = RAWFB_MMAP;
			}
#else
			rfbLog("mmap(2) not supported on system, using"
			    " slower lseek(2)\n");
			raw_fb_seek = size;
			last_mode = RAWFB_FILE;
#endif
		} else {
			raw_fb_seek = size;
			last_mode = RAWFB_FILE;

			rfbLog("rawfb: seek file: %s\n", q);
			rfbLog("   W: %d H: %d B: %d sz: %d\n", w, h, b, size);
		}
	} else {
		rfbLogEnable(1);
		rfbLog("invalid rawfb str: %s\n", str);
		clean_up_exit(1);
	}

	if (unlink_me) {
		unlink(unlink_me);
	}

	if (! raw_fb_image) {
		raw_fb_image = &ximage_struct;
	}

	initialize_clipshift();

	if (raw_fb_bytes_per_line == 0) {
		raw_fb_bytes_per_line = dpy_x*b/8;

		/*
		 * Put cases here were we can determine that
		 * raw_bytes_per_line != dpy_x*b/8
		 */
#ifdef MACOSX
		if (do_macosx) {
			raw_fb_bytes_per_line = macosxCG_CGDisplayBytesPerRow();
		}
#endif
	}

	raw_fb_image->bytes_per_line = dpy_x * b/8;
	raw_fb = (char *) malloc(dpy_y * dpy_x * b/8);
	raw_fb_image->data = raw_fb;
	raw_fb_image->format = ZPixmap;
	raw_fb_image->width  = dpy_x;
	raw_fb_image->height = dpy_y;
	raw_fb_image->bits_per_pixel = b;
	raw_fb_image->bitmap_unit = -1;


	if (use_snapfb && (raw_fb_seek || raw_fb_mmap)) {
		int b_use = b;
		if (snap_fb) {
			free(snap_fb);
		}
		if (b_use == 32 && xform24to32) {
			/*
			 * The actual framebuffer (e.g. mapped addr) and
			 * snap fb must be the same bpp.  E.g. both 24bpp.
			 * Reading FROM snap to utility image will be
			 * transformed 24->32 in copy_raw_fb_24_to_32.
			 *
			 * addr -> snap -> (scanline, fullscreen, ...)
			 */
			b_use = 24;
			raw_fb_bytes_per_line = dpy_x * b_use/8;
		}
		snap_fb = (char *) malloc(dpy_y * dpy_x * b_use/8);
		snap = &ximage_struct_snap;
		snap->data = snap_fb;
		snap->format = ZPixmap;
		snap->width  = dpy_x;
		snap->height = dpy_y;
		snap->bits_per_pixel = b_use;
		snap->bytes_per_line = dpy_x * b_use/8;
		snap->bitmap_unit = -1;
	}


	raw_fb_image->red_mask = rm;
	raw_fb_image->green_mask = gm;
	raw_fb_image->blue_mask = bm;

	raw_fb_image->depth = 0;
	m = 1;
	for (i=0; i<32; i++)  {
		if (rm & m) {
			raw_fb_image->depth++;
		}
		if (gm & m) {
			raw_fb_image->depth++;
		}
		if (bm & m) {
			raw_fb_image->depth++;
		}
		m = m << 1;
	}
	if (raw_fb_native_bpp < 8) {
		raw_fb_image->depth = raw_fb_expand_bytes * 8;
	}
	if (! raw_fb_image->depth) { 
		raw_fb_image->depth = (b == 32) ? 24 : b;
	}

	depth = raw_fb_image->depth;

	if (raw_fb_image->depth == 15) {
		/* unresolved bug with RGB555... */
		depth++;
	}

	if (clipshift || raw_fb_native_bpp < 8) {
		memset(raw_fb, 0xff, dpy_y * raw_fb_image->bytes_per_line);
	} else if (raw_fb_addr && ! xform24to32) {
		memcpy(raw_fb, raw_fb_addr + raw_fb_offset, dpy_y * raw_fb_image->bytes_per_line);
	} else {
		memset(raw_fb, 0xff, dpy_y * raw_fb_image->bytes_per_line);
	}

	if (verbose) {
		rfbLog("\n");
		rfbLog("rawfb:  raw_fb  %p\n", raw_fb);
		rfbLog("        format  %d\n", raw_fb_image->format);
		rfbLog("        width   %d\n", raw_fb_image->width);
		rfbLog("        height  %d\n", raw_fb_image->height);
		rfbLog("        bpp     %d\n", raw_fb_image->bits_per_pixel);
		rfbLog("        depth   %d\n", raw_fb_image->depth);
		rfbLog("        bpl     %d\n", raw_fb_image->bytes_per_line);
		if (use_snapfb && snap_fb) {
			rfbLog("        snap_fb %p\n", snap_fb);
		}
	}

	free(str);

	return raw_fb_image;
}

static void initialize_clipshift(void) {
	clipshift = 0;
	cdpy_x = cdpy_y = coff_x = coff_y = 0;
	if (clip_str) {
		int w, h, x, y, bad = 0;
		if (parse_geom(clip_str, &w, &h, &x, &y, wdpy_x, wdpy_y)) {
			if (x < 0) {
				x = 0;
			}
			if (y < 0) {
				y = 0;
			}
			if (x + w > wdpy_x) {
				w = wdpy_x - x;
			}
			if (y + h > wdpy_y) {
				h = wdpy_y - y;
			}
			if (w <= 0 || h <= 0) {
				bad = 1;
			}
		} else {
			bad = 1;
		}
		if (bad) {
			rfbLog("skipping invalid -clip WxH+X+Y: %s\n",
			    clip_str); 
		} else {
			/* OK, change geom behind everyone's back... */
			cdpy_x = w;
			cdpy_y = h;
			coff_x = x;
			coff_y = y;

			clipshift = 1;

			dpy_x = cdpy_x;
			dpy_y = cdpy_y;
		}
	}
}

static int wait_until_mapped(Window win) {
#if NO_X11
	if (!win) {}
	return 0;
#else
	int ms = 50, waittime = 30;
	time_t start = time(NULL);
	XWindowAttributes attr;

	while (1) {
		if (! valid_window(win, NULL, 0)) {
			if (time(NULL) > start + waittime) {
				break;
			}
			usleep(ms * 1000);
			continue;
		}
		if (dpy && ! XGetWindowAttributes(dpy, win, &attr)) {
			return 0;
		}
		if (attr.map_state == IsViewable) {
			return 1;
		}
		usleep(ms * 1000);
	}
	return 0;
#endif	/* NO_X11 */
}

/*
 * initialize a fb for the X display
 */
XImage *initialize_xdisplay_fb(void) {
#if NO_X11
	if (raw_fb_str) {
		return initialize_raw_fb(0);
	}
	return NULL;
#else
	XImage *fb;
	char *vis_str = visual_str;
	int try = 0, subwin_tries = 3;
	XErrorHandler old_handler = NULL;
	int subwin_bs;

	if (raw_fb_str) {
		return initialize_raw_fb(0);
	}

	X_LOCK;
	if (subwin) {
		if (subwin_wait_mapped) {
			wait_until_mapped(subwin);
		}
		if (!valid_window((Window) subwin, NULL, 0)) {
			rfbLogEnable(1);
			rfbLog("invalid sub-window: 0x%lx\n", subwin);
			X_UNLOCK;
			clean_up_exit(1);
		}
	}
	
	if (overlay) {
		/* 
		 * ideally we'd like to not have to cook up the
		 * visual variables but rather let it all come out
		 * of XReadScreen(), however there is no way to get
		 * a default visual out of it, so we pretend -visual
		 * TrueColor:NN was supplied with NN usually 24.
		 */
		char str[32];
		Window twin = subwin ? subwin : rootwin;
		XImage *xi;

		xi = xreadscreen(dpy, twin, 0, 0, 8, 8, False);
		sprintf(str, "TrueColor:%d", xi->depth);
		if (xi->depth != 24 && ! quiet) {
			rfbLog("warning: overlay image has depth %d "
			    "instead of 24.\n", xi->depth);
		}
		XDestroyImage(xi);
		if (visual_str != NULL && ! quiet) {
			rfbLog("warning: replacing '-visual %s' by '%s' "
			    "for use with -overlay\n", visual_str, str);
		}
		vis_str = strdup(str);
	}

	if (xform24to32) {
		if (DefaultDepth(dpy, scr) == 24) {
			vis_str = strdup("TrueColor:32");
			rfbLog("initialize_xdisplay_fb: vis_str set to: %s\n",
			    vis_str);
			visual_id = (VisualID) 0;
			visual_depth = 0;
			set_visual_str_to_something = 1;
		}
	} else if (DefaultDepth(dpy, scr) < 8) {
		/* check very low bpp case, e.g. mono or vga16 */
		Screen *s = DefaultScreenOfDisplay(dpy);
		XImage *xi = XGetImage_wr(dpy, DefaultRootWindow(dpy), 0, 0, 2, 2, AllPlanes,
		    ZPixmap);
		if (xi && xi->bits_per_pixel < 8) {
			int lowbpp = xi->bits_per_pixel; 
			if (!vis_str) {
				char tmp[32];
				sprintf(tmp, "0x%x:8", (int) s->root_visual->visualid);
				vis_str = strdup(tmp);
				rfbLog("initialize_xdisplay_fb: low bpp[%d], vis_str "
				    "set to: %s\n", lowbpp, vis_str);
			}
			if (using_shm) {
				using_shm = 0;
				rfbLog("initialize_xdisplay_fb: low bpp[%d], "
				    "disabling shm\n", lowbpp);
			}
			visual_id = (VisualID) 0;
			visual_depth = 0;
			set_visual_str_to_something = 1;
		}
		if (xi) {
			XDestroyImage(xi);
		}
	}

	if (vis_str != NULL) {
		set_visual(vis_str);
		if (vis_str != visual_str) {
			free(vis_str);
		}
	}
if (0) fprintf(stderr, "vis_str %s\n", vis_str ? vis_str : "notset");

	/* set up parameters for subwin or non-subwin cases: */

	if (! subwin) {
		/* full screen */
		window = rootwin;
		dpy_x = wdpy_x = DisplayWidth(dpy, scr);
		dpy_y = wdpy_y = DisplayHeight(dpy, scr);
		off_x = 0;
		off_y = 0;
		/* this may be overridden via visual_id below */
		default_visual = DefaultVisual(dpy, scr);
	} else {
		/* single window */
		XWindowAttributes attr;

		window = (Window) subwin;
		if (! XGetWindowAttributes(dpy, window, &attr)) {
			rfbLogEnable(1);
			rfbLog("invalid window: 0x%lx\n", window);
			X_UNLOCK;
			clean_up_exit(1);
		}
		dpy_x = wdpy_x = attr.width;
		dpy_y = wdpy_y = attr.height;

		subwin_bs = attr.backing_store;

		/* this may be overridden via visual_id below */
		default_visual = attr.visual;

		X_UNLOCK;
		set_offset();
		X_LOCK;
	}

	initialize_clipshift();

	/* initialize depth to reasonable value, visual_id may override */
	depth = DefaultDepth(dpy, scr);

if (0) fprintf(stderr, "DefaultDepth: %d  visial_id: %d\n", depth, (int) visual_id);

	if (visual_id) {
		int n;
		XVisualInfo vinfo_tmpl, *vinfo;

		/*
		 * we are in here from -visual or -overlay options
		 * visual_id and visual_depth were set in set_visual().
		 */

		vinfo_tmpl.visualid = visual_id; 
		vinfo = XGetVisualInfo(dpy, VisualIDMask, &vinfo_tmpl, &n);
		if (vinfo == NULL || n == 0) {
			rfbLogEnable(1);
			rfbLog("could not match visual_id: 0x%x\n",
			    (int) visual_id);
			X_UNLOCK;
			clean_up_exit(1);
		}
		default_visual = vinfo->visual;
		depth = vinfo->depth;
		if (visual_depth) {
			/* force it from -visual MooColor:NN */
			depth = visual_depth;
		}
		if (! quiet) {
			fprintf(stderr, " initialize_xdisplay_fb()\n");
			fprintf(stderr, " Visual*:    %p\n",
			    (void *) vinfo->visual);
			fprintf(stderr, " visualid:   0x%x\n",
			    (int) vinfo->visualid);
			fprintf(stderr, " screen:     %d\n", vinfo->screen);
			fprintf(stderr, " depth:      %d\n", vinfo->depth);
			fprintf(stderr, " class:      %d\n", vinfo->class);
			fprintf(stderr, " red_mask:   0x%08lx  %s\n",
			    vinfo->red_mask, bitprint(vinfo->red_mask, 32));
			fprintf(stderr, " green_mask: 0x%08lx  %s\n",
			    vinfo->green_mask, bitprint(vinfo->green_mask, 32));
			fprintf(stderr, " blue_mask:  0x%08lx  %s\n",
			    vinfo->blue_mask, bitprint(vinfo->blue_mask, 32));
			fprintf(stderr, " cmap_size:  %d\n",
			    vinfo->colormap_size);
			fprintf(stderr, " bits b/rgb: %d\n",
			    vinfo->bits_per_rgb);
			fprintf(stderr, "\n");
		}
		XFree_wr(vinfo);
	}

	if (! quiet) {
		rfbLog("Default visual ID: 0x%x\n",
		    (int) XVisualIDFromVisual(default_visual));
	}

	again:
	if (subwin) {
		int shift = 0;
		int subwin_x, subwin_y;
		int disp_x = DisplayWidth(dpy, scr);
		int disp_y = DisplayHeight(dpy, scr);
		Window twin;
		/* subwins can be a dicey if they are changing size... */
		trapped_xerror = 0;
		old_handler = XSetErrorHandler(trap_xerror);
		XTranslateCoordinates(dpy, window, rootwin, 0, 0, &subwin_x,
		    &subwin_y, &twin);

		if (subwin_x + wdpy_x > disp_x) {
			shift = 1;
			subwin_x = disp_x - wdpy_x - 3;
		}
		if (subwin_y + wdpy_y > disp_y) {
			shift = 1;
			subwin_y = disp_y - wdpy_y - 3;
		}
		if (subwin_x < 0) {
			shift = 1;
			subwin_x = 1;
		}
		if (subwin_y < 0) {
			shift = 1;
			subwin_y = 1;
		}

		if (shift) {
			XMoveWindow(dpy, window, subwin_x, subwin_y);
		}
		XMapRaised(dpy, window);
		XRaiseWindow(dpy, window);
		XFlush_wr(dpy);
	}
	try++;

	if (nofb) {
		/* 
		 * For -nofb we do not allocate the framebuffer, so we
		 * can save a few MB of memory. 
		 */
		fb = XCreateImage_wr(dpy, default_visual, depth, ZPixmap,
		    0, NULL, dpy_x, dpy_y, BitmapPad(dpy), 0);

	} else if (visual_id) {
		/*
		 * we need to call XCreateImage to supply the visual
		 */
		fb = XCreateImage_wr(dpy, default_visual, depth, ZPixmap,
		    0, NULL, dpy_x, dpy_y, BitmapPad(dpy), 0);
		fb->data = (char *) malloc(fb->bytes_per_line * fb->height);

	} else {
		fb = XGetImage_wr(dpy, window, 0, 0, dpy_x, dpy_y, AllPlanes,
		    ZPixmap);
		if (! quiet) {
			rfbLog("Read initial data from X display into"
			    " framebuffer.\n");
		}
	}

	if (subwin) {
		XSetErrorHandler(old_handler);
		if (trapped_xerror) {
		    rfbLog("trapped GetImage at SUBWIN creation.\n");
		    if (try < subwin_tries) {
			usleep(250 * 1000);
			if (!get_window_size(window, &wdpy_x, &wdpy_y)) {
				rfbLogEnable(1);
				rfbLog("could not get size of subwin "
				    "0x%lx\n", subwin);
				X_UNLOCK;
				clean_up_exit(1);
			}
			goto again;
		    }
		}
		trapped_xerror = 0;

	} else if (! fb && try == 1) {
		/* try once more */
		usleep(250 * 1000);
		goto again;
	}
	if (use_snapfb) {
		initialize_snap_fb();
	}
	X_UNLOCK;

	if (fb->bits_per_pixel == 24 && ! quiet) {
		rfbLog("warning: 24 bpp may have poor performance.\n");
	}
	return fb;
#endif	/* NO_X11 */
}

void parse_scale_string(char *str, double *factor_x, double *factor_y, int *scaling, int *blend,
    int *nomult4, int *pad, int *interpolate, int *numer, int *denom, int w_in, int h_in) {

	int m, n;
	char *p, *tstr;
	double f, f2;

	*factor_x = 1.0;
	*factor_y = 1.0;
	*scaling = 0;
	*blend = 1;
	*nomult4 = 0;
	*pad = 0;
	*interpolate = 0;
	*numer = 0, *denom = 0;

	if (str == NULL || str[0] == '\0') {
		return;
	}
	tstr = strdup(str);
	
	if ( (p = strchr(tstr, ':')) != NULL) {
		/* options */
		if (strstr(p+1, "nb") != NULL) {
			*blend = 0;
		}
		if (strstr(p+1, "fb") != NULL) {
			*blend = 2;
		}
		if (strstr(p+1, "n4") != NULL) {
			*nomult4 = 1;
		}
		if (strstr(p+1, "in") != NULL) {
			*interpolate = 1;
		}
		if (strstr(p+1, "pad") != NULL) {
			*pad = 1;
		}
		if (strstr(p+1, "nocr") != NULL) {
			/* global */
			scaling_copyrect = 0;
		} else if (strstr(p+1, "cr") != NULL) {
			/* global */
			scaling_copyrect = 1;
		}
		*p = '\0';
	}

	if (strchr(tstr, '.') != NULL) {
		double test, diff, eps = 1.0e-7;
		if (sscanf(tstr, "%lfx%lf", &f, &f2) == 2) {
			*factor_x = (double) f;
			*factor_y = (double) f2;
		} else if (sscanf(tstr, "%lf", &f) != 1) {
			rfbLogEnable(1);
			rfbLog("invalid -scale arg: %s\n", tstr);
			clean_up_exit(1);
		} else {
			*factor_x = (double) f;
			*factor_y = (double) f;
		}
		/* look for common fractions from small ints: */
		if (*factor_x == *factor_y) {
			for (n=2; n<=10; n++) {
				for (m=1; m<n; m++) {
					test = ((double) m)/ n;
					diff = *factor_x - test;
					if (-eps < diff && diff < eps) {
						*numer = m;
						*denom = n;
						break;
					
					}
				}
				if (*denom) {
					break;
				}
			}
			if (*factor_x < 0.01) {
				rfbLogEnable(1);
				rfbLog("-scale factor too small: %f\n", *factor_x);
				clean_up_exit(1);
			}
		}
	} else if (sscanf(tstr, "%dx%d", &m, &n) == 2 && w_in > 0 && h_in > 0) {
		*factor_x = ((double) m) / ((double) w_in);
		*factor_y = ((double) n) / ((double) h_in);
	} else {
		if (sscanf(tstr, "%d/%d", &m, &n) != 2) {
			if (sscanf(tstr, "%d", &m) != 1) {
				rfbLogEnable(1);
				rfbLog("invalid -scale arg: %s\n", tstr);
				clean_up_exit(1);
			} else {
				/* e.g. -scale 1 or -scale 2 */
				n = 1;
			}
		}
		if (n <= 0 || m <=0) {
			rfbLogEnable(1);
			rfbLog("invalid -scale arg: %s\n", tstr);
			clean_up_exit(1);
		}
		*factor_x = ((double) m)/ n;
		*factor_y = ((double) m)/ n;
		if (*factor_x < 0.01) {
			rfbLogEnable(1);
			rfbLog("-scale factor too small: %f\n", *factor_x);
			clean_up_exit(1);
		}
		*numer = m;
		*denom = n;
	}
	if (*factor_x == 1.0 && *factor_y == 1.0) {
		if (! quiet) {
			rfbLog("scaling disabled for factor %f %f\n", *factor_x, *factor_y);
		}
	} else {
		*scaling = 1;
	}
	free(tstr);
}

int parse_rotate_string(char *str, int *mode) {
	int m = ROTATE_NONE;
	if (str == NULL || !strcmp(str, "") || !strcmp(str, "0")) {
		m = ROTATE_NONE;
	} else if (!strcmp(str, "x")) {
		m = ROTATE_X;
	} else if (!strcmp(str, "y")) {
		m = ROTATE_Y;
	} else if (!strcmp(str, "xy") || !strcmp(str, "yx") ||
	    !strcmp(str,"+180") || !strcmp(str,"-180") || !strcmp(str,"180")) {
		m = ROTATE_XY;
	} else if (!strcmp(str, "+90") || !strcmp(str, "90")) {
		m = ROTATE_90;
	} else if (!strcmp(str, "+90x") || !strcmp(str, "90x")) {
		m = ROTATE_90X;
	} else if (!strcmp(str, "+90y") || !strcmp(str, "90y")) {
		m = ROTATE_90Y;
	} else if (!strcmp(str, "-90") || !strcmp(str, "270") ||
	    !strcmp(str, "+270")) {
		m = ROTATE_270;
	} else {
		rfbLog("invalid -rotate mode: %s\n", str);
	}
	if (mode) {
		*mode = m;
	}
	return m;
}

int scale_round(int len, double fac) {
	double eps = 0.000001;
	
	len = (int) (len * fac + eps);
	if (len < 1) {
		len = 1;
	}
	return len;
}

static void setup_scaling(int *width_in, int *height_in) {
	int width  = *width_in;
	int height = *height_in;

	parse_scale_string(scale_str, &scale_fac_x, &scale_fac_y, &scaling, &scaling_blend,
	    &scaling_nomult4, &scaling_pad, &scaling_interpolate,
	    &scale_numer, &scale_denom, *width_in, *height_in);

	if (scaling) {
		width  = scale_round(width,  scale_fac_x);
		height = scale_round(height, scale_fac_y);
		if (scale_denom && scaling_pad) {
			/* it is not clear this padding is useful anymore */
			rfbLog("width  %% denom: %d %% %d = %d\n", width,
			    scale_denom, width  % scale_denom);
			rfbLog("height %% denom: %d %% %d = %d\n", height,
			    scale_denom, height % scale_denom);
			if (width % scale_denom != 0) {
				int w = width;
				w += scale_denom - (w % scale_denom);
				if (!scaling_nomult4 && w % 4 != 0) {
					/* need to make mult of 4 as well */
					int c = 0;	
					while (w % 4 != 0 && c++ <= 5) {
						w += scale_denom;
					}
				}
				width = w;
				rfbLog("padded width  to: %d (mult of %d%s\n",
				    width, scale_denom, !scaling_nomult4 ?
				    " and 4)" : ")");
			}
			if (height % scale_denom != 0) {
				height += scale_denom - (height % scale_denom);
				rfbLog("padded height to: %d (mult of %d)\n",
				    height, scale_denom);
			}
		}
		if (!scaling_nomult4 && width % 4 != 0 && width > 2) {
			/* reset width to be multiple of 4 */
			int width0 = width;
			if ((width+1) % 4 == 0) {
				width = width+1;
			} else if ((width-1) % 4 == 0) {
				width = width-1;
			} else if ((width+2) % 4 == 0) {
				width = width+2;
			}
			rfbLog("reset scaled width %d -> %d to be a multiple of"
			    " 4 (to\n", width0, width);
			rfbLog("make vncviewers happy). use -scale m/n:n4 to "
			    "disable.\n");
		}
		scaled_x = width;
		scaled_y = height;

		*width_in  = width;
		*height_in = height;
	}
}

static void setup_rotating(void) {
	char *rs = rotating_str;

	rotating_cursors = 1;
	if (rs && strstr(rs, "nc:") == rs) {
		rs += strlen("nc:");
		rotating_cursors = 0;
	}

	rotating = parse_rotate_string(rs, NULL);
	if (! rotating) {
		rotating_cursors = 0;
	}

	if (rotating == ROTATE_90  || rotating == ROTATE_90X ||
	    rotating == ROTATE_90Y || rotating == ROTATE_270) {
		rotating_same = 0;
	} else {
		rotating_same = 1;
	}
}

static rfbBool set_xlate_wrapper(rfbClientPtr cl) {
	static int first = 1;
	if (first) {
		first = 0;
	} else if (ncache) {
		int save = ncache_xrootpmap;
		rfbLog("set_xlate_wrapper: clearing -ncache for new pixel format.\n");
		ncache_xrootpmap = 0;
		check_ncache(1, 0);
		ncache_xrootpmap = save;
	}
	return rfbSetTranslateFunction(cl);	
}

/*
 * initialize the rfb framebuffer/screen
 */
void initialize_screen(int *argc, char **argv, XImage *fb) {
	int have_masks = 0;
	int width  = fb->width;
	int height = fb->height;
	int create_screen = screen ? 0 : 1;
	int bits_per_color;
	int fb_bpp, fb_Bpl, fb_depth;
	
	bpp = fb->bits_per_pixel;

	fb_bpp   = (int) fb->bits_per_pixel;
	fb_Bpl   = (int) fb->bytes_per_line;
	fb_depth = (int) fb->depth;

	rfbLog("initialize_screen: fb_depth/fb_bpp/fb_Bpl %d/%d/%d\n", fb_depth,
	    fb_bpp, fb_Bpl);

	main_bytes_per_line = fb->bytes_per_line;

	if (cmap8to24) {
		if (raw_fb) {
			if (!quiet) rfbLog("disabling -8to24 mode"
			    " in raw_fb mode.\n");
			cmap8to24 = 0;
		} else if (depth == 24) {
			if (fb_bpp != 32)  {
				if (!quiet) rfbLog("disabling -8to24 mode:"
				    " bpp != 32 with depth == 24\n");
				cmap8to24 = 0;
			}
		} else if (depth <= 16) {
			/* need to cook up the screen fb to be depth 24 */
			fb_bpp = 32;
			fb_depth = 24;
		} else {
			if (!quiet) rfbLog("disabling -8to24 mode:"
			    " default depth not 16 or less\n");
			cmap8to24 = 0;
		}
	}

	setup_scaling(&width, &height);

	if (scaling) {
		rfbLog("scaling screen: %dx%d -> %dx%d\n", fb->width, fb->height, scaled_x, scaled_y);
		rfbLog("scaling screen: scale_fac_x=%.5f scale_fac_y=%.5f\n", scale_fac_x, scale_fac_y);

		rfb_bytes_per_line = (main_bytes_per_line / fb->width) * width;
	} else {
		rfb_bytes_per_line = main_bytes_per_line;
	}

	setup_rotating();

	if (rotating) {
		if (! rotating_same) {
			int t, b = main_bytes_per_line / fb->width;
			if (scaling) {
				rot_bytes_per_line = b * height;
			} else {
				rot_bytes_per_line = b * fb->height;
			}
			t = width;
			width = height;		/* The big swap... */
			height = t;
		} else {
			rot_bytes_per_line = rfb_bytes_per_line;
		}
	}

#ifndef NO_NCACHE
	if (ncache > 0 && !nofb) {
# ifdef MACOSX
		if (! raw_fb_str || macosx_console) {
# else
		if (! raw_fb_str) {
# endif
			
			char *new_fb;
			int sz = fb->height * fb->bytes_per_line;
			int ns = 1+ncache;

			if (ncache_xrootpmap) {
				ns++;
			}

			new_fb = (char *) calloc((size_t) (sz * ns), 1);
			if (fb->data) {
				memcpy(new_fb, fb->data, sz);
				free(fb->data);
			}
			fb->data = new_fb;
			fb->height *= (ns);
			height *= (ns);
			ncache0 = ncache;
		}
	}
#endif

	if (cmap8to24) {
		if (depth <= 8) {
			rfb_bytes_per_line *= 4;
			rot_bytes_per_line *= 4;
		} else if (depth <= 16) {
			rfb_bytes_per_line *= 2;
			rot_bytes_per_line *= 2;
		}
	}

	/*
	 * These are just hints wrt pixel format just to let
	 * rfbGetScreen/rfbNewFramebuffer proceed with reasonable
	 * defaults.  We manually set them in painful detail below.
	 */
	bits_per_color = guess_bits_per_color(fb_bpp);

	/* n.b. samplesPerPixel (set = 1 here) seems to be unused. */
	if (create_screen) {
		if (use_openssl) {
			openssl_init(0);
		} else if (use_stunnel) {
			setup_stunnel(0, argc, argv);
		}
		screen = rfbGetScreen(argc, argv, width, height,
		    bits_per_color, 1, fb_bpp/8);
		if (screen && http_dir) {
			http_connections(1);
		}
	} else {
		/* set set frameBuffer member below. */
		rfbLog("rfbNewFramebuffer(0x%x, 0x%x, %d, %d, %d, %d, %d)\n",
		    screen, NULL, width, height, bits_per_color, 1, fb_bpp/8);

		/* these are probably overwritten, but just to be safe: */
		screen->bitsPerPixel = fb_bpp;
		screen->depth = fb_depth;

		rfbNewFramebuffer(screen, NULL, width, height,
		    bits_per_color, 1, (int) fb_bpp/8);
	}
	if (! screen) {
		int i;
		rfbLogEnable(1);
		rfbLog("\n");
		rfbLog("failed to create rfb screen.\n");
		for (i=0; i< *argc; i++)  {
			rfbLog("\t[%d]  %s\n", i, argv[i]);
		}
		clean_up_exit(1);
	}

	if (create_screen && *argc != 1) {
		int i;
		rfbLogEnable(1);
		rfbLog("*** unrecognized option(s) ***\n");
		for (i=1; i< *argc; i++)  {
			rfbLog("\t[%d]  %s\n", i, argv[i]);
		}
		rfbLog("For a list of options run: x11vnc -opts\n");
		rfbLog("or for the full help: x11vnc -help\n");
		rfbLog("\n");
		rfbLog("Here is a list of removed or obsolete"
		    " options:\n");
		rfbLog("\n");
		rfbLog("removed: -hints, -nohints\n");
		rfbLog("removed: -cursorposall\n");
		rfbLog("removed: -nofilexfer, now the default.\n");
		rfbLog("\n");
		rfbLog("renamed: -old_copytile, use -onetile\n");
		rfbLog("renamed: -mouse,   use -cursor\n");
		rfbLog("renamed: -mouseX,  use -cursor X\n");
		rfbLog("renamed: -X,       use -cursor X\n");
		rfbLog("renamed: -nomouse, use -nocursor\n");
		rfbLog("renamed: -old_pointer, use -pointer_mode 1\n");
	
		clean_up_exit(1);
	}

	/* set up format from scratch: */
	if (rotating && ! rotating_same) {
		screen->paddedWidthInBytes = rot_bytes_per_line;
	} else {
		screen->paddedWidthInBytes = rfb_bytes_per_line;
	}
	screen->serverFormat.bitsPerPixel = fb_bpp;
	screen->serverFormat.depth = fb_depth;
	screen->serverFormat.trueColour = TRUE;

	screen->serverFormat.redShift   = 0;
	screen->serverFormat.greenShift = 0;
	screen->serverFormat.blueShift  = 0;
	screen->serverFormat.redMax     = 0;
	screen->serverFormat.greenMax   = 0;
	screen->serverFormat.blueMax    = 0;

	/* these main_* formats are used generally. */
	main_red_shift   = 0;
	main_green_shift = 0;
	main_blue_shift  = 0;
	main_red_max     = 0;
	main_green_max   = 0;
	main_blue_max    = 0;
	main_red_mask    = fb->red_mask;
	main_green_mask  = fb->green_mask;
	main_blue_mask   = fb->blue_mask;

	have_masks = ((fb->red_mask|fb->green_mask|fb->blue_mask) != 0);
	if (force_indexed_color) {
		have_masks = 0;
	}

	if (cmap8to24 && depth <= 16 && dpy) {
		XVisualInfo vinfo;
		vinfo.red_mask = 0;
		vinfo.green_mask = 0;
		vinfo.blue_mask = 0;
		/* more cooking up... */
		have_masks = 2;
		/* need to fetch TrueColor visual */
		X_LOCK;
		if (dpy
#if !NO_X11
		    && XMatchVisualInfo(dpy, scr, 24, TrueColor, &vinfo)
#endif
		    ) {
			main_red_mask   = vinfo.red_mask;
			main_green_mask = vinfo.green_mask;
			main_blue_mask  = vinfo.blue_mask;
		} else if (fb->byte_order == LSBFirst) {
			main_red_mask   = 0x00ff0000;
			main_green_mask = 0x0000ff00;
			main_blue_mask  = 0x000000ff;
		} else {
			main_red_mask   = 0x000000ff;
			main_green_mask = 0x0000ff00;
			main_blue_mask  = 0x00ff0000;
		}
		X_UNLOCK;
	}

	if (raw_fb_str && raw_fb_pixfmt) {
		char *fmt = strdup(raw_fb_pixfmt);
		uppercase(fmt);
		if (strstr(fmt, "GREY")) {
			set_greyscale_colormap();
		} else if (strstr(fmt, "HI240")) {
			set_hi240_colormap();
		} else {
			unset_colormap();
		}
		free(fmt);
	}

	if (! have_masks && screen->serverFormat.bitsPerPixel <= 16
	    && dpy && CellsOfScreen(ScreenOfDisplay(dpy, scr))) {
		/* indexed color. we let 1 <= bpp <= 16, but it is normally 8 */
		if (!quiet) {
			rfbLog("\n");
			rfbLog("X display %s is %dbpp indexed color, depth=%d\n",
			    DisplayString(dpy), screen->serverFormat.bitsPerPixel,
			    screen->serverFormat.depth);
			if (! flash_cmap && ! overlay) {
				rfbLog("\n");
				rfbLog("In 8bpp PseudoColor mode if you "
				    "experience color\n");
				rfbLog("problems you may want to enable "
				    "following the\n");
				rfbLog("changing colormap by using the "
				    "-flashcmap option.\n");
				rfbLog("\n");
			}
		}
		if (screen->serverFormat.depth < 8) {
			rfbLog("resetting serverFormat.depth %d -> 8.\n",
			    screen->serverFormat.depth);
			rfbLog("resetting serverFormat.bpp   %d -> 8.\n",
			    screen->serverFormat.bitsPerPixel);
			screen->serverFormat.depth = 8;
			screen->serverFormat.bitsPerPixel = 8;
		}
		if (screen->serverFormat.depth > 8) {
			rfbLog("WARNING: indexed color depth > 8, Tight encoding will crash.\n");
		}

		screen->serverFormat.trueColour = FALSE;
		indexed_color = 1;
		set_colormap(1);
		debug_colormap(fb);
	} else {
		/* 
		 * general case, we call it truecolor, but could be direct
		 * color, static color, etc....
		 */
		if (! quiet) {
			if (raw_fb) {
				rfbLog("\n");
				rfbLog("Raw fb at addr %p is %dbpp depth=%d "
				    "true color\n", raw_fb_addr,
				    fb_bpp, fb_depth);
			} else if (! dpy) {
				;
			} else if (have_masks == 2) {
				rfbLog("\n");
				rfbLog("X display %s is %dbpp depth=%d indexed "
				    "color (-8to24 mode)\n", DisplayString(dpy),
				    fb->bits_per_pixel, fb->depth);
			} else {
				rfbLog("\n");
				rfbLog("X display %s is %dbpp depth=%d true "
				    "color\n", DisplayString(dpy),
				    fb_bpp, fb_depth);
			}
		}

		indexed_color = 0;

		if (!have_masks) {
			int M, B = bits_per_color;

			if (3*B > fb_bpp) B--;
			if (B < 1) B = 1;
			M = (1 << B) - 1;

			rfbLog("WARNING: all TrueColor RGB masks are zero!\n");
			rfbLog("WARNING: cooking something up for VNC fb...  B=%d M=%d\n", B, M);
			main_red_mask   = M;
			main_green_mask = M;
			main_blue_mask  = M;
			main_red_mask   = main_red_mask   << (2*B);
			main_green_mask = main_green_mask << (1*B);
			main_blue_mask  = main_blue_mask  << (0*B);
			fprintf(stderr, " red_mask:   0x%08lx  %s\n", main_red_mask,
			    bitprint(main_red_mask, 32));
			fprintf(stderr, " green_mask: 0x%08lx  %s\n", main_green_mask,
			    bitprint(main_green_mask, 32));
			fprintf(stderr, " blue_mask:  0x%08lx  %s\n", main_blue_mask,
			    bitprint(main_blue_mask, 32));
		}

		/* convert masks to bit shifts and max # colors */
		if (main_red_mask) {
			while (! (main_red_mask
			    & (1 << screen->serverFormat.redShift))) {
				    screen->serverFormat.redShift++;
			}
		}
		if (main_green_mask) {
			while (! (main_green_mask
			    & (1 << screen->serverFormat.greenShift))) {
				    screen->serverFormat.greenShift++;
			}
		}
		if (main_blue_mask) {
			while (! (main_blue_mask
			    & (1 << screen->serverFormat.blueShift))) {
				    screen->serverFormat.blueShift++;
			}
		}
		screen->serverFormat.redMax
		    = main_red_mask   >> screen->serverFormat.redShift;
		screen->serverFormat.greenMax
		    = main_green_mask >> screen->serverFormat.greenShift;
		screen->serverFormat.blueMax
		    = main_blue_mask  >> screen->serverFormat.blueShift;

		main_red_max     = screen->serverFormat.redMax;
		main_green_max   = screen->serverFormat.greenMax;
		main_blue_max    = screen->serverFormat.blueMax;

		main_red_shift   = screen->serverFormat.redShift;
		main_green_shift = screen->serverFormat.greenShift;
		main_blue_shift  = screen->serverFormat.blueShift;
	}

#if !SMALL_FOOTPRINT
	if (verbose) {
		fprintf(stderr, "\n");
		fprintf(stderr, "FrameBuffer Info:\n");
		fprintf(stderr, " width:            %d\n", fb->width);
		fprintf(stderr, " height:           %d\n", fb->height);
		fprintf(stderr, " scaled_width:     %d\n", width);
		fprintf(stderr, " scaled_height:    %d\n", height);
		fprintf(stderr, " indexed_color:    %d\n", indexed_color);
		fprintf(stderr, " bits_per_pixel:   %d\n", fb->bits_per_pixel);
		fprintf(stderr, " depth:            %d\n", fb->depth);
		fprintf(stderr, " red_mask:   0x%08lx  %s\n", fb->red_mask,
		    bitprint(fb->red_mask, 32));
		fprintf(stderr, " green_mask: 0x%08lx  %s\n", fb->green_mask,
		    bitprint(fb->green_mask, 32));
		fprintf(stderr, " blue_mask:  0x%08lx  %s\n", fb->blue_mask,
		    bitprint(fb->blue_mask, 32));
		if (cmap8to24) {
			fprintf(stderr, " 8to24 info:\n");
			fprintf(stderr, " fb_bpp:           %d\n", fb_bpp);
			fprintf(stderr, " fb_depth:         %d\n", fb_depth);
			fprintf(stderr, " red_mask:   0x%08lx  %s\n", main_red_mask,
			    bitprint(main_red_mask, 32));
			fprintf(stderr, " green_mask: 0x%08lx  %s\n", main_green_mask,
			    bitprint(main_green_mask, 32));
			fprintf(stderr, " blue_mask:  0x%08lx  %s\n", main_blue_mask,
			    bitprint(main_blue_mask, 32));
		}
		fprintf(stderr, " red:   max: %3d  shift: %2d\n",
			main_red_max, main_red_shift);
		fprintf(stderr, " green: max: %3d  shift: %2d\n",
			main_green_max, main_green_shift);
		fprintf(stderr, " blue:  max: %3d  shift: %2d\n",
			main_blue_max, main_blue_shift);
		fprintf(stderr, " mainfb_bytes_per_line: %d\n",
			main_bytes_per_line);
		fprintf(stderr, " rfb_fb_bytes_per_line: %d\n",
			rfb_bytes_per_line);
		fprintf(stderr, " rot_fb_bytes_per_line: %d\n",
			rot_bytes_per_line);
		fprintf(stderr, " raw_fb_bytes_per_line: %d\n",
			raw_fb_bytes_per_line);
		switch(fb->format) {
		case XYBitmap:
			fprintf(stderr, " format:     XYBitmap\n"); break;
		case XYPixmap:
			fprintf(stderr, " format:     XYPixmap\n"); break;
		case ZPixmap:
			fprintf(stderr, " format:     ZPixmap\n"); break;
		default:
			fprintf(stderr, " format:     %d\n", fb->format); break;
		}
		switch(fb->byte_order) {
		case LSBFirst:
			fprintf(stderr, " byte_order: LSBFirst\n"); break;
		case MSBFirst:
			fprintf(stderr, " byte_order: MSBFirst\n"); break;
		default:
			fprintf(stderr, " byte_order: %d\n", fb->byte_order);
			break;
		}
		fprintf(stderr, " bitmap_pad:  %d\n", fb->bitmap_pad);
		fprintf(stderr, " bitmap_unit: %d\n", fb->bitmap_unit);
		switch(fb->bitmap_bit_order) {
		case LSBFirst:
			fprintf(stderr, " bitmap_bit_order: LSBFirst\n"); break;
		case MSBFirst:
			fprintf(stderr, " bitmap_bit_order: MSBFirst\n"); break;
		default:
			fprintf(stderr, " bitmap_bit_order: %d\n",
			fb->bitmap_bit_order); break;
		}
	}
	if (overlay && ! quiet) {
		rfbLog("\n");
		rfbLog("Overlay mode enabled:  If you experience color\n");
		rfbLog("problems when popup menus are on the screen, try\n");
		rfbLog("disabling SaveUnders in your X server, one way is\n");
		rfbLog("to start the X server with the '-su' option, e.g.:\n");
		rfbLog("Xsun -su ... see Xserver(1), xinit(1) for more info.\n");
		rfbLog("\n");
	}
#endif
	/* nofb is for pointer/keyboard only handling.  */
	if (nofb) {
		main_fb = NULL;
		rfb_fb = main_fb;
		cmap8to24_fb = NULL;
		rot_fb = NULL;
		screen->displayHook = nofb_hook;
	} else {
		main_fb = fb->data;
		rfb_fb = NULL;
		cmap8to24_fb = NULL;
		rot_fb = NULL;

		if (cmap8to24) {
			int n = main_bytes_per_line * fb->height;
			if (depth <= 8) {
				n *= 4;
			} else if (depth <= 16) {
				n *= 2;
			}
			cmap8to24_fb = (char *) malloc(n);
			memset(cmap8to24_fb, 0, n);
		}

		if (rotating) {
			int n = rot_bytes_per_line * height;
			rot_fb = (char *) malloc(n);
			memset(rot_fb, 0, n);
		}

		if (scaling) {
			int n = rfb_bytes_per_line * height;

			if (rotating && ! rotating_same) {
				n = rot_bytes_per_line * height;
			}

			rfb_fb = (char *) malloc(n);
			memset(rfb_fb, 0, n);

		} else if (cmap8to24) {
			rfb_fb = cmap8to24_fb;	
		} else {
			rfb_fb = main_fb;
		}
	}
	if (rot_fb) {
		screen->frameBuffer = rot_fb;
	} else {
		screen->frameBuffer = rfb_fb;
	}
	if (verbose) {
		fprintf(stderr, " rfb_fb:      %p\n", rfb_fb);
		fprintf(stderr, " main_fb:     %p\n", main_fb);
		fprintf(stderr, " 8to24_fb:    %p\n", cmap8to24_fb);
		fprintf(stderr, " rot_fb:      %p\n", rot_fb);
		fprintf(stderr, " snap_fb:     %p\n", snap_fb);
		fprintf(stderr, " raw_fb:      %p\n", raw_fb);
		fprintf(stderr, " fake_fb:     %p\n", fake_fb);
		fprintf(stderr, "\n");
	}

	/* may need, bpp, main_red_max, etc. */
	parse_wireframe();
	parse_scroll_copyrect();

	setup_cursors_and_push();

	if (scaling || rotating || cmap8to24) {
		mark_rect_as_modified(0, 0, dpy_x, dpy_y, 0);
	}

	if (! create_screen) {
		rfbClientIteratorPtr iter;
		rfbClientPtr cl;

		/* 
		 * since bits_per_color above may have been approximate,
		 * try to reset the individual translation tables...
		 * we do not seem to need this with rfbGetScreen()...
		 */
		if (!quiet) rfbLog("calling setTranslateFunction()...\n");
		iter = rfbGetClientIterator(screen);
		while ((cl = rfbClientIteratorNext(iter)) != NULL) {
			screen->setTranslateFunction(cl);
		}
		rfbReleaseClientIterator(iter);
		if (!quiet) rfbLog("  done.\n");
		do_copy_screen = 1;
		
		/* done for framebuffer change case */
		return;
	}

	/*
	 * the rest is screen server initialization, etc, only needed
	 * at screen creation time.
	 */

	/* event callbacks: */
	screen->newClientHook = new_client;
	screen->kbdAddEvent = keyboard;
	screen->ptrAddEvent = pointer;
	screen->setXCutText = xcut_receive;
	screen->setTranslateFunction = set_xlate_wrapper;

	screen->kbdReleaseAllKeys = kbd_release_all_keys; 
	screen->setSingleWindow = set_single_window; 
	screen->setServerInput = set_server_input; 
	screen->setTextChat = set_text_chat; 
	screen->getFileTransferPermission = get_file_transfer_permitted; 

	/* called from inetd, we need to treat stdio as our socket */
	if (inetd && use_openssl) {
		/* accept_openssl() called later */
		screen->port = 0;
	} else if (inetd) {
		int fd = dup(0);
		if (fd < 0) {
			rfbLogEnable(1);
			rfbErr("dup(0) = %d failed.\n", fd);
			rfbLogPerror("dup");
			clean_up_exit(1);
		}
		fclose(stdin);
		fclose(stdout);
		/* we keep stderr for logging */
		screen->inetdSock = fd;
		screen->port = 0;

	} else if (! got_rfbport && auto_port > 0) {
		int lport = find_free_port(auto_port, auto_port+200);
		screen->autoPort = FALSE;
		screen->port = lport;
	} else if (! got_rfbport) {
		screen->autoPort = TRUE;
	} else if (got_rfbport && got_rfbport_val == 0) {
		screen->autoPort = FALSE;
		screen->port = 0;
	}

	if (! got_nevershared && ! got_alwaysshared) {
		if (shared) {
			screen->alwaysShared = TRUE;
		} else {
			screen->neverShared = TRUE;
		}
		screen->dontDisconnect = TRUE;
	}
	if (! got_deferupdate) {
		screen->deferUpdateTime = defer_update;
	} else {
		defer_update = screen->deferUpdateTime;
	}

	rfbInitServer(screen);

	if (use_openssl) {
		openssl_port();
		if (https_port_num >= 0) {
			https_port();
		}
	}

	install_passwds();
}

#define DO_AVAHI \
	if (avahi) { \
		avahi_initialise(); \
		avahi_advertise(vnc_desktop_name, host, lport); \
		usleep(1000*1000); \
	}

void announce(int lport, int ssl, char *iface) {
	
	char *host = this_host();
	char *tvdt;

	if (! ssl) {
		tvdt = "The VNC desktop is:     ";
	} else {
		if (enc_str && !strcmp(enc_str, "none")) {
			tvdt = "The VNC desktop is:     ";
		} else if (enc_str) {
			tvdt = "The ENC VNC desktop is: ";
		} else {
			tvdt = "The SSL VNC desktop is: ";
		}
	}

	if (iface != NULL && *iface != '\0' && strcmp(iface, "any")) {
		host = iface;
	}
	if (host != NULL) {
		/* note that vncviewer special cases 5900-5999 */
		int sz = 256;
		if (inetd) {
			;	/* should not occur (port) */
		} else if (quiet) {
			if (lport >= 5900) {
				snprintf(vnc_desktop_name, sz, "%s:%d",
				    host, lport - 5900);
				DO_AVAHI
				fprintf(stderr, "\n%s %s\n", tvdt,
				    vnc_desktop_name);
			} else {
				snprintf(vnc_desktop_name, sz, "%s:%d",
				    host, lport);
				DO_AVAHI
				fprintf(stderr, "\n%s %s\n", tvdt,
				    vnc_desktop_name);
			}
		} else if (lport >= 5900) {
			snprintf(vnc_desktop_name, sz, "%s:%d",
			    host, lport - 5900);
			DO_AVAHI
			fprintf(stderr, "\n%s %s\n", tvdt, vnc_desktop_name);
			if (lport >= 6000) {
				rfbLog("possible aliases:  %s:%d, "
				    "%s::%d\n", host, lport,
				    host, lport);
			}
		} else {
			snprintf(vnc_desktop_name, sz, "%s:%d",
			    host, lport);
			DO_AVAHI
			fprintf(stderr, "\n%s %s\n", tvdt, vnc_desktop_name);
			rfbLog("possible alias:    %s::%d\n",
			    host, lport);
		}
	}
}

static void announce_http(int lport, int ssl, char *iface) {
	
	char *host = this_host();
	char *jvu;

	if (enc_str && !strcmp(enc_str, "none")) {
		jvu = "Java viewer URL:         http";
	} else if (ssl == 1) {
		jvu = "Java SSL viewer URL:     https";
	} else if (ssl == 2) {
		jvu = "Java SSL viewer URL:     http";
	} else {
		jvu = "Java viewer URL:         http";
	}

	if (iface != NULL && *iface != '\0' && strcmp(iface, "any")) {
		host = iface;
	}
	if (host != NULL) {
		if (! inetd) {
			fprintf(stderr, "%s://%s:%d/\n", jvu, host, lport);
			if (screen && enc_str && !strcmp(enc_str, "none")) {
				fprintf(stderr, "%s://%s:%d/\n", jvu, host, screen->port);
			}
		}
	}
}

void set_vnc_desktop_name(void) {
	sprintf(vnc_desktop_name, "unknown");
	if (inetd) {
		sprintf(vnc_desktop_name, "%s/inetd-no-further-clients",
		    this_host());
	}
	if (screen->port) {

		if (! quiet) {
			if (screen->httpListenSock > -1 && screen->httpPort) {
				rfbLog("\n");
				rfbLog("The URLs printed out below ('Java ... viewer URL') can\n");
				rfbLog("be used for Java enabled Web browser connections.\n");
				if (enc_str && !strcmp(enc_str, "none")) {
					;
				} else if (use_openssl || stunnel_port) {
					rfbLog("Here are some additional possibilities:\n");
					rfbLog("\n");
					rfbLog("https://host:port/proxy.vnc (MUST be used if Web Proxy used)\n");
					rfbLog("\n");
					rfbLog("https://host:port/ultra.vnc (Use UltraVNC Java Viewer)\n");
					rfbLog("https://host:port/ultraproxy.vnc (Web Proxy with UltraVNC)\n");
					rfbLog("https://host:port/ultrasigned.vnc (Signed UltraVNC Filexfer)\n");
					rfbLog("\n");
					rfbLog("Where you replace \"host:port\" with that printed below, or\n");
					rfbLog("whatever is needed to reach the host e.g. Internet IP number\n");
				}
			}
			rfbLog("\n");
		}

		if (use_openssl) {
			announce(screen->port, 1, listen_str);
		} else {
			announce(screen->port, 0, listen_str);
		}
		if (stunnel_port) {
			announce(stunnel_port, 1, NULL);
		}
		if (screen->httpListenSock > -1 && screen->httpPort) {
			if (use_openssl) {
				if (enc_str && !strcmp(enc_str, "none")) {
					;
				} else {
					announce_http(screen->port, 1, listen_str);
				}
				if (https_port_num >= 0) {
					announce_http(https_port_num, 1,
					    listen_str);
				}
				announce_http(screen->httpPort, 2, listen_str);
			} else if (use_stunnel) {
				announce_http(screen->httpPort, 2, listen_str);
			} else {
				announce_http(screen->httpPort, 0, listen_str);
			}
		}
		
		fflush(stderr);	
		if (inetd) {
			;	/* should not occur (port != 0) */
		} else {
			fprintf(stdout, "PORT=%d\n", screen->port);
			if (stunnel_port) {
				fprintf(stdout, "SSLPORT=%d\n", stunnel_port);
			} else if (use_openssl) {
				if (enc_str && !strcmp(enc_str, "none")) {
					;
				} else if (enc_str) {
					fprintf(stdout, "ENCPORT=%d\n", screen->port);
				} else {
					fprintf(stdout, "SSLPORT=%d\n", screen->port);
				}
			}
			fflush(stdout);	
			if (flagfile) {
				FILE *flag = fopen(flagfile, "w");
				if (flag) {
					fprintf(flag, "PORT=%d\n",screen->port);
					if (stunnel_port) {
						fprintf(flag, "SSL_PORT=%d\n",
						    stunnel_port);
					}
					fflush(flag);	
					fclose(flag);
				} else {
					rfbLog("could not open flag file: %s\n",
					    flagfile);
				}
			}
			if (rm_flagfile) {
				int create = 0;
				struct stat sb;
				if (strstr(rm_flagfile, "create:") == rm_flagfile) {
					char *s = rm_flagfile;
					create = 1;
					rm_flagfile = strdup(rm_flagfile + strlen("create:"));
					free(s);
				}
				if (strstr(rm_flagfile, "nocreate:") == rm_flagfile) {
					char *s = rm_flagfile;
					create = 0;
					rm_flagfile = strdup(rm_flagfile + strlen("nocreate:"));
					free(s);
				} else if (stat(rm_flagfile, &sb) != 0) {
					create = 1;
				}
				if (create) {
					FILE *flag = fopen(rm_flagfile, "w");
					if (flag) {
						fprintf(flag, "%d\n", getpid());
						fclose(flag);
					}
				}
			}
		}
		fflush(stdout);	
	}
}

static void check_cursor_changes(void) {
	static double last_push = 0.0;

	if (unixpw_in_progress) return;

	cursor_changes += check_x11_pointer();

	if (cursor_changes) {
		double tm, max_push = 0.125, multi_push = 0.01, wait = 0.02;
		int cursor_shape, dopush = 0, link, latency, netrate;

		if (! all_clients_initialized()) {
			/* play it safe */
			return;
		}

		if (0) cursor_shape = cursor_shape_updates_clients(screen);
	
		dtime0(&tm);
		link = link_rate(&latency, &netrate);
		if (link == LR_DIALUP) {
			max_push = 0.2;
			wait = 0.05;
		} else if (link == LR_BROADBAND) {
			max_push = 0.075;
			wait = 0.05;
		} else if (link == LR_LAN) {
			max_push = 0.01;
		} else if (latency < 5 && netrate > 200) {
			max_push = 0.01;
		}
		
		if (tm > last_push + max_push) {
			dopush = 1;
		} else if (cursor_changes > 1 && tm > last_push + multi_push) {
			dopush = 1;
		}

		if (dopush) { 
			mark_rect_as_modified(0, 0, 1, 1, 1);
			fb_push_wait(wait, FB_MOD);
			last_push = tm;
		} else {
			rfbPE(0);
		}
	}
	cursor_changes = 0;
}

/*
 * These watch_loop() releated were moved from x11vnc.c so we could try
 * to remove -O2 from its compilation.  TDB new file, e.g. watch.c.
 */

static void check_filexfer(void) {
	static time_t last_check = 0;
	rfbClientIteratorPtr iter;
	rfbClientPtr cl;
	int transferring = 0; 
	
	if (time(NULL) <= last_check) {
		return;
	}

#if 0
	if (getenv("NOFT")) {
		return;
	}
#endif

	iter = rfbGetClientIterator(screen);
	while( (cl = rfbClientIteratorNext(iter)) ) {
		if (cl->fileTransfer.receiving) {
			transferring = 1;
			break;
		}
		if (cl->fileTransfer.sending) {
			transferring = 1;
			break;
		}
	}
	rfbReleaseClientIterator(iter);

	if (transferring) {
		double start = dnow();
		while (dnow() < start + 0.5) {
			rfbCFD(5000);
			rfbCFD(1000);
			rfbCFD(0);
		}
	} else {
		last_check = time(NULL);
	}
}

static void record_last_fb_update(void) {
	static int rbs0 = -1;
	static time_t last_call = 0;
	time_t now = time(NULL);
	int rbs = -1;
	rfbClientIteratorPtr iter;
	rfbClientPtr cl;

	if (last_fb_bytes_sent == 0) {
		last_fb_bytes_sent = now;
		last_call = now;
	}

	if (now <= last_call + 1) {
		/* check every second or so */
		return;
	}

	if (unixpw_in_progress) return;

	last_call = now;

	if (! screen) {
		return;
	}

	iter = rfbGetClientIterator(screen);
	while( (cl = rfbClientIteratorNext(iter)) ) {
#if 0
		rbs += cl->rawBytesEquivalent;
#else
#if LIBVNCSERVER_HAS_STATS
		rbs += rfbStatGetSentBytesIfRaw(cl);
#endif
#endif
	}
	rfbReleaseClientIterator(iter);

	if (rbs != rbs0) {
		rbs0 = rbs;
		if (debug_tiles > 1) {
			fprintf(stderr, "record_last_fb_update: %d %d\n",
			    (int) now, (int) last_fb_bytes_sent);
		}
		last_fb_bytes_sent = now;
	}
}


static int choose_delay(double dt) {
	static double t0 = 0.0, t1 = 0.0, t2 = 0.0, now; 
	static int x0, y0, x1, y1, x2, y2, first = 1;
	int dx0, dy0, dx1, dy1, dm, i, msec = waitms;
	double cut1 = 0.15, cut2 = 0.075, cut3 = 0.25;
	double bogdown_time = 0.25, bave = 0.0;
	int bogdown = 1, bcnt = 0;
	int ndt = 8, nave = 3;
	double fac = 1.0;
	static int db = 0, did_set_defer = 0;
	static double dts[8];
	static int link = LR_UNSET, latency = -1, netrate = -1;
	static double last_link = 0.0;

	if (screen && did_set_defer) {
		/* reset defer in case we changed it */
		screen->deferUpdateTime = defer_update;
	}
	if (waitms == 0) {
		return waitms;
	}
	if (nofb) {
		return waitms;
	}

	if (first) {
		for(i=0; i<ndt; i++) {
			dts[i] = 0.0;
		}
		if (getenv("DEBUG_DELAY")) {
			db = atoi(getenv("DEBUG_DELAY"));
		}
		if (getenv("SET_DEFER")) {
			set_defer = atoi(getenv("SET_DEFER"));
		}
		first = 0;
	}

	now = dnow();

	if (now > last_link + 30.0 || link == LR_UNSET) {
		link = link_rate(&latency, &netrate);
		last_link = now;
	}

	/*
	 * first check for bogdown, e.g. lots of activity, scrolling text
	 * from command output, etc.
	 */
	if (nap_ok) {
		dt = 0.0;
	}
	if (! wait_bog) {
		bogdown = 0;

	} else if (button_mask || now < last_keyboard_time + 2*bogdown_time) {
		/*
		 * let scrolls & keyboard input through the normal way
		 * otherwise, it will likely just annoy them.
		 */
		bogdown = 0;

	} else if (dt > 0.0) {
		/*
		 * inspect recent dt's:
		 * 0 1 2 3 4 5 6 7 dt
		 *             ^ ^ ^
		 */
		for (i = ndt - (nave - 1); i < ndt; i++) {
			bave += dts[i];
			bcnt++;
			if (dts[i] < bogdown_time) {
				bogdown = 0;
				break;
			}
		}
		bave += dt;
		bcnt++;
		bave = bave / bcnt;
		if (dt < bogdown_time) {
			bogdown = 0;
		}
	} else {
		bogdown = 0;
	}
	/* shift for next time */
	for (i = 0; i < ndt-1; i++) {
		dts[i] = dts[i+1];
	}
	dts[ndt-1] = dt;

if (0 && dt > 0.0) fprintf(stderr, "dt: %.5f %.4f\n", dt, dnowx());
	if (bogdown) {
		if (use_xdamage) {
			/* DAMAGE can queue ~1000 rectangles for a scroll */
			clear_xdamage_mark_region(NULL, 0);
		}
		msec = (int) (1000 * 1.75 * bave);
		if (dts[ndt - nave - 1] > 0.75 * bave) {
			msec = 1.5 * msec;
			set_xdamage_mark(0, 0, dpy_x, dpy_y);
		}
		if (msec > 1500) {
			msec = 1500;
		}
		if (msec < waitms) {
			msec = waitms;
		}
		db = (db || debug_tiles);
		if (db) fprintf(stderr, "bogg[%d] %.3f %.3f %.3f %.3f\n",
		    msec, dts[ndt-4], dts[ndt-3], dts[ndt-2], dts[ndt-1]);

		return msec;
	}

	/* next check for pointer motion, keystrokes, to speed up */
	t2 = dnow();
	x2 = cursor_x;
	y2 = cursor_y;

	dx0 = nabs(x1 - x0);
	dy0 = nabs(y1 - y0);
	dx1 = nabs(x2 - x1);
	dy1 = nabs(y2 - y1);

	/* bigger displacement for most recent dt: */
	if (dx1 > dy1) {
		dm = dx1;
	} else {
		dm = dy1;
	}

	if ((dx0 || dy0) && (dx1 || dy1)) {
		/* if mouse moved the previous two times: */
		if (t2 < t0 + cut1 || t2 < t1 + cut2 || dm > 20) {
			/*
			 * if within 0.15s(0) or 0.075s(1) or mouse
			 * moved > 20pixels, set and bump up the cut
			 * down factor.
			 */
			fac = wait_ui * 1.5;
		} else if ((dx1 || dy1) && dm > 40) {
			fac = wait_ui;
		} else {
			/* still 1.0? */
			if (db > 1) fprintf(stderr, "wait_ui: still 1.0\n");
		}
	} else if ((dx1 || dy1) && dm > 40) {
		/* if mouse moved > 40 last time: */
		fac = wait_ui;
	}

	if (fac == 1.0 && t2 < last_keyboard_time + cut3) {
		/* if typed in last 0.25s set wait_ui */
		fac = wait_ui;
	}
	if (fac != 1.0) {
		if (link == LR_LAN || latency <= 3) {
			fac *= 1.5;
		}
	}

	msec = (int) (((double) waitms) / fac);
	if (msec == 0) {
		msec = 1;
	}

	if (set_defer && fac != 1.0 && screen) {
		/* this is wait_ui mode, set defer to match wait: */
		if (set_defer >= 1) {
			screen->deferUpdateTime = msec;
		} else if (set_defer <= -1) {
			screen->deferUpdateTime = 0;
		}
		if (nabs(set_defer) == 2) {
			urgent_update = 1;
		}
		did_set_defer = 1;
	}

	x0 = x1;
	y0 = y1;
	t0 = t1;

	x1 = x2;
	y1 = y2;
	t1 = t2;

	if (db > 1) fprintf(stderr, "wait: %2d defer[%02d]: %2d\n", msec, defer_update, screen->deferUpdateTime);

	return msec;
}

/*
 * main x11vnc loop: polls, checks for events, iterate libvncserver, etc.
 */
void watch_loop(void) {
	int cnt = 0, tile_diffs = 0, skip_pe = 0, wait;
	double tm, dtr, dt = 0.0;
	time_t start = time(NULL);

	if (use_threads) {
		rfbRunEventLoop(screen, -1, TRUE);
	}

	while (1) {
		char msg[] = "new client: %s taking unixpw client off hold.\n";

		got_user_input = 0;
		got_pointer_input = 0;
		got_local_pointer_input = 0;
		got_pointer_calls = 0;
		got_keyboard_input = 0;
		got_keyboard_calls = 0;
		urgent_update = 0;

		x11vnc_current = dnow();

		if (! use_threads) {
			dtime0(&tm);
			if (! skip_pe) {
				if (unixpw_in_progress) {
					rfbClientPtr cl = unixpw_client;
					if (cl && cl->onHold) {
						rfbLog(msg, cl->host);
						unixpw_client->onHold = FALSE;
					}
				} else {
					measure_send_rates(1);
				}

				unixpw_in_rfbPE = 1;

				/*
				 * do a few more since a key press may
				 * have induced a small change we want to
				 * see quickly (just 1 rfbPE will likely
				 * only process the subsequent "up" event)
				 */
				if (tm < last_keyboard_time + 0.16) {
					rfbPE(0);
					rfbPE(0);
					rfbPE(-1);
					rfbPE(0);
					rfbPE(0);
				} else {
					rfbPE(-1);
				}
				if (x11vnc_current < last_new_client + 0.5) {
					urgent_update = 1;
				}

				unixpw_in_rfbPE = 0;

				if (unixpw_in_progress) {
					/* rfbPE loop until logged in. */
					skip_pe = 0;
					check_new_clients();
					continue;
				} else {
					measure_send_rates(0);
					fb_update_sent(NULL);
				}
			} else {
				if (unixpw_in_progress) {
					skip_pe = 0;
					check_new_clients();
					continue;
				}
			}
			dtr = dtime(&tm);

			if (! cursor_shape_updates) {
				/* undo any cursor shape requests */
				disable_cursor_shape_updates(screen);
			}
			if (screen && screen->clientHead) {
				int ret = check_user_input(dt, dtr,
				    tile_diffs, &cnt);
				/* true: loop back for more input */
				if (ret == 2) {
					skip_pe = 1;
				}
				if (ret) {
					if (debug_scroll) fprintf(stderr, "watch_loop: LOOP-BACK: %d\n", ret);
					continue;
				}
			}
			/* watch for viewonly input piling up: */
			if ((got_pointer_calls > got_pointer_input) ||
			    (got_keyboard_calls > got_keyboard_input)) {
				eat_viewonly_input(10, 3);
			}
		} else {
			/* -threads here. */
			if (wireframe && button_mask) {
				check_wireframe();
			}
		}
		skip_pe = 0;

		if (shut_down) {
			clean_up_exit(0);
		}

		if (unixpw_in_progress) {
			check_new_clients();
			continue;
		}

		if (! urgent_update) {
			if (do_copy_screen) {
				do_copy_screen = 0;
				copy_screen();
			}

			check_new_clients();
			check_ncache(0, 0);
			check_xevents(0);
			check_autorepeat();
			check_pm();
			check_filexfer();
			check_keycode_state();
			check_connect_inputs();
			check_gui_inputs();
			check_stunnel();
			check_openssl();
			check_https();
			record_last_fb_update();
			check_padded_fb();
			check_fixscreen();
			check_xdamage_state();
			check_xrecord_reset(0);
			check_add_keysyms();
			check_new_passwds(0);
#ifdef ENABLE_GRABLOCAL
			if (grab_local) {
				check_local_grab();
			}
#endif
			if (started_as_root) {
				check_switched_user();
			}

			if (first_conn_timeout < 0) {
				start = time(NULL);
				first_conn_timeout = -first_conn_timeout;
			}
		}

		if (rawfb_vnc_reflect) {
			static time_t lastone = 0;
			if (time(NULL) > lastone + 10) {
				lastone = time(NULL);
				vnc_reflect_process_client();
			}
		}

		if (! screen || ! screen->clientHead) {
			/* waiting for a client */
			if (first_conn_timeout) {
				if (time(NULL) - start > first_conn_timeout) {
					rfbLog("No client after %d secs.\n",
					    first_conn_timeout);
					shut_down = 1;
				}
			}
			usleep(200 * 1000);
			continue;
		}

		if (first_conn_timeout && all_clients_initialized()) {
			first_conn_timeout = 0;
		}

		if (nofb) {
			/* no framebuffer polling needed */
			if (cursor_pos_updates) {
				check_x11_pointer();
			}
#ifdef MACOSX
			else check_x11_pointer();
#endif
			continue;
		}
		if (x11vnc_current < last_new_client + 0.5 && !all_clients_initialized()) {
			continue;
		}

		if (button_mask && (!show_dragging || pointer_mode == 0)) {
			/*
			 * if any button is pressed in this mode do
			 * not update rfb screen, but do flush the
			 * X11 display.
			 */
			X_LOCK;
			XFlush_wr(dpy);
			X_UNLOCK;
			dt = 0.0;
		} else {
			static double last_dt = 0.0;
			double xdamage_thrash = 0.4; 

			check_cursor_changes();

			/* for timing the scan to try to detect thrashing */

			if (use_xdamage && last_dt > xdamage_thrash)  {
				clear_xdamage_mark_region(NULL, 0);
			}

			if (unixpw_in_progress) continue;

			if (rawfb_vnc_reflect) {
				vnc_reflect_process_client();
			}
			dtime0(&tm);

#if !NO_X11
			if (xrandr_present && !xrandr && xrandr_maybe) {
				int delay = 180;
				/*  there may be xrandr right after xsession start */
				if (tm < x11vnc_start + delay || tm < last_client + delay) {
					int tw = 20;
					if (auth_file != NULL) {
						tw = 120;
					}
					X_LOCK;
					if (tm < x11vnc_start + tw || tm < last_client + tw) {
						XSync(dpy, False);
					} else {
						XFlush_wr(dpy);
					}
					X_UNLOCK;
				}
				check_xrandr_event("before-scan");
			}
#endif
			if (use_snapfb) {
				int t, tries = 3;
				copy_snap();
				for (t=0; t < tries; t++) {
					tile_diffs = scan_for_updates(0);
				}
			} else {
				tile_diffs = scan_for_updates(0);
			}
			dt = dtime(&tm);
			if (! nap_ok) {
				last_dt = dt;
			}

			if ((debug_tiles || debug_scroll > 1 || debug_wireframe > 1)
			    && (tile_diffs > 4 || debug_tiles > 1)) {
				double rate = (tile_x * tile_y * bpp/8 * tile_diffs) / dt;
				fprintf(stderr, "============================= TILES: %d  dt: %.4f"
				    "  t: %.4f  %.2f MB/s nap_ok: %d\n", tile_diffs, dt,
				    tm - x11vnc_start, rate/1000000.0, nap_ok);
			}

		}

		/* sleep a bit to lessen load */
		wait = choose_delay(dt);
		if (urgent_update) {
			;
		} else if (wait > 2*waitms) {
			/* bog case, break it up */
			nap_sleep(wait, 10);
		} else {
			usleep(wait * 1000);
		}

		cnt++;
	}
}


