Skip to content

🐞 Avoid cursor stutter when suppressing Mission Control - #1133

Closed
jvanderen1 wants to merge 8 commits into
mrkai77:developfrom
jvanderen1:fix/609-suppress-mission-control-stutter
Closed

jvanderen1 wants to merge 8 commits into
mrkai77:developfrom
jvanderen1:fix/609-suppress-mission-control-stutter

Conversation

@jvanderen1

@jvanderen1 jvanderen1 commented Aug 9, 2026 •

Copy link
Copy Markdown
Contributor

Description

With Suppress Mission Control enabled, dragging a window to the top of the screen stutters. #609 reported this and was closed by turning the toggle off. The stutter is still in current develop when the toggle stays on. Tracked again in #1159.

Why: Loop keeps Mission Control closed by warping the cursor 1px down on every drag event (CGWarpMouseCursorPosition). That fights the upward drag, so the cursor thrashes.

Fix: Rewrite the drag event’s location instead of warping the cursor. Snap detection stays on a listen-only tap. The active tap is created only after a window has been resolved and has moved, and it is removed on mouse-up. Drags that are not a window never hit it. A held top-edge drag is rewritten only once the previous event was already on that edge, so a single crossing between stacked displays is left alone.

Fixes #1159

How has this been tested?

Local Debug build on macOS 27.0 (also checked earlier on macOS Tahoe):

  • Suppress on + drag to top and hold — cursor stays smooth, Mission Control does not open
  • Top snap preview still appears and applies on mouse up
  • Suppress off — Mission Control can still open from a top-edge window drag
  • Unit tests for which display edge is rewritten, including a stacked-display seam and a shared corner

The checked drag tests are from the earlier build. This update changes when the tap exists: it starts only after the window moves, and it is gone on mouse-up.

Screencast

output.mp4

Checklist:

  • I have performed a self-review of my own code
  • I have made corresponding changes to the documentation if applicable
  • I have no unrelated changes in this PR.

Please describe to which degree, if any, an LLM was used in creating this pull request.

Cursor (Grok) helped investigate the stutter and draft the change in WindowDragManager, including the later event-tap hardening. I reviewed the code, confirmed it builds, and manually tested top-edge dragging before opening this PR.

jvanderen1 and others added 3 commits August 9, 2026 00:10
Rewrite top-edge drag events in an active tap instead of warping the cursor each frame, which caused the stutter reported in mrkai77#609.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@jvanderen1

Copy link
Copy Markdown
Contributor Author

@mrkai77 I was wondering if you had q chance to see this PR? I have noticed the "Supress Misson Control" option in Loop was quite laggy on the top edge. This resolves that issue.

Rewrite top-edge drags before window lookup finishes, and resolve the display with CoreGraphics so the tap thread does not touch AppKit.

Co-authored-by: Cursor <cursoragent@cursor.com>

@mrkai77 mrkai77 left a comment •

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for digging into this!

The thing stopping me from merging is the always-on active event tap (and I apologize for taking this long to respond). Right now every leftMouseDragged event on the system goes through our callback before macOS delivers it. That's true for all drags, for as long as Loop is running, even for users who have snapping or suppressMissionControlOnTopDrag turned off. A listen-only tap failing just means Loop misses events. If an active tap stalls or gets disabled by a timeout, drag input breaks for the whole system, and it's a failure point I'd rather avoid adding for this feature.

I think Rectangle handles this ideally, and it's worth a look. It rewrites the event location the same way this PR does, but it only keeps the active tap while the setting is on (startEventMonitor / reloadFromDefaults). Rectangle only blocks fast drags into Mission Control, and it may make sense to adopt this feature in Loop too.

Could we narrow the tap's lifetime along those lines? Some ideas from me:

  • Only while a window is being dragged. Keep the passive monitor as the default, create the active tap once we've resolved a window and seen it move, and remove it on mouse-up. Non-window drags would then never reach the active tap, so they'd stop getting rewritten.
  • At minimum, only while the setting is on. Use the passive monitor when suppressMissionControlOnTopDrag or snapping is off, and swap monitors when the setting changes, like Rectangle does.

If the lifetime is tied to a window drag, the generation/pause state probably goes away too. That would also remove a race I found while reviewing: the lookup Task reads generation only when it starts running, which can be after mouse-up has already bumped it, so a leftover pause can carry into the next drag.

One smaller thing: topEdgeY also matches the border between stacked displays, where Mission Control can't trigger. Rectangle's dragPrevY check avoids that by only rewriting when the cursor was already at the edge on the previous event.

An always-on active tap can stall every drag on the system. Snap detection stays listen-only, and the active tap is installed only after a resolved window moves.

Co-authored-by: Cursor <cursoragent@cursor.com>
@jvanderen1

Copy link
Copy Markdown
Contributor Author

The active tap is no longer always on.

Snap detection stays on the listen-only monitor. The active tap is created only after a window has been resolved and has moved, and it is removed on mouse-up, so a non-window drag never reaches it. The generation/pause state is gone with that. Rewriting also waits until the previous event was already on the same top edge, so crossing the seam between stacked displays once is left alone.

@jvanderen1
jvanderen1 requested a review from mrkai77 October 2, 2026 23:56
@mrkai77

mrkai77 commented Oct 9, 2026

Copy link
Copy Markdown
Owner

Hey, thanks so much for all the work on this, and sorry for how this turned out!

While digging, I found that macOS 15 added its own setting for this (Desktop & Dock > "Drag windows to top of screen to enter Mission Control"), and it catches every case. So I've opened #1175, which syncs Loop's toggle to that setting on macOS 15+ and keeps the existing cursor warp for 13-14. That makes all the event-tap code here unnecessary, so I'm going to close this PR in favor of that one.

Your PR is helped me get there! Without the experiments on this branch I'd never had found the system setting. I've added you as a co-author on the new commit. Thanks again, and sorry for the back and forth!

@mrkai77 mrkai77 closed this Oct 9, 2026
@jvanderen1

Copy link
Copy Markdown
Contributor Author

@mrkai77 No worries! Glad there is a robust solution.

I don't know if you think it's worth adding for legacy OS versions though?

Also, when is the next version going to be released? A lot of changes are on the beta version at this point.

Thanks in advance!

@jvanderen1
jvanderen1 deleted the fix/609-suppress-mission-control-stutter branch October 9, 2026 03:19
@mrkai77

mrkai77 commented Oct 9, 2026

Copy link
Copy Markdown
Owner

I don't know if you think it's worth adding for legacy OS versions though?

I did consider it, but we’re planning to drop support for macOS 13 sometime in the near future, so I’m not sure it would be worth adding at this point.

Also, when is the next version going to be released? A lot of changes are on the beta version at this point.

As for the next release, I’m aiming to get trackpad gestures a little more stable first! I’d say we’re about 90% there, with just a few more stability fixes remaining.

@jvanderen1

Copy link
Copy Markdown
Contributor Author

@mrkai77 That makes sense! Do you need any assistance with closing the remaining 10% gap?

@mrkai77

mrkai77 commented Oct 9, 2026

Copy link
Copy Markdown
Owner

@jvanderen1 any help would be appreciated! The most useful thing right now would probably be stress-testing trackpad gestures and reporting any issues you run into, even small ones.
For example, someone in our Discord recently found an edge case where Loop’s gesture blocker lets just enough of the initial events through to trigger a swipe in the app underneath, while also triggering Loop. I’m currently working on that in the kai/gesture-scroll-leak branch.
Those small but annoying edge cases are mainly what I’m trying to track down before the release!

@jvanderen1

Copy link
Copy Markdown
Contributor Author

@mrkai77 I'll take a look at the Discord, your fix, and see if there is anything else I can look at to assist 👍🏻

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🐞 Cursor stutters at the top edge when Suppress Mission Control is on

2 participants