Skip to content

Inject synthetic keyup events to recover from macOS swallowing them while a Meta key is held#7

Open
lxpollitt wants to merge 1 commit into
lanceewing:masterfrom
lxpollitt:pr1-synthetic-keyup
Open

Inject synthetic keyup events to recover from macOS swallowing them while a Meta key is held#7
lxpollitt wants to merge 1 commit into
lanceewing:masterfrom
lxpollitt:pr1-synthetic-keyup

Conversation

@lxpollitt

Copy link
Copy Markdown
Contributor

This PR addresses macOS swallowing keyup events while a Meta (CMD) key is held.

Problem

On macOS, the OS swallows keyup events for non-modifier keys whose keyup would land while a Meta (CMD) key is held. This is a long-standing macOS behaviour is inherited by Safari, Chrome, and Firefox. This results in the key becoming "stuck" down, until it is next pressed and released. So for example, if the user types CMD-X into the Oric BASIC command line, then X becomes stuck held down, generating auto-repeat X characters indefinitely until the user next presses and releases X. (And because of Oric's keyboard scanning behaviour, pressing any other key does not cancel the auto-repeat / stuck key. You have to press the X itself.)

Fix

Added a DOM-level keydown/keyup listener via JSNI to GwtKeyboardMatrix. It tracks which non-Meta keycodes have received a keydown but not yet a keyup. On the transition from CMD-held → CMD-released, it dispatches synthetic DOM keyup events for those tracked keys. The synthetic events flow through libGDX's normal listener path (it doesn't know they're synthetic) and clear the stuck pressedKeys[] entries.

Scope

  • GWT/web only. The lwjgl3 desktop backend already correctly translates GLFW_KEY_LEFT/RIGHT_SUPER to Keys.SYM, so I think it doesn't have the same pressedKeys[] gate problem. But note I've not been able to test this first hand because the desktop version doesn't currently run for me on my Mac. (I may investigate this problem another time.)
  • Listens with useCapture=true so it runs before libGDX's own listener (which uses useCapture=false).

Testing

I've confirmed the fix behaves as expected for me using both Safari and Chrome on macOS. I haven't been able to verity Windows and Linux behaves as expected as I don't have easy access to a Windows or Linux desktops. I think the code is pretty safe but it would be good to verify on at least one other OS to be sure before merging.

…hile a Meta key is held

macOS suppresses keyUp events for non-modifier keys whose keyUp would
occur while a Meta (CMD) key is held - an OS-level behaviour inherited
by Chrome, Safari and Firefox.

GwtKeyboardMatrix's UI-thread constructor now installs a DOM-level
keydown/keyup listener (capture phase, so it runs before libGDX's own
document-level bubble-phase listener). The listener tracks which non-
Meta keys have unreleased keydowns and, on Meta release, dispatches a
synthetic keyup event for each. The synthetic events flow through
libGDX's normal keyup handler and clear its internal pressedKeys[]
state.
@lanceewing

Copy link
Copy Markdown
Owner

@lxpollitt , thank you for the new batch of improvements for joric. I might struggle to find time to properly test these fixes before the end of next week, but after that I should have some time to properly check things over for each fix. Keep the fixes coming in. I think its great that someone else is contributing to this project. - Btw, in one of my other projects (a very similar one called jvic), I added a TeaVM build target, which is another web based option. The build and running code is a lot faster for the teavm version vs the gwt version. Doing solely that appears to have fixed some of the audio glitches I had on the web version of jvic, so it might be that joric would benefit from a teavm build target as well as the default for web. I am thinking about doing that at some point, also potentially using OPFS for persistence of disk writes (which I have also recently added to jvic).

@lxpollitt

lxpollitt commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

@lanceewing no rush. None of them are urgent, and I definitely don't want you to feel its overly onerous on you as the primary maintainer. Get to them whenever is good for you.

This particular one used to catch me out quite frequently given I'm primarily a macOS users. (And I only realised when I was digging into the issue for this PR that I could cancel the stuck key by pressing it again. So it was a genuine pain point for me. Then I ended thinking I really should look into why the desktop version hung for me on macOS so I could at least say I've tested with that for my changes. Then once I got that working, that lead me to take a quick look into the audio on desktop on macOS. So ended up being a longer journey and more PRs than I initially had in mind - but hopefully it is useful for others to have these fixes too.)

Love the project overall by the way. The web hosted emulator is absolutely great for me sharing games with friends that I'm resurrecting that I wrote way back in 1983!

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.

2 participants