You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libtdevnc/x11vnc/cursor.c

1903 lines
40 KiB

/* -- 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);
void set_warrow_cursor(void);
int set_cursor(int x, int y, int which);
int check_x11_pointer(void);
int store_cursor(int serial, unsigned long *data, int w, int h, int cbpp, int xhot, int yhot);
unsigned long get_cursor_serial(int mode);
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_exact_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;
if (rotating && rotating_cursors && dest->data != NULL) {
int tx, ty;
rotate_curs(dest->data, src->data, src->wx, src->wy, 1);
rotate_curs(dest->mask, src->mask, src->wx, src->wy, 1);
rotate_coords(dest->sx, dest->sy, &tx, &ty, src->wx, src->wy);
dest->sx = tx;
dest->sy = ty;
if (! rotating_same) {
dest->wx = src->wy;
dest->wy = src->wx;
}
}
}
/* 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_WARROW,
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;
if (verbose) {
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_exact_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_WARROW], &cur_arrow2); 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);
}
if (verbose) {
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;
RAWFB_RET_VOID
#if NO_X11
return;
#else
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_wr(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_wr(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_wr(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_wr(p);
classhint->res_class = NULL;
}
}
}
}
}
XSetErrorHandler(old_handler);
trapped_xerror = 0;
X_UNLOCK;
*depth = descend;
*w = wins[descend];
#endif /* NO_X11 */
}
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 unsigned long last_cursor = 0;
static int last_index = 0;
static time_t curs_times[CURS_MAX];
static unsigned long curs_index[CURS_MAX];
unsigned long get_cursor_serial(int mode) {
if (mode == 0) {
return last_cursor;
} else if (mode == 1) {
return (unsigned long) last_index;
}
}
static int get_exact_cursor(int init) {
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;
}
#ifdef MACOSX
if (! dpy) {
return macosx_get_cursor();
}
#endif
if (xfixes_present && dpy) {
#if LIBVNCSERVER_HAVE_LIBXFIXES
int last_idx = (int) get_cursor_serial(1);
XFixesCursorImage *xfc;
if (last_idx) {
which = last_idx;
}
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 */
return which;
}
got_xfixes_cursor_notify = 0;
/* retrieve the cursor info + pixels from server: */
X_LOCK;
xfc = XFixesGetCursorImage(dpy);
X_UNLOCK;
if (! xfc) {
/* failure. */
return which;
}
which = store_cursor(xfc->cursor_serial, xfc->pixels,
xfc->width, xfc->height, 32, xfc->xhot, xfc->yhot);
X_LOCK;
XFree_wr(xfc);
X_UNLOCK;
#endif
}
return(which);
}
int store_cursor(int serial, unsigned long *data, int w, int h, int cbpp,
int xhot, int yhot) {
int which = CURS_ARROW;
int use, oldest, i;
time_t oldtime, now;
#if 0
fprintf(stderr, "sc: %d %d/%d %d - %d %d\n", serial, w, h, cbpp, xhot, yhot);
#endif
oldest = CURS_DYN_MIN;
if (screen && screen->cursor == cursors[oldest]->rfb) {
oldest++;
}
oldtime = curs_times[oldest];
now = time(NULL);
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 (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;
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;
}
if (rotating && rotating_cursors) {
char *dst;
int tx, ty;
dst = (char *) malloc(w * h * cbpp/8);
rotate_curs(dst, (char *) data, w, h, cbpp/8);
memcpy(data, dst, w * h * cbpp/8);
free(dst);
rotate_coords(xhot, yhot, &tx, &ty, w, h);
xhot = tx;
yhot = ty;
if (! rotating_same) {
int tmp = w;
w = h;
h = tmp;
}
}
/* place cursor into our collection */
cursors[use]->rfb = pixels2curs(data, w, h, xhot, yhot, bpp/8);
/* update time and serial index: */
curs_times[use] = now;
curs_index[use] = serial;
last_index = use;
last_cursor = serial;
which = last_index;
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;
set_cursor_was_changed(screen);
}
}
}
int get_which_cursor(void) {
int which = CURS_ARROW;
int db = 0;
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 */
if (! cursor_drag_changes) {
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) {
int try_macosx = 0;
#ifdef MACOSX
if (! dpy) try_macosx = 1;
#endif
if ((xfixes_present && use_xfixes) || try_macosx) {
if (db) fprintf(stderr, "get_which_cursor call get_exact_cursor\n");
return get_exact_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 && dpy) {
int ratio = 10, x, y;
unsigned int w, h, bw, d;
Window r;
#if !NO_X11
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;
#endif /* NO_X11 */
}
if (which == which0) {
/* the string "term" means 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;
}
}
}
}
if (db) fprintf(stderr, "get_which_cursor which: %d\n", which);
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);
}
void set_warrow_cursor(void) {
set_rfb_cursor(CURS_WARROW);
}
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 = 0;
int root_x, root_y, win_x, win_y;
int x, y;
unsigned int mask;
int macosx_rawfb_ret = 0;
if (unixpw_in_progress) return 0;
#ifdef MACOSX
if (macosx_rawfb_ret) {
RAWFB_RET(0)
}
if (dpy) {
;
} else {
ret = macosx_get_cursor_pos(&root_x, &root_y);
}
#else
RAWFB_RET(0)
#if NO_X11
return 0;
#endif
#endif
#if ! NO_X11
if (dpy) {
X_LOCK;
ret = XQueryPointer_wr(dpy, rootwin, &root_w, &child_w, &root_x, &root_y,
&win_x, &win_y, &mask);
X_UNLOCK;
}
#endif /* NO_X11 */
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());
}