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!
**Context**
Currently, if there are multiple keybindings with a shared prefix,
they are grouped into a nested series of Binding.Sets.
For example, as reported in #2734, the following bindings:
keybind = ctrl+z>1=goto_tab:1
keybind = ctrl+z>2=goto_tab:2
keybind = ctrl+z>3=goto_tab:3
Result in roughly the following structure (in pseudo-code):
Keybinds{
Trigger("ctrl+z"): Value.leader{
Trigger("1"): Value.leaf{action: "goto_tab:1"}),
Trigger("2"): Value.leaf{action: "goto_tab:2"}),
Trigger("3"): Value.leaf{action: "goto_tab:3"}),
}
}
When this is formatted into a string (and therefore in +list-keybinds),
it is turned into the following as Value.format just concatenates
all the sibling bindings ('1', '2', '3') into consecutive bindings,
and this is then fed into a single configuration entry:
keybind = ctrl+z>1=goto_tab:1>3=goto_tab:3>2=goto_tab:2
**Fix**
To fix this, Value needs to produce a separate configuration entry
for each sibling binding in the Value.leader case.
So we can't produce the entry (formatter.formatEntry) in Keybinds
and need to pass information down the Value tree to the leaf nodes,
each of which will produce a separate entry with that function.
This is accomplished with the help of a new Value.formatEntries method
that recursively builds up the prefix for the keybinding,
finally flushing it to the formatter when it reaches a leaf node.
This is done without extra allocations by using a FixedBufferStream
with the same buffer as before, sharing it between calls to nested
siblings of the same prefix.
**Caveats**
We do not track the order in which the bindings were added
so the order is not retained in the formatConfig output.
Resolves#2734
If a transmit and display command does not specify an ID or a number,
then it should not be responded to. We currently automatically assign
IDs in this situation, which isn't ideal since collisions can happen
which shouldn't, but this at least fixes the glaring observable issue
where transmit-and-display commands yield unexpected OK responses.
Fixes#2908
When loading `config-file`, we need to ensure that all loaded
configuration is loaded _prior_ to any `-e` values from the CLI.
To do this, I inserted a new `-e` special tag type in our replay steps.
This can be used to find when `-e` starts and ensure it remains at the
end of replay steps when the replay steps are being modified.
This commit also found a similar (but not exercised) issue where this
could happen with light/dark themeing.
❤️👻
This is the icon that we'll launch Ghostty 1.0 with. It was designed by
Michael Flareup at PixelResort. It retains the style of the original
Ghostty icon by Alasdair Monk, but brings in the new Ghost character and
adds details that make it more Apple-like.
The new Ghost character is an important evolution from the original
since it separates us from looking too much like PacMan. The new Ghost
is more unique and recognizable to Ghostty (or, hopefully will be!).
The icon itself has more details: the aluminum around the edge has
texture for the large enough sizes, there are visible scanlines, the
glow of a screen emanates from the ghost.
The icon itself is stylistic more Apple-like than other platforms. I
think Apple icons tend to look very good in more environments than the
reverse and I'm a big fan of the Apple aesthetic so I wanted to bring
that to Ghostty for all platforms.
This is probably going to cause some issues, this is the soonest we've
ever tried this. CI seems quite happy about the change though so moving
forward quickly is good with me.
I'm unsure if this is an environmental issue just for me or if this is
more widespread or what other downsides this may have. I'm more than
willing to revert this if it ends up causing different issues.
I found that with NixOS 24.11 and GTK 4.14 on my system, the default
Wayland GDK backend fails to initialize EGL. With GTK 4.16 everything
is fine.
If I force X11 then everything also works fine. This commit forces X11
for GTK 4.14 specifically (4.16+ is allowed to use Wayland).
Fixes#2877
As the comment in the diff states, we rely on `mmap` to zero our memory.
When we reset we are reusing previously allocated memory so we won't hit
an `mmap`. We need to zero the memory ourselves.
This is pretty slow if there is a lot of memory but in every case except
allocation failures, we expect there to be only a few pages allocated.
Fixes#2857
Some terminal modes always reset, but there are others that should be
conditional based on how the terminal's default state is configured.
Primarily from #2857 is the grapheme clustering mode (mode 2027) which
was always resetting to false but should be conditional based on the
the `grapheme-width-method` configuration.
Fixes#2848
The proper way to convert a unicode scalar in Swift is to use the
`String` initializer that takes a `UnicodeScalar` as an argument. We
were converting a number to a string before, which is incorrect.