macOS: fix stuck drag and drop caused by clicks waiting on the state mutex - #32
Open
rufy89 wants to merge 1 commit into
Open
macOS: fix stuck drag and drop caused by clicks waiting on the state mutex#32rufy89 wants to merge 1 commit into
rufy89 wants to merge 1 commit into
Conversation
The tap runs in Default mode with the mouse buttons in its mask, so every click is held until the callback returns. The callback takes the state mutex before dispatching on event type, so a plain click waits on the manager thread even though it ends up in the empty match arm and does nothing. When that wait lands on the LeftMouseUp that ends a drag and drop, the source app never sees the release. Its tracking loop stays open and the dragged item stays stuck to the cursor until the tap goes away, which for most people means logging out. Unmodified left and right clicks cannot match a binding, so return them before the lock is taken. Clicks with modifiers still take the normal path.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The macOS tap runs in Default mode and its mask includes the left and right
mouse buttons, so every click is held until the callback returns. The callback
takes the shared state mutex before it looks at the event type. That means a
plain click waits on the manager thread, even though an unmodified click ends
up in the empty match arm and does nothing.
When the manager thread holds the lock at the wrong moment, the delayed event
can be the LeftMouseUp that ends a drag and drop. The source app never sees the
release, its tracking loop stays open, and the dragged item stays stuck to the
cursor in whatever app started the drag. Quitting Handy frees it. Most people
just log out, since nothing else works.
I hit this repeatedly on 0.8.3 and went looking. While a drag was stuck,
CGGetEventTapList showed:
327 ms average per event. A separate listen-only tap placed downstream logged
nothing at all for the 53 seconds the drag was stuck, so the events were not
lost, they were sitting in the callback. kill -TERM on Handy released them and
the drag finished on its own, no logout needed.
Not device specific: it happens with an external mouse and with the built-in
trackpad, which makes sense since the tap sits below where those converge.
The fix returns unmodified left/right clicks before the lock is taken. Same
behaviour as now, those clicks already reached a no-op arm, but they stop
queueing behind the mutex. Clicks with modifiers still go down the normal path,
so Cmd+Click style bindings keep working.
Two things this does not touch:
regardless of modifiers. That is the path in Event tap swallows mouse buttons 4/5 system-wide (back/forward navigation broken) despite keyboard-only bindings — macOS, v0.9.4 cjpais/Handy#1758.
of problem, but that is a bigger change than I wanted to make here.
One thing worth flagging: the macos-modifier-tracking branch rewrites most of
this file, and it keeps the same shape, mask still lists the mouse buttons, tap
still created with CGEventTapOptions::Default, callback still takes the lock
before dispatching. So this fix will need carrying over there rather than
merging cleanly. Happy to rebase onto that branch instead if you prefer, or to
redo it there once it lands.
Tested on macOS 14.6.1, arm64:
diff
that guards the fast path
on my machine. It fails on unpatched main too. I ran patched and unpatched
alternately and got failures on both sides, with counts swinging from 28 to
205 messages between runs, so it looks sensitive to what else the machine is
doing rather than related to this change. I see that file is deleted on the
macos-modifier-tracking branch, so this may already be known.
Disclosure: I ran into this as a user and worked out the diagnosis with Claude
(Opus 5, via Claude Code), patch included. Everything reported here was measured
and run on my machine, macOS 14.6.1 on arm64.