From a16e7217ce586bbbe7eff1fc585f77220364d512 Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 19 Jun 2002 20:24:35 +0000 Subject: [PATCH] fixed bugs in mouse motion. wasn't taking care to capture all motion events relative to the previous mouse position. for example, if two mouse events arrive, the first at x+1,y and the second at x+2,y, we used to compute deltas of 1,0 and 2,0 instead of 1,0 and 1,0. that's fixed. also worked around a bug (probably) in windows that caused a motion event after a SetCursorPos() to be lost or reported one pixel off from the correct position. now using mouse_event() which doesn't have that problem. also fixed calculation of normalized coordinates for mouse_event() when there are multiple displays. --- client/CMSWindowsSecondaryScreen.cpp | 8 +-- server/CMSWindowsPrimaryScreen.cpp | 55 ++++++++++++--- server/CMSWindowsPrimaryScreen.h | 7 ++ server/CSynergyHook.cpp | 13 +++- server/CSynergyHook.h | 3 +- server/CXWindowsPrimaryScreen.cpp | 102 +++++++++++++++++++-------- server/CXWindowsPrimaryScreen.h | 6 ++ 7 files changed, 144 insertions(+), 50 deletions(-) diff --git a/client/CMSWindowsSecondaryScreen.cpp b/client/CMSWindowsSecondaryScreen.cpp index dec2e2f9..c6cadd15 100644 --- a/client/CMSWindowsSecondaryScreen.cpp +++ b/client/CMSWindowsSecondaryScreen.cpp @@ -342,8 +342,8 @@ CMSWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y) SInt32 x0, y0, w, h; getScreenShape(x0, y0, w, h); mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, - (SInt32)(65535.99 * x / (w - 1)) + x0, - (SInt32)(65535.99 * y / (h - 1)) + y0, + (DWORD)((65535.99 * (x - x0)) / (w - 1)), + (DWORD)((65535.99 * (y - y0)) / (h - 1)), 0, 0); } @@ -560,8 +560,8 @@ CMSWindowsSecondaryScreen::onEnter(SInt32 x, SInt32 y) SInt32 x0, y0, w, h; getScreenShape(x0, y0, w, h); mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, - (DWORD)((65535.99 * x) / (w - 1)) + x0, - (DWORD)((65535.99 * y) / (h - 1)) + y0, + (DWORD)((65535.99 * (x - x0)) / (w - 1)), + (DWORD)((65535.99 * (y - y0)) / (h - 1)), 0, 0); // show cursor diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index 26915d71..417b5683 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -110,12 +110,16 @@ CMSWindowsPrimaryScreen::open(CServer* server) m_markReceived = 0; nextMark(); + // save cursor pos + POINT pos; + GetCursorPos(&pos); + m_x = pos.x; + m_y = pos.y; + // send screen info SInt32 x, y, w, h; getScreenShape(x, y, w, h); - POINT pos; - GetCursorPos(&pos); - m_server->setInfo(x, y, w, h, getJumpZoneSize(), pos.x, pos.y); + m_server->setInfo(x, y, w, h, getJumpZoneSize(), m_x, m_y); // compute center pixel of primary screen m_xCenter = GetSystemMetrics(SM_CXSCREEN) >> 1; @@ -174,7 +178,7 @@ CMSWindowsPrimaryScreen::leave() updateKeys(); // warp mouse to center of screen - warpCursor(m_xCenter, m_yCenter); + warpCursorToCenter(); // ignore this many mouse motion events (not including the already // queued events). on (at least) the win2k login desktop, one @@ -182,6 +186,7 @@ CMSWindowsPrimaryScreen::leave() // warpCursor(). i don't know why it does that and other desktops // don't have the same problem. anyway, simply ignoring that event // works around it. +// FIXME -- is this true now that we're using mouse_event? m_mouseMoveIgnore = 1; // disable ctrl+alt+del, alt+tab, etc @@ -240,6 +245,24 @@ CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) { // set the cursor position without generating an event SetCursorPos(x, y); + + // save position as last position + m_x = x; + m_y = y; +} + +void +CMSWindowsPrimaryScreen::warpCursorToCenter() +{ + // warp to center. the extra info tells the hook DLL to send + // SYNERGY_MSG_POST_WARP instead of SYNERGY_MSG_MOUSE_MOVE. + SInt32 x, y, w, h; + getScreenShape(x, y, w, h); + mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, + (DWORD)((65535.99 * (m_xCenter - x)) / (w - 1)), + (DWORD)((65535.99 * (m_yCenter - y)) / (h - 1)), + 0, + 0x12345678); } void @@ -451,16 +474,21 @@ CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) m_server->onMouseMovePrimary(x, y); } else { - // get mouse deltas - x -= m_xCenter; - y -= m_yCenter; + // compute motion delta. this is relative to the + // last known mouse position. + x -= m_x; + y -= m_y; + + // save position to compute delta of next motion + m_x = static_cast(msg->wParam); + m_y = static_cast(msg->lParam); // ignore if the mouse didn't move or we're ignoring // motion. if (m_mouseMoveIgnore == 0) { - if (x != 0 && y != 0) { - // warp mouse back to center - warpCursor(m_xCenter, m_yCenter); + if (x != 0 || y != 0) { + // back to center + warpCursorToCenter(); // send motion m_server->onMouseMoveSecondary(x, y); @@ -474,6 +502,11 @@ CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) } return true; + case SYNERGY_MSG_POST_WARP: + m_x = static_cast(msg->wParam); + m_y = static_cast(msg->lParam); + return true; + case WM_TIMER: // if current desktop is not the input desktop then switch to it if (!m_is95Family) { @@ -566,7 +599,7 @@ CMSWindowsPrimaryScreen::onEvent(HWND hwnd, UINT msg, // warp mouse to center if active if (m_active) { - warpCursor(m_xCenter, m_yCenter); + warpCursorToCenter(); } // tell hook about resize if not active diff --git a/server/CMSWindowsPrimaryScreen.h b/server/CMSWindowsPrimaryScreen.h index 22d15ac9..37db16ac 100644 --- a/server/CMSWindowsPrimaryScreen.h +++ b/server/CMSWindowsPrimaryScreen.h @@ -44,6 +44,10 @@ private: void onEnter(); bool onLeave(); + // warp mouse to center of primary display (used when computing + // motion deltas while mouse is on secondary screen). + void warpCursorToCenter(); + // discard posted messages void nextMark(); @@ -91,6 +95,9 @@ private: // map of key state BYTE m_keys[256]; + // last mouse position + SInt32 m_x, m_y; + // position of center pixel of screen SInt32 m_xCenter, m_yCenter; diff --git a/server/CSynergyHook.cpp b/server/CSynergyHook.cpp index 23d0cf62..e3f70c5b 100644 --- a/server/CSynergyHook.cpp +++ b/server/CSynergyHook.cpp @@ -169,8 +169,6 @@ mouseHook(int code, WPARAM wParam, LPARAM lParam) { const MOUSEHOOKSTRUCT* info = (const MOUSEHOOKSTRUCT*)lParam; - SInt32 x = (SInt32)info->pt.x; - SInt32 y = (SInt32)info->pt.y; // we want the cursor to be hidden at all times so we // hide the cursor on whatever window has it. but then @@ -186,7 +184,16 @@ mouseHook(int code, WPARAM wParam, LPARAM lParam) } // relay the motion - PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y); + SInt32 x = (SInt32)info->pt.x; + SInt32 y = (SInt32)info->pt.y; + if (info->dwExtraInfo == 0x12345678) { + PostThreadMessage(g_threadID, + SYNERGY_MSG_POST_WARP, x, y); + } + else { + PostThreadMessage(g_threadID, + SYNERGY_MSG_MOUSE_MOVE, x, y); + } } return 1; } diff --git a/server/CSynergyHook.h b/server/CSynergyHook.h index 34078de8..03f5eb00 100644 --- a/server/CSynergyHook.h +++ b/server/CSynergyHook.h @@ -20,7 +20,8 @@ #define SYNERGY_MSG_KEY WM_APP + 0x0012 // vk code; key data #define SYNERGY_MSG_MOUSE_BUTTON WM_APP + 0x0013 // button msg; #define SYNERGY_MSG_MOUSE_MOVE WM_APP + 0x0014 // x; y -#define SYNERGY_MSG_MOUSE_WHEEL WM_APP + 0x0015 // delta; +#define SYNERGY_MSG_POST_WARP WM_APP + 0x0015 // x; y +#define SYNERGY_MSG_MOUSE_WHEEL WM_APP + 0x0016 // delta; extern "C" { diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index d7f723c4..bbb9eefa 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -151,39 +151,70 @@ CXWindowsPrimaryScreen::run() case MotionNotify: { log((CLOG_DEBUG2 "event: MotionNotify %d,%d", xevent.xmotion.x_root, xevent.xmotion.y_root)); - SInt32 x, y; + SInt32 x = xevent.xmotion.x_root; + SInt32 y = xevent.xmotion.y_root; if (!m_active) { - x = xevent.xmotion.x_root; - y = xevent.xmotion.y_root; m_server->onMouseMovePrimary(x, y); } else { - // FIXME -- slurp up all remaining motion events? - // probably not since keystrokes may go to wrong place. + // compute motion delta. this is relative to the + // last known mouse position. + x -= m_x; + y -= m_y; -// XXX -- why call XQueryPointer? - // get mouse deltas - { + // save position to compute delta of next motion + m_x = xevent.xmotion.x_root; + m_y = xevent.xmotion.y_root; + + // if event was sent then ignore it and discard + // the event from the mouse warp. this is how we + // warp the mouse back to the center of the screen + // without that causing a corresponding motion on + // the secondary screen. + if (xevent.xmotion.send_event) { + // ignore event + x = 0; + y = 0; + + // discard events until we find the matching + // sent event. see below for where the events + // are sent. we discard the matching sent + // event and can be sure we've skipped the + // warp event. CDisplayLock display(this); - Window root, window; - int xRoot, yRoot, xWindow, yWindow; - unsigned int mask; - if (!XQueryPointer(display, m_window, &root, &window, - &xRoot, &yRoot, &xWindow, &yWindow, &mask)) { - break; - } - - // compute position of center of window - SInt32 x0, y0, w, h; - getScreenShape(x0, y0, w, h); - x = xRoot - (w >> 1); - y = yRoot - (h >> 1); - - // warp mouse back to center - warpCursorNoLock(display, w >> 1, h >> 1); + do { + XMaskEvent(display, PointerMotionMask, &xevent); + } while (!xevent.xmotion.send_event); } - m_server->onMouseMoveSecondary(x, y); + // warp mouse back to center + if (x != 0 || y != 0) { + CDisplayLock display(this); + // send an event that we can recognize before + // the mouse warp. + XEvent eventBefore = xevent; + xevent.xmotion.window = m_window; + xevent.xmotion.time = CurrentTime; + xevent.xmotion.x = m_xCenter; + xevent.xmotion.y = m_yCenter; + xevent.xmotion.x_root = m_xCenter; + xevent.xmotion.y_root = m_yCenter; + XEvent eventAfter = eventBefore; + XSendEvent(display, m_window, False, 0, &xevent); + + // warp mouse back to center + XWarpPointer(display, None, getRoot(), + 0, 0, 0, 0, m_xCenter, m_yCenter); + + // send an event that we can recognize after + // the mouse warp. + XSendEvent(display, m_window, False, 0, &xevent); + } + + // send event if mouse moved + if (x != 0 || y != 0) { + m_server->onMouseMoveSecondary(x, y); + } } } break; @@ -221,7 +252,6 @@ CXWindowsPrimaryScreen::open(CServer* server) SInt32 x, y, w, h; getScreenShape(x, y, w, h); - int mx, my; { CDisplayLock display(this); @@ -230,17 +260,25 @@ CXWindowsPrimaryScreen::open(CServer* server) // get mouse position Window root, window; - int xWindow, yWindow; + int mx, my, xWindow, yWindow; unsigned int mask; if (!XQueryPointer(display, m_window, &root, &window, &mx, &my, &xWindow, &yWindow, &mask)) { mx = w >> 1; my = h >> 1; } + + // save mouse position + m_x = x; + m_y = y; } + // save position of center of screen + m_xCenter = x + (w >> 1); + m_yCenter = y + (h >> 1); + // send screen info - m_server->setInfo(x, y, w, h, getJumpZoneSize(), mx, my); + m_server->setInfo(x, y, w, h, getJumpZoneSize(), m_x, m_y); } void @@ -341,9 +379,7 @@ CXWindowsPrimaryScreen::leave() log((CLOG_DEBUG1 "grabbed pointer and keyboard")); // move the mouse to the center of grab window - SInt32 x, y, w, h; - getScreenShape(x, y, w, h); - warpCursorNoLock(display, w >> 1, h >> 1); + warpCursorNoLock(display, m_xCenter, m_yCenter); // local client now active m_active = true; @@ -381,6 +417,10 @@ CXWindowsPrimaryScreen::warpCursorNoLock(Display* display, SInt32 x, SInt32 y) PointerMotionMask, &xevent)) { // do nothing } + + // save position as last position + m_x = x; + m_y = y; } void diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index 13b63c11..19f5feab 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -71,6 +71,12 @@ private: unsigned int m_numLockMask; unsigned int m_capsLockMask; unsigned int m_scrollLockMask; + + // last mouse position + SInt32 m_x, m_y; + + // position of center pixel of screen + SInt32 m_xCenter, m_yCenter; }; #endif