Merge remote-tracking branch 'upstream/main' into jacob/uucode
commit
b01770c21c
|
|
@ -42,7 +42,7 @@ jobs:
|
|||
/nix
|
||||
/zig
|
||||
- name: Setup Nix
|
||||
uses: cachix/install-nix-action@7be5dee1421f63d07e71ce6e0a9f8a4b07c2a487 # v31.6.1
|
||||
uses: cachix/install-nix-action@a809471b5c7c913aa67bec8f459a11a0decc3fce # v31.6.2
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ jobs:
|
|||
/nix
|
||||
/zig
|
||||
|
||||
- uses: cachix/install-nix-action@7be5dee1421f63d07e71ce6e0a9f8a4b07c2a487 # v31.6.1
|
||||
- uses: cachix/install-nix-action@a809471b5c7c913aa67bec8f459a11a0decc3fce # v31.6.2
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ jobs:
|
|||
with:
|
||||
# Important so that build number generation works
|
||||
fetch-depth: 0
|
||||
- uses: cachix/install-nix-action@7be5dee1421f63d07e71ce6e0a9f8a4b07c2a487 # v31.6.1
|
||||
- uses: cachix/install-nix-action@a809471b5c7c913aa67bec8f459a11a0decc3fce # v31.6.2
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -168,7 +168,7 @@ jobs:
|
|||
path: |
|
||||
/nix
|
||||
/zig
|
||||
- uses: cachix/install-nix-action@7be5dee1421f63d07e71ce6e0a9f8a4b07c2a487 # v31.6.1
|
||||
- uses: cachix/install-nix-action@a809471b5c7c913aa67bec8f459a11a0decc3fce # v31.6.2
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ jobs:
|
|||
needs:
|
||||
- build-bench
|
||||
- build-dist
|
||||
- build-examples
|
||||
- build-flatpak
|
||||
- build-freebsd
|
||||
- build-linux
|
||||
|
|
@ -19,8 +20,10 @@ jobs:
|
|||
- build-nix
|
||||
- build-macos
|
||||
- build-macos-matrix
|
||||
- build-snap
|
||||
- build-windows
|
||||
- test
|
||||
- test-simd
|
||||
- test-gtk
|
||||
- test-sentry-linux
|
||||
- test-macos
|
||||
|
|
@ -75,7 +78,7 @@ jobs:
|
|||
/zig
|
||||
|
||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||
- uses: cachix/install-nix-action@7be5dee1421f63d07e71ce6e0a9f8a4b07c2a487 # v31.6.1
|
||||
- uses: cachix/install-nix-action@a809471b5c7c913aa67bec8f459a11a0decc3fce # v31.6.2
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -86,6 +89,42 @@ jobs:
|
|||
- name: Build Benchmarks
|
||||
run: nix develop -c zig build -Demit-bench
|
||||
|
||||
build-examples:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
dir: [zig-vt]
|
||||
name: Example ${{ matrix.dir }}
|
||||
runs-on: namespace-profile-ghostty-sm
|
||||
needs: test
|
||||
env:
|
||||
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
||||
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@a289cf5d2fcd6874376aa92f0ef7f99dc923592a # v1.2.17
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
|
||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||
- uses: cachix/install-nix-action@a809471b5c7c913aa67bec8f459a11a0decc3fce # v31.6.2
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
with:
|
||||
name: ghostty
|
||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||
|
||||
- name: Build Example
|
||||
run: |
|
||||
cd example/${{ matrix.dir }}
|
||||
nix develop -c zig build
|
||||
|
||||
build-flatpak:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
|
@ -106,7 +145,7 @@ jobs:
|
|||
/zig
|
||||
|
||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||
- uses: cachix/install-nix-action@7be5dee1421f63d07e71ce6e0a9f8a4b07c2a487 # v31.6.1
|
||||
- uses: cachix/install-nix-action@a809471b5c7c913aa67bec8f459a11a0decc3fce # v31.6.2
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -118,7 +157,41 @@ jobs:
|
|||
run: |
|
||||
nix develop -c \
|
||||
zig build \
|
||||
-Dflatpak=true
|
||||
-Dflatpak
|
||||
|
||||
build-snap:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
runs-on: namespace-profile-ghostty-sm
|
||||
needs: test
|
||||
env:
|
||||
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
||||
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@a289cf5d2fcd6874376aa92f0ef7f99dc923592a # v1.2.17
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
|
||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||
- uses: cachix/install-nix-action@a809471b5c7c913aa67bec8f459a11a0decc3fce # v31.6.2
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
with:
|
||||
name: ghostty
|
||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||
|
||||
- name: Build with Snap
|
||||
run: |
|
||||
nix develop -c \
|
||||
zig build \
|
||||
-Dsnap
|
||||
|
||||
build-linux:
|
||||
strategy:
|
||||
|
|
@ -142,7 +215,7 @@ jobs:
|
|||
/zig
|
||||
|
||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||
- uses: cachix/install-nix-action@7be5dee1421f63d07e71ce6e0a9f8a4b07c2a487 # v31.6.1
|
||||
- uses: cachix/install-nix-action@a809471b5c7c913aa67bec8f459a11a0decc3fce # v31.6.2
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -171,7 +244,7 @@ jobs:
|
|||
/zig
|
||||
|
||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||
- uses: cachix/install-nix-action@7be5dee1421f63d07e71ce6e0a9f8a4b07c2a487 # v31.6.1
|
||||
- uses: cachix/install-nix-action@a809471b5c7c913aa67bec8f459a11a0decc3fce # v31.6.2
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -204,7 +277,7 @@ jobs:
|
|||
/zig
|
||||
|
||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||
- uses: cachix/install-nix-action@7be5dee1421f63d07e71ce6e0a9f8a4b07c2a487 # v31.6.1
|
||||
- uses: cachix/install-nix-action@a809471b5c7c913aa67bec8f459a11a0decc3fce # v31.6.2
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -250,7 +323,7 @@ jobs:
|
|||
/zig
|
||||
|
||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||
- uses: cachix/install-nix-action@7be5dee1421f63d07e71ce6e0a9f8a4b07c2a487 # v31.6.1
|
||||
- uses: cachix/install-nix-action@a809471b5c7c913aa67bec8f459a11a0decc3fce # v31.6.2
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -275,7 +348,7 @@ jobs:
|
|||
trigger-snap:
|
||||
if: github.event_name != 'pull_request'
|
||||
runs-on: namespace-profile-ghostty-xsm
|
||||
needs: build-dist
|
||||
needs: [build-dist, build-snap]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
|
@ -462,7 +535,7 @@ jobs:
|
|||
/zig
|
||||
|
||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||
- uses: cachix/install-nix-action@7be5dee1421f63d07e71ce6e0a9f8a4b07c2a487 # v31.6.1
|
||||
- uses: cachix/install-nix-action@a809471b5c7c913aa67bec8f459a11a0decc3fce # v31.6.2
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -504,7 +577,7 @@ jobs:
|
|||
/zig
|
||||
|
||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||
- uses: cachix/install-nix-action@7be5dee1421f63d07e71ce6e0a9f8a4b07c2a487 # v31.6.1
|
||||
- uses: cachix/install-nix-action@a809471b5c7c913aa67bec8f459a11a0decc3fce # v31.6.2
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -529,6 +602,41 @@ jobs:
|
|||
-Dgtk-x11=${{ matrix.x11 }} \
|
||||
-Dgtk-wayland=${{ matrix.wayland }}
|
||||
|
||||
test-simd:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
simd: ["true", "false"]
|
||||
name: Build -Dsimd=${{ matrix.simd }}
|
||||
runs-on: namespace-profile-ghostty-sm
|
||||
needs: test
|
||||
env:
|
||||
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
||||
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@a289cf5d2fcd6874376aa92f0ef7f99dc923592a # v1.2.17
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
|
||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||
- uses: cachix/install-nix-action@a809471b5c7c913aa67bec8f459a11a0decc3fce # v31.6.2
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
with:
|
||||
name: ghostty
|
||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
nix develop -c zig build test -Dsimd=${{ matrix.simd }}
|
||||
|
||||
test-sentry-linux:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
|
@ -552,7 +660,7 @@ jobs:
|
|||
/zig
|
||||
|
||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||
- uses: cachix/install-nix-action@7be5dee1421f63d07e71ce6e0a9f8a4b07c2a487 # v31.6.1
|
||||
- uses: cachix/install-nix-action@a809471b5c7c913aa67bec8f459a11a0decc3fce # v31.6.2
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -608,7 +716,7 @@ jobs:
|
|||
path: |
|
||||
/nix
|
||||
/zig
|
||||
- uses: cachix/install-nix-action@7be5dee1421f63d07e71ce6e0a9f8a4b07c2a487 # v31.6.1
|
||||
- uses: cachix/install-nix-action@a809471b5c7c913aa67bec8f459a11a0decc3fce # v31.6.2
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -636,7 +744,7 @@ jobs:
|
|||
path: |
|
||||
/nix
|
||||
/zig
|
||||
- uses: cachix/install-nix-action@7be5dee1421f63d07e71ce6e0a9f8a4b07c2a487 # v31.6.1
|
||||
- uses: cachix/install-nix-action@a809471b5c7c913aa67bec8f459a11a0decc3fce # v31.6.2
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -663,7 +771,7 @@ jobs:
|
|||
path: |
|
||||
/nix
|
||||
/zig
|
||||
- uses: cachix/install-nix-action@7be5dee1421f63d07e71ce6e0a9f8a4b07c2a487 # v31.6.1
|
||||
- uses: cachix/install-nix-action@a809471b5c7c913aa67bec8f459a11a0decc3fce # v31.6.2
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -690,7 +798,7 @@ jobs:
|
|||
path: |
|
||||
/nix
|
||||
/zig
|
||||
- uses: cachix/install-nix-action@7be5dee1421f63d07e71ce6e0a9f8a4b07c2a487 # v31.6.1
|
||||
- uses: cachix/install-nix-action@a809471b5c7c913aa67bec8f459a11a0decc3fce # v31.6.2
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -717,7 +825,7 @@ jobs:
|
|||
path: |
|
||||
/nix
|
||||
/zig
|
||||
- uses: cachix/install-nix-action@7be5dee1421f63d07e71ce6e0a9f8a4b07c2a487 # v31.6.1
|
||||
- uses: cachix/install-nix-action@a809471b5c7c913aa67bec8f459a11a0decc3fce # v31.6.2
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -744,7 +852,7 @@ jobs:
|
|||
path: |
|
||||
/nix
|
||||
/zig
|
||||
- uses: cachix/install-nix-action@7be5dee1421f63d07e71ce6e0a9f8a4b07c2a487 # v31.6.1
|
||||
- uses: cachix/install-nix-action@a809471b5c7c913aa67bec8f459a11a0decc3fce # v31.6.2
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -778,7 +886,7 @@ jobs:
|
|||
path: |
|
||||
/nix
|
||||
/zig
|
||||
- uses: cachix/install-nix-action@7be5dee1421f63d07e71ce6e0a9f8a4b07c2a487 # v31.6.1
|
||||
- uses: cachix/install-nix-action@a809471b5c7c913aa67bec8f459a11a0decc3fce # v31.6.2
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -805,7 +913,7 @@ jobs:
|
|||
path: |
|
||||
/nix
|
||||
/zig
|
||||
- uses: cachix/install-nix-action@7be5dee1421f63d07e71ce6e0a9f8a4b07c2a487 # v31.6.1
|
||||
- uses: cachix/install-nix-action@a809471b5c7c913aa67bec8f459a11a0decc3fce # v31.6.2
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -842,7 +950,7 @@ jobs:
|
|||
/zig
|
||||
|
||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||
- uses: cachix/install-nix-action@7be5dee1421f63d07e71ce6e0a9f8a4b07c2a487 # v31.6.1
|
||||
- uses: cachix/install-nix-action@a809471b5c7c913aa67bec8f459a11a0decc3fce # v31.6.2
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -857,6 +965,7 @@ jobs:
|
|||
test-debian-13:
|
||||
name: Test build on Debian 13
|
||||
runs-on: namespace-profile-ghostty-sm
|
||||
timeout-minutes: 10
|
||||
needs: [test, build-dist]
|
||||
steps:
|
||||
- name: Install and configure Namespace CLI
|
||||
|
|
@ -929,7 +1038,7 @@ jobs:
|
|||
/zig
|
||||
|
||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||
- uses: cachix/install-nix-action@7be5dee1421f63d07e71ce6e0a9f8a4b07c2a487 # v31.6.1
|
||||
- uses: cachix/install-nix-action@a809471b5c7c913aa67bec8f459a11a0decc3fce # v31.6.2
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
@ -964,7 +1073,7 @@ jobs:
|
|||
sudo systemctl start ssh
|
||||
|
||||
- name: Set up FreeBSD VM
|
||||
uses: vmactions/freebsd-vm@05856381fab64eeee9b038a0818f6cec649ca17a # v1.2.3
|
||||
uses: vmactions/freebsd-vm@487ce35b96fae3e60d45b521735f5aa436ecfade # v1.2.4
|
||||
with:
|
||||
release: ${{ matrix.release }}
|
||||
copyback: false
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ jobs:
|
|||
/zig
|
||||
|
||||
- name: Setup Nix
|
||||
uses: cachix/install-nix-action@7be5dee1421f63d07e71ce6e0a9f8a4b07c2a487 # v31.6.1
|
||||
uses: cachix/install-nix-action@a809471b5c7c913aa67bec8f459a11a0decc3fce # v31.6.2
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
|
|
|
|||
|
|
@ -184,6 +184,7 @@
|
|||
/po/ko_KR.UTF-8.po @ghostty-org/ko_KR
|
||||
/po/he_IL.UTF-8.po @ghostty-org/he_IL
|
||||
/po/it_IT.UTF-8.po @ghostty-org/it_IT
|
||||
/po/zh_TW.UTF-8.po @ghostty-org/zh_TW
|
||||
|
||||
# Packaging - Snap
|
||||
/snap/ @ghostty-org/snap
|
||||
|
|
|
|||
51
build.zig
51
build.zig
|
|
@ -8,12 +8,25 @@ comptime {
|
|||
}
|
||||
|
||||
pub fn build(b: *std.Build) !void {
|
||||
// This defines all the available build options (e.g. `-D`).
|
||||
// This defines all the available build options (e.g. `-D`). If you
|
||||
// want to know what options are available, you can run `--help` or
|
||||
// you can read `src/build/Config.zig`.
|
||||
const config = try buildpkg.Config.init(b);
|
||||
const test_filter = b.option(
|
||||
[]const u8,
|
||||
const test_filters = b.option(
|
||||
[][]const u8,
|
||||
"test-filter",
|
||||
"Filter for test. Only applies to Zig tests.",
|
||||
) orelse &[0][]const u8{};
|
||||
|
||||
// Ghostty dependencies used by many artifacts.
|
||||
const deps = try buildpkg.SharedDeps.init(b, &config);
|
||||
|
||||
// The modules exported for Zig consumers of libghostty. If you're
|
||||
// writing a Zig program that uses libghostty, read this file.
|
||||
const mod = try buildpkg.GhosttyZig.init(
|
||||
b,
|
||||
&config,
|
||||
&deps,
|
||||
);
|
||||
|
||||
// All our steps which we'll hook up later. The steps are shown
|
||||
|
|
@ -24,6 +37,10 @@ pub fn build(b: *std.Build) !void {
|
|||
"Run the app under valgrind",
|
||||
);
|
||||
const test_step = b.step("test", "Run tests");
|
||||
const test_lib_vt_step = b.step(
|
||||
"test-lib-vt",
|
||||
"Run libghostty-vt tests",
|
||||
);
|
||||
const test_valgrind_step = b.step(
|
||||
"test-valgrind",
|
||||
"Run tests under valgrind",
|
||||
|
|
@ -37,10 +54,6 @@ pub fn build(b: *std.Build) !void {
|
|||
const resources = try buildpkg.GhosttyResources.init(b, &config);
|
||||
const i18n = if (config.i18n) try buildpkg.GhosttyI18n.init(b, &config) else null;
|
||||
|
||||
// Ghostty dependencies used by many artifacts.
|
||||
const deps = try buildpkg.SharedDeps.init(b, &config);
|
||||
if (config.emit_helpgen) deps.help_strings.install();
|
||||
|
||||
// Ghostty executable, the actual runnable Ghostty program.
|
||||
const exe = try buildpkg.GhosttyExe.init(b, &config, &deps);
|
||||
|
||||
|
|
@ -83,6 +96,9 @@ pub fn build(b: *std.Build) !void {
|
|||
&deps,
|
||||
);
|
||||
|
||||
// Helpgen
|
||||
if (config.emit_helpgen) deps.help_strings.install();
|
||||
|
||||
// Runtime "none" is libghostty, anything else is an executable.
|
||||
if (config.app_runtime != .none) {
|
||||
if (config.emit_exe) {
|
||||
|
|
@ -185,7 +201,7 @@ pub fn build(b: *std.Build) !void {
|
|||
run_step.dependOn(&macos_app_native_only.open.step);
|
||||
|
||||
// If we have no test filters, install the tests too
|
||||
if (test_filter == null) {
|
||||
if (test_filters.len == 0) {
|
||||
macos_app_native_only.addTestStepDependencies(test_step);
|
||||
}
|
||||
}
|
||||
|
|
@ -216,11 +232,24 @@ pub fn build(b: *std.Build) !void {
|
|||
run_valgrind_step.dependOn(&run_cmd.step);
|
||||
}
|
||||
|
||||
// Zig module tests
|
||||
{
|
||||
const mod_vt_test = b.addTest(.{
|
||||
.root_module = mod.vt,
|
||||
.target = config.target,
|
||||
.optimize = config.optimize,
|
||||
.filters = test_filters,
|
||||
});
|
||||
const mod_vt_test_run = b.addRunArtifact(mod_vt_test);
|
||||
test_lib_vt_step.dependOn(&mod_vt_test_run.step);
|
||||
}
|
||||
|
||||
// Tests
|
||||
{
|
||||
// Full unit tests
|
||||
const test_exe = b.addTest(.{
|
||||
.name = "ghostty-test",
|
||||
.filters = if (test_filter) |v| &.{v} else &.{},
|
||||
.filters = test_filters,
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
.target = config.baselineTarget(),
|
||||
|
|
@ -230,7 +259,6 @@ pub fn build(b: *std.Build) !void {
|
|||
.unwind_tables = .sync,
|
||||
}),
|
||||
});
|
||||
|
||||
if (config.emit_test_exe) b.installArtifact(test_exe);
|
||||
_ = try deps.add(test_exe);
|
||||
|
||||
|
|
@ -246,6 +274,9 @@ pub fn build(b: *std.Build) !void {
|
|||
const test_run = b.addRunArtifact(test_exe);
|
||||
test_step.dependOn(&test_run.step);
|
||||
|
||||
// Normal tests always test our libghostty modules
|
||||
test_step.dependOn(test_lib_vt_step);
|
||||
|
||||
// Valgrind test running
|
||||
const valgrind_run = b.addSystemCommand(&.{
|
||||
"valgrind",
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@
|
|||
// codeberg ifreund/zig-wayland
|
||||
.url = "https://codeberg.org/ifreund/zig-wayland/archive/f3c5d503e540ada8cbcb056420de240af0c094f7.tar.gz",
|
||||
.hash = "wayland-0.4.0-dev-lQa1kjfIAQCmhhQu3xF0KH-94-TzeMXOqfnP0-Dg6Wyy",
|
||||
.lazy = true,
|
||||
},
|
||||
.zf = .{
|
||||
// natecraddock/zf
|
||||
|
|
@ -59,8 +60,8 @@
|
|||
.gobject = .{
|
||||
// https://github.com/jcollie/ghostty-gobject based on zig_gobject
|
||||
// Temporary until we generate them at build time automatically.
|
||||
.url = "https://github.com/jcollie/ghostty-gobject/releases/download/0.15.1-2025-09-04-48-1/ghostty-gobject-0.15.1-2025-09-04-48-1.tar.zst",
|
||||
.hash = "gobject-0.3.0-Skun7ET3nQAc0LzvO0NAvTiGGnmkF36cnmbeCAF6MB7Z",
|
||||
.url = "https://github.com/ghostty-org/zig-gobject/releases/download/2025-09-20-20-1/ghostty-gobject-2025-09-20-20-1.tar.zst",
|
||||
.hash = "gobject-0.3.0-Skun7ET3nQCqJhDL0KnF_X7M4L7o7JePsJBbrYpEr7UV",
|
||||
.lazy = true,
|
||||
},
|
||||
|
||||
|
|
@ -107,17 +108,19 @@
|
|||
.jetbrains_mono = .{
|
||||
.url = "https://deps.files.ghostty.org/JetBrainsMono-2.304.tar.gz",
|
||||
.hash = "N-V-__8AAIC5lwAVPJJzxnCAahSvZTIlG-HhtOvnM1uh-66x",
|
||||
.lazy = true,
|
||||
},
|
||||
.nerd_fonts_symbols_only = .{
|
||||
.url = "https://deps.files.ghostty.org/NerdFontsSymbolsOnly-3.4.0.tar.gz",
|
||||
.hash = "N-V-__8AAMVLTABmYkLqhZPLXnMl-KyN38R8UVYqGrxqO26s",
|
||||
.lazy = true,
|
||||
},
|
||||
|
||||
// Other
|
||||
.apple_sdk = .{ .path = "./pkg/apple-sdk" },
|
||||
.iterm2_themes = .{
|
||||
.url = "https://deps.files.ghostty.org/ghostty-themes-20250915-162204-b1fe546.tgz",
|
||||
.hash = "N-V-__8AANodAwDnyHwhlOv5cVRn2rx_dTvija-wy5YtTw1B",
|
||||
.url = "https://github.com/mbadolato/iTerm2-Color-Schemes/releases/download/release-20250916-134637-76894f0/ghostty-themes.tgz",
|
||||
.hash = "N-V-__8AAGsjAwAxRB3Y9Akv_HeLfvJA-tIqW6ACnBhWosM3",
|
||||
.lazy = true,
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -24,10 +24,10 @@
|
|||
"url": "https://deps.files.ghostty.org/glslang-12201278a1a05c0ce0b6eb6026c65cd3e9247aa041b1c260324bf29cee559dd23ba1.tar.gz",
|
||||
"hash": "sha256-FKLtu1Ccs+UamlPj9eQ12/WXFgS0uDPmPmB26MCpl7U="
|
||||
},
|
||||
"gobject-0.3.0-Skun7ET3nQAc0LzvO0NAvTiGGnmkF36cnmbeCAF6MB7Z": {
|
||||
"gobject-0.3.0-Skun7ET3nQCqJhDL0KnF_X7M4L7o7JePsJBbrYpEr7UV": {
|
||||
"name": "gobject",
|
||||
"url": "https://github.com/jcollie/ghostty-gobject/releases/download/0.15.1-2025-09-04-48-1/ghostty-gobject-0.15.1-2025-09-04-48-1.tar.zst",
|
||||
"hash": "sha256-h6aKUerGlX2ATVEeoN03eWaqDqvUmKdedgpxrSoHvrY="
|
||||
"url": "https://github.com/ghostty-org/zig-gobject/releases/download/2025-09-20-20-1/ghostty-gobject-2025-09-20-20-1.tar.zst",
|
||||
"hash": "sha256-SXiqGm81aUn6yq1wFXgNTAULdKOHS/Rzkp5OgNkkcXo="
|
||||
},
|
||||
"N-V-__8AALiNBAA-_0gprYr92CjrMj1I5bqNu0TSJOnjFNSr": {
|
||||
"name": "gtk4_layer_shell",
|
||||
|
|
@ -49,10 +49,10 @@
|
|||
"url": "https://deps.files.ghostty.org/imgui-1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402.tar.gz",
|
||||
"hash": "sha256-oF/QHgTPEat4Hig4fGIdLkIPHmBEyOJ6JeYD6pnveGA="
|
||||
},
|
||||
"N-V-__8AANodAwDnyHwhlOv5cVRn2rx_dTvija-wy5YtTw1B": {
|
||||
"N-V-__8AAGsjAwAxRB3Y9Akv_HeLfvJA-tIqW6ACnBhWosM3": {
|
||||
"name": "iterm2_themes",
|
||||
"url": "https://deps.files.ghostty.org/ghostty-themes-20250915-162204-b1fe546.tgz",
|
||||
"hash": "sha256-6rKNFpaUvSbvNZ0/+u0h4I/RRaV5V7xIPQ9y7eNVbCA="
|
||||
"url": "https://github.com/mbadolato/iTerm2-Color-Schemes/releases/download/release-20250916-134637-76894f0/ghostty-themes.tgz",
|
||||
"hash": "sha256-JPY9M50d/n6rGzWt0aQZIU7IBMWru2IAqe9Vu1x5CMw="
|
||||
},
|
||||
"N-V-__8AAIC5lwAVPJJzxnCAahSvZTIlG-HhtOvnM1uh-66x": {
|
||||
"name": "jetbrains_mono",
|
||||
|
|
@ -109,10 +109,10 @@
|
|||
"url": "https://deps.files.ghostty.org/utfcpp-1220d4d18426ca72fc2b7e56ce47273149815501d0d2395c2a98c726b31ba931e641.tar.gz",
|
||||
"hash": "sha256-/8ZooxDndgfTk/PBizJxXyI9oerExNbgV5oR345rWc8="
|
||||
},
|
||||
"uucode-0.0.0-ZZjBPgErQADBJsnLdcZKdRk94lB28CbKC4OrUDPOnSeV": {
|
||||
"uucode-0.0.0-ZZjBPk0GQACuYIoFqT_Vzkvn8Ur_M3dE7o4DNUE65Z7v": {
|
||||
"name": "uucode",
|
||||
"url": "https://github.com/jacobsandlund/uucode/archive/3512203ca991c02b2500392d1d51226c48131c99.tar.gz",
|
||||
"hash": "sha256-nbbeHgvkoMmr5DJN0qRF776hu3waTL85d8dGpvYsZBw="
|
||||
"url": "https://github.com/jacobsandlund/uucode/archive/f748edb9639d9b3c8ae13d6190953f419a615343.tar.gz",
|
||||
"hash": "sha256-j1ZNumH19olw0DHTEb6sChnCZpYhK9+1Q/rr6nxPRcQ="
|
||||
},
|
||||
"vaxis-0.1.0-BWNV_FUICQAFZnTCL11TUvnUr1Y0_ZdqtXHhd51d76Rn": {
|
||||
"name": "vaxis",
|
||||
|
|
|
|||
|
|
@ -123,11 +123,11 @@ in
|
|||
};
|
||||
}
|
||||
{
|
||||
name = "gobject-0.3.0-Skun7ET3nQAc0LzvO0NAvTiGGnmkF36cnmbeCAF6MB7Z";
|
||||
name = "gobject-0.3.0-Skun7ET3nQCqJhDL0KnF_X7M4L7o7JePsJBbrYpEr7UV";
|
||||
path = fetchZigArtifact {
|
||||
name = "gobject";
|
||||
url = "https://github.com/jcollie/ghostty-gobject/releases/download/0.15.1-2025-09-04-48-1/ghostty-gobject-0.15.1-2025-09-04-48-1.tar.zst";
|
||||
hash = "sha256-h6aKUerGlX2ATVEeoN03eWaqDqvUmKdedgpxrSoHvrY=";
|
||||
url = "https://github.com/ghostty-org/zig-gobject/releases/download/2025-09-20-20-1/ghostty-gobject-2025-09-20-20-1.tar.zst";
|
||||
hash = "sha256-SXiqGm81aUn6yq1wFXgNTAULdKOHS/Rzkp5OgNkkcXo=";
|
||||
};
|
||||
}
|
||||
{
|
||||
|
|
@ -163,11 +163,11 @@ in
|
|||
};
|
||||
}
|
||||
{
|
||||
name = "N-V-__8AANodAwDnyHwhlOv5cVRn2rx_dTvija-wy5YtTw1B";
|
||||
name = "N-V-__8AAGsjAwAxRB3Y9Akv_HeLfvJA-tIqW6ACnBhWosM3";
|
||||
path = fetchZigArtifact {
|
||||
name = "iterm2_themes";
|
||||
url = "https://deps.files.ghostty.org/ghostty-themes-20250915-162204-b1fe546.tgz";
|
||||
hash = "sha256-6rKNFpaUvSbvNZ0/+u0h4I/RRaV5V7xIPQ9y7eNVbCA=";
|
||||
url = "https://github.com/mbadolato/iTerm2-Color-Schemes/releases/download/release-20250916-134637-76894f0/ghostty-themes.tgz";
|
||||
hash = "sha256-JPY9M50d/n6rGzWt0aQZIU7IBMWru2IAqe9Vu1x5CMw=";
|
||||
};
|
||||
}
|
||||
{
|
||||
|
|
@ -259,11 +259,11 @@ in
|
|||
};
|
||||
}
|
||||
{
|
||||
name = "uucode-0.0.0-ZZjBPgErQADBJsnLdcZKdRk94lB28CbKC4OrUDPOnSeV";
|
||||
name = "uucode-0.0.0-ZZjBPk0GQACuYIoFqT_Vzkvn8Ur_M3dE7o4DNUE65Z7v";
|
||||
path = fetchZigArtifact {
|
||||
name = "uucode";
|
||||
url = "https://github.com/jacobsandlund/uucode/archive/3512203ca991c02b2500392d1d51226c48131c99.tar.gz";
|
||||
hash = "sha256-nbbeHgvkoMmr5DJN0qRF776hu3waTL85d8dGpvYsZBw=";
|
||||
url = "https://github.com/jacobsandlund/uucode/archive/f748edb9639d9b3c8ae13d6190953f419a615343.tar.gz";
|
||||
hash = "sha256-j1ZNumH19olw0DHTEb6sChnCZpYhK9+1Q/rr6nxPRcQ=";
|
||||
};
|
||||
}
|
||||
{
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ https://deps.files.ghostty.org/breakpad-b99f444ba5f6b98cac261cbb391d8766b34a5918
|
|||
https://deps.files.ghostty.org/fontconfig-2.14.2.tar.gz
|
||||
https://deps.files.ghostty.org/freetype-1220b81f6ecfb3fd222f76cf9106fecfa6554ab07ec7fdc4124b9bb063ae2adf969d.tar.gz
|
||||
https://deps.files.ghostty.org/gettext-0.24.tar.gz
|
||||
https://deps.files.ghostty.org/ghostty-themes-20250915-162204-b1fe546.tgz
|
||||
https://deps.files.ghostty.org/glslang-12201278a1a05c0ce0b6eb6026c65cd3e9247aa041b1c260324bf29cee559dd23ba1.tar.gz
|
||||
https://deps.files.ghostty.org/gtk4-layer-shell-1.1.0.tar.gz
|
||||
https://deps.files.ghostty.org/harfbuzz-11.0.0.tar.xz
|
||||
|
|
@ -28,8 +27,9 @@ https://deps.files.ghostty.org/wuffs-122037b39d577ec2db3fd7b2130e7b69ef6cc1807d6
|
|||
https://deps.files.ghostty.org/zig_js-12205a66d423259567764fa0fc60c82be35365c21aeb76c5a7dc99698401f4f6fefc.tar.gz
|
||||
https://deps.files.ghostty.org/ziglyph-b89d43d1e3fb01b6074bc1f7fc980324b04d26a5.tar.gz
|
||||
https://deps.files.ghostty.org/zlib-1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb.tar.gz
|
||||
https://github.com/jacobsandlund/uucode/archive/3512203ca991c02b2500392d1d51226c48131c99.tar.gz
|
||||
https://github.com/jcollie/ghostty-gobject/releases/download/0.15.1-2025-09-04-48-1/ghostty-gobject-0.15.1-2025-09-04-48-1.tar.zst
|
||||
https://github.com/ghostty-org/zig-gobject/releases/download/2025-09-20-20-1/ghostty-gobject-2025-09-20-20-1.tar.zst
|
||||
https://github.com/jacobsandlund/uucode/archive/f748edb9639d9b3c8ae13d6190953f419a615343.tar.gz
|
||||
https://github.com/mbadolato/iTerm2-Color-Schemes/releases/download/release-20250916-134637-76894f0/ghostty-themes.tgz
|
||||
https://github.com/mitchellh/libxev/archive/7f803181b158a10fec8619f793e3b4df515566cb.tar.gz
|
||||
https://github.com/mitchellh/zig-objc/archive/c9e917a4e15a983b672ca779c7985d738a2d517c.tar.gz
|
||||
https://github.com/natecraddock/zf/archive/7aacbe6d155d64d15937ca95ca6c014905eb531f.tar.gz
|
||||
|
|
|
|||
189
example/app.ts
189
example/app.ts
|
|
@ -1,189 +0,0 @@
|
|||
import { ZigJS } from "zig-js";
|
||||
|
||||
const zjs = new ZigJS();
|
||||
const importObject = {
|
||||
module: {},
|
||||
env: {
|
||||
memory: new WebAssembly.Memory({
|
||||
initial: 25,
|
||||
maximum: 65536,
|
||||
shared: true,
|
||||
}),
|
||||
log: (ptr: number, len: number) => {
|
||||
const arr = new Uint8ClampedArray(zjs.memory.buffer, ptr, len);
|
||||
const data = arr.slice();
|
||||
const str = new TextDecoder("utf-8").decode(data);
|
||||
console.log(str);
|
||||
},
|
||||
},
|
||||
|
||||
...zjs.importObject(),
|
||||
};
|
||||
|
||||
const url = new URL("ghostty-wasm.wasm", import.meta.url);
|
||||
fetch(url.href)
|
||||
.then((response) => response.arrayBuffer())
|
||||
.then((bytes) => WebAssembly.instantiate(bytes, importObject))
|
||||
.then((results) => {
|
||||
const memory = importObject.env.memory;
|
||||
const {
|
||||
malloc,
|
||||
free,
|
||||
config_new,
|
||||
config_free,
|
||||
config_load_string,
|
||||
config_finalize,
|
||||
face_new,
|
||||
face_free,
|
||||
face_render_glyph,
|
||||
face_debug_canvas,
|
||||
deferred_face_new,
|
||||
deferred_face_free,
|
||||
deferred_face_load,
|
||||
deferred_face_face,
|
||||
group_new,
|
||||
group_free,
|
||||
group_add_face,
|
||||
group_init_sprite_face,
|
||||
group_index_for_codepoint,
|
||||
group_render_glyph,
|
||||
group_cache_new,
|
||||
group_cache_free,
|
||||
group_cache_index_for_codepoint,
|
||||
group_cache_render_glyph,
|
||||
group_cache_atlas_grayscale,
|
||||
group_cache_atlas_color,
|
||||
atlas_new,
|
||||
atlas_free,
|
||||
atlas_debug_canvas,
|
||||
shaper_new,
|
||||
shaper_free,
|
||||
shaper_test,
|
||||
} = results.instance.exports;
|
||||
// Give us access to the zjs value for debugging.
|
||||
globalThis.zjs = zjs;
|
||||
console.log(zjs);
|
||||
|
||||
// Initialize our zig-js memory
|
||||
zjs.memory = memory;
|
||||
|
||||
// Helpers
|
||||
const makeStr = (str) => {
|
||||
const utf8 = new TextEncoder().encode(str);
|
||||
const ptr = malloc(utf8.byteLength);
|
||||
new Uint8Array(memory.buffer, ptr).set(utf8);
|
||||
return { ptr: ptr, len: utf8.byteLength };
|
||||
};
|
||||
|
||||
// Create our config
|
||||
const config = config_new();
|
||||
const config_str = makeStr("font-family = monospace");
|
||||
config_load_string(config, config_str.ptr, config_str.len);
|
||||
config_finalize(config);
|
||||
free(config_str.ptr);
|
||||
|
||||
// Create our atlas
|
||||
// const atlas = atlas_new(512, 0 /* grayscale */);
|
||||
|
||||
// Create some memory for our string
|
||||
const font_name = makeStr("monospace");
|
||||
|
||||
// Initialize our deferred face
|
||||
// const df = deferred_face_new(font_ptr, font.byteLength, 0 /* text */);
|
||||
//deferred_face_load(df, 72 /* size */);
|
||||
//const face = deferred_face_face(df);
|
||||
|
||||
// Initialize our font face
|
||||
//const face = face_new(font_ptr, font.byteLength, 72 /* size in px */);
|
||||
//free(font_ptr);
|
||||
|
||||
// Create our group
|
||||
const group = group_new(32 /* size */);
|
||||
group_add_face(
|
||||
group,
|
||||
0 /* regular */,
|
||||
deferred_face_new(font_name.ptr, font_name.len, 0 /* text */),
|
||||
);
|
||||
group_add_face(
|
||||
group,
|
||||
0 /* regular */,
|
||||
deferred_face_new(font_name.ptr, font_name.len, 1 /* emoji */),
|
||||
);
|
||||
|
||||
// Initialize our sprite font, without this we just use the browser.
|
||||
group_init_sprite_face(group);
|
||||
|
||||
// Create our group cache
|
||||
const group_cache = group_cache_new(group);
|
||||
|
||||
// Render a glyph
|
||||
// for (let i = 33; i <= 126; i++) {
|
||||
// const font_idx = group_cache_index_for_codepoint(group_cache, i, 0, -1);
|
||||
// group_cache_render_glyph(group_cache, font_idx, i, 0);
|
||||
// //face_render_glyph(face, atlas, i);
|
||||
// }
|
||||
//
|
||||
// const emoji = ["🐏","🌞","🌚","🍱","💿","🐈","📃","📀","🕡","🙃"];
|
||||
// for (let i = 0; i < emoji.length; i++) {
|
||||
// const cp = emoji[i].codePointAt(0);
|
||||
// const font_idx = group_cache_index_for_codepoint(group_cache, cp, 0, -1 /* best choice */);
|
||||
// group_cache_render_glyph(group_cache, font_idx, cp, 0);
|
||||
// }
|
||||
|
||||
for (let i = 0x2500; i <= 0x257f; i++) {
|
||||
const font_idx = group_cache_index_for_codepoint(group_cache, i, 0, -1);
|
||||
group_cache_render_glyph(group_cache, font_idx, i, 0);
|
||||
}
|
||||
for (let i = 0x2580; i <= 0x259f; i++) {
|
||||
const font_idx = group_cache_index_for_codepoint(group_cache, i, 0, -1);
|
||||
group_cache_render_glyph(group_cache, font_idx, i, 0);
|
||||
}
|
||||
for (let i = 0x2800; i <= 0x28ff; i++) {
|
||||
const font_idx = group_cache_index_for_codepoint(group_cache, i, 0, -1);
|
||||
group_cache_render_glyph(group_cache, font_idx, i, 0);
|
||||
}
|
||||
for (let i = 0x1fb00; i <= 0x1fb3b; i++) {
|
||||
const font_idx = group_cache_index_for_codepoint(group_cache, i, 0, -1);
|
||||
group_cache_render_glyph(group_cache, font_idx, i, 0);
|
||||
}
|
||||
for (let i = 0x1fb3c; i <= 0x1fb6b; i++) {
|
||||
const font_idx = group_cache_index_for_codepoint(group_cache, i, 0, -1);
|
||||
group_cache_render_glyph(group_cache, font_idx, i, 0);
|
||||
}
|
||||
|
||||
//face_render_glyph(face, atlas, "橋".codePointAt(0));
|
||||
//face_render_glyph(face, atlas, "p".codePointAt(0));
|
||||
|
||||
// Debug our canvas
|
||||
//face_debug_canvas(face);
|
||||
|
||||
// Let's try shaping
|
||||
const shaper = shaper_new(120);
|
||||
//const input = makeStr("hello🐏");
|
||||
const input = makeStr("hello🐏👍🏽");
|
||||
shaper_test(shaper, group_cache, input.ptr, input.len);
|
||||
|
||||
const cp = 1114112;
|
||||
const font_idx = group_cache_index_for_codepoint(
|
||||
group_cache,
|
||||
cp,
|
||||
0,
|
||||
-1 /* best choice */,
|
||||
);
|
||||
group_cache_render_glyph(group_cache, font_idx, cp, -1);
|
||||
|
||||
// Debug our atlas canvas
|
||||
{
|
||||
const atlas = group_cache_atlas_grayscale(group_cache);
|
||||
const id = atlas_debug_canvas(atlas);
|
||||
document.getElementById("atlas-canvas").append(zjs.deleteValue(id));
|
||||
}
|
||||
|
||||
{
|
||||
const atlas = group_cache_atlas_color(group_cache);
|
||||
const id = atlas_debug_canvas(atlas);
|
||||
document.getElementById("atlas-color-canvas").append(zjs.deleteValue(id));
|
||||
}
|
||||
|
||||
//face_free(face);
|
||||
});
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Ghostty Example</title>
|
||||
<script type="module" src="app.ts"></script>
|
||||
</head>
|
||||
<body>
|
||||
<p>Open your console, we are just debugging here.</p>
|
||||
<p>The current <b>grayscale</b> font atlas is rendered below.</p>
|
||||
<div><div id="atlas-canvas" style="display: inline-block; border: 1px solid green;"></div></div>
|
||||
<p>The current <b>color</b> font atlas is rendered below.</p>
|
||||
<div><div id="atlas-color-canvas" style="display: inline-block; border: 1px solid blue;"></div></div>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,22 +0,0 @@
|
|||
{
|
||||
"name": "ghostty example",
|
||||
"version": "0.1.0",
|
||||
"description": "Example showing ghostty and wasm.",
|
||||
"source": "index.html",
|
||||
"browserslist": "> 0.5%, last 2 versions, not dead",
|
||||
"scripts": {
|
||||
"start": "parcel",
|
||||
"build": "parcel build",
|
||||
"check": "tsc --noEmit"
|
||||
},
|
||||
"author": "Mitchell Hashimoto",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@parcel/transformer-inline-string": "^2.8.0",
|
||||
"parcel": "^2.8.0",
|
||||
"typescript": "^4.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"zig-js": "file:../vendor/zig-js/js"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# Example: `ghostty-vt` Zig Module
|
||||
|
||||
This contains a simple example of how to use the `ghostty-vt` Zig module
|
||||
exported by Ghostty to have access to a production grade terminal emulator.
|
||||
|
||||
Requires the Zig version stated in the `build.zig.zon` file.
|
||||
|
||||
## Usage
|
||||
|
||||
Run the program:
|
||||
|
||||
```shell-session
|
||||
zig build run
|
||||
```
|
||||
|
|
@ -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 test_step = b.step("test", "Run unit tests");
|
||||
|
||||
const exe_mod = b.createModule(.{
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
// You'll want to use a lazy dependency here so that ghostty is only
|
||||
// downloaded if you actually need it.
|
||||
if (b.lazyDependency("ghostty", .{})) |dep| {
|
||||
exe_mod.addImport(
|
||||
"ghostty-vt",
|
||||
dep.module("ghostty-vt"),
|
||||
);
|
||||
}
|
||||
|
||||
// Exe
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "zig_vt",
|
||||
.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);
|
||||
|
||||
// Test
|
||||
const exe_unit_tests = b.addTest(.{
|
||||
.root_module = exe_mod,
|
||||
});
|
||||
const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
|
||||
test_step.dependOn(&run_exe_unit_tests.step);
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
.{
|
||||
.name = .zig_vt,
|
||||
.version = "0.0.0",
|
||||
.fingerprint = 0x6045575a7a8387e6,
|
||||
.minimum_zig_version = "0.14.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",
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
const std = @import("std");
|
||||
const ghostty_vt = @import("ghostty-vt");
|
||||
|
||||
pub fn main() !void {
|
||||
// Use a debug allocator so we get leak checking. You probably want
|
||||
// to replace this for release builds.
|
||||
var gpa: std.heap.DebugAllocator(.{}) = .init;
|
||||
defer _ = gpa.deinit();
|
||||
const alloc = gpa.allocator();
|
||||
|
||||
// Initialize a terminal.
|
||||
var t: ghostty_vt.Terminal = try .init(alloc, .{
|
||||
.cols = 6,
|
||||
.rows = 40,
|
||||
});
|
||||
defer t.deinit(alloc);
|
||||
|
||||
// Write some text. It'll wrap because this is too long for our
|
||||
// columns size above (6).
|
||||
try t.printString("Hello, World!");
|
||||
|
||||
// Get the plain string view of the terminal screen.
|
||||
const str = try t.plainString(alloc);
|
||||
defer alloc.free(str);
|
||||
std.debug.print("{s}\n", .{str});
|
||||
}
|
||||
18
flake.lock
18
flake.lock
|
|
@ -49,15 +49,15 @@
|
|||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1755972213,
|
||||
"narHash": "sha256-VYK7aDAv8H1enXn1ECRHmGbeY6RqLnNwUJkOwloIsko=",
|
||||
"rev": "73e96df7cff5783f45e21342a75a1540c4eddce4",
|
||||
"lastModified": 1758360447,
|
||||
"narHash": "sha256-XDY3A83bclygHDtesRoaRTafUd80Q30D/Daf9KSG6bs=",
|
||||
"rev": "8eaee110344796db060382e15d3af0a9fc396e0e",
|
||||
"type": "tarball",
|
||||
"url": "https://releases.nixos.org/nixos/unstable-small/nixos-25.11pre850642.73e96df7cff5/nixexprs.tar.xz"
|
||||
"url": "https://releases.nixos.org/nixos/unstable/nixos-25.11pre864002.8eaee1103447/nixexprs.tar.xz"
|
||||
},
|
||||
"original": {
|
||||
"type": "tarball",
|
||||
"url": "https://channels.nixos.org/nixos-unstable-small/nixexprs.tar.xz"
|
||||
"url": "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
|
|
@ -115,17 +115,17 @@
|
|||
"nixpkgs": "nixpkgs_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1757167408,
|
||||
"narHash": "sha256-4XyJ6fmKd9wgJ7vHUQuULYy5ps2gUgkkDk/PrJb2OPY=",
|
||||
"lastModified": 1758405547,
|
||||
"narHash": "sha256-WgaDgvIZMPvlZcZrpPMjkaalTBnGF2lTG+62znXctWM=",
|
||||
"owner": "jcollie",
|
||||
"repo": "zon2nix",
|
||||
"rev": "dc78177e2ad28d5a407c9e783ee781bd559d7dd5",
|
||||
"rev": "bf983aa90ff169372b9fa8c02e57ea75e0b42245",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "jcollie",
|
||||
"repo": "zon2nix",
|
||||
"rev": "dc78177e2ad28d5a407c9e783ee781bd559d7dd5",
|
||||
"rev": "bf983aa90ff169372b9fa8c02e57ea75e0b42245",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
};
|
||||
|
||||
zon2nix = {
|
||||
url = "github:jcollie/zon2nix?rev=dc78177e2ad28d5a407c9e783ee781bd559d7dd5";
|
||||
url = "github:jcollie/zon2nix?rev=bf983aa90ff169372b9fa8c02e57ea75e0b42245";
|
||||
inputs = {
|
||||
# Don't override nixpkgs until Zig 0.15 is available in the Nix branch
|
||||
# we are using for "normal" builds.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
app-id: com.mitchellh.ghostty-debug
|
||||
runtime: org.gnome.Platform
|
||||
runtime-version: "48"
|
||||
runtime-version: "49"
|
||||
sdk: org.gnome.Sdk
|
||||
default-branch: tip
|
||||
command: ghostty
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
app-id: com.mitchellh.ghostty
|
||||
runtime: org.gnome.Platform
|
||||
runtime-version: "48"
|
||||
runtime-version: "49"
|
||||
sdk: org.gnome.Sdk
|
||||
default-branch: tip
|
||||
command: ghostty
|
||||
|
|
|
|||
|
|
@ -30,19 +30,6 @@ modules:
|
|||
contents: INPUT(libbz2.so)
|
||||
dest-filename: libbzip2.so
|
||||
|
||||
- name: blueprint-compiler
|
||||
buildsystem: meson
|
||||
cleanup:
|
||||
- "*"
|
||||
sources:
|
||||
- type: git
|
||||
url: https://gitlab.gnome.org/jwestman/blueprint-compiler.git
|
||||
tag: v0.16.0
|
||||
commit: 04ef0944db56ab01307a29aaa7303df6067cb3c0
|
||||
x-checker-data:
|
||||
type: git
|
||||
tag-pattern: ^v([\d.]+)$
|
||||
|
||||
- name: gtk4-layer-shell
|
||||
buildsystem: meson
|
||||
sources:
|
||||
|
|
|
|||
|
|
@ -31,9 +31,9 @@
|
|||
},
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://github.com/jcollie/ghostty-gobject/releases/download/0.15.1-2025-09-04-48-1/ghostty-gobject-0.15.1-2025-09-04-48-1.tar.zst",
|
||||
"dest": "vendor/p/gobject-0.3.0-Skun7ET3nQAc0LzvO0NAvTiGGnmkF36cnmbeCAF6MB7Z",
|
||||
"sha256": "87a68a51eac6957d804d511ea0dd377966aa0eabd498a75e760a71ad2a07beb6"
|
||||
"url": "https://github.com/ghostty-org/zig-gobject/releases/download/2025-09-20-20-1/ghostty-gobject-2025-09-20-20-1.tar.zst",
|
||||
"dest": "vendor/p/gobject-0.3.0-Skun7ET3nQCqJhDL0KnF_X7M4L7o7JePsJBbrYpEr7UV",
|
||||
"sha256": "4978aa1a6f356949facaad7015780d4c050b74a3874bf473929e4e80d924717a"
|
||||
},
|
||||
{
|
||||
"type": "archive",
|
||||
|
|
@ -61,9 +61,9 @@
|
|||
},
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://deps.files.ghostty.org/ghostty-themes-20250915-162204-b1fe546.tgz",
|
||||
"dest": "vendor/p/N-V-__8AANodAwDnyHwhlOv5cVRn2rx_dTvija-wy5YtTw1B",
|
||||
"sha256": "eab28d169694bd26ef359d3ffaed21e08fd145a57957bc483d0f72ede3556c20"
|
||||
"url": "https://github.com/mbadolato/iTerm2-Color-Schemes/releases/download/release-20250916-134637-76894f0/ghostty-themes.tgz",
|
||||
"dest": "vendor/p/N-V-__8AAGsjAwAxRB3Y9Akv_HeLfvJA-tIqW6ACnBhWosM3",
|
||||
"sha256": "24f63d339d1dfe7eab1b35add1a419214ec804c5abbb6200a9ef55bb5c7908cc"
|
||||
},
|
||||
{
|
||||
"type": "archive",
|
||||
|
|
@ -133,9 +133,9 @@
|
|||
},
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://github.com/jacobsandlund/uucode/archive/3512203ca991c02b2500392d1d51226c48131c99.tar.gz",
|
||||
"dest": "vendor/p/uucode-0.0.0-ZZjBPgErQADBJsnLdcZKdRk94lB28CbKC4OrUDPOnSeV",
|
||||
"sha256": "9db6de1e0be4a0c9abe4324dd2a445efbea1bb7c1a4cbf3977c746a6f62c641c"
|
||||
"url": "https://github.com/jacobsandlund/uucode/archive/f748edb9639d9b3c8ae13d6190953f419a615343.tar.gz",
|
||||
"dest": "vendor/p/uucode-0.0.0-ZZjBPk0GQACuYIoFqT_Vzkvn8Ur_M3dE7o4DNUE65Z7v",
|
||||
"sha256": "8f564dba61f5f68970d031d311beac0a19c26696212bdfb543faebea7c4f45c4"
|
||||
},
|
||||
{
|
||||
"type": "git",
|
||||
|
|
|
|||
|
|
@ -860,7 +860,12 @@ class AppDelegate: NSObject,
|
|||
} else {
|
||||
GlobalEventTap.shared.disable()
|
||||
}
|
||||
}
|
||||
|
||||
/// Sync the appearance of our app with the theme specified in the config.
|
||||
private func syncAppearance(config: Ghostty.Config) {
|
||||
NSApplication.shared.appearance = .init(ghosttyConfig: config)
|
||||
|
||||
switch (config.macosIcon) {
|
||||
case .official:
|
||||
self.appIcon = nil
|
||||
|
|
@ -909,11 +914,6 @@ class AppDelegate: NSObject,
|
|||
}
|
||||
}
|
||||
|
||||
/// Sync the appearance of our app with the theme specified in the config.
|
||||
private func syncAppearance(config: Ghostty.Config) {
|
||||
NSApplication.shared.appearance = .init(ghosttyConfig: config)
|
||||
}
|
||||
|
||||
//MARK: - Restorable State
|
||||
|
||||
/// We support NSSecureCoding for restorable state. Required as of macOS Sonoma (14) but a good idea anyways.
|
||||
|
|
|
|||
|
|
@ -21,13 +21,13 @@ class QuickTerminalController: BaseTerminalController {
|
|||
// The active space when the quick terminal was last shown.
|
||||
private var previousActiveSpace: CGSSpace? = nil
|
||||
|
||||
/// The window frame saved when the quick terminal's surface tree becomes empty.
|
||||
/// The saved state when the quick terminal's surface tree becomes empty.
|
||||
///
|
||||
/// This preserves the user's window size and position when all terminal surfaces
|
||||
/// are closed (e.g., via the `exit` command). When a new surface is created,
|
||||
/// the window will be restored to this frame, preventing SwiftUI from resetting
|
||||
/// the window to its default minimum size.
|
||||
private var lastClosedFrame: NSRect? = nil
|
||||
private var lastClosedFrames: NSMapTable<NSScreen, LastClosedState>
|
||||
|
||||
/// Non-nil if we have hidden dock state.
|
||||
private var hiddenDock: HiddenDock? = nil
|
||||
|
|
@ -45,6 +45,10 @@ class QuickTerminalController: BaseTerminalController {
|
|||
) {
|
||||
self.position = position
|
||||
self.derivedConfig = DerivedConfig(ghostty.config)
|
||||
|
||||
// This is a weak to strong mapping, so that our keys being NSScreens
|
||||
// can remove themselves when they disappear.
|
||||
self.lastClosedFrames = .weakToStrongObjects()
|
||||
|
||||
// Important detail here: we initialize with an empty surface tree so
|
||||
// that we don't start a terminal process. This gets started when the
|
||||
|
|
@ -360,8 +364,9 @@ class QuickTerminalController: BaseTerminalController {
|
|||
guard let screen = derivedConfig.quickTerminalScreen.screen else { return }
|
||||
|
||||
// Grab our last closed frame to use, and clear our state since we're animating in.
|
||||
let lastClosedFrame = self.lastClosedFrame
|
||||
self.lastClosedFrame = nil
|
||||
// We only use the last closed frame if we're opening on the same screen.
|
||||
let lastClosedFrame: NSRect? = lastClosedFrames.object(forKey: screen)?.frame
|
||||
lastClosedFrames.removeObject(forKey: screen)
|
||||
|
||||
// Move our window off screen to the initial animation position.
|
||||
position.setInitial(
|
||||
|
|
@ -491,8 +496,8 @@ class QuickTerminalController: BaseTerminalController {
|
|||
// the user's preferred window size and position for when the quick
|
||||
// terminal is reactivated with a new surface. Without this, SwiftUI
|
||||
// would reset the window to its minimum content size.
|
||||
if window.frame.width > 0 && window.frame.height > 0 {
|
||||
lastClosedFrame = window.frame
|
||||
if window.frame.width > 0 && window.frame.height > 0, let screen = window.screen {
|
||||
lastClosedFrames.setObject(.init(frame: window.frame), forKey: screen)
|
||||
}
|
||||
|
||||
// If we hid the dock then we unhide it.
|
||||
|
|
@ -715,6 +720,14 @@ class QuickTerminalController: BaseTerminalController {
|
|||
hidden = false
|
||||
}
|
||||
}
|
||||
|
||||
private class LastClosedState {
|
||||
let frame: NSRect
|
||||
|
||||
init(frame: NSRect) {
|
||||
self.frame = frame
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Notification.Name {
|
||||
|
|
|
|||
|
|
@ -55,7 +55,10 @@ class ServiceProvider: NSObject {
|
|||
_ = TerminalController.newWindow(delegate.ghostty, withBaseConfig: config)
|
||||
|
||||
case .tab:
|
||||
_ = TerminalController.newTab(delegate.ghostty, withBaseConfig: config)
|
||||
_ = TerminalController.newTab(
|
||||
delegate.ghostty,
|
||||
from: TerminalController.preferredParent?.window,
|
||||
withBaseConfig: config)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -688,6 +688,8 @@ class BaseTerminalController: NSWindowController,
|
|||
surfaceTree.contains(titleSurface) {
|
||||
// If we have a surface, we want to listen for title changes.
|
||||
titleSurface.$title
|
||||
.combineLatest(titleSurface.$bell)
|
||||
.map { [weak self] in self?.computeTitle(title: $0, bell: $1) ?? "" }
|
||||
.sink { [weak self] in self?.titleDidChange(to: $0) }
|
||||
.store(in: &focusedSurfaceCancellables)
|
||||
} else {
|
||||
|
|
@ -695,8 +697,17 @@ class BaseTerminalController: NSWindowController,
|
|||
titleDidChange(to: "👻")
|
||||
}
|
||||
}
|
||||
|
||||
private func computeTitle(title: String, bell: Bool) -> String {
|
||||
var result = title
|
||||
if (bell && ghostty.config.bellFeatures.contains(.title)) {
|
||||
result = "🔔 \(result)"
|
||||
}
|
||||
|
||||
func titleDidChange(to: String) {
|
||||
return result
|
||||
}
|
||||
|
||||
private func titleDidChange(to: String) {
|
||||
guard let window else { return }
|
||||
|
||||
// Set the main window title
|
||||
|
|
@ -863,14 +874,6 @@ class BaseTerminalController: NSWindowController,
|
|||
// Everything beyond here is setting up the window
|
||||
guard let window else { return }
|
||||
|
||||
// If there is a hardcoded title in the configuration, we set that
|
||||
// immediately. Future `set_title` apprt actions will override this
|
||||
// if necessary but this ensures our window loads with the proper
|
||||
// title immediately rather than on another event loop tick (see #5934)
|
||||
if let title = derivedConfig.title {
|
||||
window.title = title
|
||||
}
|
||||
|
||||
// We always initialize our fullscreen style to native if we can because
|
||||
// initialization sets up some state (i.e. observers). If its set already
|
||||
// somehow we don't do this.
|
||||
|
|
@ -1072,20 +1075,17 @@ class BaseTerminalController: NSWindowController,
|
|||
}
|
||||
|
||||
private struct DerivedConfig {
|
||||
let title: String?
|
||||
let macosTitlebarProxyIcon: Ghostty.MacOSTitlebarProxyIcon
|
||||
let windowStepResize: Bool
|
||||
let focusFollowsMouse: Bool
|
||||
|
||||
init() {
|
||||
self.title = nil
|
||||
self.macosTitlebarProxyIcon = .visible
|
||||
self.windowStepResize = false
|
||||
self.focusFollowsMouse = false
|
||||
}
|
||||
|
||||
init(_ config: Ghostty.Config) {
|
||||
self.title = config.title
|
||||
self.macosTitlebarProxyIcon = config.macosTitlebarProxyIcon
|
||||
self.windowStepResize = config.windowStepResize
|
||||
self.focusFollowsMouse = config.focusFollowsMouse
|
||||
|
|
|
|||
|
|
@ -184,8 +184,14 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
|
|||
static var preferredParent: TerminalController? {
|
||||
all.first {
|
||||
$0.window?.isMainWindow ?? false
|
||||
} ?? all.last
|
||||
} ?? lastMain ?? all.last
|
||||
}
|
||||
|
||||
// The last controller to be main. We use this when paired with "preferredParent"
|
||||
// to find the preferred window to attach new tabs, perform actions, etc. We
|
||||
// always prefer the main window but if there isn't any (because we're triggered
|
||||
// by something like an App Intent) then we prefer the most previous main.
|
||||
static private(set) weak var lastMain: TerminalController? = nil
|
||||
|
||||
/// The "new window" action.
|
||||
static func newWindow(
|
||||
|
|
@ -1036,6 +1042,9 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
|
|||
if let window {
|
||||
LastWindowPosition.shared.save(window)
|
||||
}
|
||||
|
||||
// Remember our last main
|
||||
Self.lastMain = self
|
||||
}
|
||||
|
||||
// Called when the window will be encoded. We handle the data encoding here in the
|
||||
|
|
|
|||
|
|
@ -49,6 +49,14 @@ class TerminalWindow: NSWindow {
|
|||
|
||||
// Setup our initial config
|
||||
derivedConfig = .init(config)
|
||||
|
||||
// If there is a hardcoded title in the configuration, we set that
|
||||
// immediately. Future `set_title` apprt actions will override this
|
||||
// if necessary but this ensures our window loads with the proper
|
||||
// title immediately rather than on another event loop tick (see #5934)
|
||||
if let title = derivedConfig.title {
|
||||
self.title = title
|
||||
}
|
||||
|
||||
// If window decorations are disabled, remove our title
|
||||
if (!config.windowDecorations) { styleMask.remove(.titled) }
|
||||
|
|
@ -408,11 +416,19 @@ class TerminalWindow: NSWindow {
|
|||
return
|
||||
}
|
||||
|
||||
// Orient based on the top left of the primary monitor
|
||||
let frame = screen.visibleFrame
|
||||
setFrameOrigin(.init(
|
||||
x: frame.minX + CGFloat(x),
|
||||
y: frame.maxY - (CGFloat(y) + frame.height)))
|
||||
// Convert top-left coordinates to bottom-left origin using our utility extension
|
||||
let origin = screen.origin(
|
||||
fromTopLeftOffsetX: CGFloat(x),
|
||||
offsetY: CGFloat(y),
|
||||
windowSize: frame.size)
|
||||
|
||||
// Clamp the origin to ensure the window stays fully visible on screen
|
||||
var safeOrigin = origin
|
||||
let vf = screen.visibleFrame
|
||||
safeOrigin.x = min(max(safeOrigin.x, vf.minX), vf.maxX - frame.width)
|
||||
safeOrigin.y = min(max(safeOrigin.y, vf.minY), vf.maxY - frame.height)
|
||||
|
||||
setFrameOrigin(safeOrigin)
|
||||
}
|
||||
|
||||
private func hideWindowButtons() {
|
||||
|
|
@ -424,17 +440,20 @@ class TerminalWindow: NSWindow {
|
|||
// MARK: Config
|
||||
|
||||
struct DerivedConfig {
|
||||
let title: String?
|
||||
let backgroundColor: NSColor
|
||||
let backgroundOpacity: Double
|
||||
let macosWindowButtons: Ghostty.MacOSWindowButtons
|
||||
|
||||
init() {
|
||||
self.title = nil
|
||||
self.backgroundColor = NSColor.windowBackgroundColor
|
||||
self.backgroundOpacity = 1
|
||||
self.macosWindowButtons = .visible
|
||||
}
|
||||
|
||||
init(_ config: Ghostty.Config) {
|
||||
self.title = config.title
|
||||
self.backgroundColor = NSColor(config.backgroundColor)
|
||||
self.backgroundOpacity = config.backgroundOpacity
|
||||
self.macosWindowButtons = config.macosWindowButtons
|
||||
|
|
|
|||
|
|
@ -624,10 +624,15 @@ extension Ghostty {
|
|||
) -> Bool {
|
||||
let action = Ghostty.Action.OpenURL(c: v)
|
||||
|
||||
// Convert the URL string to a URL object
|
||||
guard let url = URL(string: action.url) else {
|
||||
Ghostty.logger.warning("invalid URL for open URL action: \(action.url)")
|
||||
return false
|
||||
// If the URL doesn't have a valid scheme we assume its a file path. The URL
|
||||
// initializer will gladly take invalid URLs (e.g. plain file paths) and turn
|
||||
// them into schema-less URLs, but these won't open properly in text editors.
|
||||
// See: https://github.com/ghostty-org/ghostty/issues/8763
|
||||
let url: URL
|
||||
if let candidate = URL(string: action.url), candidate.scheme != nil {
|
||||
url = candidate
|
||||
} else {
|
||||
url = URL(filePath: action.url)
|
||||
}
|
||||
|
||||
switch action.kind {
|
||||
|
|
|
|||
|
|
@ -625,6 +625,7 @@ extension Ghostty.Config {
|
|||
static let audio = BellFeatures(rawValue: 1 << 1)
|
||||
static let attention = BellFeatures(rawValue: 1 << 2)
|
||||
static let title = BellFeatures(rawValue: 1 << 3)
|
||||
static let border = BellFeatures(rawValue: 1 << 4)
|
||||
}
|
||||
|
||||
enum MacDockDropBehavior: String {
|
||||
|
|
|
|||
|
|
@ -57,15 +57,6 @@ extension Ghostty {
|
|||
|
||||
@EnvironmentObject private var ghostty: Ghostty.App
|
||||
|
||||
var title: String {
|
||||
var result = surfaceView.title
|
||||
if (surfaceView.bell && ghostty.config.bellFeatures.contains(.title)) {
|
||||
result = "🔔 \(result)"
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
let center = NotificationCenter.default
|
||||
|
||||
|
|
@ -207,6 +198,11 @@ extension Ghostty {
|
|||
SecureInputOverlay()
|
||||
}
|
||||
#endif
|
||||
|
||||
// Show bell border if enabled
|
||||
if (ghostty.config.bellFeatures.contains(.border)) {
|
||||
BellBorderOverlay(bell: surfaceView.bell)
|
||||
}
|
||||
|
||||
// If our surface is not healthy, then we render an error view over it.
|
||||
if (!surfaceView.healthy) {
|
||||
|
|
@ -535,6 +531,22 @@ extension Ghostty {
|
|||
}
|
||||
}
|
||||
|
||||
/// Visual overlay that shows a border around the edges when the bell rings with border feature enabled.
|
||||
struct BellBorderOverlay: View {
|
||||
let bell: Bool
|
||||
|
||||
var body: some View {
|
||||
Rectangle()
|
||||
.strokeBorder(
|
||||
Color(red: 1.0, green: 0.8, blue: 0.0).opacity(0.5),
|
||||
lineWidth: 3
|
||||
)
|
||||
.allowsHitTesting(false)
|
||||
.opacity(bell ? 1.0 : 0.0)
|
||||
.animation(.easeInOut(duration: 0.3), value: bell)
|
||||
}
|
||||
}
|
||||
|
||||
#if canImport(AppKit)
|
||||
/// When changing the split state, or going full screen (native or non), the terminal view
|
||||
/// will lose focus. There has to be some nice SwiftUI-native way to fix this but I can't
|
||||
|
|
|
|||
|
|
@ -1815,18 +1815,39 @@ extension Ghostty.SurfaceView: NSServicesMenuRequestor {
|
|||
forSendType sendType: NSPasteboard.PasteboardType?,
|
||||
returnType: NSPasteboard.PasteboardType?
|
||||
) -> Any? {
|
||||
// Types that we accept sent to us
|
||||
let accepted: [NSPasteboard.PasteboardType] = [.string, .init("public.utf8-plain-text")]
|
||||
// This function confused me a bit so I'm going to add my own commentary on
|
||||
// how this works. macOS sends this callback with the given send/return types and
|
||||
// we must return the responder capable of handling the COMBINATION of those send
|
||||
// and return types (or super up if we can't handle it).
|
||||
//
|
||||
// The "COMBINATION" bit is key: we might get sent a string (we can handle that)
|
||||
// but get requested an image (we can't handle that at the time of writing this),
|
||||
// so we must bubble up.
|
||||
|
||||
// Types we can receive
|
||||
let receivable: [NSPasteboard.PasteboardType] = [.string, .init("public.utf8-plain-text")]
|
||||
|
||||
// Types that we can send. Currently the same as receivable but I'm separating
|
||||
// this out so we can modify this in the future.
|
||||
let sendable: [NSPasteboard.PasteboardType] = receivable
|
||||
|
||||
// The sendable types that require a selection (currently all)
|
||||
let sendableRequiresSelection = sendable
|
||||
|
||||
// We can always receive the accepted types
|
||||
if (returnType == nil || accepted.contains(returnType!)) {
|
||||
return self
|
||||
}
|
||||
|
||||
// If we have a selection we can send the accepted types too
|
||||
if ((self.surface != nil && ghostty_surface_has_selection(self.surface)) &&
|
||||
(sendType == nil || accepted.contains(sendType!))
|
||||
) {
|
||||
// If we expect no data to be sent/received we can obviously handle it (that's
|
||||
// the nil check), otherwise it must conform to the types we support on both sides.
|
||||
if (returnType == nil || receivable.contains(returnType!)) &&
|
||||
(sendType == nil || sendable.contains(sendType!)) {
|
||||
// If we're expected to send back a type that requires selection, then
|
||||
// verify that we have a selection. We do this within this block because
|
||||
// validateRequestor is called a LOT and we want to prevent unnecessary
|
||||
// performance hits because `ghostty_surface_has_selection` isn't free.
|
||||
if let sendType, sendableRequiresSelection.contains(sendType) {
|
||||
if surface == nil || !ghostty_surface_has_selection(surface) {
|
||||
return super.validRequestor(forSendType: sendType, returnType: returnType)
|
||||
}
|
||||
}
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,4 +41,20 @@ extension NSScreen {
|
|||
// know any other situation this is true.
|
||||
return safeAreaInsets.top > 0
|
||||
}
|
||||
|
||||
/// Converts top-left offset coordinates to bottom-left origin coordinates for window positioning.
|
||||
/// - Parameters:
|
||||
/// - x: X offset from top-left corner
|
||||
/// - y: Y offset from top-left corner
|
||||
/// - windowSize: Size of the window to be positioned
|
||||
/// - Returns: CGPoint suitable for setFrameOrigin that positions the window as requested
|
||||
func origin(fromTopLeftOffsetX x: CGFloat, offsetY y: CGFloat, windowSize: CGSize) -> CGPoint {
|
||||
let vf = visibleFrame
|
||||
|
||||
// Convert top-left coordinates to bottom-left origin
|
||||
let originX = vf.minX + x
|
||||
let originY = vf.maxY - y - windowSize.height
|
||||
|
||||
return CGPoint(x: originX, y: originY)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,99 @@
|
|||
//
|
||||
// WindowPositionTests.swift
|
||||
// GhosttyTests
|
||||
//
|
||||
// Tests for window positioning coordinate conversion functionality.
|
||||
//
|
||||
|
||||
import Testing
|
||||
import AppKit
|
||||
@testable import Ghostty
|
||||
|
||||
struct NSScreenExtensionTests {
|
||||
/// Test positive coordinate conversion from top-left to bottom-left
|
||||
@Test func testPositiveCoordinateConversion() async throws {
|
||||
// Mock screen with 1000x800 visible frame starting at (0, 100)
|
||||
let mockScreenFrame = NSRect(x: 0, y: 100, width: 1000, height: 800)
|
||||
let mockScreen = MockNSScreen(visibleFrame: mockScreenFrame)
|
||||
|
||||
// Mock window size
|
||||
let windowSize = CGSize(width: 400, height: 300)
|
||||
|
||||
// Test top-left positioning: x=15, y=15
|
||||
let origin = mockScreen.origin(
|
||||
fromTopLeftOffsetX: 15,
|
||||
offsetY: 15,
|
||||
windowSize: windowSize)
|
||||
|
||||
// Expected: x = 0 + 15 = 15, y = (100 + 800) - 15 - 300 = 585
|
||||
#expect(origin.x == 15)
|
||||
#expect(origin.y == 585)
|
||||
}
|
||||
|
||||
/// Test zero coordinates (exact top-left corner)
|
||||
@Test func testZeroCoordinates() async throws {
|
||||
let mockScreenFrame = NSRect(x: 0, y: 100, width: 1000, height: 800)
|
||||
let mockScreen = MockNSScreen(visibleFrame: mockScreenFrame)
|
||||
let windowSize = CGSize(width: 400, height: 300)
|
||||
|
||||
let origin = mockScreen.origin(
|
||||
fromTopLeftOffsetX: 0,
|
||||
offsetY: 0,
|
||||
windowSize: windowSize)
|
||||
|
||||
// Expected: x = 0, y = (100 + 800) - 0 - 300 = 600
|
||||
#expect(origin.x == 0)
|
||||
#expect(origin.y == 600)
|
||||
}
|
||||
|
||||
/// Test with offset screen (not starting at origin)
|
||||
@Test func testOffsetScreen() async throws {
|
||||
// Secondary monitor at position (1440, 0) with 1920x1080 resolution
|
||||
let mockScreenFrame = NSRect(x: 1440, y: 0, width: 1920, height: 1080)
|
||||
let mockScreen = MockNSScreen(visibleFrame: mockScreenFrame)
|
||||
let windowSize = CGSize(width: 600, height: 400)
|
||||
|
||||
let origin = mockScreen.origin(
|
||||
fromTopLeftOffsetX: 100,
|
||||
offsetY: 50,
|
||||
windowSize: windowSize)
|
||||
|
||||
// Expected: x = 1440 + 100 = 1540, y = (0 + 1080) - 50 - 400 = 630
|
||||
#expect(origin.x == 1540)
|
||||
#expect(origin.y == 630)
|
||||
}
|
||||
|
||||
/// Test large coordinates
|
||||
@Test func testLargeCoordinates() async throws {
|
||||
let mockScreenFrame = NSRect(x: 0, y: 0, width: 1920, height: 1080)
|
||||
let mockScreen = MockNSScreen(visibleFrame: mockScreenFrame)
|
||||
let windowSize = CGSize(width: 400, height: 300)
|
||||
|
||||
let origin = mockScreen.origin(
|
||||
fromTopLeftOffsetX: 500,
|
||||
offsetY: 200,
|
||||
windowSize: windowSize)
|
||||
|
||||
// Expected: x = 0 + 500 = 500, y = (0 + 1080) - 200 - 300 = 580
|
||||
#expect(origin.x == 500)
|
||||
#expect(origin.y == 580)
|
||||
}
|
||||
}
|
||||
|
||||
/// Mock NSScreen class for testing coordinate conversion
|
||||
private class MockNSScreen: NSScreen {
|
||||
private let mockVisibleFrame: NSRect
|
||||
|
||||
init(visibleFrame: NSRect) {
|
||||
self.mockVisibleFrame = visibleFrame
|
||||
super.init()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override var visibleFrame: NSRect {
|
||||
return mockVisibleFrame
|
||||
}
|
||||
}
|
||||
|
|
@ -79,7 +79,7 @@ elif [ "$1" != "--update" ]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
zon2nix "$BUILD_ZIG_ZON" --nix "$WORK_DIR/build.zig.zon.nix" --txt "$WORK_DIR/build.zig.zon.txt" --json "$WORK_DIR/build.zig.zon.json" --flatpak "$WORK_DIR/zig-packages.json"
|
||||
zon2nix "$BUILD_ZIG_ZON" --14 --nix "$WORK_DIR/build.zig.zon.nix" --txt "$WORK_DIR/build.zig.zon.txt" --json "$WORK_DIR/build.zig.zon.json" --flatpak "$WORK_DIR/zig-packages.json"
|
||||
alejandra --quiet "$WORK_DIR/build.zig.zon.nix"
|
||||
prettier --log-level warn --write "$WORK_DIR/build.zig.zon.json"
|
||||
prettier --log-level warn --write "$WORK_DIR/zig-packages.json"
|
||||
|
|
@ -118,4 +118,3 @@ else
|
|||
echo -e "OK: flatpak/zig-packages.json updated."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ pub fn build(b: *std.Build) !void {
|
|||
.optimize = optimize,
|
||||
});
|
||||
|
||||
const imgui = b.dependency("imgui", .{});
|
||||
const imgui_ = b.lazyDependency("imgui", .{});
|
||||
const lib = b.addLibrary(.{
|
||||
.name = "cimgui",
|
||||
.root_module = b.createModule(.{
|
||||
|
|
@ -52,7 +52,7 @@ pub fn build(b: *std.Build) !void {
|
|||
}
|
||||
}
|
||||
|
||||
lib.addIncludePath(imgui.path(""));
|
||||
if (imgui_) |imgui| lib.addIncludePath(imgui.path(""));
|
||||
module.addIncludePath(b.path("vendor"));
|
||||
|
||||
var flags = std.ArrayList([]const u8).init(b.allocator);
|
||||
|
|
@ -72,32 +72,33 @@ pub fn build(b: *std.Build) !void {
|
|||
});
|
||||
}
|
||||
|
||||
lib.addCSourceFile(.{ .file = b.path("vendor/cimgui.cpp"), .flags = flags.items });
|
||||
lib.addCSourceFile(.{ .file = imgui.path("imgui.cpp"), .flags = flags.items });
|
||||
lib.addCSourceFile(.{ .file = imgui.path("imgui_draw.cpp"), .flags = flags.items });
|
||||
lib.addCSourceFile(.{ .file = imgui.path("imgui_demo.cpp"), .flags = flags.items });
|
||||
lib.addCSourceFile(.{ .file = imgui.path("imgui_widgets.cpp"), .flags = flags.items });
|
||||
lib.addCSourceFile(.{ .file = imgui.path("imgui_tables.cpp"), .flags = flags.items });
|
||||
lib.addCSourceFile(.{ .file = imgui.path("misc/freetype/imgui_freetype.cpp"), .flags = flags.items });
|
||||
|
||||
lib.addCSourceFile(.{
|
||||
.file = imgui.path("backends/imgui_impl_opengl3.cpp"),
|
||||
.flags = flags.items,
|
||||
});
|
||||
|
||||
if (target.result.os.tag.isDarwin()) {
|
||||
if (!target.query.isNative()) {
|
||||
try @import("apple_sdk").addPaths(b, lib);
|
||||
}
|
||||
if (imgui_) |imgui| {
|
||||
lib.addCSourceFile(.{ .file = b.path("vendor/cimgui.cpp"), .flags = flags.items });
|
||||
lib.addCSourceFile(.{ .file = imgui.path("imgui.cpp"), .flags = flags.items });
|
||||
lib.addCSourceFile(.{ .file = imgui.path("imgui_draw.cpp"), .flags = flags.items });
|
||||
lib.addCSourceFile(.{ .file = imgui.path("imgui_demo.cpp"), .flags = flags.items });
|
||||
lib.addCSourceFile(.{ .file = imgui.path("imgui_widgets.cpp"), .flags = flags.items });
|
||||
lib.addCSourceFile(.{ .file = imgui.path("imgui_tables.cpp"), .flags = flags.items });
|
||||
lib.addCSourceFile(.{ .file = imgui.path("misc/freetype/imgui_freetype.cpp"), .flags = flags.items });
|
||||
lib.addCSourceFile(.{
|
||||
.file = imgui.path("backends/imgui_impl_metal.mm"),
|
||||
.file = imgui.path("backends/imgui_impl_opengl3.cpp"),
|
||||
.flags = flags.items,
|
||||
});
|
||||
if (target.result.os.tag == .macos) {
|
||||
|
||||
if (target.result.os.tag.isDarwin()) {
|
||||
if (!target.query.isNative()) {
|
||||
try @import("apple_sdk").addPaths(b, lib);
|
||||
}
|
||||
lib.addCSourceFile(.{
|
||||
.file = imgui.path("backends/imgui_impl_osx.mm"),
|
||||
.file = imgui.path("backends/imgui_impl_metal.mm"),
|
||||
.flags = flags.items,
|
||||
});
|
||||
if (target.result.os.tag == .macos) {
|
||||
lib.addCSourceFile(.{
|
||||
.file = imgui.path("backends/imgui_impl_osx.mm"),
|
||||
.flags = flags.items,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
// ocornut/imgui
|
||||
.url = "https://deps.files.ghostty.org/imgui-1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402.tar.gz",
|
||||
.hash = "N-V-__8AAH0GaQC8a52s6vfIxg88OZgFgEW6DFxfSK4lX_l3",
|
||||
.lazy = true,
|
||||
},
|
||||
|
||||
.apple_sdk = .{ .path = "../apple-sdk" },
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@ pub fn build(b: *std.Build) !void {
|
|||
.optimize = optimize,
|
||||
});
|
||||
|
||||
const upstream = b.dependency("glslang", .{});
|
||||
const upstream = b.lazyDependency("glslang", .{});
|
||||
const lib = try buildGlslang(b, upstream, target, optimize);
|
||||
b.installArtifact(lib);
|
||||
|
||||
module.addIncludePath(upstream.path(""));
|
||||
if (upstream) |v| module.addIncludePath(v.path(""));
|
||||
module.addIncludePath(b.path("override"));
|
||||
|
||||
if (target.query.isNative()) {
|
||||
|
|
@ -38,7 +38,7 @@ pub fn build(b: *std.Build) !void {
|
|||
|
||||
fn buildGlslang(
|
||||
b: *std.Build,
|
||||
upstream: *std.Build.Dependency,
|
||||
upstream_: ?*std.Build.Dependency,
|
||||
target: std.Build.ResolvedTarget,
|
||||
optimize: std.builtin.OptimizeMode,
|
||||
) !*std.Build.Step.Compile {
|
||||
|
|
@ -52,7 +52,7 @@ fn buildGlslang(
|
|||
});
|
||||
lib.linkLibC();
|
||||
lib.linkLibCpp();
|
||||
lib.addIncludePath(upstream.path(""));
|
||||
if (upstream_) |upstream| lib.addIncludePath(upstream.path(""));
|
||||
lib.addIncludePath(b.path("override"));
|
||||
if (target.result.os.tag.isDarwin()) {
|
||||
const apple_sdk = @import("apple_sdk");
|
||||
|
|
@ -66,87 +66,89 @@ fn buildGlslang(
|
|||
"-fno-sanitize-trap=undefined",
|
||||
});
|
||||
|
||||
lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.flags = flags.items,
|
||||
.files = &.{
|
||||
// GenericCodeGen
|
||||
"glslang/GenericCodeGen/CodeGen.cpp",
|
||||
"glslang/GenericCodeGen/Link.cpp",
|
||||
|
||||
// MachineIndependent
|
||||
//"MachineIndependent/glslang.y",
|
||||
"glslang/MachineIndependent/glslang_tab.cpp",
|
||||
"glslang/MachineIndependent/attribute.cpp",
|
||||
"glslang/MachineIndependent/Constant.cpp",
|
||||
"glslang/MachineIndependent/iomapper.cpp",
|
||||
"glslang/MachineIndependent/InfoSink.cpp",
|
||||
"glslang/MachineIndependent/Initialize.cpp",
|
||||
"glslang/MachineIndependent/IntermTraverse.cpp",
|
||||
"glslang/MachineIndependent/Intermediate.cpp",
|
||||
"glslang/MachineIndependent/ParseContextBase.cpp",
|
||||
"glslang/MachineIndependent/ParseHelper.cpp",
|
||||
"glslang/MachineIndependent/PoolAlloc.cpp",
|
||||
"glslang/MachineIndependent/RemoveTree.cpp",
|
||||
"glslang/MachineIndependent/Scan.cpp",
|
||||
"glslang/MachineIndependent/ShaderLang.cpp",
|
||||
"glslang/MachineIndependent/SpirvIntrinsics.cpp",
|
||||
"glslang/MachineIndependent/SymbolTable.cpp",
|
||||
"glslang/MachineIndependent/Versions.cpp",
|
||||
"glslang/MachineIndependent/intermOut.cpp",
|
||||
"glslang/MachineIndependent/limits.cpp",
|
||||
"glslang/MachineIndependent/linkValidate.cpp",
|
||||
"glslang/MachineIndependent/parseConst.cpp",
|
||||
"glslang/MachineIndependent/reflection.cpp",
|
||||
"glslang/MachineIndependent/preprocessor/Pp.cpp",
|
||||
"glslang/MachineIndependent/preprocessor/PpAtom.cpp",
|
||||
"glslang/MachineIndependent/preprocessor/PpContext.cpp",
|
||||
"glslang/MachineIndependent/preprocessor/PpScanner.cpp",
|
||||
"glslang/MachineIndependent/preprocessor/PpTokens.cpp",
|
||||
"glslang/MachineIndependent/propagateNoContraction.cpp",
|
||||
|
||||
// C Interface
|
||||
"glslang/CInterface/glslang_c_interface.cpp",
|
||||
|
||||
// ResourceLimits
|
||||
"glslang/ResourceLimits/ResourceLimits.cpp",
|
||||
"glslang/ResourceLimits/resource_limits_c.cpp",
|
||||
|
||||
// SPIRV
|
||||
"SPIRV/GlslangToSpv.cpp",
|
||||
"SPIRV/InReadableOrder.cpp",
|
||||
"SPIRV/Logger.cpp",
|
||||
"SPIRV/SpvBuilder.cpp",
|
||||
"SPIRV/SpvPostProcess.cpp",
|
||||
"SPIRV/doc.cpp",
|
||||
"SPIRV/disassemble.cpp",
|
||||
"SPIRV/CInterface/spirv_c_interface.cpp",
|
||||
},
|
||||
});
|
||||
|
||||
if (target.result.os.tag != .windows) {
|
||||
if (upstream_) |upstream| {
|
||||
lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.flags = flags.items,
|
||||
.files = &.{
|
||||
"glslang/OSDependent/Unix/ossource.cpp",
|
||||
},
|
||||
});
|
||||
} else {
|
||||
lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.flags = flags.items,
|
||||
.files = &.{
|
||||
"glslang/OSDependent/Windows/ossource.cpp",
|
||||
// GenericCodeGen
|
||||
"glslang/GenericCodeGen/CodeGen.cpp",
|
||||
"glslang/GenericCodeGen/Link.cpp",
|
||||
|
||||
// MachineIndependent
|
||||
//"MachineIndependent/glslang.y",
|
||||
"glslang/MachineIndependent/glslang_tab.cpp",
|
||||
"glslang/MachineIndependent/attribute.cpp",
|
||||
"glslang/MachineIndependent/Constant.cpp",
|
||||
"glslang/MachineIndependent/iomapper.cpp",
|
||||
"glslang/MachineIndependent/InfoSink.cpp",
|
||||
"glslang/MachineIndependent/Initialize.cpp",
|
||||
"glslang/MachineIndependent/IntermTraverse.cpp",
|
||||
"glslang/MachineIndependent/Intermediate.cpp",
|
||||
"glslang/MachineIndependent/ParseContextBase.cpp",
|
||||
"glslang/MachineIndependent/ParseHelper.cpp",
|
||||
"glslang/MachineIndependent/PoolAlloc.cpp",
|
||||
"glslang/MachineIndependent/RemoveTree.cpp",
|
||||
"glslang/MachineIndependent/Scan.cpp",
|
||||
"glslang/MachineIndependent/ShaderLang.cpp",
|
||||
"glslang/MachineIndependent/SpirvIntrinsics.cpp",
|
||||
"glslang/MachineIndependent/SymbolTable.cpp",
|
||||
"glslang/MachineIndependent/Versions.cpp",
|
||||
"glslang/MachineIndependent/intermOut.cpp",
|
||||
"glslang/MachineIndependent/limits.cpp",
|
||||
"glslang/MachineIndependent/linkValidate.cpp",
|
||||
"glslang/MachineIndependent/parseConst.cpp",
|
||||
"glslang/MachineIndependent/reflection.cpp",
|
||||
"glslang/MachineIndependent/preprocessor/Pp.cpp",
|
||||
"glslang/MachineIndependent/preprocessor/PpAtom.cpp",
|
||||
"glslang/MachineIndependent/preprocessor/PpContext.cpp",
|
||||
"glslang/MachineIndependent/preprocessor/PpScanner.cpp",
|
||||
"glslang/MachineIndependent/preprocessor/PpTokens.cpp",
|
||||
"glslang/MachineIndependent/propagateNoContraction.cpp",
|
||||
|
||||
// C Interface
|
||||
"glslang/CInterface/glslang_c_interface.cpp",
|
||||
|
||||
// ResourceLimits
|
||||
"glslang/ResourceLimits/ResourceLimits.cpp",
|
||||
"glslang/ResourceLimits/resource_limits_c.cpp",
|
||||
|
||||
// SPIRV
|
||||
"SPIRV/GlslangToSpv.cpp",
|
||||
"SPIRV/InReadableOrder.cpp",
|
||||
"SPIRV/Logger.cpp",
|
||||
"SPIRV/SpvBuilder.cpp",
|
||||
"SPIRV/SpvPostProcess.cpp",
|
||||
"SPIRV/doc.cpp",
|
||||
"SPIRV/disassemble.cpp",
|
||||
"SPIRV/CInterface/spirv_c_interface.cpp",
|
||||
},
|
||||
});
|
||||
|
||||
if (target.result.os.tag != .windows) {
|
||||
lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.flags = flags.items,
|
||||
.files = &.{
|
||||
"glslang/OSDependent/Unix/ossource.cpp",
|
||||
},
|
||||
});
|
||||
} else {
|
||||
lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.flags = flags.items,
|
||||
.files = &.{
|
||||
"glslang/OSDependent/Windows/ossource.cpp",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
lib.installHeadersDirectory(
|
||||
upstream.path(""),
|
||||
"",
|
||||
.{ .include_extensions = &.{".h"} },
|
||||
);
|
||||
}
|
||||
|
||||
lib.installHeadersDirectory(
|
||||
upstream.path(""),
|
||||
"",
|
||||
.{ .include_extensions = &.{".h"} },
|
||||
);
|
||||
|
||||
return lib;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
.glslang = .{
|
||||
.url = "https://deps.files.ghostty.org/glslang-12201278a1a05c0ce0b6eb6026c65cd3e9247aa041b1c260324bf29cee559dd23ba1.tar.gz",
|
||||
.hash = "N-V-__8AABzkUgISeKGgXAzgtutgJsZc0-kkeqBBscJgMkvy",
|
||||
.lazy = true,
|
||||
},
|
||||
|
||||
.apple_sdk = .{ .path = "../apple-sdk" },
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ pub fn build(b: *std.Build) !void {
|
|||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const upstream = b.dependency("highway", .{});
|
||||
const upstream_ = b.lazyDependency("highway", .{});
|
||||
|
||||
const module = b.addModule("highway", .{
|
||||
.root_source_file = b.path("main.zig"),
|
||||
|
|
@ -21,8 +21,10 @@ pub fn build(b: *std.Build) !void {
|
|||
.linkage = .static,
|
||||
});
|
||||
lib.linkLibCpp();
|
||||
lib.addIncludePath(upstream.path(""));
|
||||
module.addIncludePath(upstream.path(""));
|
||||
if (upstream_) |upstream| {
|
||||
lib.addIncludePath(upstream.path(""));
|
||||
module.addIncludePath(upstream.path(""));
|
||||
}
|
||||
|
||||
if (target.result.os.tag.isDarwin()) {
|
||||
const apple_sdk = @import("apple_sdk");
|
||||
|
|
@ -74,24 +76,26 @@ pub fn build(b: *std.Build) !void {
|
|||
}
|
||||
|
||||
lib.addCSourceFiles(.{ .flags = flags.items, .files = &.{"bridge.cpp"} });
|
||||
lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.flags = flags.items,
|
||||
.files = &.{
|
||||
"hwy/abort.cc",
|
||||
"hwy/aligned_allocator.cc",
|
||||
"hwy/nanobenchmark.cc",
|
||||
"hwy/per_target.cc",
|
||||
"hwy/print.cc",
|
||||
"hwy/targets.cc",
|
||||
"hwy/timer.cc",
|
||||
},
|
||||
});
|
||||
lib.installHeadersDirectory(
|
||||
upstream.path("hwy"),
|
||||
"hwy",
|
||||
.{ .include_extensions = &.{".h"} },
|
||||
);
|
||||
if (upstream_) |upstream| {
|
||||
lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.flags = flags.items,
|
||||
.files = &.{
|
||||
"hwy/abort.cc",
|
||||
"hwy/aligned_allocator.cc",
|
||||
"hwy/nanobenchmark.cc",
|
||||
"hwy/per_target.cc",
|
||||
"hwy/print.cc",
|
||||
"hwy/targets.cc",
|
||||
"hwy/timer.cc",
|
||||
},
|
||||
});
|
||||
lib.installHeadersDirectory(
|
||||
upstream.path("hwy"),
|
||||
"hwy",
|
||||
.{ .include_extensions = &.{".h"} },
|
||||
);
|
||||
}
|
||||
|
||||
b.installArtifact(lib);
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
.highway = .{
|
||||
.url = "https://deps.files.ghostty.org/highway-66486a10623fa0d72fe91260f96c892e41aceb06.tar.gz",
|
||||
.hash = "N-V-__8AAGmZhABbsPJLfbqrh6JTHsXhY6qCaLAQyx25e0XE",
|
||||
.lazy = true,
|
||||
},
|
||||
|
||||
.apple_sdk = .{ .path = "../apple-sdk" },
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ pub fn build(b: *std.Build) !void {
|
|||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const upstream = b.dependency("libxml2", .{});
|
||||
const upstream_ = b.lazyDependency("libxml2", .{});
|
||||
|
||||
const lib = b.addLibrary(.{
|
||||
.name = "xml2",
|
||||
|
|
@ -16,7 +16,7 @@ pub fn build(b: *std.Build) !void {
|
|||
});
|
||||
lib.linkLibC();
|
||||
|
||||
lib.addIncludePath(upstream.path("include"));
|
||||
if (upstream_) |upstream| lib.addIncludePath(upstream.path("include"));
|
||||
lib.addIncludePath(b.path("override/include"));
|
||||
if (target.result.os.tag == .windows) {
|
||||
lib.addIncludePath(b.path("override/config/win32"));
|
||||
|
|
@ -97,21 +97,23 @@ pub fn build(b: *std.Build) !void {
|
|||
}
|
||||
}
|
||||
|
||||
lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.files = srcs,
|
||||
.flags = flags.items,
|
||||
});
|
||||
if (upstream_) |upstream| {
|
||||
lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.files = srcs,
|
||||
.flags = flags.items,
|
||||
});
|
||||
|
||||
lib.installHeader(
|
||||
b.path("override/include/libxml/xmlversion.h"),
|
||||
"libxml/xmlversion.h",
|
||||
);
|
||||
lib.installHeadersDirectory(
|
||||
upstream.path("include"),
|
||||
"",
|
||||
.{ .include_extensions = &.{".h"} },
|
||||
);
|
||||
lib.installHeader(
|
||||
b.path("override/include/libxml/xmlversion.h"),
|
||||
"libxml/xmlversion.h",
|
||||
);
|
||||
lib.installHeadersDirectory(
|
||||
upstream.path("include"),
|
||||
"",
|
||||
.{ .include_extensions = &.{".h"} },
|
||||
);
|
||||
}
|
||||
|
||||
b.installArtifact(lib);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
.libxml2 = .{
|
||||
.url = "https://deps.files.ghostty.org/libxml2-2.11.5.tar.gz",
|
||||
.hash = "N-V-__8AAG3RoQEyRC2Vw7Qoro5SYBf62IHn3HjqtNVY6aWK",
|
||||
.lazy = true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ pub fn build(b: *std.Build) !void {
|
|||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const upstream = b.dependency("spirv_cross", .{});
|
||||
const upstream = b.lazyDependency("spirv_cross", .{});
|
||||
|
||||
const module = b.addModule("spirv_cross", .{ .root_source_file = b.path("main.zig") });
|
||||
module.addIncludePath(upstream.path(""));
|
||||
if (upstream) |v| module.addIncludePath(v.path(""));
|
||||
|
||||
const lib = try buildSpirvCross(b, upstream, target, optimize);
|
||||
b.installArtifact(lib);
|
||||
|
|
@ -33,7 +33,7 @@ pub fn build(b: *std.Build) !void {
|
|||
|
||||
fn buildSpirvCross(
|
||||
b: *std.Build,
|
||||
upstream: *std.Build.Dependency,
|
||||
upstream_: ?*std.Build.Dependency,
|
||||
target: std.Build.ResolvedTarget,
|
||||
optimize: std.builtin.OptimizeMode,
|
||||
) !*std.Build.Step.Compile {
|
||||
|
|
@ -62,6 +62,7 @@ fn buildSpirvCross(
|
|||
"-fno-sanitize-trap=undefined",
|
||||
});
|
||||
|
||||
const upstream = upstream_ orelse return lib;
|
||||
lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.flags = flags.items,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
.spirv_cross = .{
|
||||
.url = "https://deps.files.ghostty.org/spirv_cross-1220fb3b5586e8be67bc3feb34cbe749cf42a60d628d2953632c2f8141302748c8da.tar.gz",
|
||||
.hash = "N-V-__8AANb6pwD7O1WG6L5nvD_rNMvnSc9Cpg1ijSlTYywv",
|
||||
.lazy = true,
|
||||
},
|
||||
|
||||
.apple_sdk = .{ .path = "../apple-sdk" },
|
||||
|
|
|
|||
|
|
@ -131,12 +131,11 @@ which should be filled in accordingly. You can then add your translations
|
|||
within the newly created translation file.
|
||||
|
||||
Afterwards, you need to update the list of known locales within Ghostty's
|
||||
build system. To do so, open `src/os/i18n.zig` and find the list
|
||||
of locales under the `locales` variable, then add the full locale name
|
||||
into the list.
|
||||
build system. To do so, open `src/os/i18n_locales.zig` and find the list
|
||||
of locales after the comments, then add the full locale name into the list.
|
||||
|
||||
The order matters, so make sure to place your locale in the correct position.
|
||||
Read the comment above the variable for more details on the order. If you're
|
||||
Read the comments present in the file for more details on the order. If you're
|
||||
unsure, place it at the end of the list.
|
||||
|
||||
```zig
|
||||
|
|
@ -146,7 +145,7 @@ const locales = [_][]const u8{
|
|||
}
|
||||
```
|
||||
|
||||
You should then be able to run `zig build` and see your translations in action.
|
||||
You should then be able to run `zig build` and see your translations in action!
|
||||
|
||||
Before opening a pull request with the new translation file, you should also add
|
||||
your locale to the `CODEOWNERS` file. Find the `# Localization` section near the
|
||||
|
|
|
|||
|
|
@ -0,0 +1,314 @@
|
|||
# Traditional Chinese (Taiwan) translation for com.mitchellh.ghostty package.
|
||||
# Copyright (C) 2025 Mitchell Hashimoto
|
||||
# This file is distributed under the same license as the com.mitchellh.ghostty package.
|
||||
# Peter Dave Hello <hsu@peterdavehello.org>, 2025.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: com.mitchellh.ghostty\n"
|
||||
"Report-Msgid-Bugs-To: m@mitchellh.com\n"
|
||||
"POT-Creation-Date: 2025-07-22 17:18+0000\n"
|
||||
"PO-Revision-Date: 2025-09-21 18:59+0800\n"
|
||||
"Last-Translator: Peter Dave Hello <hsu@peterdavehello.org>\n"
|
||||
"Language-Team: Chinese (traditional)\n"
|
||||
"Language: zh_TW\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:5
|
||||
msgid "Change Terminal Title"
|
||||
msgstr "變更終端機標題"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:6
|
||||
msgid "Leave blank to restore the default title."
|
||||
msgstr "留空即可還原為預設標題。"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:9
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:10 src/apprt/gtk/ui/1.2/ccw-paste.blp:10
|
||||
#: src/apprt/gtk/CloseDialog.zig:44
|
||||
msgid "Cancel"
|
||||
msgstr "取消"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/prompt-title-dialog.blp:10
|
||||
msgid "OK"
|
||||
msgstr "確定"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:5
|
||||
#: src/apprt/gtk/ui/1.2/config-errors-dialog.blp:5
|
||||
msgid "Configuration Errors"
|
||||
msgstr "設定錯誤"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:6
|
||||
#: src/apprt/gtk/ui/1.2/config-errors-dialog.blp:6
|
||||
msgid ""
|
||||
"One or more configuration errors were found. Please review the errors below, "
|
||||
"and either reload your configuration or ignore these errors."
|
||||
msgstr ""
|
||||
"發現有設定錯誤。請檢視以下錯誤,並重新載入設定或忽略這些錯誤。"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:9
|
||||
#: src/apprt/gtk/ui/1.2/config-errors-dialog.blp:9
|
||||
msgid "Ignore"
|
||||
msgstr "忽略"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:10
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:97
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:100
|
||||
#: src/apprt/gtk/ui/1.2/config-errors-dialog.blp:10
|
||||
msgid "Reload Configuration"
|
||||
msgstr "重新載入設定"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-headerbar-split_menu.blp:6
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:38
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:50
|
||||
msgid "Split Up"
|
||||
msgstr "向上分割"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-headerbar-split_menu.blp:11
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:43
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:55
|
||||
msgid "Split Down"
|
||||
msgstr "向下分割"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-headerbar-split_menu.blp:16
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:48
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:60
|
||||
msgid "Split Left"
|
||||
msgstr "向左分割"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-headerbar-split_menu.blp:21
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:53
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:65
|
||||
msgid "Split Right"
|
||||
msgstr "向右分割"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/command-palette.blp:16
|
||||
msgid "Execute a command…"
|
||||
msgstr "執行命令…"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:6
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:6
|
||||
msgid "Copy"
|
||||
msgstr "複製"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:11
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:11
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:11 src/apprt/gtk/ui/1.2/ccw-paste.blp:11
|
||||
msgid "Paste"
|
||||
msgstr "貼上"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:18
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:73
|
||||
msgid "Clear"
|
||||
msgstr "清除"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:23
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:78
|
||||
msgid "Reset"
|
||||
msgstr "重設"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:30
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:42
|
||||
msgid "Split"
|
||||
msgstr "分割"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:33
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:45
|
||||
msgid "Change Title…"
|
||||
msgstr "變更標題…"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:59
|
||||
msgid "Tab"
|
||||
msgstr "分頁"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:62
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:30
|
||||
#: src/apprt/gtk/Window.zig:265
|
||||
msgid "New Tab"
|
||||
msgstr "開新分頁"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:67
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:35
|
||||
msgid "Close Tab"
|
||||
msgstr "關閉分頁"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:73
|
||||
msgid "Window"
|
||||
msgstr "視窗"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:76
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:18
|
||||
msgid "New Window"
|
||||
msgstr "開新視窗"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:81
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:23
|
||||
msgid "Close Window"
|
||||
msgstr "關閉視窗"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:89
|
||||
msgid "Config"
|
||||
msgstr "設定"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:92
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:95
|
||||
msgid "Open Configuration"
|
||||
msgstr "開啟設定"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:85
|
||||
msgid "Command Palette"
|
||||
msgstr "命令面板"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:90
|
||||
msgid "Terminal Inspector"
|
||||
msgstr "終端機檢查工具"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:107
|
||||
#: src/apprt/gtk/Window.zig:1038
|
||||
msgid "About Ghostty"
|
||||
msgstr "關於 Ghostty"
|
||||
|
||||
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:112
|
||||
msgid "Quit"
|
||||
msgstr "結束"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:6
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:6
|
||||
#: src/apprt/gtk/ui/1.2/ccw-osc-52-read.blp:6
|
||||
#: src/apprt/gtk/ui/1.2/ccw-osc-52-write.blp:6
|
||||
msgid "Authorize Clipboard Access"
|
||||
msgstr "授權存取剪貼簿"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:7
|
||||
#: src/apprt/gtk/ui/1.2/ccw-osc-52-read.blp:7
|
||||
msgid ""
|
||||
"An application is attempting to read from the clipboard. The current "
|
||||
"clipboard contents are shown below."
|
||||
msgstr ""
|
||||
"有應用程式正嘗試讀取剪貼簿,目前的剪貼簿內容顯示如下。"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:10
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:10
|
||||
#: src/apprt/gtk/ui/1.2/ccw-osc-52-read.blp:10
|
||||
#: src/apprt/gtk/ui/1.2/ccw-osc-52-write.blp:10
|
||||
msgid "Deny"
|
||||
msgstr "拒絕"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:11
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:11
|
||||
#: src/apprt/gtk/ui/1.2/ccw-osc-52-read.blp:11
|
||||
#: src/apprt/gtk/ui/1.2/ccw-osc-52-write.blp:11
|
||||
msgid "Allow"
|
||||
msgstr "允許"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:81
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:77
|
||||
msgid "Remember choice for this split"
|
||||
msgstr "記住此窗格的選擇"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-read.blp:82
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:78
|
||||
msgid "Reload configuration to show this prompt again"
|
||||
msgstr "重新載入設定以再次顯示此提示"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-osc-52-write.blp:7
|
||||
#: src/apprt/gtk/ui/1.2/ccw-osc-52-write.blp:7
|
||||
msgid ""
|
||||
"An application is attempting to write to the clipboard. The current "
|
||||
"clipboard contents are shown below."
|
||||
msgstr ""
|
||||
"有應用程式正嘗試寫入剪貼簿,目前的剪貼簿內容顯示如下。"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:6 src/apprt/gtk/ui/1.2/ccw-paste.blp:6
|
||||
msgid "Warning: Potentially Unsafe Paste"
|
||||
msgstr "警告:可能有潛在安全風險的貼上操作"
|
||||
|
||||
#: src/apprt/gtk/ui/1.5/ccw-paste.blp:7 src/apprt/gtk/ui/1.2/ccw-paste.blp:7
|
||||
msgid ""
|
||||
"Pasting this text into the terminal may be dangerous as it looks like some "
|
||||
"commands may be executed."
|
||||
msgstr ""
|
||||
"將這段文字貼到終端機具有潛在風險,因為它看起來像是可能會被執行的命令。"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:47 src/apprt/gtk/Surface.zig:2531
|
||||
msgid "Close"
|
||||
msgstr "關閉"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:87
|
||||
msgid "Quit Ghostty?"
|
||||
msgstr "要結束 Ghostty 嗎?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:88
|
||||
msgid "Close Window?"
|
||||
msgstr "是否要關閉視窗?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:89
|
||||
msgid "Close Tab?"
|
||||
msgstr "是否要關閉分頁?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:90
|
||||
msgid "Close Split?"
|
||||
msgstr "是否要關閉窗格?"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:96
|
||||
msgid "All terminal sessions will be terminated."
|
||||
msgstr "所有終端機工作階段都將被終止。"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:97
|
||||
msgid "All terminal sessions in this window will be terminated."
|
||||
msgstr "此視窗中的所有終端機工作階段都將被終止。"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:98
|
||||
msgid "All terminal sessions in this tab will be terminated."
|
||||
msgstr "此分頁中的所有終端機工作階段都將被終止。"
|
||||
|
||||
#: src/apprt/gtk/CloseDialog.zig:99
|
||||
msgid "The currently running process in this split will be terminated."
|
||||
msgstr "此窗格中目前執行的處理程序將被終止。"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:1266
|
||||
msgid "Copied to clipboard"
|
||||
msgstr "已複製到剪貼簿"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:1268
|
||||
msgid "Cleared clipboard"
|
||||
msgstr "已清除剪貼簿"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:2525
|
||||
msgid "Command succeeded"
|
||||
msgstr "命令執行成功"
|
||||
|
||||
#: src/apprt/gtk/Surface.zig:2527
|
||||
msgid "Command failed"
|
||||
msgstr "命令執行失敗"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:216
|
||||
msgid "Main Menu"
|
||||
msgstr "主選單"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:239
|
||||
msgid "View Open Tabs"
|
||||
msgstr "檢視已開啟的分頁"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:266
|
||||
msgid "New Split"
|
||||
msgstr "新增窗格"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:329
|
||||
msgid ""
|
||||
"⚠️ You're running a debug build of Ghostty! Performance will be degraded."
|
||||
msgstr ""
|
||||
"⚠️ 您正在執行 Ghostty 的除錯版本!程式運作效能將會受到影響。"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:775
|
||||
msgid "Reloaded the configuration"
|
||||
msgstr "已重新載入設定"
|
||||
|
||||
#: src/apprt/gtk/Window.zig:1019
|
||||
msgid "Ghostty Developers"
|
||||
msgstr "Ghostty 開發者"
|
||||
|
||||
#: src/apprt/gtk/inspector.zig:144
|
||||
msgid "Ghostty: Terminal Inspector"
|
||||
msgstr "Ghostty:終端機檢查工具"
|
||||
|
|
@ -61,10 +61,4 @@ fi
|
|||
|
||||
[ "$needs_update" = true ] && echo "LAST_REVISION=$SNAP_REVISION" > "$SNAP_USER_DATA/.last_revision"
|
||||
|
||||
# Unset all SNAP specific environment variables to keep them from leaking
|
||||
# into other snaps that might get executed from within the shell
|
||||
for var in $(printenv | grep SNAP_ | cut -d= -f1); do
|
||||
unset $var
|
||||
done
|
||||
|
||||
exec "$@"
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ platforms:
|
|||
apps:
|
||||
ghostty:
|
||||
command: bin/ghostty
|
||||
command-chain: [bin/launcher]
|
||||
command-chain: [app/launcher]
|
||||
completer: share/bash-completion/completions/ghostty.bash
|
||||
desktop: share/applications/com.mitchellh.ghostty.desktop
|
||||
#refresh-mode: ignore-running # Store rejects this, needs fix in review-tools
|
||||
|
|
@ -35,7 +35,7 @@ parts:
|
|||
source: snap/local
|
||||
source-type: local
|
||||
organize:
|
||||
launcher: bin/
|
||||
launcher: app/
|
||||
|
||||
zig:
|
||||
plugin: nil
|
||||
|
|
@ -79,7 +79,12 @@ parts:
|
|||
# TODO: Remove -fno-sys=gtk4-layer-shell when we upgrade to a version that packages it Ubuntu 24.10+
|
||||
override-build: |
|
||||
craftctl set version=$(cat VERSION)
|
||||
$CRAFT_PART_SRC/../../zig/src/zig build -Dpatch-rpath=\$ORIGIN/../usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:/snap/core24/current/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR -Doptimize=ReleaseFast -Dcpu=baseline -fno-sys=gtk4-layer-shell
|
||||
$CRAFT_PART_SRC/../../zig/src/zig build \
|
||||
-Dsnap \
|
||||
-Dpatch-rpath=\$ORIGIN/../usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR:/snap/core24/current/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR \
|
||||
-Doptimize=ReleaseFast \
|
||||
-Dcpu=baseline \
|
||||
-fno-sys=gtk4-layer-shell
|
||||
cp -rp zig-out/* $CRAFT_PART_INSTALL/
|
||||
sed -i 's|Icon=com.mitchellh.ghostty|Icon=${SNAP}/share/icons/hicolor/512x512/apps/com.mitchellh.ghostty.png|g' $CRAFT_PART_INSTALL/share/applications/com.mitchellh.ghostty.desktop
|
||||
|
||||
|
|
|
|||
|
|
@ -404,91 +404,6 @@ pub fn getData(self: Command, comptime DT: type) ?*DT {
|
|||
return if (self.data) |ptr| @ptrCast(@alignCast(ptr)) else null;
|
||||
}
|
||||
|
||||
/// Search for "cmd" in the PATH and return the absolute path. This will
|
||||
/// always allocate if there is a non-null result. The caller must free the
|
||||
/// resulting value.
|
||||
pub fn expandPath(alloc: Allocator, cmd: []const u8) !?[]u8 {
|
||||
// If the command already contains a slash, then we return it as-is
|
||||
// because it is assumed to be absolute or relative.
|
||||
if (std.mem.indexOfScalar(u8, cmd, '/') != null) {
|
||||
return try alloc.dupe(u8, cmd);
|
||||
}
|
||||
|
||||
const PATH = switch (builtin.os.tag) {
|
||||
.windows => blk: {
|
||||
const win_path = std.process.getenvW(std.unicode.utf8ToUtf16LeStringLiteral("PATH")) orelse return null;
|
||||
const path = try std.unicode.utf16LeToUtf8Alloc(alloc, win_path);
|
||||
break :blk path;
|
||||
},
|
||||
else => std.posix.getenvZ("PATH") orelse return null,
|
||||
};
|
||||
defer if (builtin.os.tag == .windows) alloc.free(PATH);
|
||||
|
||||
var path_buf: [std.fs.max_path_bytes]u8 = undefined;
|
||||
var it = std.mem.tokenizeScalar(u8, PATH, std.fs.path.delimiter);
|
||||
var seen_eacces = false;
|
||||
while (it.next()) |search_path| {
|
||||
// We need enough space in our path buffer to store this
|
||||
const path_len = search_path.len + cmd.len + 1;
|
||||
if (path_buf.len < path_len) return error.PathTooLong;
|
||||
|
||||
// Copy in the full path
|
||||
@memcpy(path_buf[0..search_path.len], search_path);
|
||||
path_buf[search_path.len] = std.fs.path.sep;
|
||||
@memcpy(path_buf[search_path.len + 1 ..][0..cmd.len], cmd);
|
||||
path_buf[path_len] = 0;
|
||||
const full_path = path_buf[0..path_len :0];
|
||||
|
||||
// Stat it
|
||||
const f = std.fs.cwd().openFile(
|
||||
full_path,
|
||||
.{},
|
||||
) catch |err| switch (err) {
|
||||
error.FileNotFound => continue,
|
||||
error.AccessDenied => {
|
||||
// Accumulate this and return it later so we can try other
|
||||
// paths that we have access to.
|
||||
seen_eacces = true;
|
||||
continue;
|
||||
},
|
||||
else => return err,
|
||||
};
|
||||
defer f.close();
|
||||
const stat = try f.stat();
|
||||
if (stat.kind != .directory and isExecutable(stat.mode)) {
|
||||
return try alloc.dupe(u8, full_path);
|
||||
}
|
||||
}
|
||||
|
||||
if (seen_eacces) return error.AccessDenied;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
fn isExecutable(mode: std.fs.File.Mode) bool {
|
||||
if (builtin.os.tag == .windows) return true;
|
||||
return mode & 0o0111 != 0;
|
||||
}
|
||||
|
||||
// `uname -n` is the *nix equivalent of `hostname.exe` on Windows
|
||||
test "expandPath: hostname" {
|
||||
const executable = if (builtin.os.tag == .windows) "hostname.exe" else "uname";
|
||||
const path = (try expandPath(testing.allocator, executable)).?;
|
||||
defer testing.allocator.free(path);
|
||||
try testing.expect(path.len > executable.len);
|
||||
}
|
||||
|
||||
test "expandPath: does not exist" {
|
||||
const path = try expandPath(testing.allocator, "thisreallyprobablydoesntexist123");
|
||||
try testing.expect(path == null);
|
||||
}
|
||||
|
||||
test "expandPath: slash" {
|
||||
const path = (try expandPath(testing.allocator, "foo/env")).?;
|
||||
defer testing.allocator.free(path);
|
||||
try testing.expect(path.len == 7);
|
||||
}
|
||||
|
||||
// Copied from Zig. This is a publicly exported function but there is no
|
||||
// way to get it from the std package.
|
||||
fn createNullDelimitedEnvMap(arena: mem.Allocator, env_map: *const EnvMap) ![:null]?[*:0]u8 {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ pub const embedded = @import("apprt/embedded.zig");
|
|||
pub const surface = @import("apprt/surface.zig");
|
||||
|
||||
pub const Action = action.Action;
|
||||
pub const Runtime = @import("apprt/runtime.zig").Runtime;
|
||||
pub const Target = action.Target;
|
||||
|
||||
pub const ContentScale = structs.ContentScale;
|
||||
|
|
@ -51,30 +52,6 @@ pub const runtime = switch (build_config.artifact) {
|
|||
pub const App = runtime.App;
|
||||
pub const Surface = runtime.Surface;
|
||||
|
||||
/// Runtime is the runtime to use for Ghostty. All runtimes do not provide
|
||||
/// equivalent feature sets.
|
||||
pub const Runtime = enum {
|
||||
/// Will not produce an executable at all when `zig build` is called.
|
||||
/// This is only useful if you're only interested in the lib only (macOS).
|
||||
none,
|
||||
|
||||
/// GTK4. Rich windowed application. This uses a full GObject-based
|
||||
/// approach to building the application.
|
||||
gtk,
|
||||
|
||||
pub fn default(target: std.Target) Runtime {
|
||||
return switch (target.os.tag) {
|
||||
// The Linux and FreeBSD default is GTK because it is a full
|
||||
// featured application.
|
||||
.linux, .freebsd => .gtk,
|
||||
// Otherwise, we do NONE so we don't create an exe and we create
|
||||
// libghostty. On macOS, Xcode is used to build the app that links
|
||||
// to libghostty.
|
||||
else => .none,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
test {
|
||||
_ = Runtime;
|
||||
_ = runtime;
|
||||
|
|
|
|||
|
|
@ -569,6 +569,15 @@ pub const SetTitle = struct {
|
|||
.title = self.title.ptr,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn format(
|
||||
value: @This(),
|
||||
comptime _: []const u8,
|
||||
_: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
try writer.print("{s}{{ {s} }}", .{ @typeName(@This()), value.title });
|
||||
}
|
||||
};
|
||||
|
||||
pub const Pwd = struct {
|
||||
|
|
@ -584,6 +593,15 @@ pub const Pwd = struct {
|
|||
.pwd = self.pwd.ptr,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn format(
|
||||
value: @This(),
|
||||
comptime _: []const u8,
|
||||
_: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
try writer.print("{s}{{ {s} }}", .{ @typeName(@This()), value.pwd });
|
||||
}
|
||||
};
|
||||
|
||||
/// The desktop notification to show.
|
||||
|
|
@ -603,6 +621,19 @@ pub const DesktopNotification = struct {
|
|||
.body = self.body.ptr,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn format(
|
||||
value: @This(),
|
||||
comptime _: []const u8,
|
||||
_: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
try writer.print("{s}{{ title: {s}, body: {s} }}", .{
|
||||
@typeName(@This()),
|
||||
value.title,
|
||||
value.body,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
pub const KeySequence = union(enum) {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ const internal_os = @import("../os/main.zig");
|
|||
// The required comptime API for any apprt.
|
||||
pub const App = @import("gtk/App.zig");
|
||||
pub const Surface = @import("gtk/Surface.zig");
|
||||
pub const resourcesDir = internal_os.resourcesDir;
|
||||
pub const resourcesDir = @import("gtk/flatpak.zig").resourcesDir;
|
||||
|
||||
// The exported API, custom for the apprt.
|
||||
pub const class = @import("gtk/class.zig");
|
||||
|
|
|
|||
|
|
@ -42,6 +42,70 @@ const GlobalShortcuts = @import("global_shortcuts.zig").GlobalShortcuts;
|
|||
|
||||
const log = std.log.scoped(.gtk_ghostty_application);
|
||||
|
||||
/// Function used to funnel GLib/GObject/GTK log messages into Zig's logging
|
||||
/// system rather than just getting dumped directly to stderr.
|
||||
fn glibLogWriterFunction(
|
||||
level: glib.LogLevelFlags,
|
||||
fields: [*]const glib.LogField,
|
||||
n_fields: usize,
|
||||
_: ?*anyopaque,
|
||||
) callconv(.c) glib.LogWriterOutput {
|
||||
const glib_log = std.log.scoped(.glib);
|
||||
|
||||
var message_: ?[]const u8 = null;
|
||||
var domain_: ?[]const u8 = null;
|
||||
for (0..n_fields) |i| {
|
||||
const field = fields[i];
|
||||
const k = std.mem.span(field.f_key orelse continue);
|
||||
const v: []const u8 = v: {
|
||||
if (field.f_length >= 0) {
|
||||
const v: [*]const u8 = @ptrCast(field.f_value orelse continue);
|
||||
break :v v[0..@intCast(field.f_length)];
|
||||
}
|
||||
const v: [*:0]const u8 = @ptrCast(field.f_value orelse continue);
|
||||
break :v std.mem.span(v);
|
||||
};
|
||||
if (std.mem.eql(u8, k, "MESSAGE")) {
|
||||
message_ = v;
|
||||
continue;
|
||||
}
|
||||
if (std.mem.eql(u8, k, "GLIB_DOMAIN")) {
|
||||
domain_ = v;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const message = message_ orelse return .unhandled;
|
||||
const domain = domain_ orelse "«unknown»";
|
||||
|
||||
if (level.level_error) {
|
||||
glib_log.err("ERROR: {s}: {s}", .{ domain, message });
|
||||
return .handled;
|
||||
}
|
||||
if (level.level_critical) {
|
||||
glib_log.err("CRITICAL: {s}: {s}", .{ domain, message });
|
||||
return .handled;
|
||||
}
|
||||
if (level.level_warning) {
|
||||
glib_log.warn("WARNING: {s}: {s}", .{ domain, message });
|
||||
return .handled;
|
||||
}
|
||||
if (level.level_message) {
|
||||
glib_log.info("MESSAGE: {s}: {s}", .{ domain, message });
|
||||
return .handled;
|
||||
}
|
||||
if (level.level_info) {
|
||||
glib_log.info("INFO: {s}: {s}", .{ domain, message });
|
||||
return .handled;
|
||||
}
|
||||
if (level.level_debug) {
|
||||
glib_log.debug("DEBUG: {s}: {s}", .{ domain, message });
|
||||
return .handled;
|
||||
}
|
||||
glib_log.debug("UNKNOWN: {s}: {s}", .{ domain, message });
|
||||
return .handled;
|
||||
}
|
||||
|
||||
/// The primary entrypoint for the Ghostty GTK application.
|
||||
///
|
||||
/// This requires a `ghostty.App` and `ghostty.Config` and takes
|
||||
|
|
@ -177,6 +241,10 @@ pub const Application = extern struct {
|
|||
) Allocator.Error!*Self {
|
||||
const alloc = core_app.alloc;
|
||||
|
||||
// Capture GLib/GObject/GTK log messages and funnel them through Zig's
|
||||
// logging system rather than just getting dumped directly to stderr.
|
||||
_ = glib.logSetWriterFunc(glibLogWriterFunction, null, null);
|
||||
|
||||
// Log our GTK versions
|
||||
gtk_version.logVersion();
|
||||
adw_version.logVersion();
|
||||
|
|
|
|||
|
|
@ -112,6 +112,25 @@ pub const SplitTree = extern struct {
|
|||
},
|
||||
);
|
||||
};
|
||||
|
||||
pub const @"is-split" = struct {
|
||||
pub const name = "is-split";
|
||||
const impl = gobject.ext.defineProperty(
|
||||
name,
|
||||
Self,
|
||||
bool,
|
||||
.{
|
||||
.default = false,
|
||||
.accessor = gobject.ext.typedAccessor(
|
||||
Self,
|
||||
bool,
|
||||
.{
|
||||
.getter = getIsSplit,
|
||||
},
|
||||
),
|
||||
},
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
pub const signals = struct {
|
||||
|
|
@ -210,6 +229,14 @@ pub const SplitTree = extern struct {
|
|||
}
|
||||
}
|
||||
|
||||
// Bind is-split property for new surface
|
||||
_ = self.as(gobject.Object).bindProperty(
|
||||
"is-split",
|
||||
surface.as(gobject.Object),
|
||||
"is-split",
|
||||
.{ .sync_create = true },
|
||||
);
|
||||
|
||||
// Create our tree
|
||||
var single_tree = try Surface.Tree.init(alloc, surface);
|
||||
defer single_tree.deinit();
|
||||
|
|
@ -511,6 +538,18 @@ pub const SplitTree = extern struct {
|
|||
));
|
||||
}
|
||||
|
||||
fn getIsSplit(self: *Self) bool {
|
||||
const tree: *const Surface.Tree = self.private().tree orelse &.empty;
|
||||
if (tree.isEmpty()) return false;
|
||||
|
||||
const root_handle: Surface.Tree.Node.Handle = .root;
|
||||
const root = tree.nodes[root_handle.idx()];
|
||||
return switch (root) {
|
||||
.leaf => false,
|
||||
.split => true,
|
||||
};
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Virtual methods
|
||||
|
||||
|
|
@ -816,6 +855,9 @@ pub const SplitTree = extern struct {
|
|||
v.grabFocus();
|
||||
}
|
||||
|
||||
// Our split status may have changed
|
||||
self.as(gobject.Object).notifyByPspec(properties.@"is-split".impl.param_spec);
|
||||
|
||||
// Our active surface may have changed
|
||||
self.as(gobject.Object).notifyByPspec(properties.@"active-surface".impl.param_spec);
|
||||
|
||||
|
|
@ -873,6 +915,7 @@ pub const SplitTree = extern struct {
|
|||
properties.@"has-surfaces".impl,
|
||||
properties.@"is-zoomed".impl,
|
||||
properties.tree.impl,
|
||||
properties.@"is-split".impl,
|
||||
});
|
||||
|
||||
// Bindings
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ const gobject = @import("gobject");
|
|||
const gtk = @import("gtk");
|
||||
|
||||
const apprt = @import("../../../apprt.zig");
|
||||
const build_config = @import("../../../build_config.zig");
|
||||
const datastruct = @import("../../../datastruct/main.zig");
|
||||
const font = @import("../../../font/main.zig");
|
||||
const input = @import("../../../input.zig");
|
||||
|
|
@ -274,6 +275,24 @@ pub const Surface = extern struct {
|
|||
},
|
||||
);
|
||||
};
|
||||
|
||||
pub const @"is-split" = struct {
|
||||
pub const name = "is-split";
|
||||
const impl = gobject.ext.defineProperty(
|
||||
name,
|
||||
Self,
|
||||
bool,
|
||||
.{
|
||||
.default = false,
|
||||
.accessor = gobject.ext.privateFieldAccessor(
|
||||
Self,
|
||||
Private,
|
||||
&Private.offset,
|
||||
"is_split",
|
||||
),
|
||||
},
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
pub const signals = struct {
|
||||
|
|
@ -502,6 +521,10 @@ pub const Surface = extern struct {
|
|||
/// A weak reference to an inspector window.
|
||||
inspector: ?*InspectorWindow = null,
|
||||
|
||||
// True if the current surface is a split, this is used to apply
|
||||
// unfocused-split-* options
|
||||
is_split: bool = false,
|
||||
|
||||
// Template binds
|
||||
child_exited_overlay: *ChildExited,
|
||||
context_menu: *gtk.PopoverMenu,
|
||||
|
|
@ -600,6 +623,16 @@ pub const Surface = extern struct {
|
|||
return @intFromBool(config.@"bell-features".border);
|
||||
}
|
||||
|
||||
/// Callback used to determine whether unfocused-split-fill / unfocused-split-opacity
|
||||
/// should be applied to the surface
|
||||
fn closureShouldUnfocusedSplitBeShown(
|
||||
_: *Self,
|
||||
focused: c_int,
|
||||
is_split: c_int,
|
||||
) callconv(.c) c_int {
|
||||
return @intFromBool(focused == 0 and is_split != 0);
|
||||
}
|
||||
|
||||
pub fn toggleFullscreen(self: *Self) void {
|
||||
signals.@"toggle-fullscreen".impl.emit(
|
||||
self,
|
||||
|
|
@ -1227,19 +1260,11 @@ pub const Surface = extern struct {
|
|||
|
||||
// Unset environment varies set by snaps if we're running in a snap.
|
||||
// This allows Ghostty to further launch additional snaps.
|
||||
if (env.get("SNAP")) |_| {
|
||||
env.remove("SNAP");
|
||||
env.remove("DRIRC_CONFIGDIR");
|
||||
env.remove("__EGL_EXTERNAL_PLATFORM_CONFIG_DIRS");
|
||||
env.remove("__EGL_VENDOR_LIBRARY_DIRS");
|
||||
env.remove("LD_LIBRARY_PATH");
|
||||
env.remove("LIBGL_DRIVERS_PATH");
|
||||
env.remove("LIBVA_DRIVERS_PATH");
|
||||
env.remove("VK_LAYER_PATH");
|
||||
env.remove("XLOCALEDIR");
|
||||
env.remove("GDK_PIXBUF_MODULEDIR");
|
||||
env.remove("GDK_PIXBUF_MODULE_FILE");
|
||||
env.remove("GTK_PATH");
|
||||
if (comptime build_config.snap) {
|
||||
if (env.get("SNAP") != null) try filterSnapPaths(
|
||||
alloc,
|
||||
&env,
|
||||
);
|
||||
}
|
||||
|
||||
// This is a hack because it ties ourselves (optionally) to the
|
||||
|
|
@ -1253,6 +1278,79 @@ pub const Surface = extern struct {
|
|||
return env;
|
||||
}
|
||||
|
||||
/// Filter out environment variables that start with forbidden prefixes.
|
||||
fn filterSnapPaths(gpa: std.mem.Allocator, env_map: *std.process.EnvMap) !void {
|
||||
comptime assert(build_config.snap);
|
||||
|
||||
const snap_vars = [_][]const u8{
|
||||
"SNAP",
|
||||
"SNAP_USER_COMMON",
|
||||
"SNAP_USER_DATA",
|
||||
"SNAP_DATA",
|
||||
"SNAP_COMMON",
|
||||
};
|
||||
|
||||
// Use an arena because everything in this function is temporary.
|
||||
var arena = std.heap.ArenaAllocator.init(gpa);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
var env_to_remove = std.ArrayList([]const u8).init(alloc);
|
||||
var env_to_update = std.ArrayList(struct {
|
||||
key: []const u8,
|
||||
value: []const u8,
|
||||
}).init(alloc);
|
||||
|
||||
var it = env_map.iterator();
|
||||
while (it.next()) |entry| {
|
||||
const key = entry.key_ptr.*;
|
||||
const value = entry.value_ptr.*;
|
||||
|
||||
// Ignore fields we set ourself
|
||||
if (std.mem.eql(u8, key, "TERMINFO")) continue;
|
||||
if (std.mem.startsWith(u8, key, "GHOSTTY")) continue;
|
||||
|
||||
// Any env var starting with SNAP must be removed
|
||||
if (std.mem.startsWith(u8, key, "SNAP_")) {
|
||||
try env_to_remove.append(key);
|
||||
continue;
|
||||
}
|
||||
|
||||
var filtered_paths = std.ArrayList([]const u8).init(alloc);
|
||||
defer filtered_paths.deinit();
|
||||
|
||||
var modified = false;
|
||||
var paths = std.mem.splitAny(u8, value, ":");
|
||||
while (paths.next()) |path| {
|
||||
var include = true;
|
||||
for (snap_vars) |k| if (env_map.get(k)) |snap_path| {
|
||||
if (snap_path.len == 0) continue;
|
||||
if (std.mem.startsWith(u8, path, snap_path)) {
|
||||
include = false;
|
||||
modified = true;
|
||||
break;
|
||||
}
|
||||
};
|
||||
if (include) try filtered_paths.append(path);
|
||||
}
|
||||
|
||||
if (modified) {
|
||||
if (filtered_paths.items.len > 0) {
|
||||
const new_value = try std.mem.join(alloc, ":", filtered_paths.items);
|
||||
try env_to_update.append(.{ .key = key, .value = new_value });
|
||||
} else {
|
||||
try env_to_remove.append(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (env_to_update.items) |item| try env_map.put(
|
||||
item.key,
|
||||
item.value,
|
||||
);
|
||||
for (env_to_remove.items) |key| _ = env_map.remove(key);
|
||||
}
|
||||
|
||||
pub fn clipboardRequest(
|
||||
self: *Self,
|
||||
clipboard_type: apprt.Clipboard,
|
||||
|
|
@ -2763,6 +2861,7 @@ pub const Surface = extern struct {
|
|||
class.bindTemplateCallback("notify_mouse_shape", &propMouseShape);
|
||||
class.bindTemplateCallback("notify_bell_ringing", &propBellRinging);
|
||||
class.bindTemplateCallback("should_border_be_shown", &closureShouldBorderBeShown);
|
||||
class.bindTemplateCallback("should_unfocused_split_be_shown", &closureShouldUnfocusedSplitBeShown);
|
||||
|
||||
// Properties
|
||||
gobject.ext.registerProperties(class, &.{
|
||||
|
|
@ -2781,6 +2880,7 @@ pub const Surface = extern struct {
|
|||
properties.title.impl,
|
||||
properties.@"title-override".impl,
|
||||
properties.zoom.impl,
|
||||
properties.@"is-split".impl,
|
||||
});
|
||||
|
||||
// Signals
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const build_config = @import("../../build_config.zig");
|
||||
const internal_os = @import("../../os/main.zig");
|
||||
const glib = @import("glib");
|
||||
|
||||
pub fn resourcesDir(alloc: Allocator) !internal_os.ResourcesDir {
|
||||
if (comptime build_config.flatpak) {
|
||||
// Only consult Flatpak runtime data for host case.
|
||||
if (internal_os.isFlatpak()) {
|
||||
var result: internal_os.ResourcesDir = .{
|
||||
.app_path = try alloc.dupe(u8, "/app/share/ghostty"),
|
||||
};
|
||||
errdefer alloc.free(result.app_path.?);
|
||||
|
||||
const keyfile = glib.KeyFile.new();
|
||||
defer keyfile.unref();
|
||||
|
||||
if (keyfile.loadFromFile("/.flatpak-info", .{}, null) == 0) return result;
|
||||
const app_dir = std.mem.span(keyfile.getString("Instance", "app-path", null)) orelse return result;
|
||||
defer glib.free(app_dir.ptr);
|
||||
|
||||
result.host_path = try std.fs.path.join(alloc, &[_][]const u8{ app_dir, "share", "ghostty" });
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return try internal_os.resourcesDir(alloc);
|
||||
}
|
||||
|
|
@ -115,6 +115,20 @@ Overlay terminal_page {
|
|||
label: bind template.mouse-hover-url;
|
||||
}
|
||||
|
||||
[overlay]
|
||||
// Apply unfocused-split-fill and unfocused-split-opacity to current surface
|
||||
// this is only applied when a tab has more than one surface
|
||||
Revealer {
|
||||
reveal-child: bind $should_unfocused_split_be_shown(template.focused, template.is-split) as <bool>;
|
||||
transition-duration: 0;
|
||||
|
||||
DrawingArea {
|
||||
styles [
|
||||
"unfocused-split",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// Event controllers for interactivity
|
||||
EventControllerFocus {
|
||||
enter => $focus_enter();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
const std = @import("std");
|
||||
|
||||
/// Runtime is the runtime to use for Ghostty. All runtimes do not provide
|
||||
/// equivalent feature sets.
|
||||
pub const Runtime = enum {
|
||||
/// Will not produce an executable at all when `zig build` is called.
|
||||
/// This is only useful if you're only interested in the lib only (macOS).
|
||||
none,
|
||||
|
||||
/// GTK4. Rich windowed application. This uses a full GObject-based
|
||||
/// approach to building the application.
|
||||
gtk,
|
||||
|
||||
pub fn default(target: std.Target) Runtime {
|
||||
return switch (target.os.tag) {
|
||||
// The Linux and FreeBSD default is GTK because it is a full
|
||||
// featured application.
|
||||
.linux, .freebsd => .gtk,
|
||||
// Otherwise, we do NONE so we don't create an exe and we create
|
||||
// libghostty. On macOS, Xcode is used to build the app that links
|
||||
// to libghostty.
|
||||
else => .none,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
test {
|
||||
_ = Runtime;
|
||||
}
|
||||
|
|
@ -10,8 +10,8 @@ const Allocator = std.mem.Allocator;
|
|||
const Benchmark = @import("Benchmark.zig");
|
||||
const options = @import("options.zig");
|
||||
const UTF8Decoder = @import("../terminal/UTF8Decoder.zig");
|
||||
const symbols = @import("../unicode/symbols.zig");
|
||||
const uucode = @import("uucode");
|
||||
const symbols_table = @import("../unicode/symbols_table.zig").table;
|
||||
|
||||
const log = std.log.scoped(.@"is-symbol-bench");
|
||||
|
||||
|
|
@ -128,7 +128,7 @@ fn stepTable(ptr: *anyopaque) Benchmark.Error!void {
|
|||
const cp_, const consumed = d.next(c);
|
||||
assert(consumed);
|
||||
if (cp_) |cp| {
|
||||
std.mem.doNotOptimizeAway(symbols.table.get(cp));
|
||||
std.mem.doNotOptimizeAway(symbols_table.get(cp));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,12 +5,13 @@ const Config = @This();
|
|||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
const apprt = @import("../apprt.zig");
|
||||
const font = @import("../font/main.zig");
|
||||
const rendererpkg = @import("../renderer.zig");
|
||||
const Command = @import("../Command.zig");
|
||||
const ApprtRuntime = @import("../apprt/runtime.zig").Runtime;
|
||||
const FontBackend = @import("../font/backend.zig").Backend;
|
||||
const RendererBackend = @import("../renderer/backend.zig").Backend;
|
||||
const TerminalBuildOptions = @import("../terminal/build_options.zig").Options;
|
||||
const XCFramework = @import("GhosttyXCFramework.zig");
|
||||
const WasmTarget = @import("../os/wasm/target.zig").Target;
|
||||
const expandPath = @import("../os/path.zig").expand;
|
||||
|
||||
const gtk = @import("gtk.zig");
|
||||
const GitVersion = @import("GitVersion.zig");
|
||||
|
|
@ -29,14 +30,15 @@ xcframework_target: XCFramework.Target = .universal,
|
|||
wasm_target: WasmTarget,
|
||||
|
||||
/// Comptime interfaces
|
||||
app_runtime: apprt.Runtime = .none,
|
||||
renderer: rendererpkg.Impl = .opengl,
|
||||
font_backend: font.Backend = .freetype,
|
||||
app_runtime: ApprtRuntime = .none,
|
||||
renderer: RendererBackend = .opengl,
|
||||
font_backend: FontBackend = .freetype,
|
||||
|
||||
/// Feature flags
|
||||
x11: bool = false,
|
||||
wayland: bool = false,
|
||||
sentry: bool = true,
|
||||
simd: bool = true,
|
||||
i18n: bool = true,
|
||||
wasm_shared: bool = true,
|
||||
|
||||
|
|
@ -51,6 +53,7 @@ patch_rpath: ?[]const u8 = null,
|
|||
|
||||
/// Artifacts
|
||||
flatpak: bool = false,
|
||||
snap: bool = false,
|
||||
emit_bench: bool = false,
|
||||
emit_docs: bool = false,
|
||||
emit_exe: bool = false,
|
||||
|
|
@ -126,22 +129,22 @@ pub fn init(b: *std.Build) !Config {
|
|||
//---------------------------------------------------------------
|
||||
// Comptime Interfaces
|
||||
config.font_backend = b.option(
|
||||
font.Backend,
|
||||
FontBackend,
|
||||
"font-backend",
|
||||
"The font backend to use for discovery and rasterization.",
|
||||
) orelse font.Backend.default(target.result, wasm_target);
|
||||
) orelse FontBackend.default(target.result, wasm_target);
|
||||
|
||||
config.app_runtime = b.option(
|
||||
apprt.Runtime,
|
||||
ApprtRuntime,
|
||||
"app-runtime",
|
||||
"The app runtime to use. Not all values supported on all platforms.",
|
||||
) orelse apprt.Runtime.default(target.result);
|
||||
) orelse ApprtRuntime.default(target.result);
|
||||
|
||||
config.renderer = b.option(
|
||||
rendererpkg.Impl,
|
||||
RendererBackend,
|
||||
"renderer",
|
||||
"The app runtime to use. Not all values supported on all platforms.",
|
||||
) orelse rendererpkg.Impl.default(target.result, wasm_target);
|
||||
) orelse RendererBackend.default(target.result, wasm_target);
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Feature Flags
|
||||
|
|
@ -152,6 +155,12 @@ pub fn init(b: *std.Build) !Config {
|
|||
"Build for Flatpak (integrates with Flatpak APIs). Only has an effect targeting Linux.",
|
||||
) orelse false;
|
||||
|
||||
config.snap = b.option(
|
||||
bool,
|
||||
"snap",
|
||||
"Build for Snap (do specific Snap operations). Only has an effect targeting Linux.",
|
||||
) orelse false;
|
||||
|
||||
config.sentry = b.option(
|
||||
bool,
|
||||
"sentry",
|
||||
|
|
@ -166,6 +175,12 @@ pub fn init(b: *std.Build) !Config {
|
|||
}
|
||||
};
|
||||
|
||||
config.simd = b.option(
|
||||
bool,
|
||||
"simd",
|
||||
"Build with SIMD-accelerated code paths. Results in significant performance improvements.",
|
||||
) orelse true;
|
||||
|
||||
config.wayland = b.option(
|
||||
bool,
|
||||
"gtk-wayland",
|
||||
|
|
@ -332,7 +347,7 @@ pub fn init(b: *std.Build) !Config {
|
|||
if (system_package) break :emit_docs true;
|
||||
|
||||
// We only default to true if we can find pandoc.
|
||||
const path = Command.expandPath(b.allocator, "pandoc") catch
|
||||
const path = expandPath(b.allocator, "pandoc") catch
|
||||
break :emit_docs false;
|
||||
defer if (path) |p| b.allocator.free(p);
|
||||
break :emit_docs path != null;
|
||||
|
|
@ -442,13 +457,15 @@ pub fn addOptions(self: *const Config, step: *std.Build.Step.Options) !void {
|
|||
// We need to break these down individual because addOption doesn't
|
||||
// support all types.
|
||||
step.addOption(bool, "flatpak", self.flatpak);
|
||||
step.addOption(bool, "snap", self.snap);
|
||||
step.addOption(bool, "x11", self.x11);
|
||||
step.addOption(bool, "wayland", self.wayland);
|
||||
step.addOption(bool, "sentry", self.sentry);
|
||||
step.addOption(bool, "simd", self.simd);
|
||||
step.addOption(bool, "i18n", self.i18n);
|
||||
step.addOption(apprt.Runtime, "app_runtime", self.app_runtime);
|
||||
step.addOption(font.Backend, "font_backend", self.font_backend);
|
||||
step.addOption(rendererpkg.Impl, "renderer", self.renderer);
|
||||
step.addOption(ApprtRuntime, "app_runtime", self.app_runtime);
|
||||
step.addOption(FontBackend, "font_backend", self.font_backend);
|
||||
step.addOption(RendererBackend, "renderer", self.renderer);
|
||||
step.addOption(ExeEntrypoint, "exe_entrypoint", self.exe_entrypoint);
|
||||
step.addOption(WasmTarget, "wasm_target", self.wasm_target);
|
||||
step.addOption(bool, "wasm_shared", self.wasm_shared);
|
||||
|
|
@ -474,6 +491,23 @@ pub fn addOptions(self: *const Config, step: *std.Build.Step.Options) !void {
|
|||
);
|
||||
}
|
||||
|
||||
/// Returns the build options for the terminal module. This assumes a
|
||||
/// Ghostty executable being built. Callers should modify this as needed.
|
||||
pub fn terminalOptions(self: *const Config) TerminalBuildOptions {
|
||||
return .{
|
||||
.artifact = .ghostty,
|
||||
.simd = self.simd,
|
||||
.oniguruma = true,
|
||||
.slow_runtime_safety = switch (self.optimize) {
|
||||
.Debug => true,
|
||||
.ReleaseSafe,
|
||||
.ReleaseSmall,
|
||||
.ReleaseFast,
|
||||
=> false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns a baseline CPU target retaining all the other CPU configs.
|
||||
pub fn baselineTarget(self: *const Config) std.Build.ResolvedTarget {
|
||||
// Set our cpu model as baseline. There may need to be other modifications
|
||||
|
|
@ -503,9 +537,10 @@ pub fn fromOptions() Config {
|
|||
|
||||
.version = options.app_version,
|
||||
.flatpak = options.flatpak,
|
||||
.app_runtime = std.meta.stringToEnum(apprt.Runtime, @tagName(options.app_runtime)).?,
|
||||
.font_backend = std.meta.stringToEnum(font.Backend, @tagName(options.font_backend)).?,
|
||||
.renderer = std.meta.stringToEnum(rendererpkg.Impl, @tagName(options.renderer)).?,
|
||||
.app_runtime = std.meta.stringToEnum(ApprtRuntime, @tagName(options.app_runtime)).?,
|
||||
.font_backend = std.meta.stringToEnum(FontBackend, @tagName(options.font_backend)).?,
|
||||
.renderer = std.meta.stringToEnum(RendererBackend, @tagName(options.renderer)).?,
|
||||
.snap = options.snap,
|
||||
.exe_entrypoint = std.meta.stringToEnum(ExeEntrypoint, @tagName(options.exe_entrypoint)).?,
|
||||
.wasm_target = std.meta.stringToEnum(WasmTarget, @tagName(options.wasm_target)).?,
|
||||
.wasm_shared = options.wasm_shared,
|
||||
|
|
|
|||
|
|
@ -25,11 +25,14 @@ pub fn init(b: *std.Build) !GhosttyFrameData {
|
|||
});
|
||||
|
||||
const run = b.addRunArtifact(exe);
|
||||
// Both the compressed framedata and the Zig source file
|
||||
// have to be put in the same directory, since the compressed file
|
||||
// has to be within the source file's include path.
|
||||
const dir = run.addOutputDirectoryArg("framedata");
|
||||
|
||||
_ = run.addOutputFileArg("framedata.compressed");
|
||||
return .{
|
||||
.exe = exe,
|
||||
.output = run.captureStdOut(),
|
||||
.output = dir.path(b, "framedata.zig"),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ const std = @import("std");
|
|||
const builtin = @import("builtin");
|
||||
const Config = @import("Config.zig");
|
||||
const gresource = @import("../apprt/gtk/build/gresource.zig");
|
||||
const internal_os = @import("../os/main.zig");
|
||||
const locales = @import("../os/i18n_locales.zig").locales;
|
||||
|
||||
const domain = "com.mitchellh.ghostty";
|
||||
|
||||
|
|
@ -21,7 +21,7 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyI18n {
|
|||
var steps = std.ArrayList(*std.Build.Step).init(b.allocator);
|
||||
defer steps.deinit();
|
||||
|
||||
inline for (internal_os.i18n.locales) |locale| {
|
||||
inline for (locales) |locale| {
|
||||
// There is no encoding suffix in the LC_MESSAGES path on FreeBSD,
|
||||
// so we need to remove it from `locale` to have a correct destination string.
|
||||
// (/usr/local/share/locale/en_AU/LC_MESSAGES)
|
||||
|
|
@ -155,7 +155,7 @@ fn createUpdateStep(b: *std.Build) !*std.Build.Step {
|
|||
"po/" ++ domain ++ ".pot",
|
||||
);
|
||||
|
||||
inline for (internal_os.i18n.locales) |locale| {
|
||||
inline for (locales) |locale| {
|
||||
const msgmerge = b.addSystemCommand(&.{ "msgmerge", "--quiet", "--no-fuzzy-matching" });
|
||||
msgmerge.addFileArg(b.path("po/" ++ locale ++ ".po"));
|
||||
msgmerge.addFileArg(xgettext.captureStdOut());
|
||||
|
|
|
|||
|
|
@ -5,9 +5,6 @@ const builtin = @import("builtin");
|
|||
const assert = std.debug.assert;
|
||||
const buildpkg = @import("main.zig");
|
||||
const Config = @import("Config.zig");
|
||||
const config_vim = @import("../config/vim.zig");
|
||||
const config_sublime_syntax = @import("../config/sublime_syntax.zig");
|
||||
const terminfo = @import("../terminfo/main.zig");
|
||||
const RunStep = std.Build.Step.Run;
|
||||
|
||||
steps: []*std.Build.Step,
|
||||
|
|
@ -16,6 +13,19 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyResources {
|
|||
var steps = std.ArrayList(*std.Build.Step).init(b.allocator);
|
||||
errdefer steps.deinit();
|
||||
|
||||
// This is the exe used to generate some build data.
|
||||
const build_data_exe = b.addExecutable(.{
|
||||
.name = "ghostty-build-data",
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("src/main_build_data.zig"),
|
||||
.target = b.graph.host,
|
||||
.strip = false,
|
||||
.omit_frame_pointer = false,
|
||||
.unwind_tables = .sync,
|
||||
}),
|
||||
});
|
||||
build_data_exe.linkLibC();
|
||||
|
||||
// Terminfo
|
||||
terminfo: {
|
||||
const os_tag = cfg.target.result.os.tag;
|
||||
|
|
@ -25,13 +35,10 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyResources {
|
|||
"terminfo";
|
||||
|
||||
// Encode our terminfo
|
||||
var str = std.ArrayList(u8).init(b.allocator);
|
||||
defer str.deinit();
|
||||
try terminfo.ghostty.encode(str.writer());
|
||||
|
||||
// Write it
|
||||
var wf = b.addWriteFiles();
|
||||
const source = wf.add("ghostty.terminfo", str.items);
|
||||
const run = b.addRunArtifact(build_data_exe);
|
||||
run.addArg("+terminfo");
|
||||
const wf = b.addWriteFiles();
|
||||
const source = wf.addCopyFile(run.captureStdOut(), "ghostty.terminfo");
|
||||
|
||||
if (cfg.emit_terminfo) {
|
||||
const source_install = b.addInstallFile(
|
||||
|
|
@ -130,8 +137,10 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyResources {
|
|||
|
||||
// Fish shell completions
|
||||
{
|
||||
const run = b.addRunArtifact(build_data_exe);
|
||||
run.addArg("+fish");
|
||||
const wf = b.addWriteFiles();
|
||||
_ = wf.add("ghostty.fish", buildpkg.fish_completions);
|
||||
_ = wf.addCopyFile(run.captureStdOut(), "ghostty.fish");
|
||||
|
||||
const install_step = b.addInstallDirectory(.{
|
||||
.source_dir = wf.getDirectory(),
|
||||
|
|
@ -143,8 +152,10 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyResources {
|
|||
|
||||
// zsh shell completions
|
||||
{
|
||||
const run = b.addRunArtifact(build_data_exe);
|
||||
run.addArg("+zsh");
|
||||
const wf = b.addWriteFiles();
|
||||
_ = wf.add("_ghostty", buildpkg.zsh_completions);
|
||||
_ = wf.addCopyFile(run.captureStdOut(), "_ghostty");
|
||||
|
||||
const install_step = b.addInstallDirectory(.{
|
||||
.source_dir = wf.getDirectory(),
|
||||
|
|
@ -156,8 +167,10 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyResources {
|
|||
|
||||
// bash shell completions
|
||||
{
|
||||
const run = b.addRunArtifact(build_data_exe);
|
||||
run.addArg("+bash");
|
||||
const wf = b.addWriteFiles();
|
||||
_ = wf.add("ghostty.bash", buildpkg.bash_completions);
|
||||
_ = wf.addCopyFile(run.captureStdOut(), "ghostty.bash");
|
||||
|
||||
const install_step = b.addInstallDirectory(.{
|
||||
.source_dir = wf.getDirectory(),
|
||||
|
|
@ -167,39 +180,44 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyResources {
|
|||
try steps.append(&install_step.step);
|
||||
}
|
||||
|
||||
// Vim plugin
|
||||
// Vim and Neovim plugin
|
||||
{
|
||||
const wf = b.addWriteFiles();
|
||||
_ = wf.add("syntax/ghostty.vim", config_vim.syntax);
|
||||
_ = wf.add("ftdetect/ghostty.vim", config_vim.ftdetect);
|
||||
_ = wf.add("ftplugin/ghostty.vim", config_vim.ftplugin);
|
||||
_ = wf.add("compiler/ghostty.vim", config_vim.compiler);
|
||||
|
||||
const install_step = b.addInstallDirectory(.{
|
||||
{
|
||||
const run = b.addRunArtifact(build_data_exe);
|
||||
run.addArg("+vim-syntax");
|
||||
_ = wf.addCopyFile(run.captureStdOut(), "syntax/ghostty.vim");
|
||||
}
|
||||
{
|
||||
const run = b.addRunArtifact(build_data_exe);
|
||||
run.addArg("+vim-ftdetect");
|
||||
_ = wf.addCopyFile(run.captureStdOut(), "ftdetect/ghostty.vim");
|
||||
}
|
||||
{
|
||||
const run = b.addRunArtifact(build_data_exe);
|
||||
run.addArg("+vim-ftplugin");
|
||||
_ = wf.addCopyFile(run.captureStdOut(), "ftplugin/ghostty.vim");
|
||||
}
|
||||
{
|
||||
const run = b.addRunArtifact(build_data_exe);
|
||||
run.addArg("+vim-compiler");
|
||||
_ = wf.addCopyFile(run.captureStdOut(), "compiler/ghostty.vim");
|
||||
}
|
||||
|
||||
const vim_step = b.addInstallDirectory(.{
|
||||
.source_dir = wf.getDirectory(),
|
||||
.install_dir = .prefix,
|
||||
.install_subdir = "share/vim/vimfiles",
|
||||
});
|
||||
try steps.append(&install_step.step);
|
||||
}
|
||||
try steps.append(&vim_step.step);
|
||||
|
||||
// Neovim plugin
|
||||
// This is just a copy-paste of the Vim plugin, but using a Neovim subdir.
|
||||
// By default, Neovim doesn't look inside share/vim/vimfiles. Some distros
|
||||
// configure it to do that however. Fedora, does not as a counterexample.
|
||||
{
|
||||
const wf = b.addWriteFiles();
|
||||
_ = wf.add("syntax/ghostty.vim", config_vim.syntax);
|
||||
_ = wf.add("ftdetect/ghostty.vim", config_vim.ftdetect);
|
||||
_ = wf.add("ftplugin/ghostty.vim", config_vim.ftplugin);
|
||||
_ = wf.add("compiler/ghostty.vim", config_vim.compiler);
|
||||
|
||||
const install_step = b.addInstallDirectory(.{
|
||||
const neovim_step = b.addInstallDirectory(.{
|
||||
.source_dir = wf.getDirectory(),
|
||||
.install_dir = .prefix,
|
||||
.install_subdir = "share/nvim/site",
|
||||
});
|
||||
try steps.append(&install_step.step);
|
||||
try steps.append(&neovim_step.step);
|
||||
}
|
||||
|
||||
// Sublime syntax highlighting for bat cli tool
|
||||
|
|
@ -209,8 +227,10 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyResources {
|
|||
// the config file within the '~.config/bat' directory
|
||||
// (ex: --map-syntax "/Users/user/.config/ghostty/config:Ghostty Config").
|
||||
{
|
||||
const run = b.addRunArtifact(build_data_exe);
|
||||
run.addArg("+sublime");
|
||||
const wf = b.addWriteFiles();
|
||||
_ = wf.add("ghostty.sublime-syntax", config_sublime_syntax.syntax);
|
||||
_ = wf.addCopyFile(run.captureStdOut(), "ghostty.sublime-syntax");
|
||||
|
||||
const install_step = b.addInstallDirectory(.{
|
||||
.source_dir = wf.getDirectory(),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
//! GhosttyZig generates the Zig modules that Ghostty exports
|
||||
//! for downstream usage.
|
||||
const GhosttyZig = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const Config = @import("Config.zig");
|
||||
const SharedDeps = @import("SharedDeps.zig");
|
||||
|
||||
vt: *std.Build.Module,
|
||||
|
||||
pub fn init(
|
||||
b: *std.Build,
|
||||
cfg: *const Config,
|
||||
deps: *const SharedDeps,
|
||||
) !GhosttyZig {
|
||||
// General build options
|
||||
const general_options = b.addOptions();
|
||||
try cfg.addOptions(general_options);
|
||||
|
||||
// Terminal module build options
|
||||
var vt_options = cfg.terminalOptions();
|
||||
vt_options.artifact = .lib;
|
||||
// We presently don't allow Oniguruma in our Zig module at all.
|
||||
// We should expose this as a build option in the future so we can
|
||||
// conditionally do this.
|
||||
vt_options.oniguruma = false;
|
||||
|
||||
const vt = b.addModule("ghostty-vt", .{
|
||||
.root_source_file = b.path("src/lib_vt.zig"),
|
||||
.target = cfg.target,
|
||||
.optimize = cfg.optimize,
|
||||
|
||||
// SIMD require libc/libcpp (both) but otherwise we don't care.
|
||||
.link_libc = if (cfg.simd) true else null,
|
||||
.link_libcpp = if (cfg.simd) true else null,
|
||||
});
|
||||
vt.addOptions("build_options", general_options);
|
||||
vt_options.add(b, vt);
|
||||
|
||||
// We always need unicode tables
|
||||
deps.unicode_tables.addModuleImport(vt);
|
||||
|
||||
// If SIMD is enabled, add all our SIMD dependencies.
|
||||
if (cfg.simd) {
|
||||
try SharedDeps.addSimd(b, vt, null);
|
||||
}
|
||||
|
||||
return .{ .vt = vt };
|
||||
}
|
||||
|
|
@ -31,9 +31,14 @@ pub fn init(b: *std.Build, cfg: *const Config) !HelpStrings {
|
|||
exe.root_module.addOptions("build_options", options);
|
||||
|
||||
const help_run = b.addRunArtifact(exe);
|
||||
|
||||
// Generated Zig files have to end with .zig
|
||||
const wf = b.addWriteFiles();
|
||||
const output = wf.addCopyFile(help_run.captureStdOut(), "helpgen.zig");
|
||||
|
||||
return .{
|
||||
.exe = exe,
|
||||
.output = help_run.captureStdOut(),
|
||||
.output = output,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -117,6 +117,9 @@ pub fn add(
|
|||
// Every exe gets build options populated
|
||||
step.root_module.addOptions("build_options", self.options);
|
||||
|
||||
// Every exe needs the terminal options
|
||||
self.config.terminalOptions().add(b, step.root_module);
|
||||
|
||||
// Freetype
|
||||
_ = b.systemIntegrationOption("freetype", .{}); // Shows it in help
|
||||
if (self.config.font_backend.hasFreetype()) {
|
||||
|
|
@ -276,21 +279,6 @@ pub fn add(
|
|||
}
|
||||
}
|
||||
|
||||
// Simdutf
|
||||
if (b.systemIntegrationOption("simdutf", .{})) {
|
||||
step.linkSystemLibrary2("simdutf", dynamic_link_opts);
|
||||
} else {
|
||||
if (b.lazyDependency("simdutf", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
})) |simdutf_dep| {
|
||||
step.linkLibrary(simdutf_dep.artifact("simdutf"));
|
||||
try static_libs.append(
|
||||
simdutf_dep.artifact("simdutf").getEmittedBin(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Sentry
|
||||
if (self.config.sentry) {
|
||||
if (b.lazyDependency("sentry", .{
|
||||
|
|
@ -319,6 +307,13 @@ pub fn add(
|
|||
}
|
||||
}
|
||||
|
||||
// Simd
|
||||
if (self.config.simd) try addSimd(
|
||||
b,
|
||||
step.root_module,
|
||||
&static_libs,
|
||||
);
|
||||
|
||||
// Wasm we do manually since it is such a different build.
|
||||
if (step.rootModuleTarget().cpu.arch == .wasm32) {
|
||||
if (b.lazyDependency("zig_js", .{
|
||||
|
|
@ -353,35 +348,8 @@ pub fn add(
|
|||
step.addIncludePath(b.path("src/apprt/gtk"));
|
||||
}
|
||||
|
||||
// C++ files
|
||||
// libcpp is required for various dependencies
|
||||
step.linkLibCpp();
|
||||
step.addIncludePath(b.path("src"));
|
||||
{
|
||||
// From hwy/detect_targets.h
|
||||
const HWY_AVX3_SPR: c_int = 1 << 4;
|
||||
const HWY_AVX3_ZEN4: c_int = 1 << 6;
|
||||
const HWY_AVX3_DL: c_int = 1 << 7;
|
||||
const HWY_AVX3: c_int = 1 << 8;
|
||||
|
||||
// Zig 0.13 bug: https://github.com/ziglang/zig/issues/20414
|
||||
// To workaround this we just disable AVX512 support completely.
|
||||
// The performance difference between AVX2 and AVX512 is not
|
||||
// significant for our use case and AVX512 is very rare on consumer
|
||||
// hardware anyways.
|
||||
const HWY_DISABLED_TARGETS: c_int = HWY_AVX3_SPR | HWY_AVX3_ZEN4 | HWY_AVX3_DL | HWY_AVX3;
|
||||
|
||||
step.addCSourceFiles(.{
|
||||
.files = &.{
|
||||
"src/simd/base64.cpp",
|
||||
"src/simd/codepoint_width.cpp",
|
||||
"src/simd/index_of.cpp",
|
||||
"src/simd/vt.cpp",
|
||||
},
|
||||
.flags = if (step.rootModuleTarget().cpu.arch == .x86_64) &.{
|
||||
b.fmt("-DHWY_DISABLED_TARGETS={}", .{HWY_DISABLED_TARGETS}),
|
||||
} else &.{},
|
||||
});
|
||||
}
|
||||
|
||||
// We always require the system SDK so that our system headers are available.
|
||||
// This makes things like `os/log.h` available for cross-compiling.
|
||||
|
|
@ -493,59 +461,43 @@ pub fn add(
|
|||
try static_libs.append(cimgui_dep.artifact("cimgui").getEmittedBin());
|
||||
}
|
||||
|
||||
// Highway
|
||||
if (b.lazyDependency("highway", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
})) |highway_dep| {
|
||||
step.linkLibrary(highway_dep.artifact("highway"));
|
||||
try static_libs.append(highway_dep.artifact("highway").getEmittedBin());
|
||||
}
|
||||
|
||||
// utfcpp - This is used as a dependency on our hand-written C++ code
|
||||
if (b.lazyDependency("utfcpp", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
})) |utfcpp_dep| {
|
||||
step.linkLibrary(utfcpp_dep.artifact("utfcpp"));
|
||||
try static_libs.append(utfcpp_dep.artifact("utfcpp").getEmittedBin());
|
||||
}
|
||||
|
||||
// Fonts
|
||||
{
|
||||
// JetBrains Mono
|
||||
const jb_mono = b.dependency("jetbrains_mono", .{});
|
||||
step.root_module.addAnonymousImport(
|
||||
"jetbrains_mono_regular",
|
||||
.{ .root_source_file = jb_mono.path("fonts/ttf/JetBrainsMono-Regular.ttf") },
|
||||
);
|
||||
step.root_module.addAnonymousImport(
|
||||
"jetbrains_mono_bold",
|
||||
.{ .root_source_file = jb_mono.path("fonts/ttf/JetBrainsMono-Bold.ttf") },
|
||||
);
|
||||
step.root_module.addAnonymousImport(
|
||||
"jetbrains_mono_italic",
|
||||
.{ .root_source_file = jb_mono.path("fonts/ttf/JetBrainsMono-Italic.ttf") },
|
||||
);
|
||||
step.root_module.addAnonymousImport(
|
||||
"jetbrains_mono_bold_italic",
|
||||
.{ .root_source_file = jb_mono.path("fonts/ttf/JetBrainsMono-BoldItalic.ttf") },
|
||||
);
|
||||
step.root_module.addAnonymousImport(
|
||||
"jetbrains_mono_variable",
|
||||
.{ .root_source_file = jb_mono.path("fonts/variable/JetBrainsMono[wght].ttf") },
|
||||
);
|
||||
step.root_module.addAnonymousImport(
|
||||
"jetbrains_mono_variable_italic",
|
||||
.{ .root_source_file = jb_mono.path("fonts/variable/JetBrainsMono-Italic[wght].ttf") },
|
||||
);
|
||||
if (b.lazyDependency("jetbrains_mono", .{})) |jb_mono| {
|
||||
step.root_module.addAnonymousImport(
|
||||
"jetbrains_mono_regular",
|
||||
.{ .root_source_file = jb_mono.path("fonts/ttf/JetBrainsMono-Regular.ttf") },
|
||||
);
|
||||
step.root_module.addAnonymousImport(
|
||||
"jetbrains_mono_bold",
|
||||
.{ .root_source_file = jb_mono.path("fonts/ttf/JetBrainsMono-Bold.ttf") },
|
||||
);
|
||||
step.root_module.addAnonymousImport(
|
||||
"jetbrains_mono_italic",
|
||||
.{ .root_source_file = jb_mono.path("fonts/ttf/JetBrainsMono-Italic.ttf") },
|
||||
);
|
||||
step.root_module.addAnonymousImport(
|
||||
"jetbrains_mono_bold_italic",
|
||||
.{ .root_source_file = jb_mono.path("fonts/ttf/JetBrainsMono-BoldItalic.ttf") },
|
||||
);
|
||||
step.root_module.addAnonymousImport(
|
||||
"jetbrains_mono_variable",
|
||||
.{ .root_source_file = jb_mono.path("fonts/variable/JetBrainsMono[wght].ttf") },
|
||||
);
|
||||
step.root_module.addAnonymousImport(
|
||||
"jetbrains_mono_variable_italic",
|
||||
.{ .root_source_file = jb_mono.path("fonts/variable/JetBrainsMono-Italic[wght].ttf") },
|
||||
);
|
||||
}
|
||||
|
||||
// Symbols-only nerd font
|
||||
const nf_symbols = b.dependency("nerd_fonts_symbols_only", .{});
|
||||
step.root_module.addAnonymousImport(
|
||||
"nerd_fonts_symbols_only",
|
||||
.{ .root_source_file = nf_symbols.path("SymbolsNerdFont-Regular.ttf") },
|
||||
);
|
||||
if (b.lazyDependency("nerd_fonts_symbols_only", .{})) |nf_symbols| {
|
||||
step.root_module.addAnonymousImport(
|
||||
"nerd_fonts_symbols_only",
|
||||
.{ .root_source_file = nf_symbols.path("SymbolsNerdFont-Regular.ttf") },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// If we're building an exe then we have additional dependencies.
|
||||
|
|
@ -627,17 +579,20 @@ fn addGtkNg(
|
|||
"plasma_wayland_protocols",
|
||||
.{},
|
||||
);
|
||||
const zig_wayland_import_ = b.lazyImport(
|
||||
@import("../../build.zig"),
|
||||
"zig_wayland",
|
||||
);
|
||||
const zig_wayland_dep_ = b.lazyDependency("zig_wayland", .{});
|
||||
|
||||
// Unwrap or return, there are no more dependencies below.
|
||||
const wayland_dep = wayland_dep_ orelse break :wayland;
|
||||
const wayland_protocols_dep = wayland_protocols_dep_ orelse break :wayland;
|
||||
const plasma_wayland_protocols_dep = plasma_wayland_protocols_dep_ orelse break :wayland;
|
||||
const zig_wayland_import = zig_wayland_import_ orelse break :wayland;
|
||||
const zig_wayland_dep = zig_wayland_dep_ orelse break :wayland;
|
||||
|
||||
// Note that zig_wayland cannot be lazy because lazy dependencies
|
||||
// can't be imported since they don't exist and imports are
|
||||
// resolved at compile time of the build.
|
||||
const zig_wayland_dep = b.dependency("zig_wayland", .{});
|
||||
const Scanner = @import("zig_wayland").Scanner;
|
||||
const Scanner = zig_wayland_import.Scanner;
|
||||
const scanner = Scanner.create(zig_wayland_dep.builder, .{
|
||||
.wayland_xml = wayland_dep.path("protocol/wayland.xml"),
|
||||
.wayland_protocols = wayland_protocols_dep.path(""),
|
||||
|
|
@ -707,6 +662,79 @@ fn addGtkNg(
|
|||
}
|
||||
}
|
||||
|
||||
/// Add only the dependencies required for `Config.simd` enbled. This also
|
||||
/// adds all the simd source files for compilation.
|
||||
pub fn addSimd(
|
||||
b: *std.Build,
|
||||
m: *std.Build.Module,
|
||||
static_libs: ?*LazyPathList,
|
||||
) !void {
|
||||
const target = m.resolved_target.?;
|
||||
const optimize = m.optimize.?;
|
||||
|
||||
// Simdutf
|
||||
if (b.systemIntegrationOption("simdutf", .{})) {
|
||||
m.linkSystemLibrary("simdutf", dynamic_link_opts);
|
||||
} else {
|
||||
if (b.lazyDependency("simdutf", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
})) |simdutf_dep| {
|
||||
m.linkLibrary(simdutf_dep.artifact("simdutf"));
|
||||
if (static_libs) |v| try v.append(
|
||||
simdutf_dep.artifact("simdutf").getEmittedBin(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Highway
|
||||
if (b.lazyDependency("highway", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
})) |highway_dep| {
|
||||
m.linkLibrary(highway_dep.artifact("highway"));
|
||||
if (static_libs) |v| try v.append(highway_dep.artifact("highway").getEmittedBin());
|
||||
}
|
||||
|
||||
// utfcpp - This is used as a dependency on our hand-written C++ code
|
||||
if (b.lazyDependency("utfcpp", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
})) |utfcpp_dep| {
|
||||
m.linkLibrary(utfcpp_dep.artifact("utfcpp"));
|
||||
if (static_libs) |v| try v.append(utfcpp_dep.artifact("utfcpp").getEmittedBin());
|
||||
}
|
||||
|
||||
// SIMD C++ files
|
||||
m.addIncludePath(b.path("src"));
|
||||
{
|
||||
// From hwy/detect_targets.h
|
||||
const HWY_AVX3_SPR: c_int = 1 << 4;
|
||||
const HWY_AVX3_ZEN4: c_int = 1 << 6;
|
||||
const HWY_AVX3_DL: c_int = 1 << 7;
|
||||
const HWY_AVX3: c_int = 1 << 8;
|
||||
|
||||
// Zig 0.13 bug: https://github.com/ziglang/zig/issues/20414
|
||||
// To workaround this we just disable AVX512 support completely.
|
||||
// The performance difference between AVX2 and AVX512 is not
|
||||
// significant for our use case and AVX512 is very rare on consumer
|
||||
// hardware anyways.
|
||||
const HWY_DISABLED_TARGETS: c_int = HWY_AVX3_SPR | HWY_AVX3_ZEN4 | HWY_AVX3_DL | HWY_AVX3;
|
||||
|
||||
m.addCSourceFiles(.{
|
||||
.files = &.{
|
||||
"src/simd/base64.cpp",
|
||||
"src/simd/codepoint_width.cpp",
|
||||
"src/simd/index_of.cpp",
|
||||
"src/simd/vt.cpp",
|
||||
},
|
||||
.flags = if (target.result.cpu.arch == .x86_64) &.{
|
||||
b.fmt("-DHWY_DISABLED_TARGETS={}", .{HWY_DISABLED_TARGETS}),
|
||||
} else &.{},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates the resources that can be prebuilt for our dist build.
|
||||
pub fn gtkNgDistResources(
|
||||
b: *std.Build,
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ pub fn init(b: *std.Build, uucode_tables: std.Build.LazyPath) !UnicodeTables {
|
|||
const props_exe = b.addExecutable(.{
|
||||
.name = "props-unigen",
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("src/unicode/props.zig"),
|
||||
.root_source_file = b.path("src/unicode/props_ziglyph.zig"),
|
||||
.target = b.graph.host,
|
||||
.strip = false,
|
||||
.omit_frame_pointer = false,
|
||||
|
|
@ -26,7 +26,7 @@ pub fn init(b: *std.Build, uucode_tables: std.Build.LazyPath) !UnicodeTables {
|
|||
const symbols_exe = b.addExecutable(.{
|
||||
.name = "symbols-unigen",
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("src/unicode/symbols.zig"),
|
||||
.root_source_file = b.path("src/unicode/symbols_ziglyph.zig"),
|
||||
.target = b.graph.host,
|
||||
.strip = false,
|
||||
.omit_frame_pointer = false,
|
||||
|
|
@ -47,22 +47,35 @@ pub fn init(b: *std.Build, uucode_tables: std.Build.LazyPath) !UnicodeTables {
|
|||
const props_run = b.addRunArtifact(props_exe);
|
||||
const symbols_run = b.addRunArtifact(symbols_exe);
|
||||
|
||||
// Generated Zig files have to end with .zig
|
||||
const wf = b.addWriteFiles();
|
||||
const props_output = wf.addCopyFile(props_run.captureStdOut(), "props.zig");
|
||||
const symbols_output = wf.addCopyFile(symbols_run.captureStdOut(), "symbols.zig");
|
||||
|
||||
return .{
|
||||
.props_exe = props_exe,
|
||||
.symbols_exe = symbols_exe,
|
||||
.props_output = props_run.captureStdOut(),
|
||||
.symbols_output = symbols_run.captureStdOut(),
|
||||
.props_output = props_output,
|
||||
.symbols_output = symbols_output,
|
||||
};
|
||||
}
|
||||
|
||||
/// Add the "unicode_tables" import.
|
||||
pub fn addImport(self: *const UnicodeTables, step: *std.Build.Step.Compile) void {
|
||||
self.props_output.addStepDependencies(&step.step);
|
||||
step.root_module.addAnonymousImport("unicode_tables", .{
|
||||
self.symbols_output.addStepDependencies(&step.step);
|
||||
self.addModuleImport(step.root_module);
|
||||
}
|
||||
|
||||
/// Add the "unicode_tables" import to a module.
|
||||
pub fn addModuleImport(
|
||||
self: *const UnicodeTables,
|
||||
module: *std.Build.Module,
|
||||
) void {
|
||||
module.addAnonymousImport("unicode_tables", .{
|
||||
.root_source_file = self.props_output,
|
||||
});
|
||||
self.symbols_output.addStepDependencies(&step.step);
|
||||
step.root_module.addAnonymousImport("symbols_tables", .{
|
||||
module.addAnonymousImport("symbols_tables", .{
|
||||
.root_source_file = self.symbols_output,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,12 +9,12 @@ pub fn main() !void {
|
|||
// Skip the exe name
|
||||
_ = arg_iter.skip();
|
||||
|
||||
const output_path = arg_iter.next() orelse return error.MissingOutputPath;
|
||||
const out_dir_path = arg_iter.next() orelse return error.MissingOutputPath;
|
||||
const compressed_out = "framedata.compressed";
|
||||
const zig_out = "framedata.zig";
|
||||
|
||||
const out_dir_path = fs.path.dirname(output_path) orelse return error.InvalidOutputPath;
|
||||
const out_dir = try fs.cwd().openDir(out_dir_path, .{});
|
||||
|
||||
const compressed_file = try out_dir.createFile(fs.path.basename(output_path), .{});
|
||||
const compressed_file = try out_dir.createFile(compressed_out, .{});
|
||||
|
||||
// Join the frames with a null byte. We'll split on this later
|
||||
const all_frames = try std.mem.join(gpa.allocator(), "\x01", &frames);
|
||||
|
|
@ -23,13 +23,15 @@ pub fn main() !void {
|
|||
const reader = fbs.reader();
|
||||
try std.compress.flate.compress(reader, compressed_file.writer(), .{});
|
||||
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
const compressed_path = try std.fs.path.join(gpa.allocator(), &.{ out_dir_path, compressed_out });
|
||||
|
||||
try stdout.print(
|
||||
const zig_file = try out_dir.createFile(zig_out, .{});
|
||||
|
||||
try zig_file.writer().print(
|
||||
\\//! This file is auto-generated. Do not edit.
|
||||
\\
|
||||
\\pub const compressed = @embedFile("{s}");
|
||||
, .{output_path});
|
||||
, .{compressed_path});
|
||||
}
|
||||
|
||||
const frames = [_][]const u8{
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ pub const GhosttyI18n = @import("GhosttyI18n.zig");
|
|||
pub const GhosttyXcodebuild = @import("GhosttyXcodebuild.zig");
|
||||
pub const GhosttyXCFramework = @import("GhosttyXCFramework.zig");
|
||||
pub const GhosttyWebdata = @import("GhosttyWebdata.zig");
|
||||
pub const GhosttyZig = @import("GhosttyZig.zig");
|
||||
pub const HelpStrings = @import("HelpStrings.zig");
|
||||
pub const SharedDeps = @import("SharedDeps.zig");
|
||||
pub const UnicodeTables = @import("UnicodeTables.zig");
|
||||
|
|
@ -28,10 +29,5 @@ pub const LipoStep = @import("LipoStep.zig");
|
|||
pub const MetallibStep = @import("MetallibStep.zig");
|
||||
pub const XCFrameworkStep = @import("XCFrameworkStep.zig");
|
||||
|
||||
// Shell completions
|
||||
pub const fish_completions = @import("fish_completions.zig").completions;
|
||||
pub const zsh_completions = @import("zsh_completions.zig").completions;
|
||||
pub const bash_completions = @import("bash_completions.zig").completions;
|
||||
|
||||
// Helpers
|
||||
pub const requireZig = @import("zig.zig").requireZig;
|
||||
|
|
|
|||
|
|
@ -38,9 +38,10 @@ pub const artifact = Artifact.detect();
|
|||
const config = BuildConfig.fromOptions();
|
||||
pub const exe_entrypoint = config.exe_entrypoint;
|
||||
pub const flatpak = options.flatpak;
|
||||
pub const snap = options.snap;
|
||||
pub const app_runtime: apprt.Runtime = config.app_runtime;
|
||||
pub const font_backend: font.Backend = config.font_backend;
|
||||
pub const renderer: rendererpkg.Impl = config.renderer;
|
||||
pub const renderer: rendererpkg.Backend = config.renderer;
|
||||
pub const i18n: bool = config.i18n;
|
||||
|
||||
/// The bundle ID for the app. This is used in many places and is currently
|
||||
|
|
|
|||
|
|
@ -48,7 +48,4 @@ pub const Wasm = if (!builtin.target.cpu.arch.isWasm()) struct {} else @import("
|
|||
|
||||
test {
|
||||
@import("std").testing.refAllDecls(@This());
|
||||
|
||||
// Vim syntax file, not used at runtime but we want to keep it tested.
|
||||
_ = @import("config/vim.zig");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ const ArenaAllocator = std.heap.ArenaAllocator;
|
|||
const global_state = &@import("../global.zig").state;
|
||||
const fontpkg = @import("../font/main.zig");
|
||||
const inputpkg = @import("../input.zig");
|
||||
const terminal = @import("../terminal/main.zig");
|
||||
const internal_os = @import("../os/main.zig");
|
||||
const cli = @import("../cli.zig");
|
||||
|
||||
|
|
@ -39,6 +38,16 @@ const RepeatableStringMap = @import("RepeatableStringMap.zig");
|
|||
pub const Path = @import("path.zig").Path;
|
||||
pub const RepeatablePath = @import("path.zig").RepeatablePath;
|
||||
|
||||
// We do this instead of importing all of terminal/main.zig to
|
||||
// limit the dependency graph. This is important because some things
|
||||
// like the `ghostty-build-data` binary depend on the Config but don't
|
||||
// want to include all the other stuff.
|
||||
const terminal = struct {
|
||||
const CursorStyle = @import("../terminal/cursor.zig").Style;
|
||||
const color = @import("../terminal/color.zig");
|
||||
const x11_color = @import("../terminal/x11_color.zig");
|
||||
};
|
||||
|
||||
const log = std.log.scoped(.config);
|
||||
|
||||
/// Used on Unixes for some defaults.
|
||||
|
|
@ -2710,7 +2719,7 @@ keybind: Keybinds = .{},
|
|||
///
|
||||
/// * `new-tab` - Create a new tab in the current window, or open
|
||||
/// a new window if none exist.
|
||||
/// * `new-window` - Create a new window unconditionally.
|
||||
/// * `window` - Create a new window unconditionally.
|
||||
///
|
||||
/// The default value is `new-tab`.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -6,8 +6,11 @@
|
|||
# {[path]s}
|
||||
#
|
||||
# The template does not set any default options, since Ghostty ships
|
||||
# with sensible defaults for all options. Users should only need to set
|
||||
# options that they want to change from the default.
|
||||
# with sensible defaults for all options.
|
||||
# Note that you should not paste the output of `ghostty +show-config
|
||||
# --default` into your config: some default options actually conflict with each other
|
||||
# when explicitly set in a configuration file. Instead, only set the
|
||||
# options you actually need.
|
||||
#
|
||||
# Run `ghostty +show-config --default --docs` to view a list of
|
||||
# all available config options and their default values.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
//! Fish completions.
|
||||
const std = @import("std");
|
||||
|
||||
const Config = @import("../config/Config.zig");
|
||||
|
|
@ -5,23 +6,23 @@ const Action = @import("../cli.zig").ghostty.Action;
|
|||
|
||||
/// A fish completions configuration that contains all the available commands
|
||||
/// and options.
|
||||
pub const completions = comptimeGenerateFishCompletions();
|
||||
pub const completions = comptimeGenerateCompletions();
|
||||
|
||||
fn comptimeGenerateFishCompletions() []const u8 {
|
||||
fn comptimeGenerateCompletions() []const u8 {
|
||||
comptime {
|
||||
@setEvalBranchQuota(50000);
|
||||
var counter = std.io.countingWriter(std.io.null_writer);
|
||||
try writeFishCompletions(&counter.writer());
|
||||
try writeCompletions(&counter.writer());
|
||||
|
||||
var buf: [counter.bytes_written]u8 = undefined;
|
||||
var stream = std.io.fixedBufferStream(&buf);
|
||||
try writeFishCompletions(stream.writer());
|
||||
try writeCompletions(stream.writer());
|
||||
const final = buf;
|
||||
return final[0..stream.getWritten().len];
|
||||
}
|
||||
}
|
||||
|
||||
fn writeFishCompletions(writer: anytype) !void {
|
||||
fn writeCompletions(writer: anytype) !void {
|
||||
{
|
||||
try writer.writeAll("set -l commands \"");
|
||||
var count: usize = 0;
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
const std = @import("std");
|
||||
const Config = @import("Config.zig");
|
||||
const Config = @import("../config/Config.zig");
|
||||
|
||||
const Template = struct {
|
||||
const header =
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
const std = @import("std");
|
||||
const Config = @import("Config.zig");
|
||||
const Config = @import("../config/Config.zig");
|
||||
|
||||
/// This is the associated Vim file as named by the variable.
|
||||
pub const syntax = comptimeGenSyntax();
|
||||
|
|
@ -2,13 +2,20 @@ const std = @import("std");
|
|||
const builtin = @import("builtin");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
/// Same as std.mem.copyForwards but prefers libc memmove if it is available
|
||||
/// because it is generally much faster.
|
||||
/// Same as std.mem.copyForwards/Backwards but prefers libc memmove if it is
|
||||
/// available because it is generally much faster.
|
||||
pub inline fn move(comptime T: type, dest: []T, source: []const T) void {
|
||||
if (builtin.link_libc) {
|
||||
_ = memmove(dest.ptr, source.ptr, source.len * @sizeOf(T));
|
||||
} else {
|
||||
std.mem.copyForwards(T, dest, source);
|
||||
// Depending on the ordering of the copy, we need to use the
|
||||
// proper call here. Unfortunately this function call is
|
||||
// too generic to know this at comptime.
|
||||
if (@intFromPtr(dest.ptr) <= @intFromPtr(source.ptr)) {
|
||||
std.mem.copyForwards(T, dest, source);
|
||||
} else {
|
||||
std.mem.copyBackwards(T, dest, source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,110 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub const Backend = enum {
|
||||
const WasmTarget = @import("../os/wasm/target.zig").Target;
|
||||
|
||||
/// FreeType for font rendering with no font discovery enabled.
|
||||
freetype,
|
||||
|
||||
/// Fontconfig for font discovery and FreeType for font rendering.
|
||||
fontconfig_freetype,
|
||||
|
||||
/// CoreText for font discovery, rendering, and shaping (macOS).
|
||||
coretext,
|
||||
|
||||
/// CoreText for font discovery, FreeType for rendering, and
|
||||
/// HarfBuzz for shaping (macOS).
|
||||
coretext_freetype,
|
||||
|
||||
/// CoreText for font discovery and rendering, HarfBuzz for shaping
|
||||
coretext_harfbuzz,
|
||||
|
||||
/// CoreText for font discovery and rendering, no shaping.
|
||||
coretext_noshape,
|
||||
|
||||
/// Use the browser font system and the Canvas API (wasm). This limits
|
||||
/// the available fonts to browser fonts (anything Canvas natively
|
||||
/// supports).
|
||||
web_canvas,
|
||||
|
||||
/// Returns the default backend for a build environment. This is
|
||||
/// meant to be called at comptime by the build.zig script. To get the
|
||||
/// backend look at build_options.
|
||||
pub fn default(
|
||||
target: std.Target,
|
||||
wasm_target: WasmTarget,
|
||||
) Backend {
|
||||
if (target.cpu.arch == .wasm32) {
|
||||
return switch (wasm_target) {
|
||||
.browser => .web_canvas,
|
||||
};
|
||||
}
|
||||
|
||||
// macOS also supports "coretext_freetype" but there is no scenario
|
||||
// that is the default. It is only used by people who want to
|
||||
// self-compile Ghostty and prefer the freetype aesthetic.
|
||||
return if (target.os.tag.isDarwin()) .coretext else .fontconfig_freetype;
|
||||
}
|
||||
|
||||
// All the functions below can be called at comptime or runtime to
|
||||
// determine if we have a certain dependency.
|
||||
|
||||
pub fn hasFreetype(self: Backend) bool {
|
||||
return switch (self) {
|
||||
.freetype,
|
||||
.fontconfig_freetype,
|
||||
.coretext_freetype,
|
||||
=> true,
|
||||
|
||||
.coretext,
|
||||
.coretext_harfbuzz,
|
||||
.coretext_noshape,
|
||||
.web_canvas,
|
||||
=> false,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn hasCoretext(self: Backend) bool {
|
||||
return switch (self) {
|
||||
.coretext,
|
||||
.coretext_freetype,
|
||||
.coretext_harfbuzz,
|
||||
.coretext_noshape,
|
||||
=> true,
|
||||
|
||||
.freetype,
|
||||
.fontconfig_freetype,
|
||||
.web_canvas,
|
||||
=> false,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn hasFontconfig(self: Backend) bool {
|
||||
return switch (self) {
|
||||
.fontconfig_freetype => true,
|
||||
|
||||
.freetype,
|
||||
.coretext,
|
||||
.coretext_freetype,
|
||||
.coretext_harfbuzz,
|
||||
.coretext_noshape,
|
||||
.web_canvas,
|
||||
=> false,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn hasHarfbuzz(self: Backend) bool {
|
||||
return switch (self) {
|
||||
.freetype,
|
||||
.fontconfig_freetype,
|
||||
.coretext_freetype,
|
||||
.coretext_harfbuzz,
|
||||
=> true,
|
||||
|
||||
.coretext,
|
||||
.coretext_noshape,
|
||||
.web_canvas,
|
||||
=> false,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
@ -5,6 +5,7 @@ const build_config = @import("../build_config.zig");
|
|||
const library = @import("library.zig");
|
||||
|
||||
pub const Atlas = @import("Atlas.zig");
|
||||
pub const Backend = @import("backend.zig").Backend;
|
||||
pub const discovery = @import("discovery.zig");
|
||||
pub const embedded = @import("embedded.zig");
|
||||
pub const face = @import("face.zig");
|
||||
|
|
@ -48,115 +49,6 @@ pub const options: struct {
|
|||
.backend = if (builtin.target.cpu.arch.isWasm()) .web_canvas else build_config.font_backend,
|
||||
};
|
||||
|
||||
pub const Backend = enum {
|
||||
const WasmTarget = @import("../os/wasm/target.zig").Target;
|
||||
|
||||
/// FreeType for font rendering with no font discovery enabled.
|
||||
freetype,
|
||||
|
||||
/// Fontconfig for font discovery and FreeType for font rendering.
|
||||
fontconfig_freetype,
|
||||
|
||||
/// CoreText for font discovery, rendering, and shaping (macOS).
|
||||
coretext,
|
||||
|
||||
/// CoreText for font discovery, FreeType for rendering, and
|
||||
/// HarfBuzz for shaping (macOS).
|
||||
coretext_freetype,
|
||||
|
||||
/// CoreText for font discovery and rendering, HarfBuzz for shaping
|
||||
coretext_harfbuzz,
|
||||
|
||||
/// CoreText for font discovery and rendering, no shaping.
|
||||
coretext_noshape,
|
||||
|
||||
/// Use the browser font system and the Canvas API (wasm). This limits
|
||||
/// the available fonts to browser fonts (anything Canvas natively
|
||||
/// supports).
|
||||
web_canvas,
|
||||
|
||||
/// Returns the default backend for a build environment. This is
|
||||
/// meant to be called at comptime by the build.zig script. To get the
|
||||
/// backend look at build_options.
|
||||
pub fn default(
|
||||
target: std.Target,
|
||||
wasm_target: WasmTarget,
|
||||
) Backend {
|
||||
if (target.cpu.arch == .wasm32) {
|
||||
return switch (wasm_target) {
|
||||
.browser => .web_canvas,
|
||||
};
|
||||
}
|
||||
|
||||
// macOS also supports "coretext_freetype" but there is no scenario
|
||||
// that is the default. It is only used by people who want to
|
||||
// self-compile Ghostty and prefer the freetype aesthetic.
|
||||
return if (target.os.tag.isDarwin()) .coretext else .fontconfig_freetype;
|
||||
}
|
||||
|
||||
// All the functions below can be called at comptime or runtime to
|
||||
// determine if we have a certain dependency.
|
||||
|
||||
pub fn hasFreetype(self: Backend) bool {
|
||||
return switch (self) {
|
||||
.freetype,
|
||||
.fontconfig_freetype,
|
||||
.coretext_freetype,
|
||||
=> true,
|
||||
|
||||
.coretext,
|
||||
.coretext_harfbuzz,
|
||||
.coretext_noshape,
|
||||
.web_canvas,
|
||||
=> false,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn hasCoretext(self: Backend) bool {
|
||||
return switch (self) {
|
||||
.coretext,
|
||||
.coretext_freetype,
|
||||
.coretext_harfbuzz,
|
||||
.coretext_noshape,
|
||||
=> true,
|
||||
|
||||
.freetype,
|
||||
.fontconfig_freetype,
|
||||
.web_canvas,
|
||||
=> false,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn hasFontconfig(self: Backend) bool {
|
||||
return switch (self) {
|
||||
.fontconfig_freetype => true,
|
||||
|
||||
.freetype,
|
||||
.coretext,
|
||||
.coretext_freetype,
|
||||
.coretext_harfbuzz,
|
||||
.coretext_noshape,
|
||||
.web_canvas,
|
||||
=> false,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn hasHarfbuzz(self: Backend) bool {
|
||||
return switch (self) {
|
||||
.freetype,
|
||||
.fontconfig_freetype,
|
||||
.coretext_freetype,
|
||||
.coretext_harfbuzz,
|
||||
=> true,
|
||||
|
||||
.coretext,
|
||||
.coretext_noshape,
|
||||
.web_canvas,
|
||||
=> false,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/// The styles that a family can take.
|
||||
pub const Style = enum(u3) {
|
||||
regular = 0,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
const std = @import("std");
|
||||
const Config = @import("config/Config.zig");
|
||||
const Action = @import("cli.zig").ghostty.Action;
|
||||
const Action = @import("cli/ghostty.zig").Action;
|
||||
const KeybindAction = @import("input/Binding.zig").Action;
|
||||
|
||||
pub fn main() !void {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
//! This is the public API of the ghostty-vt Zig module.
|
||||
//!
|
||||
//! WARNING: The API is not guaranteed to be stable.
|
||||
//!
|
||||
//! The functionality is extremely stable, since it is extracted
|
||||
//! directly from Ghostty which has been used in real world scenarios
|
||||
//! by thousands of users for years. However, the API itself (functions,
|
||||
//! types, etc.) may change without warning. We're working on stabilizing
|
||||
//! this in the future.
|
||||
|
||||
// The public API below reproduces a lot of terminal/main.zig but
|
||||
// is separate because (1) we need our root file to be in `src/`
|
||||
// so we can access other directories and (2) we may want to withhold
|
||||
// parts of `terminal` that are not ready for public consumption
|
||||
// or are too Ghostty-internal.
|
||||
const terminal = @import("terminal/main.zig");
|
||||
|
||||
pub const apc = terminal.apc;
|
||||
pub const dcs = terminal.dcs;
|
||||
pub const osc = terminal.osc;
|
||||
pub const point = terminal.point;
|
||||
pub const color = terminal.color;
|
||||
pub const device_status = terminal.device_status;
|
||||
pub const kitty = terminal.kitty;
|
||||
pub const modes = terminal.modes;
|
||||
pub const page = terminal.page;
|
||||
pub const parse_table = terminal.parse_table;
|
||||
pub const search = terminal.search;
|
||||
pub const size = terminal.size;
|
||||
pub const x11_color = terminal.x11_color;
|
||||
|
||||
pub const Charset = terminal.Charset;
|
||||
pub const CharsetSlot = terminal.Slots;
|
||||
pub const CharsetActiveSlot = terminal.ActiveSlot;
|
||||
pub const Cell = page.Cell;
|
||||
pub const Coordinate = point.Coordinate;
|
||||
pub const CSI = Parser.Action.CSI;
|
||||
pub const DCS = Parser.Action.DCS;
|
||||
pub const MouseShape = terminal.MouseShape;
|
||||
pub const Page = page.Page;
|
||||
pub const PageList = terminal.PageList;
|
||||
pub const Parser = terminal.Parser;
|
||||
pub const Pin = PageList.Pin;
|
||||
pub const Point = point.Point;
|
||||
pub const Screen = terminal.Screen;
|
||||
pub const ScreenType = Terminal.ScreenType;
|
||||
pub const Selection = terminal.Selection;
|
||||
pub const SizeReportStyle = terminal.SizeReportStyle;
|
||||
pub const StringMap = terminal.StringMap;
|
||||
pub const Style = terminal.Style;
|
||||
pub const Terminal = terminal.Terminal;
|
||||
pub const Stream = terminal.Stream;
|
||||
pub const Cursor = Screen.Cursor;
|
||||
pub const CursorStyle = Screen.CursorStyle;
|
||||
pub const CursorStyleReq = terminal.CursorStyle;
|
||||
pub const DeviceAttributeReq = terminal.DeviceAttributeReq;
|
||||
pub const Mode = modes.Mode;
|
||||
pub const ModePacked = modes.ModePacked;
|
||||
pub const ModifyKeyFormat = terminal.ModifyKeyFormat;
|
||||
pub const ProtectedMode = terminal.ProtectedMode;
|
||||
pub const StatusLineType = terminal.StatusLineType;
|
||||
pub const StatusDisplay = terminal.StatusDisplay;
|
||||
pub const EraseDisplay = terminal.EraseDisplay;
|
||||
pub const EraseLine = terminal.EraseLine;
|
||||
pub const TabClear = terminal.TabClear;
|
||||
pub const Attribute = terminal.Attribute;
|
||||
|
||||
test {
|
||||
_ = terminal;
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
//! This CLI is used to generate data that is used by the build process.
|
||||
//!
|
||||
//! We used to do this directly in our `build.zig` but the problem with
|
||||
//! that approach is that any changes to the dependencies of this data would
|
||||
//! force a rebuild of our build binary. If we're just doing something like
|
||||
//! running tests and not emitting any of the info below, then that is a
|
||||
//! complete waste.
|
||||
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const cli = @import("cli.zig");
|
||||
|
||||
pub const Action = enum {
|
||||
// Shell completions
|
||||
bash,
|
||||
fish,
|
||||
zsh,
|
||||
|
||||
// Editor syntax files
|
||||
sublime,
|
||||
@"vim-syntax",
|
||||
@"vim-ftdetect",
|
||||
@"vim-ftplugin",
|
||||
@"vim-compiler",
|
||||
|
||||
// Other
|
||||
terminfo,
|
||||
};
|
||||
|
||||
pub fn main() !void {
|
||||
const alloc = std.heap.c_allocator;
|
||||
const action_ = try cli.action.detectArgs(Action, alloc);
|
||||
const action = action_ orelse return error.NoAction;
|
||||
|
||||
// Our output always goes to stdout.
|
||||
const writer = std.io.getStdOut().writer();
|
||||
switch (action) {
|
||||
.bash => try writer.writeAll(@import("extra/bash.zig").completions),
|
||||
.fish => try writer.writeAll(@import("extra/fish.zig").completions),
|
||||
.zsh => try writer.writeAll(@import("extra/zsh.zig").completions),
|
||||
.sublime => try writer.writeAll(@import("extra/sublime.zig").syntax),
|
||||
.@"vim-syntax" => try writer.writeAll(@import("extra/vim.zig").syntax),
|
||||
.@"vim-ftdetect" => try writer.writeAll(@import("extra/vim.zig").ftdetect),
|
||||
.@"vim-ftplugin" => try writer.writeAll(@import("extra/vim.zig").ftplugin),
|
||||
.@"vim-compiler" => try writer.writeAll(@import("extra/vim.zig").compiler),
|
||||
.terminfo => try @import("terminfo/ghostty.zig").ghostty.encode(writer),
|
||||
}
|
||||
}
|
||||
|
|
@ -191,4 +191,13 @@ test {
|
|||
_ = @import("simd/main.zig");
|
||||
_ = @import("synthetic/main.zig");
|
||||
_ = @import("unicode/main.zig");
|
||||
_ = @import("unicode/props_ziglyph.zig");
|
||||
_ = @import("unicode/symbols_ziglyph.zig");
|
||||
|
||||
// Extra
|
||||
_ = @import("extra/bash.zig");
|
||||
_ = @import("extra/fish.zig");
|
||||
_ = @import("extra/sublime.zig");
|
||||
_ = @import("extra/vim.zig");
|
||||
_ = @import("extra/zsh.zig");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ const std = @import("std");
|
|||
const builtin = @import("builtin");
|
||||
const testing = std.testing;
|
||||
const Dir = std.fs.Dir;
|
||||
const internal_os = @import("main.zig");
|
||||
const allocTmpDir = @import("file.zig").allocTmpDir;
|
||||
const freeTmpDir = @import("file.zig").freeTmpDir;
|
||||
|
||||
const log = std.log.scoped(.tempdir);
|
||||
|
||||
|
|
@ -31,8 +32,8 @@ pub fn init() !TempDir {
|
|||
|
||||
const dir = dir: {
|
||||
const cwd = std.fs.cwd();
|
||||
const tmp_dir = internal_os.allocTmpDir(std.heap.page_allocator) orelse break :dir cwd;
|
||||
defer internal_os.freeTmpDir(std.heap.page_allocator, tmp_dir);
|
||||
const tmp_dir = allocTmpDir(std.heap.page_allocator) orelse break :dir cwd;
|
||||
defer freeTmpDir(std.heap.page_allocator, tmp_dir);
|
||||
break :dir try cwd.openDir(tmp_dir, .{});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -95,6 +95,21 @@ pub fn getenv(alloc: Allocator, key: []const u8) Error!?GetEnvResult {
|
|||
};
|
||||
}
|
||||
|
||||
/// Gets the value of an environment variable. Returns null if not found or the
|
||||
/// value is empty. This will allocate on Windows but not on other platforms.
|
||||
/// The returned value should have deinit called to do the proper cleanup no
|
||||
/// matter what platform you are on.
|
||||
pub fn getenvNotEmpty(alloc: Allocator, key: []const u8) !?GetEnvResult {
|
||||
const result_ = try getenv(alloc, key);
|
||||
if (result_) |result| {
|
||||
if (result.value.len == 0) {
|
||||
result.deinit(alloc);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return result_;
|
||||
}
|
||||
|
||||
pub fn setenv(key: [:0]const u8, value: [:0]const u8) c_int {
|
||||
return switch (builtin.os.tag) {
|
||||
.windows => c._putenv_s(key.ptr, value.ptr),
|
||||
|
|
|
|||
|
|
@ -1,59 +1,10 @@
|
|||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const build_config = @import("../build_config.zig");
|
||||
const locales = @import("i18n_locales.zig");
|
||||
|
||||
const log = std.log.scoped(.i18n);
|
||||
|
||||
/// Supported locales for the application. This must be kept up to date
|
||||
/// with the translations available in the `po/` directory; this is used
|
||||
/// by our build process as well runtime libghostty APIs.
|
||||
///
|
||||
/// The order also matters. For incomplete locale information (i.e. only
|
||||
/// a language code available), the first match is used. For example, if
|
||||
/// we know the user requested `zh` but has no script code, then we'd pick
|
||||
/// the first locale that matches `zh`.
|
||||
///
|
||||
/// For ordering, we prefer:
|
||||
///
|
||||
/// 1. The most common locales first, since there are places in the code
|
||||
/// where we do linear searches for a locale and we want to minimize
|
||||
/// the number of iterations for the common case.
|
||||
///
|
||||
/// 2. Alphabetical for otherwise equally common locales.
|
||||
///
|
||||
/// 3. Most preferred locale for a language without a country code.
|
||||
///
|
||||
/// Note for "most common" locales, this is subjective and based on
|
||||
/// the perceived userbase of Ghostty, which may not be representative
|
||||
/// of general populations or global language distribution. Also note
|
||||
/// that ordering may be weird when we first merge a new locale since
|
||||
/// we don't have a good way to determine this. We can always reorder
|
||||
/// with some data.
|
||||
pub const locales = [_][:0]const u8{
|
||||
"zh_CN.UTF-8",
|
||||
"de_DE.UTF-8",
|
||||
"fr_FR.UTF-8",
|
||||
"ja_JP.UTF-8",
|
||||
"nl_NL.UTF-8",
|
||||
"nb_NO.UTF-8",
|
||||
"ru_RU.UTF-8",
|
||||
"uk_UA.UTF-8",
|
||||
"pl_PL.UTF-8",
|
||||
"ko_KR.UTF-8",
|
||||
"mk_MK.UTF-8",
|
||||
"tr_TR.UTF-8",
|
||||
"id_ID.UTF-8",
|
||||
"es_BO.UTF-8",
|
||||
"es_AR.UTF-8",
|
||||
"pt_BR.UTF-8",
|
||||
"ca_ES.UTF-8",
|
||||
"it_IT.UTF-8",
|
||||
"bg_BG.UTF-8",
|
||||
"ga_IE.UTF-8",
|
||||
"hu_HU.UTF-8",
|
||||
"he_IL.UTF-8",
|
||||
};
|
||||
|
||||
/// Set for faster membership lookup of locales.
|
||||
pub const locales_map = map: {
|
||||
var kvs: [locales.len]struct { []const u8 } = undefined;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
// NOTE: This is in a separate file because our build depends on it and
|
||||
// we want to minimize the transitive dependencies of the build binary
|
||||
// itself.
|
||||
|
||||
/// Supported locales for the application. This must be kept up to date
|
||||
/// with the translations available in the `po/` directory; this is used
|
||||
/// by our build process as well runtime libghostty APIs.
|
||||
///
|
||||
/// The order also matters. For incomplete locale information (i.e. only
|
||||
/// a language code available), the first match is used. For example, if
|
||||
/// we know the user requested `zh` but has no script code, then we'd pick
|
||||
/// the first locale that matches `zh`.
|
||||
///
|
||||
/// For ordering, we prefer:
|
||||
///
|
||||
/// 1. The most common locales first, since there are places in the code
|
||||
/// where we do linear searches for a locale and we want to minimize
|
||||
/// the number of iterations for the common case.
|
||||
///
|
||||
/// 2. Alphabetical for otherwise equally common locales.
|
||||
///
|
||||
/// 3. Most preferred locale for a language without a country code.
|
||||
///
|
||||
/// Note for "most common" locales, this is subjective and based on
|
||||
/// the perceived userbase of Ghostty, which may not be representative
|
||||
/// of general populations or global language distribution. Also note
|
||||
/// that ordering may be weird when we first merge a new locale since
|
||||
/// we don't have a good way to determine this. We can always reorder
|
||||
/// with some data.
|
||||
pub const locales = [_][:0]const u8{
|
||||
"zh_CN.UTF-8",
|
||||
"de_DE.UTF-8",
|
||||
"fr_FR.UTF-8",
|
||||
"ja_JP.UTF-8",
|
||||
"nl_NL.UTF-8",
|
||||
"nb_NO.UTF-8",
|
||||
"ru_RU.UTF-8",
|
||||
"uk_UA.UTF-8",
|
||||
"pl_PL.UTF-8",
|
||||
"ko_KR.UTF-8",
|
||||
"mk_MK.UTF-8",
|
||||
"tr_TR.UTF-8",
|
||||
"id_ID.UTF-8",
|
||||
"es_BO.UTF-8",
|
||||
"es_AR.UTF-8",
|
||||
"pt_BR.UTF-8",
|
||||
"ca_ES.UTF-8",
|
||||
"it_IT.UTF-8",
|
||||
"bg_BG.UTF-8",
|
||||
"ga_IE.UTF-8",
|
||||
"hu_HU.UTF-8",
|
||||
"he_IL.UTF-8",
|
||||
"zh_TW.UTF-8",
|
||||
};
|
||||
|
|
@ -23,6 +23,7 @@ pub const args = @import("args.zig");
|
|||
pub const cgroup = @import("cgroup.zig");
|
||||
pub const hostname = @import("hostname.zig");
|
||||
pub const i18n = @import("i18n.zig");
|
||||
pub const path = @import("path.zig");
|
||||
pub const passwd = @import("passwd.zig");
|
||||
pub const xdg = @import("xdg.zig");
|
||||
pub const windows = @import("windows.zig");
|
||||
|
|
@ -65,6 +66,7 @@ pub const getKernelInfo = kernel_info.getKernelInfo;
|
|||
|
||||
test {
|
||||
_ = i18n;
|
||||
_ = path;
|
||||
|
||||
if (comptime builtin.os.tag == .linux) {
|
||||
_ = kernel_info;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,89 @@
|
|||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const testing = std.testing;
|
||||
|
||||
/// Search for "cmd" in the PATH and return the absolute path. This will
|
||||
/// always allocate if there is a non-null result. The caller must free the
|
||||
/// resulting value.
|
||||
pub fn expand(alloc: Allocator, cmd: []const u8) !?[]u8 {
|
||||
// If the command already contains a slash, then we return it as-is
|
||||
// because it is assumed to be absolute or relative.
|
||||
if (std.mem.indexOfScalar(u8, cmd, '/') != null) {
|
||||
return try alloc.dupe(u8, cmd);
|
||||
}
|
||||
|
||||
const PATH = switch (builtin.os.tag) {
|
||||
.windows => blk: {
|
||||
const win_path = std.process.getenvW(std.unicode.utf8ToUtf16LeStringLiteral("PATH")) orelse return null;
|
||||
const path = try std.unicode.utf16LeToUtf8Alloc(alloc, win_path);
|
||||
break :blk path;
|
||||
},
|
||||
else => std.posix.getenvZ("PATH") orelse return null,
|
||||
};
|
||||
defer if (builtin.os.tag == .windows) alloc.free(PATH);
|
||||
|
||||
var path_buf: [std.fs.max_path_bytes]u8 = undefined;
|
||||
var it = std.mem.tokenizeScalar(u8, PATH, std.fs.path.delimiter);
|
||||
var seen_eacces = false;
|
||||
while (it.next()) |search_path| {
|
||||
// We need enough space in our path buffer to store this
|
||||
const path_len = search_path.len + cmd.len + 1;
|
||||
if (path_buf.len < path_len) return error.PathTooLong;
|
||||
|
||||
// Copy in the full path
|
||||
@memcpy(path_buf[0..search_path.len], search_path);
|
||||
path_buf[search_path.len] = std.fs.path.sep;
|
||||
@memcpy(path_buf[search_path.len + 1 ..][0..cmd.len], cmd);
|
||||
path_buf[path_len] = 0;
|
||||
const full_path = path_buf[0..path_len :0];
|
||||
|
||||
// Stat it
|
||||
const f = std.fs.cwd().openFile(
|
||||
full_path,
|
||||
.{},
|
||||
) catch |err| switch (err) {
|
||||
error.FileNotFound => continue,
|
||||
error.AccessDenied => {
|
||||
// Accumulate this and return it later so we can try other
|
||||
// paths that we have access to.
|
||||
seen_eacces = true;
|
||||
continue;
|
||||
},
|
||||
else => return err,
|
||||
};
|
||||
defer f.close();
|
||||
const stat = try f.stat();
|
||||
if (stat.kind != .directory and isExecutable(stat.mode)) {
|
||||
return try alloc.dupe(u8, full_path);
|
||||
}
|
||||
}
|
||||
|
||||
if (seen_eacces) return error.AccessDenied;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
fn isExecutable(mode: std.fs.File.Mode) bool {
|
||||
if (builtin.os.tag == .windows) return true;
|
||||
return mode & 0o0111 != 0;
|
||||
}
|
||||
|
||||
// `uname -n` is the *nix equivalent of `hostname.exe` on Windows
|
||||
test "expand: hostname" {
|
||||
const executable = if (builtin.os.tag == .windows) "hostname.exe" else "uname";
|
||||
const path = (try expand(testing.allocator, executable)).?;
|
||||
defer testing.allocator.free(path);
|
||||
try testing.expect(path.len > executable.len);
|
||||
}
|
||||
|
||||
test "expand: does not exist" {
|
||||
const path = try expand(testing.allocator, "thisreallyprobablydoesntexist123");
|
||||
try testing.expect(path == null);
|
||||
}
|
||||
|
||||
test "expand: slash" {
|
||||
const path = (try expand(testing.allocator, "foo/env")).?;
|
||||
defer testing.allocator.free(path);
|
||||
try testing.expect(path.len == 7);
|
||||
}
|
||||
154
src/os/xdg.zig
154
src/os/xdg.zig
|
|
@ -7,6 +7,7 @@ const assert = std.debug.assert;
|
|||
const Allocator = std.mem.Allocator;
|
||||
const posix = std.posix;
|
||||
const homedir = @import("homedir.zig");
|
||||
const env_os = @import("env.zig");
|
||||
|
||||
pub const Options = struct {
|
||||
/// Subdirectories to join to the base. This avoids extra allocations
|
||||
|
|
@ -70,36 +71,22 @@ fn dir(
|
|||
// First check the env var. On Windows we have to allocate so this tracks
|
||||
// both whether we have the env var and whether we own it.
|
||||
// on Windows we treat `LOCALAPPDATA` as a fallback for `XDG_CONFIG_HOME`
|
||||
const env_, const owned = switch (builtin.os.tag) {
|
||||
else => .{ posix.getenv(internal_opts.env), false },
|
||||
.windows => windows: {
|
||||
if (std.process.getEnvVarOwned(alloc, internal_opts.env)) |env| {
|
||||
break :windows .{ env, true };
|
||||
} else |err| switch (err) {
|
||||
error.EnvironmentVariableNotFound => {
|
||||
if (std.process.getEnvVarOwned(alloc, internal_opts.windows_env)) |env| {
|
||||
break :windows .{ env, true };
|
||||
} else |err2| switch (err2) {
|
||||
error.EnvironmentVariableNotFound => break :windows .{ null, false },
|
||||
else => return err,
|
||||
}
|
||||
},
|
||||
else => return err,
|
||||
}
|
||||
},
|
||||
const env_ = try env_os.getenvNotEmpty(alloc, internal_opts.env) orelse switch (builtin.os.tag) {
|
||||
else => null,
|
||||
.windows => try env_os.getenvNotEmpty(alloc, internal_opts.windows_env),
|
||||
};
|
||||
defer if (owned) if (env_) |v| alloc.free(v);
|
||||
defer if (env_) |env| env.deinit(alloc);
|
||||
|
||||
if (env_) |env| {
|
||||
// If we have a subdir, then we use the env as-is to avoid a copy.
|
||||
if (opts.subdir) |subdir| {
|
||||
return try std.fs.path.join(alloc, &[_][]const u8{
|
||||
env,
|
||||
env.value,
|
||||
subdir,
|
||||
});
|
||||
}
|
||||
|
||||
return try alloc.dupe(u8, env);
|
||||
return try alloc.dupe(u8, env.value);
|
||||
}
|
||||
|
||||
// Get our home dir
|
||||
|
|
@ -169,6 +156,133 @@ test "cache directory paths" {
|
|||
}
|
||||
}
|
||||
|
||||
test "fallback when xdg env empty" {
|
||||
if (builtin.os.tag == .windows) return error.SkipZigTest;
|
||||
|
||||
const alloc = std.testing.allocator;
|
||||
|
||||
const saved_home = home: {
|
||||
const home = std.posix.getenv("HOME") orelse break :home null;
|
||||
break :home try alloc.dupeZ(u8, home);
|
||||
};
|
||||
defer env: {
|
||||
const home = saved_home orelse {
|
||||
_ = env_os.unsetenv("HOME");
|
||||
break :env;
|
||||
};
|
||||
_ = env_os.setenv("HOME", home);
|
||||
std.testing.allocator.free(home);
|
||||
}
|
||||
const temp_home = "/tmp/ghostty-test-home";
|
||||
_ = env_os.setenv("HOME", temp_home);
|
||||
|
||||
const DirCase = struct {
|
||||
name: [:0]const u8,
|
||||
func: fn (Allocator, Options) anyerror![]u8,
|
||||
default_subdir: []const u8,
|
||||
};
|
||||
|
||||
const cases = [_]DirCase{
|
||||
.{ .name = "XDG_CONFIG_HOME", .func = config, .default_subdir = ".config" },
|
||||
.{ .name = "XDG_CACHE_HOME", .func = cache, .default_subdir = ".cache" },
|
||||
.{ .name = "XDG_STATE_HOME", .func = state, .default_subdir = ".local/state" },
|
||||
};
|
||||
|
||||
inline for (cases) |case| {
|
||||
// Save and restore each environment variable
|
||||
const saved_env = blk: {
|
||||
const value = std.posix.getenv(case.name) orelse break :blk null;
|
||||
break :blk try alloc.dupeZ(u8, value);
|
||||
};
|
||||
defer env: {
|
||||
const value = saved_env orelse {
|
||||
_ = env_os.unsetenv(case.name);
|
||||
break :env;
|
||||
};
|
||||
_ = env_os.setenv(case.name, value);
|
||||
alloc.free(value);
|
||||
}
|
||||
|
||||
const expected = try std.fs.path.join(alloc, &[_][]const u8{
|
||||
temp_home,
|
||||
case.default_subdir,
|
||||
});
|
||||
defer alloc.free(expected);
|
||||
|
||||
// Test with empty string - should fallback to home
|
||||
_ = env_os.setenv(case.name, "");
|
||||
const actual = try case.func(alloc, .{});
|
||||
defer alloc.free(actual);
|
||||
|
||||
try std.testing.expectEqualStrings(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
test "fallback when xdg env empty and subdir" {
|
||||
if (builtin.os.tag == .windows) return error.SkipZigTest;
|
||||
|
||||
const env = @import("env.zig");
|
||||
const alloc = std.testing.allocator;
|
||||
|
||||
const saved_home = home: {
|
||||
const home = std.posix.getenv("HOME") orelse break :home null;
|
||||
break :home try alloc.dupeZ(u8, home);
|
||||
};
|
||||
defer env: {
|
||||
const home = saved_home orelse {
|
||||
_ = env.unsetenv("HOME");
|
||||
break :env;
|
||||
};
|
||||
_ = env.setenv("HOME", home);
|
||||
std.testing.allocator.free(home);
|
||||
}
|
||||
|
||||
const temp_home = "/tmp/ghostty-test-home";
|
||||
_ = env.setenv("HOME", temp_home);
|
||||
|
||||
const DirCase = struct {
|
||||
name: [:0]const u8,
|
||||
func: fn (Allocator, Options) anyerror![]u8,
|
||||
default_subdir: []const u8,
|
||||
};
|
||||
|
||||
const cases = [_]DirCase{
|
||||
.{ .name = "XDG_CONFIG_HOME", .func = config, .default_subdir = ".config" },
|
||||
.{ .name = "XDG_CACHE_HOME", .func = cache, .default_subdir = ".cache" },
|
||||
.{ .name = "XDG_STATE_HOME", .func = state, .default_subdir = ".local/state" },
|
||||
};
|
||||
|
||||
inline for (cases) |case| {
|
||||
// Save and restore each environment variable
|
||||
const saved_env = blk: {
|
||||
const value = std.posix.getenv(case.name) orelse break :blk null;
|
||||
break :blk try alloc.dupeZ(u8, value);
|
||||
};
|
||||
defer env: {
|
||||
const value = saved_env orelse {
|
||||
_ = env.unsetenv(case.name);
|
||||
break :env;
|
||||
};
|
||||
_ = env.setenv(case.name, value);
|
||||
alloc.free(value);
|
||||
}
|
||||
|
||||
const expected = try std.fs.path.join(alloc, &[_][]const u8{
|
||||
temp_home,
|
||||
case.default_subdir,
|
||||
"ghostty",
|
||||
});
|
||||
defer alloc.free(expected);
|
||||
|
||||
// Test with empty string - should fallback to home
|
||||
_ = env.setenv(case.name, "");
|
||||
const actual = try case.func(alloc, .{ .subdir = "ghostty" });
|
||||
defer alloc.free(actual);
|
||||
|
||||
try std.testing.expectEqualStrings(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
test parseTerminalExec {
|
||||
const testing = std.testing;
|
||||
|
||||
|
|
|
|||
|
|
@ -10,12 +10,12 @@
|
|||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const build_config = @import("build_config.zig");
|
||||
const WasmTarget = @import("os/wasm/target.zig").Target;
|
||||
|
||||
const cursor = @import("renderer/cursor.zig");
|
||||
const message = @import("renderer/message.zig");
|
||||
const size = @import("renderer/size.zig");
|
||||
pub const shadertoy = @import("renderer/shadertoy.zig");
|
||||
pub const Backend = @import("renderer/backend.zig").Backend;
|
||||
pub const GenericRenderer = @import("renderer/generic.zig").Renderer;
|
||||
pub const Metal = @import("renderer/Metal.zig");
|
||||
pub const OpenGL = @import("renderer/OpenGL.zig");
|
||||
|
|
@ -33,27 +33,6 @@ pub const GridSize = size.GridSize;
|
|||
pub const Padding = size.Padding;
|
||||
pub const cursorStyle = cursor.style;
|
||||
|
||||
/// Possible implementations, used for build options.
|
||||
pub const Impl = enum {
|
||||
opengl,
|
||||
metal,
|
||||
webgl,
|
||||
|
||||
pub fn default(
|
||||
target: std.Target,
|
||||
wasm_target: WasmTarget,
|
||||
) Impl {
|
||||
if (target.cpu.arch == .wasm32) {
|
||||
return switch (wasm_target) {
|
||||
.browser => .webgl,
|
||||
};
|
||||
}
|
||||
|
||||
if (target.os.tag.isDarwin()) return .metal;
|
||||
return .opengl;
|
||||
}
|
||||
};
|
||||
|
||||
/// The implementation to use for the renderer. This is comptime chosen
|
||||
/// so that every build has exactly one renderer implementation.
|
||||
pub const Renderer = switch (build_config.renderer) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
const std = @import("std");
|
||||
const WasmTarget = @import("../os/wasm/target.zig").Target;
|
||||
|
||||
/// Possible implementations, used for build options.
|
||||
pub const Backend = enum {
|
||||
opengl,
|
||||
metal,
|
||||
webgl,
|
||||
|
||||
pub fn default(
|
||||
target: std.Target,
|
||||
wasm_target: WasmTarget,
|
||||
) Backend {
|
||||
if (target.cpu.arch == .wasm32) {
|
||||
return switch (wasm_target) {
|
||||
.browser => .webgl,
|
||||
};
|
||||
}
|
||||
|
||||
if (target.os.tag.isDarwin()) return .metal;
|
||||
return .opengl;
|
||||
}
|
||||
};
|
||||
|
|
@ -6,7 +6,7 @@ const terminal = @import("../terminal/main.zig");
|
|||
const renderer = @import("../renderer.zig");
|
||||
const shaderpkg = renderer.Renderer.API.shaders;
|
||||
const ArrayListCollection = @import("../datastruct/array_list_collection.zig").ArrayListCollection;
|
||||
const symbols = @import("../unicode/symbols.zig").table;
|
||||
const symbols = @import("../unicode/symbols_table.zig").table;
|
||||
|
||||
/// The possible cell content keys that exist.
|
||||
pub const Key = enum {
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ vec4 contrasted_color(float min_ratio, vec4 fg, vec4 bg) {
|
|||
if (white_ratio > black_ratio) {
|
||||
return vec4(1.0);
|
||||
} else {
|
||||
return vec4(0.0);
|
||||
return vec4(0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue