mirror of
https://github.com/deskflow/deskflow.git
synced 2026-07-19 21:11:28 +08:00
fix: capture the mouse on macOS while the cursor is on a client
Some checks failed
CodeQL Analysis / codeql (push) Has been cancelled
Continuous Integration / lint-reuse (push) Has been cancelled
SonarCloud Analysis / sonar (push) Has been cancelled
Continuous Integration / ci-passed (push) Has been cancelled
Continuous Integration / test-results (push) Has been cancelled
Continuous Integration / lint-clang (push) Has been cancelled
Continuous Integration / analyze-valgrind (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[arch:amd64 config-args:-G Ninja name:windows-2022-x64 qt-version:6.10.3 runs-on:windows-2022 timeout:30 vcpkg-triplet:x64-windows-release]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[arch:arm64 config-args:-G Ninja name:windows-2022-arm64 qt-version:6.11.1 runs-on:windows-11-arm timeout:30 vcpkg-triplet:arm64-windows]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-DCMAKE_OSX_ARCHITECTURES="arm64" -DCMAKE_OSX_DEPLOYMENT_TARGET=14 name:macos-arm64 qt-version:6.11.1 runs-on:macos-15 timeout:10]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-DCMAKE_OSX_ARCHITECTURES="x86_64" -DCMAKE_OSX_DEPLOYMENT_TARGET=12 -DCMAKE_OSX_SYSROOT=/Applications/Xcode_16.4.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk name:macos-x64 qt-ver… (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_DEV_DOCS=ON container:archlinux:latest like:arch name:archlinux-x86_64 runs-on:ubuntu-latest timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:debian:stable-slim like:debian name:debian-arm64 runs-on:ubuntu-24.04-arm timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:debian:stable-slim like:debian name:debian-x86_64 runs-on:ubuntu-latest timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:debian:testing-slim like:debian name:debian-testing-arm64 runs-on:ubuntu-24.04-arm timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:debian:testing-slim like:debian name:debian-testing-x86_64 runs-on:ubuntu-latest timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:fedora:43 like:fedora name:fedora-43-arm64 runs-on:ubuntu-24.04-arm timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:fedora:43 like:fedora name:fedora-43-x86_64 runs-on:ubuntu-latest timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:fedora:44 like:fedora name:fedora-44-arm64 runs-on:ubuntu-24.04-arm timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:fedora:44 like:fedora name:fedora-44-x86_64 runs-on:ubuntu-latest timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:opensuse/tumbleweed:latest like:suse name:opensuse-arm64 runs-on:ubuntu-24.04-arm timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:opensuse/tumbleweed:latest like:suse name:opensuse-x86_64 runs-on:ubuntu-latest timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:ubuntu:26.04 like:debian name:ubuntu-26.04-arm64 runs-on:ubuntu-24.04-arm timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:ubuntu:26.04 like:debian name:ubuntu-26.04-x86_64 runs-on:ubuntu-latest timeout:20]) (push) Has been cancelled
Continuous Integration / unix-${{ matrix.distro.name }} (map[name:freebsd]) (push) Has been cancelled
Continuous Integration / flatpak-${{matrix.flatpak.arch}} (map[arch:aarch64 runs-on:ubuntu-24.04-arm]) (push) Has been cancelled
Continuous Integration / flatpak-${{matrix.flatpak.arch}} (map[arch:x86_64 runs-on:ubuntu-latest]) (push) Has been cancelled
Continuous Integration / release (push) Has been cancelled
Some checks failed
CodeQL Analysis / codeql (push) Has been cancelled
Continuous Integration / lint-reuse (push) Has been cancelled
SonarCloud Analysis / sonar (push) Has been cancelled
Continuous Integration / ci-passed (push) Has been cancelled
Continuous Integration / test-results (push) Has been cancelled
Continuous Integration / lint-clang (push) Has been cancelled
Continuous Integration / analyze-valgrind (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[arch:amd64 config-args:-G Ninja name:windows-2022-x64 qt-version:6.10.3 runs-on:windows-2022 timeout:30 vcpkg-triplet:x64-windows-release]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[arch:arm64 config-args:-G Ninja name:windows-2022-arm64 qt-version:6.11.1 runs-on:windows-11-arm timeout:30 vcpkg-triplet:arm64-windows]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-DCMAKE_OSX_ARCHITECTURES="arm64" -DCMAKE_OSX_DEPLOYMENT_TARGET=14 name:macos-arm64 qt-version:6.11.1 runs-on:macos-15 timeout:10]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-DCMAKE_OSX_ARCHITECTURES="x86_64" -DCMAKE_OSX_DEPLOYMENT_TARGET=12 -DCMAKE_OSX_SYSROOT=/Applications/Xcode_16.4.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk name:macos-x64 qt-ver… (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_DEV_DOCS=ON container:archlinux:latest like:arch name:archlinux-x86_64 runs-on:ubuntu-latest timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:debian:stable-slim like:debian name:debian-arm64 runs-on:ubuntu-24.04-arm timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:debian:stable-slim like:debian name:debian-x86_64 runs-on:ubuntu-latest timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:debian:testing-slim like:debian name:debian-testing-arm64 runs-on:ubuntu-24.04-arm timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:debian:testing-slim like:debian name:debian-testing-x86_64 runs-on:ubuntu-latest timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:fedora:43 like:fedora name:fedora-43-arm64 runs-on:ubuntu-24.04-arm timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:fedora:43 like:fedora name:fedora-43-x86_64 runs-on:ubuntu-latest timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:fedora:44 like:fedora name:fedora-44-arm64 runs-on:ubuntu-24.04-arm timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:fedora:44 like:fedora name:fedora-44-x86_64 runs-on:ubuntu-latest timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:opensuse/tumbleweed:latest like:suse name:opensuse-arm64 runs-on:ubuntu-24.04-arm timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:opensuse/tumbleweed:latest like:suse name:opensuse-x86_64 runs-on:ubuntu-latest timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:ubuntu:26.04 like:debian name:ubuntu-26.04-arm64 runs-on:ubuntu-24.04-arm timeout:20]) (push) Has been cancelled
Continuous Integration / ${{ matrix.target.name }} (map[config-args:-G Ninja -DCMAKE_INSTALL_PREFIX=/usr container:ubuntu:26.04 like:debian name:ubuntu-26.04-x86_64 runs-on:ubuntu-latest timeout:20]) (push) Has been cancelled
Continuous Integration / unix-${{ matrix.distro.name }} (map[name:freebsd]) (push) Has been cancelled
Continuous Integration / flatpak-${{matrix.flatpak.arch}} (map[arch:aarch64 runs-on:ubuntu-24.04-arm]) (push) Has been cancelled
Continuous Integration / flatpak-${{matrix.flatpak.arch}} (map[arch:x86_64 runs-on:ubuntu-latest]) (push) Has been cancelled
Continuous Integration / release (push) Has been cancelled
On the server, while the cursor is on a client, every physical mouse move warped the local cursor back to screen center and the tap callback returned the mouse-move event instead of consuming it. That leaked continuous motion to whatever local app sat under the centered cursor (e.g. a fullscreen game or a centered video reacting to the phantom movement). Capture the mouse the way macOS games do instead: dissociate the hardware mouse from the cursor in leave() so the cursor freezes and hides, read raw deltas from the event in onMouseMove(), consume off-screen moves in the tap callback, and re-associate in enter() (disable() already does via showCursor()). This retires the center-warp and the "bogus motion" edge heuristic that only existed to paper over the warp. fixes: #5871
This commit is contained in:
parent
89a6c7f2ed
commit
96f544de6d
@ -112,7 +112,7 @@ private:
|
||||
void sendClipboardEvent(EventTypes type, ClipboardID id) const;
|
||||
|
||||
// message handlers
|
||||
bool onMouseMove();
|
||||
bool onMouseMove(CGEventRef event);
|
||||
// mouse button handler. pressed is true if this is a mousedown
|
||||
// event, false if it is a mouseup event. macButton is the index
|
||||
// of the button pressed using the mac button mapping.
|
||||
|
||||
@ -769,6 +769,8 @@ void OSXScreen::enter()
|
||||
showCursor();
|
||||
|
||||
if (m_isPrimary) {
|
||||
// re-couple the mouse to the cursor, undoing the capture from leave()
|
||||
CGAssociateMouseAndMouseCursorPosition(true);
|
||||
setZeroSuppressionInterval();
|
||||
} else {
|
||||
// reset buttons
|
||||
@ -798,8 +800,11 @@ void OSXScreen::leave()
|
||||
|
||||
if (m_isPrimary) {
|
||||
avoidHesitatingCursor();
|
||||
LOG_VERBOSE("centering cursor on leave: %+d, %+d", m_xCenter, m_yCenter);
|
||||
warpCursor(m_xCenter, m_yCenter);
|
||||
|
||||
// capture the mouse: freeze the cursor so no local app sees motion while on
|
||||
// a client (onMouseMove reads raw deltas instead). must follow hideCursor(),
|
||||
// which re-associates. re-coupled in enter()/disable().
|
||||
CGAssociateMouseAndMouseCursorPosition(false);
|
||||
}
|
||||
|
||||
// now off screen
|
||||
@ -958,64 +963,40 @@ void OSXScreen::handleSystemEvent(const Event &event)
|
||||
}
|
||||
}
|
||||
|
||||
bool OSXScreen::onMouseMove()
|
||||
bool OSXScreen::onMouseMove(CGEventRef event)
|
||||
{
|
||||
// when we receive a mouse-move event, it is possible it was queued for a period
|
||||
// and that the mouse has already moved again since then. to handle this, we need
|
||||
// to query the current mouse position rather than using the position in the event.
|
||||
CGEventRef event = CGEventCreate(NULL);
|
||||
CGPoint pos = CGEventGetLocation(event);
|
||||
CFRelease(event);
|
||||
CGFloat mx = pos.x;
|
||||
CGFloat my = pos.y;
|
||||
|
||||
LOG_VERBOSE("mouse move %+f,%+f", mx, my);
|
||||
|
||||
CGFloat x = mx - m_xCursor;
|
||||
CGFloat y = my - m_yCursor;
|
||||
|
||||
if ((x == 0 && y == 0) || (mx == m_xCenter && mx == m_yCenter)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// save position to compute delta of next motion
|
||||
m_xCursor = (int32_t)mx;
|
||||
m_yCursor = (int32_t)my;
|
||||
|
||||
if (m_isOnScreen) {
|
||||
// motion on primary screen
|
||||
// motion on primary screen. the event may have been queued a while, so
|
||||
// query the live cursor position rather than the stale event position.
|
||||
CGEventRef posEvent = CGEventCreate(NULL);
|
||||
CGPoint pos = CGEventGetLocation(posEvent);
|
||||
CFRelease(posEvent);
|
||||
CGFloat mx = pos.x;
|
||||
CGFloat my = pos.y;
|
||||
|
||||
LOG_VERBOSE("mouse move %+f,%+f", mx, my);
|
||||
|
||||
CGFloat x = mx - m_xCursor;
|
||||
CGFloat y = my - m_yCursor;
|
||||
|
||||
if ((x == 0 && y == 0) || (mx == m_xCenter && mx == m_yCenter)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
m_xCursor = (int32_t)mx;
|
||||
m_yCursor = (int32_t)my;
|
||||
|
||||
sendEvent(EventTypes::PrimaryScreenMotionOnPrimary, MotionInfo::alloc(m_xCursor, m_yCursor));
|
||||
} else {
|
||||
// motion on secondary screen. warp mouse back to
|
||||
// center.
|
||||
warpCursor(m_xCenter, m_yCenter);
|
||||
// motion on secondary screen. the cursor is frozen (see leave()), so read
|
||||
// raw deltas from the event instead of diffing position.
|
||||
int32_t dx = (int32_t)CGEventGetIntegerValueField(event, kCGMouseEventDeltaX);
|
||||
int32_t dy = (int32_t)CGEventGetIntegerValueField(event, kCGMouseEventDeltaY);
|
||||
|
||||
// examine the motion. if it's about the distance
|
||||
// from the center of the screen to an edge then
|
||||
// it's probably a bogus motion that we want to
|
||||
// ignore (see warpCursorNoFlush() for a further
|
||||
// description).
|
||||
static int32_t bogusZoneSize = 10;
|
||||
if (-x + bogusZoneSize > m_xCenter - m_x || x + bogusZoneSize > m_x + m_w - m_xCenter ||
|
||||
-y + bogusZoneSize > m_yCenter - m_y || y + bogusZoneSize > m_y + m_h - m_yCenter) {
|
||||
LOG_DEBUG("dropped bogus motion %+d,%+d", x, y);
|
||||
} else {
|
||||
// send motion
|
||||
// Accumulate together the move into the running total
|
||||
static CGFloat m_xFractionalMove = 0;
|
||||
static CGFloat m_yFractionalMove = 0;
|
||||
LOG_VERBOSE("mouse delta %+d,%+d", dx, dy);
|
||||
|
||||
m_xFractionalMove += x;
|
||||
m_yFractionalMove += y;
|
||||
|
||||
// Return the integer part
|
||||
int32_t intX = (int32_t)m_xFractionalMove;
|
||||
int32_t intY = (int32_t)m_yFractionalMove;
|
||||
|
||||
// And keep only the fractional part
|
||||
m_xFractionalMove -= intX;
|
||||
m_yFractionalMove -= intY;
|
||||
sendEvent(EventTypes::PrimaryScreenMotionOnSecondary, MotionInfo::alloc(intX, intY));
|
||||
if (dx != 0 || dy != 0) {
|
||||
sendEvent(EventTypes::PrimaryScreenMotionOnSecondary, MotionInfo::alloc(dx, dy));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1690,15 +1671,9 @@ CGEventRef OSXScreen::handleCGInputEvent(CGEventTapProxy proxy, CGEventType type
|
||||
case kCGEventRightMouseDragged:
|
||||
case kCGEventOtherMouseDragged:
|
||||
case kCGEventMouseMoved:
|
||||
// we intentionally ignore the position in the event here as the events are
|
||||
// queued and will no longer be accurate when we process them.
|
||||
screen->onMouseMove();
|
||||
|
||||
// The system ignores our cursor-centering calls if
|
||||
// we don't return the event. This should be harmless,
|
||||
// but might register as slight movement to other apps
|
||||
// on the system. It hasn't been a problem before, though.
|
||||
return event;
|
||||
// off-screen the cursor is frozen (see leave()), so fall through to consume
|
||||
// the move below instead of returning (leaking) it to local apps.
|
||||
screen->onMouseMove(event);
|
||||
break;
|
||||
case kCGEventScrollWheel:
|
||||
screen->onMouseWheel(
|
||||
|
||||
Loading…
Reference in New Issue
Block a user