Fix high DPI breaking edge detection and mouse delta calculation #5030

This commit is contained in:
Jerry (Xinyu Hou) 2015-10-19 11:20:56 -07:00
parent 66335cd6f8
commit a09bfc5f07
4 changed files with 69 additions and 24 deletions

View File

@ -31,6 +31,7 @@
#include "synergy/App.h"
#include "synergy/ArgsBase.h"
#include "synergy/ClientApp.h"
#include "synergy/DpiHelper.h"
#include "mt/Lock.h"
#include "mt/Thread.h"
#include "arch/win32/ArchMiscWindows.h"
@ -143,6 +144,11 @@ MSWindowsScreen::MSWindowsScreen(
this, &MSWindowsScreen::updateKeysCB),
stopOnDeskSwitch);
m_keyState = new MSWindowsKeyState(m_desks, getEventTarget(), m_events);
DpiHelper::calculateDpi(
GetSystemMetrics(SM_CXVIRTUALSCREEN),
GetSystemMetrics(SM_CYVIRTUALSCREEN));
updateScreenShape();
m_class = createWindowClass();
m_window = createWindow(m_class, "Synergy");
@ -1347,6 +1353,14 @@ MSWindowsScreen::onMouseButton(WPARAM wParam, LPARAM lParam)
bool
MSWindowsScreen::onMouseMove(SInt32 mx, SInt32 my)
{
SInt32 originalMX = mx;
SInt32 originalMY = my;
if (DpiHelper::s_dpiScaled) {
mx = (SInt32)(mx / DpiHelper::getDpi());
my = (SInt32)(my / DpiHelper::getDpi());
}
// compute motion delta (relative to the last known
// mouse position)
SInt32 x = mx - m_xCursor;
@ -1370,7 +1384,7 @@ MSWindowsScreen::onMouseMove(SInt32 mx, SInt32 my)
// motion on primary screen
sendEvent(
m_events->forIPrimaryScreen().motionOnPrimary(),
MotionInfo::alloc(m_xCursor, m_yCursor));
MotionInfo::alloc(originalMX, originalMY));
if (m_buttons[kButtonLeft] == true && m_draggingStarted == false) {
m_draggingStarted = true;
@ -1527,20 +1541,25 @@ MSWindowsScreen::warpCursorNoFlush(SInt32 x, SInt32 y)
POINT cursorPos;
GetCursorPos(&cursorPos);
if ((cursorPos.x != x) && (cursorPos.y != y)) {
LOG((CLOG_DEBUG "SetCursorPos did not work; using fakeMouseMove instead"));
// when at Vista/7 login screen, SetCursorPos does not work (which could be
// an MS security feature). instead we can use fakeMouseMove, which calls
// mouse_event.
// IMPORTANT: as of implementing this function, it has an annoying side
// effect; instead of the mouse returning to the correct exit point, it
// returns to the center of the screen. this could have something to do with
// the center screen warping technique used (see comments for onMouseMove
// definition).
fakeMouseMove(x, y);
// there is a bug or round error in SetCursorPos and GetCursorPos on
// a high DPI setting. The check here is for Vista/7 login screen.
// since this feature is mainly for client, so only check on client.
if (!isPrimary()) {
if ((cursorPos.x != x) && (cursorPos.y != y)) {
LOG((CLOG_DEBUG "SetCursorPos did not work; using fakeMouseMove instead"));
LOG((CLOG_DEBUG "cursor pos %d, %d expected pos %d, %d", cursorPos.x, cursorPos.y, x, y));
// when at Vista/7 login screen, SetCursorPos does not work (which could be
// an MS security feature). instead we can use fakeMouseMove, which calls
// mouse_event.
// IMPORTANT: as of implementing this function, it has an annoying side
// effect; instead of the mouse returning to the correct exit point, it
// returns to the center of the screen. this could have something to do with
// the center screen warping technique used (see comments for onMouseMove
// definition).
fakeMouseMove(x, y);
}
}
// yield the CPU. there's a race condition when warping:
// a hardware mouse event occurs
// the mouse hook is not called because that process doesn't have the CPU
@ -1582,16 +1601,25 @@ MSWindowsScreen::ignore() const
void
MSWindowsScreen::updateScreenShape()
{
// get shape
// get shape and center
if (DpiHelper::s_dpiScaled) {
m_w = (SInt32)DpiHelper::s_resolutionWidth;
m_h = (SInt32)DpiHelper::s_resolutionHeight;
m_xCenter = (SInt32)DpiHelper::s_primaryWidthCenter;
m_yCenter = (SInt32)DpiHelper::s_primaryHeightCenter;
}
else {
m_w = GetSystemMetrics(SM_CXVIRTUALSCREEN);
m_h = GetSystemMetrics(SM_CYVIRTUALSCREEN);
m_xCenter = GetSystemMetrics(SM_CXSCREEN) >> 1;
m_yCenter = GetSystemMetrics(SM_CYSCREEN) >> 1;
}
// get position
m_x = GetSystemMetrics(SM_XVIRTUALSCREEN);
m_y = GetSystemMetrics(SM_YVIRTUALSCREEN);
m_w = GetSystemMetrics(SM_CXVIRTUALSCREEN);
m_h = GetSystemMetrics(SM_CYVIRTUALSCREEN);
// get center for cursor
m_xCenter = GetSystemMetrics(SM_CXSCREEN) >> 1;
m_yCenter = GetSystemMetrics(SM_CYSCREEN) >> 1;
// check for multiple monitors
m_multimon = (m_w != GetSystemMetrics(SM_CXSCREEN) ||
m_h != GetSystemMetrics(SM_CYSCREEN));

View File

@ -51,6 +51,12 @@ public:
*/
void getJumpCursorPos(SInt32& x, SInt32& y) const;
//! Get cursor position
/*!
Return if this proxy is for client or primary.
*/
virtual bool isPrimary() const { return false; }
//@}
// IScreen

View File

@ -148,7 +148,7 @@ public:
virtual synergy::IStream*
getStream() const { return NULL; }
bool isPrimary() const { return true; }
private:
synergy::Screen* m_screen;
bool m_clipboardDirty[kClipboardEnd];

View File

@ -33,6 +33,7 @@
#include "synergy/KeyState.h"
#include "synergy/Screen.h"
#include "synergy/PacketStreamFilter.h"
#include "synergy/DpiHelper.h"
#include "net/TCPSocket.h"
#include "net/IDataSocket.h"
#include "net/IListenSocket.h"
@ -2000,8 +2001,18 @@ Server::onMouseMoveSecondary(SInt32 dx, SInt32 dy)
m_sendFileThread = NULL;
}
SInt32 newX = m_x;
SInt32 newY = m_y;
if (DpiHelper::s_dpiScaled) {
// only scale if it's going back to server
if (newScreen->isPrimary()) {
newX = (SInt32)(newX / DpiHelper::getDpi());
newY = (SInt32)(newY / DpiHelper::getDpi());
}
}
// switch screens
switchScreen(newScreen, m_x, m_y, false);
switchScreen(newScreen, newX, newY, false);
}
else {
// same screen. clamp mouse to edge.