diff --git a/configure.ac b/configure.ac index 3203d7a..29a3ed1 100644 --- a/configure.ac +++ b/configure.ac @@ -79,6 +79,7 @@ AH_TEMPLATE(HAVE_LIBXINERAMA, [XINERAMA extension build environment present]) 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_LIBXCURSOR, [Xcursor library 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]) @@ -118,6 +119,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. @@ -230,6 +234,18 @@ 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 @@ -431,6 +447,34 @@ 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. @@ -721,6 +765,11 @@ if test "x$with_gnutls" != "xno"; then fi fi +# XI2 present? +AM_CONDITIONAL(HAVE_XI2, [ test "$HAVE_XI2" = "yes" ]) +# cairo? +AM_CONDITIONAL(HAVE_CAIRO, [ test "$with_cairo" = "yes" ]) + # IPv6 AH_TEMPLATE(IPv6, [Enable IPv6 support]) AC_ARG_WITH(ipv6, @@ -739,7 +788,6 @@ if test "x$with_ipv6" != "xno"; then fi - # Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stdlib.h string.h sys/socket.h sys/time.h sys/timeb.h syslog.h unistd.h ws2tcpip.h]) @@ -747,6 +795,7 @@ AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stdlib.h string.h sys # x11vnc only: if test "$build_x11vnc" = "yes"; then AC_CHECK_HEADERS([pwd.h sys/wait.h utmpx.h termios.h sys/ioctl.h sys/stropts.h]) + AC_CHECK_HEADER([X11/extensions/shmproto.h], , , [#include ]) fi # Checks for typedefs, structures, and compiler characteristics. diff --git a/x11vnc/Makefile.am b/x11vnc/Makefile.am index cef82f3..bc112e8 100644 --- a/x11vnc/Makefile.am +++ b/x11vnc/Makefile.am @@ -31,13 +31,13 @@ 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 +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 xi2_devices.h if HAVE_SYSTEM_LIBVNCSERVER INCLUDES_LIBVNCSERVER = @SYSTEM_LIBVNCSERVER_CFLAGS@ else INCLUDES_LIBVNCSERVER = endif -INCLUDES = $(INCLUDES_LIBVNCSERVER) @X_CFLAGS@ @AVAHI_CFLAGS@ +INCLUDES = $(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/x11vnc/cleanup.c b/x11vnc/cleanup.c index 60db819..9120a10 100644 --- a/x11vnc/cleanup.c +++ b/x11vnc/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; @@ -549,7 +563,7 @@ static void interrupted (int sig) { } else { fprintf(stderr, "caught signal: %d\n", sig); } - if (sig == SIGINT) { + if (sig == SIGINT || sig == SIGTERM) { shut_down = 1; return; } diff --git a/x11vnc/connections.c b/x11vnc/connections.c index d2a9b5c..a5f7972 100644 --- a/x11vnc/connections.c +++ b/x11vnc/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; @@ -4015,6 +4040,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) { diff --git a/x11vnc/cursor.c b/x11vnc/cursor.c index 3d613a6..639aaaa 100644 --- a/x11vnc/cursor.c +++ b/x11vnc/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,14 +63,18 @@ 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); +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; @@ -91,8 +96,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 +1006,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; @@ -1828,7 +1831,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; @@ -1853,6 +1856,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; } @@ -1899,6 +1907,19 @@ 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 */ + if(use_multipointer) { + ClientData *cd = (ClientData *) client->clientData; + if (debug_pointer) + rfbLog("cursor_position: set client pos x=%3d y=%d\n", x, y); + INPUT_LOCK; + cd->cursor_x = x; + cd->cursor_y = y; + INPUT_UNLOCK; + } + } } static void set_rfb_cursor(int which) { @@ -1973,12 +1994,63 @@ int check_x11_pointer(void) { #endif +#ifdef LIBVNCSERVER_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)) ) { + if (dpy) { + X_LOCK; + ret = XIQueryPointer(dpy, ((ClientData*)cl->clientData)->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 */ + cursor_position(x, y, cl); + } + 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 @@ -2016,10 +2088,255 @@ 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. +*/ +void save_under_cursor_buffer(rfbClientPtr cl) +{ + ClientData *cd = (ClientData *) cl->clientData; + rfbCursorPtr c = cd->cursor; + int j,x1,x2,y1,y2,bpp=screen->serverFormat.bitsPerPixel/8, + rowstride=screen->paddedWidthInBytes, + bufsize; + rfbBool wasChanged=FALSE; + + 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); +} + +void draw_cursor(rfbClientPtr cl) +{ + ClientData *cd = (ClientData *) cl->clientData; + rfbCursorPtr c = cd->cursor; + int i,j,x1,x2,y1,y2,i1,j1,bpp=screen->serverFormat.bitsPerPixel/8, + rowstride=screen->paddedWidthInBytes, w; + + 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 */ +void restore_under_cursor_buffer(rfbClientPtr cl) +{ + ClientData *cd = (ClientData *) cl->clientData; + rfbCursorPtr c=cd->cursor; + int j,x1,x2,y1,y2,bpp=screen->serverFormat.bitsPerPixel/8, + rowstride=screen->paddedWidthInBytes; + + if(!c) + return; + + /* 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->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/x11vnc/cursor.h b/x11vnc/cursor.h index 9c74944..0ce1e4d 100644 --- a/x11vnc/cursor.h +++ b/x11vnc/cursor.h @@ -59,12 +59,17 @@ 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 void save_under_cursor_buffer(rfbClientPtr cl); +extern void draw_cursor(rfbClientPtr cl); +extern void restore_under_cursor_buffer(rfbClientPtr cl); #endif /* _X11VNC_CURSOR_H */ diff --git a/x11vnc/help.c b/x11vnc/help.c index 5cd5c12..364e1e8 100644 --- a/x11vnc/help.c +++ b/x11vnc/help.c @@ -4253,6 +4253,11 @@ void print_help(int mode) { " there are problems or decrease it to live on the edge\n" " (perhaps useful on a slow machine).\n" "\n" +#ifdef LIBVNCSERVER_HAVE_XI2 +"-multiptr Enable support for multiple pointers, one per client.\n" +" Uses XInput2 aka MPX. Default is off.\n" +"\n" +#endif "-sigpipe string Broken pipe (SIGPIPE) handling. \"string\" can be\n" " \"ignore\" or \"exit\". For \"ignore\" LibVNCServer\n" " will handle the abrupt loss of a client and continue,\n" diff --git a/x11vnc/keyboard.c b/x11vnc/keyboard.c index 54ff70e..be3bc6e 100644 --- a/x11vnc/keyboard.c +++ b/x11vnc/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, 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, keycode, False, CurrentTime); + XTestFakeKeyEvent_wr(dpy, -1, keycode, False, CurrentTime);/* multipointer FIXME? */ } } XFlush_wr(dpy); @@ -331,9 +333,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); /* multipointer FIXME? */ usleep(10*1000); - XTestFakeKeyEvent_wr(dpy, key, False, CurrentTime); + 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, 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, 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, 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, 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, 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, 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, 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, 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, left_shift_code, !dn, CurrentTime); + XTestFakeKeyEvent_wr(dpy, dev_id, left_shift_code, !dn, CurrentTime); } if (mod_state & RIGHTSHIFT) { - XTestFakeKeyEvent_wr(dpy, right_shift_code, !dn, CurrentTime); + XTestFakeKeyEvent_wr(dpy, dev_id, right_shift_code, !dn, CurrentTime); } } if ( ! is_shift && mod == 1 ) { - XTestFakeKeyEvent_wr(dpy, 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, altgr, !dn, CurrentTime); + XTestFakeKeyEvent_wr(dpy, dev_id, altgr, !dn, CurrentTime); } if ( altgr && ! (mod_state & ALTGR) && mod == 2 ) { - XTestFakeKeyEvent_wr(dpy, 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, 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, k, (Bool) down, CurrentTime); + + XTestFakeKeyEvent_wr(dpy, cd->kbd_id, k, (Bool) down, CurrentTime); XFlush_wr(dpy); } diff --git a/x11vnc/misc/Makefile.am b/x11vnc/misc/Makefile.am index e6a50aa..d178438 100644 --- a/x11vnc/misc/Makefile.am +++ b/x11vnc/misc/Makefile.am @@ -1,3 +1,13 @@ SUBDIRS = turbovnc DIST_SUBDIRS = turbovnc + EXTRA_DIST=README blockdpy.c dtVncPopup rx11vnc rx11vnc.pl shm_clear ranfb.pl slide.pl vcinject.pl x11vnc_loop Xdummy ultravnc_repeater.pl connect_switch panner.pl desktop.cgi inet6to4 uinput.pl qt_tslib_inject.pl + +if HAVE_XI2 +if HAVE_CAIRO +noinst_PROGRAMS=xi2setcursor +xi2setcursor_SOURCES=xi2setcursor.c +xi2setcursor_CFLAGS= @XI2_CFLAGS@ @CAIRO_CFLAGS@ +xi2setcursor_LDADD= @X_LIBS@ @XI2_LIBS@ @CAIRO_LIBS@ +endif +endif diff --git a/x11vnc/misc/README b/x11vnc/misc/README index 2ed1dff..e135a6c 100644 --- a/x11vnc/misc/README +++ b/x11vnc/misc/README @@ -12,19 +12,19 @@ documentation near the top of the file. x11vnc -accept scripts: - blockdpy.c try to lock screen if local person knocks monitor out of DPMS - dtVncPopup CDE/dtksh by Stefan Radman to accept connections, lock screen + blockdpy.c try to lock screen if local person knocks monitor out of DPMS + dtVncPopup CDE/dtksh by Stefan Radman to accept connections, lock screen x11vnc launch wrappers: - rx11vnc simple ssh/rsh x11vnc launcher. -S option needs work... - rx11vnc.pl perl script tries to do rx11vnc -S tunnelling better. + rx11vnc simple ssh/rsh x11vnc launcher. -S option needs work... + rx11vnc.pl perl script tries to do rx11vnc -S tunnelling better. x11vnc -pipeinput/-rawfb utilities: - vcinject.pl perl script like LinuxVNC.c, for x11vnc viewing of linux console - slide.pl amusing example using x11vnc -rawfb for jpeg slideshow. - ranfb.pl example -rawfb setup:./ranfb.pl to set up a framebuffer. + vcinject.pl perl script like LinuxVNC.c, for x11vnc viewing of linux console + slide.pl amusing example using x11vnc -rawfb for jpeg slideshow. + ranfb.pl example -rawfb setup:./ranfb.pl to set up a framebuffer. uinput.pl test perl script for Linux uinput injection. @@ -38,6 +38,10 @@ Misc. scripts: Xdummy An LD_PRELOAD kludge to run the Xorg "dummy" device driver like Xvfb. + xi2setcursor.c: utility to change the cursor image of a given pointer device + into a coloroured and labeled cursor image. Useful with + -afteraccept in multi-pointer setups. + ultravnc_repeater.pl: Unix script to act as UltraVNC repeater proxy. connect_switch: Share HTTPS, VNC, SSH, etc. through a single port (e.g. 443) @@ -48,3 +52,4 @@ Misc. scripts: server. Also can do port-redirection to internal machines. inet6to4 ipv6 to ipv4 relay (i.e. until libvncserver supports ipv6) + diff --git a/x11vnc/misc/xi2setcursor.c b/x11vnc/misc/xi2setcursor.c new file mode 100644 index 0000000..ef785be --- /dev/null +++ b/x11vnc/misc/xi2setcursor.c @@ -0,0 +1,641 @@ +/* + Based on the unclutter utility by Mark M Martin, mmm@cetia.fr sep 1992., + this small (quick&dirty) helper program sets the cursor of a given (or the + last added) pointer device and afterwards tracks the pointer position and + uses unclutters subwindow trick to always show a labeled cursor, even if the + window the pointer is in specifies another one. +*/ + + +#include +#include +#include +#include +#include +#include /* for BadDevice() */ +#include +#include +#include +#include +#include +#include +#include + + + +#define CW_SIZE 10 /* width and height of cursor window */ +#define DFLT_SLEEPTIME 50 /* default milliseconds to wait in between pointer queries */ + + +char *progname; + +char **names = 0; /* -> argv list of names to avoid */ +char **classes = 0; /* -> argv list of classes to avoid */ +regex_t *nc_re = 0; /* regex for list of classes/names to avoid */ + +Display *display; + +int dev = -1; /* device id */ +int numscreens; +Window *realroot; /* array of root windows */ + +/* XI error numbers are dynamically generated, + we have to get the via macros, e.g. BadDevice(display, &my_errorcode) +*/ +int baddevice_err; + + +void pexit(char *str) +{ + fprintf(stderr,"%s: %s\n",progname,str); + exit(EXIT_FAILURE); +} + + + +void usage() +{ + pexit("usage:\n\ + -display \n\ + -dev open device with this id instead of last\n\ + created pointer\n\ + -label