diff options
author | runge <runge> | 2006-01-09 01:54:38 +0000 |
---|---|---|
committer | runge <runge> | 2006-01-09 01:54:38 +0000 |
commit | 71f2ec79180185a6c3db0c87f9d53c491dc31e76 (patch) | |
tree | 67c341571cbeb1bd9a0744cc8eb03b30ef04f381 /x11vnc/cursor.c | |
parent | def301266373e462f4a5e90eab443087ccfc7ccc (diff) | |
download | libtdevnc-71f2ec79180185a6c3db0c87f9d53c491dc31e76.tar.gz libtdevnc-71f2ec79180185a6c3db0c87f9d53c491dc31e76.zip |
x11vnc: the big split.
Diffstat (limited to 'x11vnc/cursor.c')
-rw-r--r-- | x11vnc/cursor.c | 1786 |
1 files changed, 1786 insertions, 0 deletions
diff --git a/x11vnc/cursor.c b/x11vnc/cursor.c new file mode 100644 index 0000000..4c8cca7 --- /dev/null +++ b/x11vnc/cursor.c @@ -0,0 +1,1786 @@ +/* -- 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, 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 (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()); +} + + |