From 935766a48de111fd5422d556b35546f9358f2ca2 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sun, 28 Dec 2014 10:36:47 +0100 Subject: [PATCH 01/26] multipointer: Add detection of XInput2, libXCursor and Cairo to build system. --- Makefile.am | 5 ++--- configure.ac | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/Makefile.am b/Makefile.am index fcc011da..4afcc7e5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -31,9 +31,8 @@ endif bin_PROGRAMS=x11vnc x11vnc_SOURCES = 8to24.c appshare.c avahi.c cleanup.c connections.c cursor.c gui.c help.c inet.c keyboard.c linuxfb.c macosx.c macosxCG.c macosxCGP.c macosxCGS.c macosx_opengl.c options.c pm.c pointer.c rates.c remote.c scan.c screen.c selection.c solid.c sslcmds.c sslhelper.c uinput.c unixpw.c user.c userinput.c util.c v4l.c win_utils.c x11vnc.c x11vnc_defs.c xdamage.c xevents.c xinerama.c xkb_bell.c xrandr.c xrecord.c xwrappers.c 8to24.h allowed_input_t.h avahi.h blackout_t.h cleanup.h connections.h cursor.h enc.h enums.h gui.h help.h inet.h keyboard.h linuxfb.h macosx.h macosxCG.h macosxCGP.h macosxCGS.h macosx_opengl.h nox11.h nox11_funcs.h options.h params.h pm.h pointer.h rates.h remote.h scan.h screen.h scrollevent_t.h selection.h solid.h sslcmds.h sslhelper.h ssltools.h tkx11vnc.h uinput.h unixpw.h user.h userinput.h util.h v4l.h win_utils.h winattr_t.h x11vnc.h xdamage.h xevents.h xinerama.h xkb_bell.h xrandr.h xrecord.h xwrappers.h default8x16.h - INCLUDES_LIBVNCSERVER = @LIBVNCSERVER_CFLAGS@ @LIBVNCCLIENT_CFLAGS@ -AM_CPPFLAGS = $(INCLUDES_LIBVNCSERVER) @X_CFLAGS@ @AVAHI_CFLAGS@ +AM_CPPFLAGS = $(INCLUDES_LIBVNCSERVER) @X_CFLAGS@ @AVAHI_CFLAGS@ @XI2_CFLAGS@ @CAIRO_CFLAGS@ -x11vnc_LDADD=$(LDADD) @SSL_LIBS@ @CRYPT_LIBS@ @X_LIBS@ @AVAHI_LIBS@ $(LD_CYGIPC) +x11vnc_LDADD=$(LDADD) @SSL_LIBS@ @CRYPT_LIBS@ @X_LIBS@ @AVAHI_LIBS@ @XI2_LIBS@ @CAIRO_LIBS@ $(LD_CYGIPC) diff --git a/configure.ac b/configure.ac index 2809771f..5e2048ac 100644 --- a/configure.ac +++ b/configure.ac @@ -91,6 +91,8 @@ AH_TEMPLATE(HAVE_LIBXRANDR, [XRANDR extension build environment present]) AH_TEMPLATE(HAVE_LIBXFIXES, [XFIXES extension build environment present]) AH_TEMPLATE(HAVE_LIBXDAMAGE, [XDAMAGE extension build environment present]) AH_TEMPLATE(HAVE_LIBXCOMPOSITE, [XCOMPOSITE extension build environment present]) +AH_TEMPLATE(HAVE_LIBXCURSOR, [Xcursor library build environment present]) +AH_TEMPLATE(HAVE_XI2, [XINPUT 2 extension build environment present]) AH_TEMPLATE(HAVE_LIBXTRAP, [DEC-XTRAP extension build environment present]) AH_TEMPLATE(HAVE_RECORD, [RECORD extension build environment present]) AH_TEMPLATE(HAVE_SOLARIS_XREADSCREEN, [Solaris XReadScreen available]) @@ -132,6 +134,9 @@ AC_ARG_WITH(uinput, [ --without-uinput disable linux uinput device support],,) AC_ARG_WITH(macosx-native, [ --without-macosx-native disable MacOS X native display support],,) +AC_ARG_WITH(colormultipointer, +[ --without-colormultipointer disable color support for multiple pointers] +[ --with-cairo=DIR use cairo include/library files in DIR (needed for color cursor support)],,) fi # end x11vnc only. @@ -251,6 +256,19 @@ elif test "$X_CFLAGS" != "-DX_DISPLAY_MISSING"; then $X_LIBS $X_PRELIBS -lX11 $X_EXTRA_LIBS) fi + # check for XI2 support + PKG_CHECK_MODULES(XI2, [xi >= 1.2.99] [inputproto >= 1.9.99.9], + HAVE_XI2="yes"; AC_DEFINE(HAVE_XI2, 1, [XI2 available]), + HAVE_XI2="no"); + + if test "$HAVE_XI2" = "yes" -a "x$with_colormultipointer" != "xno"; then + AC_CHECK_LIB(Xcursor, XcursorImageLoadCursor, + X_PRELIBS="$X_PRELIBS -lXcursor" + [AC_DEFINE(HAVE_LIBXCURSOR) HAVE_LIBXCURSOR="true"], , + $X_LIBS $X_PRELIBS -lX11 $X_EXTRA_LIBS) + fi + + if test ! -z "$HAVE_LIBXFIXES" -o ! -z "$HAVE_LIBXDAMAGE"; then # need /usr/sfw/lib in RPATH for Solaris 10 and later case `(uname -sr) 2>/dev/null` in @@ -361,9 +379,42 @@ if test "x$with_avahi" = "xyes"; then AC_SUBST(AVAHI_LIBS) fi +if test "$HAVE_XI2" = "yes"; then +AH_TEMPLATE(HAVE_CAIRO, [cairo graphics library present]) +if test "x$with_cairo" != "xno"; then + printf "checking for cairo... " + if test ! -z "$with_cairo" -a "x$with_cairo" != "xyes"; then + CAIRO_CFLAGS="-I$with_cairo/include" + CAIRO_LIBS="-L$with_cairo/lib -lcairo" + echo "using $with_cairo" + with_cairo=yes + elif pkg-config cairo >/dev/null 2>&1; then + CAIRO_CFLAGS=`pkg-config --cflags cairo` + CAIRO_LIBS=`pkg-config --libs cairo` + with_cairo=yes + echo yes + else + with_cairo=no + echo no + fi +fi +if test "x$with_cairo" = "xyes"; then + AC_DEFINE(HAVE_CAIRO) + AC_SUBST(CAIRO_CFLAGS) + AC_SUBST(CAIRO_LIBS) +fi +fi + + fi # end x11vnc only. +# XI2 present? +AM_CONDITIONAL(HAVE_XI2, [ test "$HAVE_XI2" = "yes" ]) +# cairo? +AM_CONDITIONAL(HAVE_CAIRO, [ test "$with_cairo" = "yes" ]) + + # only used in x11vnc/Makefile.am but needs to always be defined: AM_CONDITIONAL(OSX_OPENGL, test "$HAVE_MACOSX_OPENGL_H" = "true") From 0c967dc259c295198bc2c6e32fa358208a3c932d Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sun, 28 Dec 2014 11:01:21 +0100 Subject: [PATCH 02/26] multipointer: Add functions to find client window, stolen from xwininfo. --- win_utils.c | 130 +++++++++++++++++++++++++++++++++++++++++++++------- win_utils.h | 16 +++++++ 2 files changed, 130 insertions(+), 16 deletions(-) diff --git a/win_utils.c b/win_utils.c index 7aa9b4c2..45f70d4a 100644 --- a/win_utils.c +++ b/win_utils.c @@ -40,26 +40,12 @@ so, delete this exception statement from your version. #include "connections.h" #include "xrandr.h" #include "macosx.h" +#include "win_utils.h" winattr_t *stack_list = NULL; int stack_list_len = 0; int stack_list_num = 0; - - -Window parent_window(Window win, char **name); -int valid_window(Window win, XWindowAttributes *attr_ret, int bequiet); -Bool xtranslate(Window src, Window dst, int src_x, int src_y, int *dst_x, - int *dst_y, Window *child, int bequiet); -int get_window_size(Window win, int *w, int *h); -void snapshot_stack_list(int free_only, double allowed_age); -int get_boff(void); -int get_bwin(void); -void update_stack_list(void); -Window query_pointer(Window start); -unsigned int mask_state(void); -int pick_windowid(unsigned long *num); -Window descend_pointer(int depth, Window start, char *name_info, int len); -void id_cmd(char *cmd); +static Atom atom_wm_state = None; Window parent_window(Window win, char **name) { @@ -769,3 +755,115 @@ void id_cmd(char *cmd) { #endif } + + +/* + * Check if window has given property + */ +Bool window_has_property(Display * dpy, Window win, Atom atom) +{ + Atom type_ret; + int format_ret; + unsigned char *prop_ret; + unsigned long bytes_after, num_ret; + + type_ret = None; + prop_ret = NULL; + XGetWindowProperty(dpy, win, atom, 0, 0, False, AnyPropertyType, + &type_ret, &format_ret, &num_ret, + &bytes_after, &prop_ret); + if (prop_ret) + XFree(prop_ret); + + return (type_ret != None) ? True : False; +} + +/* + * Check if window is viewable + */ +Bool window_is_viewable(Display * dpy, Window win) +{ + Bool ok; + XWindowAttributes xwa; + + XGetWindowAttributes(dpy, win, &xwa); + + ok = (xwa.class == InputOutput) && (xwa.map_state == IsViewable); + + return ok; +} + + +/* + * Find a window that has WM_STATE set in the window tree below win. + * Unmapped/unviewable windows are not considered valid matches. + * Children are searched in top-down stacking order. + * The first matching window is returned, None if no match is found. + */ +static Window find_client_in_children(Display * dpy, Window win) +{ + Window root, parent; + Window *children; + unsigned int n_children; + int i; + + if (!XQueryTree(dpy, win, &root, &parent, &children, &n_children)) + return None; + if (!children) + return None; + + /* Check each child for WM_STATE and other validity */ + win = None; + for (i = (int) n_children - 1; i >= 0; i--) { + if (!window_is_viewable(dpy, children[i])) { + children[i] = None; /* Don't bother descending into this one */ + continue; + } + if (!window_has_property(dpy, children[i], atom_wm_state)) + continue; + + /* Got one */ + win = children[i]; + goto done; + } + + /* No children matched, now descend into each child */ + for (i = (int) n_children - 1; i >= 0; i--) { + if (children[i] == None) + continue; + win = find_client_in_children(dpy, children[i]); + if (win != None) + break; + } + + done: + XFree(children); + + return win; +} + +/* + find a client window, either subwin itself or one of its children +*/ +Window find_client(Display * dpy, Window root, Window subwin) +{ + Window win; + + if (atom_wm_state == None) { + atom_wm_state = XInternAtom(dpy, "WM_STATE", False); + if (!atom_wm_state) + return subwin; + } + + /* Check if subwin has WM_STATE */ + if (window_has_property(dpy, subwin, atom_wm_state)) + return subwin; + + /* Attempt to find a client window in subwin's children */ + win = find_client_in_children(dpy, subwin); + if (win != None) + return win; /* Found a client */ + + /* Did not find a client */ + return subwin; +} diff --git a/win_utils.h b/win_utils.h index 0b991652..3c229535 100644 --- a/win_utils.h +++ b/win_utils.h @@ -56,4 +56,20 @@ extern int pick_windowid(unsigned long *num); extern Window descend_pointer(int depth, Window start, char *name_info, int len); extern void id_cmd(char *cmd); +/** + * Find a client window, either subwin itself or one of its children. + */ +extern Window find_client(Display * dpy, Window root, Window subwin); + +/** + * Check if window has given property. + */ +extern Bool window_has_property(Display * dpy, Window win, Atom atom); + +/** + * Check if window is viewable. + */ +extern Bool window_is_viewable(Display * dpy, Window win); + + #endif /* _X11VNC_WIN_UTILS_H */ From 3c730efc329859bd3d8df7e40acdcd510759777e Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sun, 28 Dec 2014 11:01:43 +0100 Subject: [PATCH 03/26] multipointer: Make pixels2curs() public. --- cursor.c | 5 ++--- cursor.h | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cursor.c b/cursor.c index ada89129..31157779 100644 --- a/cursor.c +++ b/cursor.c @@ -69,6 +69,7 @@ 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); +rfbCursorPtr pixels2curs(unsigned long *pixels, int w, int h, int xhot, int yhot, int Bpp); typedef struct win_str_info { @@ -91,8 +92,6 @@ 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); @@ -1003,7 +1002,7 @@ void initialize_xfixes(void) { #endif } -static rfbCursorPtr pixels2curs(unsigned long *pixels, int w, int h, +rfbCursorPtr pixels2curs(unsigned long *pixels, int w, int h, int xhot, int yhot, int Bpp) { rfbCursorPtr c; static unsigned long black = 0, white = 1; diff --git a/cursor.h b/cursor.h index 9c749440..05986dd3 100644 --- a/cursor.h +++ b/cursor.h @@ -66,5 +66,6 @@ extern int set_cursor(int x, int y, int which); extern int check_x11_pointer(void); extern int store_cursor(int serial, unsigned long *data, int w, int h, int cbpp, int xhot, int yhot); extern unsigned long get_cursor_serial(int mode); +extern rfbCursorPtr pixels2curs(unsigned long *pixels, int w, int h, int xhot, int yhot, int Bpp); #endif /* _X11VNC_CURSOR_H */ From a5f394286de173f696d2296b00b744ae63db6224 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sun, 28 Dec 2014 11:02:08 +0100 Subject: [PATCH 04/26] multipointer: Add xi2 device handling routines. --- Makefile.am | 2 +- xi2_devices.c | 410 ++++++++++++++++++++++++++++++++++++++++++++++++++ xi2_devices.h | 74 +++++++++ 3 files changed, 485 insertions(+), 1 deletion(-) create mode 100644 xi2_devices.c create mode 100644 xi2_devices.h diff --git a/Makefile.am b/Makefile.am index 4afcc7e5..84250bdd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -29,7 +29,7 @@ LD_CYGIPC=-lcygipc endif bin_PROGRAMS=x11vnc -x11vnc_SOURCES = 8to24.c appshare.c avahi.c cleanup.c connections.c cursor.c gui.c help.c inet.c keyboard.c linuxfb.c macosx.c macosxCG.c macosxCGP.c macosxCGS.c macosx_opengl.c options.c pm.c pointer.c rates.c remote.c scan.c screen.c selection.c solid.c sslcmds.c sslhelper.c uinput.c unixpw.c user.c userinput.c util.c v4l.c win_utils.c x11vnc.c x11vnc_defs.c xdamage.c xevents.c xinerama.c xkb_bell.c xrandr.c xrecord.c xwrappers.c 8to24.h allowed_input_t.h avahi.h blackout_t.h cleanup.h connections.h cursor.h enc.h enums.h gui.h help.h inet.h keyboard.h linuxfb.h macosx.h macosxCG.h macosxCGP.h macosxCGS.h macosx_opengl.h nox11.h nox11_funcs.h options.h params.h pm.h pointer.h rates.h remote.h scan.h screen.h scrollevent_t.h selection.h solid.h sslcmds.h sslhelper.h ssltools.h tkx11vnc.h uinput.h unixpw.h user.h userinput.h util.h v4l.h win_utils.h winattr_t.h x11vnc.h xdamage.h xevents.h xinerama.h xkb_bell.h xrandr.h xrecord.h xwrappers.h default8x16.h +x11vnc_SOURCES = 8to24.c appshare.c avahi.c cleanup.c connections.c cursor.c gui.c help.c inet.c keyboard.c linuxfb.c macosx.c macosxCG.c macosxCGP.c macosxCGS.c macosx_opengl.c options.c pm.c pointer.c rates.c remote.c scan.c screen.c selection.c solid.c sslcmds.c sslhelper.c uinput.c unixpw.c user.c userinput.c util.c v4l.c win_utils.c x11vnc.c x11vnc_defs.c xdamage.c xevents.c xinerama.c xkb_bell.c xrandr.c xrecord.c xwrappers.c xi2_devices.c 8to24.h allowed_input_t.h avahi.h blackout_t.h cleanup.h connections.h cursor.h enc.h enums.h gui.h help.h inet.h keyboard.h linuxfb.h macosx.h macosxCG.h macosxCGP.h macosxCGS.h macosx_opengl.h nox11.h nox11_funcs.h options.h params.h pm.h pointer.h rates.h remote.h scan.h screen.h scrollevent_t.h selection.h solid.h sslcmds.h sslhelper.h ssltools.h tkx11vnc.h uinput.h unixpw.h user.h userinput.h util.h v4l.h win_utils.h winattr_t.h x11vnc.h xdamage.h xevents.h xinerama.h xkb_bell.h xrandr.h xrecord.h xwrappers.h default8x16.h xi2_devices.h INCLUDES_LIBVNCSERVER = @LIBVNCSERVER_CFLAGS@ @LIBVNCCLIENT_CFLAGS@ diff --git a/xi2_devices.c b/xi2_devices.c new file mode 100644 index 00000000..aa239156 --- /dev/null +++ b/xi2_devices.c @@ -0,0 +1,410 @@ +/* + XInput2 device handling routines for x11vnc. + + Copyright (C) 2009-2010 Christian Beier + All rights reserved. + + This file is part of x11vnc. + + x11vnc is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at + your option) any later version. + + x11vnc is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with x11vnc; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA + or see . +*/ + +#include +#include +#include + +#include "x11vnc.h" +#include "cursor.h" +#include "cleanup.h" +#include "win_utils.h" +#include "xi2_devices.h" + +#ifdef HAVE_LIBXCURSOR +#include +#ifdef HAVE_CAIRO +#include +#endif +#endif + + +/* does the X version we're running on support XI2? */ +int xinput2_present; +int xi2_device_creation_in_progress; + + +/* + create MD with given name + returns device id, -1 on error +*/ +int createMD(Display* dpy, char* name) +{ +#ifndef HAVE_XI2 + return -1; +#else + int dev_id = -1; + XErrorHandler old_handler; + XIAddMasterInfo c; + XIDeviceInfo *devinfo; + int num_devices, i; + char handle[256]; /* device name */ + snprintf(handle, 256, "%s pointer", name); + + c.type = XIAddMaster; + c.name = name; + c.send_core = 1; + c.enable = 1; + + X_LOCK; + + trapped_xerror = 0; + old_handler = XSetErrorHandler(trap_xerror); + + XIChangeHierarchy(dpy, (XIAnyHierarchyChangeInfo*)&c, 1); + XSync(dpy, False); + + if(trapped_xerror) { + XSetErrorHandler(old_handler); + trapped_xerror = 0; + X_UNLOCK; + return -1; + } + + XSetErrorHandler(old_handler); + trapped_xerror = 0; + + /* find newly created dev by name + FIXME: better wait for XIHierarchy event here? */ + devinfo = XIQueryDevice(dpy, XIAllMasterDevices, &num_devices); + for(i = num_devices-1; i >= 0; --i) + if(strcmp(devinfo[i].name, handle) == 0) + { + dev_id = devinfo[i].deviceid; + break; + } + + XIFreeDeviceInfo(devinfo); + + X_UNLOCK; + + return dev_id; +#endif +} + + + +/* + remove device + return 1 on success, 0 on failure +*/ +int removeMD(Display* dpy, int dev_id) +{ +#ifndef HAVE_XI2 + return 0; +#else + int found = 0, res = 0; + XIDeviceInfo *devinfo; + int num_devices, i; + + if(dev_id < 0) + return 0; + + X_LOCK; + + /* see if this device exists */ + devinfo = XIQueryDevice(dpy, XIAllMasterDevices, &num_devices); + for(i = 0; i < num_devices; ++i) + if(devinfo[i].deviceid == dev_id) + found = 1; + XIFreeDeviceInfo(devinfo); + + if(found) { + XIRemoveMasterInfo r; + + /* we need to unset client pointer */ + XISetClientPointer(dpy, None, dev_id); + XSync(dpy, False); + + /* actually remove device pair */ + r.type = XIRemoveMaster; + r.deviceid = dev_id; + r.return_mode = XIFloating; + + res = XIChangeHierarchy(dpy, (XIAnyHierarchyChangeInfo*)&r, 1) == Success ? 1 : 0; + XSync(dpy, False); + } + + X_UNLOCK; + + return res; +#endif +} + + + + + +int getPairedMD(Display* dpy, int dev_id) +{ +#ifndef HAVE_XI2 + return -1; +#else + int paired = -1; + XIDeviceInfo* devinfo; + int devicecount = 0; + + if(dev_id < 0) + return paired; + + X_LOCK; + + devinfo = XIQueryDevice(dpy, dev_id, &devicecount); + if(devicecount) + paired = devinfo->attachment; + XIFreeDeviceInfo(devinfo); + + X_UNLOCK; + + return paired; +#endif +} + + + + + + +/* + set cursor of pointer dev. + returns the cursor shape as an rfbCursorPtr +*/ +rfbCursorPtr setClientCursor(Display *dpy, int dev_id, float r, float g, float b, char *label) +{ +#ifndef HAVE_LIBXCURSOR + return NULL; +#else +#ifndef HAVE_CAIRO + return NULL; +#else + + /* label setup */ + const int idFontSize = 18; + const int idXOffset = 11; + const int idYOffset = 25; + const size_t textsz = 64; + char text[textsz]; + int total_width, total_height; + cairo_surface_t* main_surface; + cairo_surface_t* dummy_surface; + cairo_surface_t* barecursor_surface; + cairo_t* cr; + cairo_text_extents_t est; + Cursor cursor; + XcursorImage *cursor_image = NULL; + rfbCursorPtr rfbcursor = NULL; + + if(dev_id < 0) + return NULL; + + if(label) + snprintf(text, textsz, "%s", label); + else + snprintf(text, textsz, "%i", (int) dev_id); + + /* simple cursor w/o label */ + barecursor_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 24, 24); + cr = cairo_create(barecursor_surface); + cairo_move_to (cr, 1, 1); + cairo_line_to (cr, 12, 8); + cairo_line_to (cr, 5, 15); + cairo_close_path (cr); + cairo_set_source_rgba(cr, r, g, b, 0.9); + cairo_fill_preserve (cr); + cairo_set_source_rgba(cr, 0, 0, 0, 0.8); + cairo_set_line_width (cr, 0.8); + cairo_stroke (cr); + + + /* get estimated text extents */ + dummy_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 500, 10);/* ah well, but should fit */ + cr = cairo_create(dummy_surface); + cairo_select_font_face (cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); + cairo_set_font_size (cr, idFontSize); + cairo_text_extents(cr, text, &est); + + /* an from these calculate our final size */ + total_width = (int)(idXOffset + est.width + est.x_bearing); + total_height = (int)(idYOffset + est.height + est.y_bearing); + + /* draw evrything */ + main_surface = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, total_width, total_height ); + cr = cairo_create(main_surface); + cairo_set_source_surface(cr, barecursor_surface, 0, 0); + cairo_paint (cr); + cairo_select_font_face (cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); + cairo_set_font_size (cr, idFontSize); + cairo_set_source_rgba (cr, r, g, b, 0.8); + cairo_move_to(cr, idXOffset, idYOffset); + cairo_show_text(cr,text); + + X_LOCK; + /* copy cairo surface to cursor image */ + cursor_image = XcursorImageCreate(total_width, total_height); + /* this is important! otherwise we get badmatch, badcursor xerrrors galore... */ + cursor_image->xhot = cursor_image->yhot = 0; + memcpy(cursor_image->pixels, cairo_image_surface_get_data (main_surface), sizeof(CARD32) * total_width * total_height); + X_UNLOCK; + + /* convert to rfb cursor which we return later */ + rfbcursor = pixels2curs((unsigned long*)cursor_image->pixels, + cursor_image->width, + cursor_image->height, + cursor_image->xhot, + cursor_image->yhot, + bpp/8); + + X_LOCK; + + /* and display */ + cursor = XcursorImageLoadCursor(dpy, cursor_image); + XIDefineCursor(dpy, dev_id, RootWindow(dpy, DefaultScreen(dpy)), cursor); + XFreeCursor(dpy, cursor); + + /* clean up */ + cairo_destroy(cr); + cairo_surface_destroy(dummy_surface); + cairo_surface_destroy(main_surface); + cairo_surface_destroy(barecursor_surface); + XcursorImageDestroy(cursor_image); + + X_UNLOCK; + + return rfbcursor; +#endif +#endif +} + + + +/* + Sets the paired keyboard's focus to the window underneath the given pointer. + returns 1 on success, 0 on fail +*/ +int setDeviceFocus(Display* dpy, int ptr_id) +{ +#ifndef HAVE_XI2 + return 0; +#else + + XErrorHandler old_handler; + + Window root_return; + Window child_return; + double root_x_return; + double root_y_return; + double win_x_return; + double win_y_return; + XIButtonState buttons_return; + XIModifierState modifiers_return; + XIGroupState group_return; + + if(ptr_id < 0) + return 0; + + X_LOCK; + + trapped_xerror = 0; + old_handler = XSetErrorHandler(trap_xerror); + + /* get window the pointer is in */ + XIQueryPointer(dpy, ptr_id, rootwin, &root_return, &child_return, + &root_x_return, &root_y_return, &win_x_return, &win_y_return, + &buttons_return, &modifiers_return, &group_return); + + XISetFocus(dpy, getPairedMD(dpy, ptr_id), find_client(dpy, root_return, child_return), CurrentTime); + XSync(dpy, False); + + if(trapped_xerror) { + XSetErrorHandler(old_handler); + trapped_xerror = 0; + X_UNLOCK; + return 0; + } + + XSetErrorHandler(old_handler); + trapped_xerror = 0; + + X_UNLOCK; + + return 1; +#endif +} + + + +/* + sets the XI client pointer for client window underneath this pointer. + returns 1 on success, 0 on fail +*/ +int setXIClientPointer(Display* dpy, int dev_id) +{ +#ifndef HAVE_XI2 + return 0; +#else + + XErrorHandler old_handler; + + Window root_return; + Window child_return; + double root_x_return; + double root_y_return; + double win_x_return; + double win_y_return; + XIButtonState buttons_return; + XIModifierState modifiers_return; + XIGroupState group_return; + + if(dev_id < 0) + return 0; + + X_LOCK; + + trapped_xerror = 0; + old_handler = XSetErrorHandler(trap_xerror); + + /* get window the pointer is in */ + XIQueryPointer(dpy, dev_id, rootwin, &root_return, &child_return, + &root_x_return, &root_y_return, &win_x_return, &win_y_return, + &buttons_return, &modifiers_return, &group_return); + + XISetClientPointer(dpy, find_client(dpy, root_return, child_return), dev_id); + XSync(dpy, False); + + if(trapped_xerror) { + XSetErrorHandler(old_handler); + fprintf(stderr, "got x error\n"); + trapped_xerror = 0; + X_UNLOCK; + return 0; + } + + XSetErrorHandler(old_handler); + trapped_xerror = 0; + + X_UNLOCK; + + return 1; +#endif +} diff --git a/xi2_devices.h b/xi2_devices.h new file mode 100644 index 00000000..88c771ff --- /dev/null +++ b/xi2_devices.h @@ -0,0 +1,74 @@ +/* + XInput2 device handling routines for x11vnc. + + Copyright (C) 2009-2010 Christian Beier + All rights reserved. + + This file is part of x11vnc. + + x11vnc is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at + your option) any later version. + + x11vnc is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with x11vnc; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA + or see . +*/ + +#ifndef _X11VNC_XI2_DEVICES +#define _X11VNC_XI2_DEVICES + +#ifdef HAVE_XI2 +#include +#endif + +extern int xinput2_present; +extern int use_multipointer; +extern int xi2_device_creation_in_progress; + + +/* + create xi2 master device with given name + returns device_id, -1 on error +*/ +extern int createMD(Display* dpy, char* name); + +/* + remove master device + returns 1 on success, 0 on failure +*/ +extern int removeMD(Display* dpy, int dev_id); + +/* + gets the paired pointer/keyboard id to dev_id + returns -1 on error +*/ +extern int getPairedMD(Display* dpy, int dev_id); + + +/* + set cursor of pointer dev +*/ +extern rfbCursorPtr setClientCursor(Display *dpy, int dev_id, float r, float g, float b, char *label); + +/* + Sets the paired keyboard's focus to the window underneath the given pointer. + returns 1 on success, 0 on fail +*/ +extern int setDeviceFocus(Display* dpy, int ptr_id); + +/* + sets the XI client pointer for client window underneath this pointer. + returns 1 on success, 0 on fail +*/ +int setXIClientPointer(Display* dpy, int dev_id); + + +#endif /* _X11VNC_XI2_DEVICES */ From ceffcec9423fe01297730c5878f6b1f3b8e1c434 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sun, 28 Dec 2014 11:12:19 +0100 Subject: [PATCH 05/26] multipointer: Add multipointer-specific members to ClientData. --- x11vnc.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/x11vnc.h b/x11vnc.h index 2140698e..f10c464d 100644 --- a/x11vnc.h +++ b/x11vnc.h @@ -696,6 +696,18 @@ typedef struct _ClientData { int cmp_bytes_sent; int raw_bytes_sent; + int ptr_id; /* pointer and keyboard device ids used in multipointer mode */ + int kbd_id; + int ptr_buttonmask; + int cursor_x; /* these are used in multipointer mode to draw */ + int cursor_y; /* client cursors directly into the framebuffer */ + int cursor_x_saved; + int cursor_y_saved; + rfbCursorPtr cursor; + char* under_cursor_buffer; + int under_cursor_buffer_len; + sraRegionPtr cursor_region; + } ClientData; extern void nox11_exit(int rc); From b74e4536e2a741e9e454a668dbd1ac2c12f8a45e Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sun, 28 Dec 2014 11:40:02 +0100 Subject: [PATCH 06/26] multipointer: Add a XInputQueryVersion_wr() function to get XInput version. --- xwrappers.c | 21 +++++++++++++++++++++ xwrappers.h | 1 + 2 files changed, 22 insertions(+) diff --git a/xwrappers.c b/xwrappers.c index 3b8f1c02..2ed21bec 100644 --- a/xwrappers.c +++ b/xwrappers.c @@ -1319,6 +1319,27 @@ Bool XRecordQueryVersion_wr(Display *dpy, int *maj, int *min) { #endif } + +Bool XInputQueryVersion_wr(Display *dpy, int *maj, int *min) { + RAWFB_RET(False) +#if NO_X11 + rfbLog("This x11vnc was built without X11 support (-rawfb only).\n"); + if (!display_name || !d || !db) {} + return NULL; +#else + int ignore; + if(! XQueryExtension (dpy, "XInputExtension", &ignore, &ignore, &ignore)) + return False; +#ifdef HAVE_XI2 + if (XIQueryVersion(dpy, maj, min) != Success) + return False; +#endif + + return True; +#endif /* NO_X11 */ +} + + int xauth_raw(int on) { char tmp[] = "/tmp/x11vnc-xauth.XXXXXX"; int tmp_fd = -1; diff --git a/xwrappers.h b/xwrappers.h index f1e0fbf2..cf7c3980 100644 --- a/xwrappers.h +++ b/xwrappers.h @@ -101,6 +101,7 @@ extern int XTRAP_GrabControl_wr(Display *dpy, Bool impervious); extern void disable_grabserver(Display *in_dpy, int change); extern Bool XRecordQueryVersion_wr(Display *dpy, int *maj, int *min); +extern Bool XInputQueryVersion_wr(Display *dpy, int *maj, int *min); extern int xauth_raw(int on); extern Display *XOpenDisplay_wr(char *display_name); From 5c23f3a63b3e2934100cd4b953901071780c192c Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sun, 28 Dec 2014 11:41:22 +0100 Subject: [PATCH 07/26] multipointer: Init XInput2 at startup if available. --- options.c | 6 ++++++ x11vnc.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/options.c b/options.c index b9f31e51..745dc76c 100644 --- a/options.c +++ b/options.c @@ -192,6 +192,12 @@ int avahi = AVAHI; /* -avahi, -mdns */ int vnc_redirect = 0; int vnc_redirect_sock = -1; +#ifdef HAVE_XI2 +int use_multipointer = 1; /* enable MPX support */ +#else +int use_multipointer = 0; /* disable MPX support */ +#endif + int use_modifier_tweak = 1; /* use the shift/altgr modifier tweak */ int watch_capslock = 0; /* -capslock */ int skip_lockkeys = 0; /* -skip_lockkeys */ diff --git a/x11vnc.c b/x11vnc.c index ce5a1e62..6eefeeda 100644 --- a/x11vnc.c +++ b/x11vnc.c @@ -161,6 +161,7 @@ #include "selection.h" #include "pm.h" #include "solid.h" +#include "xi2_devices.h" /* * main routine for the x11vnc program @@ -1610,6 +1611,9 @@ static void print_settings(int try_http, int bg, char *gui_str) { fprintf(stderr, " xd_area: %d\n", xdamage_max_area); fprintf(stderr, " xd_mem: %.3f\n", xdamage_memory); fprintf(stderr, " xcomposite: %d\n", use_xcomposite); +#ifdef HAVE_XI2 + fprintf(stderr, " multiptr: %d\n", use_multipointer); +#endif fprintf(stderr, " sigpipe: %s\n", sigpipe ? sigpipe : "null"); fprintf(stderr, " threads: %d\n", use_threads); @@ -3795,6 +3799,13 @@ int main(int argc, char* argv[]) { use_xcomposite = 0; continue; } +#ifdef HAVE_XI2 + if (!strcmp(arg, "-multiptr")) { + use_multipointer++; + continue; + } +#endif + if (!strcmp(arg, "-sigpipe") || !strcmp(arg, "-sig")) { CHECK_ARGC if (known_sigpipe_mode(argv[++i])) { @@ -5292,6 +5303,16 @@ int main(int argc, char* argv[]) { use_xfixes = 0; } + if(use_multipointer) + { + /* XFixesGetCursorImage() gets confused with multiple pointers and crashes */ + use_xfixes = 0; + rfbLog("Disabled XFIXES while using multiple pointer support.\n"); + /* disable these as most clients expect only a single cursor */ + cursor_shape_updates = 0; + rfbLog("Drawing cursors into framebuffer while using multiple pointer support.\n"); + } + #if HAVE_LIBXDAMAGE if (! XDamageQueryExtension(dpy, &xdamage_base_event_type, &er)) { if (! quiet && ! raw_fb_str) { @@ -5494,6 +5515,30 @@ int main(int argc, char* argv[]) { initialize_xrecord(); +#ifdef HAVE_XI2 + /* check for XInput 2 */ + maj = 2; + min = 0; + if (! XInputQueryVersion_wr(dpy, &maj, &min)) { + xinput2_present = 0; + if (! quiet) { + rfbLog("\n"); + rfbLog("XInput2 support was not found on this display.\n"); + rfbLog("Multi pointer support will not be available.\n"); + rfbLog("\n"); + } + } else { + xinput2_present = 1; + /* set the virtual core pointer (id 2) as client pointer so + that ambigious calls like XQueryPointer() get this one */ + XISetClientPointer(dpy, None, 2); + } + + if(!xinput2_present) + use_multipointer = 0; +#endif + + tmpi = 1; if (scroll_copyrect) { if (strstr(scroll_copyrect, "never")) { From 4d65ae059a5d559f68b106c10f725c86e678846d Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sun, 28 Dec 2014 12:03:59 +0100 Subject: [PATCH 08/26] multipointer: Add XI[Warp|Query]Pointer_wr() functions. --- xwrappers.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++ xwrappers.h | 28 ++++++++++++++++++ 2 files changed, 110 insertions(+) diff --git a/xwrappers.c b/xwrappers.c index 2ed21bec..09c80a71 100644 --- a/xwrappers.c +++ b/xwrappers.c @@ -39,6 +39,7 @@ so, delete this exception statement from your version. #include "connections.h" #include "cleanup.h" #include "macosx.h" +#include "xwrappers.h" int xshm_present = 0; int xshm_opcode = 0; @@ -1500,7 +1501,88 @@ Bool XQueryPointer_wr(Display *display, Window w, Window *root_return, return rc; #endif /* NO_X11 */ } + +Bool XIQueryPointer_wr( Display *display, + int deviceid, + Window win, + Window *root_return, + Window *child_return, + double *root_x_return, + double *root_y_return, + double *win_x_return, + double *win_y_return, + XIButtonState *buttons_return, + XIModifierState *modifiers_return, + XIGroupState *group_return) +{ +#if NO_X11 + return False; +#else + Bool rc; + XErrorHandler old_handler; + + if (! display) { + return False; + } + + /* there can be a race condition where this is called when the XI2 device has not yet been created */ + old_handler = XSetErrorHandler(trap_xerror); + trapped_xerror = 0; + + rc = XIQueryPointer(display, deviceid, win, root_return, child_return, + root_x_return, root_y_return, win_x_return, win_y_return, + buttons_return, modifiers_return, group_return); + + XSetErrorHandler(old_handler); + if (trapped_xerror) { + trapped_xerror = 0; + rc = 0; + } + + return rc; +#endif /* NO_X11 */ +} + + +Bool XIWarpPointer_wr( Display *display, + int deviceid, + Window src_w, + Window dest_w, + double src_x, + double src_y, + int src_width, + int src_height, + double dest_x, + double dest_y) +{ +#if NO_X11 + return False; +#else + Bool rc; + XErrorHandler old_handler; + + if (! display) { + return False; + } + + /* there can be a race condition where this is called when the XI2 device has not yet been created */ + old_handler = XSetErrorHandler(trap_xerror); + trapped_xerror = 0; + + rc = XIWarpPointer(display, deviceid, src_w, dest_w, src_x, src_y, src_width, src_height, dest_x, dest_y); + + XSetErrorHandler(old_handler); + if (trapped_xerror) { + trapped_xerror = 0; + rc = 0; + } + + return rc; +#endif /* NO_X11 */ +} + + Status XQueryTree_wr(Display *display, Window w, Window *root_return, Window *parent_return, Window **children_return, diff --git a/xwrappers.h b/xwrappers.h index cf7c3980..fdaa30bd 100644 --- a/xwrappers.h +++ b/xwrappers.h @@ -33,6 +33,10 @@ so, delete this exception statement from your version. #ifndef _X11VNC_XWRAPPERS_H #define _X11VNC_XWRAPPERS_H +#ifdef HAVE_XI2 +#include +#endif + /* -- xwrappers.h -- */ extern int xshm_present; @@ -111,6 +115,30 @@ extern Bool XQueryPointer_wr(Display *display, Window w, Window *root_return, Window *child_return, int *root_x_return, int *root_y_return, int *win_x_return, int *win_y_return, unsigned int *mask_return); +extern Bool XIQueryPointer_wr( Display *display, + int deviceid, + Window win, + Window *root_return, + Window *child_return, + double *root_x_return, + double *root_y_return, + double *win_x_return, + double *win_y_return, + XIButtonState *buttons_return, + XIModifierState *modifiers_return, + XIGroupState *group_return); + +extern Bool XIWarpPointer_wr(Display *display, + int deviceid, + Window src_w, + Window dest_w, + double src_x, + double src_y, + int src_width, + int src_height, + double dest_x, + double dest_y); + extern Status XQueryTree_wr(Display *display, Window w, Window *root_return, Window *parent_return, Window **children_return, unsigned int *nchildren_return); From e9524800b97cea4a533c62f71f7aad9f741eb219 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sun, 28 Dec 2014 14:05:14 +0100 Subject: [PATCH 09/26] multipointer: Extend XTestFake[Key|Button|Motion]Event_wr() to be device specific. --- keyboard.c | 38 +++++++++++++------------- pointer.c | 8 +++--- remote.c | 8 +++--- x11vnc.c | 12 ++++---- xevents.c | 14 +++++----- xwrappers.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++------ xwrappers.h | 6 ++-- 7 files changed, 113 insertions(+), 52 deletions(-) diff --git a/keyboard.c b/keyboard.c index d74d5ba3..a4750b94 100644 --- a/keyboard.c +++ b/keyboard.c @@ -188,7 +188,7 @@ void clear_modifiers(int init) { rfbLog("clear_modifiers: up: %-10s (0x%x) " "keycode=0x%x\n", keystrs[i], keysym, keycode); } - XTestFakeKeyEvent_wr(dpy, keycode, False, CurrentTime); + XTestFakeKeyEvent_wr(dpy, -1, keycode, False, CurrentTime); } XFlush_wr(dpy); #endif /* NO_X11 */ @@ -277,7 +277,7 @@ void clear_keys(void) { if (keystate[k]) { KeyCode keycode = (KeyCode) k; rfbLog("clear_keys: keycode=%d\n", keycode); - XTestFakeKeyEvent_wr(dpy, keycode, False, CurrentTime); + XTestFakeKeyEvent_wr(dpy, -1, keycode, False, CurrentTime); } } XFlush_wr(dpy); @@ -331,9 +331,9 @@ void clear_locks(void) { char *nm = XKeysymToString(ks); rfbLog("toggling: %03d / %03d -- %s\n", key, ks, nm ? nm : "BadKey"); did = 1; - XTestFakeKeyEvent_wr(dpy, key, True, CurrentTime); + XTestFakeKeyEvent_wr(dpy, -1, key, True, CurrentTime); usleep(10*1000); - XTestFakeKeyEvent_wr(dpy, key, False, CurrentTime); + XTestFakeKeyEvent_wr(dpy, -1, key, False, CurrentTime); XFlush_wr(dpy); } } @@ -2277,7 +2277,7 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, "inadvertent Multi_key from Shift " "(doing %03d up now)\n", shift_is_down); } - XTestFakeKeyEvent_wr(dpy, shift_is_down, False, + XTestFakeKeyEvent_wr(dpy, -1, shift_is_down, False, CurrentTime); } else { involves_multi_key = 0; @@ -2289,7 +2289,7 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, if (sentmods[i] == 0) continue; dn = (Bool) needmods[i]; if (dn) continue; - XTestFakeKeyEvent_wr(dpy, sentmods[i], dn, CurrentTime); + XTestFakeKeyEvent_wr(dpy, -1, sentmods[i], dn, CurrentTime); } for (j=0; j<8; j++) { /* next, do the Mod downs */ @@ -2297,7 +2297,7 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, if (sentmods[i] == 0) continue; dn = (Bool) needmods[i]; if (!dn) continue; - XTestFakeKeyEvent_wr(dpy, sentmods[i], dn, CurrentTime); + XTestFakeKeyEvent_wr(dpy, -1, sentmods[i], dn, CurrentTime); } if (involves_multi_key) { @@ -2309,14 +2309,14 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, "inadvertent Multi_key from Shift " "(doing %03d down now)\n", shift_is_down); } - XTestFakeKeyEvent_wr(dpy, shift_is_down, True, + XTestFakeKeyEvent_wr(dpy, -1, shift_is_down, True, CurrentTime); } /* * With the above modifier work done, send the actual keycode: */ - XTestFakeKeyEvent_wr(dpy, Kc_f, (Bool) down, CurrentTime); + XTestFakeKeyEvent_wr(dpy, -1, Kc_f, (Bool) down, CurrentTime); /* * Now undo the modifier work: @@ -2327,7 +2327,7 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, if (sentmods[i] == 0) continue; dn = (Bool) needmods[i]; if (!dn) continue; - XTestFakeKeyEvent_wr(dpy, sentmods[i], !dn, + XTestFakeKeyEvent_wr(dpy, -1, sentmods[i], !dn, CurrentTime); } for (j=7; j>=0; j--) { @@ -2336,13 +2336,13 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, if (sentmods[i] == 0) continue; dn = (Bool) needmods[i]; if (dn) continue; - XTestFakeKeyEvent_wr(dpy, sentmods[i], !dn, + XTestFakeKeyEvent_wr(dpy, -1, sentmods[i], !dn, CurrentTime); } } else { /* for up case, hopefully just need to pop it up: */ - XTestFakeKeyEvent_wr(dpy, Kc_f, (Bool) down, CurrentTime); + XTestFakeKeyEvent_wr(dpy, -1, Kc_f, (Bool) down, CurrentTime); } X_UNLOCK; } @@ -2628,20 +2628,20 @@ static void tweak_mod(signed char mod, rfbBool down) { X_LOCK; if (is_shift && mod != 1) { if (mod_state & LEFTSHIFT) { - XTestFakeKeyEvent_wr(dpy, left_shift_code, !dn, CurrentTime); + XTestFakeKeyEvent_wr(dpy, -1, left_shift_code, !dn, CurrentTime); } if (mod_state & RIGHTSHIFT) { - XTestFakeKeyEvent_wr(dpy, right_shift_code, !dn, CurrentTime); + XTestFakeKeyEvent_wr(dpy, -1, right_shift_code, !dn, CurrentTime); } } if ( ! is_shift && mod == 1 ) { - XTestFakeKeyEvent_wr(dpy, left_shift_code, dn, CurrentTime); + XTestFakeKeyEvent_wr(dpy, -1, left_shift_code, dn, CurrentTime); } if ( altgr && (mod_state & ALTGR) && mod != 2 ) { - XTestFakeKeyEvent_wr(dpy, altgr, !dn, CurrentTime); + XTestFakeKeyEvent_wr(dpy, -1, altgr, !dn, CurrentTime); } if ( altgr && ! (mod_state & ALTGR) && mod == 2 ) { - XTestFakeKeyEvent_wr(dpy, altgr, dn, CurrentTime); + XTestFakeKeyEvent_wr(dpy, -1, altgr, dn, CurrentTime); } X_UNLOCK; @@ -2734,7 +2734,7 @@ static void modifier_tweak_keyboard(rfbBool down, rfbKeySym keysym, } if ( k != NoSymbol ) { X_LOCK; - XTestFakeKeyEvent_wr(dpy, k, (Bool) down, CurrentTime); + XTestFakeKeyEvent_wr(dpy, -1, k, (Bool) down, CurrentTime); X_UNLOCK; } @@ -3407,7 +3407,7 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { } if ( k != NoSymbol ) { - XTestFakeKeyEvent_wr(dpy, k, (Bool) down, CurrentTime); + XTestFakeKeyEvent_wr(dpy, -1, k, (Bool) down, CurrentTime); XFlush_wr(dpy); } diff --git a/pointer.c b/pointer.c index f2995c3c..95c0444d 100644 --- a/pointer.c +++ b/pointer.c @@ -356,7 +356,7 @@ void update_x11_pointer_position(int x, int y) { XWarpPointer(dpy, None, window, 0, 0, 0, 0, x + coff_x, y + coff_y); } else { - XTestFakeMotionEvent_wr(dpy, scr, x + off_x + coff_x, + XTestFakeMotionEvent_wr(dpy, -1, scr, x + off_x + coff_x, y + off_y + coff_y, CurrentTime); } X_UNLOCK; @@ -412,7 +412,7 @@ void do_button_mask_change(int mask, int button) { " %s (event %d)\n", mb, bmask ? "down" : "up", k+1); } - XTestFakeButtonEvent_wr(dpy, mb, (mask & (1< 0) { /* try button motion*/ int scr = DefaultScreen(dpy); fprintf(stderr, "**bust_grab: x=%d y=%d %.4f\n", x, y, dnowx()); - XTestFakeMotionEvent_wr(dpy, scr, x, y, CurrentTime); + XTestFakeMotionEvent_wr(dpy, -1, scr, x, y, CurrentTime); XFlush_wr(dpy); usleep(50 * 1000); /* followed by button press */ button = 1; fprintf(stderr, "**bust_grab: button%d\n", button); - XTestFakeButtonEvent_wr(dpy, button, True, CurrentTime); + XTestFakeButtonEvent_wr(dpy, -1, button, True, CurrentTime); XFlush_wr(dpy); usleep(50 * 1000); - XTestFakeButtonEvent_wr(dpy, button, False, CurrentTime); + XTestFakeButtonEvent_wr(dpy, -1, button, False, CurrentTime); } else { /* try Escape or Space press+release */ fprintf(stderr, "**bust_grab: keycode: %d %.4f\n", (int) key, dnowx()); - XTestFakeKeyEvent_wr(dpy, key, True, CurrentTime); + XTestFakeKeyEvent_wr(dpy, -1, key, True, CurrentTime); XFlush_wr(dpy); usleep(50 * 1000); - XTestFakeKeyEvent_wr(dpy, key, False, CurrentTime); + XTestFakeKeyEvent_wr(dpy, -1, key, False, CurrentTime); } XFlush_wr(dpy); last_bust = time(NULL); diff --git a/xwrappers.c b/xwrappers.c index 09c80a71..077d6ae2 100644 --- a/xwrappers.c +++ b/xwrappers.c @@ -39,6 +39,7 @@ so, delete this exception statement from your version. #include "connections.h" #include "cleanup.h" #include "macosx.h" +#include "xi2_devices.h" #include "xwrappers.h" int xshm_present = 0; @@ -84,15 +85,15 @@ 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, +void XTestFakeKeyEvent_wr(Display* dpy, int dev_id, 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, +void XTestFakeButtonEvent_wr(Display* dpy, int dev_id, 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, +void XTestFakeMotionEvent_wr(Display* dpy, int dev_id, int screen, int x, int y, unsigned long delay); Bool XTestCompareCurrentCursorWithWindow_wr(Display* dpy, Window w); @@ -929,7 +930,7 @@ void XTRAP_FakeKeyEvent_wr(Display* dpy, KeyCode key, Bool down, #endif /* NO_X11 */ } -void XTestFakeKeyEvent_wr(Display* dpy, KeyCode key, Bool down, +void XTestFakeKeyEvent_wr(Display* dpy, int dev_id, KeyCode key, Bool down, unsigned long delay) { static int first = 1; int regrab = 0; @@ -982,7 +983,27 @@ void XTestFakeKeyEvent_wr(Display* dpy, KeyCode key, Bool down, key, down, dnowx()); } #if HAVE_XTEST - XTestFakeKeyEvent(dpy, key, down, delay); +#ifdef HAVE_XI2 + if(use_multipointer && dev_id >= 0) + { + XErrorHandler old_handler; + XDevice xdev; + xdev.device_id = dev_id; + + /* there can be a race condition where this is called when the XI2 device has not yet been created */ + old_handler = XSetErrorHandler(trap_xerror); + trapped_xerror = 0; + + XTestFakeDeviceKeyEvent(dpy, &xdev, key, down, NULL, 0, delay); + + XSetErrorHandler(old_handler); + if (trapped_xerror) + trapped_xerror = 0; + } + else +#endif + XTestFakeKeyEvent(dpy, key, down, delay); + if (regrab) { adjust_grabs(1, 1); } @@ -1022,7 +1043,7 @@ void XTRAP_FakeButtonEvent_wr(Display* dpy, unsigned int button, Bool is_press, #endif /* NO_X11 */ } -void XTestFakeButtonEvent_wr(Display* dpy, unsigned int button, Bool is_press, +void XTestFakeButtonEvent_wr(Display* dpy, int dev_id, unsigned int button, Bool is_press, unsigned long delay) { int regrab = 0; @@ -1059,7 +1080,26 @@ void XTestFakeButtonEvent_wr(Display* dpy, unsigned int button, Bool is_press, button, is_press, dnowx()); } #if HAVE_XTEST - XTestFakeButtonEvent(dpy, button, is_press, delay); +#ifdef HAVE_XI2 + if(use_multipointer && dev_id >= 0) + { + XErrorHandler old_handler; + XDevice xdev; + xdev.device_id = dev_id; + + /* there can be a race condition where this is called when the XI2 device has not yet been created */ + old_handler = XSetErrorHandler(trap_xerror); + trapped_xerror = 0; + + XTestFakeDeviceButtonEvent(dpy, &xdev, button, is_press, NULL, 0, delay); + + XSetErrorHandler(old_handler); + if (trapped_xerror) + trapped_xerror = 0; + } + else +#endif + XTestFakeButtonEvent(dpy, button, is_press, delay); #endif if (regrab) { adjust_grabs(1, 1); @@ -1067,6 +1107,7 @@ void XTestFakeButtonEvent_wr(Display* dpy, unsigned int button, Bool is_press, #endif /* NO_X11 */ } + void XTRAP_FakeMotionEvent_wr(Display* dpy, int screen, int x, int y, unsigned long delay) { @@ -1093,7 +1134,7 @@ void XTRAP_FakeMotionEvent_wr(Display* dpy, int screen, int x, int y, #endif /* NO_X11 */ } -void XTestFakeMotionEvent_wr(Display* dpy, int screen, int x, int y, +void XTestFakeMotionEvent_wr(Display* dpy, int dev_id, int screen, int x, int y, unsigned long delay) { int regrab = 0; @@ -1126,7 +1167,27 @@ void XTestFakeMotionEvent_wr(Display* dpy, int screen, int x, int y, x, y, dnowx()); } #if HAVE_XTEST - XTestFakeMotionEvent(dpy, screen, x, y, delay); +#ifdef HAVE_XI2 + if(use_multipointer && dev_id >= 0) + { + XErrorHandler old_handler; + int axes[] = {x, y}; + XDevice xdev; + xdev.device_id = dev_id; + + /* there can be a race condition where this is called when the XI2 device has not yet been created */ + old_handler = XSetErrorHandler(trap_xerror); + trapped_xerror = 0; + + XTestFakeDeviceMotionEvent(dpy, &xdev, 0, 0, axes, 2, delay); + + XSetErrorHandler(old_handler); + if (trapped_xerror) + trapped_xerror = 0; + } + else +#endif + XTestFakeMotionEvent(dpy, screen, x, y, delay); #endif if (regrab) { adjust_grabs(1, 1); diff --git a/xwrappers.h b/xwrappers.h index fdaa30bd..ae24ab1c 100644 --- a/xwrappers.h +++ b/xwrappers.h @@ -83,15 +83,15 @@ extern void init_track_keycode_state(void); extern void XTRAP_FakeKeyEvent_wr(Display* dpy, KeyCode key, Bool down, unsigned long delay); -extern void XTestFakeKeyEvent_wr(Display* dpy, KeyCode key, Bool down, +extern void XTestFakeKeyEvent_wr(Display* dpy, int dev_id, KeyCode key, Bool down, unsigned long delay); extern void XTRAP_FakeButtonEvent_wr(Display* dpy, unsigned int button, Bool is_press, unsigned long delay); -extern void XTestFakeButtonEvent_wr(Display* dpy, unsigned int button, Bool is_press, +extern void XTestFakeButtonEvent_wr(Display* dpy, int dev_id, unsigned int button, Bool is_press, unsigned long delay); extern void XTRAP_FakeMotionEvent_wr(Display* dpy, int screen, int x, int y, unsigned long delay); -extern void XTestFakeMotionEvent_wr(Display* dpy, int screen, int x, int y, +extern void XTestFakeMotionEvent_wr(Display* dpy, int dev_id, int screen, int x, int y, unsigned long delay); extern Bool XTestCompareCurrentCursorWithWindow_wr(Display* dpy, Window w); From b2769b09c5746312a9d68de08553e3f21396582a Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sun, 28 Dec 2014 14:14:24 +0100 Subject: [PATCH 10/26] multipointer: Disable per default, add option help string. --- help.c | 3 +++ options.c | 6 +----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/help.c b/help.c index 5cd5c12d..59991449 100644 --- a/help.c +++ b/help.c @@ -4040,6 +4040,9 @@ void print_help(int mode) { "-input_eagerly Similar to -allinput but use the handleEventsEagerly\n" " mechanism built into LibVNCServer.\n" "\n" +"-multiptr Enable support for per-client input devices. Each\n" +" client will get its own cursor and keyboard focus.\n" +"\n" "-speeds rd,bw,lat x11vnc tries to estimate some speed parameters that\n" " are used to optimize scheduling (e.g. -pointer_mode\n" " 4, -wireframe, -scrollcopyrect) and other things.\n" diff --git a/options.c b/options.c index 745dc76c..7dea8441 100644 --- a/options.c +++ b/options.c @@ -192,11 +192,7 @@ int avahi = AVAHI; /* -avahi, -mdns */ int vnc_redirect = 0; int vnc_redirect_sock = -1; -#ifdef HAVE_XI2 -int use_multipointer = 1; /* enable MPX support */ -#else -int use_multipointer = 0; /* disable MPX support */ -#endif +int use_multipointer = 0; /* MPX support */ int use_modifier_tweak = 1; /* use the shift/altgr modifier tweak */ int watch_capslock = 0; /* -capslock */ From a9c95879b8822dac524c5ca6d74575f41bcf6914 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sun, 28 Dec 2014 14:56:52 +0100 Subject: [PATCH 11/26] multipointer: Make cursor updates device specific. Added framebuffer drawing of per-client cursors, made pixels2curs() work on 64-bit platforms as well. --- cursor.c | 397 ++++++++++++++++++++++++++++++++++++++++++++++++-- cursor.h | 4 +- pointer.c | 2 +- screen.c | 4 +- xi2_devices.c | 2 +- 5 files changed, 390 insertions(+), 19 deletions(-) diff --git a/cursor.c b/cursor.c index 31157779..8802dbf3 100644 --- a/cursor.c +++ b/cursor.c @@ -39,6 +39,7 @@ so, delete this exception statement from your version. #include "scan.h" #include "unixpw.h" #include "macosx.h" +#include "xi2_devices.h" int xfixes_present = 0; int xfixes_first_initialized = 0; @@ -62,15 +63,17 @@ 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 cursor_position(int x, int y, rfbClientPtr client); 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); -rfbCursorPtr pixels2curs(unsigned long *pixels, int w, int h, int xhot, int yhot, int Bpp); - +rfbCursorPtr pixels2curs(uint32_t *pixels, int w, int h, int xhot, int yhot, int Bpp); +static void save_under_cursor_buffer(rfbClientPtr cl); +static void draw_cursor(rfbClientPtr cl); +static void restore_under_cursor_buffer(rfbClientPtr cl); typedef struct win_str_info { char *wm_name; @@ -746,20 +749,20 @@ static void setup_cursors(void) { if (scaling_cursor && (scale_cursor_fac_x != 1.0 || scale_cursor_fac_y != 1.0)) { int w, h, x, y, k; - unsigned long *pixels; + uint32_t *pixels; w = ci->wx; h = ci->wy; - pixels = (unsigned long *) malloc(w * h - * sizeof(unsigned long)); + pixels = (uint32_t *) malloc(w * h + * sizeof(uint32_t)); k = 0; for (y=0; ydata[k]; char m = ci->mask[k]; - unsigned long *p; + uint32_t *p; p = pixels + k; @@ -1002,7 +1005,7 @@ void initialize_xfixes(void) { #endif } -rfbCursorPtr pixels2curs(unsigned long *pixels, int w, int h, +rfbCursorPtr pixels2curs(uint32_t *pixels, int w, int h, int xhot, int yhot, int Bpp) { rfbCursorPtr c; static unsigned long black = 0, white = 1; @@ -1083,7 +1086,7 @@ rfbCursorPtr pixels2curs(unsigned long *pixels, int w, int h, } } - pixels = (unsigned long *) pixels_new; + pixels = (uint32_t *) pixels_new; xhot = scale_round(xhot, scale_cursor_fac_x); yhot = scale_round(yhot, scale_cursor_fac_y); @@ -1450,7 +1453,7 @@ fprintf(stderr, "sc: %d %d/%d %d - %d %d\n", serial, w, h, cbpp, xhot, yhot); } /* place cursor into our collection */ - cursors[use]->rfb = pixels2curs(data, w, h, xhot, yhot, bpp/8); + cursors[use]->rfb = pixels2curs((uint32_t*)data, w, h, xhot, yhot, bpp/8); /* update time and serial index: */ curs_times[use] = now; @@ -1827,7 +1830,7 @@ int cursor_pos_updates_clients(rfbScreenInfoPtr s) { * 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) { +void cursor_position(int x, int y, rfbClientPtr client) { rfbClientIteratorPtr iter; rfbClientPtr cl; int cnt = 0, nonCursorPosUpdates_clients = 0; @@ -1852,6 +1855,11 @@ void cursor_position(int x, int y) { if (y >= dpy_y) y = dpy_y-1; } + + if(client == NULL) { + /* handle screen's master cursor */ + if (debug_pointer) + rfbLog("cursor_position: set screen pos x=%3d y=%d\n", x, y); if (x == screen->cursorX && y == screen->cursorY) { return; } @@ -1898,6 +1906,55 @@ void cursor_position(int x, int y) { rfbLog("cursor_position: sent position x=%3d y=%3d to %d" " clients\n", x, y, cnt); } + } + else { + /* if client is non-NULL, handle client cursor */ + ClientData *cd = (ClientData *) client->clientData; + if(cd && use_multipointer) { + /* make sure we do this while no rfbSendFramebufferUpdate() to this client is running! + DO NOT REMOVE THE cl->sendMutex LOCKS IN watch_loop() !!! + */ + { + /* disable cursor shape updates so the screen's single + master pointer gets drawn into the frame buffer */ + if (client->enableCursorShapeUpdates) { + cd->had_cursor_shape_updates = 1; + client->enableCursorShapeUpdates = FALSE; + if (debug_pointer) + rfbLog("%s disable HCSU\n", client->host); + + } + + /* disable these cause they send the screen's master pointer pos, not the client pointer's */ + if (client->enableCursorPosUpdates) { + cd->had_cursor_pos_updates = 1; + client->enableCursorPosUpdates = FALSE; + if (debug_pointer) + rfbLog("%s disable HCPU\n", client->host); + } + + client->cursorWasChanged = FALSE; + } + + + /* restore saved under-cursor-buffer */ + if(cd->cursor_x_saved >= 0 && cd->cursor_y_saved >= 0) + restore_under_cursor_buffer(client); + + /* save maybe new fb region */ + cd->cursor_x = x; + cd->cursor_y = y; + save_under_cursor_buffer(client); + cd->cursor_x_saved = x; + cd->cursor_y_saved = y; + + /* and draw */ + draw_cursor(client); + + if (debug_pointer) + rfbLog("cursor_position: set client pos x=%3d y=%d\n", x, y); + } + } } static void set_rfb_cursor(int which) { @@ -1972,12 +2029,66 @@ int check_x11_pointer(void) { #endif +#ifdef HAVE_XI2 +#if ! NO_X11 + /* if we are in multipointer mode, + check the position of all client pointers here */ + if(use_multipointer && screen) { + rfbClientIteratorPtr iter; + rfbClientPtr cl; + double root_x, root_y, win_x, win_y; + XIButtonState buttons_return; + XIModifierState modifiers_return; + XIGroupState group_return; + + iter = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(iter)) ) { + ClientData* cd = cl->clientData; + if (dpy && cd) { + X_LOCK; + ret = XIQueryPointer_wr(dpy, cd->ptr_id, rootwin, &root_w, &child_w, + &root_x, &root_y, &win_x, &win_y, + &buttons_return, &modifiers_return, &group_return); + X_UNLOCK; + } + + if(!ret) + continue; + + if (debug_pointer) + rfbLog("XIQueryPointer: x:%4f, y:%4f)\n", root_x, root_y); + + /* offset subtracted since XIQueryPointer relative to rootwin */ + x = root_x - off_x - coff_x; + y = root_y - off_y - coff_y; + + if (clipshift) { + static int cnt = 0; + if (x < 0 || y < 0 || x >= dpy_x || y >= dpy_y) { + if (cnt++ % 4 != 0) { + if (debug_pointer) + rfbLog("Skipping cursor_position() outside our clipshift\n"); + continue; + } + } + } + + /* record the cursor position in the rfb screen */ + INPUT_LOCK; + cursor_position(x, y, cl); + INPUT_UNLOCK; + } + rfbReleaseClientIterator(iter); + } +#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); + &win_x, &win_y, &mask); X_UNLOCK; } #else @@ -2015,10 +2126,270 @@ if (0) fprintf(stderr, "check_x11_pointer %d %d\n", root_x, root_y); } /* record the cursor position in the rfb screen */ - cursor_position(x, y); + cursor_position(x, y, NULL); /* change the cursor shape if necessary */ rint = set_cursor(x, y, get_which_cursor()); return rint; } + + +/* + the following routines save what's under a cursor, draw a cursor into + the framebuffer and restore the saved framebuffer region. most of + the code stolen from libvncserver. + this is mostly used in multi-pointer mode: because RFB only has the + notion of a single cursor, we draw the extra client cursor directly + into the framebuffer to provide some visual feedback to the user. +*/ +static void save_under_cursor_buffer(rfbClientPtr cl) +{ + ClientData *cd = (ClientData *) cl->clientData; + rfbCursorPtr c; + int j,x1,x2,y1,y2,bpp=screen->serverFormat.bitsPerPixel/8, + rowstride=screen->paddedWidthInBytes, + bufsize; + rfbBool wasChanged=FALSE; + + if(!cd) + return; + + c = cd->cursor; + + if(!c) + return; + + bufsize = c->width * c->height * bpp; + + /* make sure the buffer is big enough */ + if(cd->under_cursor_buffer_len < bufsize) { + LOCK(cl->updateMutex); + cd->under_cursor_buffer = realloc(cd->under_cursor_buffer, bufsize); + cd->under_cursor_buffer_len = bufsize; + UNLOCK(cl->updateMutex); + } + + /* sanity checks */ + x1 = cd->cursor_x - c->xhot; + x2 = x1 + c->width; + if(x1<0) { x1=0; } + if(x2 >= screen->width) x2= screen->width-1; + x2 -= x1; /* width */ + if(x2<=0) + return; /* nothing to do */ + + y1 = cd->cursor_y - c->yhot; + y2 = y1 + c->height; + if(y1<0) { y1=0; } + if(y2>=screen->height) y2=screen->height-1; + y2 -= y1; /* height */ + if(y2<=0) + return; /* nothing to do */ + + LOCK(cl->updateMutex); + /* save what's under the cursor now */ + for(j=0;junder_cursor_buffer+j*x2*bpp; + const char* src = screen->frameBuffer+(y1+j)*rowstride+x1*bpp; + unsigned int count=x2*bpp; + if(wasChanged || memcmp(dest,src,count)) { + wasChanged=TRUE; + memcpy(dest,src,count); + } + } + UNLOCK(cl->updateMutex); +} + +static void draw_cursor(rfbClientPtr cl) +{ + ClientData *cd = (ClientData *) cl->clientData; + rfbCursorPtr c; + int i,j,x1,x2,y1,y2,i1,j1,bpp=screen->serverFormat.bitsPerPixel/8, + rowstride=screen->paddedWidthInBytes, w; + + if(!cd) + return; + + c = cd->cursor; + + if(!c) + return; + + w = (c->width+7)/8; + + /* sanity checks */ + i1=j1=0; + + x1 = cd->cursor_x - c->xhot; + x2 = x1 + c->width; + if(x1<0) { i1=-x1; x1=0; } + if(x2 >= screen->width) x2= screen->width-1; + x2 -= x1; /* width */ + if(x2<=0) + return; /* nothing to do */ + + y1 = cd->cursor_y - c->yhot; + y2 = y1 + c->height; + if(y1<0) { j1=-y1; y1=0; } + if(y2>=screen->height) y2=screen->height-1; + y2 -= y1; /* height */ + if(y2<=0) + return; /* nothing to do */ + + LOCK(cl->screen->cursorMutex); + + if (c->alphaSource) { + int rmax, rshift; + int gmax, gshift; + int bmax, bshift; + int amax = 255; /* alphaSource is always 8bits of info per pixel */ + unsigned int rmask, gmask, bmask; + + rmax = screen->serverFormat.redMax; + gmax = screen->serverFormat.greenMax; + bmax = screen->serverFormat.blueMax; + rshift = screen->serverFormat.redShift; + gshift = screen->serverFormat.greenShift; + bshift = screen->serverFormat.blueShift; + + rmask = (rmax << rshift); + gmask = (gmax << gshift); + bmask = (bmax << bshift); + + for(j=0;jmask[], + * using the extracted alpha value instead. + */ + char *dest; + unsigned char *src, *aptr; + unsigned int val, dval, sval; + int rdst, gdst, bdst; /* fb RGB */ + int asrc, rsrc, gsrc, bsrc; /* rich source ARGB */ + + dest = screen->frameBuffer + (j+y1)*rowstride + (i+x1)*bpp; + src = c->richSource + (j+j1)*c->width*bpp + (i+i1)*bpp; + aptr = c->alphaSource + (j+j1)*c->width + (i+i1); + + asrc = *aptr; + if (!asrc) { + continue; + } + + if (bpp == 1) { + dval = *((unsigned char*) dest); + sval = *((unsigned char*) src); + } else if (bpp == 2) { + dval = *((unsigned short*) dest); + sval = *((unsigned short*) src); + } else if (bpp == 3) { + unsigned char *dst = (unsigned char *) dest; + dval = 0; + dval |= ((*(dst+0)) << 0); + dval |= ((*(dst+1)) << 8); + dval |= ((*(dst+2)) << 16); + sval = 0; + sval |= ((*(src+0)) << 0); + sval |= ((*(src+1)) << 8); + sval |= ((*(src+2)) << 16); + } else if (bpp == 4) { + dval = *((unsigned int*) dest); + sval = *((unsigned int*) src); + } else { + continue; + } + + /* extract dest and src RGB */ + rdst = (dval & rmask) >> rshift; /* fb */ + gdst = (dval & gmask) >> gshift; + bdst = (dval & bmask) >> bshift; + + rsrc = (sval & rmask) >> rshift; /* richcursor */ + gsrc = (sval & gmask) >> gshift; + bsrc = (sval & bmask) >> bshift; + + /* blend in fb data. */ + if (! c->alphaPreMultiplied) { + rsrc = (asrc * rsrc)/amax; + gsrc = (asrc * gsrc)/amax; + bsrc = (asrc * bsrc)/amax; + } + rdst = rsrc + ((amax - asrc) * rdst)/amax; + gdst = gsrc + ((amax - asrc) * gdst)/amax; + bdst = bsrc + ((amax - asrc) * bdst)/amax; + + val = 0; + val |= (rdst << rshift); + val |= (gdst << gshift); + val |= (bdst << bshift); + + /* insert the cooked pixel into the fb */ + memcpy(dest, &val, bpp); + } + } + } + else { + /* no alpha */ + for(j=0;jmask[(j+j1)*w+(i+i1)/8]<<((i+i1)&7))&0x80) + memcpy(screen->frameBuffer+(j+y1)*rowstride+(i+x1)*bpp, + c->richSource+(j+j1)*c->width*bpp+(i+i1)*bpp,bpp); + } + + mark_rect_as_modified(x1, y1, x1+x2, y1+y2, 1); + + UNLOCK(cl->screen->cursorMutex); +} + + +/* this restores the under cursor buffer we saved in + draw_cursor to the framebuffer */ +static void restore_under_cursor_buffer(rfbClientPtr cl) +{ + ClientData *cd = (ClientData *) cl->clientData; + rfbCursorPtr c; + int j,x1,x2,y1,y2,bpp=screen->serverFormat.bitsPerPixel/8, + rowstride=screen->paddedWidthInBytes; + + if(!cd) + return; + + c = cd->cursor; + + if(!c) + return; + + /* sanity checks */ + x1 = cd->cursor_x_saved - c->xhot; + x2 = x1 + c->width; + if(x1<0) { x1=0; } + if(x2 >= screen->width) x2= screen->width-1; + x2 -= x1; /* width */ + if(x2<=0) + return; /* nothing to do */ + + y1 = cd->cursor_y_saved - c->yhot; + y2 = y1 + c->height; + if(y1<0) { y1=0; } + if(y2>=screen->height) y2=screen->height-1; + y2 -= y1; /* height */ + if(y2<=0) + return; /* nothing to do */ + + LOCK(cl->screen->cursorMutex); + /* restore framebuffer from saved data */ + if(cd->under_cursor_buffer_len > 0) { + for(j=0;jframeBuffer+(y1+j)*rowstride+x1*bpp, + cd->under_cursor_buffer+j*x2*bpp, + x2*bpp); + + /* seems the additional w/2 and h/2 rect extension is needed in threaded mode */ + mark_rect_as_modified(x1-x2/2, y1-y2/2, x1+x2+x2/2, y1+y2+y2/2, 1); + } + UNLOCK(cl->screen->cursorMutex); +} + diff --git a/cursor.h b/cursor.h index 05986dd3..43c35c2c 100644 --- a/cursor.h +++ b/cursor.h @@ -59,13 +59,13 @@ extern void disable_cursor_shape_updates(rfbScreenInfoPtr s); extern int cursor_shape_updates_clients(rfbScreenInfoPtr s); extern int cursor_noshape_updates_clients(rfbScreenInfoPtr s); extern int cursor_pos_updates_clients(rfbScreenInfoPtr s); -extern void cursor_position(int x, int y); +extern void cursor_position(int x, int y, rfbClientPtr client); extern void set_no_cursor(void); extern void set_warrow_cursor(void); extern int set_cursor(int x, int y, int which); extern int check_x11_pointer(void); extern int store_cursor(int serial, unsigned long *data, int w, int h, int cbpp, int xhot, int yhot); extern unsigned long get_cursor_serial(int mode); -extern rfbCursorPtr pixels2curs(unsigned long *pixels, int w, int h, int xhot, int yhot, int Bpp); +extern rfbCursorPtr pixels2curs(uint32_t *pixels, int w, int h, int xhot, int yhot, int Bpp); #endif /* _X11VNC_CURSOR_H */ diff --git a/pointer.c b/pointer.c index 95c0444d..7c327670 100644 --- a/pointer.c +++ b/pointer.c @@ -369,7 +369,7 @@ void update_x11_pointer_position(int x, int y) { cursor_y = y; /* record the x, y position for the rfb screen as well. */ - cursor_position(x, y); + cursor_position(x, y, NULL); /* change the cursor shape if necessary */ rc = set_cursor(x, y, get_which_cursor()); diff --git a/screen.c b/screen.c index 77dd13a6..68bbcfcd 100644 --- a/screen.c +++ b/screen.c @@ -1286,7 +1286,7 @@ rfbBool vnc_reflect_cursor_pos(rfbClient *cl, int x, int y) { return TRUE; /* some clients initializing, cannot send */ } - cursor_position(x, y); + cursor_position(x, y, NULL); set_cursor(x, y, get_which_cursor()); return TRUE; @@ -1584,7 +1584,7 @@ rfbBool vnc_reflect_send_pointer(int x, int y, int mask) { cursor_y = y; /* record the x, y position for the rfb screen as well. */ - cursor_position(x, y); + cursor_position(x, y, NULL); /* change the cursor shape if necessary */ rc = set_cursor(x, y, get_which_cursor()); diff --git a/xi2_devices.c b/xi2_devices.c index aa239156..66a71c69 100644 --- a/xi2_devices.c +++ b/xi2_devices.c @@ -268,7 +268,7 @@ rfbCursorPtr setClientCursor(Display *dpy, int dev_id, float r, float g, float b X_UNLOCK; /* convert to rfb cursor which we return later */ - rfbcursor = pixels2curs((unsigned long*)cursor_image->pixels, + rfbcursor = pixels2curs(cursor_image->pixels, cursor_image->width, cursor_image->height, cursor_image->xhot, From 2b8ea322cd07890371fe5c62c99a292a015254c0 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sun, 28 Dec 2014 17:37:20 +0100 Subject: [PATCH 12/26] multipointer: Make pointer and keyboard handling use the device specific wrappers. --- keyboard.c | 88 ++++++++++++++++++++++++---------------- pointer.c | 115 +++++++++++++++++++++++++++++++++++++--------------- pointer.h | 4 +- xevents.c | 2 +- xwrappers.c | 2 +- 5 files changed, 139 insertions(+), 72 deletions(-) diff --git a/keyboard.c b/keyboard.c index a4750b94..8ac312f8 100644 --- a/keyboard.c +++ b/keyboard.c @@ -48,6 +48,8 @@ so, delete this exception statement from your version. #include "uinput.h" #include "macosx.h" #include "screen.h" +#include "xi2_devices.h" + void get_keystate(int *keystate); void clear_modifiers(int init); @@ -80,7 +82,7 @@ static void add_dead_keysyms(char *str); static void initialize_xkb_modtweak(void); static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client); -static void tweak_mod(signed char mod, rfbBool down); +static void tweak_mod(signed char mod, rfbBool down, int dev_id); static void modifier_tweak_keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client); static void pipe_keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client); @@ -188,7 +190,7 @@ void clear_modifiers(int init) { rfbLog("clear_modifiers: up: %-10s (0x%x) " "keycode=0x%x\n", keystrs[i], keysym, keycode); } - XTestFakeKeyEvent_wr(dpy, -1, keycode, False, CurrentTime); + XTestFakeKeyEvent_wr(dpy, -1, keycode, False, CurrentTime); /* multipointer FIXME? */ } XFlush_wr(dpy); #endif /* NO_X11 */ @@ -277,7 +279,7 @@ void clear_keys(void) { if (keystate[k]) { KeyCode keycode = (KeyCode) k; rfbLog("clear_keys: keycode=%d\n", keycode); - XTestFakeKeyEvent_wr(dpy, -1, keycode, False, CurrentTime); + XTestFakeKeyEvent_wr(dpy, -1, keycode, False, CurrentTime);/* multipointer FIXME? */ } } XFlush_wr(dpy); @@ -331,7 +333,7 @@ void clear_locks(void) { char *nm = XKeysymToString(ks); rfbLog("toggling: %03d / %03d -- %s\n", key, ks, nm ? nm : "BadKey"); did = 1; - XTestFakeKeyEvent_wr(dpy, -1, key, True, CurrentTime); + XTestFakeKeyEvent_wr(dpy, -1, key, True, CurrentTime); /* multipointer FIXME? */ usleep(10*1000); XTestFakeKeyEvent_wr(dpy, -1, key, False, CurrentTime); XFlush_wr(dpy); @@ -1593,7 +1595,10 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, if (!client || !down || !keysym) {} /* unused vars warning: */ - RAWFB_RET_VOID + RAWFB_RET_VOID; + + ClientData *cd = (ClientData *) client->clientData; + X_LOCK; @@ -1740,7 +1745,10 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, if (debug_keyboard > 1) { /* get state early for debug output */ - XkbGetState(dpy, XkbUseCoreKbd, &kbstate); + if(use_multipointer) + XkbGetState(dpy, cd->kbd_id, &kbstate); + else + XkbGetState(dpy, XkbUseCoreKbd, &kbstate); got_kbstate = 1; PKBSTATE } @@ -1778,8 +1786,11 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, int best = 0, best_score = -1; /* need to break the tie... */ if (! got_kbstate) { - XkbGetState(dpy, XkbUseCoreKbd, &kbstate); - got_kbstate = 1; + if(use_multipointer) + XkbGetState(dpy, cd->kbd_id, &kbstate); + else + XkbGetState(dpy, XkbUseCoreKbd, &kbstate); + got_kbstate = 1; } if (khints && keysym < 0x100) { int ks = (int) keysym, j; @@ -2034,8 +2045,11 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, if (! got_kbstate) { /* get the current modifier state if we haven't yet */ - XkbGetState(dpy, XkbUseCoreKbd, &kbstate); - got_kbstate = 1; + if(use_multipointer) + XkbGetState(dpy, cd->kbd_id, &kbstate); + else + XkbGetState(dpy, XkbUseCoreKbd, &kbstate); + got_kbstate = 1; } /* @@ -2277,7 +2291,7 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, "inadvertent Multi_key from Shift " "(doing %03d up now)\n", shift_is_down); } - XTestFakeKeyEvent_wr(dpy, -1, shift_is_down, False, + XTestFakeKeyEvent_wr(dpy, cd->kbd_id, shift_is_down, False, CurrentTime); } else { involves_multi_key = 0; @@ -2289,7 +2303,7 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, if (sentmods[i] == 0) continue; dn = (Bool) needmods[i]; if (dn) continue; - XTestFakeKeyEvent_wr(dpy, -1, sentmods[i], dn, CurrentTime); + XTestFakeKeyEvent_wr(dpy, cd->kbd_id, sentmods[i], dn, CurrentTime); } for (j=0; j<8; j++) { /* next, do the Mod downs */ @@ -2297,7 +2311,7 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, if (sentmods[i] == 0) continue; dn = (Bool) needmods[i]; if (!dn) continue; - XTestFakeKeyEvent_wr(dpy, -1, sentmods[i], dn, CurrentTime); + XTestFakeKeyEvent_wr(dpy, cd->kbd_id, sentmods[i], dn, CurrentTime); } if (involves_multi_key) { @@ -2309,14 +2323,14 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, "inadvertent Multi_key from Shift " "(doing %03d down now)\n", shift_is_down); } - XTestFakeKeyEvent_wr(dpy, -1, shift_is_down, True, + XTestFakeKeyEvent_wr(dpy, cd->kbd_id, shift_is_down, True, CurrentTime); } /* * With the above modifier work done, send the actual keycode: */ - XTestFakeKeyEvent_wr(dpy, -1, Kc_f, (Bool) down, CurrentTime); + XTestFakeKeyEvent_wr(dpy, cd->kbd_id, Kc_f, (Bool) down, CurrentTime); /* * Now undo the modifier work: @@ -2327,7 +2341,7 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, if (sentmods[i] == 0) continue; dn = (Bool) needmods[i]; if (!dn) continue; - XTestFakeKeyEvent_wr(dpy, -1, sentmods[i], !dn, + XTestFakeKeyEvent_wr(dpy, cd->kbd_id, sentmods[i], !dn, CurrentTime); } for (j=7; j>=0; j--) { @@ -2336,13 +2350,13 @@ static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, if (sentmods[i] == 0) continue; dn = (Bool) needmods[i]; if (dn) continue; - XTestFakeKeyEvent_wr(dpy, -1, sentmods[i], !dn, + XTestFakeKeyEvent_wr(dpy, cd->kbd_id, sentmods[i], !dn, CurrentTime); } } else { /* for up case, hopefully just need to pop it up: */ - XTestFakeKeyEvent_wr(dpy, -1, Kc_f, (Bool) down, CurrentTime); + XTestFakeKeyEvent_wr(dpy, cd->kbd_id, Kc_f, (Bool) down, CurrentTime); } X_UNLOCK; } @@ -2601,7 +2615,7 @@ void initialize_modtweak(void) { /* * does the actual tweak: */ -static void tweak_mod(signed char mod, rfbBool down) { +static void tweak_mod(signed char mod, rfbBool down, int dev_id) { rfbBool is_shift = mod_state & (LEFTSHIFT|RIGHTSHIFT); Bool dn = (Bool) down; KeyCode altgr = altgr_code; @@ -2628,20 +2642,20 @@ static void tweak_mod(signed char mod, rfbBool down) { X_LOCK; if (is_shift && mod != 1) { if (mod_state & LEFTSHIFT) { - XTestFakeKeyEvent_wr(dpy, -1, left_shift_code, !dn, CurrentTime); + XTestFakeKeyEvent_wr(dpy, dev_id, left_shift_code, !dn, CurrentTime); } if (mod_state & RIGHTSHIFT) { - XTestFakeKeyEvent_wr(dpy, -1, right_shift_code, !dn, CurrentTime); + XTestFakeKeyEvent_wr(dpy, dev_id, right_shift_code, !dn, CurrentTime); } } if ( ! is_shift && mod == 1 ) { - XTestFakeKeyEvent_wr(dpy, -1, left_shift_code, dn, CurrentTime); + XTestFakeKeyEvent_wr(dpy, dev_id, left_shift_code, dn, CurrentTime); } if ( altgr && (mod_state & ALTGR) && mod != 2 ) { - XTestFakeKeyEvent_wr(dpy, -1, altgr, !dn, CurrentTime); + XTestFakeKeyEvent_wr(dpy, dev_id, altgr, !dn, CurrentTime); } if ( altgr && ! (mod_state & ALTGR) && mod == 2 ) { - XTestFakeKeyEvent_wr(dpy, -1, altgr, dn, CurrentTime); + XTestFakeKeyEvent_wr(dpy, dev_id, altgr, dn, CurrentTime); } X_UNLOCK; @@ -2664,6 +2678,7 @@ static void modifier_tweak_keyboard(rfbBool down, rfbKeySym keysym, #else KeyCode k; int tweak = 0; + ClientData *cd = (ClientData *) client->clientData; RAWFB_RET_VOID @@ -2704,7 +2719,7 @@ static void modifier_tweak_keyboard(rfbBool down, rfbKeySym keysym, X_UNLOCK; tweak = 0; } else { - tweak_mod(modifiers[keysym], True); + tweak_mod(modifiers[keysym], True, cd->kbd_id); k = keycodes[keysym]; } } else { @@ -2734,12 +2749,12 @@ static void modifier_tweak_keyboard(rfbBool down, rfbKeySym keysym, } if ( k != NoSymbol ) { X_LOCK; - XTestFakeKeyEvent_wr(dpy, -1, k, (Bool) down, CurrentTime); + XTestFakeKeyEvent_wr(dpy, cd->kbd_id, k, (Bool) down, CurrentTime); X_UNLOCK; } if ( tweak ) { - tweak_mod(modifiers[keysym], False); + tweak_mod(modifiers[keysym], False, cd->kbd_id); } #endif /* NO_X11 */ } @@ -2872,7 +2887,7 @@ static void pipe_keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { int mask, button = (int) keysym; int x = cursor_x, y = cursor_y; char *b, bstr[32]; - + if (!down) { return; } @@ -2903,7 +2918,7 @@ static void pipe_keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { pointer_event(mask, x, y, client); } b++; - } + } return; } @@ -3059,6 +3074,7 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { static rfbKeySym max_keyrepeat_last_keysym = NoSymbol; static double max_keyrepeat_last_time = 0.0; static double max_keyrepeat_always = -1.0; + ClientData *cd = (ClientData *) client->clientData; if (threads_drop_input) { return; @@ -3310,7 +3326,7 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { got_user_input++; got_keyboard_input++; - RAWFB_RET_VOID + RAWFB_RET_VOID; apply_remap(&keysym, &isbutton); @@ -3340,7 +3356,7 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { if (isbutton) { int mask, button = (int) keysym; char *b, bstr[32]; - + if (! down) { INPUT_UNLOCK; return; /* nothing to send */ @@ -3357,7 +3373,7 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { * remap the button click to keystroke sequences! * Usually just will simulate the button click. */ - + /* loop over possible multiclicks: Button123 */ sprintf(bstr, "%d", button); b = bstr; @@ -3368,9 +3384,9 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { t[1] = '\0'; if (sscanf(t, "%d", &butt) == 1) { mask = 1<<(butt-1); - do_button_mask_change(mask, butt); /* down */ + do_button_mask_change(mask, butt, client); /* down */ mask = 0; - do_button_mask_change(mask, butt); /* up */ + do_button_mask_change(mask, butt, client); /* up */ } b++; } @@ -3406,8 +3422,10 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { k ? "" : " *ignored*"); } + if ( k != NoSymbol ) { - XTestFakeKeyEvent_wr(dpy, -1, k, (Bool) down, CurrentTime); + + XTestFakeKeyEvent_wr(dpy, cd->kbd_id, k, (Bool) down, CurrentTime); XFlush_wr(dpy); } diff --git a/pointer.c b/pointer.c index 7c327670..2f7cd614 100644 --- a/pointer.c +++ b/pointer.c @@ -49,19 +49,21 @@ so, delete this exception statement from your version. #include "scan.h" #include "macosx.h" #include "screen.h" +#include "xi2_devices.h" + int pointer_queued_sent = 0; void initialize_pointer_map(char *pointer_remap); -void do_button_mask_change(int mask, int button); +void do_button_mask_change(int mask, int button, rfbClientPtr client); void pointer_event(int mask, int x, int y, rfbClientPtr client); void initialize_pipeinput(void); int check_pipeinput(void); -void update_x11_pointer_position(int x, int y); +void update_x11_pointer_position(int x, int y, rfbClientPtr client); static void buttonparse(int from, char **s); -static void update_x11_pointer_mask(int mask); +static void update_x11_pointer_mask(int mask, rfbClientPtr client); static void pipe_pointer(int mask, int x, int y, rfbClientPtr client); /* @@ -335,7 +337,7 @@ void initialize_pointer_map(char *pointer_remap) { /* * Send a pointer position event to the X server. */ -void update_x11_pointer_position(int x, int y) { +void update_x11_pointer_position(int x, int y, rfbClientPtr client) { #if NO_X11 RAWFB_RET_VOID if (!x || !y) {} @@ -343,8 +345,13 @@ void update_x11_pointer_position(int x, int y) { #else int rc; + int ptr_id = -1; + RAWFB_RET_VOID + if(client) + ptr_id = ((ClientData*)client->clientData)->ptr_id; + X_LOCK; if (!always_inject && cursor_x == x && cursor_y == y) { ; @@ -353,11 +360,12 @@ void update_x11_pointer_position(int x, int y) { * off_x and off_y not needed with XWarpPointer since * window is used: */ - XWarpPointer(dpy, None, window, 0, 0, 0, 0, x + coff_x, - y + coff_y); + if(use_multipointer) + XIWarpPointer_wr(dpy, ptr_id, None, window, 0, 0, 0, 0, x + coff_x, y + coff_y); + else + XWarpPointer(dpy, None, window, 0, 0, 0, 0, x + coff_x, y + coff_y); } else { - XTestFakeMotionEvent_wr(dpy, -1, scr, x + off_x + coff_x, - y + off_y + coff_y, CurrentTime); + XTestFakeMotionEvent_wr(dpy, ptr_id, scr, x + off_x + coff_x, y + off_y + coff_y, CurrentTime); } X_UNLOCK; @@ -368,9 +376,6 @@ void update_x11_pointer_position(int x, int y) { cursor_x = x; cursor_y = y; - /* record the x, y position for the rfb screen as well. */ - cursor_position(x, y, NULL); - /* change the cursor shape if necessary */ rc = set_cursor(x, y, get_which_cursor()); cursor_changes += rc; @@ -379,12 +384,23 @@ void update_x11_pointer_position(int x, int y) { #endif /* NO_X11 */ } -void do_button_mask_change(int mask, int button) { + + +void do_button_mask_change(int mask, int button, rfbClientPtr client) { #if NO_X11 if (!mask || !button) {} return; #else int mb, k, i = button-1; + int ptr_buttonmask = button_mask; + int ptr_id = -1, kbd_id = -1; + + if(client) { + ClientData *cd = (ClientData*)client->clientData; + ptr_buttonmask = cd->ptr_buttonmask; + ptr_id = cd->ptr_id; + kbd_id = cd->kbd_id; + } /* * this expands to any pointer_map button -> keystrokes @@ -404,7 +420,7 @@ void do_button_mask_change(int mask, int button) { if ((num_buttons && mb > num_buttons) || mb < 1) { rfbLog("ignoring mouse button out of " "bounds: %d>%d mask: 0x%x -> 0x%x\n", - mb, num_buttons, button_mask, mask); + mb, num_buttons, ptr_buttonmask, mask); continue; } if (debug_pointer) { @@ -412,7 +428,7 @@ void do_button_mask_change(int mask, int button) { " %s (event %d)\n", mb, bmask ? "down" : "up", k+1); } - XTestFakeButtonEvent_wr(dpy, -1, mb, (mask & (1<clientData) { + ptr_buttonmask = ((ClientData*)client->clientData)->ptr_buttonmask; + + /* set XIClientPointer */ + /* FIXME done all the time while button down, maybe establish + some concept like window ownership? */ + if(mask) /* down */ + setXIClientPointer(dpy, ((ClientData*)client->clientData)->ptr_id); + + } + + if (mask != ptr_buttonmask) { + last_pointer_click_time = dnow(); } if (nofb) { @@ -474,7 +502,7 @@ static void update_x11_pointer_mask(int mask) { xr_mouse = 0; } else if (skip_cr_when_scaling("scroll")) { xr_mouse = 0; - } else if (xrecord_skip_button(mask, button_mask)) { + } else if (xrecord_skip_button(mask, ptr_buttonmask)) { xr_mouse = 0; } @@ -484,7 +512,7 @@ static void update_x11_pointer_mask(int mask) { Window frame = None, mwin = None; int skip = 0; - if (!button_mask) { + if (!ptr_buttonmask) { X_LOCK; if (get_wm_frame_pos(&px, &py, &x, &y, &w, &h, &frame, &mwin)) { @@ -535,7 +563,7 @@ if (debug_scroll > 1) fprintf(stderr, "internal scrollbar: %dx%d\n", w, h); xrecord_watch(1, SCR_MOUSE); snapshot_stack_list(0, 0.50); snapped = 1; - if (button_mask) { + if (ptr_buttonmask) { xrecord_set_by_mouse = 1; } else { update_stack_list(); @@ -544,7 +572,14 @@ if (debug_scroll > 1) fprintf(stderr, "internal scrollbar: %dx%d\n", w, h); } } - if (mask && !button_mask) { + /* some button down, was up before */ + if (mask && !ptr_buttonmask) { + /* set keyboard focus to window underneath this pointer. + we do it ourselves since most window managers are buggy wrt XI2 */ + /* FIXME can maybe be removed once XISetClientPointer is working */ + if(client && client->clientData) + setDeviceFocus(dpy,((ClientData*)client->clientData)->ptr_id); + /* button down, snapshot the stacking list before flushing */ if (wireframe && !wireframe_in_progress && strcmp(wireframe_copyrect, "never")) { @@ -558,12 +593,12 @@ if (debug_scroll > 1) fprintf(stderr, "internal scrollbar: %dx%d\n", w, h); /* look for buttons that have be clicked or released: */ for (i=0; i < MAX_BUTTONS; i++) { - if ( (button_mask & (1< " - "0x%x button: %d\n", button_mask, mask,i+1); + "0x%x button: %d\n", ptr_buttonmask, mask,i+1); } - do_button_mask_change(mask, i+1); /* button # is i+1 */ + do_button_mask_change(mask, i+1, client); /* button # is i+1 */ } } @@ -575,6 +610,8 @@ if (debug_scroll > 1) fprintf(stderr, "internal scrollbar: %dx%d\n", w, h); */ button_mask_prev = button_mask; button_mask = mask; + if(client) + ((ClientData*)client->clientData)->ptr_buttonmask = mask; #endif /* NO_X11 */ } @@ -617,14 +654,14 @@ static void pipe_pointer(int mask, int x, int y, rfbClientPtr client) { } hint[0] = '\0'; - if (mask == button_mask) { + if (mask == cd->ptr_buttonmask) { strcat(hint, "None"); } else { int i, old, newb, m = 1, cnt = 0; for (i=0; iptr_buttonmask & m; newb = mask & m; m = m << 1; @@ -663,6 +700,14 @@ void pointer_event(int mask, int x, int y, rfbClientPtr client) { allowed_input_t input; int sent = 0, buffer_it = 0; double now; + ClientData *cd = NULL; + + if(client) + cd = (ClientData *) client->clientData; + + /* needed to allow multiple dragging actions at once */ + if(client && use_multipointer) + client->screen->pointerClient = NULL; if (threads_drop_input) { return; @@ -731,6 +776,8 @@ void pointer_event(int mask, int x, int y, rfbClientPtr client) { /* raw_fb hack track button state */ button_mask_prev = button_mask; button_mask = mask; + if(cd) + cd->ptr_buttonmask = mask; } if (!view_only && (input.motion || input.button)) { last_rfb_ptr_injected = dnow(); @@ -802,6 +849,7 @@ void pointer_event(int mask, int x, int y, rfbClientPtr client) { /* storage for the event queue */ static int nevents = 0; static int ev[NEV][3]; + static rfbClientPtr ev_cl[NEV]; int i; /* timer things */ static double dt = 0.0, tmr = 0.0, maxwait = 0.4; @@ -843,6 +891,7 @@ void pointer_event(int mask, int x, int y, rfbClientPtr client) { ev[i][0] = mask; ev[i][1] = x; ev[i][2] = y; + ev_cl[i] = client; if (! input.button) { ev[i][0] = -1; } @@ -878,11 +927,11 @@ void pointer_event(int mask, int x, int y, rfbClientPtr client) { i+1, dnowx()); } if (ev[i][1] >= 0) { - update_x11_pointer_position(ev[i][1], ev[i][2]); + update_x11_pointer_position(ev[i][1], ev[i][2], ev_cl[i]); sent = 1; } if (ev[i][0] >= 0) { - update_x11_pointer_mask(ev[i][0]); + update_x11_pointer_mask(ev[i][0], ev_cl[i]); sent = 1; } @@ -920,15 +969,15 @@ void pointer_event(int mask, int x, int y, rfbClientPtr client) { /* update the X display with the event: */ if (input.motion) { - update_x11_pointer_position(x, y); + update_x11_pointer_position(x, y, client); sent = 1; } if (input.button) { - if (mask != button_mask) { + if (mask != button_mask) { /*FIXME multipointer?*/ button_change_x = cursor_x; button_change_y = cursor_y; } - update_x11_pointer_mask(mask); + update_x11_pointer_mask(mask, client); sent = 1; } diff --git a/pointer.h b/pointer.h index 1fe5e0bc..ff624749 100644 --- a/pointer.h +++ b/pointer.h @@ -38,10 +38,10 @@ so, delete this exception statement from your version. extern int pointer_queued_sent; extern void initialize_pointer_map(char *pointer_remap); -extern void do_button_mask_change(int mask, int button); +extern void do_button_mask_change(int mask, int button, rfbClientPtr client); extern void pointer_event(int mask, int x, int y, rfbClientPtr client); extern int check_pipeinput(void); extern void initialize_pipeinput(void); -extern void update_x11_pointer_position(int x, int y); +extern void update_x11_pointer_position(int x, int y, rfbClientPtr client); #endif /* _X11VNC_POINTER_H */ diff --git a/xevents.c b/xevents.c index 74e92858..7d9fcf60 100644 --- a/xevents.c +++ b/xevents.c @@ -1901,7 +1901,7 @@ void set_single_window(rfbClientPtr cl, int x, int y) { int rootx, rooty, wx, wy; unsigned int mask; - update_x11_pointer_position(x, y); + update_x11_pointer_position(x, y, cl); XSync(dpy, False); if (XQueryPointer_wr(dpy, rootwin, &r, &c, &rootx, &rooty, diff --git a/xwrappers.c b/xwrappers.c index 077d6ae2..33408434 100644 --- a/xwrappers.c +++ b/xwrappers.c @@ -1617,7 +1617,7 @@ Bool XIWarpPointer_wr( Display *display, double dest_x, double dest_y) { -#if NO_X11 +#if NO_X11 || !HAVE_XI2 return False; #else Bool rc; From 4819a776f7988141c6065a099edfb292650f07e5 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sun, 28 Dec 2014 17:45:56 +0100 Subject: [PATCH 13/26] multipointer: Make uinput injection device specific. --- uinput.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/uinput.c b/uinput.c index 92bd1d81..ac0324f1 100644 --- a/uinput.c +++ b/uinput.c @@ -994,6 +994,7 @@ void uinput_pointer_command(int mask, int x, int y, rfbClientPtr client) { int do_reset, reset_lower_right = 1; double now; static int first = 1; + ClientData *cd = (ClientData *) client->clientData; if (first) { if (getenv("RESET_ALWAYS")) { @@ -1155,7 +1156,7 @@ void uinput_pointer_command(int mask, int x, int y, rfbClientPtr client) { fprintf(stderr, "mask: %s\n", bitprint(mask, 16)); fprintf(stderr, "bmask: %s\n", bitprint(bmask, 16)); fprintf(stderr, "last_mask: %s\n", bitprint(last_mask, 16)); - fprintf(stderr, "button_mask: %s\n", bitprint(button_mask, 16)); + fprintf(stderr, "button_mask: %s\n", bitprint(cd->ptr_buttonmask, 16)); } if (uinput_touchscreen) { From f6c68364f3bcc91850604bfd225f4f89537088df Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sun, 28 Dec 2014 17:55:34 +0100 Subject: [PATCH 14/26] multipointer: Save and restore under cursor frame buffers for non-threaded case. --- util.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/util.c b/util.c index 80b16079..aaf8269e 100644 --- a/util.c +++ b/util.c @@ -37,6 +37,8 @@ so, delete this exception statement from your version. #include "win_utils.h" #include "unixpw.h" #include "connections.h" +#include "cursor.h" +#include "xi2_devices.h" struct timeval _mysleep; @@ -578,10 +580,32 @@ int rfbPE(long usec) { } if (! use_threads) { rfbBool r; + rfbClientIteratorPtr iter; + rfbClientPtr cl; + + if(use_multipointer) { + iter = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(iter)) ) + save_under_cursor_buffer(cl); + rfbReleaseClientIterator(iter); + + iter = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(iter)) ) + draw_cursor(cl); + rfbReleaseClientIterator(iter); + } + r = rfbProcessEvents(screen, usec); if (r) { res = 1; } + + if(use_multipointer) { + iter = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(iter)) ) + restore_under_cursor_buffer(cl); + rfbReleaseClientIterator(iter); + } } if (unixpw && unixpw_in_progress && !uip0) { From 43af8ac2aaeb99d628f8eebbd271dfe7bf508b3f Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sun, 28 Dec 2014 17:58:51 +0100 Subject: [PATCH 15/26] multipointer: Actually create/remove XI2 devices upon client connect/disconnect. --- connections.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/connections.c b/connections.c index 4885e4b1..7503f18b 100644 --- a/connections.c +++ b/connections.c @@ -54,6 +54,8 @@ so, delete this exception statement from your version. #include "userinput.h" #include "pointer.h" #include "xrandr.h" +#include "xi2_devices.h" + /* * routines for handling incoming, outgoing, etc connections @@ -783,6 +785,18 @@ static void free_client_data(rfbClientPtr client) { free(cd->unixname); cd->unixname = NULL; } + if (cd->cursor) { + rfbFreeCursor(cd->cursor); + cd->cursor = NULL; + } + if (cd->under_cursor_buffer) { + free(cd->under_cursor_buffer); + cd->under_cursor_buffer = NULL; + } + if (cd->cursor_region) { + sraRgnDestroy(cd->cursor_region); + cd->cursor_region = NULL; + } } free(client->clientData); client->clientData = NULL; @@ -886,6 +900,11 @@ void client_gone(rfbClientPtr client) { } } + /* remove clients XInput2 master device */ + if(use_multipointer) + if(removeMD(dpy, cd->ptr_id)) + rfbLog("removed XInput2 MD for client %s.\n", client->host); + free_client_data(client); if (inetd && client == inetd_client) { @@ -3964,6 +3983,12 @@ enum rfbNewClientAction new_client(rfbClientPtr client) { return(RFB_CLIENT_REFUSE); } + if(use_multipointer && xi2_device_creation_in_progress) { + rfbLog("denying additional client: %s during MD creation.\n", client->host); + CLIENT_UNLOCK; + return(RFB_CLIENT_REFUSE); + } + client->clientData = (void *) calloc(sizeof(ClientData), 1); cd = (ClientData *) client->clientData; @@ -3971,6 +3996,7 @@ enum rfbNewClientAction new_client(rfbClientPtr client) { cd->client_port = -1; cd->username = strdup(""); cd->unixname = strdup(""); + cd->cursor_x_saved = cd->cursor_y_saved = -1; cd->input[0] = '-'; cd->login_viewonly = -1; @@ -4015,6 +4041,38 @@ enum rfbNewClientAction new_client(rfbClientPtr client) { cd->uid = clients_served; + /* + create new XInput2 master device and add it it to client + */ + if(use_multipointer) + { + char tmp[256]; + snprintf(tmp, 256, "x11vnc %s", client->host); + + xi2_device_creation_in_progress = 1; + + if((cd->ptr_id = createMD(dpy, tmp)) < 0) { + rfbLog("ERROR creating XInput2 MD for client %s, denying client.\n", client->host); + free_client_data(client); + xi2_device_creation_in_progress = 0; + CLIENT_UNLOCK; + return(RFB_CLIENT_REFUSE); + } + + cd->kbd_id = getPairedMD(dpy, cd->ptr_id); + + rfbLog("Created XInput2 MD %i %i for client %s.\n", cd->ptr_id, cd->kbd_id, client->host); + + xi2_device_creation_in_progress = 0; + + snprintf(tmp, 256, "%i", cd->uid); + cd->cursor = setClientCursor(dpy, cd->ptr_id, 0.4*(cd->ptr_id%3), 0.2*(cd->ptr_id%5), 1*(cd->ptr_id%2), tmp); + if(!cd->cursor) + rfbLog("Setting cursor for client %s failed.\n", client->host); + + cd->cursor_region = sraRgnCreate(); + } + client->clientGoneHook = client_gone; if (client_count) { From a5c987cad121274c90304deb5e7af093baf54edb Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sun, 28 Dec 2014 18:01:12 +0100 Subject: [PATCH 16/26] multipointer: Remove all created XI2 devices upon shutdown. --- cleanup.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/cleanup.c b/cleanup.c index 344db0c1..c0b87774 100644 --- a/cleanup.c +++ b/cleanup.c @@ -51,6 +51,7 @@ so, delete this exception statement from your version. #include "xrecord.h" #include "xevents.h" #include "uinput.h" +#include "xi2_devices.h" /* * Exiting and error handling routines @@ -143,6 +144,7 @@ static void clean_icon_mode(void) { } } + /* * Normal exiting */ @@ -210,6 +212,18 @@ void clean_up_exit(int ret) { /* X keyboard cleanups */ delete_added_keycodes(0); + /* remove all created XInput2 devices */ + if(use_multipointer) { + rfbClientIteratorPtr iter = rfbGetClientIterator(screen); + rfbClientPtr cl; + while((cl = rfbClientIteratorNext(iter))) { + ClientData *cd = (ClientData *) cl->clientData; + if(removeMD(dpy, cd->ptr_id)) + rfbLog("cleanup: removed XInput2 MD for client %s.\n", cl->host); + } + rfbReleaseClientIterator(iter); + } + if (clear_mods == 1) { clear_modifiers(0); } else if (clear_mods == 2) { @@ -529,11 +543,11 @@ static void interrupted (int sig) { if (exit_flag) { fprintf(stderr, "extra[%d] signal: %d\n", exit_flag, sig); exit_flag++; - if (use_threads) { - usleep2(250 * 1000); - } else if (exit_flag <= 2) { - return; - } + if (use_threads) + usleep2(250 * 1000); + if (exit_flag <= 2) + return; + if (rm_flagfile) { unlink(rm_flagfile); rm_flagfile = NULL; From 6e18d742bae21cc5177b1e9e852bdb1143e5475d Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sun, 28 Dec 2014 18:16:54 +0100 Subject: [PATCH 17/26] multipointer: Setup screen struct for multipointer, adapt watch_loop(). --- screen.c | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/screen.c b/screen.c index 68bbcfcd..e03f00ad 100644 --- a/screen.c +++ b/screen.c @@ -61,6 +61,7 @@ so, delete this exception statement from your version. #include "xrandr.h" #include "xrecord.h" #include "pm.h" +#include "xi2_devices.h" #include @@ -3553,6 +3554,11 @@ void initialize_screen(int *argc, char **argv, XImage *fb) { cmap8to24_fb = NULL; rot_fb = NULL; + if (use_multipointer) { + /* needed to allow multiple dragging actions at once */ + screen->deferPtrUpdateTime = 0; + } + if (cmap8to24) { int n = main_bytes_per_line * fb->height; if (depth <= 8) { @@ -4588,7 +4594,7 @@ void watch_loop(void) { } } - if (skip_scan_for_updates) { + if (skip_scan_for_updates || nofb) { ; } else if (button_mask && (!show_dragging || pointer_mode == 0)) { /* @@ -4600,13 +4606,11 @@ void watch_loop(void) { XFlush_wr(dpy); X_UNLOCK; dt = 0.0; - } else { + } else { /* scan for updates case */ static double last_dt = 0.0; double xdamage_thrash = 0.4; static int tilecut = -1; - check_cursor_changes(); - /* for timing the scan to try to detect thrashing */ if (use_xdamage && last_dt > xdamage_thrash) { @@ -4643,6 +4647,18 @@ void watch_loop(void) { X_UNLOCK; } #endif + /* Now, for scanning and drawing soft cursors (i.e. writing to the framebuffer), + make sure we're not sending any updates to clients (i.e. reading the framebuffer). + Otherwise we get flicker! */ + { + rfbClientPtr cl; + rfbClientIteratorPtr iter = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(iter)) ) { + LOCK(cl->sendMutex); + } + rfbReleaseClientIterator(iter); + } + if (use_snapfb) { int t, tries = 3; copy_snap(); @@ -4672,7 +4688,22 @@ void watch_loop(void) { tm - x11vnc_start, rate/1000000.0, nap_ok); } - } + /* important to have this here since it draws cursors into framebuffer */ + check_cursor_changes(); + + /* + Release the send ban again. + */ + { + rfbClientPtr cl; + rfbClientIteratorPtr iter = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(iter)) ) { + UNLOCK(cl->sendMutex); + } + rfbReleaseClientIterator(iter); + } + + } /* END scan for updates case */ /* sleep a bit to lessen load */ wait = choose_delay(dt); From e78c24ca5424cba9749b346d96ad2d5d609309f2 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sun, 28 Dec 2014 18:30:16 +0100 Subject: [PATCH 18/26] multipointer: Check for clientData as well. --- pointer.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pointer.c b/pointer.c index 2f7cd614..b819d1f0 100644 --- a/pointer.c +++ b/pointer.c @@ -349,7 +349,7 @@ void update_x11_pointer_position(int x, int y, rfbClientPtr client) { RAWFB_RET_VOID - if(client) + if(client && client->clientData) ptr_id = ((ClientData*)client->clientData)->ptr_id; X_LOCK; @@ -395,7 +395,7 @@ void do_button_mask_change(int mask, int button, rfbClientPtr client) { int ptr_buttonmask = button_mask; int ptr_id = -1, kbd_id = -1; - if(client) { + if(client && client->clientData) { ClientData *cd = (ClientData*)client->clientData; ptr_buttonmask = cd->ptr_buttonmask; ptr_id = cd->ptr_id; @@ -610,7 +610,7 @@ if (debug_scroll > 1) fprintf(stderr, "internal scrollbar: %dx%d\n", w, h); */ button_mask_prev = button_mask; button_mask = mask; - if(client) + if(client && client->clientData) ((ClientData*)client->clientData)->ptr_buttonmask = mask; #endif /* NO_X11 */ } @@ -702,7 +702,7 @@ void pointer_event(int mask, int x, int y, rfbClientPtr client) { double now; ClientData *cd = NULL; - if(client) + if(client && client->clientData) cd = (ClientData *) client->clientData; /* needed to allow multiple dragging actions at once */ From ed8a84a85267c99a182a4961197479fad798fdd9 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sun, 28 Dec 2014 18:35:37 +0100 Subject: [PATCH 19/26] multipointer: Make cursor drawing routines public. --- cursor.c | 12 ++++++------ cursor.h | 3 +++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/cursor.c b/cursor.c index 8802dbf3..8c14b6c7 100644 --- a/cursor.c +++ b/cursor.c @@ -71,9 +71,9 @@ 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); rfbCursorPtr pixels2curs(uint32_t *pixels, int w, int h, int xhot, int yhot, int Bpp); -static void save_under_cursor_buffer(rfbClientPtr cl); -static void draw_cursor(rfbClientPtr cl); -static void restore_under_cursor_buffer(rfbClientPtr cl); +void save_under_cursor_buffer(rfbClientPtr cl); +void draw_cursor(rfbClientPtr cl); +void restore_under_cursor_buffer(rfbClientPtr cl); typedef struct win_str_info { char *wm_name; @@ -2143,7 +2143,7 @@ if (0) fprintf(stderr, "check_x11_pointer %d %d\n", root_x, root_y); notion of a single cursor, we draw the extra client cursor directly into the framebuffer to provide some visual feedback to the user. */ -static void save_under_cursor_buffer(rfbClientPtr cl) +void save_under_cursor_buffer(rfbClientPtr cl) { ClientData *cd = (ClientData *) cl->clientData; rfbCursorPtr c; @@ -2201,7 +2201,7 @@ static void save_under_cursor_buffer(rfbClientPtr cl) UNLOCK(cl->updateMutex); } -static void draw_cursor(rfbClientPtr cl) +void draw_cursor(rfbClientPtr cl) { ClientData *cd = (ClientData *) cl->clientData; rfbCursorPtr c; @@ -2347,7 +2347,7 @@ static void draw_cursor(rfbClientPtr cl) /* this restores the under cursor buffer we saved in draw_cursor to the framebuffer */ -static void restore_under_cursor_buffer(rfbClientPtr cl) +void restore_under_cursor_buffer(rfbClientPtr cl) { ClientData *cd = (ClientData *) cl->clientData; rfbCursorPtr c; diff --git a/cursor.h b/cursor.h index 43c35c2c..0cd42d18 100644 --- a/cursor.h +++ b/cursor.h @@ -67,5 +67,8 @@ extern int check_x11_pointer(void); extern int store_cursor(int serial, unsigned long *data, int w, int h, int cbpp, int xhot, int yhot); extern unsigned long get_cursor_serial(int mode); extern rfbCursorPtr pixels2curs(uint32_t *pixels, int w, int h, int xhot, int yhot, int Bpp); +void save_under_cursor_buffer(rfbClientPtr cl); +void draw_cursor(rfbClientPtr cl); +void restore_under_cursor_buffer(rfbClientPtr cl); #endif /* _X11VNC_CURSOR_H */ From 3ddba1b3253bd229f6167f8368885beb24d0e648 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Tue, 30 Dec 2014 13:08:31 +0100 Subject: [PATCH 20/26] multipointer: Only set device focus and client ptr if in multipointer mode. --- pointer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pointer.c b/pointer.c index b819d1f0..8f0bafbf 100644 --- a/pointer.c +++ b/pointer.c @@ -485,7 +485,7 @@ static void update_x11_pointer_mask(int mask, rfbClientPtr client) { /* set XIClientPointer */ /* FIXME done all the time while button down, maybe establish some concept like window ownership? */ - if(mask) /* down */ + if(use_multipointer && mask) /* down */ setXIClientPointer(dpy, ((ClientData*)client->clientData)->ptr_id); } @@ -577,7 +577,7 @@ if (debug_scroll > 1) fprintf(stderr, "internal scrollbar: %dx%d\n", w, h); /* set keyboard focus to window underneath this pointer. we do it ourselves since most window managers are buggy wrt XI2 */ /* FIXME can maybe be removed once XISetClientPointer is working */ - if(client && client->clientData) + if(use_multipointer && client && client->clientData) setDeviceFocus(dpy,((ClientData*)client->clientData)->ptr_id); /* button down, snapshot the stacking list before flushing */ From b07445c19942b52e157f0070e66848236ae996cc Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Tue, 30 Dec 2014 13:47:31 +0100 Subject: [PATCH 21/26] multipointer: make sure we always cleanup created XI2 devices. --- cleanup.c | 21 +++++++++++---------- xi2_devices.c | 13 +++++++++++++ xi2_devices.h | 7 +++++++ 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/cleanup.c b/cleanup.c index c0b87774..ab4f5f20 100644 --- a/cleanup.c +++ b/cleanup.c @@ -213,16 +213,8 @@ void clean_up_exit(int ret) { delete_added_keycodes(0); /* remove all created XInput2 devices */ - if(use_multipointer) { - rfbClientIteratorPtr iter = rfbGetClientIterator(screen); - rfbClientPtr cl; - while((cl = rfbClientIteratorNext(iter))) { - ClientData *cd = (ClientData *) cl->clientData; - if(removeMD(dpy, cd->ptr_id)) - rfbLog("cleanup: removed XInput2 MD for client %s.\n", cl->host); - } - rfbReleaseClientIterator(iter); - } + if(use_multipointer) + removeAllMDs(dpy); if (clear_mods == 1) { clear_modifiers(0); @@ -552,6 +544,11 @@ static void interrupted (int sig) { unlink(rm_flagfile); rm_flagfile = NULL; } + + /* remove all created XInput2 devices */ + if(use_multipointer) + removeAllMDs(dpy); + exit(4); } exit_flag++; @@ -592,6 +589,10 @@ static void interrupted (int sig) { /* X keyboard cleanups */ delete_added_keycodes(0); + /* remove all created XInput2 devices */ + if(use_multipointer) + removeAllMDs(dpy); + if (clear_mods == 1) { clear_modifiers(0); } else if (clear_mods == 2) { diff --git a/xi2_devices.c b/xi2_devices.c index 66a71c69..a93cb137 100644 --- a/xi2_devices.c +++ b/xi2_devices.c @@ -153,6 +153,19 @@ int removeMD(Display* dpy, int dev_id) } +void removeAllMDs(Display *dpy) +{ + /* remove all created XInput2 devices */ + rfbClientIteratorPtr iter = rfbGetClientIterator(screen); + rfbClientPtr cl; + while((cl = rfbClientIteratorNext(iter))) { + ClientData *cd = (ClientData *) cl->clientData; + if(removeMD(dpy, cd->ptr_id)) + rfbLog("cleanup: removed XInput2 MD for client %s.\n", cl->host); + } + rfbReleaseClientIterator(iter); +} + diff --git a/xi2_devices.h b/xi2_devices.h index 88c771ff..c233ef15 100644 --- a/xi2_devices.h +++ b/xi2_devices.h @@ -46,6 +46,13 @@ extern int createMD(Display* dpy, char* name); */ extern int removeMD(Display* dpy, int dev_id); + +/** + removes all created xi2 master devices. +*/ +void removeAllMDs(Display *dpy); + + /* gets the paired pointer/keyboard id to dev_id returns -1 on error From 5b22faeb9bec5638f540908a2270f604d27fb9e9 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Fri, 2 Jan 2015 16:34:13 +0100 Subject: [PATCH 22/26] multipointer: fix deadlock in SetDeviceFocus(). --- xi2_devices.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/xi2_devices.c b/xi2_devices.c index a93cb137..41091833 100644 --- a/xi2_devices.c +++ b/xi2_devices.c @@ -333,6 +333,10 @@ int setDeviceFocus(Display* dpy, int ptr_id) XIModifierState modifiers_return; XIGroupState group_return; + XIDeviceInfo* devinfo; + int devicecount = 0; + int paired = -1; + if(ptr_id < 0) return 0; @@ -346,7 +350,13 @@ int setDeviceFocus(Display* dpy, int ptr_id) &root_x_return, &root_y_return, &win_x_return, &win_y_return, &buttons_return, &modifiers_return, &group_return); - XISetFocus(dpy, getPairedMD(dpy, ptr_id), find_client(dpy, root_return, child_return), CurrentTime); + /* get paired keyboard */ + devinfo = XIQueryDevice(dpy, ptr_id, &devicecount); + if(devicecount) + paired = devinfo->attachment; + XIFreeDeviceInfo(devinfo); + + XISetFocus(dpy, paired, find_client(dpy, root_return, child_return), CurrentTime); XSync(dpy, False); if(trapped_xerror) { From 3b2ff1bc75f66556abfdbb9661c3f0364f8ba09f Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Fri, 2 Jan 2015 16:44:49 +0100 Subject: [PATCH 23/26] multipointer: Fix deadlock in non-threaded mode. --- screen.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/screen.c b/screen.c index e03f00ad..5d37761c 100644 --- a/screen.c +++ b/screen.c @@ -4650,7 +4650,7 @@ void watch_loop(void) { /* Now, for scanning and drawing soft cursors (i.e. writing to the framebuffer), make sure we're not sending any updates to clients (i.e. reading the framebuffer). Otherwise we get flicker! */ - { + if(use_threads){ rfbClientPtr cl; rfbClientIteratorPtr iter = rfbGetClientIterator(screen); while( (cl = rfbClientIteratorNext(iter)) ) { @@ -4694,7 +4694,7 @@ void watch_loop(void) { /* Release the send ban again. */ - { + if(use_threads){ rfbClientPtr cl; rfbClientIteratorPtr iter = rfbGetClientIterator(screen); while( (cl = rfbClientIteratorNext(iter)) ) { From 50f3c18fd0841ad8d35e0613312e742c092213a4 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Fri, 2 Jan 2015 16:56:38 +0100 Subject: [PATCH 24/26] Revert "multipointer: Save and restore under cursor frame buffers for non-threaded case." This reverts commit f6c68364f3bcc91850604bfd225f4f89537088df. Was actually not needed and caused flicker. --- util.c | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/util.c b/util.c index aaf8269e..80b16079 100644 --- a/util.c +++ b/util.c @@ -37,8 +37,6 @@ so, delete this exception statement from your version. #include "win_utils.h" #include "unixpw.h" #include "connections.h" -#include "cursor.h" -#include "xi2_devices.h" struct timeval _mysleep; @@ -580,32 +578,10 @@ int rfbPE(long usec) { } if (! use_threads) { rfbBool r; - rfbClientIteratorPtr iter; - rfbClientPtr cl; - - if(use_multipointer) { - iter = rfbGetClientIterator(screen); - while( (cl = rfbClientIteratorNext(iter)) ) - save_under_cursor_buffer(cl); - rfbReleaseClientIterator(iter); - - iter = rfbGetClientIterator(screen); - while( (cl = rfbClientIteratorNext(iter)) ) - draw_cursor(cl); - rfbReleaseClientIterator(iter); - } - r = rfbProcessEvents(screen, usec); if (r) { res = 1; } - - if(use_multipointer) { - iter = rfbGetClientIterator(screen); - while( (cl = rfbClientIteratorNext(iter)) ) - restore_under_cursor_buffer(cl); - rfbReleaseClientIterator(iter); - } } if (unixpw && unixpw_in_progress && !uip0) { From a4d2044eba593be56c7b1dabbc492f9f7b880460 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Fri, 2 Jan 2015 18:22:20 +0100 Subject: [PATCH 25/26] multipointer: remove obsolete FIXME. --- pointer.c | 1 - 1 file changed, 1 deletion(-) diff --git a/pointer.c b/pointer.c index 8f0bafbf..f96a40ef 100644 --- a/pointer.c +++ b/pointer.c @@ -576,7 +576,6 @@ if (debug_scroll > 1) fprintf(stderr, "internal scrollbar: %dx%d\n", w, h); if (mask && !ptr_buttonmask) { /* set keyboard focus to window underneath this pointer. we do it ourselves since most window managers are buggy wrt XI2 */ - /* FIXME can maybe be removed once XISetClientPointer is working */ if(use_multipointer && client && client->clientData) setDeviceFocus(dpy,((ClientData*)client->clientData)->ptr_id); From 43a2fa1512e4732c2be98e55723ee9f37cfb5637 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sat, 3 Jan 2015 18:43:28 +0100 Subject: [PATCH 26/26] multipointer: Make sure XI2 support is disabled when there's no X server. --- x11vnc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/x11vnc.c b/x11vnc.c index ed8253c3..0138424b 100644 --- a/x11vnc.c +++ b/x11vnc.c @@ -5147,6 +5147,7 @@ int main(int argc, char* argv[]) { if (! dpy && raw_fb_str) { rfbLog("Continuing without X display in -rawfb mode.\n"); + use_multipointer = 0; /* XI2 multipointer makes no sense without X */ goto raw_fb_pass_go_and_collect_200_dollars; }