fix(client): restore relative cursor positions locally

Avoid stale server enter coordinates in relative mouse mode by saving and reusing the client's local cursor position around leave and enter.

Store Windows restore positions per desktop so UAC and secure desktop transitions do not reuse normal desktop coordinates. This affects relative mouse movement restore behavior while preserving the existing center-on-leave path.
This commit is contained in:
Momoyama 2026-05-26 22:20:19 +08:00 committed by Nick Bolton
parent 8420d61d34
commit 16141d7156
4 changed files with 89 additions and 4 deletions

View file

@ -1,6 +1,6 @@
/* /*
* Deskflow -- mouse and keyboard sharing utility * Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2025 Deskflow Developers * SPDX-FileCopyrightText: (C) 2025 - 2026 Deskflow Developers
* SPDX-FileCopyrightText: (C) 2012 - 2016, 2026 Synergy App Ltd * SPDX-FileCopyrightText: (C) 2012 - 2016, 2026 Synergy App Ltd
* SPDX-FileCopyrightText: (C) 2002 Chris Schoeneman * SPDX-FileCopyrightText: (C) 2002 Chris Schoeneman
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
@ -154,6 +154,9 @@ void Client::handshakeComplete()
{ {
m_ready = true; m_ready = true;
m_screen->enable(); m_screen->enable();
if (m_relativeMouseMoves && !m_hasRelativeRestorePosition) {
saveRelativeRestorePosition();
}
sendEvent(EventTypes::ClientConnected); sendEvent(EventTypes::ClientConnected);
} }
@ -195,12 +198,20 @@ void Client::getCursorPos(int32_t &x, int32_t &y) const
void Client::enter(int32_t xAbs, int32_t yAbs, uint32_t, KeyModifierMask mask, bool) void Client::enter(int32_t xAbs, int32_t yAbs, uint32_t, KeyModifierMask mask, bool)
{ {
m_active = true; m_active = true;
if (m_relativeMouseMoves && m_hasRelativeRestorePosition) {
xAbs = m_relativeRestoreX;
yAbs = m_relativeRestoreY;
LOG_VERBOSE("using relative restore position: %d,%d", xAbs, yAbs);
}
m_screen->mouseMove(xAbs, yAbs); m_screen->mouseMove(xAbs, yAbs);
m_screen->enter(mask); m_screen->enter(mask);
} }
bool Client::leave() bool Client::leave()
{ {
if (m_relativeMouseMoves) {
saveRelativeRestorePosition();
}
m_active = false; m_active = false;
m_screen->leave(); m_screen->leave();
@ -283,6 +294,8 @@ void Client::screensaver(bool activate)
void Client::resetOptions() void Client::resetOptions()
{ {
m_relativeMouseMoves = false;
m_hasRelativeRestorePosition = false;
m_screen->resetOptions(); m_screen->resetOptions();
} }
@ -303,6 +316,14 @@ void Client::setOptions(const OptionsList &options)
if (index != options.end()) { if (index != options.end()) {
m_maximumClipboardSize = *index; m_maximumClipboardSize = *index;
} }
} else if (id == kOptionRelativeMouseMoves) {
index++;
if (index != options.end()) {
m_relativeMouseMoves = (*index != 0);
if (m_relativeMouseMoves && m_ready && !m_hasRelativeRestorePosition) {
saveRelativeRestorePosition();
}
}
} }
} }
@ -314,6 +335,13 @@ void Client::setOptions(const OptionsList &options)
m_screen->setOptions(options); m_screen->setOptions(options);
} }
void Client::saveRelativeRestorePosition()
{
m_screen->getCursorPos(m_relativeRestoreX, m_relativeRestoreY);
m_hasRelativeRestorePosition = true;
LOG_VERBOSE("saved relative restore position: %d,%d", m_relativeRestoreX, m_relativeRestoreY);
}
std::string Client::getName() const std::string Client::getName() const
{ {
return m_name; return m_name;

View file

@ -1,6 +1,6 @@
/* /*
* Deskflow -- mouse and keyboard sharing utility * Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2025 Deskflow Developers * SPDX-FileCopyrightText: (C) 2025 - 2026 Deskflow Developers
* SPDX-FileCopyrightText: (C) 2012 - 2016, 2026 Synergy App Ltd * SPDX-FileCopyrightText: (C) 2012 - 2016, 2026 Synergy App Ltd
* SPDX-FileCopyrightText: (C) 2002 Chris Schoeneman * SPDX-FileCopyrightText: (C) 2002 Chris Schoeneman
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
@ -155,6 +155,7 @@ public:
std::string getName() const override; std::string getName() const override;
private: private:
void saveRelativeRestorePosition();
void sendClipboard(ClipboardID); void sendClipboard(ClipboardID);
void sendEvent(deskflow::EventTypes); void sendEvent(deskflow::EventTypes);
void sendConnectionFailedEvent(const char *msg); void sendConnectionFailedEvent(const char *msg);
@ -200,6 +201,10 @@ private:
IEventQueue *m_events = nullptr; IEventQueue *m_events = nullptr;
bool m_useSecureNetwork = false; bool m_useSecureNetwork = false;
bool m_enableClipboard = true; bool m_enableClipboard = true;
bool m_relativeMouseMoves = false;
bool m_hasRelativeRestorePosition = false;
int32_t m_relativeRestoreX = 0;
int32_t m_relativeRestoreY = 0;
size_t m_maximumClipboardSize = INT_MAX; size_t m_maximumClipboardSize = INT_MAX;
size_t m_resolvedAddressesCount = 0; size_t m_resolvedAddressesCount = 0;
}; };

View file

@ -1,6 +1,6 @@
/* /*
* Deskflow -- mouse and keyboard sharing utility * Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2025 Deskflow Developers * SPDX-FileCopyrightText: (C) 2025 - 2026 Deskflow Developers
* SPDX-FileCopyrightText: (C) 2012 - 2016 Synergy App Ltd * SPDX-FileCopyrightText: (C) 2012 - 2016 Synergy App Ltd
* SPDX-FileCopyrightText: (C) 2004 Chris Schoeneman * SPDX-FileCopyrightText: (C) 2004 Chris Schoeneman
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
@ -181,6 +181,10 @@ void MSWindowsDesks::leave(HKL keyLayout)
void MSWindowsDesks::resetOptions() void MSWindowsDesks::resetOptions()
{ {
m_leaveForegroundOption = false; m_leaveForegroundOption = false;
m_relativeMouseMoves = false;
for (auto &entry : m_desks) {
entry.second->m_hasRelativeRestorePosition = false;
}
} }
void MSWindowsDesks::setOptions(const OptionsList &options) void MSWindowsDesks::setOptions(const OptionsList &options)
@ -189,6 +193,8 @@ void MSWindowsDesks::setOptions(const OptionsList &options)
if (options[i] == kOptionWin32KeepForeground) { if (options[i] == kOptionWin32KeepForeground) {
m_leaveForegroundOption = (options[i + 1] != 0); m_leaveForegroundOption = (options[i + 1] != 0);
LOG_VERBOSE("%s the foreground window", m_leaveForegroundOption ? "don\'t grab" : "grab"); LOG_VERBOSE("%s the foreground window", m_leaveForegroundOption ? "don\'t grab" : "grab");
} else if (options[i] == kOptionRelativeMouseMoves) {
m_relativeMouseMoves = (options[i + 1] != 0);
} }
} }
} }
@ -309,6 +315,39 @@ void MSWindowsDesks::fakeMouseWheel(int32_t xDelta, int32_t yDelta) const
sendMessage(DESKFLOW_MSG_FAKE_WHEEL, xDelta, yDelta); sendMessage(DESKFLOW_MSG_FAKE_WHEEL, xDelta, yDelta);
} }
void MSWindowsDesks::saveRelativeRestorePosition(Desk *desk) const
{
POINT pos{0, 0};
if (!GetCursorPos(&pos)) {
LOG_DEBUG(
"could not save relative restore position on desk \"%ls\", error: %lu", desk->m_name.c_str(), GetLastError()
);
return;
}
desk->m_relativeRestoreX = pos.x;
desk->m_relativeRestoreY = pos.y;
desk->m_hasRelativeRestorePosition = true;
LOG_VERBOSE(
"saved relative restore position on desk \"%ls\": %+d,%+d", desk->m_name.c_str(), desk->m_relativeRestoreX,
desk->m_relativeRestoreY
);
}
bool MSWindowsDesks::restoreRelativeCursorPosition(Desk *desk) const
{
if (!desk->m_hasRelativeRestorePosition) {
return false;
}
LOG_VERBOSE(
"restoring relative cursor position on desk \"%ls\": %+d,%+d", desk->m_name.c_str(), desk->m_relativeRestoreX,
desk->m_relativeRestoreY
);
deskMouseMove(desk->m_relativeRestoreX, desk->m_relativeRestoreY);
return true;
}
void MSWindowsDesks::sendMessage(UINT msg, WPARAM wParam, LPARAM lParam) const void MSWindowsDesks::sendMessage(UINT msg, WPARAM wParam, LPARAM lParam) const
{ {
if (m_activeDesk != nullptr && m_activeDesk->m_window != nullptr) { if (m_activeDesk != nullptr && m_activeDesk->m_window != nullptr) {
@ -496,6 +535,9 @@ void MSWindowsDesks::deskEnter(Desk *desk)
{ {
if (!m_isPrimary) { if (!m_isPrimary) {
ReleaseCapture(); ReleaseCapture();
if (m_relativeMouseMoves) {
restoreRelativeCursorPosition(desk);
}
} }
setCursorVisibility(true); setCursorVisibility(true);
@ -519,6 +561,10 @@ void MSWindowsDesks::deskEnter(Desk *desk)
void MSWindowsDesks::deskLeave(Desk *desk, HKL keyLayout) void MSWindowsDesks::deskLeave(Desk *desk, HKL keyLayout)
{ {
if (!m_isPrimary && m_relativeMouseMoves) {
saveRelativeRestorePosition(desk);
}
setCursorVisibility(false); setCursorVisibility(false);
if (m_isPrimary) { if (m_isPrimary) {

View file

@ -1,6 +1,6 @@
/* /*
* Deskflow -- mouse and keyboard sharing utility * Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2025 Deskflow Developers * SPDX-FileCopyrightText: (C) 2025 - 2026 Deskflow Developers
* SPDX-FileCopyrightText: (C) 2012 - 2016 Synergy App Ltd * SPDX-FileCopyrightText: (C) 2012 - 2016 Synergy App Ltd
* SPDX-FileCopyrightText: (C) 2004 Chris Schoeneman * SPDX-FileCopyrightText: (C) 2004 Chris Schoeneman
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
@ -187,6 +187,9 @@ private:
HWND m_window; HWND m_window;
HWND m_foregroundWindow; HWND m_foregroundWindow;
bool m_lowLevel; bool m_lowLevel;
bool m_hasRelativeRestorePosition = false;
int32_t m_relativeRestoreX = 0;
int32_t m_relativeRestoreY = 0;
}; };
using Desks = std::map<std::wstring, Desk *>; using Desks = std::map<std::wstring, Desk *>;
@ -201,6 +204,8 @@ private:
// message handlers // message handlers
void deskMouseMove(int32_t x, int32_t y) const; void deskMouseMove(int32_t x, int32_t y) const;
void deskMouseRelativeMove(int32_t dx, int32_t dy) const; void deskMouseRelativeMove(int32_t dx, int32_t dy) const;
void saveRelativeRestorePosition(Desk *desk) const;
bool restoreRelativeCursorPosition(Desk *desk) const;
void deskEnter(Desk *desk); void deskEnter(Desk *desk);
void deskLeave(Desk *desk, HKL keyLayout); void deskLeave(Desk *desk, HKL keyLayout);
void deskThread(const void *vdesk); void deskThread(const void *vdesk);
@ -276,6 +281,7 @@ private:
// options // options
bool m_leaveForegroundOption; bool m_leaveForegroundOption;
bool m_relativeMouseMoves = false;
IEventQueue *m_events; IEventQueue *m_events;
}; };