From 4eefef462562b2ab6aa3a2e8e7f8ffa05a0d2ec0 Mon Sep 17 00:00:00 2001 From: Stefan Verleysen Date: Thu, 12 Feb 2026 11:38:57 -0500 Subject: [PATCH 1/2] chore: gitignore local strategy docs and PDFs Co-Authored-By: Claude Opus 4.6 --- .gitignore | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.gitignore b/.gitignore index 1723cb2a9..0d2e7ed23 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,10 @@ deskflow-config.toml /scripts/*.egg-info /*.user *.ui.autosave + +# local docs +*.pdf +PR147-FEEDBACK.md +NIH-REFACTOR.md +WAYLAND.md +WHEREWELEFTOFF.md From 3f36ca8dfcf1cf51a85450a5b581677aa953cac0 Mon Sep 17 00:00:00 2001 From: Stefan Verleysen Date: Fri, 20 Feb 2026 17:45:32 -0500 Subject: [PATCH 2/2] fix: scroll wheel lag and missing horizontal scroll on Windows Scroll events blocked on a synchronous round-trip to the desktop thread per tick. Smooth-scroll mice (100+ events/sec) caused cascading lag that froze the client. Additionally, horizontal scroll (WM_MOUSEHWHEEL) was never captured. - Add scroll event compression in ServerProxy (accumulate deltas when stream has buffered data, same pattern as mouse move compression) - Make fakeMouseWheel non-blocking (fire-and-forget to desktop thread) - Capture WM_MOUSEHWHEEL in LL hook, inject via MOUSEEVENTF_HWHEEL Co-Authored-By: Claude Opus 4.6 --- src/lib/client/ServerProxy.cpp | 29 ++++++++++++++++++++++++---- src/lib/client/ServerProxy.h | 2 ++ src/lib/platform/MSWindowsDesks.cpp | 8 ++++++-- src/lib/platform/MSWindowsHook.cpp | 7 ++++++- src/lib/platform/MSWindowsScreen.cpp | 3 +-- 5 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/lib/client/ServerProxy.cpp b/src/lib/client/ServerProxy.cpp index e0b4cb853..db03bab85 100644 --- a/src/lib/client/ServerProxy.cpp +++ b/src/lib/client/ServerProxy.cpp @@ -47,10 +47,13 @@ ServerProxy::ServerProxy(Client *client, deskflow::IStream *stream, IEventQueue m_seqNum(0), m_compressMouse(false), m_compressMouseRelative(false), + m_compressWheel(false), m_xMouse(0), m_yMouse(0), m_dxMouse(0), m_dyMouse(0), + m_xDeltaWheel(0), + m_yDeltaWheel(0), m_ignoreMouse(false), m_keepAliveAlarm(0.0), m_keepAliveAlarmTimer(NULL), @@ -392,6 +395,12 @@ void ServerProxy::flushCompressedMouse() m_dxMouse = 0; m_dyMouse = 0; } + if (m_compressWheel) { + m_compressWheel = false; + m_client->mouseWheel(m_xDeltaWheel, m_yDeltaWheel); + m_xDeltaWheel = 0; + m_yDeltaWheel = 0; + } } void ServerProxy::sendInfo(const ClientInfo &info) @@ -514,8 +523,11 @@ void ServerProxy::enter() // discard old compressed mouse motion, if any m_compressMouse = false; m_compressMouseRelative = false; + m_compressWheel = false; m_dxMouse = 0; m_dyMouse = 0; + m_xDeltaWheel = 0; + m_yDeltaWheel = 0; m_seqNum = seqNum; m_serverLanguage = ""; m_isUserNotifiedAboutLanguageSyncError = false; @@ -729,15 +741,24 @@ void ServerProxy::mouseRelativeMove() void ServerProxy::mouseWheel() { - // get mouse up to date - flushCompressedMouse(); - // parse SInt16 xDelta, yDelta; ProtocolUtil::readf(m_stream, kMsgDMouseWheel + 4, &xDelta, &yDelta); + + if (!m_compressWheel && m_stream->isReady()) { + m_compressWheel = true; + } + + if (m_compressWheel) { + m_xDeltaWheel += xDelta; + m_yDeltaWheel += yDelta; + return; + } + LOG((CLOG_DEBUG2 "recv mouse wheel %+d,%+d", xDelta, yDelta)); - // forward + flushCompressedMouse(); + m_client->mouseWheel(xDelta, yDelta); } diff --git a/src/lib/client/ServerProxy.h b/src/lib/client/ServerProxy.h index 743c3393f..a35dd21ec 100644 --- a/src/lib/client/ServerProxy.h +++ b/src/lib/client/ServerProxy.h @@ -139,8 +139,10 @@ class ServerProxy bool m_compressMouse; bool m_compressMouseRelative; + bool m_compressWheel; SInt32 m_xMouse, m_yMouse; SInt32 m_dxMouse, m_dyMouse; + SInt32 m_xDeltaWheel, m_yDeltaWheel; bool m_ignoreMouse; diff --git a/src/lib/platform/MSWindowsDesks.cpp b/src/lib/platform/MSWindowsDesks.cpp index c9afc4d31..f6e82b042 100644 --- a/src/lib/platform/MSWindowsDesks.cpp +++ b/src/lib/platform/MSWindowsDesks.cpp @@ -328,7 +328,9 @@ void MSWindowsDesks::fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const void MSWindowsDesks::fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const { - sendMessage(DESKFLOW_MSG_FAKE_WHEEL, xDelta, yDelta); + if (m_activeDesk != NULL && m_activeDesk->m_window != NULL) { + PostThreadMessage(m_activeDesk->m_threadID, DESKFLOW_MSG_FAKE_WHEEL, xDelta, yDelta); + } } void MSWindowsDesks::sendMessage(UINT msg, WPARAM wParam, LPARAM lParam) const @@ -720,7 +722,9 @@ void MSWindowsDesks::deskThread(void *vdesk) break; case DESKFLOW_MSG_FAKE_WHEEL: - // XXX -- add support for x-axis scrolling + if (msg.wParam != 0) { + send_mouse_input(MOUSEEVENTF_HWHEEL, 0, 0, (DWORD)msg.wParam); + } if (msg.lParam != 0) { send_mouse_input(MOUSEEVENTF_WHEEL, 0, 0, (DWORD)msg.lParam); } diff --git a/src/lib/platform/MSWindowsHook.cpp b/src/lib/platform/MSWindowsHook.cpp index b51b1a734..031fd5f49 100644 --- a/src/lib/platform/MSWindowsHook.cpp +++ b/src/lib/platform/MSWindowsHook.cpp @@ -510,7 +510,12 @@ static bool mouseHookHandler(WPARAM wParam, SInt32 x, SInt32 y, SInt32 data) case WM_MOUSEWHEEL: if (g_mode == kHOOK_RELAY_EVENTS) { - // relay event + PostThreadMessage(g_threadID, DESKFLOW_MSG_MOUSE_WHEEL, 0, data); + } + return (g_mode == kHOOK_RELAY_EVENTS); + + case WM_MOUSEHWHEEL: + if (g_mode == kHOOK_RELAY_EVENTS) { PostThreadMessage(g_threadID, DESKFLOW_MSG_MOUSE_WHEEL, data, 0); } return (g_mode == kHOOK_RELAY_EVENTS); diff --git a/src/lib/platform/MSWindowsScreen.cpp b/src/lib/platform/MSWindowsScreen.cpp index f585110cf..9460c2f75 100644 --- a/src/lib/platform/MSWindowsScreen.cpp +++ b/src/lib/platform/MSWindowsScreen.cpp @@ -985,8 +985,7 @@ bool MSWindowsScreen::onPreDispatchPrimary(HWND, UINT message, WPARAM wParam, LP return onMouseMove(static_cast(wParam), static_cast(lParam)); case DESKFLOW_MSG_MOUSE_WHEEL: - // XXX -- support x-axis scrolling - return onMouseWheel(0, static_cast(wParam)); + return onMouseWheel(static_cast(wParam), static_cast(lParam)); case DESKFLOW_MSG_PRE_WARP: { // save position to compute delta of next motion