Commit Graph

89 Commits

Author SHA1 Message Date
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
Mitchell Hashimoto
7ad94caaeb bench/page-init 2024-03-22 20:27:19 -07:00
Mitchell Hashimoto
dc6de51472 terminal/new: add bench 2024-03-22 20:27:17 -07:00
Mitchell Hashimoto
a1c14d1859 terminal/new: print single lines of ascii chars lol 2024-03-22 20:27:17 -07:00
Mitchell Hashimoto
b5d7b0a87a terminal/new: lots of code thrown at the wall 2024-03-22 20:27:17 -07:00
Mitchell Hashimoto
1473b3edf2 terminal/new: PageList 2024-03-22 20:27:17 -07:00
Mitchell Hashimoto
1216603e68 terminal/new: Screen beginnings 2024-03-22 20:27:17 -07:00
Mitchell Hashimoto
18810f89f7 terminal: copy stdlib hash_map 2024-03-22 20:27:15 -07:00
Mitchell Hashimoto
be8745c70a terminal: bunch of junk for paged terminal 2024-03-22 20:27:14 -07:00
Mitchell Hashimoto
986fa34d3e terminal: remove unused const 2024-02-02 13:28:19 -08:00
Mitchell Hashimoto
3c26828a3f terminal: distinguish between DSRs with "?" and not 2024-02-02 12:42:22 -08:00
Mitchell Hashimoto
cf8763561d terminal: use comptime generated X11 lookup table from rgb.txt 2024-01-10 20:36:47 -08:00