Fixes#5929
Replaces #5984
On macOS, native applications typically never encode any text for
key events that use the command key. This is because the command key
is used for key equivalents and "commands" and should not be used
for text input.
This can be verified with apps like TextEdit but also terminals like
Terminal.app officially but also iTerm2 unofficially. Anything such as
`Cmd+b` or `Cmd+Shift+b` will not produce any text input.
Cross-platform terminals generally don't follow this, for example Kitty
performs CSI-u encoding and Alacritty and WezTerm encode the text as-is
(i.e. `Cmd+b` will produce `b`).
On Linux, the super key (command-equivalent) does produce text input.
For example, `Super+b` will produce `b` in Gnome Console, Foot, and
all the cross-platform terminals mentioned above.
In the interest of matching the behavior of native macOS applications,
we should not encode text for command key events on macOS. We continue
to encode text for the super key on non-macOS platforms.
Fixes#4518
If our UTF8 encoding is not recognized, we fall back to the ASCII
mapping of the logical key for the control sequence. This allows
cyrillic control characters to work.
I also verified that non-cyrllic (US) and alternate layouts (Dvorak)
work as expected still.
The [fixterms](http://www.leonerd.org.uk/hacks/fixterms/) "Really
Special Keypresses" section suggests using CSI 1 ; Ps R for F3, but this
is also a valid cursor position report. The intention was to make back-
compatible changes, so this is fairly considered a specification bug.
This changes F3 in legacy mode to send CSI 13 ; Ps ~ instead, this is a
variant listed in fixterms, is what kitty protocol uses, and lacks the
problematic overlap with cursor positions.
The KeyEncoder.zig unit test has been changed accordingly, and all tests
pass on my machine.
A common issue for US-centric users of a terminal is that the "option"
key on macOS is not treated as the "alt" key in the terminal.
## Background
macOS does not have an "alt" key, but instead has an "option" key. The "option"
key is used for a variety of purposes, but the troublesome behavior for some
(and expected/desired behavior for others) is that it is used to input special
characters.
For example, on a US standard layout, `option-b` inputs `∫`. This is not
a typically desired character when using a terminal and most users will
instead expect that `option-b` maps to `alt-b` for keybinding purposes
with whatever shell, TUI, editor, etc. they're using.
On non-US layouts, the "option" key is a critical modifier key for
inputting certain characters in the same way "shift" is a critical
modifier key for inputting certain characters on US layouts.
We previously tried to change the default for `macos-option-as-alt`
to `left` (so that the left option key behaves as alt) because I had the
wrong assumption that international users always used the right option
key with terminals or were used to this. But very quickly beta users
with different layouts (such as German, I believe) noted that this is
not the case and broke their idiomatic input behavior. This behavior was
therefore reverted.
## Solution
This confusing behavior happened frequently enough that I decided to
implement the more complex behavior in this commit. The new behavior is
that when a US layout is active, `macos-option-as-alt` defaults to true
if it is unset. When a non-US layout is active, `macos-option-as-alt`
defaults to false if it is unset. This happens live as users change
their keyboard layout.
**An important goal of Ghostty is to have zero-config defaults** that
satisfy the majority of users. Fiddling with configurations is -- for
most -- an annoying task and software that works well enough out of the
box is delightful. Based on surveying beta users, I believe this commit
will result in less configuration for the majority of users.
## Other Terminals
This behavior is unique amongst terminals as far as I know.
Terminal.app, Kitty, iTerm2, Alacritty (I stopped checking there) all
default to the default macOS behavior (option is option and special
characters are inputted).
All of the aforementioned terminals have a setting to change this
behavior, identical to Ghostty (or, Ghostty identical to them perhaps
since they all predate Ghostty).
I couldn't find any history where users requested the behavior of
defaulting this to something else for US based keyboards. That's
interesting since this has come up so frequently during the Ghostty
beta!
This was recently introduced a few days ago. Unfortunately, this doesn't
work as expected. The "function" modifier is not actually the fn key
but used by macOS to represent a variety of "functional" key presses.
This breaks other bindings such as #2411.
I can't find a source on the internet that reliably tells me how we
can detect fn key presses, but I do find a number of sources that tell
us we can't.
Related to #1802
CSIu format sends a unicode codepoint but we were just sending the first
byte of the utf8 sequence, which is very much not right. This fixes that
by parsing the utf-8.
It isn't defined what to do if the utf-8 sequence is invalid or has
multiple codepoints so we just skip CSIu encoding in that case.
Reproduction: HU layout Ctrl+ő
On release, we were previously not sending an alt text (we were sending
it properly on press). Kitty sends it also on release and the spec makes
it clear we should send it on release. This was just due to some faulty
logic; added a test and fixed that.
In korean input method on macos, it should simply return empty string.
Issue was created after https://github.com/mitchellh/ghostty/pull/1659.
```
gksr<BS> # 한ㄱ<BS>
```
'ㄱ' at the end should be removed with single <BS>, but for now
it requires two <bs> to remove 'ㄱ'.
Ghostty does not report associated text on macOS when
macos-option-as-alt is enabled for _any_ key press, whether or not the
Alt modifier is actually present. The "option as alt" decision should
only be made when the alt modifier is present.
This follows a specific behavior Kitty has, but no other terminal seems
to have, but it is a reasonable behavior that is in use by real programs
as found by our beta testing community.
We diverge from Kitty though in that we only apply this to ASCII
letters. Kitty does not CSIu encode special characters like `@` or `$`.
For these characters, Ghostty still encodes it as specified by fixterms.
Related to #1507
The comments in this change have most of the detail. The primary gist:
- caps-lock is handled correctly so #1501 is still fixed
- only characters pressed with the shift key are affected
Fixes#1505
I verified this behavior with every other terminal and I've added test
cases for it. We previously had a test case to assert the opposite,
which is incorrect.
Previously, we encoded `ctrl+_` in the CSIu format[1]. This breaks most
notably emacs which expects the legacy ambiguous encoding.
This commit utilizes the generator from Kitty to generate our control
key mappings. We also switch from keycode mapping to key contents
mapping which appears to be the correct behavior also compared to other
terminals.
In the course of doing this, I also found one bug with our fixterms
implementation. Fixterms states: "The Shift key should not be considered
as a modifier for Unicode characters, because it is most likely used to
obtain the character in the first place (e.g. the shift key is often
required to obtain the ! symbol)." We were not applying that logic and
now do.
[1]: https://www.leonerd.org.uk/hacks/fixterms/
Fixes#1099
We previously applied application keypad mode logic (`ESC=` or mode 66)
whenever it was active. However, from looking at the behavior of other
terminals (xterm and foot) it appears this isn't correct.
For xterm, application keypad mode only applies unconditionally if the
keyboard mode is VT220 (`-kt vt220`). For modern terminals, application
keypad mode is only applied if mode 1035 is disabled.
Mode 1035 is the "ignore numpad state with keypad mode" mode. It
defaults to true on terminal startup. If this is true, keypads are
always encoded in numerical mode. If this is false, the numlock state
will be respected.
Associated text should only be reported when it was generated from the
key event. To my knowledge, there are no release events which produce
text. This now matches kitty reporting for release events.
> When the key event is related to an actual modifier key, the corresponding
> modifier's bit must be set to the modifier state including the effect for the
> current event. For example, when pressing the :kbd:`LEFT_CONTROL` key, the
> ``ctrl`` bit must be set and when releasing it, it must be reset. When both
> left and right control keys are pressed and one is released, the release event
> must have the ``ctrl`` bit set. See :iss:`6913` for discussion of this design.
Related to https://github.com/mitchellh/ghostty/issues/1082
This fixes two separate issues to follow along with the new spec changes
Kovid pushed to Kitty:
1. When two modifiers are pressed and one is released, this shows up
as a proper release event with the correct side. Previously, the
correct side was shown but as a press event.
2. When two modifiers are pressed and one is released, the Kitty event
should not have that specific modifier set. For example, pressing
left ctrl, then right ctrl, then releasing right ctrl should encode
as "right ctrl released" but with NO modifiers still present.
Associated text should only be sent to the terminal when printable text
is generated from the keypress. Prevent sending associated text when any
modifier is pressed, except for Shift, NumLock, and Capslock
This brings Ghostty inline with the output of Kitty.
This enables shifted alt-prefixed keys, such as `shift+alt+.` on
US standard becoming `M->`. To do this, we needed to fix a few bugs:
(1) translation mods should strip alt even if other mods are set
(2) AppKit translation event needs to construct new characters with
the translation mods.
(3) Alt-prefix handling in KeyEncoder needs to allow ASCII utf8
translations even for macOS.
This regressed sometime -- I can't find the exact commit -- but in any
case I've moved this handling directly into the KeyEncoder so we can
unit test it and prevent future regressions.
User input withe Alt modifier is typically ESC prefixed. Escape
prefixing a non-ascii character can cause bugs in some applications. For
example in bash, emitting an Alt+ф allows the user to backspace one
character into the prompt. This can be repeated multiple times.
When a character is outside the ASCII range (exclusive of 0x7F, this is
handled as a control sequence), print the character as is, with no
prefix.
Fix reporting of alternate keys when using the kitty protocol. Alternate
keyboard layouts were failing to report the "base layout" key. This
implementation now matches kitty's output 1:1, and has some added unit
tests for cyrillic characters.
This also fixes a bug where a caps_lock modified key would report the
shifted key as well. The protocol explicitly requires that shifted keys
are only reported if the shift modifier is true.
This matches Kitty behavior on both macOS and Linux. In certain keyboard
modes and Kitty keyboard modes, the behavior changes but those already
matched (tested).