Commit Graph

611 Commits

Author SHA1 Message Date
Riccardo Mazzarini
c20fcfa1b4 Fix zero-width grapheme attachment during pending wrap
This PR fixes an issue where a zero-width combining mark could attach to
the wrong cell when the preceding character was written in the final
column and the cursor had a pending wrap.
2026-05-04 23:16:41 +02:00
Mitchell Hashimoto
810ebae8e8 terminal: lower default kitty image storage limit for libghostty
The default kitty image storage limit was 320 MB for all build
artifacts. For libghostty, this is overly generous since it is an
embedded library where conservative memory usage is preferred.
Lower the default to 10 MB when building as the lib artifact while
keeping the 320 MB default for the full Ghostty application.
2026-04-06 06:56:20 -07:00
Mitchell Hashimoto
935d37fbf1 terminal: add kitty image limits to Terminal.Options
Move kitty_image_storage_limit and kitty_image_loading_limits into
Terminal.Options so callers can set them at construction time
rather than calling setter functions after init. The values flow
through to Screen.Options during ScreenSet initialization. Termio
now passes both at construction, keeping the setter functions for
the updateConfig path.
2026-04-05 07:21:15 -07:00
Mitchell Hashimoto
64dcb91c1f terminal/kitty: add loading limits to kitty graphics protocol
Add a Limits type to LoadingImage that controls which transmission
mediums (file, temporary_file, shared_memory) are allowed when
loading images. This defaults to "direct" (most restrictive) on
ImageStorage and is set to "all" by Termio, allowing apprt
embedders like libghostty to restrict medium types for resource or
security reasons.

The limits are stored on ImageStorage, plumbed through
Screen.Options for screen initialization and inheritance, and
enforced in graphics_exec during both query and transmit. Two new
Terminal methods (setKittyGraphicsSizeLimit, setKittyGraphicsLoadingLimits)
centralize updating all screens, replacing the manual iteration
previously done in Termio.
2026-04-05 07:18:45 -07:00
Mitchell Hashimoto
d784600fd6 terminal: update page_serial_min in erasePage
Fixes #11957

erasePage now updates page_serial_min when the first page is erased,
and asserts that only front or back pages are erased since
page_serial_min cannot represent serial gaps from middle erasure.

To enforce this invariant at the API level, PageList.eraseRows is
now private. Two public wrappers replace it: eraseHistory always
starts from the beginning of history, and eraseActive takes a y
coordinate (with bounds assertion) and always starts from the top
of the active area. This makes middle-page erasure impossible by
construction.
2026-03-29 15:10:12 -07:00
Mitchell Hashimoto
f50aa90ced terminal: add lib.zig to centralize lib target and re-exports
Previously every file in the terminal package independently imported
build_options and ../lib/main.zig, then computed the same
lib_target constant. This was repetitive and meant each file needed
both imports just to get the target.

Introduce src/terminal/lib.zig which computes the target once and
re-exports the commonly used lib types (Enum, TaggedUnion, Struct,
String, checkGhosttyHEnum, structSizedFieldFits). All terminal
package files now import lib.zig and use lib.target instead of the
local lib_target constant, removing the per-file boilerplate.
2026-03-25 07:25:16 -07:00
Mitchell Hashimoto
8f1ac0bd4e vt: expose title and pwd in C API
Add title and pwd as both gettable data keys
(GHOSTTY_TERMINAL_DATA_TITLE/PWD) and settable options
(GHOSTTY_TERMINAL_OPT_TITLE/PWD) in the C terminal API. Getting
returns a borrowed GhosttyString; setting copies the data into the
terminal via setTitle/setPwd.

The underlying Terminal.setTitle/setPwd now append a null sentinel so
that getTitle/getPwd can return sentinel-terminated slices ([:0]const
u8), which is useful for downstream consumers that need C strings.

Change ghostty_terminal_set to return GhosttyResult instead of void
so that the new title/pwd options can report allocation failures.
Existing option-setting calls cannot fail so the return value is
backwards-compatible for callers that discard it.
2026-03-24 13:13:29 -07:00
Mitchell Hashimoto
08a44d7e69 terminal: store title set by escape sequences
Add a title field to Terminal, mirroring the existing pwd field.
The title is set via setTitle/getTitle and tracks the most recent
value written by OSC 0/2 sequences. The stream handler now persists
the title in terminal state in addition to forwarding it to the
surface. The field is cleared on full reset.
2026-03-23 14:17:10 -07:00
Mitchell Hashimoto
67d8d86efd terminal: rename ReadonlyStream to TerminalStream
Rename stream_readonly.zig to stream_terminal.zig and its exported
types from ReadonlyStream/ReadonlyHandler to TerminalStream. The
"readonly" name is now wrong since the handler now supports
settable effects callbacks. The new name better reflects that this
is a stream handler for updating terminal state.
2026-03-23 14:17:10 -07:00
Mitchell Hashimoto
918840cf1d vt: persist VT stream state across vt_write calls
Previously, every call to vt_write created a fresh ReadonlyStream with
new Parser and UTF8Decoder state. This meant escape sequences split
across write boundaries (e.g. ESC in one write, [27m in the next)
would lose parser state, causing the second write to start in ground
state and print the CSI parameters as literal text.

The C API now stores a persistent ReadonlyStream in the TerminalWrapper
struct, which is created when the Terminal is initialized. The vt_write
function feeds bytes through this stored stream, allowing it to maintain
parser state across calls. This change ensures that escape sequences
split across write boundaries are correctly parsed and rendered.
2026-03-21 11:18:02 -07:00
Mitchell Hashimoto
f9f92f2e0f terminal: consolidate mouse types into mouse.zig
Move MouseEvent and MouseFormat out of Terminal.zig and MouseShape out
of mouse_shape.zig into a new mouse.zig file. The types are named
without the Mouse prefix inside the module (Event, Format, Shape) and
re-exported with the prefix from terminal/main.zig for external use.

Update all call sites (mouse_encode.zig, surface_mouse.zig, stream.zig)
to import through terminal/main.zig or directly from mouse.zig. Remove
the now-unused mouse_shape.zig.
2026-03-15 15:48:36 -07:00
Mitchell Hashimoto
ac5e57ce67 input: extract mouse encoding to a pure, testable file
Move mouse event encoding logic from Surface.zig into a new
input/mouse_encode.zig file.

The new file encapsulates event filtering (shouldReport),
button code computation, viewport bounds checking, motion
deduplication, and all five wire formats (X10, UTF-8, SGR,
urxvt, SGR-pixels). This makes the encoding independently
testable and adds unit tests covering each format and edge
case.

Additionally, Surface `mouseReport` can no longer fail, since the only
failure mode is no buffer space which should be impossible. Updated
the signature to remove the error set.
2026-03-15 15:37:54 -07:00
Mitchell Hashimoto
8b9afe35a7 vt: ghostty_terminal_scroll_viewport 2026-03-13 19:51:04 -07:00
Mitchell Hashimoto
302e68fd3d vt: expose ghostty_terminal_new/free 2026-03-13 14:10:57 -07:00
Mitchell Hashimoto
3ba49a784f terminal: fix grapheme edge-wrap hyperlink integrity panic
When a grapheme expands to width 2 at the screen edge, this path can write
spacer_head before printWrap() sets row.wrap. With an active hyperlink,
printCell triggers hyperlink bookkeeping and page integrity checks in that
intermediate state, causing UnwrappedSpacerHead.

Mark row.wrap before writing spacer_head in this grapheme-wrap path to keep
the intermediate state valid.
2026-03-09 07:53:08 -07:00
Mitchell Hashimoto
177612a4cf terminal: fix insertBlanks orphaned spacer_tail beyond right margin
When insertBlanks clears the entire region from cursor to the right
margin (scroll_amount == 0), a wide character whose head is at the right
margin gets cleared but its spacer_tail just beyond the margin is left
behind, causing a "spacer tail not following wide" page integrity
violation.

Move the right-margin wide-char cleanup from inside the scroll_amount >
0 block to before it, so it runs unconditionally — matching the
rowWillBeShifted pattern of cleaning up boundary-straddling wide chars
up front.

Found via AFL++ fuzzing. #11109
2026-03-02 11:24:54 -08:00
Mitchell Hashimoto
b39a00ddfa terminal: fix insertLines/deleteLines orphaned cells on full clear
When deleteLines or insertLines count >= scroll region height, all rows
go through the clear-only path (no shifting). This path did not call
rowWillBeShifted, leaving orphaned spacer_tail cells when wide characters
straddled the right margin boundary, causing a "spacer tail not following
wide" page integrity violation.

Add rowWillBeShifted before clearCells in the else branch of both
functions.

Found via AFL++ fuzzing. #11109
2026-03-02 10:57:41 -08:00
Mitchell Hashimoto
e7030e73db terminal: fix printCell corrupting previous row when overwriting wide char
printCell, when overwriting a wide cell with a narrow cell at x<=1 and
y>0, unconditionally sets the last cell of the previous row to .narrow.
This is intended to clear a spacer_head left by a wrapped wide char, but
the cell could be a spacer_tail if a wide char fit entirely on the
previous row. Setting a spacer_tail to .narrow orphans the preceding
.wide cell, which later causes an integrity violation in insertBlanks
(assert that the cell after a .wide is .spacer_tail).

Fix by guarding the assignment so it only fires when the previous row's
last cell is actually a .spacer_head. The same fix is applied in both
the .wide and .spacer_tail branches of printCell.

Found by AFL++ stream fuzzer.
2026-03-02 07:28:12 -08:00
Mitchell Hashimoto
90e96a3891 terminal: fix insertBlanks integrity violation with wide char at right margin
insertBlanks checks whether the last source cell being shifted is wide
and clears it to avoid splitting, but it did not check the destination
cells at the right edge of the scroll region. When a wide character
straddles the right scroll margin (head at the margin, spacer_tail just
beyond it), the swap loop displaced the wide head without clearing the
orphaned spacer_tail, causing a page integrity violation
(InvalidSpacerTailLocation).

Fix by checking the cell at the right margin (last destination cell)
before the swap loop and clearing it along with its spacer_tail when it
is wide. 

Found by AFL++ stream fuzzer. #11109
2026-03-02 06:37:43 -08:00
Mitchell Hashimoto
97c11af347 terminal: fix integrity violation printing wide char with hyperlink at right edge
Printing a wide character at the right edge of the screen with an active
hyperlink triggered a page integrity violation (UnwrappedSpacerHead).
printCell wrote the spacer_head to the cell and then called
cursorSetHyperlink, whose internal integrity check observed the
spacer_head before printWrap had a chance to set the row wrap flag.

Fix by setting row.wrap = true before calling printCell for the
spacer_head case, so all integrity checks see a consistent state.
printWrap sets wrap again afterward, which is harmless. Found by AFL++
stream fuzzer.
2026-03-01 19:56:32 -08:00
Mitchell Hashimoto
9157eb439a terminal: insertBlanks should not crash with count 0 and CSI @ clamps [1,)
CSI @ (ICH) with an explicit parameter of 0 should be clamped to 1,
matching xterm behavior. Previously, a zero count reached
Terminal.insertBlanks which called clearCells with an empty slice,
triggering an out-of-bounds panic.

Fix the stream dispatch to clamp 0 to 1 via @max, and add a defensive
guard in insertBlanks for count == 0. Found by AFL++ stream fuzzer.
2026-03-01 14:50:24 -08:00
Jacob Sandlund
f53e4b43c4 Merge remote-tracking branch 'upstream/main' into grapheme-width-changes 2026-02-23 08:39:10 -05:00
Mitchell Hashimoto
eb335fb8dd cleanup by just scrolling in the renderer 2026-02-19 14:06:58 -08:00
Jacob Sandlund
bc7bbb27af Merge remote-tracking branch 'upstream/main' into grapheme-width-changes 2026-02-12 09:31:16 -05:00
Mitchell Hashimoto
b827e587d9 terminal: set semantic_prompt.click based on OSC133A options 2026-02-02 09:28:55 -08:00
Mitchell Hashimoto
9ff9298707 terminal: parse OSC 133 cl values correctly 2026-02-02 09:01:01 -08:00
Jacob Sandlund
96c623ee33 Merge remote-tracking branch 'upstream/main' into grapheme-width-changes 2026-02-02 08:30:41 -05:00
Jacob Sandlund
1c3fc062e1 clarify comments 2026-02-02 08:30:30 -05:00
Mitchell Hashimoto
a909a1f120 terminal: mark newlines for input lines as prompt continuation rows 2026-02-01 13:01:03 -08:00
Mitchell Hashimoto
853fee9496 terminal: when semantic cursor is prompt, assume newline is prompt
This works around Fish (at least v4.2) having a non-compliant OSC133 
implementation paired with not having the hooks to fix this via shell
integration. We have to instead resort to heuristics in the terminal
emulator. Womp, womp.

The issue is that Fish does not emit OSC133 secondary prompt (`k=s`)
markers at the beginning of continuation lines. And, since Fish doesn't
provide a PS2-equivalent, we can't do this via shell integration.

We fix this by assuming on newline (`\n`) that a cursor that is already
painting prompt cells is continuing a prior prompt line, and
pre-emptively mark it as a prompt line. But this has two further issues
we have to work around:

  1. Newline/index (`\n`) is one of the _hottest path_ functions in
     terminal emulation. It sucks to add any new conditional logic here.
     We do our best to gate this on unlikely conditions that the branch
     predictor can easily optimize away.

  2. Fish also emits these for auto-complete hints that may be deleted 
     later. So, we also have to handle the scenario where a prompt is 
     continued, then replaced by command output, and fix up the prompt 
     continuation flag to go back to output mode.

Point 2 is ALMOST automatically handled, because Fish does emit a `CSI J`
(erase display below) to erase the auto-complete hint. This resets all
our rows back to output rows. **Unfortunately**, Fish emits `\n` before
triggering the preexec hooks which set OSC133C. So we get the newline
logic FIRST (sets the prompt line), THEN sets the output cursor. If they
switched ordering here everything would just work (with the one
heuristic). But now, we need two!

To address this, I put some extra heuristic logic in the OSC133C
(output starting) handler: if our row is marked as a prompt AND our 
cursor is at x=0, we assume that the prompt continuation was deleted
and we unmark it. 

I put the heuristic logic dependent on OSC133C because that's way colder
of a path than putting something in `printCell` (which is the actual
hottest path in Ghostty).

We could get more rigorous here by also checking if every cell is empty
but that doesn't seem to be necessary at this time for any Fish version
I've tested. I hope thats correct.

I'd really love for Fish to improve their OSC133 implementation to
conform more closely to the terminal-wg spec, but we're going to need
these workarounds indefinitely to handle older Fish versions anyway.
2026-01-31 20:45:01 -08:00
Mitchell Hashimoto
918c2934a3 terminal: add redraw=last for bash for OSC133 2026-01-31 15:26:14 -08:00
Mitchell Hashimoto
a4b7a766fe PR review 2026-01-31 11:03:21 -08:00
Mitchell Hashimoto
c3e15a5cb6 terminal: rename semantic prompt 2026-01-31 11:01:03 -08:00
Mitchell Hashimoto
1b2376d366 terminal: remove last semantic_prompt usage from Terminal 2026-01-31 11:01:03 -08:00
Mitchell Hashimoto
10bc88766b terminal: soft wrap preserves new semantic prompt state 2026-01-31 11:01:03 -08:00
Mitchell Hashimoto
917a42876e terminal: cursorIsAtPrompt uses new APIs 2026-01-31 11:01:03 -08:00
Mitchell Hashimoto
112db8211d terminal: remove clearPrompt and integrate it into resize 2026-01-31 11:01:03 -08:00
Mitchell Hashimoto
b62ac468dc terminal: change Screen.resize to take an options struct 2026-01-31 11:01:02 -08:00
Mitchell Hashimoto
07dce38cc5 terminal: Screen tracks semantic content seen 2026-01-31 11:01:02 -08:00
Mitchell Hashimoto
fd016fdb2a terminal: move cursor semantic content functions into Screen 2026-01-31 11:01:02 -08:00
Mitchell Hashimoto
a80b3f34c0 terminal: add semantic_prompt2 to Row to track prompt state 2026-01-31 11:01:02 -08:00
Mitchell Hashimoto
ae65998d5b terminal: OSC 133;I 2026-01-31 11:01:01 -08:00
Mitchell Hashimoto
4d555f878e terminal: OSC 133 N 2026-01-31 11:01:01 -08:00
Mitchell Hashimoto
af12241d88 terminal: OSC 133 P 2026-01-31 11:01:01 -08:00
Mitchell Hashimoto
acd7a448e1 terminal: OSC 133 B handling 2026-01-31 11:01:01 -08:00
Mitchell Hashimoto
3fa6320478 terminal: handle fresh_line_new_prompt 2026-01-31 11:01:01 -08:00
Mitchell Hashimoto
24bf642bdc terminal: start implementing proper semantic prompt behaviors 2026-01-31 11:01:01 -08:00
Mitchell Hashimoto
7a69e2bf86 terminal: printCell writes with the current pen's content type 2026-01-31 11:01:01 -08:00
Jacob Sandlund
a7080b6fab Make VS15 test check that previous grapheme is not affected 2026-01-27 10:23:53 -05:00
Jacob Sandlund
6b2caf69db Merge remote-tracking branch 'upstream/main' into grapheme-width-changes 2026-01-27 09:44:55 -05:00