/* -- cursor.c -- */

#include "x11vnc.h"
#include "xwrappers.h"
#include "cleanup.h"
#include "screen.h"
#include "scan.h"

int xfixes_present = 0;
int use_xfixes = 1;
int got_xfixes_cursor_notify = 0;
int cursor_changes = 0;
int alpha_threshold = 240;
double alpha_frac = 0.33;
int alpha_remove = 0;
int alpha_blend = 1;
int alt_arrow = 1;


void first_cursor(void);
void setup_cursors_and_push(void);
void initialize_xfixes(void);
int known_cursors_mode(char *s);
void initialize_cursors_mode(void);
int get_which_cursor(void);
void restore_cursor_shape_updates(rfbScreenInfoPtr s);
void disable_cursor_shape_updates(rfbScreenInfoPtr s);
int cursor_shape_updates_clients(rfbScreenInfoPtr s);
int cursor_pos_updates_clients(rfbScreenInfoPtr s);
void cursor_position(int x, int y);
void set_no_cursor(void);
int set_cursor(int x, int y, int which);
int check_x11_pointer(void);


typedef struct win_str_info {
	char *wm_name;
	char *res_name;
	char *res_class;
} win_str_info_t;

typedef struct cursor_info {
	char *data;	/* data and mask pointers */
	char *mask;
	int wx, wy;	/* size of cursor */
	int sx, sy;	/* shift to its centering point */
	int reverse;	/* swap black and white */
	rfbCursorPtr rfb;
} cursor_info_t;


static void curs_copy(cursor_info_t *dest, cursor_info_t *src);
static void setup_cursors(void);
static void set_rfb_cursor(int which);
static void tree_descend_cursor(int *depth, Window *w, win_str_info_t *winfo);
static rfbCursorPtr pixels2curs(unsigned long *pixels, int w, int h,
    int xhot, int yhot, int Bpp);
static int get_xfixes_cursor(int init);
static void set_cursor_was_changed(rfbScreenInfoPtr s);


/*
 * Here begins a bit of a mess to experiment with multiple cursors 
 * drawn on the remote background ...
 */
static void curs_copy(cursor_info_t *dest, cursor_info_t *src) {
	if (src->data != NULL) {
		dest->data = strdup(src->data);
	} else {
		dest->data = NULL;
	}
	if (src->mask != NULL) {
		dest->mask = strdup(src->mask);
	} else {
		dest->mask = NULL;
	}
	dest->wx = src->wx;
	dest->wy = src->wy;
	dest->sx = src->sx;
	dest->sy = src->sy;
	dest->reverse = src->reverse;
	dest->rfb = src->rfb;
}

/* empty cursor */
static char* curs_empty_data =
"  "
"  ";

static char* curs_empty_mask =
"  "
"  ";
static cursor_info_t cur_empty = {NULL, NULL, 2, 2, 0, 0, 0, NULL};

/* dot cursor */
static char* curs_dot_data =
"  "
" x";

static char* curs_dot_mask =
"  "
" x";
static cursor_info_t cur_dot = {NULL, NULL, 2, 2, 0, 0, 0, NULL};


/* main cursor */
static char* curs_arrow_data =
"                  "
" x                "
" xx               "
" xxx              "
" xxxx             "
" xxxxx            "
" xxxxxx           "
" xxxxxxx          "
" xxxxxxxx         "
" xxxxx            "
" xx xx            "
" x   xx           "
"     xx           "
"      xx          "
"      xx          "
"                  "
"                  "
"                  ";

static char* curs_arrow_mask =
"xx                "
"xxx               "
"xxxx              "
"xxxxx             "
"xxxxxx            "
"xxxxxxx           "
"xxxxxxxx          "
"xxxxxxxxx         "
"xxxxxxxxxx        "
"xxxxxxxxxx        "
"xxxxxxx           "
"xxx xxxx          "
"xx  xxxx          "
"     xxxx         "
"     xxxx         "
"      xx          "
"                  "
"                  ";
static cursor_info_t cur_arrow = {NULL, NULL, 18, 18, 0, 0, 1, NULL};

static char* curs_arrow2_data =
"                  "
" x                "
" xx               "
" xxx              "
" xxxx             "
" xxxxx            "
" xxxxxx           "
" xxxxxxx          "
" xxxxxxxx         "
" xxxxx            "
" xx xx            "
" x   xx           "
"     xx           "
"      xx          "
"      xx          "
"                  "
"                  "
"                  ";

static char* curs_arrow2_mask =
"xx                "
"xxx               "
"xxxx              "
"xxxxx             "
"xxxxxx            "
"xxxxxxx           "
"xxxxxxxx          "
"xxxxxxxxx         "
"xxxxxxxxxx        "
"xxxxxxxxxx        "
"xxxxxxx           "
"xxx xxxx          "
"xx  xxxx          "
"     xxxx         "
"     xxxx         "
"      xx          "
"                  "
"                  ";
static cursor_info_t cur_arrow2 = {NULL, NULL, 18, 18, 0, 0, 0, NULL};

static char* curs_arrow3_data = 
"                "
" xx             "
" xxxx           "
"  xxxxx         "
"  xxxxxxx       "
"   xxxxxxxx     "
"   xxxxxxxxxx   "
"    xxxxx       "
"    xxxxx       "
"     xx  x      "
"     xx   x     "
"      x    x    "
"      x     x   "
"             x  "
"              x "
"                ";

static char* curs_arrow3_mask = 
"xxx             "
"xxxxx           "
"xxxxxxx         "
" xxxxxxxx       "
" xxxxxxxxxx     "
"  xxxxxxxxxxxx  "
"  xxxxxxxxxxxx  "
"   xxxxxxxxxxx  "
"   xxxxxxx      "
"    xxxxxxx     "
"    xxxx xxx    "
"     xxx  xxx   "
"     xxx   xxx  "
"     xxx    xxx "
"             xxx"
"              xx";

static cursor_info_t cur_arrow3 = {NULL, NULL, 16, 16, 0, 0, 1, NULL};

static char* curs_arrow4_data = 
"                "
" xx             "
" xxxx           "
"  xxxxx         "
"  xxxxxxx       "
"   xxxxxxxx     "
"   xxxxxxxxxx   "
"    xxxxx       "
"    xxxxx       "
"     xx  x      "
"     xx   x     "
"      x    x    "
"      x     x   "
"             x  "
"              x "
"                ";

static char* curs_arrow4_mask = 
"xxx             "
"xxxxx           "
"xxxxxxx         "
" xxxxxxxx       "
" xxxxxxxxxx     "
"  xxxxxxxxxxxx  "
"  xxxxxxxxxxxx  "
"   xxxxxxxxxxx  "
"   xxxxxxx      "
"    xxxxxxx     "
"    xxxx xxx    "
"     xxx  xxx   "
"     xxx   xxx  "
"     xxx    xxx "
"             xxx"
"              xx";

static cursor_info_t cur_arrow4 = {NULL, NULL, 16, 16, 0, 0, 0, NULL};

static char* curs_arrow5_data = 
"x              "
" xx            "
" xxxx          "
"  xxxxx        "
"  xxxxxxx      "
"   xxx         "
"   xx x        "
"    x  x       "
"    x   x      "
"         x     "
"          x    "
"           x   "
"            x  "
"             x "
"              x";

static char* curs_arrow5_mask = 
"xx             "
"xxxx           "
" xxxxx         "
" xxxxxxx       "
"  xxxxxxxx     "
"  xxxxxxxx     "
"   xxxxx       "
"   xxxxxx      "
"    xx xxx     "
"     x  xxx    "
"         xxx   "
"          xxx  "
"           xxx "
"            xxx"
"             xx";

static cursor_info_t cur_arrow5 = {NULL, NULL, 15, 15, 0, 0, 1, NULL};

static char* curs_arrow6_data = 
"x              "
" xx            "
" xxxx          "
"  xxxxx        "
"  xxxxxxx      "
"   xxx         "
"   xx x        "
"    x  x       "
"    x   x      "
"         x     "
"          x    "
"           x   "
"            x  "
"             x "
"              x";

static char* curs_arrow6_mask = 
"xx             "
"xxxx           "
" xxxxx         "
" xxxxxxx       "
"  xxxxxxxx     "
"  xxxxxxxx     "
"   xxxxx       "
"   xxxxxx      "
"    xx xxx     "
"     x  xxx    "
"         xxx   "
"          xxx  "
"           xxx "
"            xxx"
"             xx";

static cursor_info_t cur_arrow6 = {NULL, NULL, 15, 15, 0, 0, 0, NULL};

int alt_arrow_max = 6;
/*
 * It turns out we can at least detect mouse is on the root window so 
 * show it (under -cursor X) with this familiar cursor... 
 */
static char* curs_root_data =
"                  "
"                  "
"  xxx        xxx  "
"  xxxx      xxxx  "
"  xxxxx    xxxxx  "
"   xxxxx  xxxxx   "
"    xxxxxxxxxx    "
"     xxxxxxxx     "
"      xxxxxx      "
"      xxxxxx      "
"     xxxxxxxx     "
"    xxxxxxxxxx    "
"   xxxxx  xxxxx   "
"  xxxxx    xxxxx  "
"  xxxx      xxxx  "
"  xxx        xxx  "
"                  "
"                  ";

static char* curs_root_mask =
"                  "
" xxxx        xxxx "
" xxxxx      xxxxx "
" xxxxxx    xxxxxx "
" xxxxxxx  xxxxxxx "
"  xxxxxxxxxxxxxx  "
"   xxxxxxxxxxxx   "
"    xxxxxxxxxx    "
"     xxxxxxxx     "
"     xxxxxxxx     "
"    xxxxxxxxxx    "
"   xxxxxxxxxxxx   "
"  xxxxxxxxxxxxxx  "
" xxxxxxx  xxxxxxx "
" xxxxxx    xxxxxx "
" xxxxx      xxxxx "
" xxxx        xxxx "
"                  ";
static cursor_info_t cur_root = {NULL, NULL, 18, 18, 8, 8, 1, NULL};

static char* curs_fleur_data = 
"                "
"       xx       "
"      xxxx      "
"     xxxxxx     "
"       xx       "
"   x   xx   x   "
"  xx   xx   xx  "
" xxxxxxxxxxxxxx "
" xxxxxxxxxxxxxx "
"  xx   xx   xx  "
"   x   xx   x   "
"       xx       "
"     xxxxxx     "
"      xxxx      "
"       xx       "
"                ";

static char* curs_fleur_mask = 
"      xxxx      "
"      xxxxx     "
"     xxxxxx     "
"    xxxxxxxx    "
"   x xxxxxx x   "
"  xxx xxxx xxx  "
"xxxxxxxxxxxxxxxx"
"xxxxxxxxxxxxxxxx"
"xxxxxxxxxxxxxxxx"
"xxxxxxxxxxxxxxxx"
"  xxx xxxx xxx  "
"   x xxxxxx x   "
"    xxxxxxxx    "
"     xxxxxx     "
"      xxxx      "
"      xxxx      ";

static cursor_info_t cur_fleur = {NULL, NULL, 16, 16, 8, 8, 1, NULL};

static char* curs_plus_data = 
"            "
"     xx     "
"     xx     "
"     xx     "
"     xx     "
" xxxxxxxxxx "
" xxxxxxxxxx "
"     xx     "
"     xx     "
"     xx     "
"     xx     "
"            ";

static char* curs_plus_mask = 
"    xxxx    "
"    xxxx    "
"    xxxx    "
"    xxxx    "
"xxxxxxxxxxxx"
"xxxxxxxxxxxx"
"xxxxxxxxxxxx"
"xxxxxxxxxxxx"
"    xxxx    "
"    xxxx    "
"    xxxx    "
"    xxxx    ";
static cursor_info_t cur_plus = {NULL, NULL, 12, 12, 5, 6, 1, NULL};

static char* curs_xterm_data = 
"                "
"     xxx xxx    "
"       xxx      "
"        x       "
"        x       "
"        x       "
"        x       "
"        x       "
"        x       "
"        x       "
"        x       "
"        x       "
"        x       "
"       xxx      "
"     xxx xxx    "
"                ";

static char* curs_xterm_mask = 
"    xxxx xxxx   "
"    xxxxxxxxx   "
"    xxxxxxxxx   "
"      xxxxx     "
"       xxx      "
"       xxx      "
"       xxx      "
"       xxx      "
"       xxx      "
"       xxx      "
"       xxx      "
"       xxx      "
"      xxxxx     "
"    xxxxxxxxx   "
"    xxxxxxxxx   "
"    xxxx xxxx   ";
static cursor_info_t cur_xterm = {NULL, NULL, 16, 16, 8, 8, 1, NULL};

enum cursor_names {
	CURS_EMPTY = 0,
	CURS_DOT,

	CURS_ARROW,
	CURS_ROOT,
	CURS_WM,
	CURS_TERM,
	CURS_PLUS,

	CURS_DYN1,
	CURS_DYN2,
	CURS_DYN3,
	CURS_DYN4,
	CURS_DYN5,
	CURS_DYN6,
	CURS_DYN7,
	CURS_DYN8,
	CURS_DYN9,
	CURS_DYN10,
	CURS_DYN11,
	CURS_DYN12,
	CURS_DYN13,
	CURS_DYN14,
	CURS_DYN15,
	CURS_DYN16
};

#define CURS_DYN_MIN CURS_DYN1
#define CURS_DYN_MAX CURS_DYN16
#define CURS_DYN_NUM (CURS_DYN_MAX - CURS_DYN_MIN + 1)

#define CURS_MAX 32
static cursor_info_t *cursors[CURS_MAX];

void first_cursor(void) {
	if (! screen) {
		return;
	}
	if (! show_cursor) {
		screen->cursor = NULL;
	} else {
		got_xfixes_cursor_notify++;
		set_rfb_cursor(get_which_cursor());
		set_cursor_was_changed(screen);
	}
}

static void setup_cursors(void) {
	rfbCursorPtr rfb_curs;
	char *scale = NULL;
	int i, j, n = 0;
	static int first = 1;

	rfbLog("setting up %d cursors...\n", CURS_MAX);

	if (first) {
		for (i=0; i<CURS_MAX; i++) {
			cursors[i] = NULL;
		}
	}
	first = 0;

	if (screen) {
		screen->cursor = NULL;
		LOCK(screen->cursorMutex);
	}

	for (i=0; i<CURS_MAX; i++) {
		cursor_info_t *ci;
		if (cursors[i]) {
			/* clear out any existing ones: */
			ci = cursors[i];
			if (ci->rfb) {
				/* this is the rfbCursor part: */
				if (ci->rfb->richSource) {
					free(ci->rfb->richSource);
					ci->rfb->richSource = NULL;
				}
				if (ci->rfb->source) {
					free(ci->rfb->source);
					ci->rfb->source = NULL;
				}
				if (ci->rfb->mask) {
					free(ci->rfb->mask);
					ci->rfb->mask = NULL;
				}
				free(ci->rfb);
				ci->rfb = NULL;
			}
			if (ci->data) {
				free(ci->data);
				ci->data = NULL;
			}
			if (ci->mask) {
				free(ci->mask);
				ci->mask = NULL;
			}
			free(ci);
			ci = NULL;
		}

		/* create new struct: */
		ci = (cursor_info_t *) malloc(sizeof(cursor_info_t));
		ci->data = NULL; 
		ci->mask = NULL; 
		ci->wx = 0;
		ci->wy = 0;
		ci->sx = 0;
		ci->sy = 0;
		ci->reverse = 0;
		ci->rfb = NULL;
		cursors[i] = ci;
	}

	/* clear any xfixes cursor cache (no freeing is done) */
	get_xfixes_cursor(1);

	/* manually fill in the data+masks: */
	cur_empty.data	= curs_empty_data;
	cur_empty.mask	= curs_empty_mask;

	cur_dot.data	= curs_dot_data;
	cur_dot.mask	= curs_dot_mask;

	cur_arrow.data	= curs_arrow_data;
	cur_arrow.mask	= curs_arrow_mask;
	cur_arrow2.data	= curs_arrow2_data;
	cur_arrow2.mask	= curs_arrow2_mask;
	cur_arrow3.data	= curs_arrow3_data;
	cur_arrow3.mask	= curs_arrow3_mask;
	cur_arrow4.data	= curs_arrow4_data;
	cur_arrow4.mask	= curs_arrow4_mask;
	cur_arrow5.data	= curs_arrow5_data;
	cur_arrow5.mask	= curs_arrow5_mask;
	cur_arrow6.data	= curs_arrow6_data;
	cur_arrow6.mask	= curs_arrow6_mask;

	cur_root.data	= curs_root_data;
	cur_root.mask	= curs_root_mask;

	cur_plus.data	= curs_plus_data;
	cur_plus.mask	= curs_plus_mask;

	cur_fleur.data	= curs_fleur_data;
	cur_fleur.mask	= curs_fleur_mask;

	cur_xterm.data	= curs_xterm_data;
	cur_xterm.mask	= curs_xterm_mask;

	curs_copy(cursors[CURS_EMPTY], &cur_empty);	n++;
	curs_copy(cursors[CURS_DOT],   &cur_dot);	n++;

	if (alt_arrow < 1 || alt_arrow > alt_arrow_max) {
		alt_arrow = 1;
	}
	if (alt_arrow == 1) {
		curs_copy(cursors[CURS_ARROW], &cur_arrow);	n++;
	} else if (alt_arrow == 2) {
		curs_copy(cursors[CURS_ARROW], &cur_arrow2);	n++;
	} else if (alt_arrow == 3) {
		curs_copy(cursors[CURS_ARROW], &cur_arrow3);	n++;
	} else if (alt_arrow == 4) {
		curs_copy(cursors[CURS_ARROW], &cur_arrow4);	n++;
	} else if (alt_arrow == 5) {
		curs_copy(cursors[CURS_ARROW], &cur_arrow5);	n++;
	} else if (alt_arrow == 6) {
		curs_copy(cursors[CURS_ARROW], &cur_arrow6);	n++;
	} else {
		alt_arrow = 1;
		curs_copy(cursors[CURS_ARROW], &cur_arrow);	n++;
	}

	curs_copy(cursors[CURS_ROOT], &cur_root);	n++;
	curs_copy(cursors[CURS_WM],   &cur_fleur);	n++;
	curs_copy(cursors[CURS_TERM], &cur_xterm);	n++;
	curs_copy(cursors[CURS_PLUS], &cur_plus);	n++;

	if (scale_cursor_str) {
		scale = scale_cursor_str;
	} else if (scaling && scale_str) {
		scale = scale_str;
	}
	/* scale = NULL zeroes everything */
	parse_scale_string(scale, &scale_cursor_fac, &scaling_cursor,
	    &scaling_cursor_blend, &j, &j, &scaling_cursor_interpolate,
	    &scale_cursor_numer, &scale_cursor_denom);

	for (i=0; i<n; i++) {
		/* create rfbCursors for the special cursors: */

		cursor_info_t *ci = cursors[i];

		if (scaling_cursor && scale_cursor_fac != 1.0) {
			int w, h, x, y, k;
			unsigned long *pixels;

			w = ci->wx;
			h = ci->wy;

			pixels = (unsigned long *) malloc(w * h
			    * sizeof(unsigned long));

			k = 0;
			for (y=0; y<h; y++) {
				for (x=0; x<w; x++) {
					char d = ci->data[k];
					char m = ci->mask[k];
					unsigned long *p;

					p = pixels + k;

					/* set alpha on */
					*p = 0xff000000;

					if (d == ' ' && m == ' ') {
						/* alpha off */
						*p = 0x00000000;
					} else if (d != ' ') {
						/* body */
						if (ci->reverse) {
							*p |= 0x00000000;
						} else {
							*p |= 0x00ffffff;
						}
					} else if (m != ' ') {
						/* edge */
						if (ci->reverse) {
							*p |= 0x00ffffff;
						} else {
							*p |= 0x00000000;
						}
					}
					k++;
				}
			}

			rfb_curs = pixels2curs(pixels, w, h, ci->sx, ci->sy,
			    bpp/8);

			free(pixels);

		} else {

			/* standard X cursor */
			rfb_curs = rfbMakeXCursor(ci->wx, ci->wy,
			    ci->data, ci->mask);

			if (ci->reverse) {
				rfb_curs->foreRed   = 0x0000;
				rfb_curs->foreGreen = 0x0000;
				rfb_curs->foreBlue  = 0x0000;
				rfb_curs->backRed   = 0xffff;
				rfb_curs->backGreen = 0xffff;
				rfb_curs->backBlue  = 0xffff;
			}
			rfb_curs->alphaSource = NULL;

			rfb_curs->xhot = ci->sx;
			rfb_curs->yhot = ci->sy;
			rfb_curs->cleanup = FALSE;
			rfb_curs->cleanupSource = FALSE;
			rfb_curs->cleanupMask = FALSE;
			rfb_curs->cleanupRichSource = FALSE;

			if (bpp == 8 && indexed_color) {
				/*
				 * use richsource in PseudoColor for better
				 * looking cursors (i.e. two-color).
				 */
				int x, y, k = 0, bw;
				int black = 0, white = 1;
				char d, m;

				if (dpy) {	/* raw_fb hack */
					black = BlackPixel(dpy, scr);
					white = WhitePixel(dpy, scr);
				}

				rfb_curs->richSource = (unsigned char *)
				    calloc(ci->wx * ci->wy, 1);

				for (y = 0; y < ci->wy; y++) {
				    for (x = 0; x < ci->wx; x++) {
					d = *(ci->data + k);
					m = *(ci->mask + k);
					if (d == ' ' && m == ' ') {
						k++;
						continue;
					} else if (m != ' ' && d == ' ') {
						bw = black;
					} else {
						bw = white;
					}
					if (ci->reverse) {
						if (bw == black) {
							bw = white;
						} else {
							bw = black;
						}
					}
					*(rfb_curs->richSource+k) =
					    (unsigned char) bw;
					k++;
				    }
				}
			}
		}
		ci->rfb = rfb_curs;
	}
	if (screen) {
		UNLOCK(screen->cursorMutex);
	}
	rfbLog("  done.\n");
}

void setup_cursors_and_push(void) {
	setup_cursors();
	first_cursor();
}

/*
 * Descends window tree at pointer until the window cursor matches the current 
 * cursor.  So far only used to detect if mouse is on root background or not.
 * (returns 0 in that case, 1 otherwise).
 *
 */
static void tree_descend_cursor(int *depth, Window *w, win_str_info_t *winfo) {
	Window r, c;
	int i, rx, ry, wx, wy;
	unsigned int mask;
	Window wins[10];
	int descend, maxtries = 10;
	char *name, *s = multiple_cursors_mode;
	static XClassHint *classhint = NULL;
	int nm_info = 1;
	XErrorHandler old_handler;

	X_LOCK;

	if (!strcmp(s, "default") || !strcmp(s, "X") || !strcmp(s, "arrow")) {
		nm_info = 0;
	}

	*(winfo->wm_name)   = '\0';
	*(winfo->res_name)  = '\0';
	*(winfo->res_class) = '\0';


	/* some times a window can go away before we get to it */
	trapped_xerror = 0;
	old_handler = XSetErrorHandler(trap_xerror);

	c = window;
	descend = -1;

	while (c) {
		wins[++descend] = c;
		if (descend >= maxtries - 1) {
			break;
		}
		if ( XTestCompareCurrentCursorWithWindow_wr(dpy, c) ) {
			break;
		}
		/* TBD: query_pointer() */
		XQueryPointer(dpy, c, &r, &c, &rx, &ry, &wx, &wy, &mask);
	}

	if (nm_info) {
		int got_wm_name = 0, got_res_name = 0, got_res_class = 0;

		if (! classhint) {
			classhint = XAllocClassHint();
		}

		for (i = descend; i >=0; i--) {
			c = wins[i];
			if (! c) {
				continue;
			}
			
			if (! got_wm_name && XFetchName(dpy, c, &name)) {
				if (name) {
					if (*name != '\0') {
						strcpy(winfo->wm_name, name);
						got_wm_name = 1;
					}
					XFree(name);
				}
			}
			if (classhint && (! got_res_name || ! got_res_class)) {
			    if (XGetClassHint(dpy, c, classhint)) {
				char *p;
				p = classhint->res_name;
				if (p) {
					if (*p != '\0' && ! got_res_name) {
						strcpy(winfo->res_name, p);
						got_res_name = 1;
					}
					XFree(p);
					classhint->res_name = NULL;
				}
				p = classhint->res_class;
				if (p) {
					if (*p != '\0' && ! got_res_class) {
						strcpy(winfo->res_class, p);
						got_res_class = 1;
					}
					XFree(p);
					classhint->res_class = NULL;
				}
			    }
			}
		}
	}

	XSetErrorHandler(old_handler);
	trapped_xerror = 0;

	X_UNLOCK;

	*depth = descend;
	*w = wins[descend];
}

void initialize_xfixes(void) {
#if LIBVNCSERVER_HAVE_LIBXFIXES
	if (xfixes_present) {
		X_LOCK;
		if (use_xfixes) {
			XFixesSelectCursorInput(dpy, rootwin,
				XFixesDisplayCursorNotifyMask);
		} else {
			XFixesSelectCursorInput(dpy, rootwin, 0);
		}
		X_UNLOCK;
	}
#endif
}

static rfbCursorPtr pixels2curs(unsigned long *pixels, int w, int h,
    int xhot, int yhot, int Bpp) {
	rfbCursorPtr c;
	static unsigned long black = 0, white = 1;
	static int first = 1;
	char *bitmap, *rich, *alpha;
	char *pixels_new = NULL;
	int n_opaque, n_trans, n_alpha, len, histo[256];
	int send_alpha = 0, alpha_shift = 0, thresh;
	int i, x, y;

	if (first && dpy) {	/* raw_fb hack */
		X_LOCK;
		black = BlackPixel(dpy, scr);
		white = WhitePixel(dpy, scr);
		X_UNLOCK;
		first = 0;
	}

	if (cmap8to24 && cmap8to24_fb && depth == 8) {
		if (Bpp == 1) {
			Bpp = 4;
		}
	}

	if (scaling_cursor && scale_cursor_fac != 1.0) {
		int W, H;
		char *pixels_use = (char *) pixels;
		unsigned int *pixels32 = NULL;

		W = w;
		H = h;

		w = scale_round(W, scale_cursor_fac);
		h = scale_round(H, scale_cursor_fac);

		pixels_new = (char *) malloc(4*w*h);

		if (sizeof(unsigned long) == 8) {
			int i, j, k = 0;
			/*
			 * to avoid 64bpp code in scale_rect() we knock
			 * down to unsigned int on 64bit machines:
			 */
			pixels32 = (unsigned int*) malloc(4*W*H);
			for (j=0; j<H; j++) {
			    for (i=0; i<W; i++) {
				*(pixels32+k) = 0xffffffff & (*(pixels+k));
				k++;
			    }
			}
			pixels_use = (char *) pixels32;
		}

		scale_rect(scale_cursor_fac, scaling_cursor_blend,
		    scaling_cursor_interpolate,
		    4, pixels_use, 4*W, pixels_new, 4*w,
		    W, H, w, h, 0, 0, W, H, 0);

		if (sizeof(unsigned long) == 8) {
			int i, j, k = 0;
			unsigned long *pixels64;
			unsigned int* source = (unsigned int*) pixels_new;
			/*
			 * now knock it back up to unsigned long:
			 */
			pixels64 = (unsigned long*) malloc(8*w*h);
			for (j=0; j<h; j++) {
			    for (i=0; i<w; i++) {
				*(pixels64+k) = (unsigned long) (*(source+k));
				k++;
			    }
			}
			free(pixels_new);
			pixels_new = (char *) pixels64;
			if (pixels32) {
				free(pixels32);
				pixels32 = NULL;
			}
		}
			
		pixels = (unsigned long *) pixels_new;

		xhot = scale_round(xhot, scale_cursor_fac);
		yhot = scale_round(yhot, scale_cursor_fac);
	}

	len = w * h;
	/* for bitmap data */
	bitmap = (char *) malloc(len+1);
	bitmap[len] = '\0';

	/* for rich cursor pixel data */
	rich  = (char *)calloc(Bpp*len, 1);
	alpha = (char *)calloc(1*len, 1);

	n_opaque = 0;
	n_trans = 0;
	n_alpha = 0;
	for (i=0; i<256; i++) {
		histo[i] = 0;
	}

	i = 0;
	for (y = 0; y < h; y++) {
		for (x = 0; x < w; x++) {
			unsigned long a;

			a = 0xff000000 & (*(pixels+i));
			a = a >> 24;	/* alpha channel */
			if (a > 0) {
				n_alpha++;
			}
			histo[a]++;
			if (a < (unsigned int) alpha_threshold) {
				n_trans++;
			} else {
				n_opaque++;
			}
			i++;
		}
	}
	if (alpha_blend) {
		send_alpha = 0;
		if (Bpp == 4) {
			send_alpha = 1;
		}
		alpha_shift = 24;
		if (main_red_shift == 24 || main_green_shift == 24 ||
		    main_blue_shift == 24)  {
			alpha_shift = 0;	/* XXX correct? */
		}
	}
	if (n_opaque >= alpha_frac * n_alpha) {
		thresh = alpha_threshold;
	} else {
		n_opaque = 0;
		for (i=255; i>=0; i--) {
			n_opaque += histo[i];
			thresh = i;
			if (n_opaque >= alpha_frac * n_alpha) {
				break;
			}
		}
	}

	i = 0;
	for (y = 0; y < h; y++) {
		for (x = 0; x < w; x++) {
			unsigned long r, g, b, a;
			unsigned int ui;
			char *p;

			a = 0xff000000 & (*(pixels+i));
			a = a >> 24;	/* alpha channel */


			if (a < (unsigned int) thresh) {
				bitmap[i] = ' ';
			} else {
				bitmap[i] = 'x';
			}

			r = 0x00ff0000 & (*(pixels+i));
			g = 0x0000ff00 & (*(pixels+i));
			b = 0x000000ff & (*(pixels+i));
			r = r >> 16;	/* red */
			g = g >> 8;	/* green */
			b = b >> 0;	/* blue */

			if (alpha_remove && a != 0) {
				r = (255 * r) / a;
				g = (255 * g) / a;
				b = (255 * b) / a;
				if (r > 255) r = 255;
				if (g > 255) g = 255;
				if (b > 255) b = 255;
			}

			if (indexed_color) {
				/*
				 * Choose black or white for
				 * PseudoColor case.
				 */
				int value = (r+g+b)/3;
				if (value > 127) {
					ui = white;
				} else {
					ui = black;
				}
			} else {
				/*
				 * Otherwise map the RGB data onto
				 * the framebuffer format:
				 */
				r = (main_red_max   * r)/255;
				g = (main_green_max * g)/255;
				b = (main_blue_max  * b)/255;
				ui = 0;
				ui |= (r << main_red_shift);
				ui |= (g << main_green_shift);
				ui |= (b << main_blue_shift);
				if (send_alpha) {
					ui |= (a << alpha_shift);
				}
			}

			/* insert value into rich source: */
			p = rich + Bpp*i;

			if (Bpp == 1) {
				*((unsigned char *)p)
				= (unsigned char) ui;
			} else if (Bpp == 2) {
				*((unsigned short *)p)
				= (unsigned short) ui;
			} else if (Bpp == 3) {
				*((unsigned char *)p)
				= (unsigned char) ((ui & 0x0000ff) >> 0);
				*((unsigned char *)(p+1))
				= (unsigned char) ((ui & 0x00ff00) >> 8);
				*((unsigned char *)(p+2))
				= (unsigned char) ((ui & 0xff0000) >> 16);
			} else if (Bpp == 4) {
				*((unsigned int *)p)
				= (unsigned int) ui;
			}

			/* insert alpha value into alpha source: */
			p = alpha + i;
			*((unsigned char *)p) = (unsigned char) a;

			i++;
		}
	}

	/* create the cursor with the bitmap: */
	c = rfbMakeXCursor(w, h, bitmap, bitmap);
	free(bitmap);

	if (pixels_new) {
		free(pixels_new);
	}

	/* set up the cursor parameters: */
	c->xhot = xhot;
	c->yhot = yhot;
	c->cleanup = FALSE;
	c->cleanupSource = FALSE;
	c->cleanupMask = FALSE;
	c->cleanupRichSource = FALSE;
	c->richSource = (unsigned char *) rich;

	if (alpha_blend && !indexed_color) {
		c->alphaSource = (unsigned char *) alpha;
		c->alphaPreMultiplied = TRUE;
	} else {
		free(alpha);
		c->alphaSource = NULL;
	}
	return c;
}

static int get_xfixes_cursor(int init) {
	static unsigned long last_cursor = 0;
	static int last_index = 0;
	static time_t curs_times[CURS_MAX];
	static unsigned long curs_index[CURS_MAX];
	int which = CURS_ARROW;

	if (init) {
		/* zero out our cache (cursors are not freed) */
		int i;
		for (i=0; i<CURS_MAX; i++) {
			curs_times[i] = 0;
			curs_index[i] = 0;
		}
		last_cursor = 0;
		last_index = 0;
		return -1;
	}

	if (xfixes_present) {
#if LIBVNCSERVER_HAVE_LIBXFIXES
		int use, oldest, i;
		time_t oldtime, now;
		XFixesCursorImage *xfc;

		if (! got_xfixes_cursor_notify && xfixes_base_event_type) {
			/* try again for XFixesCursorNotify event */
			XEvent xev;
			X_LOCK;
			if (XCheckTypedEvent(dpy, xfixes_base_event_type +
			    XFixesCursorNotify, &xev)) {
				got_xfixes_cursor_notify++;
			}
			X_UNLOCK;
		}
		if (! got_xfixes_cursor_notify) {
			/* evidently no cursor change, just return last one */
			if (last_index) {
				return last_index;
			} else {
				return CURS_ARROW;
			}
		}
		got_xfixes_cursor_notify = 0;

		/* retrieve the cursor info + pixels from server: */
		X_LOCK;
		xfc = XFixesGetCursorImage(dpy);
		X_UNLOCK;
		if (! xfc) {
			/* failure. */
			return(which);
		}

		if (xfc->cursor_serial == last_cursor) {
			/* same serial index: no change */
			X_LOCK;
			XFree(xfc);
			X_UNLOCK;
			if (last_index) {
				return last_index;
			} else {
				return CURS_ARROW;
			}
		}

		oldest = CURS_DYN_MIN;
		if (screen && screen->cursor == cursors[oldest]->rfb) {
			oldest++;
		}
		oldtime = curs_times[oldest];
		now = time(0);
		for (i = CURS_DYN_MIN; i <= CURS_DYN_MAX; i++) {
			if (screen && screen->cursor == cursors[i]->rfb) {
				;
			} else if (curs_times[i] < oldtime) {
				/* watch for oldest one to overwrite */
				oldest = i;
				oldtime = curs_times[i];
			}
			if (xfc->cursor_serial == curs_index[i]) {
				/*
				 * got a hit with an existing cursor,
				 * use that one.
				 */
				last_cursor = curs_index[i];
				curs_times[i] = now;
				last_index = i;
				X_LOCK;
				XFree(xfc);
				X_UNLOCK;
				return last_index;
			}
		}

		/* we need to create the cursor and overwrite oldest */
		use = oldest;
		if (cursors[use]->rfb) {
			/* clean up oldest if it exists */
			if (cursors[use]->rfb->richSource) {
				free(cursors[use]->rfb->richSource);
				cursors[use]->rfb->richSource = NULL;
			}
			if (cursors[use]->rfb->alphaSource) {
				free(cursors[use]->rfb->alphaSource);
				cursors[use]->rfb->alphaSource = NULL;
			}
			if (cursors[use]->rfb->source) {
				free(cursors[use]->rfb->source);
				cursors[use]->rfb->source = NULL;
			}
			if (cursors[use]->rfb->mask) {
				free(cursors[use]->rfb->mask);
				cursors[use]->rfb->mask = NULL;
			}
			free(cursors[use]->rfb);
			cursors[use]->rfb = NULL;
		}

		/* place cursor into our collection */
		cursors[use]->rfb = pixels2curs(xfc->pixels, xfc->width,
		    xfc->height, xfc->xhot, xfc->yhot, bpp/8);

		/* update time and serial index: */
		curs_times[use] = now;
		curs_index[use] = xfc->cursor_serial;
		last_index = use;
		last_cursor = xfc->cursor_serial;

		which = last_index;

		X_LOCK;
		XFree(xfc);
		X_UNLOCK;
#endif
	}
	return(which);
}

int known_cursors_mode(char *s) {
/*
 * default:	see initialize_cursors_mode() for default behavior.
 * arrow:	unchanging white arrow.
 * Xn*:		show X on root background.  Optional n sets treedepth.
 * some:	do the heuristics for root, wm, term detection.
 * most:	if display have overlay or xfixes, show all cursors,
 *		otherwise do the same as "some"
 * none:	show no cursor.
 */
	if (strcmp(s, "default") && strcmp(s, "arrow") && *s != 'X' &&
	    strcmp(s, "some") && strcmp(s, "most") && strcmp(s, "none")) {
		return 0;
	} else {
		return 1;
	}
}

void initialize_cursors_mode(void) {
	char *s = multiple_cursors_mode;
	if (!s || !known_cursors_mode(s)) {
		rfbLog("unknown cursors mode: %s\n", s);
		rfbLog("resetting cursors mode to \"default\"\n");
		if (multiple_cursors_mode) free(multiple_cursors_mode);
		multiple_cursors_mode = strdup("default");
		s = multiple_cursors_mode;
	}
	if (!strcmp(s, "none")) {
		show_cursor = 0;
	} else {
		/* we do NOT set show_cursor = 1, let the caller do that */
	}

	show_multiple_cursors = 0;
	if (show_cursor) {
		if (!strcmp(s, "default")) {
			if(multiple_cursors_mode) free(multiple_cursors_mode);
			multiple_cursors_mode = strdup("X");
			s = multiple_cursors_mode;
		}
		if (*s == 'X' || !strcmp(s, "some") || !strcmp(s, "most")) {
			show_multiple_cursors = 1;
		} else {
			show_multiple_cursors = 0;
			/* hmmm, some bug going back to arrow mode.. */
			set_rfb_cursor(CURS_ARROW);
		}
		if (screen) {
			set_cursor_was_changed(screen);
		}
	} else {
		if (screen) {
			screen->cursor = NULL;	/* dangerous? */
			set_cursor_was_changed(screen);
		}
	}
}

int get_which_cursor(void) {
	int which = CURS_ARROW;

	if (show_multiple_cursors) {
		int depth;
		static win_str_info_t winfo;
		static int first = 1, depth_cutoff = -1;
		Window win;
		XErrorHandler old_handler;
		int mode = 0;

		if (drag_in_progress || button_mask) {
			/* XXX not exactly what we want for menus */
			return -1;
		}

		if (!strcmp(multiple_cursors_mode, "arrow")) {
			/* should not happen... */
			return CURS_ARROW;
		} else if (!strcmp(multiple_cursors_mode, "default")) {
			mode = 0;
		} else if (!strcmp(multiple_cursors_mode, "X")) {
			mode = 1;
		} else if (!strcmp(multiple_cursors_mode, "some")) {
			mode = 2;
		} else if (!strcmp(multiple_cursors_mode, "most")) {
			mode = 3;
		}

		if (mode == 3 && xfixes_present && use_xfixes) {
			return get_xfixes_cursor(0);
		}

		if (depth_cutoff < 0) {
			int din;
			if (sscanf(multiple_cursors_mode, "X%d", &din) == 1) {
				depth_cutoff = din;
			} else {
				depth_cutoff = 0;
			}
		}

		if (first) {
			winfo.wm_name   = (char *) malloc(1024);
			winfo.res_name  = (char *) malloc(1024);
			winfo.res_class = (char *) malloc(1024);
		}
		first = 0;
		
		tree_descend_cursor(&depth, &win, &winfo);

		if (depth <= depth_cutoff && !subwin) {
			which = CURS_ROOT;

		} else if (mode == 2 || mode == 3) {
			int which0 = which;

			/* apply crude heuristics to choose a cursor... */
			if (win) {
				int ratio = 10, x, y;
				unsigned int w, h, bw, d;  
				Window r;

				trapped_xerror = 0;
				X_LOCK;
				old_handler = XSetErrorHandler(trap_xerror);

				/* "narrow" windows are WM */
				if (XGetGeometry(dpy, win, &r, &x, &y, &w, &h,
				    &bw, &d)) {
					if (w > ratio * h || h > ratio * w) {
						which = CURS_WM;
					}
				}
				XSetErrorHandler(old_handler);
				X_UNLOCK;
				trapped_xerror = 0;
			}
			if (which == which0) {
				/* the string "term" mean I-beam. */
				char *name, *class;
				lowercase(winfo.res_name);
				lowercase(winfo.res_class);
				name  = winfo.res_name;
				class = winfo.res_class;
				if (strstr(name, "term")) {
					which = CURS_TERM;
				} else if (strstr(class, "term")) {
					which = CURS_TERM;
				} else if (strstr(name,  "text")) {
					which = CURS_TERM;
				} else if (strstr(class, "text")) {
					which = CURS_TERM;
				} else if (strstr(name,  "onsole")) {
					which = CURS_TERM;
				} else if (strstr(class, "onsole")) {
					which = CURS_TERM;
				} else if (strstr(name,  "cmdtool")) {
					which = CURS_TERM;
				} else if (strstr(class, "cmdtool")) {
					which = CURS_TERM;
				} else if (strstr(name,  "shelltool")) {
					which = CURS_TERM;
				} else if (strstr(class, "shelltool")) {
					which = CURS_TERM;
				}
			}
		}
	}
	return which;
}

static void set_cursor_was_changed(rfbScreenInfoPtr s) {
	rfbClientIteratorPtr iter;
	rfbClientPtr cl;

	if (! s) {
		return;
	}
	iter = rfbGetClientIterator(s);
	while( (cl = rfbClientIteratorNext(iter)) ) {
		cl->cursorWasChanged = TRUE;
	}
	rfbReleaseClientIterator(iter);
}

#if 0
/* not yet used */
static void set_cursor_was_moved(rfbScreenInfoPtr s) {
	rfbClientIteratorPtr iter;
	rfbClientPtr cl;

	if (! s) {
		return;
	}
	iter = rfbGetClientIterator(s);
	while( (cl = rfbClientIteratorNext(iter)) ) {
		cl->cursorWasMoved = TRUE;
	}
	rfbReleaseClientIterator(iter);
}
#endif

void restore_cursor_shape_updates(rfbScreenInfoPtr s) {
	rfbClientIteratorPtr iter;
	rfbClientPtr cl;
	int count = 0;

	if (! s || ! s->clientHead) {
		return;
	}
	iter = rfbGetClientIterator(s);
	while( (cl = rfbClientIteratorNext(iter)) ) {
		int changed = 0;
		ClientData *cd = (ClientData *) cl->clientData;

		if (cd->had_cursor_shape_updates) {
			rfbLog("restoring enableCursorShapeUpdates for client"
			    " 0x%x\n", cl);
			cl->enableCursorShapeUpdates = TRUE;	
			changed = 1;
		}
		if (cd->had_cursor_pos_updates) {
			rfbLog("restoring enableCursorPosUpdates for client"
			    " 0x%x\n", cl);
			cl->enableCursorPosUpdates = TRUE;	
			changed = 1;
		}
		if (changed) {
			cl->cursorWasChanged = TRUE;
			count++;
		}
	}
	rfbReleaseClientIterator(iter);
}

void disable_cursor_shape_updates(rfbScreenInfoPtr s) {
	rfbClientIteratorPtr iter;
	rfbClientPtr cl;
	static int changed = 0;
	int count = 0;

	if (! s || ! s->clientHead) {
		return;
	}

	iter = rfbGetClientIterator(s);
	while( (cl = rfbClientIteratorNext(iter)) ) {
		ClientData *cd;
		cd = (ClientData *) cl->clientData;

		if (cl->enableCursorShapeUpdates) {
			cd->had_cursor_shape_updates = 1;
			count++;
			if (debug_pointer) {
				rfbLog("%s disable HCSU\n", cl->host);
			}
		}
		if (cl->enableCursorPosUpdates) {
			cd->had_cursor_pos_updates = 1;
			count++;
			if (debug_pointer) {
				rfbLog("%s disable HCPU\n", cl->host);
			}
		}
		
		cl->enableCursorShapeUpdates = FALSE;
		cl->enableCursorPosUpdates = FALSE;
		cl->cursorWasChanged = FALSE;
	}
	rfbReleaseClientIterator(iter);

	if (count) {
		changed = 1;
	}
}

int cursor_shape_updates_clients(rfbScreenInfoPtr s) {
	rfbClientIteratorPtr iter;
	rfbClientPtr cl;
	int count = 0;

	if (! s) {
		return 0;
	}
	iter = rfbGetClientIterator(s);
	while( (cl = rfbClientIteratorNext(iter)) ) {
		if (cl->enableCursorShapeUpdates) {
			count++;
		}
	}
	rfbReleaseClientIterator(iter);
	return count;
}

int cursor_pos_updates_clients(rfbScreenInfoPtr s) {
	rfbClientIteratorPtr iter;
	rfbClientPtr cl;
	int count = 0;

	if (! s) {
		return 0;
	}
	iter = rfbGetClientIterator(s);
	while( (cl = rfbClientIteratorNext(iter)) ) {
		if (cl->enableCursorPosUpdates) {
			count++;
		}
	}
	rfbReleaseClientIterator(iter);
	return count;
}

/*
 * Record rfb cursor position screen->cursorX, etc (a la defaultPtrAddEvent())
 * Then set up for sending rfbCursorPosUpdates back
 * to clients that understand them.  This seems to be TightVNC specific.
 */
void cursor_position(int x, int y) {
	rfbClientIteratorPtr iter;
	rfbClientPtr cl;
	int cnt = 0, nonCursorPosUpdates_clients = 0;
	int x_in = x, y_in = y;

	/* x and y are current positions of X11 pointer on the X11 display */
	if (!screen) {
		return;
	}

	if (scaling) {
		x = ((double) x / dpy_x) * scaled_x;
		x = nfix(x, scaled_x);
		y = ((double) y / dpy_y) * scaled_y;
		y = nfix(y, scaled_y);
	}

	if (x == screen->cursorX && y == screen->cursorY) {
		return;
	}

	LOCK(screen->cursorMutex);
	screen->cursorX = x;
	screen->cursorY = y;
	UNLOCK(screen->cursorMutex);

	iter = rfbGetClientIterator(screen);
	while( (cl = rfbClientIteratorNext(iter)) ) {
		if (! cl->enableCursorPosUpdates) {
			nonCursorPosUpdates_clients++;
			continue;
		}
		if (! cursor_pos_updates) {
			continue;
		}
		if (cl == last_pointer_client) {
			/*
			 * special case if this client was the last one to
			 * send a pointer position.
			 */
			if (x_in == cursor_x && y_in == cursor_y) {
				cl->cursorWasMoved = FALSE;
			} else {
				/* an X11 app evidently warped the pointer */
				if (debug_pointer) {
					rfbLog("cursor_position: warp "
					    "detected dx=%3d dy=%3d\n",
					    cursor_x - x, cursor_y - y);
				}
				cl->cursorWasMoved = TRUE;
				cnt++;
			}
		} else {
			cl->cursorWasMoved = TRUE;
			cnt++;
		}
	}
	rfbReleaseClientIterator(iter);

	if (debug_pointer && cnt) {
		rfbLog("cursor_position: sent position x=%3d y=%3d to %d"
		    " clients\n", x, y, cnt);
	}
}

static void set_rfb_cursor(int which) {

	if (! show_cursor) {
		return;
	}
	if (! screen) {
		return;
	}
	
	if (!cursors[which] || !cursors[which]->rfb) {
		rfbLog("non-existent cursor: which=%d\n", which);
		return;
	} else {
		rfbSetCursor(screen, cursors[which]->rfb);
	}
}

void set_no_cursor(void) {
	set_rfb_cursor(CURS_EMPTY);
}

int set_cursor(int x, int y, int which) {
	static int last = -1;
	int changed_cursor = 0;

	if (x || y) {} /* unused vars warning: */

	if (which < 0) {
		which = last;	
	}
	if (last < 0 || which != last) {
		set_rfb_cursor(which);
		changed_cursor = 1;
	}
	last = which;

	return changed_cursor;
}

/*
 * routine called periodically to update cursor aspects, this catches
 * warps and cursor shape changes. 
 */
int check_x11_pointer(void) {
	Window root_w, child_w;
	rfbBool ret;
	int root_x, root_y, win_x, win_y;
	int x, y;
	unsigned int mask;

	if (raw_fb && ! dpy) return 0;	/* raw_fb hack */

	X_LOCK;
	ret = XQueryPointer(dpy, rootwin, &root_w, &child_w, &root_x, &root_y,
	    &win_x, &win_y, &mask);
	X_UNLOCK;

	if (! ret) {
		return 0;
	}
	if (debug_pointer) {
		static int last_x = -1, last_y = -1;
		if (root_x != last_x || root_y != last_y) {
			rfbLog("XQueryPointer:     x:%4d, y:%4d)\n",
			    root_x, root_y);
		}
		last_x = root_x;
		last_y = root_y;
	}

	/* offset subtracted since XQueryPointer relative to rootwin */
	x = root_x - off_x - coff_x;
	y = root_y - off_y - coff_y;

	/* record the cursor position in the rfb screen */
	cursor_position(x, y);

	/* change the cursor shape if necessary */
	return set_cursor(x, y, get_which_cursor());
}