Skip to content

Keyboard: ignore host Option-level characters on macOS - #2159

Open
sndpl wants to merge 1 commit into
openMSX:masterfrom
sndpl:macos-option-character
Open

sndpl wants to merge 1 commit into
openMSX:masterfrom
sndpl:macos-option-character

Conversation

@sndpl

@sndpl sndpl commented Jul 26, 2026 •

Copy link
Copy Markdown
Contributor

Addresses #797 and #974 (and the macOS side of #1331, partly - see the bottom).
Not using a closing keyword, since none of those tickets is only about this.

On macOS, in CHARACTER mapping mode, GRAPH and CODE combinations produce the
wrong MSX character or nothing at all. Reported since 2010, and reproducible
today.

Cause

Both Option keys are MSX modifier keys for us: left Option is GRAPH
(processGraphChange on SDLK_LALT), right Option is CODE/KANA by default.
But the host also uses Option to select an alternate character from the
keyboard layout, and it does so before openMSX sees the key. So option+f
reaches us as U+0192, not as f.

In CHARACTER mode that character is looked up in the MSX keymap. Whenever it
happens to exist there, we press the keys that produce that character on the
MSX rather than the key the user actually hit.

That explains a detail which had made this look erratic: it depends on whether
the host's Option-level character exists in that machine's character set. On a
Toshiba HX-10, holding GRAPH and pressing F G H J:

                             MSX keys pressed
  KEY mode (correct)         CHARACTER mode (before)
  Option+F  [F, GRAPH]       [1, CODE, GRAPH]      <-- wrong
  Option+G  [G, GRAPH]       [G, GRAPH]
  Option+H  [H, GRAPH]       [H, GRAPH]
  Option+J  [J, GRAPH]       [J, GRAPH]

Only F is wrong, because U+0192 is part of the MSX character set so the lookup
succeeds. U+00A9, U+02D9 and U+2206 are not, so the lookup fails and the
existing fall-back to key-to-matrix mapping already does the right thing. Which
keys break therefore varies with the host layout and the emulated machine's
character set, which is why the reports each name a different set of keys.

Fix

Ignore the host's interpretation while such an Option key is held, and use that
same key-to-matrix fall-back. This is exactly what SDL3 does for us through
SDL_HINT_MAC_OPTION_AS_ALT, whose ReplaceEvent() rebuilds the NSEvent using
charactersIgnoringModifiers; that hint does not exist in SDL2. Combinations
with Command or Control are left alone, matching SDL3, so the Cmd+I -> Insert
handling and the hotkeys are untouched. KEY and POSITIONAL mode already force
the unicode to zero, so nothing changes there, and on non-Apple builds the test
folds to false.

How it was verified

Driven against a real emulator run with synthetic host key events. Ordinary
CGEventCreateKeyboardEvent() cannot reproduce this: it computes the event's
character payload from the keycode alone, so a synthetic option+f carries f
and the bug never appears. Using CGEventKeyboardSetUnicodeString() to supply
the character macOS actually produces replays the real event exactly - the
resulting kbd_trace_key_presses output matches the trace posted from real
hardware in #797 value for value:

unicode: 0x0192 (f-hook)  keyCode: F  keyName: F+ALT
unicode: 0x00a9 (c)       keyCode: G  keyName: G+ALT
unicode: 0x02d9 (dot)     keyCode: H  keyName: H+ALT
unicode: 0x2206 (delta)   keyCode: J  keyName: J+ALT

The oracle is KEY mapping mode, which both #797 and #974 report as working. The
check reads the emulated key matrix rather than the screen, so it does not
depend on character sets or screen mode. After the change, CHARACTER mode
presses the same MSX keys as KEY mode for all four combinations, over repeated
runs.

Also checked unchanged:

Not covered

Host dead keys. option+e arms a composition in the host's input context, and
the composed character then arrives with the next keystroke, which no longer
has Option held, so nothing on our side can distinguish it. Only the SDL3 hint
prevents the composition being armed at all. That is the remaining macOS half
of #1331.

On macOS both Option keys are MSX modifier keys for us: left Option is GRAPH,
right Option is CODE/KANA by default. But the host also uses Option to select
an alternate character from the keyboard layout, and it does so before we see
the key: option+f arrives as U+0192 rather than 'f'.

In CHARACTER mapping mode that character gets looked up in the MSX keymap.
Whenever it happens to exist there, we press the keys that produce *that*
character on the MSX instead of the key the user actually hit. On a Toshiba
HX-10, holding GRAPH and pressing F presses MSX CODE+1 instead of F, because
U+0192 is part of the MSX character set. Option+G/H/J are fine only because
U+00A9, U+02D9 and U+2206 are not in the MSX character set: the lookup fails
and the existing fall-back to key-to-matrix mapping does the right thing. That
is also why the various reports of this each name different keys.

So ignore the host's interpretation while such an Option key is held and use
that same fall-back, which is what SDL3 does for us via
SDL_HINT_MAC_OPTION_AS_ALT (not available in SDL2). Combinations with Command
or Control are left alone, matching SDL3. KEY and POSITIONAL mapping mode
already force the unicode to zero, so nothing changes there.

This does not help for host dead keys: option+e arms a composition in the host
input context, and the composed character then arrives with the next keystroke,
which no longer has Option held. Preventing that needs the SDL3 hint.
@sndpl

sndpl commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

Latest stable release:
latest stable release
With fix from this PR:
fixed release

@MBilderbeek

Copy link
Copy Markdown
Member

I understand that (part of?) this fix comes from SDL3 code. We are also preparing to migrate to SDL3. Would it make sense to first migrate to SDL3 and then look at the keyboard situation again?
And if we don't, we should probably remove/revise this fix in the SDL3 branch. (And probably first upgrade to the latest SDL2 release?) @m9710797 what do you think?

@MBilderbeek

Copy link
Copy Markdown
Member

As for this fix: the problem seems that we use a host key as modifier key, that is also used on the host side to compose characters with (i.e. functions as a different modifier key on the host).
As far as I understand this: this patch says that the host key must be first seen as MSX modifier key and not as host composer/modifier key. But suppose you want to type in an MSX character (in CHARACTER mode) that requires that host modifier key ("Option") to type it, how would that work then, @sndpl ? Isn't that now blocked with this patch?

@sndpl

sndpl commented Jul 26, 2026 •

Copy link
Copy Markdown
Contributor Author

Yes you are correct, there seems to be a fix for this issue in SDL3, but it seems I missed that there is a SDL3 branch (no draft PR for it?)

For you second question, you mean if you want to type an ö on the MSX? because then you still need to use CODE + F like on a real MSX with an international keyboard.

@m9710797

Copy link
Copy Markdown
Contributor

I'm only following this thread from a distance.

But I think the example is invalid. The point of CHARACTER mode is that if you want to type "ö", you need to type the host key combination, not the MSX key combination (CODE + F). So I don't immediately see it as a problem that CODE + F doesn't work to produce "ö".

And maybe that means not all MSX characters can be typed (when there is no corresponding host key combination). But that's fine for CHARACTER mode IMHO.

@MBilderbeek

Copy link
Copy Markdown
Member

Yes you are correct, there seems to be a fix for this issue in SDL3, but it seems I missed that there is a SDL3 branch (no draft PR for it?)

There is only this branch: https://github.com/openMSX/openMSX/tree/sdl3 - no linked PR.

For you second question, you mean if you want to type an ö on the MSX? because then you still need to use CODE + F like on a real MSX with an international keyboard.

Yeah, as @m9710797 also said: the point of CHARACTER mode is that you can use the host key combination (so as you are used to it on a host) to type the character and that openMSX will perform the MSX keyboard combination to get that character.

Suppose you have a QWERTZ host keyboard, then it is very logical that you can directly type the ö from the keyboard even on an English MSX keyboard.

It seems that there are situations of host and MSX keyboard in which both CHARACTER and POSITIONAL modes are not ideal. Not sure what the best solution is then. But to me it seems good to keep these modes consistent.

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.

3 participants