Relevant Links
Related: #10713 #11342
Problem
On my main dev system (Arch Linux, Wayland, KDE, with ghostty + tmux), I was noticing some issues where left clicks were not responding properly but it was resolved after I closed that particular ghostty window and launched a new one. I'm not sure if the related issues above are the exact same issue but they still might be relevant.
Fix
This is a potential fix but there's still some gaps that I would appreciate some discussion on. The main thing I observed in the current main build is that the side mouse buttons through gtk will get stuck in ghostty, and this is observable easily in the inspector. Buttons {8} and {9} are clicked even though I have released them:
This seems to happen if you click and drag with these side mouse buttons, which can happen accidentally quite easily, and interferes with normal right and left click operations.
The fix in this PR currently is to add handler to listen to the gtk unpaired-release event , and only handle this for buttons > 5. From what I understand, the current gcMouseDown and gcMouseUp handlers have behaviors for mouse buttons 1-5, so I think this PRs fix has the least interference with existing code.
The fix in this PR adds a handler to listen to the gtk unpaired-release event. From what I understand, gtk resets its click gesture if you move the mouse while holding a button > 5 (since it only tracks buttons 1-5 with its modifier masks), and also if you press a second button while one is already held down. When that happens the release comes through unpaired-release instead of the normal released event, so ghostty never sees the button get released.
After @jcollie's review, the handler now only forwards the release if ghostty's core still thinks that button is pressed. So releases for presses that ghostty never saw, like the second button in a chord or a click that just focused a split, get dropped and shouldn't trigger any clicks.
The fix works for these mouse buttons, and can be tested by observing the click state in the inspector, or running printf '\e[?1003h\e[?1006h'; cat -v in a shell and observing whether the code <35 comes through, which indicates that no buttons were held while the mouse pointer moved. Specifically the actions to replicate a stuck button can be something like:
- hold down button
- move mouse
- release button
- move mouse again and see whether
<35is in the code printed
Since the MacOS build doesn't use gtk, the side mouse button issue isn't a bug there.
However when testing all this I discovered it's kinda easy to get other buttons stuck, and I don't know the desired behavior for this is, so I would appreciate advice from contributors/maintainers. Similar things happen in both MacOS and Linux, though the behavior is slightly different when using something like tmux that has its own right-click menu and doesn't open ghostty's right click menu. But for example, if you do these steps:
hold down the left mouse buttonright click oncedrag the mouserelease left click
You will observe in the inspector that L is stuck in the "click state" row, and then gets unstuck when you click the left mouse button again. This can happen with the above steps if you reverse the left and right mouse buttons too.
I'm not really sure if this is a big deal since it gets unstuck so easily, unlike the buttons > 5 that my PR addresses, but I figured that maybe this was a gap worth investigating before merging this PR if some kind of overall better fix is deemed necessary.
Update: originally I asked here about how easy it is to get other buttons stuck. It turns out this is caused by the same gtk gesture reset, so with the change from @jcollie's review this PR fixes it on Linux too, and you can check it the same way as above by moving the mouse again and seeing whether <35 comes through. This should also work if you reverse the left and right mouse buttons. The similar behavior on MacOS isn't affected by this PR since it doesn't touch the MacOS app.
AI Disclosure
EDIT: Sorry I forgot to include this in the description but it was in my vouch request: I used an LLM to write the code and the test because I'm not super familiar with zig nor this codebase. I hope the potential gaps I've identified help identify the extent I understand this change and also what needs a closer look from an experienced contributor.
Activity
7Probably should forward an unpaired release for any button only when the core still has that button down (core_surface.mouse.click_state[button] == .press).
Good idea, I'll try this out and update the PR if it works.
Ok @jcollie this fix works much better overall by adding the mouseButtonPressed(Button) check instead of just checking whether button > 5.
One tiny little nit.
Probably should forward an unpaired release for any button only when the core still has that button down (core_surface.mouse.click_state[button] == .press).