/* -- cursor.c -- */ #include "x11vnc.h" #include "xwrappers.h" #include "cleanup.h" #include "screen.h" #include "scan.h" #include "unixpw.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; icursor = NULL; LOCK(screen->cursorMutex); } for (i=0; irfb) { /* 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; iwx; h = ci->wy; pixels = (unsigned long *) malloc(w * h * sizeof(unsigned long)); k = 0; for (y=0; ydata[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"); rfbLog("\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> 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; icursor_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) { continue; } 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; } if (unixpw_in_progress) return; iter = rfbGetClientIterator(s); while( (cl = rfbClientIteratorNext(iter)) ) { ClientData *cd; cd = (ClientData *) cl->clientData; if (cl->enableCursorShapeUpdates) { if (cd) { cd->had_cursor_shape_updates = 1; } count++; if (debug_pointer) { rfbLog("%s disable HCSU\n", cl->host); } } if (cl->enableCursorPosUpdates) { if (cd) { 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 */ if (unixpw_in_progress) return 0; 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()); }