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.
This commit is contained in:
parent
bebb63ac53
commit
a16e7217ce
|
@ -342,8 +342,8 @@ CMSWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y)
|
||||||
SInt32 x0, y0, w, h;
|
SInt32 x0, y0, w, h;
|
||||||
getScreenShape(x0, y0, w, h);
|
getScreenShape(x0, y0, w, h);
|
||||||
mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
|
mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
|
||||||
(SInt32)(65535.99 * x / (w - 1)) + x0,
|
(DWORD)((65535.99 * (x - x0)) / (w - 1)),
|
||||||
(SInt32)(65535.99 * y / (h - 1)) + y0,
|
(DWORD)((65535.99 * (y - y0)) / (h - 1)),
|
||||||
0, 0);
|
0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -560,8 +560,8 @@ CMSWindowsSecondaryScreen::onEnter(SInt32 x, SInt32 y)
|
||||||
SInt32 x0, y0, w, h;
|
SInt32 x0, y0, w, h;
|
||||||
getScreenShape(x0, y0, w, h);
|
getScreenShape(x0, y0, w, h);
|
||||||
mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
|
mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
|
||||||
(DWORD)((65535.99 * x) / (w - 1)) + x0,
|
(DWORD)((65535.99 * (x - x0)) / (w - 1)),
|
||||||
(DWORD)((65535.99 * y) / (h - 1)) + y0,
|
(DWORD)((65535.99 * (y - y0)) / (h - 1)),
|
||||||
0, 0);
|
0, 0);
|
||||||
|
|
||||||
// show cursor
|
// show cursor
|
||||||
|
|
|
@ -110,12 +110,16 @@ CMSWindowsPrimaryScreen::open(CServer* server)
|
||||||
m_markReceived = 0;
|
m_markReceived = 0;
|
||||||
nextMark();
|
nextMark();
|
||||||
|
|
||||||
|
// save cursor pos
|
||||||
|
POINT pos;
|
||||||
|
GetCursorPos(&pos);
|
||||||
|
m_x = pos.x;
|
||||||
|
m_y = pos.y;
|
||||||
|
|
||||||
// send screen info
|
// send screen info
|
||||||
SInt32 x, y, w, h;
|
SInt32 x, y, w, h;
|
||||||
getScreenShape(x, y, w, h);
|
getScreenShape(x, y, w, h);
|
||||||
POINT pos;
|
m_server->setInfo(x, y, w, h, getJumpZoneSize(), m_x, m_y);
|
||||||
GetCursorPos(&pos);
|
|
||||||
m_server->setInfo(x, y, w, h, getJumpZoneSize(), pos.x, pos.y);
|
|
||||||
|
|
||||||
// compute center pixel of primary screen
|
// compute center pixel of primary screen
|
||||||
m_xCenter = GetSystemMetrics(SM_CXSCREEN) >> 1;
|
m_xCenter = GetSystemMetrics(SM_CXSCREEN) >> 1;
|
||||||
|
@ -174,7 +178,7 @@ CMSWindowsPrimaryScreen::leave()
|
||||||
updateKeys();
|
updateKeys();
|
||||||
|
|
||||||
// warp mouse to center of screen
|
// warp mouse to center of screen
|
||||||
warpCursor(m_xCenter, m_yCenter);
|
warpCursorToCenter();
|
||||||
|
|
||||||
// ignore this many mouse motion events (not including the already
|
// ignore this many mouse motion events (not including the already
|
||||||
// queued events). on (at least) the win2k login desktop, one
|
// 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
|
// warpCursor(). i don't know why it does that and other desktops
|
||||||
// don't have the same problem. anyway, simply ignoring that event
|
// don't have the same problem. anyway, simply ignoring that event
|
||||||
// works around it.
|
// works around it.
|
||||||
|
// FIXME -- is this true now that we're using mouse_event?
|
||||||
m_mouseMoveIgnore = 1;
|
m_mouseMoveIgnore = 1;
|
||||||
|
|
||||||
// disable ctrl+alt+del, alt+tab, etc
|
// 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
|
// set the cursor position without generating an event
|
||||||
SetCursorPos(x, y);
|
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
|
void
|
||||||
|
@ -451,16 +474,21 @@ CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg)
|
||||||
m_server->onMouseMovePrimary(x, y);
|
m_server->onMouseMovePrimary(x, y);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// get mouse deltas
|
// compute motion delta. this is relative to the
|
||||||
x -= m_xCenter;
|
// last known mouse position.
|
||||||
y -= m_yCenter;
|
x -= m_x;
|
||||||
|
y -= m_y;
|
||||||
|
|
||||||
|
// save position to compute delta of next motion
|
||||||
|
m_x = static_cast<SInt32>(msg->wParam);
|
||||||
|
m_y = static_cast<SInt32>(msg->lParam);
|
||||||
|
|
||||||
// ignore if the mouse didn't move or we're ignoring
|
// ignore if the mouse didn't move or we're ignoring
|
||||||
// motion.
|
// motion.
|
||||||
if (m_mouseMoveIgnore == 0) {
|
if (m_mouseMoveIgnore == 0) {
|
||||||
if (x != 0 && y != 0) {
|
if (x != 0 || y != 0) {
|
||||||
// warp mouse back to center
|
// back to center
|
||||||
warpCursor(m_xCenter, m_yCenter);
|
warpCursorToCenter();
|
||||||
|
|
||||||
// send motion
|
// send motion
|
||||||
m_server->onMouseMoveSecondary(x, y);
|
m_server->onMouseMoveSecondary(x, y);
|
||||||
|
@ -474,6 +502,11 @@ CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg)
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case SYNERGY_MSG_POST_WARP:
|
||||||
|
m_x = static_cast<SInt32>(msg->wParam);
|
||||||
|
m_y = static_cast<SInt32>(msg->lParam);
|
||||||
|
return true;
|
||||||
|
|
||||||
case WM_TIMER:
|
case WM_TIMER:
|
||||||
// if current desktop is not the input desktop then switch to it
|
// if current desktop is not the input desktop then switch to it
|
||||||
if (!m_is95Family) {
|
if (!m_is95Family) {
|
||||||
|
@ -566,7 +599,7 @@ CMSWindowsPrimaryScreen::onEvent(HWND hwnd, UINT msg,
|
||||||
|
|
||||||
// warp mouse to center if active
|
// warp mouse to center if active
|
||||||
if (m_active) {
|
if (m_active) {
|
||||||
warpCursor(m_xCenter, m_yCenter);
|
warpCursorToCenter();
|
||||||
}
|
}
|
||||||
|
|
||||||
// tell hook about resize if not active
|
// tell hook about resize if not active
|
||||||
|
|
|
@ -44,6 +44,10 @@ private:
|
||||||
void onEnter();
|
void onEnter();
|
||||||
bool onLeave();
|
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
|
// discard posted messages
|
||||||
void nextMark();
|
void nextMark();
|
||||||
|
|
||||||
|
@ -91,6 +95,9 @@ private:
|
||||||
// map of key state
|
// map of key state
|
||||||
BYTE m_keys[256];
|
BYTE m_keys[256];
|
||||||
|
|
||||||
|
// last mouse position
|
||||||
|
SInt32 m_x, m_y;
|
||||||
|
|
||||||
// position of center pixel of screen
|
// position of center pixel of screen
|
||||||
SInt32 m_xCenter, m_yCenter;
|
SInt32 m_xCenter, m_yCenter;
|
||||||
|
|
||||||
|
|
|
@ -169,8 +169,6 @@ mouseHook(int code, WPARAM wParam, LPARAM lParam)
|
||||||
{
|
{
|
||||||
const MOUSEHOOKSTRUCT* info =
|
const MOUSEHOOKSTRUCT* info =
|
||||||
(const MOUSEHOOKSTRUCT*)lParam;
|
(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
|
// we want the cursor to be hidden at all times so we
|
||||||
// hide the cursor on whatever window has it. but then
|
// hide the cursor on whatever window has it. but then
|
||||||
|
@ -186,7 +184,16 @@ mouseHook(int code, WPARAM wParam, LPARAM lParam)
|
||||||
}
|
}
|
||||||
|
|
||||||
// relay the motion
|
// 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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,8 @@
|
||||||
#define SYNERGY_MSG_KEY WM_APP + 0x0012 // vk code; key data
|
#define SYNERGY_MSG_KEY WM_APP + 0x0012 // vk code; key data
|
||||||
#define SYNERGY_MSG_MOUSE_BUTTON WM_APP + 0x0013 // button msg; <unused>
|
#define SYNERGY_MSG_MOUSE_BUTTON WM_APP + 0x0013 // button msg; <unused>
|
||||||
#define SYNERGY_MSG_MOUSE_MOVE WM_APP + 0x0014 // x; y
|
#define SYNERGY_MSG_MOUSE_MOVE WM_APP + 0x0014 // x; y
|
||||||
#define SYNERGY_MSG_MOUSE_WHEEL WM_APP + 0x0015 // delta; <unused>
|
#define SYNERGY_MSG_POST_WARP WM_APP + 0x0015 // x; y
|
||||||
|
#define SYNERGY_MSG_MOUSE_WHEEL WM_APP + 0x0016 // delta; <unused>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
|
|
|
@ -151,39 +151,70 @@ CXWindowsPrimaryScreen::run()
|
||||||
case MotionNotify:
|
case MotionNotify:
|
||||||
{
|
{
|
||||||
log((CLOG_DEBUG2 "event: MotionNotify %d,%d", xevent.xmotion.x_root, xevent.xmotion.y_root));
|
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) {
|
if (!m_active) {
|
||||||
x = xevent.xmotion.x_root;
|
|
||||||
y = xevent.xmotion.y_root;
|
|
||||||
m_server->onMouseMovePrimary(x, y);
|
m_server->onMouseMovePrimary(x, y);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// FIXME -- slurp up all remaining motion events?
|
// compute motion delta. this is relative to the
|
||||||
// probably not since keystrokes may go to wrong place.
|
// last known mouse position.
|
||||||
|
x -= m_x;
|
||||||
|
y -= m_y;
|
||||||
|
|
||||||
// XXX -- why call XQueryPointer?
|
// save position to compute delta of next motion
|
||||||
// get mouse deltas
|
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);
|
CDisplayLock display(this);
|
||||||
Window root, window;
|
do {
|
||||||
int xRoot, yRoot, xWindow, yWindow;
|
XMaskEvent(display, PointerMotionMask, &xevent);
|
||||||
unsigned int mask;
|
} while (!xevent.xmotion.send_event);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
break;
|
||||||
|
@ -221,7 +252,6 @@ CXWindowsPrimaryScreen::open(CServer* server)
|
||||||
SInt32 x, y, w, h;
|
SInt32 x, y, w, h;
|
||||||
getScreenShape(x, y, w, h);
|
getScreenShape(x, y, w, h);
|
||||||
|
|
||||||
int mx, my;
|
|
||||||
{
|
{
|
||||||
CDisplayLock display(this);
|
CDisplayLock display(this);
|
||||||
|
|
||||||
|
@ -230,17 +260,25 @@ CXWindowsPrimaryScreen::open(CServer* server)
|
||||||
|
|
||||||
// get mouse position
|
// get mouse position
|
||||||
Window root, window;
|
Window root, window;
|
||||||
int xWindow, yWindow;
|
int mx, my, xWindow, yWindow;
|
||||||
unsigned int mask;
|
unsigned int mask;
|
||||||
if (!XQueryPointer(display, m_window, &root, &window,
|
if (!XQueryPointer(display, m_window, &root, &window,
|
||||||
&mx, &my, &xWindow, &yWindow, &mask)) {
|
&mx, &my, &xWindow, &yWindow, &mask)) {
|
||||||
mx = w >> 1;
|
mx = w >> 1;
|
||||||
my = h >> 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
|
// 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
|
void
|
||||||
|
@ -341,9 +379,7 @@ CXWindowsPrimaryScreen::leave()
|
||||||
log((CLOG_DEBUG1 "grabbed pointer and keyboard"));
|
log((CLOG_DEBUG1 "grabbed pointer and keyboard"));
|
||||||
|
|
||||||
// move the mouse to the center of grab window
|
// move the mouse to the center of grab window
|
||||||
SInt32 x, y, w, h;
|
warpCursorNoLock(display, m_xCenter, m_yCenter);
|
||||||
getScreenShape(x, y, w, h);
|
|
||||||
warpCursorNoLock(display, w >> 1, h >> 1);
|
|
||||||
|
|
||||||
// local client now active
|
// local client now active
|
||||||
m_active = true;
|
m_active = true;
|
||||||
|
@ -381,6 +417,10 @@ CXWindowsPrimaryScreen::warpCursorNoLock(Display* display, SInt32 x, SInt32 y)
|
||||||
PointerMotionMask, &xevent)) {
|
PointerMotionMask, &xevent)) {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// save position as last position
|
||||||
|
m_x = x;
|
||||||
|
m_y = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -71,6 +71,12 @@ private:
|
||||||
unsigned int m_numLockMask;
|
unsigned int m_numLockMask;
|
||||||
unsigned int m_capsLockMask;
|
unsigned int m_capsLockMask;
|
||||||
unsigned int m_scrollLockMask;
|
unsigned int m_scrollLockMask;
|
||||||
|
|
||||||
|
// last mouse position
|
||||||
|
SInt32 m_x, m_y;
|
||||||
|
|
||||||
|
// position of center pixel of screen
|
||||||
|
SInt32 m_xCenter, m_yCenter;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue