aboutsummaryrefslogtreecommitdiffstats
path: root/vncviewer/EmulateMB.cxx
diff options
context:
space:
mode:
authorAlex Tanskanen <aleta@cendio.com>2020-04-22 12:15:06 +0200
committerPierre Ossman <ossman@cendio.se>2020-05-26 10:25:42 +0200
commit9197db186b65e1da2d59c60e103f7bf92353ab27 (patch)
treebc6d246a7f709ef65cf7e182bed5d5b4260c6439 /vncviewer/EmulateMB.cxx
parent91559a36462d4653a1713831f8b8eb89aba24dce (diff)
downloadtigervnc-9197db186b65e1da2d59c60e103f7bf92353ab27.tar.gz
tigervnc-9197db186b65e1da2d59c60e103f7bf92353ab27.zip
Fix position for click and drag with EmulateMB
If you have the setting "Emulate middle mouse button" turned on, a click and drag can fail if it is done very quickly. The position of the initial click will be incorrect in such a case because the timeout will delay events.
Diffstat (limited to 'vncviewer/EmulateMB.cxx')
-rw-r--r--vncviewer/EmulateMB.cxx72
1 files changed, 60 insertions, 12 deletions
diff --git a/vncviewer/EmulateMB.cxx b/vncviewer/EmulateMB.cxx
index 22332745..3cd3fea6 100644
--- a/vncviewer/EmulateMB.cxx
+++ b/vncviewer/EmulateMB.cxx
@@ -205,6 +205,7 @@ void EmulateMB::filterPointerEvent(const rfb::Point& pos, int buttonMask)
int action1, action2;
int lastState;
+ // Just pass through events if the emulate setting is disabled
if (!emulateMiddleButton) {
sendPointerEvent(pos, buttonMask);
return;
@@ -225,16 +226,39 @@ void EmulateMB::filterPointerEvent(const rfb::Point& pos, int buttonMask)
throw rfb::Exception(_("Invalid state for 3 button emulation"));
action1 = stateTab[state][btstate][0];
- if (action1 != 0)
- sendAction(pos, buttonMask, action1);
+
+ if (action1 != 0) {
+ // Some presses are delayed, that means we have to check if that's
+ // the case and send the position corresponding to where the event
+ // first was initiated
+ if ((stateTab[state][4][2] >= 0) && action1 > 0)
+ // We have a timeout state and a button press (a delayed press),
+ // always use the original position when leaving a timeout state,
+ // whether the timeout was triggered or not
+ sendAction(origPos, buttonMask, action1);
+ else
+ // Normal non-delayed event
+ sendAction(pos, buttonMask, action1);
+ }
action2 = stateTab[state][btstate][1];
- if (action2 != 0)
- sendAction(pos, buttonMask, action2);
- if ((action1 == 0) && (action2 == 0)) {
- buttonMask &= ~0x5;
- buttonMask |= emulatedButtonMask;
+ // In our case with the state machine, action2 always occurs during a button
+ // release but if this change we need handle action2 accordingly
+ if (action2 != 0) {
+ if ((stateTab[state][4][2] >= 0) && action2 > 0)
+ sendAction(origPos, buttonMask, action2);
+ else
+ // Normal non-delayed event
+ sendAction(pos, buttonMask, action2);
+ }
+
+ // Still send a pointer move event even if there are no actions.
+ // However if the timer is running then we are supressing _all_
+ // events, even movement. The pointer's actual position will be
+ // sent once the timer fires or is abandoned.
+ if ((action1 == 0) && (action2 == 0) && !timer.isStarted()) {
+ buttonMask = createButtonMask(buttonMask);
sendPointerEvent(pos, buttonMask);
}
@@ -244,14 +268,19 @@ void EmulateMB::filterPointerEvent(const rfb::Point& pos, int buttonMask)
if (lastState != state) {
timer.stop();
- if (stateTab[state][4][2] >= 0)
+ if (stateTab[state][4][2] >= 0) {
+ // We need to save the original position so that
+ // drags start from the correct position
+ origPos = pos;
timer.start(50);
+ }
}
}
bool EmulateMB::handleTimeout(rfb::Timer *t)
{
int action1, action2;
+ int buttonMask;
if (&timer != t)
return false;
@@ -259,15 +288,26 @@ bool EmulateMB::handleTimeout(rfb::Timer *t)
if ((state > 10) || (state < 0))
throw rfb::Exception(_("Invalid state for 3 button emulation"));
+ // Timeout shouldn't trigger when there's no timeout action
assert(stateTab[state][4][2] >= 0);
action1 = stateTab[state][4][0];
if (action1 != 0)
- sendAction(lastPos, lastButtonMask, action1);
+ sendAction(origPos, lastButtonMask, action1);
action2 = stateTab[state][4][1];
if (action2 != 0)
- sendAction(lastPos, lastButtonMask, action2);
+ sendAction(origPos, lastButtonMask, action2);
+
+ buttonMask = lastButtonMask;
+
+ // Pointer move events are not sent when waiting for the timeout.
+ // However, we can't let the position get out of sync so when
+ // the pointer has moved we have to send the latest position here.
+ if (!origPos.equals(lastPos)) {
+ buttonMask = createButtonMask(buttonMask);
+ sendPointerEvent(lastPos, buttonMask);
+ }
state = stateTab[state][4][2];
@@ -283,7 +323,15 @@ void EmulateMB::sendAction(const rfb::Point& pos, int buttonMask, int action)
else
emulatedButtonMask |= (1 << (action - 1));
- buttonMask &= ~0x5;
- buttonMask |= emulatedButtonMask;
+ buttonMask = createButtonMask(buttonMask);
sendPointerEvent(pos, buttonMask);
}
+
+int EmulateMB::createButtonMask(int buttonMask)
+{
+ // Unset left and right buttons in the mask
+ buttonMask &= ~0x5;
+
+ // Set the left and right buttons according to the action
+ return buttonMask |= emulatedButtonMask;
+} \ No newline at end of file