Merge remote-tracking branch 'origin/main' into close-split-gtk

# Conflicts:
#	po/hu.po
#	po/id.po
pull/11173/head
Caleb 2026-03-25 16:24:11 -07:00
commit bcf12937d0
369 changed files with 25011 additions and 4443 deletions

View File

@ -1,64 +0,0 @@
#!/usr/bin/env nu
# A command to generate an agent prompt to diagnose and formulate
# a plan for resolving a GitHub issue.
#
# IMPORTANT: This command is prompted to NOT write any code and to ONLY
# produce a plan. You should still be vigilant when running this but that
# is the expected behavior.
#
# The `<issue>` parameter can be either an issue number or a full GitHub
# issue URL.
def main [
issue: any, # Ghostty issue number or URL
--repo: string = "ghostty-org/ghostty" # GitHub repository in the format "owner/repo"
] {
# TODO: This whole script doesn't handle errors very well. I actually
# don't know Nu well enough to know the proper way to handle it all.
let issueData = gh issue view $issue --json author,title,number,body,comments | from json
let comments = $issueData.comments | each { |comment|
$"
### Comment by ($comment.author.login)
($comment.body)
" | str trim
} | str join "\n\n"
$"
Deep-dive on this GitHub issue. Find the problem and generate a plan.
Do not write code. Explain the problem clearly and propose a comprehensive plan
to solve it.
# ($issueData.title) \(($issueData.number)\)
## Description
($issueData.body)
## Comments
($comments)
## Your Tasks
You are an experienced software developer tasked with diagnosing issues.
1. Review the issue context and details.
2. Examine the relevant parts of the codebase. Analyze the code thoroughly
until you have a solid understanding of how it works.
3. Explain the issue in detail, including the problem and its root cause.
4. Create a comprehensive plan to solve the issue. The plan should include:
- Required code changes
- Potential impacts on other parts of the system
- Necessary tests to be written or updated
- Documentation updates
- Performance considerations
- Security implications
- Backwards compatibility \(if applicable\)
- Include the reference link to the source issue and any related discussions
4. Think deeply about all aspects of the task. Consider edge cases, potential
challenges, and best practices for addressing the issue. Review the plan
with the oracle and adjust it based on its feedback.
**ONLY CREATE A PLAN. DO NOT WRITE ANY CODE.** Your task is to create
a thorough, comprehensive strategy for understanding and resolving the issue.
" | str trim
}

View File

@ -0,0 +1,62 @@
---
name: writing-commit-messages
description: >-
Writes Git commit messages. Activates when the user asks to write
a commit message, draft a commit message, or similar.
---
# Writing Commit Messages
Write commit messages that follow commit style guidelines for the project.
## Format
```
<subsystem>: <summary>
<reference issues/PRs/etc.>
<long form description>
```
## Rules
### Subject line
- **Subsystem prefix**: Use a short, lowercase identifier for the
area of code changed (e.g., `terminal`, `vt`, `lib`, `config`,
`font`). Determine this from the file paths in the diff. If
changes span the macOS app, use `macos`. For GTK, use `gtk`. For
build system, use `build`. Use nested subsystems with `/` when
helpful and exclusive (e.g., `terminal/osc`).
- **Summary**: Lowercase start (not capitalized), imperative mood,
no trailing period. Keep it concise—ideally under 60 characters
total for the whole subject line.
### References
- If the change relates to a GitHub issue, PR, or discussion, list
the relevant numbers on their own lines after the subject, separated
by a blank line. E.g. `#1234`
- If there are no references, omit this section entirely (no blank
line).
### Long form description
- Describe **what changed**, **what the previous behavior was**,
and **how the new behavior works** at a high level.
- Use plain prose, not bullet points. Wrap lines at ~72 characters.
- Focus on the _why_ and _how_ rather than restating the diff.
- Keep the tone direct and technical without no filler phrases.
- Don't exceed a handful of paragraphs; less is more.
## Workflow
- If `.jj` is present, use `jj` instead of `git` for all commands.
- Run a diff to see what changes are present since the last commit.
- Identify the subsystem from the changed file paths.
- Identify any referenced issues/PRs from the diff context or
branch name.
- Draft the commit message following the format above.
- Apply the commit
- Don't push the commit; leave that to the user.

1
.gitattributes vendored
View File

@ -12,3 +12,4 @@ src/font/nerd_font_attributes.zig linguist-generated=true
src/font/nerd_font_codepoint_tables.py linguist-generated=true
src/font/res/** linguist-vendored
src/terminal/res/** linguist-vendored
src/terminal/res/rgb.txt -text

47
.github/VOUCHED.td vendored
View File

@ -19,6 +19,7 @@
# discussion by the author. Maintainers can denounce users by commenting
# "!denounce" or "!denounce [username]" on a discussion.
00-kat
04cb
aalhendi
abdurrahmanski
abudvytis
@ -28,8 +29,12 @@ alaasdk
alanmoyano
alexfeijoo44
alexjuca
alosarjos
amadeus
andrejdaskalov
anhthang
anmitalidev
anthonyzhoon
atomk
balazs-szucs
bennettp123
@ -40,24 +45,36 @@ bitigchi
bkircher
bo2themax
brentschroeter
brianc442
cespare
charliie-dev
chernetskyi
chronologos
cmwetherell
crayxt
craziestowl
curtismoncoq
d-dudas
daiimus
damyanbogoev
danulqua
dariogriffo
davidsanchez222
deblasis
dervedro
devsunb
diaaeddin
dmehala
doprz
douglance
douglas
drepper
dzhlobo
ekaterinepapava
elias8
ephemera
eriksremess
faukah
filip7
flou
francescarpi
@ -69,12 +86,20 @@ gpanders
guilhermetk
hakonhagland
halosatrio
heaths
heddxh heddxh
-highimpact-dev Disrespectful AI user
hlcfan
hqnna
hulet
icodesign
j0hnm4r5
jacobsandlund
jake-stewart
jcollie
jesusvazquez
jguthmiller
jmcgover
johnslavik
josephmart
jparise
@ -85,6 +110,7 @@ khipp
kirwiisp
kjvdven
kloneets
-kody-w
koranir
kristina8888
kristofersoler
@ -92,12 +118,18 @@ laxystem
liby
linustalacko
lonsagisawa
luisnquin
lynicis
mac0ne
mahnokropotkinvich
marijagjorgjieva
markdorison
markhuot
marler8997
marrocco-simone
matkotiric
micaeljarniac
michielvk
miguelelgallo
mihi314
mikailmm
@ -105,6 +137,7 @@ misairuzame
mischief
mitchellh
miupa
molechowski
mrmage
mtak
natesmyth
@ -113,17 +146,23 @@ nicosuave
nmggithub
noib3
nwehg
ocean6954
oshdubh
paaloeye
pan93412
pangoraw
pauley-unsaturated
peilingjiang
peterdavehello
philocalyst
phush0
piedrahitac
pluiedev
pouwerkerk
poweruser64
prakhar54-byte
priyans-hu
puzza007
qwerasd205
reo101
rgehan
@ -133,18 +172,26 @@ rmunn
rockorager
rpfaeffle
secrus
seruman
silveirapf
slsrepo
sunshine-syz
tdgroot
tdslot
ticclick
tnagatomi
trag1c
tristan957
turbolent
tweedbeetle
uhojin
unphased
uzaaft
vaughanandrews
viruslobster
vlsi
wyounas
yamshta
ydah
zenyr
zeshi09

View File

@ -30,7 +30,7 @@ jobs:
runs-on: ${{ matrix.variant.runner }}
steps:
- name: Download Source Tarball Artifacts
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
run-id: ${{ inputs.source-run-id }}
artifact-ids: ${{ inputs.source-artifact-id }}

View File

@ -11,12 +11,15 @@ on:
jobs:
update-milestone:
# Ignore bot-authored pull requests (dependabot, app bots, etc)
# and CI-only PRs.
if: github.event_name == 'issues' || (github.event.pull_request.user.type != 'Bot' && !startsWith(github.event.pull_request.title, 'ci:'))
runs-on: namespace-profile-ghostty-sm
name: Milestone Update
steps:
- name: Set Milestone for PR
uses: hustcer/milestone-action@ebed8d5daafd855a600d7e665c1b130f06d24130 # v3.1
if: github.event.pull_request.merged == true
if: github.event.pull_request.merged == true && !contains(github.event.pull_request.title, 'VOUCHED') && !startsWith(github.event.pull_request.title, 'ci:')
with:
action: bind-pr # `bind-pr` is the default action
github-token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -47,10 +47,10 @@ jobs:
/nix
/zig
- name: Setup Nix
uses: cachix/install-nix-action@19effe9fe722874e6d46dd7182e4b8b7a43c4a99 # v31.10.0
uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"

View File

@ -89,11 +89,11 @@ jobs:
/nix
/zig
- uses: cachix/install-nix-action@19effe9fe722874e6d46dd7182e4b8b7a43c4a99 # v31.10.0
- uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -147,7 +147,7 @@ jobs:
- uses: DeterminateSystems/nix-installer-action@main
with:
determinate: true
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -299,7 +299,7 @@ jobs:
curl -sL https://sentry.io/get-cli/ | bash
- name: Download macOS Artifacts
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: macos
@ -322,7 +322,7 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Download macOS Artifacts
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: macos
@ -370,17 +370,17 @@ jobs:
GHOSTTY_VERSION: ${{ needs.setup.outputs.version }}
steps:
- name: Download macOS Artifacts
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: macos
- name: Download Sparkle Artifacts
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: sparkle
- name: Download Source Tarball Artifacts
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: source-tarball

View File

@ -42,10 +42,10 @@ jobs:
with:
path: |
/nix
- uses: cachix/install-nix-action@19effe9fe722874e6d46dd7182e4b8b7a43c4a99 # v31.10.0
- uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -175,10 +175,10 @@ jobs:
path: |
/nix
/zig
- uses: cachix/install-nix-action@19effe9fe722874e6d46dd7182e4b8b7a43c4a99 # v31.10.0
- uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -195,7 +195,7 @@ jobs:
nix develop -c minisign -S -m ghostty-source.tar.gz -s minisign.key < minisign.password
- name: Update Release
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
with:
name: 'Ghostty Tip ("Nightly")'
prerelease: true
@ -245,7 +245,7 @@ jobs:
- uses: DeterminateSystems/nix-installer-action@main
with:
determinate: true
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -378,7 +378,7 @@ jobs:
# Update Release
- name: Release
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
with:
name: 'Ghostty Tip ("Nightly")'
prerelease: true
@ -501,7 +501,7 @@ jobs:
- uses: DeterminateSystems/nix-installer-action@main
with:
determinate: true
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -627,7 +627,7 @@ jobs:
# Update Release
- name: Release
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
with:
name: 'Ghostty Tip ("Nightly")'
prerelease: true
@ -698,7 +698,7 @@ jobs:
- uses: DeterminateSystems/nix-installer-action@main
with:
determinate: true
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -824,7 +824,7 @@ jobs:
# Update Release
- name: Release
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
with:
name: 'Ghostty Tip ("Nightly")'
prerelease: true

View File

@ -26,7 +26,7 @@ jobs:
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
steps:
- name: Download Source Tarball Artifacts
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
run-id: ${{ inputs.source-run-id }}
artifact-ids: ${{ inputs.source-artifact-id }}

View File

@ -31,19 +31,19 @@ jobs:
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: filter_every
with:
token: ${{ secrets.GITHUB_TOKEN }}
token: ""
predicate-quantifier: "every"
filters: |
code:
- '**'
- '!.github/VOUCHED.td'
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: filter_any
with:
token: ${{ secrets.GITHUB_TOKEN }}
token: ""
filters: |
macos:
- '.swiftlint.yml'
@ -85,24 +85,28 @@ jobs:
- skip
- build-bench
- build-dist
- build-examples
- build-examples-zig
- build-examples-cmake
- build-examples-cmake-windows
- build-cmake
- build-flatpak
- build-libghostty-vt
- build-libghostty-vt-android
- build-libghostty-vt-macos
- build-libghostty-vt-windows
- build-linux
- build-linux-libghostty
- build-nix
- build-macos
- build-macos-freetype
- build-snap
- build-windows
- test
- test-simd
- test-gtk
- test-sentry-linux
- test-i18n
- test-fuzz-libghostty
- test-lib-vt
- test-macos
- pinact
- prettier
@ -156,10 +160,10 @@ jobs:
/zig
# Install Nix and use that to run our tests so our environment matches exactly.
- uses: cachix/install-nix-action@19effe9fe722874e6d46dd7182e4b8b7a43c4a99 # v31.10.0
- uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -167,21 +171,123 @@ jobs:
- name: Build Benchmarks
run: nix develop -c zig build -Demit-bench
build-examples:
list-examples:
if: github.repository == 'ghostty-org/ghostty' && needs.skip.outputs.skip != 'true'
needs: skip
runs-on: namespace-profile-ghostty-xsm
outputs:
zig: ${{ steps.list.outputs.zig }}
cmake: ${{ steps.list.outputs.cmake }}
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- id: list
name: List example directories
run: |
zig=$(ls example/*/build.zig.zon 2>/dev/null | xargs -n1 dirname | xargs -n1 basename | jq -R -s -c 'split("\n") | map(select(. != ""))')
echo "$zig" | jq .
echo "zig=$zig" >> "$GITHUB_OUTPUT"
cmake=$(ls example/*/CMakeLists.txt 2>/dev/null | xargs -n1 dirname | xargs -n1 basename | jq -R -s -c 'split("\n") | map(select(. != ""))')
echo "$cmake" | jq .
echo "cmake=$cmake" >> "$GITHUB_OUTPUT"
build-examples-zig:
strategy:
fail-fast: false
matrix:
dir:
[
c-vt,
c-vt-key-encode,
c-vt-paste,
c-vt-sgr,
zig-formatter,
zig-vt,
zig-vt-stream,
]
dir: ${{ fromJSON(needs.list-examples.outputs.zig) }}
name: Example ${{ matrix.dir }}
runs-on: namespace-profile-ghostty-xsm
needs: [test, list-examples]
env:
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Cache
uses: namespacelabs/nscloud-cache-action@a90bb5d4b27522ce881c6e98eebd7d7e6d1653f9 # v1.4.2
with:
path: |
/nix
/zig
# Install Nix and use that to run our tests so our environment matches exactly.
- uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- name: Build Example
run: |
cd example/${{ matrix.dir }}
nix develop -c zig build
build-examples-cmake:
strategy:
fail-fast: false
matrix:
dir: ${{ fromJSON(needs.list-examples.outputs.cmake) }}
name: Example ${{ matrix.dir }}
runs-on: namespace-profile-ghostty-xsm
needs: [test, list-examples]
env:
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Cache
uses: namespacelabs/nscloud-cache-action@a90bb5d4b27522ce881c6e98eebd7d7e6d1653f9 # v1.4.2
with:
path: |
/nix
/zig
# Install Nix and use that to run our tests so our environment matches exactly.
- uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- name: Build Example
run: |
cd example/${{ matrix.dir }}
nix develop -c cmake -B build -DFETCHCONTENT_SOURCE_DIR_GHOSTTY=${{ github.workspace }}
nix develop -c cmake --build build
build-examples-cmake-windows:
strategy:
fail-fast: false
matrix:
dir: ${{ fromJSON(needs.list-examples.outputs.cmake) }}
name: Example ${{ matrix.dir }} (Windows)
runs-on: windows-2025
timeout-minutes: 45
needs: [test, list-examples]
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Install zig
uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1
- name: Build Example
shell: pwsh
run: |
cd example/${{ matrix.dir }}
cmake -B build -DFETCHCONTENT_SOURCE_DIR_GHOSTTY=${{ github.workspace }}
cmake --build build
build-cmake:
runs-on: namespace-profile-ghostty-sm
needs: test
env:
@ -199,18 +305,25 @@ jobs:
/zig
# Install Nix and use that to run our tests so our environment matches exactly.
- uses: cachix/install-nix-action@19effe9fe722874e6d46dd7182e4b8b7a43c4a99 # v31.10.0
- uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- name: Build Example
- name: Build
run: |
cd example/${{ matrix.dir }}
nix develop -c zig build
nix develop -c cmake -B build
nix develop -c cmake --build build
- name: Verify artifacts
run: |
test -f zig-out/lib/libghostty-vt.so.0.1.0
test -d zig-out/include/ghostty
ls -la zig-out/lib/
ls -la zig-out/include/ghostty/
build-flatpak:
strategy:
@ -232,10 +345,10 @@ jobs:
/zig
# Install Nix and use that to run our tests so our environment matches exactly.
- uses: cachix/install-nix-action@19effe9fe722874e6d46dd7182e4b8b7a43c4a99 # v31.10.0
- uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -266,10 +379,10 @@ jobs:
/zig
# Install Nix and use that to run our tests so our environment matches exactly.
- uses: cachix/install-nix-action@19effe9fe722874e6d46dd7182e4b8b7a43c4a99 # v31.10.0
- uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -310,21 +423,21 @@ jobs:
/zig
# Install Nix and use that to run our tests so our environment matches exactly.
- uses: cachix/install-nix-action@19effe9fe722874e6d46dd7182e4b8b7a43c4a99 # v31.10.0
- uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- name: Build
run: |
nix develop -c zig build lib-vt \
nix develop -c zig build -Demit-lib-vt \
-Dtarget=${{ matrix.target }} \
-Dsimd=false
# lib-vt requires macOS runner for macOS/iOS builds becauase it requires the `apple_sdk` path
# lib-vt requires macOS runner for macOS/iOS builds because it requires the `apple_sdk` path
build-libghostty-vt-macos:
strategy:
matrix:
@ -350,7 +463,7 @@ jobs:
- uses: DeterminateSystems/nix-installer-action@main
with:
determinate: true
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -360,7 +473,7 @@ jobs:
- name: Build
run: |
nix develop -c zig build lib-vt \
nix develop -c zig build -Demit-lib-vt \
-Dtarget=${{ matrix.target }}
# lib-vt requires the Android NDK for Android builds
@ -387,10 +500,10 @@ jobs:
/zig
# Install Nix and use that to run our tests so our environment matches exactly.
- uses: cachix/install-nix-action@19effe9fe722874e6d46dd7182e4b8b7a43c4a99 # v31.10.0
- uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -406,11 +519,28 @@ jobs:
- name: Build
run: |
nix develop -c zig build lib-vt \
nix develop -c zig build -Demit-lib-vt \
-Dtarget=${{ matrix.target }}
env:
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
build-libghostty-vt-windows:
runs-on: windows-2025
timeout-minutes: 45
needs: test
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Zig
uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1
- name: Test libghostty-vt
run: zig build test-lib-vt
- name: Build libghostty-vt
run: zig build -Demit-lib-vt
build-linux:
strategy:
fail-fast: false
@ -433,10 +563,10 @@ jobs:
/zig
# Install Nix and use that to run our tests so our environment matches exactly.
- uses: cachix/install-nix-action@19effe9fe722874e6d46dd7182e4b8b7a43c4a99 # v31.10.0
- uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -462,10 +592,10 @@ jobs:
/zig
# Install Nix and use that to run our tests so our environment matches exactly.
- uses: cachix/install-nix-action@19effe9fe722874e6d46dd7182e4b8b7a43c4a99 # v31.10.0
- uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -495,10 +625,10 @@ jobs:
/zig
# Install Nix and use that to run our tests so our environment matches exactly.
- uses: cachix/install-nix-action@19effe9fe722874e6d46dd7182e4b8b7a43c4a99 # v31.10.0
- uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -541,10 +671,10 @@ jobs:
/zig
# Install Nix and use that to run our tests so our environment matches exactly.
- uses: cachix/install-nix-action@19effe9fe722874e6d46dd7182e4b8b7a43c4a99 # v31.10.0
- uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -621,7 +751,7 @@ jobs:
- uses: DeterminateSystems/nix-installer-action@main
with:
determinate: true
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -681,7 +811,7 @@ jobs:
- uses: DeterminateSystems/nix-installer-action@main
with:
determinate: true
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -696,83 +826,16 @@ jobs:
id: deps
run: nix build -L .#deps && echo "deps=$(readlink ./result)" >> $GITHUB_OUTPUT
# We run tests with an empty test filter so it runs all unit tests
# but skips Xcode tests
- name: Test All
run: |
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Drenderer=metal -Dfont-backend=coretext_freetype
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Drenderer=metal -Dfont-backend=coretext_freetype -Dtest-filter=""
- name: Build All
run: |
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Demit-macos-app=false -Drenderer=metal -Dfont-backend=coretext_freetype
build-windows:
runs-on: windows-2022
# this will not stop other jobs from running
continue-on-error: true
timeout-minutes: 45
needs: test
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
# This could be from a script if we wanted to but inlining here for now
# in one place.
# Using powershell so that we do not need to install WSL components. Also,
# WSLv1 is only installed on Github runners.
- name: Install zig
shell: pwsh
run: |
# Get the zig version from build.zig.zon so that it only needs to be updated
$fileContent = Get-Content -Path "build.zig.zon" -Raw
$pattern = 'minimum_zig_version\s*=\s*"([^"]+)"'
$zigVersion = [regex]::Match($fileContent, $pattern).Groups[1].Value
$version = "zig-x86_64-windows-$zigVersion"
Write-Output $version
$uri = "https://ziglang.org/download/$zigVersion/$version.zip"
Invoke-WebRequest -Uri "$uri" -OutFile ".\zig-windows.zip"
Expand-Archive -Path ".\zig-windows.zip" -DestinationPath ".\" -Force
Remove-Item -Path ".\zig-windows.zip"
Rename-Item -Path ".\$version" -NewName ".\zig"
Write-Host "Zig installed."
.\zig\zig.exe version
- name: Generate build testing script
shell: pwsh
run: |
# Generate a script so that we can swallow the errors
$scriptContent = @"
.\zig\zig.exe build test 2>&1 | Out-File -FilePath "build.log" -Append
exit 0
"@
$scriptPath = "zigbuild.ps1"
# Write the script content to a file
$scriptContent | Set-Content -Path $scriptPath
Write-Host "Script generated at: $scriptPath"
- name: Test Windows
shell: pwsh
run: .\zigbuild.ps1 -ErrorAction SilentlyContinue
- name: Generate build script
shell: pwsh
run: |
# Generate a script so that we can swallow the errors
$scriptContent = @"
.\zig\zig.exe build 2>&1 | Out-File -FilePath "build.log" -Append
exit 0
"@
$scriptPath = "zigbuild.ps1"
# Write the script content to a file
$scriptContent | Set-Content -Path $scriptPath
Write-Host "Script generated at: $scriptPath"
- name: Build Windows
shell: pwsh
run: .\zigbuild.ps1 -ErrorAction SilentlyContinue
- name: Dump logs
shell: pwsh
run: Get-Content -Path ".\build.log"
test:
if: github.repository == 'ghostty-org/ghostty' && needs.skip.outputs.skip != 'true'
needs: skip
@ -799,10 +862,10 @@ jobs:
/zig
# Install Nix and use that to run our tests so our environment matches exactly.
- uses: cachix/install-nix-action@19effe9fe722874e6d46dd7182e4b8b7a43c4a99 # v31.10.0
- uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -817,6 +880,36 @@ jobs:
- name: Test System Build
run: nix develop -c zig build --system ${ZIG_GLOBAL_CACHE_DIR}/p
test-lib-vt:
if: github.repository == 'ghostty-org/ghostty' && needs.skip.outputs.skip != 'true'
needs: skip
runs-on: namespace-profile-ghostty-md
env:
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Cache
uses: namespacelabs/nscloud-cache-action@a90bb5d4b27522ce881c6e98eebd7d7e6d1653f9 # v1.4.2
with:
path: |
/nix
/zig
# Install Nix and use that to run our tests so our environment matches exactly.
- uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- name: Test
run: nix develop -c zig build test-lib-vt
test-gtk:
strategy:
fail-fast: false
@ -841,10 +934,10 @@ jobs:
/zig
# Install Nix and use that to run our tests so our environment matches exactly.
- uses: cachix/install-nix-action@19effe9fe722874e6d46dd7182e4b8b7a43c4a99 # v31.10.0
- uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -889,10 +982,10 @@ jobs:
/zig
# Install Nix and use that to run our tests so our environment matches exactly.
- uses: cachix/install-nix-action@19effe9fe722874e6d46dd7182e4b8b7a43c4a99 # v31.10.0
- uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -924,10 +1017,10 @@ jobs:
/zig
# Install Nix and use that to run our tests so our environment matches exactly.
- uses: cachix/install-nix-action@19effe9fe722874e6d46dd7182e4b8b7a43c4a99 # v31.10.0
- uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -958,7 +1051,7 @@ jobs:
- uses: DeterminateSystems/nix-installer-action@main
with:
determinate: true
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -999,10 +1092,10 @@ jobs:
/zig
# Install Nix and use that to run our tests so our environment matches exactly.
- uses: cachix/install-nix-action@19effe9fe722874e6d46dd7182e4b8b7a43c4a99 # v31.10.0
- uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -1030,10 +1123,10 @@ jobs:
/zig
# Install Nix and use that to run our tests so our environment matches exactly.
- uses: cachix/install-nix-action@19effe9fe722874e6d46dd7182e4b8b7a43c4a99 # v31.10.0
- uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -1072,10 +1165,10 @@ jobs:
path: |
/nix
/zig
- uses: cachix/install-nix-action@19effe9fe722874e6d46dd7182e4b8b7a43c4a99 # v31.10.0
- uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -1103,10 +1196,10 @@ jobs:
path: |
/nix
/zig
- uses: cachix/install-nix-action@19effe9fe722874e6d46dd7182e4b8b7a43c4a99 # v31.10.0
- uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -1133,10 +1226,10 @@ jobs:
path: |
/nix
/zig
- uses: cachix/install-nix-action@19effe9fe722874e6d46dd7182e4b8b7a43c4a99 # v31.10.0
- uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -1165,7 +1258,7 @@ jobs:
- uses: DeterminateSystems/nix-installer-action@main
with:
determinate: true
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -1191,10 +1284,10 @@ jobs:
path: |
/nix
/zig
- uses: cachix/install-nix-action@19effe9fe722874e6d46dd7182e4b8b7a43c4a99 # v31.10.0
- uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -1219,10 +1312,10 @@ jobs:
path: |
/nix
/zig
- uses: cachix/install-nix-action@19effe9fe722874e6d46dd7182e4b8b7a43c4a99 # v31.10.0
- uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -1247,10 +1340,10 @@ jobs:
path: |
/nix
/zig
- uses: cachix/install-nix-action@19effe9fe722874e6d46dd7182e4b8b7a43c4a99 # v31.10.0
- uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -1280,10 +1373,10 @@ jobs:
path: |
/nix
/zig
- uses: cachix/install-nix-action@19effe9fe722874e6d46dd7182e4b8b7a43c4a99 # v31.10.0
- uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -1308,10 +1401,10 @@ jobs:
path: |
/nix
/zig
- uses: cachix/install-nix-action@19effe9fe722874e6d46dd7182e4b8b7a43c4a99 # v31.10.0
- uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -1345,10 +1438,10 @@ jobs:
/zig
# Install Nix and use that to run our tests so our environment matches exactly.
- uses: cachix/install-nix-action@19effe9fe722874e6d46dd7182e4b8b7a43c4a99 # v31.10.0
- uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
@ -1364,13 +1457,13 @@ jobs:
needs: [test, build-dist]
steps:
- name: Install and configure Namespace CLI
uses: namespacelabs/nscloud-setup@f378676225212387f1283f4da878712af2c4cd60 # v0.0.11
uses: namespacelabs/nscloud-setup@df198f982fcecfb8264bea3f1274b56a61b6dfdc # v0.0.12
- name: Configure Namespace powered Buildx
uses: namespacelabs/nscloud-setup-buildx-action@f5814dcf37a16cce0624d5bec2ab879654294aa0 # v0.0.22
uses: namespacelabs/nscloud-setup-buildx-action@d059ed7184f0bc7c8b27e8810cea153d02bcc6dd # v0.0.23
- name: Download Source Tarball Artifacts
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: source-tarball
@ -1380,7 +1473,7 @@ jobs:
tar --verbose --extract --strip-components 1 --directory dist --file ghostty-source.tar.gz
- name: Build and push
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
with:
context: dist
file: dist/src/build/docker/debian/Dockerfile
@ -1407,10 +1500,10 @@ jobs:
/zig
# Install Nix and use that to run our tests so our environment matches exactly.
- uses: cachix/install-nix-action@19effe9fe722874e6d46dd7182e4b8b7a43c4a99 # v31.10.0
- uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"

View File

@ -29,10 +29,10 @@ jobs:
/zig
- name: Setup Nix
uses: cachix/install-nix-action@19effe9fe722874e6d46dd7182e4b8b7a43c4a99 # v31.10.0
uses: cachix/install-nix-action@51f3067b56fe8ae331890c77d4e454f6d60615ff # v31.10.2
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
- uses: cachix/cachix-action@1eb2ef646ac0255473d23a5907ad7b04ce94065c # v17
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"

View File

@ -8,7 +8,7 @@ jobs:
check:
runs-on: namespace-profile-ghostty-xsm
steps:
- uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
- uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0
id: app-token
with:
app-id: ${{ secrets.VOUCH_APP_ID }}

View File

@ -8,7 +8,7 @@ jobs:
check:
runs-on: namespace-profile-ghostty-xsm
steps:
- uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
- uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0
id: app-token
with:
app-id: ${{ secrets.VOUCH_APP_ID }}

View File

@ -12,7 +12,7 @@ jobs:
manage:
runs-on: namespace-profile-ghostty-xsm
steps:
- uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
- uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0
id: app-token
with:
app-id: ${{ secrets.VOUCH_APP_ID }}

View File

@ -12,7 +12,7 @@ jobs:
manage:
runs-on: namespace-profile-ghostty-xsm
steps:
- uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
- uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0
id: app-token
with:
app-id: ${{ secrets.VOUCH_APP_ID }}

View File

@ -13,7 +13,7 @@ jobs:
sync:
runs-on: namespace-profile-ghostty-xsm
steps:
- uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
- uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0
id: app-token
with:
app-id: ${{ secrets.VOUCH_APP_ID }}

4
.gitignore vendored
View File

@ -10,6 +10,9 @@
zig-cache/
.zig-cache/
zig-out/
build-cmake/
CMakeCache.txt
CMakeFiles/
/build.zig.zon.bak
/result*
/.nixos-test-history
@ -25,3 +28,4 @@ glad.zip
/ghostty.qcow2
vgcore.*

View File

@ -11,6 +11,9 @@ zig-out/
# macos is managed by XCode GUI
macos/
# Xcode asset catalogs
**/*.xcassets/
# produced by Icon Composer on macOS
images/Ghostty.icon/icon.json

View File

@ -5,16 +5,20 @@ A file for [guiding coding agents](https://agents.md/).
## Commands
- **Build:** `zig build`
- If you're on macOS and don't need to build the macOS app, use
`-Demit-macos-app=false` to skip building the app bundle and speed up
compilation.
- **Test (Zig):** `zig build test`
- Prefer to run targeted tests with `-Dtest-filter` because the full
test suite is slow to run.
- **Test filter (Zig)**: `zig build test -Dtest-filter=<test name>`
- **Formatting (Zig)**: `zig fmt .`
- **Formatting (Swift)**: `swiftlint lint --fix`
- **Formatting (Swift)**: `swiftlint lint --strict --fix`
- **Formatting (other)**: `prettier -w .`
## Directory Structure
- Shared Zig core: `src/`
- C API: `include`
- macOS app: `macos/`
- GTK (Linux and FreeBSD) app: `src/apprt/gtk`

230
CMakeLists.txt Normal file
View File

@ -0,0 +1,230 @@
# CMake wrapper for libghostty-vt
#
# This file delegates to `zig build -Demit-lib-vt` to produce the shared library,
# headers, and pkg-config file. It exists so that CMake-based projects can
# consume libghostty-vt without interacting with the Zig build system
# directly. However, downstream users do still require `zig` on the PATH.
# Please consult the Ghostty docs for the required Zig version:
#
# https://ghostty.org/docs/install/build
#
# Building within the Ghostty repo
# ---------------------------------
#
# cmake -B build
# cmake --build build
# cmake --install build --prefix /usr/local
#
# Pass extra flags to the Zig build with GHOSTTY_ZIG_BUILD_FLAGS:
#
# cmake -B build -DGHOSTTY_ZIG_BUILD_FLAGS="-Demit-macos-app=false"
#
# Integrating into a downstream CMake project
# ---------------------------------------------
#
# Option 1 FetchContent (recommended, no manual install step):
#
# include(FetchContent)
# FetchContent_Declare(ghostty
# GIT_REPOSITORY https://github.com/ghostty-org/ghostty.git
# GIT_TAG main
# )
# FetchContent_MakeAvailable(ghostty)
#
# target_link_libraries(myapp PRIVATE ghostty-vt) # shared
# target_link_libraries(myapp PRIVATE ghostty-vt-static) # static
#
# To use a local checkout instead of fetching:
#
# cmake -B build -DFETCHCONTENT_SOURCE_DIR_GHOSTTY=/path/to/ghostty
#
# Option 2 find_package (after installing to a prefix):
#
# find_package(ghostty-vt REQUIRED)
# target_link_libraries(myapp PRIVATE ghostty-vt::ghostty-vt) # shared
# target_link_libraries(myapp PRIVATE ghostty-vt::ghostty-vt-static) # static
#
# See dist/cmake/README.md for more details and example/c-vt-cmake/ for a
# complete working example.
cmake_minimum_required(VERSION 3.19)
project(ghostty-vt VERSION 0.1.0 LANGUAGES C)
# --- Options ----------------------------------------------------------------
set(GHOSTTY_ZIG_BUILD_FLAGS "" CACHE STRING "Additional flags to pass to zig build")
# Map CMake build types to Zig optimization levels.
if(CMAKE_BUILD_TYPE)
string(TOUPPER "${CMAKE_BUILD_TYPE}" _bt)
if(_bt STREQUAL "RELEASE" OR _bt STREQUAL "MINSIZEREL" OR _bt STREQUAL "RELWITHDEBINFO")
list(APPEND GHOSTTY_ZIG_BUILD_FLAGS "-Doptimize=ReleaseFast")
endif()
unset(_bt)
endif()
# --- Find Zig ----------------------------------------------------------------
find_program(ZIG_EXECUTABLE zig REQUIRED)
message(STATUS "Found zig: ${ZIG_EXECUTABLE}")
# --- Build via zig build -----------------------------------------------------
# The zig build installs into zig-out/ relative to the source tree.
set(ZIG_OUT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/zig-out")
# Shared library names (zig build produces both shared and static).
if(APPLE)
set(GHOSTTY_VT_LIBNAME "${CMAKE_SHARED_LIBRARY_PREFIX}ghostty-vt${CMAKE_SHARED_LIBRARY_SUFFIX}")
set(GHOSTTY_VT_SONAME "${CMAKE_SHARED_LIBRARY_PREFIX}ghostty-vt.0${CMAKE_SHARED_LIBRARY_SUFFIX}")
set(GHOSTTY_VT_REALNAME "${CMAKE_SHARED_LIBRARY_PREFIX}ghostty-vt.0.1.0${CMAKE_SHARED_LIBRARY_SUFFIX}")
elseif(WIN32)
set(GHOSTTY_VT_LIBNAME "ghostty-vt.dll")
set(GHOSTTY_VT_REALNAME "ghostty-vt.dll")
set(GHOSTTY_VT_IMPLIB "ghostty-vt.lib")
else()
set(GHOSTTY_VT_LIBNAME "${CMAKE_SHARED_LIBRARY_PREFIX}ghostty-vt${CMAKE_SHARED_LIBRARY_SUFFIX}")
set(GHOSTTY_VT_SONAME "${CMAKE_SHARED_LIBRARY_PREFIX}ghostty-vt${CMAKE_SHARED_LIBRARY_SUFFIX}.0")
set(GHOSTTY_VT_REALNAME "${CMAKE_SHARED_LIBRARY_PREFIX}ghostty-vt${CMAKE_SHARED_LIBRARY_SUFFIX}.0.1.0")
endif()
if(WIN32)
set(GHOSTTY_VT_SHARED_LIBRARY "${ZIG_OUT_DIR}/bin/${GHOSTTY_VT_REALNAME}")
else()
set(GHOSTTY_VT_SHARED_LIBRARY "${ZIG_OUT_DIR}/lib/${GHOSTTY_VT_REALNAME}")
endif()
# Static library name.
# On Windows, the static lib is named "ghostty-vt-static.lib" to avoid
# colliding with the DLL import library "ghostty-vt.lib".
if(WIN32)
set(GHOSTTY_VT_STATIC_REALNAME "ghostty-vt-static.lib")
else()
set(GHOSTTY_VT_STATIC_REALNAME "libghostty-vt.a")
endif()
set(GHOSTTY_VT_STATIC_LIBRARY "${ZIG_OUT_DIR}/lib/${GHOSTTY_VT_STATIC_REALNAME}")
# Ensure the output directories exist so CMake doesn't reject the
# INTERFACE_INCLUDE_DIRECTORIES before the zig build has run.
file(MAKE_DIRECTORY "${ZIG_OUT_DIR}/include")
# Custom command: run zig build -Demit-lib-vt (produces both shared and static)
add_custom_command(
OUTPUT "${GHOSTTY_VT_SHARED_LIBRARY}" "${GHOSTTY_VT_STATIC_LIBRARY}" "${ZIG_OUT_DIR}/lib/${GHOSTTY_VT_IMPLIB}"
COMMAND "${ZIG_EXECUTABLE}" build -Demit-lib-vt ${GHOSTTY_ZIG_BUILD_FLAGS}
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
COMMENT "Building libghostty-vt via zig build..."
USES_TERMINAL
)
add_custom_target(zig_build_lib_vt ALL
DEPENDS "${GHOSTTY_VT_SHARED_LIBRARY}" "${GHOSTTY_VT_STATIC_LIBRARY}"
)
# Tell CMake's clean target to also remove Zig's output directory.
set_property(DIRECTORY APPEND PROPERTY
ADDITIONAL_CLEAN_FILES "${ZIG_OUT_DIR}"
)
# --- IMPORTED library targets ------------------------------------------------
# Shared
add_library(ghostty-vt SHARED IMPORTED GLOBAL)
set_target_properties(ghostty-vt PROPERTIES
IMPORTED_LOCATION "${GHOSTTY_VT_SHARED_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${ZIG_OUT_DIR}/include"
)
if(APPLE)
set_target_properties(ghostty-vt PROPERTIES
IMPORTED_SONAME "@rpath/${GHOSTTY_VT_SONAME}"
)
elseif(WIN32)
set_target_properties(ghostty-vt PROPERTIES
IMPORTED_IMPLIB "${ZIG_OUT_DIR}/lib/${GHOSTTY_VT_IMPLIB}"
)
else()
set_target_properties(ghostty-vt PROPERTIES
IMPORTED_SONAME "${GHOSTTY_VT_SONAME}"
)
endif()
add_dependencies(ghostty-vt zig_build_lib_vt)
# Static
#
# When linking the static library, consumers must also link its transitive
# dependencies. By default (with SIMD enabled), these are:
# - libc
# - libc++ (or libstdc++ on Linux)
# - highway
# - simdutf
#
# Building with -Dsimd=false removes the C++ / highway / simdutf
# dependencies, leaving only libc.
add_library(ghostty-vt-static STATIC IMPORTED GLOBAL)
set_target_properties(ghostty-vt-static PROPERTIES
IMPORTED_LOCATION "${GHOSTTY_VT_STATIC_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${ZIG_OUT_DIR}/include"
)
if(WIN32)
# On Windows, the Zig standard library uses NT API functions
# (NtClose, NtCreateSection, etc.) and kernel32 functions that
# consumers must link when using the static library.
set_target_properties(ghostty-vt-static PROPERTIES
INTERFACE_LINK_LIBRARIES "ntdll;kernel32"
)
endif()
add_dependencies(ghostty-vt-static zig_build_lib_vt)
# --- Install ------------------------------------------------------------------
include(GNUInstallDirs)
# Install shared library
if(WIN32)
# On Windows, install the DLL and PDB to bin/ and the import library to lib/
install(FILES "${GHOSTTY_VT_SHARED_LIBRARY}" "${ZIG_OUT_DIR}/bin/ghostty-vt.pdb" TYPE BIN)
install(FILES "${ZIG_OUT_DIR}/lib/${GHOSTTY_VT_IMPLIB}" TYPE LIB)
else()
install(FILES "${GHOSTTY_VT_SHARED_LIBRARY}" TYPE LIB)
# Install symlinks
install(CODE "
execute_process(COMMAND \${CMAKE_COMMAND} -E create_symlink
\"${GHOSTTY_VT_REALNAME}\"
\"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/${GHOSTTY_VT_SONAME}\")
execute_process(COMMAND \${CMAKE_COMMAND} -E create_symlink
\"${GHOSTTY_VT_SONAME}\"
\"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/${GHOSTTY_VT_LIBNAME}\")
")
endif()
# Install static library
install(FILES "${GHOSTTY_VT_STATIC_LIBRARY}" TYPE LIB)
# Install headers
install(DIRECTORY "${ZIG_OUT_DIR}/include/ghostty" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
# --- CMake package config for find_package() ----------------------------------
include(CMakePackageConfigHelpers)
# Generate the config file
configure_package_config_file(
"${CMAKE_CURRENT_SOURCE_DIR}/dist/cmake/ghostty-vt-config.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/ghostty-vt-config.cmake"
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/ghostty-vt"
)
# Generate the version file
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/ghostty-vt-config-version.cmake"
VERSION "${PROJECT_VERSION}"
COMPATIBILITY SameMajorVersion
)
# Install the config files
install(
FILES
"${CMAKE_CURRENT_BINARY_DIR}/ghostty-vt-config.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/ghostty-vt-config-version.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/ghostty-vt"
)

View File

@ -169,6 +169,7 @@
/po/de.po @ghostty-org/de_DE
/po/es_AR.po @ghostty-org/es_AR
/po/es_BO.po @ghostty-org/es_BO
/po/es_ES.po @ghostty-org/es_ES
/po/fr.po @ghostty-org/fr_FR
/po/ga.po @ghostty-org/ga_IE
/po/he.po @ghostty-org/he_IL
@ -189,6 +190,7 @@
/po/ru.po @ghostty-org/ru_RU
/po/tr.po @ghostty-org/tr_TR
/po/uk.po @ghostty-org/uk_UA
/po/vi.po @ghostty-org/vi_VN
/po/zh_CN.po @ghostty-org/zh_CN
/po/zh_TW.po @ghostty-org/zh_TW

143
README.md
View File

@ -7,6 +7,8 @@
<p align="center">
Fast, native, feature-rich terminal emulator pushing modern features.
<br />
A native GUI or embeddable library via <code>libghostty</code>.
<br />
<a href="#about">About</a>
·
<a href="https://ghostty.org/download">Download</a>
@ -26,20 +28,13 @@ fast, feature-rich, and native. While there are many excellent terminal
emulators available, they all force you to choose between speed,
features, or native UIs. Ghostty provides all three.
In all categories, I am not trying to claim that Ghostty is the
best (i.e. the fastest, most feature-rich, or most native). But
Ghostty is competitive in all three categories and Ghostty
doesn't make you choose between them.
Ghostty also intends to push the boundaries of what is possible with a
terminal emulator by exposing modern, opt-in features that enable CLI tool
developers to build more feature rich, interactive applications.
While aiming for this ambitious goal, our first step is to make Ghostty
one of the best fully standards compliant terminal emulator, remaining
compatible with all existing shells and software while supporting all of
the latest terminal innovations in the ecosystem. You can use Ghostty
as a drop-in replacement for your existing terminal emulator.
**`libghostty`** is a cross-platform, zero-dependency C and Zig library
for building terminal emulators or utilizing terminal functionality
(such as style parsing). Anyone can use `libghostty` to build a terminal
emulator or embed a terminal into their own applications. See
[Ghostling](https://github.com/ghostty-org/ghostling) for a minimal complete project
example or the [`examples` directory](https://github.com/ghostty-org/ghostty/tree/main/example)
for smaller examples of using `libghostty` in C and Zig.
For more details, see [About Ghostty](https://ghostty.org/docs/about).
@ -61,30 +56,37 @@ to get involved with Ghostty's development as well should also read the
## Roadmap and Status
Ghostty is stable and in use by millions of people and machines daily.
The high-level ambitious plan for the project, in order:
| # | Step | Status |
| :-: | --------------------------------------------------------- | :----: |
| 1 | Standards-compliant terminal emulation | ✅ |
| 2 | Competitive performance | ✅ |
| 3 | Basic customizability -- fonts, bg colors, etc. | ✅ |
| 4 | Richer windowing features -- multi-window, tabbing, panes | ✅ |
| 5 | Native Platform Experiences (i.e. Mac Preference Panel) | ⚠️ |
| 6 | Cross-platform `libghostty` for Embeddable Terminals | ⚠️ |
| 7 | Windows Terminals (including PowerShell, Cmd, WSL) | ❌ |
| N | Fancy features (to be expanded upon later) | ❌ |
| # | Step | Status |
| :-: | ------------------------------------------------------- | :----: |
| 1 | Standards-compliant terminal emulation | ✅ |
| 2 | Competitive performance | ✅ |
| 3 | Rich windowing features -- multi-window, tabbing, panes | ✅ |
| 4 | Native Platform Experiences | ✅ |
| 5 | Cross-platform `libghostty` for Embeddable Terminals | ✅ |
| 6 | Ghostty-only Terminal Control Sequences | ❌ |
Additional details for each step in the big roadmap below:
#### Standards-Compliant Terminal Emulation
Ghostty implements enough control sequences to be used by hundreds of
testers daily for over the past year. Further, we've done a
[comprehensive xterm audit](https://github.com/ghostty-org/ghostty/issues/632)
Ghostty implements all of the regularly used control sequences and
can run every mainstream terminal program without issue. For legacy sequences,
we've done a [comprehensive xterm audit](https://github.com/ghostty-org/ghostty/issues/632)
comparing Ghostty's behavior to xterm and building a set of conformance
test cases.
We believe Ghostty is one of the most compliant terminal emulators available.
In addition to legacy sequences (what you'd call real "terminal" emulation),
Ghostty also supports more modern sequences than almost any other terminal
emulator. These features include things like the Kitty graphics protocol,
Kitty image protocol, clipboard sequences, synchronized rendering,
light/dark mode notifications, and many, many more.
We believe Ghostty is one of the most compliant and feature-rich terminal
emulators available.
Terminal behavior is partially a de jure standard
(i.e. [ECMA-48](https://ecma-international.org/publications-and-standards/standards/ecma-48/))
@ -96,33 +98,30 @@ views as a "standard."
#### Competitive Performance
We need better benchmarks to continuously verify this, but Ghostty is
generally in the same performance category as the other highest performing
terminal emulators.
Ghostty is generally in the same performance category as the other highest
performing terminal emulators.
For rendering, we have a multi-renderer architecture that uses OpenGL on
Linux and Metal on macOS. As far as I'm aware, we're the only terminal
emulator other than iTerm that uses Metal directly. And we're the only
terminal emulator that has a Metal renderer that supports ligatures (iTerm
uses a CPU renderer if ligatures are enabled). We can maintain around 60fps
under heavy load and much more generally -- though the terminal is
usually rendering much lower due to little screen changes.
"The same performance category" means that Ghostty is much faster than
traditional or "slow" terminals and is within an unnoticeable margin of the
well-known "fast" terminals. For example, Ghostty and Alacritty are usually within
a few percentage points of each other on various benchmarks, but are both
something like 100x faster than Terminal.app and iTerm. However, Ghostty
is much more feature rich than Alacritty and has a much more native app
experience.
For IO, we have a dedicated IO thread that maintains very little jitter
under heavy IO load (i.e. `cat <big file>.txt`). On benchmarks for IO,
we're usually within a small margin of other fast terminal emulators.
For example, reading a dump of plain text is 4x faster compared to iTerm and
Kitty, and 2x faster than Terminal.app. Alacritty is very fast but we're still
around the same speed (give or take) and our app experience is much more
feature rich.
This performance is achieved through high-level architectural decisions and
low-level optimizations. At a high-level, Ghostty has a multi-threaded
architecture with a dedicated read thread, write thread, and render thread
per terminal. Our renderer uses OpenGL on Linux and Metal on macOS.
Our read thread has a heavily optimized terminal parser that leverages
CPU-specific SIMD instructions. Etc.
> [!NOTE]
> Despite being _very fast_, there is a lot of room for improvement here.
#### Richer Windowing Features
#### Rich Windowing Features
The Mac and Linux (build with GTK) apps support multi-window, tabbing, and
splits.
splits with additional features such as tab renaming, coloring, etc. These
features allow for a higher degree of organization and customization than
single-window terminals.
#### Native Platform Experiences
@ -133,10 +132,15 @@ in Zig but we do a lot of platform-native things:
- The macOS app is a true SwiftUI-based application with all the things you
would expect such as real windowing, menu bars, a settings GUI, etc.
- macOS uses a true Metal renderer with CoreText for font discovery.
- macOS supports AppleScript, Apple Shortcuts (AppIntents), etc.
- The Linux app is built with GTK.
- The Linux app integrates deeply with systemd if available for things
like always-on, new windows in a single instance, cgroup isolation, etc.
There are more improvements to be made. The macOS settings window is still
a work-in-progress. Similar improvements will follow with Linux.
Our goal with Ghostty is for users of whatever platform they run Ghostty
on to think that Ghostty was built for their platform first and maybe even
exclusively. We want Ghostty to feel like a native app on every platform,
for the best definition of "native" on each platform.
#### Cross-platform `libghostty` for Embeddable Terminals
@ -151,15 +155,34 @@ terminal state. This is covered in more detail in this
[blog post](https://mitchellh.com/writing/libghostty-is-coming).
`libghostty-vt` is already available and usable today for Zig and C and
is compatible for macOS, Linux, Windows, and WebAssembly. At the time of
writing this, the API isn't stable yet and we haven't tagged an official
release, but the core logic is well proven (since Ghostty uses it) and
we're working hard on it now.
is compatible for macOS, Linux, Windows, and WebAssembly. The functionality
is extremely stable (since its been proven in Ghostty GUI for a long time),
but the API signatures are still in flux.
The ultimate goal is not hypothetical! The macOS app is a `libghostty` consumer.
The macOS app is a native Swift app developed in Xcode and `main()` is
within Swift. The Swift app links to `libghostty` and uses the C API to
render terminals.
`libghostty` is already heavily in use. See [`examples`](https://github.com/ghostty-org/ghostty/tree/main/example)
for small examples of using `libghostty` in C and Zig or the
[Ghostling](https://github.com/ghostty-org/ghostling) project for a
complete example. See [awesome-libghostty](https://github.com/Uzaaft/awesome-libghostty)
for a list of projects and resources related to `libghostty`.
We haven't tagged libghostty with a version yet and we're still working
on a better docs experience, but our [Doxygen website](https://libghostty.tip.ghostty.org/)
is a good resource for the C API.
#### Ghostty-only Terminal Control Sequences
We want and believe that terminal applications can and should be able
to do so much more. We've worked hard to support a wide variety of modern
sequences created by other terminal emulators towards this end, but we also
want to fill the gaps by creating our own sequences.
We've been hesitant to do this up until now because we don't want to create
more fragmentation in the terminal ecosystem by creating sequences that only
work in Ghostty. But, we do want to balance that with the desire to push the
terminal forward with stagnant standards and the slow pace of change in the
terminal ecosystem.
We haven't done any of this yet.
## Crash Reports

View File

@ -35,7 +35,6 @@ pub fn build(b: *std.Build) !void {
// All our steps which we'll hook up later. The steps are shown
// up here just so that they are more self-documenting.
const libvt_step = b.step("lib-vt", "Build libghostty-vt");
const run_step = b.step("run", "Run the app");
const run_valgrind_step = b.step(
"run-valgrind",
@ -91,16 +90,6 @@ pub fn build(b: *std.Build) !void {
check_step.dependOn(dist.install_step);
}
// libghostty (internal, big)
const libghostty_shared = try buildpkg.GhosttyLib.initShared(
b,
&deps,
);
const libghostty_static = try buildpkg.GhosttyLib.initStatic(
b,
&deps,
);
// libghostty-vt
const libghostty_vt_shared = shared: {
if (config.target.result.cpu.arch.isWasm()) {
@ -115,9 +104,31 @@ pub fn build(b: *std.Build) !void {
&mod,
);
};
libghostty_vt_shared.install(libvt_step);
libghostty_vt_shared.install(b.getInstallStep());
// libghostty-vt static lib
const libghostty_vt_static = try buildpkg.GhosttyLibVt.initStatic(
b,
&mod,
);
if (config.is_dep) {
// If we're a dependency, we need to install everything as-is
// so that dep.artifact("ghostty-vt-static") works.
libghostty_vt_static.install(b.getInstallStep());
} else {
// If we're not a dependency, we rename the static lib to
// be idiomatic. On Windows, we use a distinct name to avoid
// colliding with the DLL import library (ghostty-vt.lib).
const static_lib_name = if (config.target.result.os.tag == .windows)
"ghostty-vt-static.lib"
else
"libghostty-vt.a";
b.getInstallStep().dependOn(&b.addInstallLibFile(
libghostty_vt_static.output,
static_lib_name,
).step);
}
// Helpgen
if (config.emit_helpgen) deps.help_strings.install();
@ -128,26 +139,34 @@ pub fn build(b: *std.Build) !void {
resources.install();
if (i18n) |v| v.install();
}
} else {
// Libghostty
} else if (!config.emit_lib_vt) {
// The macOS Ghostty Library
//
// Note: libghostty is not stable for general purpose use. It is used
// heavily by Ghostty on macOS but it isn't built to be reusable yet.
// As such, these build steps are lacking. For example, the Darwin
// build only produces an xcframework.
// This is NOT libghostty (even though its named that for historical
// reasons). It is just the glue between Ghostty GUI on macOS and
// the full Ghostty GUI core.
const lib_shared = try buildpkg.GhosttyLib.initShared(b, &deps);
const lib_static = try buildpkg.GhosttyLib.initStatic(b, &deps);
// We shouldn't have this guard but we don't currently
// build on macOS this way ironically so we need to fix that.
if (!config.target.result.os.tag.isDarwin()) {
libghostty_shared.installHeader(); // Only need one header
libghostty_shared.install("libghostty.so");
libghostty_static.install("libghostty.a");
lib_shared.installHeader(); // Only need one header
if (config.target.result.os.tag == .windows) {
lib_shared.install("ghostty.dll");
lib_static.install("ghostty-static.lib");
} else {
lib_shared.install("libghostty.so");
lib_static.install("libghostty.a");
}
}
}
// macOS only artifacts. These will error if they're initialized for
// other targets.
if (config.target.result.os.tag.isDarwin()) {
if (config.target.result.os.tag.isDarwin() and
(config.emit_xcframework or config.emit_macos_app))
{
// Ghostty xcframework
const xcframework = try buildpkg.GhosttyXCFramework.init(
b,
@ -202,7 +221,9 @@ pub fn build(b: *std.Build) !void {
// On macOS we can run the macOS app. For "run" we always force
// a native-only build so that we can run as quickly as possible.
if (config.target.result.os.tag.isDarwin()) {
if (config.target.result.os.tag.isDarwin() and
(config.emit_xcframework or config.emit_macos_app))
{
const xcframework_native = try buildpkg.GhosttyXCFramework.init(
b,
&deps,

View File

@ -1,6 +1,6 @@
.{
.name = .ghostty,
.version = "1.3.0-dev",
.version = "1.3.2-dev",
.paths = .{""},
.fingerprint = 0x64407a2a0b4147e5,
.minimum_zig_version = "0.15.2",
@ -91,8 +91,8 @@
.lazy = true,
},
.wayland_protocols = .{
.url = "https://deps.files.ghostty.org/wayland-protocols-258d8f88f2c8c25a830c6316f87d23ce1a0f12d9.tar.gz",
.hash = "N-V-__8AAKw-DAAaV8bOAAGqA0-oD7o-HNIlPFYKRXSPT03S",
.url = "https://gitlab.freedesktop.org/wayland/wayland-protocols/-/archive/1.47/wayland-protocols-1.47.tar.gz",
.hash = "N-V-__8AAFdWDwA0ktbNUi9pFBHCRN4weXIgIfCrVjfGxqgA",
.lazy = true,
},
.plasma_wayland_protocols = .{

5
build.zig.zon.json generated
View File

@ -139,6 +139,11 @@
"url": "https://deps.files.ghostty.org/wayland-protocols-258d8f88f2c8c25a830c6316f87d23ce1a0f12d9.tar.gz",
"hash": "sha256-XO3K3egbdeYPI+XoO13SuOtO+5+Peb16NH0UiusFMPg="
},
"N-V-__8AAFdWDwA0ktbNUi9pFBHCRN4weXIgIfCrVjfGxqgA": {
"name": "wayland_protocols",
"url": "https://gitlab.freedesktop.org/wayland/wayland-protocols/-/archive/1.47/wayland-protocols-1.47.tar.gz",
"hash": "sha256-3S3xSrX0EDgleq7cxLX7msDuAY8/D5SvkJcCjmDTMiM="
},
"N-V-__8AAAzZywE3s51XfsLbP9eyEw57ae9swYB9aGB6fCMs": {
"name": "wuffs",
"url": "https://deps.files.ghostty.org/wuffs-122037b39d577ec2db3fd7b2130e7b69ef6cc1807d68607a7c232c958315d381b5cd.tar.gz",

8
build.zig.zon.nix generated
View File

@ -306,6 +306,14 @@ in
hash = "sha256-XO3K3egbdeYPI+XoO13SuOtO+5+Peb16NH0UiusFMPg=";
};
}
{
name = "N-V-__8AAFdWDwA0ktbNUi9pFBHCRN4weXIgIfCrVjfGxqgA";
path = fetchZigArtifact {
name = "wayland_protocols";
url = "https://gitlab.freedesktop.org/wayland/wayland-protocols/-/archive/1.47/wayland-protocols-1.47.tar.gz";
hash = "sha256-3S3xSrX0EDgleq7cxLX7msDuAY8/D5SvkJcCjmDTMiM=";
};
}
{
name = "N-V-__8AAAzZywE3s51XfsLbP9eyEw57ae9swYB9aGB6fCMs";
path = fetchZigArtifact {

1
build.zig.zon.txt generated
View File

@ -34,3 +34,4 @@ https://deps.files.ghostty.org/zig_wayland-1b5c038ec10da20ed3a15b0b2a6db1c21383e
https://deps.files.ghostty.org/zlib-1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb.tar.gz
https://github.com/ivanstepanovftw/zigimg/archive/d7b7ab0ba0899643831ef042bd73289510b39906.tar.gz
https://github.com/ocornut/imgui/archive/refs/tags/v1.92.5-docking.tar.gz
https://gitlab.freedesktop.org/wayland/wayland-protocols/-/archive/1.47/wayland-protocols-1.47.tar.gz

67
dist/cmake/README.md vendored Normal file
View File

@ -0,0 +1,67 @@
# CMake Support for libghostty-vt
The top-level `CMakeLists.txt` wraps the Zig build system so that CMake
projects can consume libghostty-vt without invoking `zig build` manually.
Running `cmake --build` triggers `zig build -Demit-lib-vt` automatically.
This means downstream projects do require a working Zig compiler on
`PATH` to build, but don't need to know any Zig-specific details.
## Using FetchContent (recommended)
Add the following to your project's `CMakeLists.txt`:
```cmake
include(FetchContent)
FetchContent_Declare(ghostty
GIT_REPOSITORY https://github.com/ghostty-org/ghostty.git
GIT_TAG main
)
FetchContent_MakeAvailable(ghostty)
add_executable(myapp main.c)
target_link_libraries(myapp PRIVATE ghostty-vt)
```
This fetches the Ghostty source, builds libghostty-vt via Zig during your
CMake build, and links it into your target. Headers are added to the
include path automatically.
### Using a local checkout
If you already have the Ghostty source checked out, skip the download by
pointing CMake at it:
```shell-session
cmake -B build -DFETCHCONTENT_SOURCE_DIR_GHOSTTY=/path/to/ghostty
cmake --build build
```
## Using find_package (install-based)
Build and install libghostty-vt first:
```shell-session
cd /path/to/ghostty
cmake -B build
cmake --build build
cmake --install build --prefix /usr/local
```
Then in your project:
```cmake
find_package(ghostty-vt REQUIRED)
add_executable(myapp main.c)
target_link_libraries(myapp PRIVATE ghostty-vt::ghostty-vt)
```
## Files
- `ghostty-vt-config.cmake.in` — template for the CMake package config
file installed alongside the library, enabling `find_package()` support.
## Example
See `example/c-vt-cmake/` for a complete working example.

65
dist/cmake/ghostty-vt-config.cmake.in vendored Normal file
View File

@ -0,0 +1,65 @@
@PACKAGE_INIT@
include(CMakeFindDependencyMacro)
set(_ghostty_vt_libdir "${PACKAGE_PREFIX_DIR}/@CMAKE_INSTALL_LIBDIR@")
# Shared library target
if(NOT TARGET ghostty-vt::ghostty-vt)
add_library(ghostty-vt::ghostty-vt SHARED IMPORTED)
if(WIN32)
set(_ghostty_vt_shared_location "${PACKAGE_PREFIX_DIR}/@CMAKE_INSTALL_BINDIR@/@GHOSTTY_VT_REALNAME@")
else()
set(_ghostty_vt_shared_location "${_ghostty_vt_libdir}/@GHOSTTY_VT_REALNAME@")
endif()
set_target_properties(ghostty-vt::ghostty-vt PROPERTIES
IMPORTED_LOCATION "${_ghostty_vt_shared_location}"
INTERFACE_INCLUDE_DIRECTORIES "${PACKAGE_PREFIX_DIR}/@CMAKE_INSTALL_INCLUDEDIR@"
)
unset(_ghostty_vt_shared_location)
if(APPLE)
set_target_properties(ghostty-vt::ghostty-vt PROPERTIES
IMPORTED_SONAME "@rpath/@GHOSTTY_VT_SONAME@"
INTERFACE_LINK_DIRECTORIES "${_ghostty_vt_libdir}"
)
# Ensure consumers can find the @rpath dylib at runtime
set_property(TARGET ghostty-vt::ghostty-vt APPEND PROPERTY
INTERFACE_LINK_OPTIONS "LINKER:-rpath,${_ghostty_vt_libdir}"
)
elseif(WIN32)
set_target_properties(ghostty-vt::ghostty-vt PROPERTIES
IMPORTED_IMPLIB "${_ghostty_vt_libdir}/@GHOSTTY_VT_IMPLIB@"
)
else()
set_target_properties(ghostty-vt::ghostty-vt PROPERTIES
IMPORTED_SONAME "@GHOSTTY_VT_SONAME@"
)
endif()
endif()
# Static library target
#
# Consumers must link transitive dependencies themselves. By default (with
# SIMD enabled): libc, libc++ (or libstdc++ on Linux), highway, and
# simdutf. Building with -Dsimd=false removes the C++ / highway / simdutf
# dependencies.
if(NOT TARGET ghostty-vt::ghostty-vt-static)
add_library(ghostty-vt::ghostty-vt-static STATIC IMPORTED)
set_target_properties(ghostty-vt::ghostty-vt-static PROPERTIES
IMPORTED_LOCATION "${_ghostty_vt_libdir}/@GHOSTTY_VT_STATIC_REALNAME@"
INTERFACE_INCLUDE_DIRECTORIES "${PACKAGE_PREFIX_DIR}/@CMAKE_INSTALL_INCLUDEDIR@"
)
if(WIN32)
set_target_properties(ghostty-vt::ghostty-vt-static PROPERTIES
INTERFACE_LINK_LIBRARIES "ntdll;kernel32"
)
endif()
endif()
unset(_ghostty_vt_libdir)
check_required_components(ghostty-vt)

View File

@ -52,6 +52,12 @@
<releases>
<!-- TODO: Generate this automatically -->
<release version="1.3.1" date="2026-03-13">
<url type="details">https://ghostty.org/docs/install/release-notes/1-3-1</url>
</release>
<release version="1.3.0" date="2026-03-09">
<url type="details">https://ghostty.org/docs/install/release-notes/1-3-0</url>
</release>
<release version="1.0.1" date="2024-12-31">
<url type="details">https://ghostty.org/docs/install/release-notes/1-0-1</url>
</release>

View File

@ -7,5 +7,4 @@ Actions=RunGhosttyDir
[Desktop Action RunGhosttyDir]
Name=Open Ghostty Here
Icon=com.mitchellh.ghostty
Exec=ghostty --working-directory=%F --gtk-single-instance=false
Exec=ghostty +new-window --working-directory=%F

1
example/.gitignore vendored
View File

@ -2,3 +2,4 @@
dist/
node_modules/
example.wasm*
build/

39
example/AGENTS.md Normal file
View File

@ -0,0 +1,39 @@
# Example Libghostty Projects
Each example is a standalone project with its own `build.zig`,
`build.zig.zon`, `README.md`, and `src/main.c` (or `.zig`). Examples are
auto-discovered by CI via `example/*/build.zig.zon`, so no workflow file
edits are needed when adding a new example.
## Adding a New Example
1. Copy an existing example directory (e.g., `c-vt-encode-focus/`) as a
starting point.
2. Update `build.zig.zon`: change `.name`, generate a **new unique**
`.fingerprint` value (a random `u64` hex literal), and keep
`.minimum_zig_version` matching the others.
3. Update `build.zig`: change the executable `.name` to match the directory.
4. Write a `README.md` following the existing format.
## Doxygen Snippet Tags
Example source files use Doxygen `@snippet` tags so the corresponding
header in `include/ghostty/vt/` can reference them. Wrap the relevant
code with `//! [snippet-name]` markers:
```c
//! [my-snippet]
int main() { ... }
//! [my-snippet]
```
The header then uses `@snippet <dir>/src/main.c my-snippet` instead of
inline `@code` blocks. Never duplicate example code inline in the
headers — always use `@snippet`. When modifying example code, keep the
snippet markers in sync with the headers in `include/ghostty/vt/`.
## Conventions
- Executable names use underscores: `c_vt_encode_focus` (not hyphens).
- All C examples link `ghostty-vt` via `lazyDependency("ghostty", ...)`.
- `build.zig` files follow a common template — keep them consistent.

17
example/README.md Normal file
View File

@ -0,0 +1,17 @@
# Examples
Standalone projects demonstrating the Ghostty library APIs.
The directories starting with `c-` use the C API and the directories
starting with `zig-` use the Zig API.
Every example can be built and run using `zig build` and `zig build run`
from within the respective example directory.
Even the C API examples use the Zig build system (not the language) to
build the project.
## Running an Example
```shell-session
cd example/<dir>
zig build run
```

View File

@ -0,0 +1,17 @@
# Example: `ghostty-vt` Build Info
This contains a simple example of how to use the `ghostty-vt` build info
API to query compile-time build configuration.
This uses a `build.zig` and `Zig` to build the C program so that we
can reuse a lot of our build logic and depend directly on our source
tree, but Ghostty emits a standard C library that can be used with any
C tooling.
## Usage
Run the program:
```shell-session
zig build run
```

View File

@ -29,7 +29,7 @@ pub fn build(b: *std.Build) void {
// Exe
const exe = b.addExecutable(.{
.name = "c_vt_key_encode",
.name = "c_vt_build_info",
.root_module = exe_mod,
});
b.installArtifact(exe);

View File

@ -0,0 +1,24 @@
.{
.name = .c_vt_build_info,
.version = "0.0.0",
.fingerprint = 0xc6b57ed4f83fb16,
.minimum_zig_version = "0.15.1",
.dependencies = .{
// Ghostty dependency. In reality, you'd probably use a URL-based
// dependency like the one showed (and commented out) below this one.
// We use a path dependency here for simplicity and to ensure our
// examples always test against the source they're bundled with.
.ghostty = .{ .path = "../../" },
// Example of what a URL-based dependency looks like:
// .ghostty = .{
// .url = "https://github.com/ghostty-org/ghostty/archive/COMMIT.tar.gz",
// .hash = "N-V-__8AAMVLTABmYkLqhZPLXnMl-KyN38R8UVYqGrxqO36s",
// },
},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
}

View File

@ -0,0 +1,23 @@
#include <stdio.h>
#include <ghostty/vt.h>
//! [build-info-query]
void query_build_info() {
bool simd = false;
bool kitty_graphics = false;
bool tmux_control_mode = false;
ghostty_build_info(GHOSTTY_BUILD_INFO_SIMD, &simd);
ghostty_build_info(GHOSTTY_BUILD_INFO_KITTY_GRAPHICS, &kitty_graphics);
ghostty_build_info(GHOSTTY_BUILD_INFO_TMUX_CONTROL_MODE, &tmux_control_mode);
printf("SIMD: %s\n", simd ? "enabled" : "disabled");
printf("Kitty graphics: %s\n", kitty_graphics ? "enabled" : "disabled");
printf("Tmux control mode: %s\n", tmux_control_mode ? "enabled" : "disabled");
}
//! [build-info-query]
int main() {
query_build_info();
return 0;
}

View File

@ -0,0 +1,13 @@
cmake_minimum_required(VERSION 3.19)
project(c-vt-cmake-static LANGUAGES C)
include(FetchContent)
FetchContent_Declare(ghostty
GIT_REPOSITORY https://github.com/ghostty-org/ghostty.git
GIT_TAG main
)
set(GHOSTTY_ZIG_BUILD_FLAGS "-Dsimd=false" CACHE STRING "" FORCE)
FetchContent_MakeAvailable(ghostty)
add_executable(c_vt_cmake_static src/main.c)
target_link_libraries(c_vt_cmake_static PRIVATE ghostty-vt-static)

View File

@ -0,0 +1,21 @@
# c-vt-cmake-static
Demonstrates consuming libghostty-vt as a **static** library from a CMake
project using `FetchContent`. Creates a terminal, writes VT sequences into
it, and formats the screen contents as plain text.
## Building
```shell-session
cd example/c-vt-cmake-static
cmake -B build
cmake --build build
./build/c_vt_cmake_static
```
To build against a local checkout instead of fetching from GitHub:
```shell-session
cmake -B build -DFETCHCONTENT_SOURCE_DIR_GHOSTTY=../..
cmake --build build
```

View File

@ -0,0 +1,52 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ghostty/vt.h>
int main() {
// Create a terminal with a small grid
GhosttyTerminal terminal;
GhosttyTerminalOptions opts = {
.cols = 80,
.rows = 24,
.max_scrollback = 0,
};
GhosttyResult result = ghostty_terminal_new(NULL, &terminal, opts);
assert(result == GHOSTTY_SUCCESS);
// Write some VT-encoded content into the terminal
const char *commands[] = {
"Hello from a \033[1mCMake\033[0m-built program (static)!\r\n",
"Line 2: \033[4munderlined\033[0m text\r\n",
"Line 3: \033[31mred\033[0m \033[32mgreen\033[0m \033[34mblue\033[0m\r\n",
};
for (size_t i = 0; i < sizeof(commands) / sizeof(commands[0]); i++) {
ghostty_terminal_vt_write(terminal, (const uint8_t *)commands[i],
strlen(commands[i]));
}
// Format the terminal contents as plain text
GhosttyFormatterTerminalOptions fmt_opts =
GHOSTTY_INIT_SIZED(GhosttyFormatterTerminalOptions);
fmt_opts.emit = GHOSTTY_FORMATTER_FORMAT_PLAIN;
fmt_opts.trim = true;
GhosttyFormatter formatter;
result = ghostty_formatter_terminal_new(NULL, &formatter, terminal, fmt_opts);
assert(result == GHOSTTY_SUCCESS);
uint8_t *buf = NULL;
size_t len = 0;
result = ghostty_formatter_format_alloc(formatter, NULL, &buf, &len);
assert(result == GHOSTTY_SUCCESS);
printf("Plain text (%zu bytes):\n", len);
fwrite(buf, 1, len, stdout);
printf("\n");
ghostty_free(NULL, buf, len);
ghostty_formatter_free(formatter);
ghostty_terminal_free(terminal);
return 0;
}

View File

@ -0,0 +1,12 @@
cmake_minimum_required(VERSION 3.19)
project(c-vt-cmake LANGUAGES C)
include(FetchContent)
FetchContent_Declare(ghostty
GIT_REPOSITORY https://github.com/ghostty-org/ghostty.git
GIT_TAG main
)
FetchContent_MakeAvailable(ghostty)
add_executable(c_vt_cmake src/main.c)
target_link_libraries(c_vt_cmake PRIVATE ghostty-vt)

View File

@ -0,0 +1,21 @@
# c-vt-cmake
Demonstrates consuming libghostty-vt from a CMake project using
`FetchContent`. Creates a terminal, writes VT sequences into it, and
formats the screen contents as plain text.
## Building
```shell-session
cd example/c-vt-cmake
cmake -B build
cmake --build build
./build/c_vt_cmake
```
To build against a local checkout instead of fetching from GitHub:
```shell-session
cmake -B build -DFETCHCONTENT_SOURCE_DIR_GHOSTTY=../..
cmake --build build
```

View File

@ -0,0 +1,52 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ghostty/vt.h>
int main() {
// Create a terminal with a small grid
GhosttyTerminal terminal;
GhosttyTerminalOptions opts = {
.cols = 80,
.rows = 24,
.max_scrollback = 0,
};
GhosttyResult result = ghostty_terminal_new(NULL, &terminal, opts);
assert(result == GHOSTTY_SUCCESS);
// Write some VT-encoded content into the terminal
const char *commands[] = {
"Hello from a \033[1mCMake\033[0m-built program!\r\n",
"Line 2: \033[4munderlined\033[0m text\r\n",
"Line 3: \033[31mred\033[0m \033[32mgreen\033[0m \033[34mblue\033[0m\r\n",
};
for (size_t i = 0; i < sizeof(commands) / sizeof(commands[0]); i++) {
ghostty_terminal_vt_write(terminal, (const uint8_t *)commands[i],
strlen(commands[i]));
}
// Format the terminal contents as plain text
GhosttyFormatterTerminalOptions fmt_opts =
GHOSTTY_INIT_SIZED(GhosttyFormatterTerminalOptions);
fmt_opts.emit = GHOSTTY_FORMATTER_FORMAT_PLAIN;
fmt_opts.trim = true;
GhosttyFormatter formatter;
result = ghostty_formatter_terminal_new(NULL, &formatter, terminal, fmt_opts);
assert(result == GHOSTTY_SUCCESS);
uint8_t *buf = NULL;
size_t len = 0;
result = ghostty_formatter_format_alloc(formatter, NULL, &buf, &len);
assert(result == GHOSTTY_SUCCESS);
printf("Plain text (%zu bytes):\n", len);
fwrite(buf, 1, len, stdout);
printf("\n");
ghostty_free(NULL, buf, len);
ghostty_formatter_free(formatter);
ghostty_terminal_free(terminal);
return 0;
}

View File

@ -0,0 +1,18 @@
# Example: `ghostty-vt` Terminal Effects
This contains a simple example of how to register and use terminal
effect callbacks (`write_pty`, `bell`, `title_changed`) with the
`ghostty-vt` C library.
This uses a `build.zig` and `Zig` to build the C program so that we
can reuse a lot of our build logic and depend directly on our source
tree, but Ghostty emits a standard C library that can be used with any
C tooling.
## Usage
Run the program:
```shell-session
zig build run
```

View File

@ -0,0 +1,42 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const run_step = b.step("run", "Run the app");
const exe_mod = b.createModule(.{
.target = target,
.optimize = optimize,
});
exe_mod.addCSourceFiles(.{
.root = b.path("src"),
.files = &.{"main.c"},
});
// You'll want to use a lazy dependency here so that ghostty is only
// downloaded if you actually need it.
if (b.lazyDependency("ghostty", .{
// Setting simd to false will force a pure static build that
// doesn't even require libc, but it has a significant performance
// penalty. If your embedding app requires libc anyway, you should
// always keep simd enabled.
// .simd = false,
})) |dep| {
exe_mod.linkLibrary(dep.artifact("ghostty-vt"));
}
// Exe
const exe = b.addExecutable(.{
.name = "c_vt_effects",
.root_module = exe_mod,
});
b.installArtifact(exe);
// Run
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| run_cmd.addArgs(args);
run_step.dependOn(&run_cmd.step);
}

View File

@ -0,0 +1,24 @@
.{
.name = .c_vt_effects,
.version = "0.0.0",
.fingerprint = 0xc02634cd65f5b583,
.minimum_zig_version = "0.15.1",
.dependencies = .{
// Ghostty dependency. In reality, you'd probably use a URL-based
// dependency like the one showed (and commented out) below this one.
// We use a path dependency here for simplicity and to ensure our
// examples always test against the source they're bundled with.
.ghostty = .{ .path = "../../" },
// Example of what a URL-based dependency looks like:
// .ghostty = .{
// .url = "https://github.com/ghostty-org/ghostty/archive/COMMIT.tar.gz",
// .hash = "N-V-__8AAMVLTABmYkLqhZPLXnMl-KyN38R8UVYqGrxqO36s",
// },
},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
}

View File

@ -0,0 +1,97 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <ghostty/vt.h>
//! [effects-write-pty]
void on_write_pty(GhosttyTerminal terminal,
void* userdata,
const uint8_t* data,
size_t len) {
(void)terminal;
(void)userdata;
printf(" write_pty (%zu bytes): ", len);
fwrite(data, 1, len, stdout);
printf("\n");
}
//! [effects-write-pty]
//! [effects-bell]
void on_bell(GhosttyTerminal terminal, void* userdata) {
(void)terminal;
int* count = (int*)userdata;
(*count)++;
printf(" bell! (count=%d)\n", *count);
}
//! [effects-bell]
//! [effects-title-changed]
void on_title_changed(GhosttyTerminal terminal, void* userdata) {
(void)userdata;
// Query the cursor position to confirm the terminal processed the
// title change (the title itself is tracked by the embedder via the
// OSC parser or its own state).
uint16_t col = 0;
ghostty_terminal_get(terminal, GHOSTTY_TERMINAL_DATA_CURSOR_X, &col);
printf(" title changed (cursor at col %u)\n", col);
}
//! [effects-title-changed]
//! [effects-register]
int main() {
// Create a terminal
GhosttyTerminal terminal = NULL;
GhosttyTerminalOptions opts = {
.cols = 80,
.rows = 24,
.max_scrollback = 0,
};
if (ghostty_terminal_new(NULL, &terminal, opts) != GHOSTTY_SUCCESS) {
fprintf(stderr, "Failed to create terminal\n");
return 1;
}
// Set up userdata — a simple bell counter
int bell_count = 0;
ghostty_terminal_set(terminal, GHOSTTY_TERMINAL_OPT_USERDATA, &bell_count);
// Register effect callbacks
ghostty_terminal_set(terminal, GHOSTTY_TERMINAL_OPT_WRITE_PTY,
(const void *)on_write_pty);
ghostty_terminal_set(terminal, GHOSTTY_TERMINAL_OPT_BELL,
(const void *)on_bell);
ghostty_terminal_set(terminal, GHOSTTY_TERMINAL_OPT_TITLE_CHANGED,
(const void *)on_title_changed);
// Feed VT data that triggers effects:
// 1. Bell (BEL = 0x07)
printf("Sending BEL:\n");
const uint8_t bel = 0x07;
ghostty_terminal_vt_write(terminal, &bel, 1);
// 2. Title change (OSC 2 ; <title> ST)
printf("Sending title change:\n");
const char* title_seq = "\x1B]2;Hello Effects\x1B\\";
ghostty_terminal_vt_write(terminal, (const uint8_t*)title_seq,
strlen(title_seq));
// 3. Device status report (DECRQM for wraparound mode ?7)
// triggers write_pty with the response
printf("Sending DECRQM query:\n");
const char* decrqm = "\x1B[?7$p";
ghostty_terminal_vt_write(terminal, (const uint8_t*)decrqm,
strlen(decrqm));
// 4. Another bell to show the counter increments
printf("Sending another BEL:\n");
ghostty_terminal_vt_write(terminal, &bel, 1);
printf("Total bells: %d\n", bell_count);
ghostty_terminal_free(terminal);
return 0;
}
//! [effects-register]

View File

@ -0,0 +1,17 @@
# Example: `ghostty-vt` Encode Focus
This contains a simple example of how to use the `ghostty-vt` focus
encoding API to encode focus gained/lost events into escape sequences.
This uses a `build.zig` and `Zig` to build the C program so that we
can reuse a lot of our build logic and depend directly on our source
tree, but Ghostty emits a standard C library that can be used with any
C tooling.
## Usage
Run the program:
```shell-session
zig build run
```

View File

@ -0,0 +1,42 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const run_step = b.step("run", "Run the app");
const exe_mod = b.createModule(.{
.target = target,
.optimize = optimize,
});
exe_mod.addCSourceFiles(.{
.root = b.path("src"),
.files = &.{"main.c"},
});
// You'll want to use a lazy dependency here so that ghostty is only
// downloaded if you actually need it.
if (b.lazyDependency("ghostty", .{
// Setting simd to false will force a pure static build that
// doesn't even require libc, but it has a significant performance
// penalty. If your embedding app requires libc anyway, you should
// always keep simd enabled.
// .simd = false,
})) |dep| {
exe_mod.linkLibrary(dep.artifact("ghostty-vt"));
}
// Exe
const exe = b.addExecutable(.{
.name = "c_vt_encode_focus",
.root_module = exe_mod,
});
b.installArtifact(exe);
// Run
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| run_cmd.addArgs(args);
run_step.dependOn(&run_cmd.step);
}

View File

@ -0,0 +1,24 @@
.{
.name = .c_vt_encode_focus,
.version = "0.0.0",
.fingerprint = 0x89f01fd829fcc550,
.minimum_zig_version = "0.15.1",
.dependencies = .{
// Ghostty dependency. In reality, you'd probably use a URL-based
// dependency like the one showed (and commented out) below this one.
// We use a path dependency here for simplicity and to ensure our
// examples always test against the source they're bundled with.
.ghostty = .{ .path = "../../" },
// Example of what a URL-based dependency looks like:
// .ghostty = .{
// .url = "https://github.com/ghostty-org/ghostty/archive/COMMIT.tar.gz",
// .hash = "N-V-__8AAMVLTABmYkLqhZPLXnMl-KyN38R8UVYqGrxqO36s",
// },
},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
}

View File

@ -0,0 +1,20 @@
#include <stdio.h>
#include <ghostty/vt.h>
//! [focus-encode]
int main() {
char buf[8];
size_t written = 0;
GhosttyResult result = ghostty_focus_encode(
GHOSTTY_FOCUS_GAINED, buf, sizeof(buf), &written);
if (result == GHOSTTY_SUCCESS) {
printf("Encoded %zu bytes: ", written);
fwrite(buf, 1, written, stdout);
printf("\n");
}
return 0;
}
//! [focus-encode]

View File

@ -0,0 +1,42 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const run_step = b.step("run", "Run the app");
const exe_mod = b.createModule(.{
.target = target,
.optimize = optimize,
});
exe_mod.addCSourceFiles(.{
.root = b.path("src"),
.files = &.{"main.c"},
});
// You'll want to use a lazy dependency here so that ghostty is only
// downloaded if you actually need it.
if (b.lazyDependency("ghostty", .{
// Setting simd to false will force a pure static build that
// doesn't even require libc, but it has a significant performance
// penalty. If your embedding app requires libc anyway, you should
// always keep simd enabled.
// .simd = false,
})) |dep| {
exe_mod.linkLibrary(dep.artifact("ghostty-vt"));
}
// Exe
const exe = b.addExecutable(.{
.name = "c_vt_encode_key",
.root_module = exe_mod,
});
b.installArtifact(exe);
// Run
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| run_cmd.addArgs(args);
run_step.dependOn(&run_cmd.step);
}

View File

@ -0,0 +1,40 @@
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <ghostty/vt.h>
//! [key-encode]
int main() {
// Create encoder
GhosttyKeyEncoder encoder;
GhosttyResult result = ghostty_key_encoder_new(NULL, &encoder);
assert(result == GHOSTTY_SUCCESS);
// Enable Kitty keyboard protocol with all features
ghostty_key_encoder_setopt(encoder, GHOSTTY_KEY_ENCODER_OPT_KITTY_FLAGS,
&(uint8_t){GHOSTTY_KITTY_KEY_ALL});
// Create and configure key event for Ctrl+C press
GhosttyKeyEvent event;
result = ghostty_key_event_new(NULL, &event);
assert(result == GHOSTTY_SUCCESS);
ghostty_key_event_set_action(event, GHOSTTY_KEY_ACTION_PRESS);
ghostty_key_event_set_key(event, GHOSTTY_KEY_C);
ghostty_key_event_set_mods(event, GHOSTTY_MODS_CTRL);
// Encode the key event
char buf[128];
size_t written = 0;
result = ghostty_key_encoder_encode(encoder, event, buf, sizeof(buf), &written);
assert(result == GHOSTTY_SUCCESS);
// Use the encoded sequence (e.g., write to terminal)
fwrite(buf, 1, written, stdout);
// Cleanup
ghostty_key_event_free(event);
ghostty_key_encoder_free(encoder);
return 0;
}
//! [key-encode]

View File

@ -0,0 +1,23 @@
# Example: `ghostty-vt` C Mouse Encoding
This example demonstrates how to use the `ghostty-vt` C library to encode mouse
events into terminal escape sequences.
This example specifically shows how to:
1. Create a mouse encoder with the C API
2. Configure tracking mode and output format (this example uses SGR)
3. Set terminal geometry for pixel-to-cell coordinate mapping
4. Create and configure a mouse event
5. Encode the mouse event into a terminal escape sequence
The example encodes a left button press at pixel position (50, 40) using SGR
format, producing an escape sequence like `\x1b[<0;6;3M`.
## Usage
Run the program:
```shell-session
zig build run
```

View File

@ -0,0 +1,42 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const run_step = b.step("run", "Run the app");
const exe_mod = b.createModule(.{
.target = target,
.optimize = optimize,
});
exe_mod.addCSourceFiles(.{
.root = b.path("src"),
.files = &.{"main.c"},
});
// You'll want to use a lazy dependency here so that ghostty is only
// downloaded if you actually need it.
if (b.lazyDependency("ghostty", .{
// Setting simd to false will force a pure static build that
// doesn't even require libc, but it has a significant performance
// penalty. If your embedding app requires libc anyway, you should
// always keep simd enabled.
// .simd = false,
})) |dep| {
exe_mod.linkLibrary(dep.artifact("ghostty-vt"));
}
// Exe
const exe = b.addExecutable(.{
.name = "c_vt_encode_mouse",
.root_module = exe_mod,
});
b.installArtifact(exe);
// Run
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| run_cmd.addArgs(args);
run_step.dependOn(&run_cmd.step);
}

View File

@ -0,0 +1,24 @@
.{
.name = .c_vt,
.version = "0.0.0",
.fingerprint = 0x413a8529a6dd3c51,
.minimum_zig_version = "0.15.1",
.dependencies = .{
// Ghostty dependency. In reality, you'd probably use a URL-based
// dependency like the one showed (and commented out) below this one.
// We use a path dependency here for simplicity and to ensure our
// examples always test against the source they're bundled with.
.ghostty = .{ .path = "../../" },
// Example of what a URL-based dependency looks like:
// .ghostty = .{
// .url = "https://github.com/ghostty-org/ghostty/archive/COMMIT.tar.gz",
// .hash = "N-V-__8AAMVLTABmYkLqhZPLXnMl-KyN38R8UVYqGrxqO36s",
// },
},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
}

View File

@ -0,0 +1,52 @@
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <ghostty/vt.h>
//! [mouse-encode]
int main() {
// Create encoder
GhosttyMouseEncoder encoder;
GhosttyResult result = ghostty_mouse_encoder_new(NULL, &encoder);
assert(result == GHOSTTY_SUCCESS);
// Configure SGR format with normal tracking
ghostty_mouse_encoder_setopt(encoder, GHOSTTY_MOUSE_ENCODER_OPT_EVENT,
&(GhosttyMouseTrackingMode){GHOSTTY_MOUSE_TRACKING_NORMAL});
ghostty_mouse_encoder_setopt(encoder, GHOSTTY_MOUSE_ENCODER_OPT_FORMAT,
&(GhosttyMouseFormat){GHOSTTY_MOUSE_FORMAT_SGR});
// Set terminal geometry for coordinate mapping
ghostty_mouse_encoder_setopt(encoder, GHOSTTY_MOUSE_ENCODER_OPT_SIZE,
&(GhosttyMouseEncoderSize){
.size = sizeof(GhosttyMouseEncoderSize),
.screen_width = 800, .screen_height = 600,
.cell_width = 10, .cell_height = 20,
});
// Create and configure a left button press event
GhosttyMouseEvent event;
result = ghostty_mouse_event_new(NULL, &event);
assert(result == GHOSTTY_SUCCESS);
ghostty_mouse_event_set_action(event, GHOSTTY_MOUSE_ACTION_PRESS);
ghostty_mouse_event_set_button(event, GHOSTTY_MOUSE_BUTTON_LEFT);
ghostty_mouse_event_set_position(event,
(GhosttyMousePosition){.x = 50.0f, .y = 40.0f});
// Encode the mouse event
char buf[128];
size_t written = 0;
result = ghostty_mouse_encoder_encode(encoder, event,
buf, sizeof(buf), &written);
assert(result == GHOSTTY_SUCCESS);
// Use the encoded sequence (e.g., write to terminal)
fwrite(buf, 1, written, stdout);
// Cleanup
ghostty_mouse_event_free(event);
ghostty_mouse_encoder_free(encoder);
return 0;
}
//! [mouse-encode]

View File

@ -0,0 +1,18 @@
# Example: `ghostty-vt` Terminal Formatter
This contains a simple example of how to use the `ghostty-vt` terminal and
formatter APIs to create a terminal, write VT-encoded content into it, and
format the screen contents as plain text.
This uses a `build.zig` and `Zig` to build the C program so that we
can reuse a lot of our build logic and depend directly on our source
tree, but Ghostty emits a standard C library that can be used with any
C tooling.
## Usage
Run the program:
```shell-session
zig build run
```

View File

@ -0,0 +1,42 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const run_step = b.step("run", "Run the app");
const exe_mod = b.createModule(.{
.target = target,
.optimize = optimize,
});
exe_mod.addCSourceFiles(.{
.root = b.path("src"),
.files = &.{"main.c"},
});
// You'll want to use a lazy dependency here so that ghostty is only
// downloaded if you actually need it.
if (b.lazyDependency("ghostty", .{
// Setting simd to false will force a pure static build that
// doesn't even require libc, but it has a significant performance
// penalty. If your embedding app requires libc anyway, you should
// always keep simd enabled.
// .simd = false,
})) |dep| {
exe_mod.linkLibrary(dep.artifact("ghostty-vt"));
}
// Exe
const exe = b.addExecutable(.{
.name = "c_vt_formatter",
.root_module = exe_mod,
});
b.installArtifact(exe);
// Run
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| run_cmd.addArgs(args);
run_step.dependOn(&run_cmd.step);
}

View File

@ -0,0 +1,24 @@
.{
.name = .c_vt_formatter,
.version = "0.0.0",
.fingerprint = 0x9e3758265677a0c4,
.minimum_zig_version = "0.15.1",
.dependencies = .{
// Ghostty dependency. In reality, you'd probably use a URL-based
// dependency like the one showed (and commented out) below this one.
// We use a path dependency here for simplicity and to ensure our
// examples always test against the source they're bundled with.
.ghostty = .{ .path = "../../" },
// Example of what a URL-based dependency looks like:
// .ghostty = .{
// .url = "https://github.com/ghostty-org/ghostty/archive/COMMIT.tar.gz",
// .hash = "N-V-__8AAMVLTABmYkLqhZPLXnMl-KyN38R8UVYqGrxqO36s",
// },
},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
}

View File

@ -0,0 +1,63 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ghostty/vt.h>
int main() {
// Create a terminal with a small grid
GhosttyTerminal terminal;
GhosttyTerminalOptions opts = {
.cols = 80,
.rows = 24,
.max_scrollback = 0,
};
GhosttyResult result = ghostty_terminal_new(NULL, &terminal, opts);
assert(result == GHOSTTY_SUCCESS);
// Write VT-encoded content into the terminal to exercise various
// cursor movement and styling sequences.
const char *commands[] = {
"Line 1: Hello World!\r\n", // Simple text on row 1
"Line 2: \033[1mBold\033[0m and " // Bold text on row 2
"\033[4mUnderline\033[0m\r\n",
"Line 3: placeholder\r\n", // Will be overwritten below
"\033[3;1H", // CUP: move cursor back to row 3, col 1
"\033[2K", // EL: erase the entire line
"Line 3: Overwritten!\r\n", // Rewrite row 3 with new content
"\033[5;10H", // CUP: jump to row 5, col 10
"Placed at (5,10)", // Write at that position
"\033[1;72H", // CUP: jump to row 1, col 72
"RIGHT->", // Near the right edge of row 1
};
for (size_t i = 0; i < sizeof(commands) / sizeof(commands[0]); i++) {
ghostty_terminal_vt_write(terminal, (const uint8_t *)commands[i],
strlen(commands[i]));
}
// Create a plain-text formatter for the terminal
GhosttyFormatterTerminalOptions fmt_opts = GHOSTTY_INIT_SIZED(GhosttyFormatterTerminalOptions);
fmt_opts.emit = GHOSTTY_FORMATTER_FORMAT_PLAIN;
fmt_opts.trim = true;
GhosttyFormatter formatter;
result = ghostty_formatter_terminal_new(NULL, &formatter, terminal, fmt_opts);
assert(result == GHOSTTY_SUCCESS);
// Format into an allocated buffer
uint8_t *buf = NULL;
size_t len = 0;
result = ghostty_formatter_format_alloc(formatter, NULL, &buf, &len);
assert(result == GHOSTTY_SUCCESS);
// Print the formatted output
printf("Formatted output (%zu bytes):\n", len);
fwrite(buf, 1, len, stdout);
printf("\n");
// Clean up
ghostty_free(NULL, buf, len);
ghostty_formatter_free(formatter);
ghostty_terminal_free(terminal);
return 0;
}

View File

@ -0,0 +1,19 @@
# Example: `ghostty-vt` Grid Traversal
This contains a simple example of how to use the `ghostty-vt` terminal and
grid reference APIs to create a terminal, write content into it, and then
traverse the entire grid cell-by-cell using grid refs to inspect codepoints,
row state, and styles.
This uses a `build.zig` and `Zig` to build the C program so that we
can reuse a lot of our build logic and depend directly on our source
tree, but Ghostty emits a standard C library that can be used with any
C tooling.
## Usage
Run the program:
```shell-session
zig build run
```

View File

@ -0,0 +1,42 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const run_step = b.step("run", "Run the app");
const exe_mod = b.createModule(.{
.target = target,
.optimize = optimize,
});
exe_mod.addCSourceFiles(.{
.root = b.path("src"),
.files = &.{"main.c"},
});
// You'll want to use a lazy dependency here so that ghostty is only
// downloaded if you actually need it.
if (b.lazyDependency("ghostty", .{
// Setting simd to false will force a pure static build that
// doesn't even require libc, but it has a significant performance
// penalty. If your embedding app requires libc anyway, you should
// always keep simd enabled.
// .simd = false,
})) |dep| {
exe_mod.linkLibrary(dep.artifact("ghostty-vt"));
}
// Exe
const exe = b.addExecutable(.{
.name = "c_vt_grid_traverse",
.root_module = exe_mod,
});
b.installArtifact(exe);
// Run
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| run_cmd.addArgs(args);
run_step.dependOn(&run_cmd.step);
}

View File

@ -0,0 +1,24 @@
.{
.name = .c_vt_grid_traverse,
.version = "0.0.0",
.fingerprint = 0xf694dd12db9be040,
.minimum_zig_version = "0.15.1",
.dependencies = .{
// Ghostty dependency. In reality, you'd probably use a URL-based
// dependency like the one showed (and commented out) below this one.
// We use a path dependency here for simplicity and to ensure our
// examples always test against the source they're bundled with.
.ghostty = .{ .path = "../../" },
// Example of what a URL-based dependency looks like:
// .ghostty = .{
// .url = "https://github.com/ghostty-org/ghostty/archive/COMMIT.tar.gz",
// .hash = "N-V-__8AAMVLTABmYkLqhZPLXnMl-KyN38R8UVYqGrxqO36s",
// },
},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
}

View File

@ -0,0 +1,85 @@
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <ghostty/vt.h>
//! [grid-ref-traverse]
int main() {
// Create a small terminal
GhosttyTerminal terminal;
GhosttyTerminalOptions opts = {
.cols = 10,
.rows = 3,
.max_scrollback = 0,
};
GhosttyResult result = ghostty_terminal_new(NULL, &terminal, opts);
assert(result == GHOSTTY_SUCCESS);
// Write some content so the grid has interesting data
const char *text = "Hello!\r\n" // Row 0: H e l l o !
"World\r\n" // Row 1: W o r l d
"\033[1mBold"; // Row 2: B o l d (bold style)
ghostty_terminal_vt_write(
terminal, (const uint8_t *)text, strlen(text));
// Get terminal dimensions
uint16_t cols, rows;
ghostty_terminal_get(terminal, GHOSTTY_TERMINAL_DATA_COLS, &cols);
ghostty_terminal_get(terminal, GHOSTTY_TERMINAL_DATA_ROWS, &rows);
// Traverse the entire grid using grid refs
for (uint16_t row = 0; row < rows; row++) {
printf("Row %u: ", row);
for (uint16_t col = 0; col < cols; col++) {
// Resolve the point to a grid reference
GhosttyGridRef ref = GHOSTTY_INIT_SIZED(GhosttyGridRef);
GhosttyPoint pt = {
.tag = GHOSTTY_POINT_TAG_ACTIVE,
.value = { .coordinate = { .x = col, .y = row } },
};
result = ghostty_terminal_grid_ref(terminal, pt, &ref);
assert(result == GHOSTTY_SUCCESS);
// Read the cell from the grid ref
GhosttyCell cell;
result = ghostty_grid_ref_cell(&ref, &cell);
assert(result == GHOSTTY_SUCCESS);
// Check if the cell has text
bool has_text = false;
ghostty_cell_get(cell, GHOSTTY_CELL_DATA_HAS_TEXT, &has_text);
if (has_text) {
uint32_t codepoint = 0;
ghostty_cell_get(cell, GHOSTTY_CELL_DATA_CODEPOINT, &codepoint);
printf("%c", (char)codepoint);
} else {
printf(".");
}
}
// Also inspect the row for wrap state
GhosttyGridRef ref = GHOSTTY_INIT_SIZED(GhosttyGridRef);
GhosttyPoint pt = {
.tag = GHOSTTY_POINT_TAG_ACTIVE,
.value = { .coordinate = { .x = 0, .y = row } },
};
ghostty_terminal_grid_ref(terminal, pt, &ref);
GhosttyRow grid_row;
ghostty_grid_ref_row(&ref, &grid_row);
bool wrap = false;
ghostty_row_get(grid_row, GHOSTTY_ROW_DATA_WRAP, &wrap);
printf(" (wrap=%s", wrap ? "true" : "false");
// Check the style of the first cell with text
GhosttyStyle style = GHOSTTY_INIT_SIZED(GhosttyStyle);
ghostty_grid_ref_style(&ref, &style);
printf(", bold=%s)\n", style.bold ? "true" : "false");
}
ghostty_terminal_free(terminal);
return 0;
}
//! [grid-ref-traverse]

View File

@ -1,59 +0,0 @@
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <ghostty/vt.h>
int main() {
GhosttyKeyEncoder encoder;
GhosttyResult result = ghostty_key_encoder_new(NULL, &encoder);
assert(result == GHOSTTY_SUCCESS);
// Set kitty flags with all features enabled
ghostty_key_encoder_setopt(encoder, GHOSTTY_KEY_ENCODER_OPT_KITTY_FLAGS, &(uint8_t){GHOSTTY_KITTY_KEY_ALL});
// Create key event
GhosttyKeyEvent event;
result = ghostty_key_event_new(NULL, &event);
assert(result == GHOSTTY_SUCCESS);
ghostty_key_event_set_action(event, GHOSTTY_KEY_ACTION_RELEASE);
ghostty_key_event_set_key(event, GHOSTTY_KEY_CONTROL_LEFT);
ghostty_key_event_set_mods(event, GHOSTTY_MODS_CTRL);
printf("Encoding event: left ctrl release with all Kitty flags enabled\n");
// Optionally, encode with null buffer to get required size. You can
// skip this step and provide a sufficiently large buffer directly.
// If there isn't enoug hspace, the function will return an out of memory
// error.
size_t required = 0;
result = ghostty_key_encoder_encode(encoder, event, NULL, 0, &required);
assert(result == GHOSTTY_OUT_OF_MEMORY);
printf("Required buffer size: %zu bytes\n", required);
// Encode the key event. We don't use our required size above because
// that was just an example; we know 128 bytes is enough.
char buf[128];
size_t written = 0;
result = ghostty_key_encoder_encode(encoder, event, buf, sizeof(buf), &written);
assert(result == GHOSTTY_SUCCESS);
printf("Encoded %zu bytes\n", written);
// Print the encoded sequence (hex and string)
printf("Hex: ");
for (size_t i = 0; i < written; i++) printf("%02x ", (unsigned char)buf[i]);
printf("\n");
printf("String: ");
for (size_t i = 0; i < written; i++) {
if (buf[i] == 0x1b) {
printf("\\x1b");
} else {
printf("%c", buf[i]);
}
}
printf("\n");
ghostty_key_event_free(event);
ghostty_key_encoder_free(encoder);
return 0;
}

View File

@ -0,0 +1,18 @@
# Example: `ghostty-vt` Mode Utilities
This contains a simple example of how to use the `ghostty-vt` mode
utilities to pack and unpack terminal mode identifiers and encode
DECRPM responses.
This uses a `build.zig` and `Zig` to build the C program so that we
can reuse a lot of our build logic and depend directly on our source
tree, but Ghostty emits a standard C library that can be used with any
C tooling.
## Usage
Run the program:
```shell-session
zig build run
```

View File

@ -0,0 +1,42 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const run_step = b.step("run", "Run the app");
const exe_mod = b.createModule(.{
.target = target,
.optimize = optimize,
});
exe_mod.addCSourceFiles(.{
.root = b.path("src"),
.files = &.{"main.c"},
});
// You'll want to use a lazy dependency here so that ghostty is only
// downloaded if you actually need it.
if (b.lazyDependency("ghostty", .{
// Setting simd to false will force a pure static build that
// doesn't even require libc, but it has a significant performance
// penalty. If your embedding app requires libc anyway, you should
// always keep simd enabled.
// .simd = false,
})) |dep| {
exe_mod.linkLibrary(dep.artifact("ghostty-vt"));
}
// Exe
const exe = b.addExecutable(.{
.name = "c_vt_modes",
.root_module = exe_mod,
});
b.installArtifact(exe);
// Run
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| run_cmd.addArgs(args);
run_step.dependOn(&run_cmd.step);
}

View File

@ -0,0 +1,24 @@
.{
.name = .c_vt_modes,
.version = "0.0.0",
.fingerprint = 0x67ce079ebc70a02a,
.minimum_zig_version = "0.15.1",
.dependencies = .{
// Ghostty dependency. In reality, you'd probably use a URL-based
// dependency like the one showed (and commented out) below this one.
// We use a path dependency here for simplicity and to ensure our
// examples always test against the source they're bundled with.
.ghostty = .{ .path = "../../" },
// Example of what a URL-based dependency looks like:
// .ghostty = .{
// .url = "https://github.com/ghostty-org/ghostty/archive/COMMIT.tar.gz",
// .hash = "N-V-__8AAMVLTABmYkLqhZPLXnMl-KyN38R8UVYqGrxqO36s",
// },
},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
}

View File

@ -0,0 +1,45 @@
#include <stdio.h>
#include <ghostty/vt.h>
//! [modes-pack-unpack]
void modes_example() {
// Create a mode for DEC mode 25 (cursor visible)
GhosttyMode tag = ghostty_mode_new(25, false);
printf("value=%u ansi=%d packed=0x%04x\n",
ghostty_mode_value(tag),
ghostty_mode_ansi(tag),
tag);
// Create a mode for ANSI mode 4 (insert mode)
GhosttyMode ansi_tag = ghostty_mode_new(4, true);
printf("value=%u ansi=%d packed=0x%04x\n",
ghostty_mode_value(ansi_tag),
ghostty_mode_ansi(ansi_tag),
ansi_tag);
}
//! [modes-pack-unpack]
//! [modes-decrpm]
void decrpm_example() {
char buf[32];
size_t written = 0;
// Encode a report that DEC mode 25 (cursor visible) is set
GhosttyResult result = ghostty_mode_report_encode(
GHOSTTY_MODE_CURSOR_VISIBLE,
GHOSTTY_MODE_REPORT_SET,
buf, sizeof(buf), &written);
if (result == GHOSTTY_SUCCESS) {
printf("Encoded %zu bytes: ", written);
fwrite(buf, 1, written, stdout);
printf("\n"); // prints: ESC[?25;1$y
}
}
//! [modes-decrpm]
int main() {
modes_example();
decrpm_example();
return 0;
}

View File

@ -2,18 +2,23 @@
#include <string.h>
#include <ghostty/vt.h>
int main() {
// Test safe paste data
const char *safe_data = "hello world";
//! [paste-safety]
void basic_example() {
const char* safe_data = "hello world";
const char* unsafe_data = "rm -rf /\n";
if (ghostty_paste_is_safe(safe_data, strlen(safe_data))) {
printf("'%s' is safe to paste\n", safe_data);
printf("Safe to paste\n");
}
// Test unsafe paste data with newline
const char *unsafe_newline = "rm -rf /\n";
if (!ghostty_paste_is_safe(unsafe_newline, strlen(unsafe_newline))) {
printf("'%s' is UNSAFE - contains newline\n", unsafe_newline);
if (!ghostty_paste_is_safe(unsafe_data, strlen(unsafe_data))) {
printf("Unsafe! Contains newline\n");
}
}
//! [paste-safety]
int main() {
basic_example();
// Test unsafe paste data with bracketed paste end sequence
const char *unsafe_escape = "evil\x1b[201~code";

View File

@ -0,0 +1,19 @@
# Example: `ghostty-vt` Render State
This contains an example of how to use the `ghostty-vt` render-state API
to create a render state, update it from terminal content, iterate rows
and cells, read styles and colors, inspect cursor state, and manage dirty
tracking.
This uses a `build.zig` and `Zig` to build the C program so that we
can reuse a lot of our build logic and depend directly on our source
tree, but Ghostty emits a standard C library that can be used with any
C tooling.
## Usage
Run the program:
```shell-session
zig build run
```

View File

@ -0,0 +1,42 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const run_step = b.step("run", "Run the app");
const exe_mod = b.createModule(.{
.target = target,
.optimize = optimize,
});
exe_mod.addCSourceFiles(.{
.root = b.path("src"),
.files = &.{"main.c"},
});
// You'll want to use a lazy dependency here so that ghostty is only
// downloaded if you actually need it.
if (b.lazyDependency("ghostty", .{
// Setting simd to false will force a pure static build that
// doesn't even require libc, but it has a significant performance
// penalty. If your embedding app requires libc anyway, you should
// always keep simd enabled.
// .simd = false,
})) |dep| {
exe_mod.linkLibrary(dep.artifact("ghostty-vt"));
}
// Exe
const exe = b.addExecutable(.{
.name = "c_vt_render",
.root_module = exe_mod,
});
b.installArtifact(exe);
// Run
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| run_cmd.addArgs(args);
run_step.dependOn(&run_cmd.step);
}

View File

@ -0,0 +1,24 @@
.{
.name = .c_vt_render,
.version = "0.0.0",
.fingerprint = 0xb10e18b2fab773c9,
.minimum_zig_version = "0.15.1",
.dependencies = .{
// Ghostty dependency. In reality, you'd probably use a URL-based
// dependency like the one showed (and commented out) below this one.
// We use a path dependency here for simplicity and to ensure our
// examples always test against the source they're bundled with.
.ghostty = .{ .path = "../../" },
// Example of what a URL-based dependency looks like:
// .ghostty = .{
// .url = "https://github.com/ghostty-org/ghostty/archive/COMMIT.tar.gz",
// .hash = "N-V-__8AAMVLTABmYkLqhZPLXnMl-KyN38R8UVYqGrxqO36s",
// },
},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
}

View File

@ -0,0 +1,234 @@
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <ghostty/vt.h>
/// Helper: resolve a style color to an RGB value using the palette.
static GhosttyColorRgb resolve_color(GhosttyStyleColor color,
const GhosttyRenderStateColors* colors,
GhosttyColorRgb fallback) {
switch (color.tag) {
case GHOSTTY_STYLE_COLOR_RGB:
return color.value.rgb;
case GHOSTTY_STYLE_COLOR_PALETTE:
return colors->palette[color.value.palette];
default:
return fallback;
}
}
int main(void) {
GhosttyResult result;
//! [render-state-update]
// Create a terminal and render state, then update the render state
// from the terminal. The render state captures a snapshot of everything
// needed to draw a frame.
GhosttyTerminal terminal = NULL;
GhosttyTerminalOptions terminal_opts = {
.cols = 40,
.rows = 5,
.max_scrollback = 10000,
};
result = ghostty_terminal_new(NULL, &terminal, terminal_opts);
assert(result == GHOSTTY_SUCCESS);
GhosttyRenderState render_state = NULL;
result = ghostty_render_state_new(NULL, &render_state);
assert(result == GHOSTTY_SUCCESS);
// Feed some styled content into the terminal.
const char* content =
"Hello, \033[1;32mworld\033[0m!\r\n" // bold green "world"
"\033[4munderlined\033[0m text\r\n" // underlined text
"\033[38;2;255;128;0morange\033[0m\r\n"; // 24-bit orange fg
ghostty_terminal_vt_write(
terminal, (const uint8_t*)content, strlen(content));
result = ghostty_render_state_update(render_state, terminal);
assert(result == GHOSTTY_SUCCESS);
//! [render-state-update]
//! [render-dirty-check]
// Check the global dirty state to decide how much work the renderer
// needs to do. After rendering, reset it to false.
GhosttyRenderStateDirty dirty;
result = ghostty_render_state_get(
render_state, GHOSTTY_RENDER_STATE_DATA_DIRTY, &dirty);
assert(result == GHOSTTY_SUCCESS);
switch (dirty) {
case GHOSTTY_RENDER_STATE_DIRTY_FALSE:
printf("Frame is clean, nothing to draw.\n");
break;
case GHOSTTY_RENDER_STATE_DIRTY_PARTIAL:
printf("Partial redraw needed.\n");
break;
case GHOSTTY_RENDER_STATE_DIRTY_FULL:
printf("Full redraw needed.\n");
break;
}
//! [render-dirty-check]
//! [render-colors]
// Retrieve colors (background, foreground, palette) from the render
// state. These are needed to resolve palette-indexed cell colors.
GhosttyRenderStateColors colors =
GHOSTTY_INIT_SIZED(GhosttyRenderStateColors);
result = ghostty_render_state_colors_get(render_state, &colors);
assert(result == GHOSTTY_SUCCESS);
printf("Background: #%02x%02x%02x\n",
colors.background.r, colors.background.g, colors.background.b);
printf("Foreground: #%02x%02x%02x\n",
colors.foreground.r, colors.foreground.g, colors.foreground.b);
//! [render-colors]
//! [render-cursor]
// Read cursor position and visual style from the render state.
bool cursor_visible = false;
ghostty_render_state_get(
render_state, GHOSTTY_RENDER_STATE_DATA_CURSOR_VISIBLE,
&cursor_visible);
bool cursor_in_viewport = false;
ghostty_render_state_get(
render_state, GHOSTTY_RENDER_STATE_DATA_CURSOR_VIEWPORT_HAS_VALUE,
&cursor_in_viewport);
if (cursor_visible && cursor_in_viewport) {
uint16_t cx, cy;
ghostty_render_state_get(
render_state, GHOSTTY_RENDER_STATE_DATA_CURSOR_VIEWPORT_X, &cx);
ghostty_render_state_get(
render_state, GHOSTTY_RENDER_STATE_DATA_CURSOR_VIEWPORT_Y, &cy);
GhosttyRenderStateCursorVisualStyle style;
ghostty_render_state_get(
render_state, GHOSTTY_RENDER_STATE_DATA_CURSOR_VISUAL_STYLE,
&style);
const char* style_name = "unknown";
switch (style) {
case GHOSTTY_RENDER_STATE_CURSOR_VISUAL_STYLE_BAR:
style_name = "bar";
break;
case GHOSTTY_RENDER_STATE_CURSOR_VISUAL_STYLE_BLOCK:
style_name = "block";
break;
case GHOSTTY_RENDER_STATE_CURSOR_VISUAL_STYLE_UNDERLINE:
style_name = "underline";
break;
case GHOSTTY_RENDER_STATE_CURSOR_VISUAL_STYLE_BLOCK_HOLLOW:
style_name = "hollow";
break;
}
printf("Cursor at (%u, %u), style: %s\n", cx, cy, style_name);
}
//! [render-cursor]
//! [render-row-iterate]
// Iterate rows via the row iterator. For each dirty row, iterate its
// cells, read codepoints/graphemes and styles, and emit ANSI-colored
// output as a simple "renderer".
GhosttyRenderStateRowIterator row_iter = NULL;
result = ghostty_render_state_row_iterator_new(NULL, &row_iter);
assert(result == GHOSTTY_SUCCESS);
result = ghostty_render_state_get(
render_state, GHOSTTY_RENDER_STATE_DATA_ROW_ITERATOR, &row_iter);
assert(result == GHOSTTY_SUCCESS);
GhosttyRenderStateRowCells cells = NULL;
result = ghostty_render_state_row_cells_new(NULL, &cells);
assert(result == GHOSTTY_SUCCESS);
int row_index = 0;
while (ghostty_render_state_row_iterator_next(row_iter)) {
// Check per-row dirty state; a real renderer would skip clean rows.
bool row_dirty = false;
ghostty_render_state_row_get(
row_iter, GHOSTTY_RENDER_STATE_ROW_DATA_DIRTY, &row_dirty);
printf("Row %2d [%s]: ", row_index,
row_dirty ? "dirty" : "clean");
// Get cells for this row (reuses the same cells handle).
result = ghostty_render_state_row_get(
row_iter, GHOSTTY_RENDER_STATE_ROW_DATA_CELLS, &cells);
assert(result == GHOSTTY_SUCCESS);
while (ghostty_render_state_row_cells_next(cells)) {
// Get the grapheme length; 0 means the cell is empty.
uint32_t grapheme_len = 0;
ghostty_render_state_row_cells_get(
cells, GHOSTTY_RENDER_STATE_ROW_CELLS_DATA_GRAPHEMES_LEN,
&grapheme_len);
if (grapheme_len == 0) {
putchar(' ');
continue;
}
// Read the style for this cell. Returns the default style for
// cells that have no explicit styling.
GhosttyStyle style = GHOSTTY_INIT_SIZED(GhosttyStyle);
ghostty_render_state_row_cells_get(
cells, GHOSTTY_RENDER_STATE_ROW_CELLS_DATA_STYLE, &style);
// Resolve foreground color for this cell.
GhosttyColorRgb fg =
resolve_color(style.fg_color, &colors, colors.foreground);
// Emit ANSI true-color escape for the foreground.
printf("\033[38;2;%u;%u;%um", fg.r, fg.g, fg.b);
if (style.bold) printf("\033[1m");
if (style.underline) printf("\033[4m");
// Read grapheme codepoints into a buffer and print them.
// The buffer must be at least grapheme_len elements.
uint32_t codepoints[16];
uint32_t len = grapheme_len < 16 ? grapheme_len : 16;
ghostty_render_state_row_cells_get(
cells, GHOSTTY_RENDER_STATE_ROW_CELLS_DATA_GRAPHEMES_BUF,
codepoints);
for (uint32_t i = 0; i < len; i++) {
// Simple ASCII print; a real renderer would handle UTF-8.
if (codepoints[i] < 128)
putchar((char)codepoints[i]);
else
printf("U+%04X", codepoints[i]);
}
printf("\033[0m"); // Reset style after each cell.
}
printf("\n");
// Clear per-row dirty flag after "rendering" it.
bool clean = false;
ghostty_render_state_row_set(
row_iter, GHOSTTY_RENDER_STATE_ROW_OPTION_DIRTY, &clean);
row_index++;
}
//! [render-row-iterate]
//! [render-dirty-reset]
// After finishing the frame, reset the global dirty state so the next
// update can report changes accurately.
GhosttyRenderStateDirty clean_state = GHOSTTY_RENDER_STATE_DIRTY_FALSE;
result = ghostty_render_state_set(
render_state, GHOSTTY_RENDER_STATE_OPTION_DIRTY, &clean_state);
assert(result == GHOSTTY_SUCCESS);
//! [render-dirty-reset]
// Cleanup
ghostty_render_state_row_cells_free(cells);
ghostty_render_state_row_iterator_free(row_iter);
ghostty_render_state_free(render_state);
ghostty_terminal_free(terminal);
return 0;
}

View File

@ -2,12 +2,43 @@
#include <stdio.h>
#include <ghostty/vt.h>
int main() {
//! [sgr-basic]
void basic_example() {
// Create parser
GhosttySgrParser parser;
GhosttyResult result = ghostty_sgr_new(NULL, &parser);
assert(result == GHOSTTY_SUCCESS);
// Parse "bold, red foreground" sequence: ESC[1;31m
uint16_t params[] = {1, 31};
result = ghostty_sgr_set_params(parser, params, NULL, 2);
assert(result == GHOSTTY_SUCCESS);
// 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\n", attr.value.fg_8);
break;
default:
break;
}
}
// Cleanup
ghostty_sgr_free(parser);
}
//! [sgr-basic]
void advanced_example() {
GhosttySgrParser parser;
GhosttyResult result = ghostty_sgr_new(NULL, &parser);
assert(result == GHOSTTY_SUCCESS);
// Parse a complex SGR sequence from Kakoune
// This corresponds to the escape sequence:
// ESC[4:3;38;2;51;51;51;48;2;170;170;170;58;2;255;97;136m
@ -26,10 +57,9 @@ int main() {
result = ghostty_sgr_set_params(parser, params, separators, sizeof(params) / sizeof(params[0]));
assert(result == GHOSTTY_SUCCESS);
printf("Parsing Kakoune SGR sequence:\n");
printf("\nParsing Kakoune SGR sequence:\n");
printf("ESC[4:3;38;2;51;51;51;48;2;170;170;170;58;2;255;97;136m\n\n");
// Iterate through attributes
GhosttySgrAttribute attr;
int count = 0;
while (ghostty_sgr_next(parser, &attr)) {
@ -124,8 +154,11 @@ int main() {
}
printf("\nTotal attributes parsed: %d\n", count);
// Cleanup
ghostty_sgr_free(parser);
}
int main() {
basic_example();
advanced_example();
return 0;
}

View File

@ -0,0 +1,17 @@
# Example: `ghostty-vt` Size Report Encoding
This contains a simple example of how to use the `ghostty-vt` size report
encoding API to encode terminal size reports into escape sequences.
This uses a `build.zig` and `Zig` to build the C program so that we
can reuse a lot of our build logic and depend directly on our source
tree, but Ghostty emits a standard C library that can be used with any
C tooling.
## Usage
Run the program:
```shell-session
zig build run
```

View File

@ -0,0 +1,42 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const run_step = b.step("run", "Run the app");
const exe_mod = b.createModule(.{
.target = target,
.optimize = optimize,
});
exe_mod.addCSourceFiles(.{
.root = b.path("src"),
.files = &.{"main.c"},
});
// You'll want to use a lazy dependency here so that ghostty is only
// downloaded if you actually need it.
if (b.lazyDependency("ghostty", .{
// Setting simd to false will force a pure static build that
// doesn't even require libc, but it has a significant performance
// penalty. If your embedding app requires libc anyway, you should
// always keep simd enabled.
// .simd = false,
})) |dep| {
exe_mod.linkLibrary(dep.artifact("ghostty-vt"));
}
// Exe
const exe = b.addExecutable(.{
.name = "c_vt_size_report",
.root_module = exe_mod,
});
b.installArtifact(exe);
// Run
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| run_cmd.addArgs(args);
run_step.dependOn(&run_cmd.step);
}

View File

@ -0,0 +1,24 @@
.{
.name = .c_vt_size_report,
.version = "0.0.0",
.fingerprint = 0x17e8cdb658fab232,
.minimum_zig_version = "0.15.1",
.dependencies = .{
// Ghostty dependency. In reality, you'd probably use a URL-based
// dependency like the one showed (and commented out) below this one.
// We use a path dependency here for simplicity and to ensure our
// examples always test against the source they're bundled with.
.ghostty = .{ .path = "../../" },
// Example of what a URL-based dependency looks like:
// .ghostty = .{
// .url = "https://github.com/ghostty-org/ghostty/archive/COMMIT.tar.gz",
// .hash = "N-V-__8AAMVLTABmYkLqhZPLXnMl-KyN38R8UVYqGrxqO36s",
// },
},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
}

View File

@ -0,0 +1,27 @@
#include <stdio.h>
#include <ghostty/vt.h>
//! [size-report-encode]
int main() {
GhosttySizeReportSize size = {
.rows = 24,
.columns = 80,
.cell_width = 9,
.cell_height = 18,
};
char buf[64];
size_t written = 0;
GhosttyResult result = ghostty_size_report_encode(
GHOSTTY_SIZE_REPORT_MODE_2048, size, buf, sizeof(buf), &written);
if (result == GHOSTTY_SUCCESS) {
printf("Encoded %zu bytes: ", written);
fwrite(buf, 1, written, stdout);
printf("\n");
}
return 0;
}
//! [size-report-encode]

View File

@ -0,0 +1,18 @@
# Example: `ghostty-vt` Static Linking
This contains a simple example of how to statically link the `ghostty-vt`
C library with a C program using the `ghostty-vt-static` artifact. It is
otherwise identical to the `c-vt` example.
This uses a `build.zig` and `Zig` to build the C program so that we
can reuse a lot of our build logic and depend directly on our source
tree, but Ghostty emits a standard C library that can be used with any
C tooling.
## Usage
Run the program:
```shell-session
zig build run
```

View File

@ -0,0 +1,44 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const run_step = b.step("run", "Run the app");
const exe_mod = b.createModule(.{
.target = target,
.optimize = optimize,
});
exe_mod.addCSourceFiles(.{
.root = b.path("src"),
.files = &.{"main.c"},
});
// You'll want to use a lazy dependency here so that ghostty is only
// downloaded if you actually need it.
if (b.lazyDependency("ghostty", .{
// Setting simd to false will force a pure static build that
// doesn't even require libc, but it has a significant performance
// penalty. If your embedding app requires libc anyway, you should
// always keep simd enabled.
// .simd = false,
})) |dep| {
// Use "ghostty-vt-static" for static linking instead of
// "ghostty-vt" which provides a shared library.
exe_mod.linkLibrary(dep.artifact("ghostty-vt-static"));
}
// Exe
const exe = b.addExecutable(.{
.name = "c_vt_static",
.root_module = exe_mod,
});
b.installArtifact(exe);
// Run
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| run_cmd.addArgs(args);
run_step.dependOn(&run_cmd.step);
}

View File

@ -0,0 +1,24 @@
.{
.name = .c_vt_static,
.version = "0.0.0",
.fingerprint = 0xa592a9fdd5d87ed2,
.minimum_zig_version = "0.15.1",
.dependencies = .{
// Ghostty dependency. In reality, you'd probably use a URL-based
// dependency like the one showed (and commented out) below this one.
// We use a path dependency here for simplicity and to ensure our
// examples always test against the source they're bundled with.
.ghostty = .{ .path = "../../" },
// Example of what a URL-based dependency looks like:
// .ghostty = .{
// .url = "https://github.com/ghostty-org/ghostty/archive/COMMIT.tar.gz",
// .hash = "N-V-__8AAMVLTABmYkLqhZPLXnMl-KyN38R8UVYqGrxqO36s",
// },
},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
}

View File

@ -0,0 +1,36 @@
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <ghostty/vt.h>
int main() {
GhosttyOscParser parser;
if (ghostty_osc_new(NULL, &parser) != GHOSTTY_SUCCESS) {
return 1;
}
// Setup change window title command to change the title to "hello"
ghostty_osc_next(parser, '0');
ghostty_osc_next(parser, ';');
const char *title = "hello";
for (size_t i = 0; i < strlen(title); i++) {
ghostty_osc_next(parser, title[i]);
}
// End parsing and get command
GhosttyOscCommand command = ghostty_osc_end(parser, 0);
// Get and print command type
GhosttyOscCommandType type = ghostty_osc_command_type(command);
printf("Command type: %d\n", type);
// Extract and print the title
if (ghostty_osc_command_data(command, GHOSTTY_OSC_DATA_CHANGE_WINDOW_TITLE_STR, &title)) {
printf("Extracted title: %s\n", title);
} else {
printf("Failed to extract title\n");
}
ghostty_osc_free(parser);
return 0;
}

View File

@ -8,7 +8,7 @@ to encode key events into terminal escape sequences.
First, build the WebAssembly module:
```bash
zig build lib-vt -Dtarget=wasm32-freestanding -Doptimize=ReleaseSmall
zig build -Demit-lib-vt -Dtarget=wasm32-freestanding -Doptimize=ReleaseSmall
```
This will create `zig-out/bin/ghostty-vt.wasm`.

View File

@ -9,7 +9,7 @@ styling attributes.
First, build the WebAssembly module:
```bash
zig build lib-vt -Dtarget=wasm32-freestanding -Doptimize=ReleaseSmall
zig build -Demit-lib-vt -Dtarget=wasm32-freestanding -Doptimize=ReleaseSmall
```
This will create `zig-out/bin/ghostty-vt.wasm`.

View File

@ -23,8 +23,8 @@ pub fn main() !void {
// Replace \n with \r\n
for (buf[0..n]) |byte| {
if (byte == '\n') try stream.next('\r');
try stream.next(byte);
if (byte == '\n') stream.next('\r');
stream.next(byte);
}
}

View File

@ -14,24 +14,24 @@ pub fn main() !void {
defer stream.deinit();
// Basic text with newline
try stream.nextSlice("Hello, World!\r\n");
stream.nextSlice("Hello, World!\r\n");
// ANSI color codes: ESC[1;32m = bold green, ESC[0m = reset
try stream.nextSlice("\x1b[1;32mGreen Text\x1b[0m\r\n");
stream.nextSlice("\x1b[1;32mGreen Text\x1b[0m\r\n");
// Cursor positioning: ESC[1;1H = move to row 1, column 1
try stream.nextSlice("\x1b[1;1HTop-left corner\r\n");
stream.nextSlice("\x1b[1;1HTop-left corner\r\n");
// Cursor movement: ESC[5B = move down 5 lines
try stream.nextSlice("\x1b[5B");
try stream.nextSlice("Moved down!\r\n");
stream.nextSlice("\x1b[5B");
stream.nextSlice("Moved down!\r\n");
// Erase line: ESC[2K = clear entire line
try stream.nextSlice("\x1b[2K");
try stream.nextSlice("New content\r\n");
stream.nextSlice("\x1b[2K");
stream.nextSlice("New content\r\n");
// Multiple lines
try stream.nextSlice("Line A\r\nLine B\r\nLine C\r\n");
stream.nextSlice("Line A\r\nLine B\r\nLine C\r\n");
// Get the final terminal state as a plain string
const str = try t.plainString(alloc);

View File

@ -16,24 +16,6 @@
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"home-manager": {
"inputs": {
"nixpkgs": [
@ -70,14 +52,15 @@
"root": {
"inputs": {
"flake-compat": "flake-compat",
"flake-utils": "flake-utils",
"home-manager": "home-manager",
"nixpkgs": "nixpkgs",
"systems": "systems",
"zig": "zig",
"zon2nix": "zon2nix"
}
},
"systems": {
"flake": false,
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
@ -97,19 +80,19 @@
"flake-compat": [
"flake-compat"
],
"flake-utils": [
"flake-utils"
],
"nixpkgs": [
"nixpkgs"
],
"systems": [
"systems"
]
},
"locked": {
"lastModified": 1763295135,
"narHash": "sha256-sGv/NHCmEnJivguGwB5w8LRmVqr1P72OjS+NzcJsssE=",
"lastModified": 1773145353,
"narHash": "sha256-dE8zx8WA54TRmFFQBvA48x/sXGDTP7YaDmY6nNKMAYw=",
"owner": "mitchellh",
"repo": "zig-overlay",
"rev": "64f8b42cfc615b2cf99144adf2b7728c7847c72a",
"rev": "8666155d83bf792956a7c40915508e6d4b2b8716",
"type": "github"
},
"original": {

View File

@ -10,7 +10,6 @@
# Gnome 49/Gtk 4.20.
#
nixpkgs.url = "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz";
flake-utils.url = "github:numtide/flake-utils";
# Used for shell.nix
flake-compat = {
@ -18,12 +17,17 @@
flake = false;
};
systems = {
url = "github:nix-systems/default";
flake = false;
};
zig = {
url = "github:mitchellh/zig-overlay";
inputs = {
nixpkgs.follows = "nixpkgs";
flake-utils.follows = "flake-utils";
flake-compat.follows = "flake-compat";
systems.follows = "systems";
};
};

View File

@ -167,6 +167,12 @@
"dest": "vendor/p/N-V-__8AAKw-DAAaV8bOAAGqA0-oD7o-HNIlPFYKRXSPT03S",
"sha256": "5cedcadde81b75e60f23e5e83b5dd2b8eb4efb9f8f79bd7a347d148aeb0530f8"
},
{
"type": "archive",
"url": "https://gitlab.freedesktop.org/wayland/wayland-protocols/-/archive/1.47/wayland-protocols-1.47.tar.gz",
"dest": "vendor/p/N-V-__8AAFdWDwA0ktbNUi9pFBHCRN4weXIgIfCrVjfGxqgA",
"sha256": "dd2df14ab5f41038257aaedcc4b5fb9ac0ee018f3f0f94af9097028e60d33223"
},
{
"type": "archive",
"url": "https://deps.files.ghostty.org/wuffs-122037b39d577ec2db3fd7b2130e7b69ef6cc1807d68607a7c232c958315d381b5cd.tar.gz",

View File

@ -17,6 +17,11 @@ extern "C" {
#include <stdint.h>
#include <sys/types.h>
#ifdef _MSC_VER
#include <BaseTsd.h>
typedef SSIZE_T ssize_t;
#endif
//-------------------------------------------------------------------
// Macros
@ -889,6 +894,7 @@ typedef enum {
GHOSTTY_ACTION_RENDER_INSPECTOR,
GHOSTTY_ACTION_DESKTOP_NOTIFICATION,
GHOSTTY_ACTION_SET_TITLE,
GHOSTTY_ACTION_SET_TAB_TITLE,
GHOSTTY_ACTION_PROMPT_TITLE,
GHOSTTY_ACTION_PWD,
GHOSTTY_ACTION_MOUSE_SHAPE,
@ -937,6 +943,7 @@ typedef union {
ghostty_action_inspector_e inspector;
ghostty_action_desktop_notification_s desktop_notification;
ghostty_action_set_title_s set_title;
ghostty_action_set_title_s set_tab_title;
ghostty_action_prompt_title_e prompt_title;
ghostty_action_pwd_s pwd;
ghostty_action_mouse_shape_e mouse_shape;
@ -968,7 +975,7 @@ typedef struct {
} ghostty_action_s;
typedef void (*ghostty_runtime_wakeup_cb)(void*);
typedef void (*ghostty_runtime_read_clipboard_cb)(void*,
typedef bool (*ghostty_runtime_read_clipboard_cb)(void*,
ghostty_clipboard_e,
void*);
typedef void (*ghostty_runtime_confirm_read_clipboard_cb)(

View File

@ -28,33 +28,55 @@
* @section groups_sec API Reference
*
* The API is organized into the following groups:
* - @ref key "Key Encoding" - Encode key events into terminal sequences
* - @ref terminal "Terminal" - Complete terminal emulator state and rendering
* - @ref render "Render State" - Incremental render state updates for custom renderers
* - @ref formatter "Formatter" - Format terminal content as plain text, VT sequences, or HTML
* - @ref osc "OSC Parser" - Parse OSC (Operating System Command) sequences
* - @ref sgr "SGR Parser" - Parse SGR (Select Graphic Rendition) sequences
* - @ref paste "Paste Utilities" - Validate paste data safety
* - @ref build_info "Build Info" - Query compile-time build configuration
* - @ref allocator "Memory Management" - Memory management and custom allocators
* - @ref wasm "WebAssembly Utilities" - WebAssembly convenience functions
*
* Encoding related APIs:
* - @ref focus "Focus Encoding" - Encode focus in/out events into terminal sequences
* - @ref key "Key Encoding" - Encode key events into terminal sequences
* - @ref mouse "Mouse Encoding" - Encode mouse events into terminal sequences
*
* @section examples_sec Examples
*
* Complete working examples:
* - @ref c-vt-build-info/src/main.c - Build info query example
* - @ref c-vt/src/main.c - OSC parser example
* - @ref c-vt-key-encode/src/main.c - Key encoding example
* - @ref c-vt-encode-key/src/main.c - Key encoding example
* - @ref c-vt-encode-mouse/src/main.c - Mouse encoding example
* - @ref c-vt-paste/src/main.c - Paste safety check example
* - @ref c-vt-sgr/src/main.c - SGR parser example
* - @ref c-vt-formatter/src/main.c - Terminal formatter example
* - @ref c-vt-grid-traverse/src/main.c - Grid traversal example using grid refs
*
*/
/** @example c-vt-build-info/src/main.c
* This example demonstrates how to query compile-time build configuration
* such as SIMD support, Kitty graphics, and tmux control mode availability.
*/
/** @example c-vt/src/main.c
* This example demonstrates how to use the OSC parser to parse an OSC sequence,
* extract command information, and retrieve command-specific data like window titles.
*/
/** @example c-vt-key-encode/src/main.c
/** @example c-vt-encode-key/src/main.c
* This example demonstrates how to use the key encoder to convert key events
* into terminal escape sequences using the Kitty keyboard protocol.
*/
/** @example c-vt-encode-mouse/src/main.c
* This example demonstrates how to use the mouse encoder to convert mouse events
* into terminal escape sequences using the SGR mouse format.
*/
/** @example c-vt-paste/src/main.c
* This example demonstrates how to use the paste utilities to check if
* paste data is safe before sending it to the terminal.
@ -65,6 +87,17 @@
* styling sequences and extract text attributes like colors and underline styles.
*/
/** @example c-vt-formatter/src/main.c
* This example demonstrates how to use the terminal and formatter APIs to
* create a terminal, write VT-encoded content into it, and format the screen
* contents as plain text.
*/
/** @example c-vt-grid-traverse/src/main.c
* This example demonstrates how to traverse the entire terminal grid using
* grid refs to inspect cell codepoints, row wrap state, and cell styles.
*/
#ifndef GHOSTTY_VT_H
#define GHOSTTY_VT_H
@ -72,12 +105,25 @@
extern "C" {
#endif
#include <ghostty/vt/result.h>
#include <ghostty/vt/types.h>
#include <ghostty/vt/allocator.h>
#include <ghostty/vt/build_info.h>
#include <ghostty/vt/color.h>
#include <ghostty/vt/device.h>
#include <ghostty/vt/focus.h>
#include <ghostty/vt/formatter.h>
#include <ghostty/vt/render.h>
#include <ghostty/vt/terminal.h>
#include <ghostty/vt/grid_ref.h>
#include <ghostty/vt/osc.h>
#include <ghostty/vt/sgr.h>
#include <ghostty/vt/style.h>
#include <ghostty/vt/key.h>
#include <ghostty/vt/modes.h>
#include <ghostty/vt/mouse.h>
#include <ghostty/vt/paste.h>
#include <ghostty/vt/screen.h>
#include <ghostty/vt/size_report.h>
#include <ghostty/vt/wasm.h>
#ifdef __cplusplus

View File

@ -44,6 +44,24 @@
* 2. Create a GhosttyAllocator struct with your vtable and context
* 3. Pass the allocator to functions that accept one
*
* ## Alloc/Free Helpers
*
* ghostty_alloc() and ghostty_free() provide a simple malloc/free-style
* interface for allocating and freeing byte buffers through the library's
* allocator. These are useful when:
*
* - You need to allocate a buffer to pass into a libghostty-vt function
* (e.g. preparing input data for ghostty_terminal_vt_write()).
* - You need to free a buffer returned by a libghostty-vt function
* (e.g. the output of ghostty_formatter_format_alloc()).
* - You are on a platform where the library's internal allocator differs
* from the consumer's C runtime (e.g. Windows, where Zig's libc and
* MSVC's CRT maintain separate heaps), so calling the standard C
* free() on library-allocated memory would be undefined behavior.
*
* Always use the same allocator (or NULL) for both the allocation and
* the corresponding free.
*
* @{
*/
@ -191,6 +209,46 @@ typedef struct GhosttyAllocator {
const GhosttyAllocatorVtable *vtable;
} GhosttyAllocator;
/**
* Allocate a buffer of `len` bytes.
*
* Uses the provided allocator, or the default allocator if NULL is passed.
* The returned buffer must be freed with ghostty_free() using the same
* allocator.
*
* @param allocator Pointer to the allocator to use, or NULL for the default
* @param len Number of bytes to allocate
* @return Pointer to the allocated buffer, or NULL if allocation failed
*
* @ingroup allocator
*/
uint8_t* ghostty_alloc(const GhosttyAllocator* allocator, size_t len);
/**
* Free memory that was allocated by a libghostty-vt function.
*
* Use this to free buffers returned by functions such as
* ghostty_formatter_format_alloc(). Pass the same allocator that was
* used for the allocation, or NULL if the default allocator was used.
*
* On platforms where the library's internal allocator differs from the
* consumer's C runtime (e.g. Windows, where Zig's libc and MSVC's CRT
* maintain separate heaps), calling the standard C free() on memory
* allocated by the library causes undefined behavior. This function
* guarantees the correct allocator is used regardless of platform.
*
* It is safe to pass a NULL pointer; the call is a no-op in that case.
*
* @param allocator Pointer to the allocator that was used to allocate the
* memory, or NULL if the default allocator was used
* @param ptr Pointer to the memory to free (may be NULL)
* @param len Length of the allocation in bytes (must match the original
* allocation size)
*
* @ingroup allocator
*/
void ghostty_free(const GhosttyAllocator* allocator, uint8_t* ptr, size_t len);
/** @} */
#endif /* GHOSTTY_VT_ALLOCATOR_H */

View File

@ -0,0 +1,103 @@
/**
* @file build_info.h
*
* Build info - query compile-time build configuration of libghostty-vt.
*/
#ifndef GHOSTTY_VT_BUILD_INFO_H
#define GHOSTTY_VT_BUILD_INFO_H
/** @defgroup build_info Build Info
*
* Query compile-time build configuration of libghostty-vt.
*
* These values reflect the options the library was built with and are
* constant for the lifetime of the process.
*
* ## Basic Usage
*
* Use ghostty_build_info() to query individual build options:
*
* @snippet c-vt-build-info/src/main.c build-info-query
*
* @{
*/
#include <stdbool.h>
#include <ghostty/vt/types.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Build optimization mode.
*/
typedef enum {
GHOSTTY_OPTIMIZE_DEBUG = 0,
GHOSTTY_OPTIMIZE_RELEASE_SAFE = 1,
GHOSTTY_OPTIMIZE_RELEASE_SMALL = 2,
GHOSTTY_OPTIMIZE_RELEASE_FAST = 3,
} GhosttyOptimizeMode;
/**
* Build info data types that can be queried.
*
* Each variant documents the expected output pointer type.
*/
typedef enum {
/** Invalid data type. Never results in any data extraction. */
GHOSTTY_BUILD_INFO_INVALID = 0,
/**
* Whether SIMD-accelerated code paths are enabled.
*
* Output type: bool *
*/
GHOSTTY_BUILD_INFO_SIMD = 1,
/**
* Whether Kitty graphics protocol support is available.
*
* Output type: bool *
*/
GHOSTTY_BUILD_INFO_KITTY_GRAPHICS = 2,
/**
* Whether tmux control mode support is available.
*
* Output type: bool *
*/
GHOSTTY_BUILD_INFO_TMUX_CONTROL_MODE = 3,
/**
* The optimization mode the library was built with.
*
* Output type: GhosttyOptimizeMode *
*/
GHOSTTY_BUILD_INFO_OPTIMIZE = 4,
} GhosttyBuildInfo;
/**
* Query a compile-time build configuration value.
*
* The caller must pass a pointer to the correct output type for the
* requested data (see GhosttyBuildInfo variants for types).
*
* @param data The build info field to query
* @param out Pointer to store the result (type depends on data parameter)
* @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if the
* data type is invalid
*
* @ingroup build_info
*/
GhosttyResult ghostty_build_info(GhosttyBuildInfo data, void *out);
#ifdef __cplusplus
}
#endif
/** @} */
#endif /* GHOSTTY_VT_BUILD_INFO_H */

150
include/ghostty/vt/device.h Normal file
View File

@ -0,0 +1,150 @@
/**
* @file device.h
*
* Device types used by the terminal for device status and device attribute
* queries.
*/
#ifndef GHOSTTY_VT_DEVICE_H
#define GHOSTTY_VT_DEVICE_H
#include <stddef.h>
#include <stdint.h>
/* DA1 conformance levels (Pp parameter). */
#define GHOSTTY_DA_CONFORMANCE_VT100 1
#define GHOSTTY_DA_CONFORMANCE_VT101 1
#define GHOSTTY_DA_CONFORMANCE_VT102 6
#define GHOSTTY_DA_CONFORMANCE_VT125 12
#define GHOSTTY_DA_CONFORMANCE_VT131 7
#define GHOSTTY_DA_CONFORMANCE_VT132 4
#define GHOSTTY_DA_CONFORMANCE_VT220 62
#define GHOSTTY_DA_CONFORMANCE_VT240 62
#define GHOSTTY_DA_CONFORMANCE_VT320 63
#define GHOSTTY_DA_CONFORMANCE_VT340 63
#define GHOSTTY_DA_CONFORMANCE_VT420 64
#define GHOSTTY_DA_CONFORMANCE_VT510 65
#define GHOSTTY_DA_CONFORMANCE_VT520 65
#define GHOSTTY_DA_CONFORMANCE_VT525 65
#define GHOSTTY_DA_CONFORMANCE_LEVEL_2 62
#define GHOSTTY_DA_CONFORMANCE_LEVEL_3 63
#define GHOSTTY_DA_CONFORMANCE_LEVEL_4 64
#define GHOSTTY_DA_CONFORMANCE_LEVEL_5 65
/* DA1 feature codes (Ps parameters). */
#define GHOSTTY_DA_FEATURE_COLUMNS_132 1
#define GHOSTTY_DA_FEATURE_PRINTER 2
#define GHOSTTY_DA_FEATURE_REGIS 3
#define GHOSTTY_DA_FEATURE_SIXEL 4
#define GHOSTTY_DA_FEATURE_SELECTIVE_ERASE 6
#define GHOSTTY_DA_FEATURE_USER_DEFINED_KEYS 8
#define GHOSTTY_DA_FEATURE_NATIONAL_REPLACEMENT 9
#define GHOSTTY_DA_FEATURE_TECHNICAL_CHARACTERS 15
#define GHOSTTY_DA_FEATURE_LOCATOR 16
#define GHOSTTY_DA_FEATURE_TERMINAL_STATE 17
#define GHOSTTY_DA_FEATURE_WINDOWING 18
#define GHOSTTY_DA_FEATURE_HORIZONTAL_SCROLLING 21
#define GHOSTTY_DA_FEATURE_ANSI_COLOR 22
#define GHOSTTY_DA_FEATURE_RECTANGULAR_EDITING 28
#define GHOSTTY_DA_FEATURE_ANSI_TEXT_LOCATOR 29
#define GHOSTTY_DA_FEATURE_CLIPBOARD 52
/* DA2 device type identifiers (Pp parameter). */
#define GHOSTTY_DA_DEVICE_TYPE_VT100 0
#define GHOSTTY_DA_DEVICE_TYPE_VT220 1
#define GHOSTTY_DA_DEVICE_TYPE_VT240 2
#define GHOSTTY_DA_DEVICE_TYPE_VT330 18
#define GHOSTTY_DA_DEVICE_TYPE_VT340 19
#define GHOSTTY_DA_DEVICE_TYPE_VT320 24
#define GHOSTTY_DA_DEVICE_TYPE_VT382 32
#define GHOSTTY_DA_DEVICE_TYPE_VT420 41
#define GHOSTTY_DA_DEVICE_TYPE_VT510 61
#define GHOSTTY_DA_DEVICE_TYPE_VT520 64
#define GHOSTTY_DA_DEVICE_TYPE_VT525 65
#ifdef __cplusplus
extern "C" {
#endif
/**
* Color scheme reported in response to a CSI ? 996 n query.
*
* @ingroup terminal
*/
typedef enum {
GHOSTTY_COLOR_SCHEME_LIGHT = 0,
GHOSTTY_COLOR_SCHEME_DARK = 1,
} GhosttyColorScheme;
/**
* Primary device attributes (DA1) response data.
*
* Returned as part of GhosttyDeviceAttributes in response to a CSI c query.
* The conformance_level is the Pp parameter and features contains the Ps
* feature codes.
*
* @ingroup terminal
*/
typedef struct {
/** Conformance level (Pp parameter). E.g. 62 for VT220. */
uint16_t conformance_level;
/** DA1 feature codes. Only the first num_features entries are valid. */
uint16_t features[64];
/** Number of valid entries in the features array. */
size_t num_features;
} GhosttyDeviceAttributesPrimary;
/**
* Secondary device attributes (DA2) response data.
*
* Returned as part of GhosttyDeviceAttributes in response to a CSI > c query.
* Response format: CSI > Pp ; Pv ; Pc c
*
* @ingroup terminal
*/
typedef struct {
/** Terminal type identifier (Pp). E.g. 1 for VT220. */
uint16_t device_type;
/** Firmware/patch version number (Pv). */
uint16_t firmware_version;
/** ROM cartridge registration number (Pc). Always 0 for emulators. */
uint16_t rom_cartridge;
} GhosttyDeviceAttributesSecondary;
/**
* Tertiary device attributes (DA3) response data.
*
* Returned as part of GhosttyDeviceAttributes in response to a CSI = c query.
* Response format: DCS ! | D...D ST (DECRPTUI).
*
* @ingroup terminal
*/
typedef struct {
/** Unit ID encoded as 8 uppercase hex digits in the response. */
uint32_t unit_id;
} GhosttyDeviceAttributesTertiary;
/**
* Device attributes response data for all three DA levels.
*
* Filled by the device_attributes callback in response to CSI c,
* CSI > c, or CSI = c queries. The terminal uses whichever sub-struct
* matches the request type.
*
* @ingroup terminal
*/
typedef struct {
GhosttyDeviceAttributesPrimary primary;
GhosttyDeviceAttributesSecondary secondary;
GhosttyDeviceAttributesTertiary tertiary;
} GhosttyDeviceAttributes;
#ifdef __cplusplus
}
#endif
#endif /* GHOSTTY_VT_DEVICE_H */

Some files were not shown because too many files have changed in this diff Show More