diff --git a/ChangeLog.md b/ChangeLog.md index e8276563c..c25a8e8d2 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -12,6 +12,14 @@ number of simultaneously connected viewers. server. (These events can be generated with horizontal scroll gestures on a trackpad or, with certain mice, by side-clicking the scroll wheel.) +3. By default, the TurboVNC Server now limits the amount of time that it will +wait for a new pointer event from a connected viewer that is dragging the mouse +(and thus has exclusive control over the pointer.) This prevents other viewers +connected to the same session from being locked out of pointer control +indefinitely if a viewer's network connection drops while it is dragging the +mouse. A new Xvnc command-line option (`-pointerlocktimeout`) can be used to +specify the time limit. + 3.1.1 ===== diff --git a/unix/Xvnc/programs/Xserver/Xvnc.man.in b/unix/Xvnc/programs/Xserver/Xvnc.man.in index c1aaad2e2..1e9b60931 100644 --- a/unix/Xvnc/programs/Xserver/Xvnc.man.in +++ b/unix/Xvnc/programs/Xserver/Xvnc.man.in @@ -2,7 +2,7 @@ .\" ** The above line should force tbl to be a preprocessor ** .\" Man page for Xvnc .\" -.\" Copyright (C) 2010, 2012, 2014-2022 D. R. Commander +.\" Copyright (C) 2010, 2012, 2014-2022, 2024 D. R. Commander .\" Copyright (C) 2021 Steffen Kieß .\" Copyright (C) 2010 University Corporation for Atmospheric Research .\" Copyright (C) 2005-2008 Sun Microsystems, Inc. @@ -14,7 +14,7 @@ .\" License as specified in the file LICENCE.TXT that comes with the .\" TurboVNC distribution. .\" -.TH Xvnc 1 "March 2021" "" "TurboVNC" +.TH Xvnc 1 "July 2024" "" "TurboVNC" .SH NAME Xvnc \- the TurboVNC X server .SH SYNOPSIS @@ -213,6 +213,17 @@ version of Xvnc by AT&T labs. \fB\-nocursor\fR Don't display a mouse pointer on the remote desktop. +.TP +\fB\-pointerlocktimeout \fItime\fR +Maximum amount of time, in milliseconds, to wait for a new pointer event from a +connected viewer that is dragging the mouse (moving the mouse with one or more +mouse buttons held down) and thus has exclusive control over the pointer +[default: 3000]. 0 = indefinitely. + +This prevents other viewers connected to the same session from being locked out +of pointer control indefinitely if a viewer's network connection drops while it +is dragging the mouse. + .TP \fB\-viewonly\fR Don't accept keyboard and pointer events from viewers. All viewers will diff --git a/unix/Xvnc/programs/Xserver/hw/vnc/init.c b/unix/Xvnc/programs/Xserver/hw/vnc/init.c index d9f356d12..c317cdd31 100644 --- a/unix/Xvnc/programs/Xserver/hw/vnc/init.c +++ b/unix/Xvnc/programs/Xserver/hw/vnc/init.c @@ -417,6 +417,16 @@ int ddxProcessArgument(int argc, char *argv[], int i) return 1; } + if (strcasecmp(argv[i], "-pointerlocktimeout") == 0) { + REQUIRE_ARG(); + rfbPointerLockTimeout = atoi(argv[i + 1]); + if (rfbPointerLockTimeout < 0) { + UseMsg(); + exit(1); + } + return 2; + } + /* Run server in view-only mode - Ehud Karni SW */ if (strcasecmp(argv[i], "-viewonly") == 0) { rfbViewOnly = TRUE; @@ -1627,6 +1637,7 @@ void ddxGiveUp(enum ExitCode error) rfbPAMEnd(cl); #endif ShutdownTightThreads(); + TimerFree(pointerLockTimer); free(rfbFB.pfbMemory); if (initOutputCalled) { char unixSocketName[32]; @@ -1738,6 +1749,12 @@ void ddxUseMsg(void) ErrorF("======================\n"); ErrorF("-compatiblekbd set META key = ALT key as in the original VNC\n"); ErrorF("-nocursor don't display a cursor\n"); + ErrorF("-pointerlocktimeout time\n"); + ErrorF(" max time in ms (0 = indefinitely) to wait for a new\n"); + ErrorF(" pointer event from a connected viewer that is dragging\n"); + ErrorF(" the mouse (and thus has exclusive control over the\n"); + ErrorF(" pointer) [default: %d]\n", + DEFAULT_POINTER_LOCK_TIMEOUT); ErrorF("-viewonly only let viewers view, not control, the remote desktop\n"); ErrorF("-virtualtablet set up virtual stylus and eraser devices for this\n"); ErrorF(" session, to emulate a Wacom tablet, and map all\n"); diff --git a/unix/Xvnc/programs/Xserver/hw/vnc/rfb.h b/unix/Xvnc/programs/Xserver/hw/vnc/rfb.h index 69cb03ff8..e4a628ec8 100644 --- a/unix/Xvnc/programs/Xserver/hw/vnc/rfb.h +++ b/unix/Xvnc/programs/Xserver/hw/vnc/rfb.h @@ -98,6 +98,8 @@ #define DEFAULT_MAX_CLIENT_WAIT 20000 +#define DEFAULT_POINTER_LOCK_TIMEOUT 3000 + /* Constants for handling the UltraVNC Viewer's multitouch GII valuator event format */ #define UVNCGII_MAX_TOUCHES 10 @@ -986,6 +988,9 @@ extern int rfbNumThreads; extern char *rfbCaptureFile; +extern int rfbPointerLockTimeout; +extern OsTimerPtr pointerLockTimer; + #define debugregion(r, m) \ rfbLog(m" %d, %d %d x %d\n", (r).extents.x1, (r).extents.y1, \ (r).extents.x2 - (r).extents.x1, (r).extents.y2 - (r).extents.y1) diff --git a/unix/Xvnc/programs/Xserver/hw/vnc/rfbserver.c b/unix/Xvnc/programs/Xserver/hw/vnc/rfbserver.c index bec75fcc6..501f1990e 100644 --- a/unix/Xvnc/programs/Xserver/hw/vnc/rfbserver.c +++ b/unix/Xvnc/programs/Xserver/hw/vnc/rfbserver.c @@ -3,9 +3,9 @@ */ /* Copyright (C) 2009-2022, 2024 D. R. Commander. All Rights Reserved. + * Copyright (C) 2021, 2024 AnatoScope SA. All Rights Reserved. * Copyright (C) 2015-2017, 2020-2021 Pierre Ossman for Cendio AB. * All Rights Reserved. - * Copyright (C) 2021 AnatoScope SA. All Rights Reserved. * Copyright (C) 2011 Joel Martin * Copyright (C) 2010 University Corporation for Atmospheric Research. * All Rights Reserved. @@ -54,6 +54,13 @@ rfbClientPtr rfbClientHead = NULL; /* The client that is currently dragging the pointer This serves as a mutex for RFB pointer events. */ rfbClientPtr pointerDragClient = NULL; +/* Pointer lock timeout (in milliseconds) + Maximum amount of time, in milliseconds, to wait for a new pointer event + from a connected viewer that is dragging the mouse (and thus has exclusive + control over the pointer.) This prevents other viewers connected to the + same session from being locked out of pointer control indefinitely if a + viewer's network connection drops while it is dragging the mouse. */ +int rfbPointerLockTimeout = DEFAULT_POINTER_LOCK_TIMEOUT; /* The client that last moved the pointer Other clients will automatically receive cursor updates via the traditional mechanism of drawing the cursor into the framebuffer (AKA "server-side @@ -124,6 +131,23 @@ void rfbWriteCapture(int captureFD, char *buf, int len) } +/* + * Pointer lock timeout + */ + +OsTimerPtr pointerLockTimer; + +static CARD32 PointerLockTimerCallback(OsTimerPtr timer, CARD32 time, + void *arg) +{ + if (pointerDragClient != NULL) + rfbLog("[%u] Timeout expired. Releasing pointer lock\n", + pointerDragClient->id); + pointerDragClient = NULL; + return 0; +} + + /* * Idle timeout */ @@ -570,8 +594,11 @@ void rfbClientConnectionGone(rfbClientPtr cl) deflateEnd(&cl->zsStruct[i]); } - if (pointerDragClient == cl) + if (pointerDragClient == cl) { + RFBLOGID("Releasing pointer lock\n"); pointerDragClient = NULL; + TimerCancel(pointerLockTimer); + } if (pointerOwner == cl) pointerOwner = NULL; @@ -1282,10 +1309,18 @@ static void rfbProcessClientNormalMessage(rfbClientPtr cl) if (pointerDragClient && (pointerDragClient != cl)) return; - if (msg.pe.buttonMask == 0) - pointerDragClient = NULL; - else + if (msg.pe.buttonMask == 0) { + if (pointerDragClient != NULL) { + pointerDragClient = NULL; + TimerCancel(pointerLockTimer); + } + } else { pointerDragClient = cl; + if (rfbPointerLockTimeout) + pointerLockTimer = TimerSet(pointerLockTimer, 0, + rfbPointerLockTimeout, + PointerLockTimerCallback, NULL); + } if (!rfbViewOnly && !cl->viewOnly) { cl->cursorX = (int)Swap16IfLE(msg.pe.x);