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/xwrappers.c

698 lines
17 KiB

/* -- xwrappers.c -- */
#include "x11vnc.h"
#include "xrecord.h"
#include "keyboard.h"
int xshm_present = 0;
int xtest_present = 0;
int xtrap_present = 0;
int xrecord_present = 0;
int xkb_present = 0;
int xinerama_present = 0;
int keycode_state[256];
int rootshift = 0;
int clipshift = 0;
int guess_bits_per_color(int bits_per_pixel);
Status XShmGetImage_wr(Display *disp, Drawable d, XImage *image, int x, int y,
unsigned long mask);
XImage *XShmCreateImage_wr(Display* disp, Visual* vis, unsigned int depth,
int format, char* data, XShmSegmentInfo* shminfo, unsigned int width,
unsigned int height);
Status XShmAttach_wr(Display *disp, XShmSegmentInfo *shminfo);
Status XShmDetach_wr(Display *disp, XShmSegmentInfo *shminfo);
Bool XShmQueryExtension_wr(Display *disp);
XImage *xreadscreen(Display *disp, Drawable d, int x, int y,
unsigned int width, unsigned int height, Bool show_cursor);
XImage *XGetSubImage_wr(Display *disp, Drawable d, int x, int y,
unsigned int width, unsigned int height, unsigned long plane_mask,
int format, XImage *dest_image, int dest_x, int dest_y);
XImage *XGetImage_wr(Display *disp, Drawable d, int x, int y,
unsigned int width, unsigned int height, unsigned long plane_mask,
int format);
XImage *XCreateImage_wr(Display *disp, Visual *visual, unsigned int depth,
int format, int offset, char *data, unsigned int width,
unsigned int height, int bitmap_pad, int bytes_per_line);
void copy_image(XImage *dest, int x, int y, unsigned int w, unsigned int h);
void init_track_keycode_state(void);
void XTRAP_FakeKeyEvent_wr(Display* dpy, KeyCode key, Bool down,
unsigned long delay);
void XTestFakeKeyEvent_wr(Display* dpy, KeyCode key, Bool down,
unsigned long delay);
void XTRAP_FakeButtonEvent_wr(Display* dpy, unsigned int button, Bool is_press,
unsigned long delay);
void XTestFakeButtonEvent_wr(Display* dpy, unsigned int button, Bool is_press,
unsigned long delay);
void XTRAP_FakeMotionEvent_wr(Display* dpy, int screen, int x, int y,
unsigned long delay);
void XTestFakeMotionEvent_wr(Display* dpy, int screen, int x, int y,
unsigned long delay);
Bool XTestCompareCurrentCursorWithWindow_wr(Display* dpy, Window w);
Bool XTestCompareCursorWithWindow_wr(Display* dpy, Window w, Cursor cursor);
Bool XTestQueryExtension_wr(Display *dpy, int *ev, int *er, int *maj,
int *min);
void XTestDiscard_wr(Display *dpy);
Bool XETrapQueryExtension_wr(Display *dpy, int *ev, int *er, int *op);
int XTestGrabControl_wr(Display *dpy, Bool impervious);
int XTRAP_GrabControl_wr(Display *dpy, Bool impervious);
void disable_grabserver(Display *in_dpy, int change);
Bool XRecordQueryVersion_wr(Display *dpy, int *maj, int *min);
static void copy_raw_fb(XImage *dest, int x, int y, unsigned int w, unsigned int h);
static void upup_downdown_warning(KeyCode key, Bool down);
/*
* used in rfbGetScreen and rfbNewFramebuffer: and estimate to the number
* of bits per color, of course for some visuals, e.g. 565, the number
* is not the same for each color. This is just a sane default.
*/
int guess_bits_per_color(int bits_per_pixel) {
int bits_per_color;
/* first guess, spread them "evenly" over R, G, and B */
bits_per_color = bits_per_pixel/3;
if (bits_per_color < 1) {
bits_per_color = 1; /* 1bpp, 2bpp... */
}
/* choose safe values for usual cases: */
if (bits_per_pixel == 8) {
bits_per_color = 2;
} else if (bits_per_pixel == 15 || bits_per_pixel == 16) {
bits_per_color = 5;
} else if (bits_per_pixel == 24 || bits_per_pixel == 32) {
bits_per_color = 8;
}
return bits_per_color;
}
/*
* Kludge to interpose image gets and limit to a subset rectangle of
* the rootwin. This is the -sid option trying to work around invisible
* saveUnders menu, etc, windows. Also -clip option.
*/
#define ADJUST_ROOTSHIFT \
if (rootshift && subwin) { \
d = rootwin; \
x += off_x; \
y += off_y; \
} \
if (clipshift) { \
x += coff_x; \
y += coff_y; \
}
/*
* Wrappers for Image related X calls
*/
Status XShmGetImage_wr(Display *disp, Drawable d, XImage *image, int x, int y,
unsigned long mask) {
ADJUST_ROOTSHIFT
/* Note: the Solaris overlay stuff is all non-shm (using_shm = 0) */
#if LIBVNCSERVER_HAVE_XSHM
return XShmGetImage(disp, d, image, x, y, mask);
#else
return (Status) 0;
#endif
}
XImage *XShmCreateImage_wr(Display* disp, Visual* vis, unsigned int depth,
int format, char* data, XShmSegmentInfo* shminfo, unsigned int width,
unsigned int height) {
#if LIBVNCSERVER_HAVE_XSHM
return XShmCreateImage(disp, vis, depth, format, data, shminfo,
width, height);
#else
return (XImage *) 0;
#endif
}
Status XShmAttach_wr(Display *disp, XShmSegmentInfo *shminfo) {
#if LIBVNCSERVER_HAVE_XSHM
return XShmAttach(disp, shminfo);
#else
return (Status) 0;
#endif
}
Status XShmDetach_wr(Display *disp, XShmSegmentInfo *shminfo) {
#if LIBVNCSERVER_HAVE_XSHM
return XShmDetach(disp, shminfo);
#else
return (Status) 0;
#endif
}
Bool XShmQueryExtension_wr(Display *disp) {
#if LIBVNCSERVER_HAVE_XSHM
return XShmQueryExtension(disp);
#else
return False;
#endif
}
/* wrapper for overlay screen reading: */
XImage *xreadscreen(Display *disp, Drawable d, int x, int y,
unsigned int width, unsigned int height, Bool show_cursor) {
#ifdef SOLARIS_OVERLAY
return XReadScreen(disp, d, x, y, width, height,
show_cursor);
#else
# ifdef IRIX_OVERLAY
{ unsigned long hints = 0, hints_ret;
if (show_cursor) hints |= XRD_READ_POINTER;
return XReadDisplay(disp, d, x, y, width, height,
hints, &hints_ret);
}
# else
/* unused vars warning: */
if (disp || d || x || y || width || height || show_cursor) {}
return NULL;
# endif
#endif
}
XImage *XGetSubImage_wr(Display *disp, Drawable d, int x, int y,
unsigned int width, unsigned int height, unsigned long plane_mask,
int format, XImage *dest_image, int dest_x, int dest_y) {
ADJUST_ROOTSHIFT
if (overlay && dest_x == 0 && dest_y == 0) {
size_t size = dest_image->height * dest_image->bytes_per_line;
XImage *xi;
xi = xreadscreen(disp, d, x, y, width, height,
(Bool) overlay_cursor);
if (! xi) return NULL;
/*
* There is extra overhead from memcpy and free...
* this is not like the real XGetSubImage(). We hope
* this significant overhead is still small compared to
* the time to retrieve the fb data.
*/
memcpy(dest_image->data, xi->data, size);
XDestroyImage(xi);
return (dest_image);
}
return XGetSubImage(disp, d, x, y, width, height, plane_mask,
format, dest_image, dest_x, dest_y);
}
XImage *XGetImage_wr(Display *disp, Drawable d, int x, int y,
unsigned int width, unsigned int height, unsigned long plane_mask,
int format) {
ADJUST_ROOTSHIFT
if (overlay) {
return xreadscreen(disp, d, x, y, width, height,
(Bool) overlay_cursor);
}
return XGetImage(disp, d, x, y, width, height, plane_mask, format);
}
XImage *XCreateImage_wr(Display *disp, Visual *visual, unsigned int depth,
int format, int offset, char *data, unsigned int width,
unsigned int height, int bitmap_pad, int bytes_per_line) {
/*
* This is a kludge to get a created XImage to exactly match what
* XReadScreen returns: we noticed the rgb masks are different
* from XCreateImage with the high color visual (red mask <->
* blue mask). Note we read from the root window(!) then free
* the data.
*/
if (raw_fb) { /* raw_fb hack */
XImage *xi;
xi = (XImage *) malloc(sizeof(XImage));
memset(xi, 0, sizeof(XImage));
xi->depth = depth;
xi->bits_per_pixel = (depth == 24) ? 32 : depth;
xi->format = format;
xi->xoffset = offset;
xi->data = data;
xi->width = width;
xi->height = height;
xi->bitmap_pad = bitmap_pad;
xi->bytes_per_line = bytes_per_line ? bytes_per_line :
xi->width * xi->bits_per_pixel / 8;
return xi;
}
if (overlay) {
XImage *xi;
xi = xreadscreen(disp, window, 0, 0, width, height, False);
if (xi == NULL) {
return xi;
}
if (xi->data != NULL) {
free(xi->data);
}
xi->data = data;
return xi;
}
return XCreateImage(disp, visual, depth, format, offset, data,
width, height, bitmap_pad, bytes_per_line);
}
static void copy_raw_fb(XImage *dest, int x, int y, unsigned int w, unsigned int h) {
char *src, *dst;
unsigned int line;
int pixelsize = bpp/8;
int bpl = wdpy_x * pixelsize;
if (clipshift) {
x += coff_x;
y += coff_y;
}
if (! raw_fb_seek) {
src = raw_fb_addr + raw_fb_offset + bpl*y + pixelsize*x;
dst = dest->data;
for (line = 0; line < h; line++) {
memcpy(dst, src, w * pixelsize);
src += bpl;
dst += dest->bytes_per_line;
}
} else{
int n, len, del, sz = w * pixelsize;
off_t off = (off_t) (raw_fb_offset + bpl*y + pixelsize*x);
lseek(raw_fb_fd, off, SEEK_SET);
dst = dest->data;
for (line = 0; line < h; line++) {
len = sz;
del = 0;
while (len > 0) {
n = read(raw_fb_fd, dst + del, len);
if (n > 0) {
del += n;
len -= n;
} else if (n == 0) {
break;
} else {
/* overkill... */
if (errno != EINTR && errno != EAGAIN) {
break;
}
}
}
if (bpl > sz) {
off = (off_t) (bpl - sz);
lseek(raw_fb_fd, off, SEEK_CUR);
}
dst += dest->bytes_per_line;
}
}
}
void copy_image(XImage *dest, int x, int y, unsigned int w, unsigned int h) {
/* default (w=0, h=0) is the fill the entire XImage */
if (w < 1) {
w = dest->width;
}
if (h < 1) {
h = dest->height;
}
if (use_snapfb && snap_fb && dest != snaprect) {
char *src, *dst;
unsigned int line;
int pixelsize = bpp/8;
src = snap->data + snap->bytes_per_line*y + pixelsize*x;
dst = dest->data;
for (line = 0; line < h; line++) {
memcpy(dst, src, w * pixelsize);
src += snap->bytes_per_line;
dst += dest->bytes_per_line;
}
} else if (raw_fb) {
copy_raw_fb(dest, x, y, w, h);
} else if (using_shm && (int) w == dest->width &&
(int) h == dest->height) {
XShmGetImage_wr(dpy, window, dest, x, y, AllPlanes);
} else {
XGetSubImage_wr(dpy, window, x, y, w, h, AllPlanes,
ZPixmap, dest, 0, 0);
}
}
#define DEBUG_SKIPPED_INPUT(dbg, str) \
if (dbg) { \
rfbLog("skipped input: %s\n", str); \
}
void init_track_keycode_state(void) {
int i;
for (i=0; i<256; i++) {
keycode_state[i] = 0;
}
get_keystate(keycode_state);
}
static void upup_downdown_warning(KeyCode key, Bool down) {
if ((down ? 1:0) == keycode_state[(int) key]) {
rfbLog("XTestFakeKeyEvent: keycode=0x%x \"%s\" is *already* "
"%s\n", key, XKeysymToString(XKeycodeToKeysym(dpy, key, 0)),
down ? "down":"up");
}
}
/*
* wrappers for XTestFakeKeyEvent, etc..
* also for XTrap equivalents XESimulateXEventRequest
*/
void XTRAP_FakeKeyEvent_wr(Display* dpy, KeyCode key, Bool down,
unsigned long delay) {
if (! xtrap_present) {
DEBUG_SKIPPED_INPUT(debug_keyboard, "keyboard: no-XTRAP");
return;
}
/* unused vars warning: */
if (dpy || key || down || delay) {}
#if LIBVNCSERVER_HAVE_LIBXTRAP
XESimulateXEventRequest(trap_ctx, down ? KeyPress : KeyRelease,
key, 0, 0, 0);
if (debug_keyboard) {
upup_downdown_warning(key, down);
}
keycode_state[(int) key] = down ? 1 : 0;
#else
DEBUG_SKIPPED_INPUT(debug_keyboard, "keyboard: no-XTRAP-build");
#endif
}
void XTestFakeKeyEvent_wr(Display* dpy, KeyCode key, Bool down,
unsigned long delay) {
static int first = 1;
if (debug_keyboard) {
rfbLog("XTestFakeKeyEvent(dpy, keycode=0x%x \"%s\", %s)\n",
key, XKeysymToString(XKeycodeToKeysym(dpy, key, 0)),
down ? "down":"up");
}
if (first) {
init_track_keycode_state();
first = 0;
}
if (down) {
last_keyboard_keycode = -key;
} else {
last_keyboard_keycode = key;
}
if (xtrap_input) {
XTRAP_FakeKeyEvent_wr(dpy, key, down, delay);
return;
}
if (! xtest_present) {
DEBUG_SKIPPED_INPUT(debug_keyboard, "keyboard: no-XTEST");
return;
}
if (debug_keyboard) {
rfbLog("calling XTestFakeKeyEvent(%d, %d) %.4f\n",
key, down, dnow() - x11vnc_start);
}
#if LIBVNCSERVER_HAVE_XTEST
XTestFakeKeyEvent(dpy, key, down, delay);
if (debug_keyboard) {
upup_downdown_warning(key, down);
}
keycode_state[(int) key] = down ? 1 : 0;
#endif
}
void XTRAP_FakeButtonEvent_wr(Display* dpy, unsigned int button, Bool is_press,
unsigned long delay) {
if (! xtrap_present) {
DEBUG_SKIPPED_INPUT(debug_keyboard, "button: no-XTRAP");
return;
}
/* unused vars warning: */
if (dpy || button || is_press || delay) {}
#if LIBVNCSERVER_HAVE_LIBXTRAP
XESimulateXEventRequest(trap_ctx,
is_press ? ButtonPress : ButtonRelease, button, 0, 0, 0);
#else
DEBUG_SKIPPED_INPUT(debug_keyboard, "button: no-XTRAP-build");
#endif
}
void XTestFakeButtonEvent_wr(Display* dpy, unsigned int button, Bool is_press,
unsigned long delay) {
if (xtrap_input) {
XTRAP_FakeButtonEvent_wr(dpy, button, is_press, delay);
return;
}
if (! xtest_present) {
DEBUG_SKIPPED_INPUT(debug_keyboard, "button: no-XTEST");
return;
}
if (debug_pointer) {
rfbLog("calling XTestFakeButtonEvent(%d, %d) %.4f\n",
button, is_press, dnow() - x11vnc_start);
}
#if LIBVNCSERVER_HAVE_XTEST
XTestFakeButtonEvent(dpy, button, is_press, delay);
#endif
}
void XTRAP_FakeMotionEvent_wr(Display* dpy, int screen, int x, int y,
unsigned long delay) {
if (! xtrap_present) {
DEBUG_SKIPPED_INPUT(debug_keyboard, "motion: no-XTRAP");
return;
}
/* unused vars warning: */
if (dpy || screen || x || y || delay) {}
#if LIBVNCSERVER_HAVE_LIBXTRAP
XESimulateXEventRequest(trap_ctx, MotionNotify, 0, x, y, 0);
#else
DEBUG_SKIPPED_INPUT(debug_keyboard, "motion: no-XTRAP-build");
#endif
}
void XTestFakeMotionEvent_wr(Display* dpy, int screen, int x, int y,
unsigned long delay) {
if (xtrap_input) {
XTRAP_FakeMotionEvent_wr(dpy, screen, x, y, delay);
return;
}
if (debug_pointer) {
rfbLog("calling XTestFakeMotionEvent(%d, %d) %.4f\n",
x, y, dnow() - x11vnc_start);
}
#if LIBVNCSERVER_HAVE_XTEST
XTestFakeMotionEvent(dpy, screen, x, y, delay);
#endif
}
Bool XTestCompareCurrentCursorWithWindow_wr(Display* dpy, Window w) {
if (! xtest_present) {
return False;
}
#if LIBVNCSERVER_HAVE_XTEST
return XTestCompareCurrentCursorWithWindow(dpy, w);
#else
return False;
#endif
}
Bool XTestCompareCursorWithWindow_wr(Display* dpy, Window w, Cursor cursor) {
if (! xtest_present) {
return False;
}
#if LIBVNCSERVER_HAVE_XTEST
return XTestCompareCursorWithWindow(dpy, w, cursor);
#else
return False;
#endif
}
Bool XTestQueryExtension_wr(Display *dpy, int *ev, int *er, int *maj,
int *min) {
#if LIBVNCSERVER_HAVE_XTEST
return XTestQueryExtension(dpy, ev, er, maj, min);
#else
return False;
#endif
}
void XTestDiscard_wr(Display *dpy) {
if (! xtest_present) {
return;
}
#if LIBVNCSERVER_HAVE_XTEST
XTestDiscard(dpy);
#endif
}
Bool XETrapQueryExtension_wr(Display *dpy, int *ev, int *er, int *op) {
#if LIBVNCSERVER_HAVE_LIBXTRAP
return XETrapQueryExtension(dpy, (INT32 *)ev, (INT32 *)er,
(INT32 *)op);
#else
/* unused vars warning: */
if (dpy || ev || er || op) {}
return False;
#endif
}
int XTestGrabControl_wr(Display *dpy, Bool impervious) {
if (! xtest_present) {
return 0;
}
#if LIBVNCSERVER_HAVE_XTEST && LIBVNCSERVER_HAVE_XTESTGRABCONTROL
XTestGrabControl(dpy, impervious);
return 1;
#else
return 0;
#endif
}
int XTRAP_GrabControl_wr(Display *dpy, Bool impervious) {
if (! xtrap_present) {
/* unused vars warning: */
if (dpy || impervious) {}
return 0;
}
#if LIBVNCSERVER_HAVE_LIBXTRAP
else {
ReqFlags requests;
if (! impervious) {
if (trap_ctx) {
XEFreeTC(trap_ctx);
}
trap_ctx = NULL;
return 1;
}
if (! trap_ctx) {
trap_ctx = XECreateTC(dpy, 0, NULL);
if (! trap_ctx) {
rfbLog("DEC-XTRAP XECreateTC failed. Watch "
"out for XGrabServer from wm's\n");
return 0;
}
XEStartTrapRequest(trap_ctx);
memset(requests, 0, sizeof(requests));
BitTrue(requests, X_GrabServer);
BitTrue(requests, X_UngrabServer);
XETrapSetRequests(trap_ctx, True, requests);
XETrapSetGrabServer(trap_ctx, True);
}
return 1;
}
#endif
return 0;
}
void disable_grabserver(Display *in_dpy, int change) {
int ok = 0;
static int didmsg = 0;
if (debug_grabs) {
fprintf(stderr, "disable_grabserver/%d %.5f\n",
xserver_grabbed, dnowx());
didmsg = 0;
}
if (! xtrap_input) {
if (XTestGrabControl_wr(in_dpy, True)) {
if (change) {
XTRAP_GrabControl_wr(in_dpy, False);
}
if (! didmsg) {
rfbLog("GrabServer control via XTEST.\n");
didmsg = 1;
}
ok = 1;
} else {
if (XTRAP_GrabControl_wr(in_dpy, True)) {
ok = 1;
if (! didmsg) {
rfbLog("Using DEC-XTRAP for protection"
" from XGrabServer.\n");
didmsg = 1;
}
}
}
} else {
if (XTRAP_GrabControl_wr(in_dpy, True)) {
if (change) {
XTestGrabControl_wr(in_dpy, False);
}
if (! didmsg) {
rfbLog("GrabServer control via DEC-XTRAP.\n");
didmsg = 1;
}
ok = 1;
} else {
if (XTestGrabControl_wr(in_dpy, True)) {
ok = 1;
if (! didmsg) {
rfbLog("DEC-XTRAP XGrabServer "
"protection not available, "
"using XTEST.\n");
didmsg = 1;
}
}
}
}
if (! ok && ! didmsg) {
rfbLog("No XTEST or DEC-XTRAP protection from XGrabServer.\n");
rfbLog("Deadlock if your window manager calls XGrabServer!!\n");
}
XFlush(in_dpy);
}
Bool XRecordQueryVersion_wr(Display *dpy, int *maj, int *min) {
#if LIBVNCSERVER_HAVE_RECORD
return XRecordQueryVersion(dpy, maj, min);
#else
return False;
#endif
}