This causes linker issues for some libghostty users. I don't know why we
never saw these issues with Ghostty release builds, but generally
speaking I think its fine to do this for 3rd party code unless we've
witnessed an issue. And these deps have been stable for a long, long
time.
It's clear from the surrounding code that the RGB values returned from
`cell_text_vertex` are expected to be linearized. This was not the case
for the cursor cell, resulting in the cursor text being too bright when
using `cursor-text = cell-background`.
Fixes #8353/#9138.
xref #8960, which would also fixe this issue, but I don't know where
that's heading
This causes linker issues for some libghostty users. I don't know why we
never saw these issues with Ghostty release builds, but generally
speaking I think its fine to do this for 3rd party code unless we've
witnessed an issue. And these deps have been stable for a long, long
time.
The march towards #189 continues.
This hooks up the search thread to the main surface and all the state
necessary for the renderer to show search results in the viewport! This
also adds a `search` binding which takes a query to start/stop a search.
**This still doesn't add any search GUI,** which will come later, the
internals must happen first.
A non-blank binding will start or change the search term. An empty
binding will stop search:
```
keybind = cmd+f=search:Hello
keybind = shift+cmd+f=search:
```
> [!NOTE]
>
> Obviously, search will eventually have a GUI. The point of this PR is
primarily to connect all the various internal systems more than
anything. GUI will come soon.
## Demo
https://github.com/user-attachments/assets/06af5a3b-280e-4804-b506-419b92a95f99
## Major Changes
The only major changes required as part of this is the introduction of
what I'm calling the terminal "highlight" system. This is a generic
system for highlighting portions of the terminal contents. These will
ultimately underpin what we currently call "selections" (selecting text
with your mouse/keyboard) but that is far too large a change to make in
one PR.
Therefore, this PR introduces highlights and the only consumer is the
entire search subsystem.
## Limitations
Still plenty of limitations we need to keep marching towards resolving:
- ~~Search matches are styled the same as selections. They should be
different.~~
- There is no way to iterate search matches, yet.
- There is no GUI for inputting a search query or viewing total matches,
yet.
- ~~I think searches are case-sensitive currently and they should
probably not be.~~ Done, for ASCII.
But hey, it's something!
These now properly match the FreeType API- compared directly in the unit
tests against the values provided by the FreeType header itself.
This was ridiculously wrong before, like... wow.
FcLangSetHasLang returns FcLangResult enum values:
- FcLangEqual (0): Exact match
- FcLangDifferentTerritory (1): Same language, different territory
- FcLangDifferentLang (2): Different language
The previous comparison to FcTrue (1) caused:
- Exact matches (0) to incorrectly return false
- Partial matches (1) to incorrectly return true
This fix changes the comparison to FcLangEqual (0) so hasLang()
correctly returns true only for exact language matches.
Fixes emoji font detection which relies on checking for 'und-zsye'
language tag support.
## Summary
Adds a new environment variable `GHOSTTY_QUICK_TERMINAL=1` that is set
when a terminal surface is launched as a quick terminal. This allows
shell configurations to conditionally adjust behavior based on whether
the terminal is a quick terminal.
Closes#3985
<img width="1430" height="228" alt="CleanShot 2025-11-23 at 12 39 27
png"
src="https://github.com/user-attachments/assets/1085c19b-9d58-4603-a03d-662bfde47095"
/>
## Motivation
Quick terminals are designed as ephemeral, singleton windows for quick
commands. Many users (especially tmux users) have shell configurations
that automatically attach to or start tmux sessions on terminal launch.
This automatic behavior conflicts with the quick terminal use case.
Example from the issue:
```zsh
# Users want to skip this in quick terminals
if [[ -z "$TMUX" ]]; then
tmux attach || tmux new
fi
With this PR, users can now detect quick terminals and adjust their shell behavior:
if [[ -z "$TMUX" ]] && [[ -z "$GHOSTTY_QUICK_TERMINAL" ]]; then
tmux attach || tmux new
fi
```
## Implementation
- macOS: Added GHOSTTY_QUICK_TERMINAL=1 to SurfaceConfiguration
environment variables in QuickTerminalController.swift:345-351
- Linux/GTK: Added environment variable check in
src/apprt/gtk/class/surface.zig:1469-1472 within defaultTermioEnv()
The environment variable is automatically inherited by split surfaces.
## Note on Scripting API
I'm aware that @rhodes-b mentioned this functionality was planned for
the scripting API (issue #3985). However, I believe this simpler
environment variable approach provides immediate value for tmux users
and shell configuration use cases, while the scripting API can later
provide more comprehensive surface introspection capabilities.
## AI Assistance Disclosure
This PR was written with Claude Code assistance. The Linux/GTK
implementation was fully AI-generated. I implemented the mac version
myself after Claude Code helped me understand the codebase architecture
and locate the appropriate files.
This PR builds on https://github.com/ghostty-org/ghostty/pull/9678 ~so
the diff from there is included here (it's not possible to stack PRs
unless it's a PR against my own fork)--review that one first!~
This PR fixes an issue with the VS15/VS16 handling in terminal `print`,
where before we didn't check for valid sequences if the base code point
is an emoji. This meant that a VS15 after an emoji that isn't part of a
valid sequence would narrow the cell, despite that the emoji doesn't
have a text presentation.
This PR also uses a single `is_emoji_vs_base` instead of the
`is_emoji_vs_emoji` and `is_emoji_vs_text`. See the [uucode
comment](215ff09730/src/config.zig (L239-L254))
for why I'm recommending this.
AI was used in some of the uucode changes in
https://github.com/ghostty-org/ghostty/pull/9678 (Amp--primarily for
tests), but everything was carefully vetted and much of it done by hand.
This PR was made without AI.
This updates uucode to the latest version, with the following changes:
b309dfb4e2...31655fba3c
For this PR, the new `uucode` version has two changes that contribute to
the diff:
* The `grapheme_break` now includes `emoji_modifier` and
`emoji_modifier_base`, so we don't need to grab those from
`is_emoji_modifier` and `is_emoji_modifier_base`.
* The `wcwidth` calculation has been split into `wcwidth_standalone`
(width of a code point when it's the only code point in a grapheme) and
`wcwidth_zero_in_grapheme` (whether the code point is _not_ contributing
to the width of a multi-code-point grapheme). To keep the current
Ghostty behavior for this PR, this sets `width` to 0 if
`wcwidth_zero_in_grapheme`, but with the exception of
`is_emoji_modifier` (see comment).
* While this PR isn't affected by it, take a look at
[wcwidth.zig](https://github.com/jacobsandlund/uucode/blob/main/src/x/config_x/wcwidth.zig)
for the considerations that went into determining the width of a code
point, along with many comments.
* See
[x/grapheme.zig](https://github.com/jacobsandlund/uucode/blob/main/src/x/grapheme.zig)
for the calculation of the width of a grapheme based on the width of the
code points with exceptions, again with many comments.
* See
[resources/wcwidth](https://github.com/jacobsandlund/uucode/tree/main/resources/wcwidth)
for a comparison of other unicode libraries calculation of "wcwidth".
PRs will follow this in a moment to also take advantage of the new
`uucode` version for:
* Better grapheme segmentation
* Correct VS15/VS16 handling
* More correct cell width calculation (especially certain scripts such
as Devanagari)
This makes `cursorStyle` utilize `RenderState` to determine the
appropriate cursor style. This moves the cursor style logic outside the
critical area, although it was cheap to begin with.
This always removes `viewport_is_bottom` which had no practical use.
This makes `cursorStyle` utilize `RenderState` to determine the
appropriate cursor style. This moves the cursor style logic outside the
critical area, although it was cheap to begin with.
This always removes `viewport_is_bottom` which had no practical use.