diff options
author | Alex Tanskanen <aleta@cendio.com> | 2020-04-22 12:15:06 +0200 |
---|---|---|
committer | Pierre Ossman <ossman@cendio.se> | 2020-05-26 10:25:42 +0200 |
commit | 9197db186b65e1da2d59c60e103f7bf92353ab27 (patch) | |
tree | bc6d246a7f709ef65cf7e182bed5d5b4260c6439 /vncviewer/EmulateMB.cxx | |
parent | 91559a36462d4653a1713831f8b8eb89aba24dce (diff) | |
download | tigervnc-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.cxx | 72 |
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 |