Commit Graph

100 Commits

Author SHA1 Message Date
Mitchell Hashimoto
181254d36a terminal: debounce compression by page activity
Compression scheduling previously postponed its idle timer after every
renderer wake. The inspector redraw loop wakes the renderer
continuously, so opening it could prevent pending scrollback compression
from ever starting.

Track compression-relevant PageList activity with a wrapping 48-bit
token and restart the timer only when the composite Terminal token
changes. This removes the separate dirty bit while reserving 16 bits for
future Terminal-owned compression triggers.

Expose target availability through the terminal package and leave
renderer compression state undefined on unsupported targets so its timer
is never initialized.
2026-07-09 09:47:13 -07:00
Mitchell Hashimoto
f6fd4cb087 terminal: generalize page memory reclamation
PageList's virtual-memory helpers were tied to pool items even though
the underlying decommit and recommit operations also apply to retained
page mappings.

Move the helpers into terminal/mem.zig and express their different
failure contracts as generic modes. Zero mode preserves the existing
pool invariant and fallbacks, while strict mode only succeeds when the
operating system accepts reclamation and avoids touching memory that
will be restored.

PageList now uses the shared zero mode for its page pool. The strict
path is tested in isolation and remains unused, so this does not enable
page compression yet.
2026-07-09 09:47:11 -07:00
Mitchell Hashimoto
ebc3ffd222 terminal: add compressed page representation
The standalone LZ4 codec had no representation for terminal page
ownership or metadata, so PageList integration would otherwise need to
reconstruct every Page field independently.

Add compress.Page, which embeds the complete terminal Page while
retaining its original virtual mapping and owns only an exact-sized
encoded block. Compression is kept only when the encoded state is
strictly smaller, and scratch output is capped at that profitability
boundary so a future PageList caller can borrow a standard pool item.

Extend the page-compression benchmark with a store mode that measures
the encoded copy, allocation, bounded retention, and eviction path.
Nothing uses compression from PageList yet; this remains isolated
groundwork.
2026-07-09 09:47:11 -07:00
Mitchell Hashimoto
c62c159841 terminal: add standalone LZ4 block codec
Scrollback compression needs a codec that can be used from libghostty-vt
without pulling in libc, and we need to measure it before integrating it
with terminal page ownership.

This adds an allocation-free raw LZ4 block codec in scalar Zig.  Callers
provide the input, output, and fixed-size scratch table. The decoder
uses an exact-size output contract so page metadata mismatches fail
cleanly. Compatibility vectors, boundary cases, random round trips, and
fuzz coverage exercise the block format.

Also adds a page-compression benchmark that operates on reusable raw
page corpora. Compression and decompression have separate modes with
setup outside the timed region, plus a ratio report and no-op baseline.
Nothing uses compression in the terminal yet; this is the isolated codec
and measurement groundwork.
2026-07-09 09:47:11 -07:00
Mitchell Hashimoto
d86ff37a58 terminal: SelectionGesture, but only with mouse press 2026-05-25 15:48:16 -07:00
Mitchell Hashimoto
29e3de737e terminal: make wuffs runtime-swappable, enable Kitty graphics for libvt
Introduce terminal/sys.zig which provides runtime-swappable function
pointers for operations that depend on external implementations. This
allows embedders of the terminal package to swap out implementations
at startup without hard dependencies on specific libraries.

The first function exposed is decode_png, which defaults to a wuffs
implementation. The kitty graphics image loader now calls through
sys.decode_png instead of importing wuffs directly.

This allows us to enable Kitty graphics support in libghostty-vt
for all targets except wasm32-freestanding.
2026-04-04 22:01:38 -07:00
Mitchell Hashimoto
b31dcf9a4c terminal: add device_attributes module
Introduce a dedicated device_attributes.zig module that consolidates
all device attribute types and encoding logic. This moves
DeviceAttributeReq out of ansi.zig and adds structured response
types for DA1 (primary), DA2 (secondary), and DA3 (tertiary) with
self-encoding methods.

Primary DA uses a ConformanceLevel enum covering VT100-series
per-model values and VT200+ conformance levels, plus a Feature
enum with all known xterm DA1 attribute codes (132-col, printer,
sixel, color, clipboard, etc.) as a simple slice. Secondary DA
uses a DeviceType enum matching the xterm decTerminalID values.
Tertiary DA encodes the DECRPTUI unit ID as a u32 formatted to
8 hex digits.

This is preparatory work for exposing device attributes through
the stream_terminal Effects callback system.
2026-03-23 14:50:26 -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
a1d7ad9243 terminal: extract size report encoder
Size report escape sequences were previously formatted inline in
Termio.sizeReportLocked, and termio.Message carried a duplicate enum for
report styles. That made the encoding logic harder to reuse and kept
the style type scoped to termio.

Move the encoding into terminal.size_report and export it through
terminal.main. The encoder now takes renderer.Size directly and derives
grid and pixel dimensions from one source of truth. termio.Message now
aliases terminal.size_report.Style, and Termio writes reports via the
shared encoder.
2026-03-17 16:21:34 -07:00
Mitchell Hashimoto
bed9d92f04 vt: expose focus encoding in C and Zig APIs
Add focus event encoding (CSI I / CSI O) to the libghostty-vt public
API, following the same patterns as key and mouse encoding.

The focus Event enum uses lib.Enum for C ABI compatibility. The C API
provides ghostty_focus_encode() which writes into a caller-provided
buffer and returns GHOSTTY_OUT_OF_SPACE with the required size when
the buffer is too small.

Also update key and mouse encoders to return GHOSTTY_OUT_OF_SPACE
instead of GHOSTTY_OUT_OF_MEMORY for buffer-too-small errors,
reserving OUT_OF_MEMORY for actual allocation failures. Update all
corresponding header documentation.
2026-03-16 14:38:19 -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
dbfc3eb679 Remove unused imports 2025-11-27 13:37:53 -08:00
Mitchell Hashimoto
ec5bdf1a5a terminal: highlights 2025-11-24 19:55:27 -08:00
Mitchell Hashimoto
a90fe1656a terminal: RenderState 2025-11-20 22:00:42 -08:00
Mitchell Hashimoto
de545eeae1 lib-vt: export stream.Action for custom streams 2025-11-14 16:01:57 -08:00
Mitchell Hashimoto
3aff5f0aff ScreenSet 2025-11-14 15:08:10 -08:00
Mitchell Hashimoto
17f2dc59fa terminal: formatter that can emit VT sequences (#9374)
This adds a new formatter that can be used with standard Zig `{f}`
formatting that emits any portion of the terminal screen as VT
sequences. In addition to simply styling, this can emit the entire
terminal/screen state such as cursor positions, active style, terminal
modes, etc.

To do this, I've extracted all formatting to a dedicated `formatter`
package within `terminal`. This handles all formatting types (currently
plaintext and VT formatting, but can imagine things like HTML in the
future). Presently, we have "formatting" split out across a variety of
places in Terminal, Screen, PageList, and Page. I didn't remove this
code yet but I intend to unify it all on formatter in the future.

This also doesn't expose this functionality in any user-facing way yet.
This PR just adds it to the ghostty-vt Zig module and unit tests it.
Ghostty app changes will come later.

**This also improves the readonly stream** to handle OSC color
operations for _setting_ but it doesn't emit any responses of course,
since its readonly.
2025-10-28 07:23:00 -07:00
Mitchell Hashimoto
a82ad89ef3 lib-vt: C API for SGR parser (#9352)
This exposes the SGR parser to the C and Wasm APIs. An example is shown
in c-vt-sgr.

Compressed example:

```c
#include <assert.h>
#include <stdio.h>
#include <ghostty/vt.h>

int main() {
  // Create parser
  GhosttySgrParser parser;
  assert(ghostty_sgr_new(NULL, &parser) == GHOSTTY_SUCCESS);

  // Parse: ESC[1;31m (bold + red foreground)
  uint16_t params[] = {1, 31};
  assert(ghostty_sgr_set_params(parser, params, NULL, 2) == GHOSTTY_SUCCESS);

  printf("Parsing: ESC[1;31m\n\n");

  // Iterate through attributes
  GhosttySgrAttribute attr;
  while (ghostty_sgr_next(parser, &attr)) {
    switch (attr.tag) {
      case GHOSTTY_SGR_ATTR_BOLD:
        printf("✓ Bold enabled\n");
        break;
      case GHOSTTY_SGR_ATTR_FG_8:
        printf("✓ Foreground color: %d (red)\n", attr.value.fg_8);
        break;
      default:
        break;
    }
  }

  ghostty_sgr_free(parser);
  return 0;
}
```

**AI disclosure:** Amp wrote most of the C headers, but I verified it
all. https://ampcode.com/threads/T-d9f145cb-e6ef-48a8-ad63-e5fc85c0d43e
2025-10-25 21:26:06 -07:00
Mitchell Hashimoto
580262c96f terminal: add ReadonlyStream that updates terminal state (#9346)
This adds a new stream handler implementation that updates terminal
state in reaction to VT sequences, but doesn't perform any of the
actions that would require responses (e.g. queries).

This is exposed in two ways: first, as a standalone `ReadonlyStream` and
`ReadonlyHandler` type that contains all the implementation. Second, as
a convenience func on `Terminal` as `vtStream` and `vtHandler` which
return their respective types preconfigured to update the calling
terminal state.

This dramatically simplifies libghostty-vt usage from Zig (and will
eventually be exposed to C, too) since a Terminal on its own is ready to
go as a full VT parser and state machine without needing to build any
custom types!

There's a second big bonus here which is that our `stream_readonly.zig`
tests are true end-to-end tests for raw bytes to terminal state. This
will let us test a wider variety of situations more broadly. To start,
there are only a handful of tests implemented here.

**AI disclosure:** Amp wrote basically this whole thing, but I reviewed
it. https://ampcode.com/threads/T-3490efd2-1137-4112-96f6-4bf8a0141ff5
2025-10-25 14:52:33 -07:00
Mitchell Hashimoto
5ba451d073 terminal: configureCharset 2025-10-24 11:27:48 -07:00
Mitchell Hashimoto
e1b527fb9a core: PageList tracks minimum metadata for rendering a scrollbar (#9225)
Related to #111

This adds the necessary logic and data for the `PageList` data structure
to keep track of **total length** of the screen, **offset** into the
viewport, and **length** of the viewport. These three values are
necessary to _render_ a scrollbar. This PR updates the renderer to grab
this information but stops short of actually drawing a scrollbar (which
we'll do with native UI), in the interest of having a PR that doesn't
contain too many changes.

**This doesn't yet draw a scrollbar, these are just the internal changes
necessary to support it.**

## Background

The `PageList` structure is very core to how we represent terminal
state. It maintains a doubly linked list of "pages" (not literally
virtual memory pages, but close). Each page stores cell information,
styles, hyperlinks, etc fully self-contained in a contiguous sets of VM
pages using offset addresses rather than full pointers. **Pages are not
guaranteed to be equal sizes.** (This is where scrollbars get difficult)

Because it is a linked list structure of non-equal sized nodes, it isn't
amenable to typical scrollbar behavior. A scrollbar needs to know: full
size, offset, and length in order to draw the scrollbar properly.
Getting these values naively is `O(N)` within the data structure that is
on the hottest IO performance path in all of Ghostty.

## Implementation

### PageList

We now maintain two cached values for **total length** and **viewport
offset**.

The total length is relatively straightforward, we just have to be
careful to update it in every operation that could add or remove rows.
I've done this and ensured that every place we update it is covered with
unit test coverage.

The viewport offset is nasty, but I came up with what I believe is a
good solution. The viewport when arbitrarily scrolled is defined as a
direct pointer to the linked list node plus a row offset into that node.
The only way to calculate offset from the top is `O(N)`.

But we have a couple shortcuts:

1. If the viewport is at the bottom (most common) or top, calculating
the offset is `O(1)`: bottom is `total_rows - active_rows`, both readily
available. And top is `0` by definition.

2. Operations on the PageList typically add or remove rows. We don't do
arbitrary linked list surgery. If we instrument those areas with delta
updates to our cache, we can avoid the `O(N)` cost for most operations,
including scrolling a scrollbar. The only expensive operation is a full,
arbitrary jump (new node pointer).

Point 1 was quick to implement, so I focused all the complexity on point
2. Whenever we have an operation that adds or removes rows (for example
pruning the scroll back, adding more, erase rows within the active area,
etc.) then I do the math to calculate the delta change required for the
offset if we've already calculated it, and apply that directly.

### Renderer

The other issue was how to notify the apprts of scrollbar state. Sending
messages on any terminal change within the IO thread is a non-option
because (1) sending messages is slow (2) the terminal changes a lot and
(3) any slowness in the IO thread slows down overall terminal
throughput.

The solution was to **trigger scrollbar notifications with the renderer
vsync**. We read the scrollbar information when we render a frame,
compare it to renderer previous state, and if the scrollbar changed,
send a message to the apprt _after the frame is GPU-renderer_.

The renderer spends _most_ of its time sleeping compared to the IO
thread, and has more opportunities for optimizing its awake time.
Additionally, there's no reason to update the scrollbar information if
the renderer hasn't rendered the new frames because the user can't even
see the stuff the scrollbar wants to scroll to. We're talking about
millisecond scale stuff here at worst but it adds up.

## Performance

No noticeable performance impact for the additional metrics:

<img width="1012" height="738" alt="image"
src="https://github.com/user-attachments/assets/4ed0a3e8-6d76-40c1-b249-e34041c2f6fd"
/>

## AI Usage

I used Amp to help audit the codebase and write tests. I wrote all the
main implementation code manually. I came up with the main design
myself. Relevant threads:

-
https://ampcode.com/threads/T-95fff686-75bb-4553-a2fb-e41fe4cd4b77#message-0-block-0
-
https://ampcode.com/threads/T-48e9a288-b280-4eec-83b7-ca73d029b4ef#message-91-block-0

## Future

This is just the internal changes necessary to _draw_ a scrollbar. There
will be other changes we'll need to add to handle grabbing and actually
jumping the scrollbar. I have a good idea of how to implement those
performantly as well.
2025-10-15 19:42:49 -07:00
Mitchell Hashimoto
d4dcecb071 Move paste encoding to the input package, test, optimize away one alloc
This moves our paste logic to `src/input` in preparation for exposing
this as part of libghostty-vt. This yields an immediate benefit of
unit tests for paste encoding. 

Additionally, we were able to remove one allocation on every unbracketed
paste path unless the input specifically contains a newline. Unlikely to
be noticable, but nice.

NOTE: This also includes one change in behavior: we no longer encode
`\r\n` and a single `\r`, but as a duplicate `\r\r`. This matches xterm
behavior and I don't think will result in any issues since duplicate
carriage returns should do nothing in well-behaved terminals.
2025-10-04 14:05:32 -07:00
Mitchell Hashimoto
f614fb7c1b build: use build options to configure terminal C ABI mode
Fixes various issues:

- C ABI detection was faulty, which caused some Zig programs to use
the C ABI mode and some C programs not to. Let's be explicit.

- Unit tests now tests C ABI mode.

- Build binary no longer rebuilds on any terminal change (a regression).

- Zig programs can choose to depend on the C ABI version of the terminal
  lib by using the `ghostty-vt-c` module.
2025-09-28 14:17:51 -07:00
Mitchell Hashimoto
cfe9f19454 lib-vt: expose command type enum 2025-09-28 07:21:46 -07:00
Mitchell Hashimoto
397e47c274 terminal: use LibEnum for the command keys 2025-09-27 14:32:49 -07:00
Mitchell Hashimoto
8a1dc5bd97 terminal: shuffle some C APIs to make it more long term maintainable 2025-09-27 13:20:54 -07:00
Mitchell Hashimoto
4d165fbaaa remove unused items 2025-09-24 11:06:23 -07:00
Mitchell Hashimoto
b006101ddd lib-vt: boilerplate to build a shared object 2025-09-23 12:35:39 -07:00
Mitchell Hashimoto
1b46884e72 terminal: add build option for oniguruma, which controls tmux cc mode 2025-09-21 19:39:59 -07:00
Mitchell Hashimoto
c5f921bb06 apprt/embedded: improve text reading APIs (selection, random points) 2025-06-15 07:59:19 -07:00
Mitchell Hashimoto
61c5fb8115 terminal: single pagelist node search 2024-12-03 15:53:12 -08:00
Mitchell Hashimoto
853ba9e3c7 terminal: reset should preserve desired default mode values
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.
2024-11-29 14:42:01 -08:00
Mitchell Hashimoto
d8f43b34ba terminal: yeet usingnamespace 2024-08-16 10:32:43 -07:00
Mitchell Hashimoto
ccf62a4960 stylistic nitpicks 2024-08-10 11:03:56 -07:00
Jeffrey C. Ollie
ce5e55d4aa Implement the XTWINOPS (CSI t) control sequences that "make sense".
These sequences were implemented:

CSI 14 t - report the text area size in pixels
CSI 16 t - report the cell size in pixels
CSI 18 t - report the text area size in cells
CSI 21 t - report the window title

These sequences were not implemented because they manuipulate the window
state in ways that we do not want.

CSI 1 t
CSI 2 t
CSI 3 ; x ; y t
CSI 4 ; height ; width ; t
CSI 5 t
CSI 6 t
CSI 7 t
CSI 8 ; height ; width ; t
CSI 9 ; 0 t
CSI 9 ; 1 t
CSI 9 ; 2 t
CSI 9 ; 3 t
CSI 10 ; 0 t
CSI 10 ; 1 t
CSI 10 ; 2 t
CSI 24 t

These sequences were not implemented because they do not make sense in
a Wayland context:

CSI 11 t
CSI 13 t
CSI 14 ; 2 t

These sequences were not implemented because they provide information
about the screen that is unnecessary.

CSI 15 t
CSI 19 t

These sequences were not implemeted because Ghostty does not maintain an
icon title for windows.

CSI 20 t
CSI 22 ; 0 t
CSI 22 ; 1 t
CSI 23 ; 0 t
CSI 23 ; 1 t

These sequences were not implemented because of the additional
complexity of maintaining a stack of window titles.

CSI 22 ; 2 t
CSI 23 ; 2 t
2024-08-07 00:12:20 -05:00
Mitchell Hashimoto
8c3559ecff terminal: move tmux control mode parsing out to dedicated file 2024-07-12 09:49:59 -07:00
Mitchell Hashimoto
d1f41e2035 terminal: hyperlink start/end on screen 2024-07-05 21:39:55 -07:00
Mitchell Hashimoto
15b7a37cf9 terminal: export Coordinate 2024-04-25 19:07:10 -07:00
Mitchell Hashimoto
0bc831d19f terminal: relax grapheme integrity check for fast paths 2024-03-22 20:28:22 -07:00
Mitchell Hashimoto
d664840b7f terminal: add StringMap back 2024-03-22 20:28:05 -07:00
Mitchell Hashimoto
bca51ee771 terminal: selectionString takes a struct for opts 2024-03-22 20:28:05 -07:00
Mitchell Hashimoto
9015b7548f inspector: support cell pinning again 2024-03-22 20:28:04 -07:00
Mitchell Hashimoto
8ccc30da10 core: surface now tracks left click pin 2024-03-22 20:27:55 -07:00
Mitchell Hashimoto
25d84d697a termio/exec: get compiler errors gone 2024-03-22 20:27:44 -07:00
Mitchell Hashimoto
9b4ab0e209 zig build test with renamed terminal package 2024-03-22 20:27:44 -07:00
Mitchell Hashimoto
0e62076f52 Revert "terminal: remove new import"
This reverts commit 7dbac298ff834ec927186891eed91974042e970d.
2024-03-22 20:27:40 -07:00
Mitchell Hashimoto
a972a885ce terminal: remove new import 2024-03-22 20:27:40 -07:00
Mitchell Hashimoto
6b364f81c0 terminal: todo for paged-terminal 2024-03-22 20:27:40 -07:00
Mitchell Hashimoto
100e6ed254 terminal/new => terminal2 so we can figure out what depends on what 2024-03-22 20:27:36 -07:00
Mitchell Hashimoto
ff4a0fce7f terminal/new: add scrollViewport 2024-03-22 20:27:35 -07:00