mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
Merge branch 'main' into ssh-integration
This commit is contained in:
4
.github/pinact.yml
vendored
Normal file
4
.github/pinact.yml
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
version: 3
|
||||||
|
ignore_actions:
|
||||||
|
- name: "DeterminateSystems/nix-installer-action"
|
||||||
|
ref: "main"
|
2
.github/workflows/clean-artifacts.yml
vendored
2
.github/workflows/clean-artifacts.yml
vendored
@ -10,7 +10,7 @@ jobs:
|
|||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
steps:
|
steps:
|
||||||
- name: Remove old artifacts
|
- name: Remove old artifacts
|
||||||
uses: c-hive/gha-remove-artifacts@v1
|
uses: c-hive/gha-remove-artifacts@44fc7acaf1b3d0987da0e8d4707a989d80e9554b # v1.4.0
|
||||||
with:
|
with:
|
||||||
age: "1 week"
|
age: "1 week"
|
||||||
skip-tags: true
|
skip-tags: true
|
||||||
|
4
.github/workflows/milestone.yml
vendored
4
.github/workflows/milestone.yml
vendored
@ -15,7 +15,7 @@ jobs:
|
|||||||
name: Milestone Update
|
name: Milestone Update
|
||||||
steps:
|
steps:
|
||||||
- name: Set Milestone for PR
|
- name: Set Milestone for PR
|
||||||
uses: hustcer/milestone-action@v2
|
uses: hustcer/milestone-action@09bdc6fda0f43a4df28cda5815cc47df74cfdba7 # v2.8
|
||||||
if: github.event.pull_request.merged == true
|
if: github.event.pull_request.merged == true
|
||||||
with:
|
with:
|
||||||
action: bind-pr # `bind-pr` is the default action
|
action: bind-pr # `bind-pr` is the default action
|
||||||
@ -24,7 +24,7 @@ jobs:
|
|||||||
|
|
||||||
# Bind milestone to closed issue that has a merged PR fix
|
# Bind milestone to closed issue that has a merged PR fix
|
||||||
- name: Set Milestone for Issue
|
- name: Set Milestone for Issue
|
||||||
uses: hustcer/milestone-action@v2
|
uses: hustcer/milestone-action@09bdc6fda0f43a4df28cda5815cc47df74cfdba7 # v2.8
|
||||||
if: github.event.issue.state == 'closed'
|
if: github.event.issue.state == 'closed'
|
||||||
with:
|
with:
|
||||||
action: bind-issue
|
action: bind-issue
|
||||||
|
8
.github/workflows/nix.yml
vendored
8
.github/workflows/nix.yml
vendored
@ -34,18 +34,18 @@ jobs:
|
|||||||
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- name: Setup Cache
|
- name: Setup Cache
|
||||||
uses: namespacelabs/nscloud-cache-action@v1.2.8
|
uses: namespacelabs/nscloud-cache-action@449c929cd5138e6607e7e78458e88cc476e76f89 # v1.2.8
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
/nix
|
/nix
|
||||||
/zig
|
/zig
|
||||||
- name: Setup Nix
|
- name: Setup Nix
|
||||||
uses: cachix/install-nix-action@v31
|
uses: cachix/install-nix-action@f0fe604f8a612776892427721526b4c7cfb23aba # v31.4.1
|
||||||
with:
|
with:
|
||||||
nix_path: nixpkgs=channel:nixos-unstable
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
- uses: cachix/cachix-action@v16
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
with:
|
with:
|
||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
|
2
.github/workflows/publish-tag.yml
vendored
2
.github/workflows/publish-tag.yml
vendored
@ -64,7 +64,7 @@ jobs:
|
|||||||
mkdir blob
|
mkdir blob
|
||||||
mv appcast.xml blob/appcast.xml
|
mv appcast.xml blob/appcast.xml
|
||||||
- name: Upload Appcast to R2
|
- name: Upload Appcast to R2
|
||||||
uses: ryand56/r2-upload-action@latest
|
uses: ryand56/r2-upload-action@b801a390acbdeb034c5e684ff5e1361c06639e7c # v1
|
||||||
with:
|
with:
|
||||||
r2-account-id: ${{ secrets.CF_R2_RELEASE_ACCOUNT_ID }}
|
r2-account-id: ${{ secrets.CF_R2_RELEASE_ACCOUNT_ID }}
|
||||||
r2-access-key-id: ${{ secrets.CF_R2_RELEASE_AWS_KEY }}
|
r2-access-key-id: ${{ secrets.CF_R2_RELEASE_AWS_KEY }}
|
||||||
|
20
.github/workflows/release-pr.yml
vendored
20
.github/workflows/release-pr.yml
vendored
@ -8,7 +8,7 @@ jobs:
|
|||||||
runs-on: namespace-profile-ghostty-sm
|
runs-on: namespace-profile-ghostty-sm
|
||||||
needs: [build-macos-debug]
|
needs: [build-macos-debug]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Install sentry-cli
|
- name: Install sentry-cli
|
||||||
run: |
|
run: |
|
||||||
@ -29,7 +29,7 @@ jobs:
|
|||||||
runs-on: namespace-profile-ghostty-sm
|
runs-on: namespace-profile-ghostty-sm
|
||||||
needs: [build-macos]
|
needs: [build-macos]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Install sentry-cli
|
- name: Install sentry-cli
|
||||||
run: |
|
run: |
|
||||||
@ -51,16 +51,16 @@ jobs:
|
|||||||
timeout-minutes: 90
|
timeout-minutes: 90
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
# Important so that build number generation works
|
# Important so that build number generation works
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||||
- uses: cachix/install-nix-action@v31
|
- uses: cachix/install-nix-action@f0fe604f8a612776892427721526b4c7cfb23aba # v31.4.1
|
||||||
with:
|
with:
|
||||||
nix_path: nixpkgs=channel:nixos-unstable
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
- uses: cachix/cachix-action@v16
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
with:
|
with:
|
||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
@ -189,7 +189,7 @@ jobs:
|
|||||||
cp ghostty-macos-universal.zip blob/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal.zip
|
cp ghostty-macos-universal.zip blob/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal.zip
|
||||||
cp ghostty-macos-universal-dsym.zip blob/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal-dsym.zip
|
cp ghostty-macos-universal-dsym.zip blob/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal-dsym.zip
|
||||||
- name: Upload to R2
|
- name: Upload to R2
|
||||||
uses: ryand56/r2-upload-action@latest
|
uses: ryand56/r2-upload-action@b801a390acbdeb034c5e684ff5e1361c06639e7c # v1.4
|
||||||
with:
|
with:
|
||||||
r2-account-id: ${{ secrets.CF_R2_PR_ACCOUNT_ID }}
|
r2-account-id: ${{ secrets.CF_R2_PR_ACCOUNT_ID }}
|
||||||
r2-access-key-id: ${{ secrets.CF_R2_PR_AWS_KEY }}
|
r2-access-key-id: ${{ secrets.CF_R2_PR_AWS_KEY }}
|
||||||
@ -203,16 +203,16 @@ jobs:
|
|||||||
timeout-minutes: 90
|
timeout-minutes: 90
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
# Important so that build number generation works
|
# Important so that build number generation works
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||||
- uses: cachix/install-nix-action@v31
|
- uses: cachix/install-nix-action@f0fe604f8a612776892427721526b4c7cfb23aba # v31.4.1
|
||||||
with:
|
with:
|
||||||
nix_path: nixpkgs=channel:nixos-unstable
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
- uses: cachix/cachix-action@v16
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
with:
|
with:
|
||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
@ -341,7 +341,7 @@ jobs:
|
|||||||
cp ghostty-macos-universal-debug.zip blob/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal-debug.zip
|
cp ghostty-macos-universal-debug.zip blob/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal-debug.zip
|
||||||
cp ghostty-macos-universal-debug-dsym.zip blob/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal-debug-dsym.zip
|
cp ghostty-macos-universal-debug-dsym.zip blob/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal-debug-dsym.zip
|
||||||
- name: Upload to R2
|
- name: Upload to R2
|
||||||
uses: ryand56/r2-upload-action@latest
|
uses: ryand56/r2-upload-action@b801a390acbdeb034c5e684ff5e1361c06639e7c # v1.4
|
||||||
with:
|
with:
|
||||||
r2-account-id: ${{ secrets.CF_R2_PR_ACCOUNT_ID }}
|
r2-account-id: ${{ secrets.CF_R2_PR_ACCOUNT_ID }}
|
||||||
r2-access-key-id: ${{ secrets.CF_R2_PR_AWS_KEY }}
|
r2-access-key-id: ${{ secrets.CF_R2_PR_AWS_KEY }}
|
||||||
|
36
.github/workflows/release-tag.yml
vendored
36
.github/workflows/release-tag.yml
vendored
@ -56,7 +56,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
# Important so that build number generation works
|
# Important so that build number generation works
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
@ -80,20 +80,20 @@ jobs:
|
|||||||
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
||||||
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Setup Cache
|
- name: Setup Cache
|
||||||
uses: namespacelabs/nscloud-cache-action@v1.2.8
|
uses: namespacelabs/nscloud-cache-action@449c929cd5138e6607e7e78458e88cc476e76f89 # v1.2.8
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
/nix
|
/nix
|
||||||
/zig
|
/zig
|
||||||
|
|
||||||
- uses: cachix/install-nix-action@v31
|
- uses: cachix/install-nix-action@f0fe604f8a612776892427721526b4c7cfb23aba # v31.4.1
|
||||||
with:
|
with:
|
||||||
nix_path: nixpkgs=channel:nixos-unstable
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
|
|
||||||
- uses: cachix/cachix-action@v16
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
with:
|
with:
|
||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
@ -111,7 +111,7 @@ jobs:
|
|||||||
nix develop -c minisign -S -m "ghostty-source.tar.gz" -s minisign.key < minisign.password
|
nix develop -c minisign -S -m "ghostty-source.tar.gz" -s minisign.key < minisign.password
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
with:
|
with:
|
||||||
name: source-tarball
|
name: source-tarball
|
||||||
path: |-
|
path: |-
|
||||||
@ -128,12 +128,12 @@ jobs:
|
|||||||
GHOSTTY_COMMIT: ${{ needs.setup.outputs.commit }}
|
GHOSTTY_COMMIT: ${{ needs.setup.outputs.commit }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- uses: cachix/install-nix-action@v31
|
- uses: cachix/install-nix-action@f0fe604f8a612776892427721526b4c7cfb23aba # v31.4.1
|
||||||
with:
|
with:
|
||||||
nix_path: nixpkgs=channel:nixos-unstable
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
- uses: cachix/cachix-action@v16
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
with:
|
with:
|
||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
@ -260,7 +260,7 @@ jobs:
|
|||||||
zip -9 -r --symlinks ../../../ghostty-macos-universal-dsym.zip Ghostty.app.dSYM/
|
zip -9 -r --symlinks ../../../ghostty-macos-universal-dsym.zip Ghostty.app.dSYM/
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
with:
|
with:
|
||||||
name: macos
|
name: macos
|
||||||
path: |-
|
path: |-
|
||||||
@ -277,7 +277,7 @@ jobs:
|
|||||||
curl -sL https://sentry.io/get-cli/ | bash
|
curl -sL https://sentry.io/get-cli/ | bash
|
||||||
|
|
||||||
- name: Download macOS Artifacts
|
- name: Download macOS Artifacts
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||||
with:
|
with:
|
||||||
name: macos
|
name: macos
|
||||||
|
|
||||||
@ -297,10 +297,10 @@ jobs:
|
|||||||
GHOSTTY_COMMIT_LONG: ${{ needs.setup.outputs.commit_long }}
|
GHOSTTY_COMMIT_LONG: ${{ needs.setup.outputs.commit_long }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Download macOS Artifacts
|
- name: Download macOS Artifacts
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||||
with:
|
with:
|
||||||
name: macos
|
name: macos
|
||||||
|
|
||||||
@ -331,7 +331,7 @@ jobs:
|
|||||||
mv appcast_new.xml appcast.xml
|
mv appcast_new.xml appcast.xml
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
with:
|
with:
|
||||||
name: sparkle
|
name: sparkle
|
||||||
path: |-
|
path: |-
|
||||||
@ -348,17 +348,17 @@ jobs:
|
|||||||
GHOSTTY_VERSION: ${{ needs.setup.outputs.version }}
|
GHOSTTY_VERSION: ${{ needs.setup.outputs.version }}
|
||||||
steps:
|
steps:
|
||||||
- name: Download macOS Artifacts
|
- name: Download macOS Artifacts
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||||
with:
|
with:
|
||||||
name: macos
|
name: macos
|
||||||
|
|
||||||
- name: Download Sparkle Artifacts
|
- name: Download Sparkle Artifacts
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||||
with:
|
with:
|
||||||
name: sparkle
|
name: sparkle
|
||||||
|
|
||||||
- name: Download Source Tarball Artifacts
|
- name: Download Source Tarball Artifacts
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||||
with:
|
with:
|
||||||
name: source-tarball
|
name: source-tarball
|
||||||
|
|
||||||
@ -378,7 +378,7 @@ jobs:
|
|||||||
mv Ghostty.dmg blob/${GHOSTTY_VERSION}/Ghostty.dmg
|
mv Ghostty.dmg blob/${GHOSTTY_VERSION}/Ghostty.dmg
|
||||||
mv appcast.xml blob/${GHOSTTY_VERSION}/appcast-staged.xml
|
mv appcast.xml blob/${GHOSTTY_VERSION}/appcast-staged.xml
|
||||||
- name: Upload to R2
|
- name: Upload to R2
|
||||||
uses: ryand56/r2-upload-action@latest
|
uses: ryand56/r2-upload-action@b801a390acbdeb034c5e684ff5e1361c06639e7c # v1.4
|
||||||
with:
|
with:
|
||||||
r2-account-id: ${{ secrets.CF_R2_RELEASE_ACCOUNT_ID }}
|
r2-account-id: ${{ secrets.CF_R2_RELEASE_ACCOUNT_ID }}
|
||||||
r2-access-key-id: ${{ secrets.CF_R2_RELEASE_AWS_KEY }}
|
r2-access-key-id: ${{ secrets.CF_R2_RELEASE_AWS_KEY }}
|
||||||
|
50
.github/workflows/release-tip.yml
vendored
50
.github/workflows/release-tip.yml
vendored
@ -19,7 +19,7 @@ jobs:
|
|||||||
runs-on: namespace-profile-ghostty-sm
|
runs-on: namespace-profile-ghostty-sm
|
||||||
needs: [build-macos]
|
needs: [build-macos]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- name: Tip Tag
|
- name: Tip Tag
|
||||||
run: |
|
run: |
|
||||||
git config user.name "github-actions[bot]"
|
git config user.name "github-actions[bot]"
|
||||||
@ -31,7 +31,7 @@ jobs:
|
|||||||
runs-on: namespace-profile-ghostty-sm
|
runs-on: namespace-profile-ghostty-sm
|
||||||
needs: [build-macos-debug-slow]
|
needs: [build-macos-debug-slow]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Install sentry-cli
|
- name: Install sentry-cli
|
||||||
run: |
|
run: |
|
||||||
@ -52,7 +52,7 @@ jobs:
|
|||||||
runs-on: namespace-profile-ghostty-sm
|
runs-on: namespace-profile-ghostty-sm
|
||||||
needs: [build-macos-debug-fast]
|
needs: [build-macos-debug-fast]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Install sentry-cli
|
- name: Install sentry-cli
|
||||||
run: |
|
run: |
|
||||||
@ -73,7 +73,7 @@ jobs:
|
|||||||
runs-on: namespace-profile-ghostty-sm
|
runs-on: namespace-profile-ghostty-sm
|
||||||
needs: [build-macos]
|
needs: [build-macos]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Install sentry-cli
|
- name: Install sentry-cli
|
||||||
run: |
|
run: |
|
||||||
@ -105,17 +105,17 @@ jobs:
|
|||||||
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
||||||
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- name: Setup Cache
|
- name: Setup Cache
|
||||||
uses: namespacelabs/nscloud-cache-action@v1.2.8
|
uses: namespacelabs/nscloud-cache-action@449c929cd5138e6607e7e78458e88cc476e76f89 # v1.2.8
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
/nix
|
/nix
|
||||||
/zig
|
/zig
|
||||||
- uses: cachix/install-nix-action@v31
|
- uses: cachix/install-nix-action@f0fe604f8a612776892427721526b4c7cfb23aba # v31.4.1
|
||||||
with:
|
with:
|
||||||
nix_path: nixpkgs=channel:nixos-unstable
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
- uses: cachix/cachix-action@v16
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
with:
|
with:
|
||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
@ -132,7 +132,7 @@ jobs:
|
|||||||
nix develop -c minisign -S -m ghostty-source.tar.gz -s minisign.key < minisign.password
|
nix develop -c minisign -S -m ghostty-source.tar.gz -s minisign.key < minisign.password
|
||||||
|
|
||||||
- name: Update Release
|
- name: Update Release
|
||||||
uses: softprops/action-gh-release@v2.3.2
|
uses: softprops/action-gh-release@72f2c25fcb47643c292f7107632f7a47c1df5cd8 # v2.3.2
|
||||||
with:
|
with:
|
||||||
name: 'Ghostty Tip ("Nightly")'
|
name: 'Ghostty Tip ("Nightly")'
|
||||||
prerelease: true
|
prerelease: true
|
||||||
@ -158,16 +158,16 @@ jobs:
|
|||||||
timeout-minutes: 90
|
timeout-minutes: 90
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
# Important so that build number generation works
|
# Important so that build number generation works
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||||
- uses: cachix/install-nix-action@v31
|
- uses: cachix/install-nix-action@f0fe604f8a612776892427721526b4c7cfb23aba # v31.4.1
|
||||||
with:
|
with:
|
||||||
nix_path: nixpkgs=channel:nixos-unstable
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
- uses: cachix/cachix-action@v16
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
with:
|
with:
|
||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
@ -299,7 +299,7 @@ jobs:
|
|||||||
|
|
||||||
# Update Release
|
# Update Release
|
||||||
- name: Release
|
- name: Release
|
||||||
uses: softprops/action-gh-release@v2.3.2
|
uses: softprops/action-gh-release@72f2c25fcb47643c292f7107632f7a47c1df5cd8 # v2.3.2
|
||||||
with:
|
with:
|
||||||
name: 'Ghostty Tip ("Nightly")'
|
name: 'Ghostty Tip ("Nightly")'
|
||||||
prerelease: true
|
prerelease: true
|
||||||
@ -331,7 +331,7 @@ jobs:
|
|||||||
cp Ghostty.dmg blob/${GHOSTTY_COMMIT_LONG}/Ghostty.dmg
|
cp Ghostty.dmg blob/${GHOSTTY_COMMIT_LONG}/Ghostty.dmg
|
||||||
|
|
||||||
- name: Upload to R2
|
- name: Upload to R2
|
||||||
uses: ryand56/r2-upload-action@latest
|
uses: ryand56/r2-upload-action@b801a390acbdeb034c5e684ff5e1361c06639e7c # v1.4
|
||||||
with:
|
with:
|
||||||
r2-account-id: ${{ secrets.CF_R2_TIP_ACCOUNT_ID }}
|
r2-account-id: ${{ secrets.CF_R2_TIP_ACCOUNT_ID }}
|
||||||
r2-access-key-id: ${{ secrets.CF_R2_TIP_AWS_KEY }}
|
r2-access-key-id: ${{ secrets.CF_R2_TIP_AWS_KEY }}
|
||||||
@ -349,7 +349,7 @@ jobs:
|
|||||||
cp appcast_new.xml blob/appcast.xml
|
cp appcast_new.xml blob/appcast.xml
|
||||||
|
|
||||||
- name: Upload Appcast to R2
|
- name: Upload Appcast to R2
|
||||||
uses: ryand56/r2-upload-action@latest
|
uses: ryand56/r2-upload-action@b801a390acbdeb034c5e684ff5e1361c06639e7c # v1.4
|
||||||
with:
|
with:
|
||||||
r2-account-id: ${{ secrets.CF_R2_TIP_ACCOUNT_ID }}
|
r2-account-id: ${{ secrets.CF_R2_TIP_ACCOUNT_ID }}
|
||||||
r2-access-key-id: ${{ secrets.CF_R2_TIP_AWS_KEY }}
|
r2-access-key-id: ${{ secrets.CF_R2_TIP_AWS_KEY }}
|
||||||
@ -373,16 +373,16 @@ jobs:
|
|||||||
timeout-minutes: 90
|
timeout-minutes: 90
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
# Important so that build number generation works
|
# Important so that build number generation works
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||||
- uses: cachix/install-nix-action@v31
|
- uses: cachix/install-nix-action@f0fe604f8a612776892427721526b4c7cfb23aba # v31.4.1
|
||||||
with:
|
with:
|
||||||
nix_path: nixpkgs=channel:nixos-unstable
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
- uses: cachix/cachix-action@v16
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
with:
|
with:
|
||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
@ -507,7 +507,7 @@ jobs:
|
|||||||
|
|
||||||
# Update Release
|
# Update Release
|
||||||
- name: Release
|
- name: Release
|
||||||
uses: softprops/action-gh-release@v2.3.2
|
uses: softprops/action-gh-release@72f2c25fcb47643c292f7107632f7a47c1df5cd8 # v2.3.2
|
||||||
with:
|
with:
|
||||||
name: 'Ghostty Tip ("Nightly")'
|
name: 'Ghostty Tip ("Nightly")'
|
||||||
prerelease: true
|
prerelease: true
|
||||||
@ -524,7 +524,7 @@ jobs:
|
|||||||
cp ghostty-macos-universal-debug-slow.zip blob/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal-debug-slow.zip
|
cp ghostty-macos-universal-debug-slow.zip blob/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal-debug-slow.zip
|
||||||
cp ghostty-macos-universal-debug-slow-dsym.zip blob/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal-debug-slow-dsym.zip
|
cp ghostty-macos-universal-debug-slow-dsym.zip blob/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal-debug-slow-dsym.zip
|
||||||
- name: Upload to R2
|
- name: Upload to R2
|
||||||
uses: ryand56/r2-upload-action@latest
|
uses: ryand56/r2-upload-action@b801a390acbdeb034c5e684ff5e1361c06639e7c # v1.4
|
||||||
with:
|
with:
|
||||||
r2-account-id: ${{ secrets.CF_R2_TIP_ACCOUNT_ID }}
|
r2-account-id: ${{ secrets.CF_R2_TIP_ACCOUNT_ID }}
|
||||||
r2-access-key-id: ${{ secrets.CF_R2_TIP_AWS_KEY }}
|
r2-access-key-id: ${{ secrets.CF_R2_TIP_AWS_KEY }}
|
||||||
@ -548,16 +548,16 @@ jobs:
|
|||||||
timeout-minutes: 90
|
timeout-minutes: 90
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
# Important so that build number generation works
|
# Important so that build number generation works
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||||
- uses: cachix/install-nix-action@v31
|
- uses: cachix/install-nix-action@f0fe604f8a612776892427721526b4c7cfb23aba # v31.4.1
|
||||||
with:
|
with:
|
||||||
nix_path: nixpkgs=channel:nixos-unstable
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
- uses: cachix/cachix-action@v16
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
with:
|
with:
|
||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
@ -682,7 +682,7 @@ jobs:
|
|||||||
|
|
||||||
# Update Release
|
# Update Release
|
||||||
- name: Release
|
- name: Release
|
||||||
uses: softprops/action-gh-release@v2.3.2
|
uses: softprops/action-gh-release@72f2c25fcb47643c292f7107632f7a47c1df5cd8 # v2.3.2
|
||||||
with:
|
with:
|
||||||
name: 'Ghostty Tip ("Nightly")'
|
name: 'Ghostty Tip ("Nightly")'
|
||||||
prerelease: true
|
prerelease: true
|
||||||
@ -699,7 +699,7 @@ jobs:
|
|||||||
cp ghostty-macos-universal-debug-fast.zip blob/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal-debug-fast.zip
|
cp ghostty-macos-universal-debug-fast.zip blob/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal-debug-fast.zip
|
||||||
cp ghostty-macos-universal-debug-fast-dsym.zip blob/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal-debug-fast-dsym.zip
|
cp ghostty-macos-universal-debug-fast-dsym.zip blob/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal-debug-fast-dsym.zip
|
||||||
- name: Upload to R2
|
- name: Upload to R2
|
||||||
uses: ryand56/r2-upload-action@latest
|
uses: ryand56/r2-upload-action@b801a390acbdeb034c5e684ff5e1361c06639e7c # v1.4
|
||||||
with:
|
with:
|
||||||
r2-account-id: ${{ secrets.CF_R2_TIP_ACCOUNT_ID }}
|
r2-account-id: ${{ secrets.CF_R2_TIP_ACCOUNT_ID }}
|
||||||
r2-access-key-id: ${{ secrets.CF_R2_TIP_AWS_KEY }}
|
r2-access-key-id: ${{ secrets.CF_R2_TIP_AWS_KEY }}
|
||||||
|
209
.github/workflows/test.yml
vendored
209
.github/workflows/test.yml
vendored
@ -27,6 +27,7 @@ jobs:
|
|||||||
- test-gtk
|
- test-gtk
|
||||||
- test-sentry-linux
|
- test-sentry-linux
|
||||||
- test-macos
|
- test-macos
|
||||||
|
- pinact
|
||||||
- prettier
|
- prettier
|
||||||
- alejandra
|
- alejandra
|
||||||
- typos
|
- typos
|
||||||
@ -64,20 +65,20 @@ jobs:
|
|||||||
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Setup Cache
|
- name: Setup Cache
|
||||||
uses: namespacelabs/nscloud-cache-action@v1.2.8
|
uses: namespacelabs/nscloud-cache-action@449c929cd5138e6607e7e78458e88cc476e76f89 # v1.2.8
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
/nix
|
/nix
|
||||||
/zig
|
/zig
|
||||||
|
|
||||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||||
- uses: cachix/install-nix-action@v31
|
- uses: cachix/install-nix-action@f0fe604f8a612776892427721526b4c7cfb23aba # v31.4.1
|
||||||
with:
|
with:
|
||||||
nix_path: nixpkgs=channel:nixos-unstable
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
- uses: cachix/cachix-action@v16
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
with:
|
with:
|
||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
@ -95,20 +96,20 @@ jobs:
|
|||||||
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Setup Cache
|
- name: Setup Cache
|
||||||
uses: namespacelabs/nscloud-cache-action@v1.2.8
|
uses: namespacelabs/nscloud-cache-action@449c929cd5138e6607e7e78458e88cc476e76f89 # v1.2.8
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
/nix
|
/nix
|
||||||
/zig
|
/zig
|
||||||
|
|
||||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||||
- uses: cachix/install-nix-action@v31
|
- uses: cachix/install-nix-action@f0fe604f8a612776892427721526b4c7cfb23aba # v31.4.1
|
||||||
with:
|
with:
|
||||||
nix_path: nixpkgs=channel:nixos-unstable
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
- uses: cachix/cachix-action@v16
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
with:
|
with:
|
||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
@ -131,20 +132,20 @@ jobs:
|
|||||||
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Setup Cache
|
- name: Setup Cache
|
||||||
uses: namespacelabs/nscloud-cache-action@v1.2.8
|
uses: namespacelabs/nscloud-cache-action@449c929cd5138e6607e7e78458e88cc476e76f89 # v1.2.8
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
/nix
|
/nix
|
||||||
/zig
|
/zig
|
||||||
|
|
||||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||||
- uses: cachix/install-nix-action@v31
|
- uses: cachix/install-nix-action@f0fe604f8a612776892427721526b4c7cfb23aba # v31.4.1
|
||||||
with:
|
with:
|
||||||
nix_path: nixpkgs=channel:nixos-unstable
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
- uses: cachix/cachix-action@v16
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
with:
|
with:
|
||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
@ -160,20 +161,20 @@ jobs:
|
|||||||
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Setup Cache
|
- name: Setup Cache
|
||||||
uses: namespacelabs/nscloud-cache-action@v1.2.8
|
uses: namespacelabs/nscloud-cache-action@449c929cd5138e6607e7e78458e88cc476e76f89 # v1.2.8
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
/nix
|
/nix
|
||||||
/zig
|
/zig
|
||||||
|
|
||||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||||
- uses: cachix/install-nix-action@v31
|
- uses: cachix/install-nix-action@f0fe604f8a612776892427721526b4c7cfb23aba # v31.4.1
|
||||||
with:
|
with:
|
||||||
nix_path: nixpkgs=channel:nixos-unstable
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
- uses: cachix/cachix-action@v16
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
with:
|
with:
|
||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
@ -193,20 +194,20 @@ jobs:
|
|||||||
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Setup Cache
|
- name: Setup Cache
|
||||||
uses: namespacelabs/nscloud-cache-action@v1.2.8
|
uses: namespacelabs/nscloud-cache-action@449c929cd5138e6607e7e78458e88cc476e76f89 # v1.2.8
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
/nix
|
/nix
|
||||||
/zig
|
/zig
|
||||||
|
|
||||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||||
- uses: cachix/install-nix-action@v31
|
- uses: cachix/install-nix-action@f0fe604f8a612776892427721526b4c7cfb23aba # v31.4.1
|
||||||
with:
|
with:
|
||||||
nix_path: nixpkgs=channel:nixos-unstable
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
- uses: cachix/cachix-action@v16
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
with:
|
with:
|
||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
@ -237,20 +238,20 @@ jobs:
|
|||||||
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Setup Cache
|
- name: Setup Cache
|
||||||
uses: namespacelabs/nscloud-cache-action@v1.2.8
|
uses: namespacelabs/nscloud-cache-action@449c929cd5138e6607e7e78458e88cc476e76f89 # v1.2.8
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
/nix
|
/nix
|
||||||
/zig
|
/zig
|
||||||
|
|
||||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||||
- uses: cachix/install-nix-action@v31
|
- uses: cachix/install-nix-action@f0fe604f8a612776892427721526b4c7cfb23aba # v31.4.1
|
||||||
with:
|
with:
|
||||||
nix_path: nixpkgs=channel:nixos-unstable
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
- uses: cachix/cachix-action@v16
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
with:
|
with:
|
||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
@ -262,7 +263,7 @@ jobs:
|
|||||||
cp zig-out/dist/*.tar.gz ghostty-source.tar.gz
|
cp zig-out/dist/*.tar.gz ghostty-source.tar.gz
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
with:
|
with:
|
||||||
name: source-tarball
|
name: source-tarball
|
||||||
path: |-
|
path: |-
|
||||||
@ -273,13 +274,13 @@ jobs:
|
|||||||
needs: test
|
needs: test
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||||
- uses: cachix/install-nix-action@v31
|
- uses: cachix/install-nix-action@f0fe604f8a612776892427721526b4c7cfb23aba # v31.4.1
|
||||||
with:
|
with:
|
||||||
nix_path: nixpkgs=channel:nixos-unstable
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
- uses: cachix/cachix-action@v16
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
with:
|
with:
|
||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
@ -313,13 +314,13 @@ jobs:
|
|||||||
needs: test
|
needs: test
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
# TODO(tahoe): https://github.com/NixOS/nix/issues/13342
|
# TODO(tahoe): https://github.com/NixOS/nix/issues/13342
|
||||||
- uses: DeterminateSystems/nix-installer-action@main
|
- uses: DeterminateSystems/nix-installer-action@main
|
||||||
with:
|
with:
|
||||||
determinate: true
|
determinate: true
|
||||||
- uses: cachix/cachix-action@v16
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
with:
|
with:
|
||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
@ -353,13 +354,13 @@ jobs:
|
|||||||
needs: test
|
needs: test
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||||
- uses: cachix/install-nix-action@v31
|
- uses: cachix/install-nix-action@f0fe604f8a612776892427721526b4c7cfb23aba # v31.4.1
|
||||||
with:
|
with:
|
||||||
nix_path: nixpkgs=channel:nixos-unstable
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
- uses: cachix/cachix-action@v16
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
with:
|
with:
|
||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
@ -400,7 +401,7 @@ jobs:
|
|||||||
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||||
steps:
|
steps:
|
||||||
- name: Download Source Tarball Artifacts
|
- name: Download Source Tarball Artifacts
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||||
with:
|
with:
|
||||||
name: source-tarball
|
name: source-tarball
|
||||||
- name: Extract tarball
|
- name: Extract tarball
|
||||||
@ -408,7 +409,7 @@ jobs:
|
|||||||
mkdir dist
|
mkdir dist
|
||||||
tar --verbose --extract --strip-components 1 --directory dist --file ghostty-source.tar.gz
|
tar --verbose --extract --strip-components 1 --directory dist --file ghostty-source.tar.gz
|
||||||
- name: Setup Cache
|
- name: Setup Cache
|
||||||
uses: namespacelabs/nscloud-cache-action@v1.2.8
|
uses: namespacelabs/nscloud-cache-action@449c929cd5138e6607e7e78458e88cc476e76f89 # v1.2.8
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
/nix
|
/nix
|
||||||
@ -420,7 +421,7 @@ jobs:
|
|||||||
_LXD_SNAP_DEVCGROUP_CONFIG="/var/lib/snapd/cgroup/snap.lxd.device"
|
_LXD_SNAP_DEVCGROUP_CONFIG="/var/lib/snapd/cgroup/snap.lxd.device"
|
||||||
sudo mkdir -p /var/lib/snapd/cgroup
|
sudo mkdir -p /var/lib/snapd/cgroup
|
||||||
echo 'self-managed=true' | sudo tee "${_LXD_SNAP_DEVCGROUP_CONFIG}"
|
echo 'self-managed=true' | sudo tee "${_LXD_SNAP_DEVCGROUP_CONFIG}"
|
||||||
- uses: snapcore/action-build@v1
|
- uses: snapcore/action-build@3bdaa03e1ba6bf59a65f84a751d943d549a54e79 # v1.3.0
|
||||||
with:
|
with:
|
||||||
path: dist
|
path: dist
|
||||||
|
|
||||||
@ -431,7 +432,7 @@ jobs:
|
|||||||
needs: test
|
needs: test
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
# This could be from a script if we wanted to but inlining here for now
|
# This could be from a script if we wanted to but inlining here for now
|
||||||
# in one place.
|
# in one place.
|
||||||
@ -500,20 +501,20 @@ jobs:
|
|||||||
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Setup Cache
|
- name: Setup Cache
|
||||||
uses: namespacelabs/nscloud-cache-action@v1.2.8
|
uses: namespacelabs/nscloud-cache-action@449c929cd5138e6607e7e78458e88cc476e76f89 # v1.2.8
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
/nix
|
/nix
|
||||||
/zig
|
/zig
|
||||||
|
|
||||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||||
- uses: cachix/install-nix-action@v31
|
- uses: cachix/install-nix-action@f0fe604f8a612776892427721526b4c7cfb23aba # v31.4.1
|
||||||
with:
|
with:
|
||||||
nix_path: nixpkgs=channel:nixos-unstable
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
- uses: cachix/cachix-action@v16
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
with:
|
with:
|
||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
@ -542,20 +543,20 @@ jobs:
|
|||||||
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Setup Cache
|
- name: Setup Cache
|
||||||
uses: namespacelabs/nscloud-cache-action@v1.2.8
|
uses: namespacelabs/nscloud-cache-action@449c929cd5138e6607e7e78458e88cc476e76f89 # v1.2.8
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
/nix
|
/nix
|
||||||
/zig
|
/zig
|
||||||
|
|
||||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||||
- uses: cachix/install-nix-action@v31
|
- uses: cachix/install-nix-action@f0fe604f8a612776892427721526b4c7cfb23aba # v31.4.1
|
||||||
with:
|
with:
|
||||||
nix_path: nixpkgs=channel:nixos-unstable
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
- uses: cachix/cachix-action@v16
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
with:
|
with:
|
||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
@ -581,20 +582,20 @@ jobs:
|
|||||||
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Setup Cache
|
- name: Setup Cache
|
||||||
uses: namespacelabs/nscloud-cache-action@v1.2.8
|
uses: namespacelabs/nscloud-cache-action@449c929cd5138e6607e7e78458e88cc476e76f89 # v1.2.8
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
/nix
|
/nix
|
||||||
/zig
|
/zig
|
||||||
|
|
||||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||||
- uses: cachix/install-nix-action@v31
|
- uses: cachix/install-nix-action@f0fe604f8a612776892427721526b4c7cfb23aba # v31.4.1
|
||||||
with:
|
with:
|
||||||
nix_path: nixpkgs=channel:nixos-unstable
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
- uses: cachix/cachix-action@v16
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
with:
|
with:
|
||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
@ -608,13 +609,13 @@ jobs:
|
|||||||
needs: test
|
needs: test
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||||
- uses: cachix/install-nix-action@v31
|
- uses: cachix/install-nix-action@f0fe604f8a612776892427721526b4c7cfb23aba # v31.4.1
|
||||||
with:
|
with:
|
||||||
nix_path: nixpkgs=channel:nixos-unstable
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
- uses: cachix/cachix-action@v16
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
with:
|
with:
|
||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
@ -637,17 +638,17 @@ jobs:
|
|||||||
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
||||||
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4 # Check out repo so we can lint it
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- name: Setup Cache
|
- name: Setup Cache
|
||||||
uses: namespacelabs/nscloud-cache-action@v1.2.8
|
uses: namespacelabs/nscloud-cache-action@449c929cd5138e6607e7e78458e88cc476e76f89 # v1.2.8
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
/nix
|
/nix
|
||||||
/zig
|
/zig
|
||||||
- uses: cachix/install-nix-action@v31
|
- uses: cachix/install-nix-action@f0fe604f8a612776892427721526b4c7cfb23aba # v31.4.1
|
||||||
with:
|
with:
|
||||||
nix_path: nixpkgs=channel:nixos-unstable
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
- uses: cachix/cachix-action@v16
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
with:
|
with:
|
||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
@ -656,6 +657,34 @@ jobs:
|
|||||||
- name: zig fmt
|
- name: zig fmt
|
||||||
run: nix develop -c zig fmt --check .
|
run: nix develop -c zig fmt --check .
|
||||||
|
|
||||||
|
pinact:
|
||||||
|
name: "GitHub Actions Pins"
|
||||||
|
if: github.repository == 'ghostty-org/ghostty'
|
||||||
|
runs-on: namespace-profile-ghostty-xsm
|
||||||
|
timeout-minutes: 60
|
||||||
|
env:
|
||||||
|
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
||||||
|
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
- name: Setup Cache
|
||||||
|
uses: namespacelabs/nscloud-cache-action@449c929cd5138e6607e7e78458e88cc476e76f89 # v1.2.8
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
/nix
|
||||||
|
/zig
|
||||||
|
- uses: cachix/install-nix-action@f0fe604f8a612776892427721526b4c7cfb23aba # v31.4.1
|
||||||
|
with:
|
||||||
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
|
with:
|
||||||
|
name: ghostty
|
||||||
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
|
skipPush: true
|
||||||
|
useDaemon: false # sometimes fails on short jobs
|
||||||
|
- name: pinact check
|
||||||
|
run: nix develop -c pinact run --check
|
||||||
|
|
||||||
prettier:
|
prettier:
|
||||||
if: github.repository == 'ghostty-org/ghostty'
|
if: github.repository == 'ghostty-org/ghostty'
|
||||||
runs-on: namespace-profile-ghostty-xsm
|
runs-on: namespace-profile-ghostty-xsm
|
||||||
@ -664,17 +693,17 @@ jobs:
|
|||||||
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
||||||
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4 # Check out repo so we can lint it
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- name: Setup Cache
|
- name: Setup Cache
|
||||||
uses: namespacelabs/nscloud-cache-action@v1.2.8
|
uses: namespacelabs/nscloud-cache-action@449c929cd5138e6607e7e78458e88cc476e76f89 # v1.2.8
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
/nix
|
/nix
|
||||||
/zig
|
/zig
|
||||||
- uses: cachix/install-nix-action@v31
|
- uses: cachix/install-nix-action@f0fe604f8a612776892427721526b4c7cfb23aba # v31.4.1
|
||||||
with:
|
with:
|
||||||
nix_path: nixpkgs=channel:nixos-unstable
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
- uses: cachix/cachix-action@v16
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
with:
|
with:
|
||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
@ -691,17 +720,17 @@ jobs:
|
|||||||
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
||||||
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4 # Check out repo so we can lint it
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- name: Setup Cache
|
- name: Setup Cache
|
||||||
uses: namespacelabs/nscloud-cache-action@v1.2.8
|
uses: namespacelabs/nscloud-cache-action@449c929cd5138e6607e7e78458e88cc476e76f89 # v1.2.8
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
/nix
|
/nix
|
||||||
/zig
|
/zig
|
||||||
- uses: cachix/install-nix-action@v31
|
- uses: cachix/install-nix-action@f0fe604f8a612776892427721526b4c7cfb23aba # v31.4.1
|
||||||
with:
|
with:
|
||||||
nix_path: nixpkgs=channel:nixos-unstable
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
- uses: cachix/cachix-action@v16
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
with:
|
with:
|
||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
@ -718,17 +747,17 @@ jobs:
|
|||||||
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
||||||
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4 # Check out repo so we can lint it
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- name: Setup Cache
|
- name: Setup Cache
|
||||||
uses: namespacelabs/nscloud-cache-action@v1.2.8
|
uses: namespacelabs/nscloud-cache-action@449c929cd5138e6607e7e78458e88cc476e76f89 # v1.2.8
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
/nix
|
/nix
|
||||||
/zig
|
/zig
|
||||||
- uses: cachix/install-nix-action@v31
|
- uses: cachix/install-nix-action@f0fe604f8a612776892427721526b4c7cfb23aba # v31.4.1
|
||||||
with:
|
with:
|
||||||
nix_path: nixpkgs=channel:nixos-unstable
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
- uses: cachix/cachix-action@v16
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
with:
|
with:
|
||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
@ -745,17 +774,17 @@ jobs:
|
|||||||
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
||||||
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4 # Check out repo so we can lint it
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- name: Setup Cache
|
- name: Setup Cache
|
||||||
uses: namespacelabs/nscloud-cache-action@v1.2.8
|
uses: namespacelabs/nscloud-cache-action@449c929cd5138e6607e7e78458e88cc476e76f89 # v1.2.8
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
/nix
|
/nix
|
||||||
/zig
|
/zig
|
||||||
- uses: cachix/install-nix-action@v31
|
- uses: cachix/install-nix-action@f0fe604f8a612776892427721526b4c7cfb23aba # v31.4.1
|
||||||
with:
|
with:
|
||||||
nix_path: nixpkgs=channel:nixos-unstable
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
- uses: cachix/cachix-action@v16
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
with:
|
with:
|
||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
@ -772,17 +801,17 @@ jobs:
|
|||||||
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
||||||
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4 # Check out repo so we can lint it
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- name: Setup Cache
|
- name: Setup Cache
|
||||||
uses: namespacelabs/nscloud-cache-action@v1.2.8
|
uses: namespacelabs/nscloud-cache-action@449c929cd5138e6607e7e78458e88cc476e76f89 # v1.2.8
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
/nix
|
/nix
|
||||||
/zig
|
/zig
|
||||||
- uses: cachix/install-nix-action@v31
|
- uses: cachix/install-nix-action@f0fe604f8a612776892427721526b4c7cfb23aba # v31.4.1
|
||||||
with:
|
with:
|
||||||
nix_path: nixpkgs=channel:nixos-unstable
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
- uses: cachix/cachix-action@v16
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
with:
|
with:
|
||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
@ -806,20 +835,20 @@ jobs:
|
|||||||
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Setup Cache
|
- name: Setup Cache
|
||||||
uses: namespacelabs/nscloud-cache-action@v1.2.8
|
uses: namespacelabs/nscloud-cache-action@449c929cd5138e6607e7e78458e88cc476e76f89 # v1.2.8
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
/nix
|
/nix
|
||||||
/zig
|
/zig
|
||||||
|
|
||||||
# Install Nix and use that to run our tests so our environment matches exactly.
|
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||||
- uses: cachix/install-nix-action@v31
|
- uses: cachix/install-nix-action@f0fe604f8a612776892427721526b4c7cfb23aba # v31.4.1
|
||||||
with:
|
with:
|
||||||
nix_path: nixpkgs=channel:nixos-unstable
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
- uses: cachix/cachix-action@v16
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
with:
|
with:
|
||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
@ -834,13 +863,13 @@ jobs:
|
|||||||
needs: [test, build-dist]
|
needs: [test, build-dist]
|
||||||
steps:
|
steps:
|
||||||
- name: Install and configure Namespace CLI
|
- name: Install and configure Namespace CLI
|
||||||
uses: namespacelabs/nscloud-setup@v0
|
uses: namespacelabs/nscloud-setup@d1c625762f7c926a54bd39252efff0705fd11c64 # v0.0.10
|
||||||
|
|
||||||
- name: Configure Namespace powered Buildx
|
- name: Configure Namespace powered Buildx
|
||||||
uses: namespacelabs/nscloud-setup-buildx-action@v0
|
uses: namespacelabs/nscloud-setup-buildx-action@01628ae51ea5d6b0c90109c7dccbf511953aff29 # v0.0.18
|
||||||
|
|
||||||
- name: Download Source Tarball Artifacts
|
- name: Download Source Tarball Artifacts
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||||
with:
|
with:
|
||||||
name: source-tarball
|
name: source-tarball
|
||||||
|
|
||||||
@ -850,7 +879,7 @@ jobs:
|
|||||||
tar --verbose --extract --strip-components 1 --directory dist --file ghostty-source.tar.gz
|
tar --verbose --extract --strip-components 1 --directory dist --file ghostty-source.tar.gz
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||||
with:
|
with:
|
||||||
context: dist
|
context: dist
|
||||||
file: dist/src/build/docker/debian/Dockerfile
|
file: dist/src/build/docker/debian/Dockerfile
|
||||||
@ -865,18 +894,18 @@ jobs:
|
|||||||
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- name: Setup Cache
|
- name: Setup Cache
|
||||||
uses: namespacelabs/nscloud-cache-action@v1.2.8
|
uses: namespacelabs/nscloud-cache-action@449c929cd5138e6607e7e78458e88cc476e76f89 # v1.2.8
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
/nix
|
/nix
|
||||||
/zig
|
/zig
|
||||||
- name: Setup Nix
|
- name: Setup Nix
|
||||||
uses: cachix/install-nix-action@v31
|
uses: cachix/install-nix-action@f0fe604f8a612776892427721526b4c7cfb23aba # v31.4.1
|
||||||
with:
|
with:
|
||||||
nix_path: nixpkgs=channel:nixos-unstable
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
- uses: cachix/cachix-action@v16
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
with:
|
with:
|
||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
@ -901,8 +930,8 @@ jobs:
|
|||||||
runs-on: ${{ matrix.variant.runner }}
|
runs-on: ${{ matrix.variant.runner }}
|
||||||
needs: [flatpak-check-zig-cache, test]
|
needs: [flatpak-check-zig-cache, test]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- uses: flatpak/flatpak-github-actions/flatpak-builder@v6
|
- uses: flatpak/flatpak-github-actions/flatpak-builder@10a3c29f0162516f0f68006be14c92f34bd4fa6c # v6.5
|
||||||
with:
|
with:
|
||||||
bundle: com.mitchellh.ghostty
|
bundle: com.mitchellh.ghostty
|
||||||
manifest-path: flatpak/com.mitchellh.ghostty.yml
|
manifest-path: flatpak/com.mitchellh.ghostty.yml
|
||||||
|
10
.github/workflows/update-colorschemes.yml
vendored
10
.github/workflows/update-colorschemes.yml
vendored
@ -17,22 +17,22 @@ jobs:
|
|||||||
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Setup Cache
|
- name: Setup Cache
|
||||||
uses: namespacelabs/nscloud-cache-action@v1.2.8
|
uses: namespacelabs/nscloud-cache-action@449c929cd5138e6607e7e78458e88cc476e76f89 # v1.2.8
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
/nix
|
/nix
|
||||||
/zig
|
/zig
|
||||||
|
|
||||||
- name: Setup Nix
|
- name: Setup Nix
|
||||||
uses: cachix/install-nix-action@v31
|
uses: cachix/install-nix-action@f0fe604f8a612776892427721526b4c7cfb23aba # v31.4.1
|
||||||
with:
|
with:
|
||||||
nix_path: nixpkgs=channel:nixos-unstable
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
- uses: cachix/cachix-action@v16
|
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||||
with:
|
with:
|
||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
@ -60,7 +60,7 @@ jobs:
|
|||||||
run: nix build .#ghostty
|
run: nix build .#ghostty
|
||||||
|
|
||||||
- name: Create pull request
|
- name: Create pull request
|
||||||
uses: peter-evans/create-pull-request@v7
|
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
|
||||||
with:
|
with:
|
||||||
title: Update iTerm2 colorschemes
|
title: Update iTerm2 colorschemes
|
||||||
base: main
|
base: main
|
||||||
|
@ -155,6 +155,7 @@
|
|||||||
/src/input/KeyEncoder.zig @ghostty-org/terminal
|
/src/input/KeyEncoder.zig @ghostty-org/terminal
|
||||||
/src/terminal/ @ghostty-org/terminal
|
/src/terminal/ @ghostty-org/terminal
|
||||||
/src/terminfo/ @ghostty-org/terminal
|
/src/terminfo/ @ghostty-org/terminal
|
||||||
|
/src/termio/ @ghostty-org/terminal
|
||||||
/src/unicode/ @ghostty-org/terminal
|
/src/unicode/ @ghostty-org/terminal
|
||||||
/src/Surface.zig @ghostty-org/terminal
|
/src/Surface.zig @ghostty-org/terminal
|
||||||
/src/surface_mouse.zig @ghostty-org/terminal
|
/src/surface_mouse.zig @ghostty-org/terminal
|
||||||
@ -180,6 +181,7 @@
|
|||||||
/po/zh_CN.UTF-8.po @ghostty-org/zh_CN
|
/po/zh_CN.UTF-8.po @ghostty-org/zh_CN
|
||||||
/po/ga_IE.UTF-8.po @ghostty-org/ga_IE
|
/po/ga_IE.UTF-8.po @ghostty-org/ga_IE
|
||||||
/po/ko_KR.UTF-8.po @ghostty-org/ko_KR
|
/po/ko_KR.UTF-8.po @ghostty-org/ko_KR
|
||||||
|
/po/he_IL.UTF-8.po @ghostty-org/he_IL
|
||||||
|
|
||||||
# Packaging - Snap
|
# Packaging - Snap
|
||||||
/snap/ @ghostty-org/snap
|
/snap/ @ghostty-org/snap
|
||||||
|
1
dist/linux/app.desktop.in
vendored
1
dist/linux/app.desktop.in
vendored
@ -19,6 +19,7 @@ X-TerminalArgAppId=--class=
|
|||||||
X-TerminalArgDir=--working-directory=
|
X-TerminalArgDir=--working-directory=
|
||||||
X-TerminalArgHold=--wait-after-command
|
X-TerminalArgHold=--wait-after-command
|
||||||
DBusActivatable=true
|
DBusActivatable=true
|
||||||
|
X-KDE-Shortcuts=Ctrl+Alt+T
|
||||||
|
|
||||||
[Desktop Action new-window]
|
[Desktop Action new-window]
|
||||||
Name=New Window
|
Name=New Window
|
||||||
|
@ -350,6 +350,11 @@ typedef struct {
|
|||||||
const char* message;
|
const char* message;
|
||||||
} ghostty_diagnostic_s;
|
} ghostty_diagnostic_s;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char* ptr;
|
||||||
|
uintptr_t len;
|
||||||
|
} ghostty_string_s;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
double tl_px_x;
|
double tl_px_x;
|
||||||
double tl_px_y;
|
double tl_px_y;
|
||||||
@ -662,6 +667,19 @@ typedef struct {
|
|||||||
bool soft;
|
bool soft;
|
||||||
} ghostty_action_reload_config_s;
|
} ghostty_action_reload_config_s;
|
||||||
|
|
||||||
|
// apprt.action.OpenUrlKind
|
||||||
|
typedef enum {
|
||||||
|
GHOSTTY_ACTION_OPEN_URL_KIND_UNKNOWN,
|
||||||
|
GHOSTTY_ACTION_OPEN_URL_KIND_TEXT,
|
||||||
|
} ghostty_action_open_url_kind_e;
|
||||||
|
|
||||||
|
// apprt.action.OpenUrl.C
|
||||||
|
typedef struct {
|
||||||
|
ghostty_action_open_url_kind_e kind;
|
||||||
|
const char* url;
|
||||||
|
uintptr_t len;
|
||||||
|
} ghostty_action_open_url_s;
|
||||||
|
|
||||||
// apprt.Action.Key
|
// apprt.Action.Key
|
||||||
typedef enum {
|
typedef enum {
|
||||||
GHOSTTY_ACTION_QUIT,
|
GHOSTTY_ACTION_QUIT,
|
||||||
@ -711,7 +729,8 @@ typedef enum {
|
|||||||
GHOSTTY_ACTION_RING_BELL,
|
GHOSTTY_ACTION_RING_BELL,
|
||||||
GHOSTTY_ACTION_UNDO,
|
GHOSTTY_ACTION_UNDO,
|
||||||
GHOSTTY_ACTION_REDO,
|
GHOSTTY_ACTION_REDO,
|
||||||
GHOSTTY_ACTION_CHECK_FOR_UPDATES
|
GHOSTTY_ACTION_CHECK_FOR_UPDATES,
|
||||||
|
GHOSTTY_ACTION_OPEN_URL,
|
||||||
} ghostty_action_tag_e;
|
} ghostty_action_tag_e;
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
@ -739,6 +758,7 @@ typedef union {
|
|||||||
ghostty_action_color_change_s color_change;
|
ghostty_action_color_change_s color_change;
|
||||||
ghostty_action_reload_config_s reload_config;
|
ghostty_action_reload_config_s reload_config;
|
||||||
ghostty_action_config_change_s config_change;
|
ghostty_action_config_change_s config_change;
|
||||||
|
ghostty_action_open_url_s open_url;
|
||||||
} ghostty_action_u;
|
} ghostty_action_u;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -778,10 +798,11 @@ typedef struct {
|
|||||||
//-------------------------------------------------------------------
|
//-------------------------------------------------------------------
|
||||||
// Published API
|
// Published API
|
||||||
|
|
||||||
int ghostty_init(void);
|
int ghostty_init(uintptr_t, char**);
|
||||||
void ghostty_cli_main(uintptr_t, char**);
|
void ghostty_cli_try_action(void);
|
||||||
ghostty_info_s ghostty_info(void);
|
ghostty_info_s ghostty_info(void);
|
||||||
const char* ghostty_translate(const char*);
|
const char* ghostty_translate(const char*);
|
||||||
|
void ghostty_string_free(ghostty_string_s);
|
||||||
|
|
||||||
ghostty_config_t ghostty_config_new();
|
ghostty_config_t ghostty_config_new();
|
||||||
void ghostty_config_free(ghostty_config_t);
|
void ghostty_config_free(ghostty_config_t);
|
||||||
@ -796,7 +817,7 @@ ghostty_input_trigger_s ghostty_config_trigger(ghostty_config_t,
|
|||||||
uintptr_t);
|
uintptr_t);
|
||||||
uint32_t ghostty_config_diagnostics_count(ghostty_config_t);
|
uint32_t ghostty_config_diagnostics_count(ghostty_config_t);
|
||||||
ghostty_diagnostic_s ghostty_config_get_diagnostic(ghostty_config_t, uint32_t);
|
ghostty_diagnostic_s ghostty_config_get_diagnostic(ghostty_config_t, uint32_t);
|
||||||
void ghostty_config_open();
|
ghostty_string_s ghostty_config_open_path(void);
|
||||||
|
|
||||||
ghostty_app_t ghostty_app_new(const ghostty_runtime_config_s*,
|
ghostty_app_t ghostty_app_new(const ghostty_runtime_config_s*,
|
||||||
ghostty_config_t);
|
ghostty_config_t);
|
||||||
|
@ -48,8 +48,8 @@
|
|||||||
<string></string>
|
<string></string>
|
||||||
<key>LSEnvironment</key>
|
<key>LSEnvironment</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>GHOSTTY_MAC_APP</key>
|
<key>GHOSTTY_MAC_LAUNCH_SOURCE</key>
|
||||||
<string>1</string>
|
<string>app</string>
|
||||||
</dict>
|
</dict>
|
||||||
<key>MDItemKeywords</key>
|
<key>MDItemKeywords</key>
|
||||||
<string>Terminal</string>
|
<string>Terminal</string>
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
857F63812A5E64F200CA4815 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 857F63802A5E64F200CA4815 /* MainMenu.xib */; };
|
857F63812A5E64F200CA4815 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 857F63802A5E64F200CA4815 /* MainMenu.xib */; };
|
||||||
9351BE8E3D22937F003B3499 /* nvim in Resources */ = {isa = PBXBuildFile; fileRef = 9351BE8E2D22937F003B3499 /* nvim */; };
|
9351BE8E3D22937F003B3499 /* nvim in Resources */ = {isa = PBXBuildFile; fileRef = 9351BE8E2D22937F003B3499 /* nvim */; };
|
||||||
A50297352DFA0F3400B4E924 /* Double+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50297342DFA0F3300B4E924 /* Double+Extension.swift */; };
|
A50297352DFA0F3400B4E924 /* Double+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A50297342DFA0F3300B4E924 /* Double+Extension.swift */; };
|
||||||
|
A505D21D2E1A2FA20018808F /* FileHandle+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A505D21C2E1A2F9E0018808F /* FileHandle+Extension.swift */; };
|
||||||
|
A505D21F2E1B6DE00018808F /* NSWorkspace+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A505D21E2E1B6DDC0018808F /* NSWorkspace+Extension.swift */; };
|
||||||
A511940F2E050595007258CC /* CloseTerminalIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A511940E2E050590007258CC /* CloseTerminalIntent.swift */; };
|
A511940F2E050595007258CC /* CloseTerminalIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A511940E2E050590007258CC /* CloseTerminalIntent.swift */; };
|
||||||
A51194112E05A483007258CC /* QuickTerminalIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51194102E05A480007258CC /* QuickTerminalIntent.swift */; };
|
A51194112E05A483007258CC /* QuickTerminalIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51194102E05A480007258CC /* QuickTerminalIntent.swift */; };
|
||||||
A51194132E05D006007258CC /* Optional+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51194122E05D003007258CC /* Optional+Extension.swift */; };
|
A51194132E05D006007258CC /* Optional+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51194122E05D003007258CC /* Optional+Extension.swift */; };
|
||||||
@ -158,6 +160,8 @@
|
|||||||
857F63802A5E64F200CA4815 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = "<group>"; };
|
857F63802A5E64F200CA4815 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = "<group>"; };
|
||||||
9351BE8E2D22937F003B3499 /* nvim */ = {isa = PBXFileReference; lastKnownFileType = folder; name = nvim; path = "../zig-out/share/nvim"; sourceTree = "<group>"; };
|
9351BE8E2D22937F003B3499 /* nvim */ = {isa = PBXFileReference; lastKnownFileType = folder; name = nvim; path = "../zig-out/share/nvim"; sourceTree = "<group>"; };
|
||||||
A50297342DFA0F3300B4E924 /* Double+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Double+Extension.swift"; sourceTree = "<group>"; };
|
A50297342DFA0F3300B4E924 /* Double+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Double+Extension.swift"; sourceTree = "<group>"; };
|
||||||
|
A505D21C2E1A2F9E0018808F /* FileHandle+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileHandle+Extension.swift"; sourceTree = "<group>"; };
|
||||||
|
A505D21E2E1B6DDC0018808F /* NSWorkspace+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSWorkspace+Extension.swift"; sourceTree = "<group>"; };
|
||||||
A511940E2E050590007258CC /* CloseTerminalIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseTerminalIntent.swift; sourceTree = "<group>"; };
|
A511940E2E050590007258CC /* CloseTerminalIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseTerminalIntent.swift; sourceTree = "<group>"; };
|
||||||
A51194102E05A480007258CC /* QuickTerminalIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickTerminalIntent.swift; sourceTree = "<group>"; };
|
A51194102E05A480007258CC /* QuickTerminalIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickTerminalIntent.swift; sourceTree = "<group>"; };
|
||||||
A51194122E05D003007258CC /* Optional+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Optional+Extension.swift"; sourceTree = "<group>"; };
|
A51194122E05D003007258CC /* Optional+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Optional+Extension.swift"; sourceTree = "<group>"; };
|
||||||
@ -516,6 +520,7 @@
|
|||||||
A586366A2DF0A98900E04A10 /* Array+Extension.swift */,
|
A586366A2DF0A98900E04A10 /* Array+Extension.swift */,
|
||||||
A50297342DFA0F3300B4E924 /* Double+Extension.swift */,
|
A50297342DFA0F3300B4E924 /* Double+Extension.swift */,
|
||||||
A586366E2DF25D8300E04A10 /* Duration+Extension.swift */,
|
A586366E2DF25D8300E04A10 /* Duration+Extension.swift */,
|
||||||
|
A505D21C2E1A2F9E0018808F /* FileHandle+Extension.swift */,
|
||||||
A53A29802DB44A5E00B6E02C /* KeyboardShortcut+Extension.swift */,
|
A53A29802DB44A5E00B6E02C /* KeyboardShortcut+Extension.swift */,
|
||||||
A53A297E2DB4480A00B6E02C /* EventModifiers+Extension.swift */,
|
A53A297E2DB4480A00B6E02C /* EventModifiers+Extension.swift */,
|
||||||
A51194122E05D003007258CC /* Optional+Extension.swift */,
|
A51194122E05D003007258CC /* Optional+Extension.swift */,
|
||||||
@ -528,6 +533,7 @@
|
|||||||
AEE8B3442B9AA39600260C5E /* NSPasteboard+Extension.swift */,
|
AEE8B3442B9AA39600260C5E /* NSPasteboard+Extension.swift */,
|
||||||
C1F26EA62B738B9900404083 /* NSView+Extension.swift */,
|
C1F26EA62B738B9900404083 /* NSView+Extension.swift */,
|
||||||
A5874D9C2DAD785F00E83852 /* NSWindow+Extension.swift */,
|
A5874D9C2DAD785F00E83852 /* NSWindow+Extension.swift */,
|
||||||
|
A505D21E2E1B6DDC0018808F /* NSWorkspace+Extension.swift */,
|
||||||
A5985CD62C320C4500C57AD3 /* String+Extension.swift */,
|
A5985CD62C320C4500C57AD3 /* String+Extension.swift */,
|
||||||
A58636722DF4813000E04A10 /* UndoManager+Extension.swift */,
|
A58636722DF4813000E04A10 /* UndoManager+Extension.swift */,
|
||||||
A5CC36142C9CDA03004D6760 /* View+Extension.swift */,
|
A5CC36142C9CDA03004D6760 /* View+Extension.swift */,
|
||||||
@ -799,6 +805,7 @@
|
|||||||
A5874D9D2DAD786100E83852 /* NSWindow+Extension.swift in Sources */,
|
A5874D9D2DAD786100E83852 /* NSWindow+Extension.swift in Sources */,
|
||||||
A54D786C2CA7978E001B19B1 /* BaseTerminalController.swift in Sources */,
|
A54D786C2CA7978E001B19B1 /* BaseTerminalController.swift in Sources */,
|
||||||
A58636732DF4813400E04A10 /* UndoManager+Extension.swift in Sources */,
|
A58636732DF4813400E04A10 /* UndoManager+Extension.swift in Sources */,
|
||||||
|
A505D21D2E1A2FA20018808F /* FileHandle+Extension.swift in Sources */,
|
||||||
A59FB5CF2AE0DB50009128F3 /* InspectorView.swift in Sources */,
|
A59FB5CF2AE0DB50009128F3 /* InspectorView.swift in Sources */,
|
||||||
CFBB5FEA2D231E5000FD62EE /* QuickTerminalSpaceBehavior.swift in Sources */,
|
CFBB5FEA2D231E5000FD62EE /* QuickTerminalSpaceBehavior.swift in Sources */,
|
||||||
A54B0CE92D0CECD100CBEFF8 /* ColorizedGhosttyIconView.swift in Sources */,
|
A54B0CE92D0CECD100CBEFF8 /* ColorizedGhosttyIconView.swift in Sources */,
|
||||||
@ -815,6 +822,7 @@
|
|||||||
A5CC36152C9CDA06004D6760 /* View+Extension.swift in Sources */,
|
A5CC36152C9CDA06004D6760 /* View+Extension.swift in Sources */,
|
||||||
A56D58892ACDE6CA00508D2C /* ServiceProvider.swift in Sources */,
|
A56D58892ACDE6CA00508D2C /* ServiceProvider.swift in Sources */,
|
||||||
A5CBD0602CA0C90A0017A1AE /* QuickTerminalWindow.swift in Sources */,
|
A5CBD0602CA0C90A0017A1AE /* QuickTerminalWindow.swift in Sources */,
|
||||||
|
A505D21F2E1B6DE00018808F /* NSWorkspace+Extension.swift in Sources */,
|
||||||
A5CBD05E2CA0C5EC0017A1AE /* QuickTerminalController.swift in Sources */,
|
A5CBD05E2CA0C5EC0017A1AE /* QuickTerminalController.swift in Sources */,
|
||||||
A5CF66D72D29DDB500139794 /* Ghostty.Event.swift in Sources */,
|
A5CF66D72D29DDB500139794 /* Ghostty.Event.swift in Sources */,
|
||||||
A511940F2E050595007258CC /* CloseTerminalIntent.swift in Sources */,
|
A511940F2E050595007258CC /* CloseTerminalIntent.swift in Sources */,
|
||||||
|
@ -256,9 +256,8 @@ class AppDelegate: NSObject,
|
|||||||
// Setup signal handlers
|
// Setup signal handlers
|
||||||
setupSignals()
|
setupSignals()
|
||||||
|
|
||||||
// This is a hack used by our build scripts, specifically `zig build run`,
|
// If we launched via zig run then we need to force foreground.
|
||||||
// to force our app to the foreground.
|
if Ghostty.launchSource == .zig_run {
|
||||||
if ProcessInfo.processInfo.environment["GHOSTTY_MAC_ACTIVATE"] == "1" {
|
|
||||||
// This never gets called until we click the dock icon. This forces it
|
// This never gets called until we click the dock icon. This forces it
|
||||||
// activate immediately.
|
// activate immediately.
|
||||||
applicationDidBecomeActive(.init(name: NSApplication.didBecomeActiveNotification))
|
applicationDidBecomeActive(.init(name: NSApplication.didBecomeActiveNotification))
|
||||||
@ -933,7 +932,7 @@ class AppDelegate: NSObject,
|
|||||||
//MARK: - IB Actions
|
//MARK: - IB Actions
|
||||||
|
|
||||||
@IBAction func openConfig(_ sender: Any?) {
|
@IBAction func openConfig(_ sender: Any?) {
|
||||||
ghostty.openConfig()
|
Ghostty.App.openConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func reloadConfig(_ sender: Any?) {
|
@IBAction func reloadConfig(_ sender: Any?) {
|
||||||
|
@ -2,13 +2,32 @@ import AppKit
|
|||||||
import Cocoa
|
import Cocoa
|
||||||
import GhosttyKit
|
import GhosttyKit
|
||||||
|
|
||||||
// We put the GHOSTTY_MAC_APP env var into the Info.plist to detect
|
// Initialize Ghostty global state. We do this once right away because the
|
||||||
// whether we launch from the app or not. A user can fake this if
|
// CLI APIs require it and it lets us ensure it is done immediately for the
|
||||||
// they want but they're doing so at their own detriment...
|
// rest of the app.
|
||||||
let process = ProcessInfo.processInfo
|
if ghostty_init(UInt(CommandLine.argc), CommandLine.unsafeArgv) != GHOSTTY_SUCCESS {
|
||||||
if ((process.environment["GHOSTTY_MAC_APP"] ?? "") == "") {
|
Ghostty.logger.critical("ghostty_init failed")
|
||||||
ghostty_cli_main(UInt(CommandLine.argc), CommandLine.unsafeArgv)
|
|
||||||
exit(1)
|
// We also write to stderr if this is executed from the CLI or zig run
|
||||||
|
switch Ghostty.launchSource {
|
||||||
|
case .cli, .zig_run:
|
||||||
|
let stderrHandle = FileHandle.standardError
|
||||||
|
stderrHandle.write(
|
||||||
|
"Ghostty failed to initialize! If you're executing Ghostty from the command line\n" +
|
||||||
|
"then this is usually because an invalid action or multiple actions were specified.\n" +
|
||||||
|
"Actions start with the `+` character.\n\n" +
|
||||||
|
"View all available actions by running `ghostty +help`.\n")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
case .app:
|
||||||
|
// For the app we exit immediately. We should handle this case more
|
||||||
|
// gracefully in the future.
|
||||||
|
exit(1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This will run the CLI action and exit if one was specified. A CLI
|
||||||
|
// action is a command starting with a `+`, such as `ghostty +boo`.
|
||||||
|
ghostty_cli_try_action();
|
||||||
|
|
||||||
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
|
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
|
||||||
|
@ -40,4 +40,34 @@ extension Ghostty.Action {
|
|||||||
self.amount = c.amount
|
self.amount = c.amount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct OpenURL {
|
||||||
|
enum Kind {
|
||||||
|
case unknown
|
||||||
|
case text
|
||||||
|
|
||||||
|
init(_ c: ghostty_action_open_url_kind_e) {
|
||||||
|
switch c {
|
||||||
|
case GHOSTTY_ACTION_OPEN_URL_KIND_TEXT:
|
||||||
|
self = .text
|
||||||
|
default:
|
||||||
|
self = .unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let kind: Kind
|
||||||
|
let url: String
|
||||||
|
|
||||||
|
init(c: ghostty_action_open_url_s) {
|
||||||
|
self.kind = Kind(c.kind)
|
||||||
|
|
||||||
|
if let urlCString = c.url {
|
||||||
|
let data = Data(bytes: urlCString, count: Int(c.len))
|
||||||
|
self.url = String(data: data, encoding: .utf8) ?? ""
|
||||||
|
} else {
|
||||||
|
self.url = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,12 +45,6 @@ extension Ghostty {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
// Initialize ghostty global state. This happens once per process.
|
|
||||||
if ghostty_init() != GHOSTTY_SUCCESS {
|
|
||||||
logger.critical("ghostty_init failed, weird things may happen")
|
|
||||||
readiness = .error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the global configuration.
|
// Initialize the global configuration.
|
||||||
self.config = Config()
|
self.config = Config()
|
||||||
if self.config.config == nil {
|
if self.config.config == nil {
|
||||||
@ -120,9 +114,21 @@ extension Ghostty {
|
|||||||
ghostty_app_tick(app)
|
ghostty_app_tick(app)
|
||||||
}
|
}
|
||||||
|
|
||||||
func openConfig() {
|
static func openConfig() {
|
||||||
guard let app = self.app else { return }
|
let str = Ghostty.AllocatedString(ghostty_config_open_path()).string
|
||||||
ghostty_app_open_config(app)
|
guard !str.isEmpty else { return }
|
||||||
|
#if os(macOS)
|
||||||
|
let fileURL = URL(fileURLWithPath: str).absoluteString
|
||||||
|
var action = ghostty_action_open_url_s()
|
||||||
|
action.kind = GHOSTTY_ACTION_OPEN_URL_KIND_TEXT
|
||||||
|
fileURL.withCString { cStr in
|
||||||
|
action.url = cStr
|
||||||
|
action.len = UInt(fileURL.count)
|
||||||
|
_ = openURL(action)
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
fatalError("Unsupported platform for opening config file")
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reload the configuration.
|
/// Reload the configuration.
|
||||||
@ -494,7 +500,7 @@ extension Ghostty {
|
|||||||
pwdChanged(app, target: target, v: action.action.pwd)
|
pwdChanged(app, target: target, v: action.action.pwd)
|
||||||
|
|
||||||
case GHOSTTY_ACTION_OPEN_CONFIG:
|
case GHOSTTY_ACTION_OPEN_CONFIG:
|
||||||
ghostty_config_open()
|
openConfig()
|
||||||
|
|
||||||
case GHOSTTY_ACTION_FLOAT_WINDOW:
|
case GHOSTTY_ACTION_FLOAT_WINDOW:
|
||||||
toggleFloatWindow(app, target: target, mode: action.action.float_window)
|
toggleFloatWindow(app, target: target, mode: action.action.float_window)
|
||||||
@ -553,6 +559,9 @@ extension Ghostty {
|
|||||||
case GHOSTTY_ACTION_CHECK_FOR_UPDATES:
|
case GHOSTTY_ACTION_CHECK_FOR_UPDATES:
|
||||||
checkForUpdates(app)
|
checkForUpdates(app)
|
||||||
|
|
||||||
|
case GHOSTTY_ACTION_OPEN_URL:
|
||||||
|
return openURL(action.action.open_url)
|
||||||
|
|
||||||
case GHOSTTY_ACTION_UNDO:
|
case GHOSTTY_ACTION_UNDO:
|
||||||
return undo(app, target: target)
|
return undo(app, target: target)
|
||||||
|
|
||||||
@ -605,6 +614,34 @@ extension Ghostty {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static func openURL(
|
||||||
|
_ v: ghostty_action_open_url_s
|
||||||
|
) -> 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
|
||||||
|
}
|
||||||
|
|
||||||
|
switch action.kind {
|
||||||
|
case .text:
|
||||||
|
// Open with the default text editor
|
||||||
|
if let textEditor = NSWorkspace.shared.defaultTextEditor {
|
||||||
|
NSWorkspace.shared.open([url], withApplicationAt: textEditor, configuration: NSWorkspace.OpenConfiguration())
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
case .unknown:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open with the default application for the URL
|
||||||
|
NSWorkspace.shared.open(url)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
private static func undo(_ app: ghostty_app_t, target: ghostty_target_s) -> Bool {
|
private static func undo(_ app: ghostty_app_t, target: ghostty_target_s) -> Bool {
|
||||||
let undoManager: UndoManager?
|
let undoManager: UndoManager?
|
||||||
switch (target.tag) {
|
switch (target.tag) {
|
||||||
|
@ -48,8 +48,51 @@ extension Ghostty {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: General Helpers
|
||||||
|
|
||||||
|
extension Ghostty {
|
||||||
|
enum LaunchSource: String {
|
||||||
|
case cli
|
||||||
|
case app
|
||||||
|
case zig_run
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the mechanism that launched the app. This is based on an env var so
|
||||||
|
/// its up to the env var being set in the correct circumstance.
|
||||||
|
static var launchSource: LaunchSource {
|
||||||
|
guard let envValue = ProcessInfo.processInfo.environment["GHOSTTY_MAC_LAUNCH_SOURCE"] else {
|
||||||
|
// We default to the CLI because the app bundle always sets the
|
||||||
|
// source. If its unset we assume we're in a CLI environment.
|
||||||
|
return .cli
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the env var is set but its unknown then we default back to the app.
|
||||||
|
return LaunchSource(rawValue: envValue) ?? .app
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: Swift Types for C Types
|
// MARK: Swift Types for C Types
|
||||||
|
|
||||||
|
extension Ghostty {
|
||||||
|
class AllocatedString {
|
||||||
|
private let cString: ghostty_string_s
|
||||||
|
|
||||||
|
init(_ c: ghostty_string_s) {
|
||||||
|
self.cString = c
|
||||||
|
}
|
||||||
|
|
||||||
|
var string: String {
|
||||||
|
guard let ptr = cString.ptr else { return "" }
|
||||||
|
let data = Data(bytes: ptr, count: Int(cString.len))
|
||||||
|
return String(data: data, encoding: .utf8) ?? ""
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
ghostty_string_free(cString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension Ghostty {
|
extension Ghostty {
|
||||||
enum SetFloatWIndow {
|
enum SetFloatWIndow {
|
||||||
case on
|
case on
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension FileHandle: @retroactive TextOutputStream {
|
||||||
|
/// Write a string to a filehandle.
|
||||||
|
public func write(_ string: String) {
|
||||||
|
let data = Data(string.utf8)
|
||||||
|
self.write(data)
|
||||||
|
}
|
||||||
|
}
|
29
macos/Sources/Helpers/Extensions/NSWorkspace+Extension.swift
Normal file
29
macos/Sources/Helpers/Extensions/NSWorkspace+Extension.swift
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import AppKit
|
||||||
|
import UniformTypeIdentifiers
|
||||||
|
|
||||||
|
extension NSWorkspace {
|
||||||
|
/// Returns the URL of the default text editor application.
|
||||||
|
/// - Returns: The URL of the default text editor, or nil if no default text editor is found.
|
||||||
|
var defaultTextEditor: URL? {
|
||||||
|
defaultApplicationURL(forContentType: UTType.plainText.identifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the URL of the default application for opening files with the specified content type.
|
||||||
|
/// - Parameter contentType: The content type identifier (UTI) to find the default application for.
|
||||||
|
/// - Returns: The URL of the default application, or nil if no default application is found.
|
||||||
|
func defaultApplicationURL(forContentType contentType: String) -> URL? {
|
||||||
|
return LSCopyDefaultApplicationURLForContentType(
|
||||||
|
contentType as CFString,
|
||||||
|
.all,
|
||||||
|
nil
|
||||||
|
)?.takeRetainedValue() as? URL
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the URL of the default application for opening files with the specified file extension.
|
||||||
|
/// - Parameter ext: The file extension to find the default application for.
|
||||||
|
/// - Returns: The URL of the default application, or nil if no default application is found.
|
||||||
|
func defaultApplicationURL(forExtension ext: String) -> URL? {
|
||||||
|
guard let uti = UTType(filenameExtension: ext) else { return nil}
|
||||||
|
return defaultApplicationURL(forContentType: uti.identifier)
|
||||||
|
}
|
||||||
|
}
|
@ -58,6 +58,7 @@
|
|||||||
jq,
|
jq,
|
||||||
minisign,
|
minisign,
|
||||||
pandoc,
|
pandoc,
|
||||||
|
pinact,
|
||||||
hyperfine,
|
hyperfine,
|
||||||
typos,
|
typos,
|
||||||
uv,
|
uv,
|
||||||
@ -98,6 +99,7 @@ in
|
|||||||
# Linting
|
# Linting
|
||||||
nodePackages.prettier
|
nodePackages.prettier
|
||||||
alejandra
|
alejandra
|
||||||
|
pinact
|
||||||
typos
|
typos
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
|
@ -92,6 +92,30 @@ pub const Format = enum(c_uint) {
|
|||||||
_,
|
_,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Minification filter for textures.
|
||||||
|
pub const MinFilter = enum(c_int) {
|
||||||
|
nearest = c.GL_NEAREST,
|
||||||
|
linear = c.GL_LINEAR,
|
||||||
|
nearest_mipmap_nearest = c.GL_NEAREST_MIPMAP_NEAREST,
|
||||||
|
linear_mipmap_nearest = c.GL_LINEAR_MIPMAP_NEAREST,
|
||||||
|
nearest_mipmap_linear = c.GL_NEAREST_MIPMAP_LINEAR,
|
||||||
|
linear_mipmap_linear = c.GL_LINEAR_MIPMAP_LINEAR,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Magnification filter for textures.
|
||||||
|
pub const MagFilter = enum(c_int) {
|
||||||
|
nearest = c.GL_NEAREST,
|
||||||
|
linear = c.GL_LINEAR,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Texture coordinate wrapping mode.
|
||||||
|
pub const Wrap = enum(c_int) {
|
||||||
|
clamp_to_edge = c.GL_CLAMP_TO_EDGE,
|
||||||
|
clamp_to_border = c.GL_CLAMP_TO_BORDER,
|
||||||
|
mirrored_repeat = c.GL_MIRRORED_REPEAT,
|
||||||
|
repeat = c.GL_REPEAT,
|
||||||
|
};
|
||||||
|
|
||||||
/// Data type for texture images.
|
/// Data type for texture images.
|
||||||
pub const DataType = enum(c_uint) {
|
pub const DataType = enum(c_uint) {
|
||||||
UnsignedByte = c.GL_UNSIGNED_BYTE,
|
UnsignedByte = c.GL_UNSIGNED_BYTE,
|
||||||
|
298
po/he_IL.UTF-8.po
Normal file
298
po/he_IL.UTF-8.po
Normal file
@ -0,0 +1,298 @@
|
|||||||
|
# Hebrew translations for com.mitchellh.ghostty.
|
||||||
|
# Copyright (C) 2025 Mitchell Hashimoto
|
||||||
|
# This file is distributed under the same license as the com.mitchellh.ghostty package.
|
||||||
|
# Sl (Shahaf Levi), Sl's Repository Ltd <ghostty@slsrepo.com>, 2025.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: com.mitchellh.ghostty\n"
|
||||||
|
"Report-Msgid-Bugs-To: m@mitchellh.com\n"
|
||||||
|
"POT-Creation-Date: 2025-06-28 17:01+0200\n"
|
||||||
|
"PO-Revision-Date: 2025-03-13 00:00+0000\n"
|
||||||
|
"Last-Translator: Sl (Shahaf Levi), Sl's Repository Ltd <ghostty@slsrepo.com>\n"
|
||||||
|
"Language-Team: Hebrew <he_IL@lists.sourceforge.net>\n"
|
||||||
|
"Language: he\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\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:263
|
||||||
|
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:1036
|
||||||
|
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/Window.zig:216
|
||||||
|
msgid "Main Menu"
|
||||||
|
msgstr "תפריט ראשי"
|
||||||
|
|
||||||
|
#: src/apprt/gtk/Window.zig:238
|
||||||
|
msgid "View Open Tabs"
|
||||||
|
msgstr "הצג/י כרטיסיות פתוחות"
|
||||||
|
|
||||||
|
#: src/apprt/gtk/Window.zig:264
|
||||||
|
msgid "New Split"
|
||||||
|
msgstr "פיצול חדש"
|
||||||
|
|
||||||
|
#: src/apprt/gtk/Window.zig:327
|
||||||
|
msgid ""
|
||||||
|
"⚠️ You're running a debug build of Ghostty! Performance will be degraded."
|
||||||
|
msgstr "⚠️ את/ה מריץ/ה גרסת ניפוי שגיאות של Ghostty! הביצועים יהיו ירודים."
|
||||||
|
|
||||||
|
#: src/apprt/gtk/Window.zig:773
|
||||||
|
msgid "Reloaded the configuration"
|
||||||
|
msgstr "ההגדרות הוטענו מחדש"
|
||||||
|
|
||||||
|
#: src/apprt/gtk/Window.zig:1017
|
||||||
|
msgid "Ghostty Developers"
|
||||||
|
msgstr "המפתחים של Ghostty"
|
||||||
|
|
||||||
|
#: src/apprt/gtk/inspector.zig:144
|
||||||
|
msgid "Ghostty: Terminal Inspector"
|
||||||
|
msgstr "Ghostty: בודק המסוף"
|
||||||
|
|
||||||
|
#: src/apprt/gtk/CloseDialog.zig:47
|
||||||
|
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:1257
|
||||||
|
msgid "Copied to clipboard"
|
||||||
|
msgstr "הועתק ללוח ההעתקה"
|
@ -270,6 +270,7 @@ const DerivedConfig = struct {
|
|||||||
title: ?[:0]const u8,
|
title: ?[:0]const u8,
|
||||||
title_report: bool,
|
title_report: bool,
|
||||||
links: []Link,
|
links: []Link,
|
||||||
|
link_previews: configpkg.LinkPreviews,
|
||||||
|
|
||||||
const Link = struct {
|
const Link = struct {
|
||||||
regex: oni.Regex,
|
regex: oni.Regex,
|
||||||
@ -336,6 +337,7 @@ const DerivedConfig = struct {
|
|||||||
.title = config.title,
|
.title = config.title,
|
||||||
.title_report = config.@"title-report",
|
.title_report = config.@"title-report",
|
||||||
.links = links,
|
.links = links,
|
||||||
|
.link_previews = config.@"link-previews",
|
||||||
|
|
||||||
// Assignments happen sequentially so we have to do this last
|
// Assignments happen sequentially so we have to do this last
|
||||||
// so that the memory is captured from allocs above.
|
// so that the memory is captured from allocs above.
|
||||||
@ -1242,7 +1244,7 @@ fn mouseRefreshLinks(
|
|||||||
// Get our link at the current position. This returns null if there
|
// Get our link at the current position. This returns null if there
|
||||||
// isn't a link OR if we shouldn't be showing links for some reason
|
// isn't a link OR if we shouldn't be showing links for some reason
|
||||||
// (see further comments for cases).
|
// (see further comments for cases).
|
||||||
const link_: ?apprt.action.MouseOverLink = link: {
|
const link_: ?apprt.action.MouseOverLink, const preview: bool = link: {
|
||||||
// If we clicked and our mouse moved cells then we never
|
// If we clicked and our mouse moved cells then we never
|
||||||
// highlight links until the mouse is unclicked. This follows
|
// highlight links until the mouse is unclicked. This follows
|
||||||
// standard macOS and Linux behavior where a click and drag cancels
|
// standard macOS and Linux behavior where a click and drag cancels
|
||||||
@ -1257,18 +1259,21 @@ fn mouseRefreshLinks(
|
|||||||
|
|
||||||
if (!click_pt.coord().eql(pos_vp)) {
|
if (!click_pt.coord().eql(pos_vp)) {
|
||||||
log.debug("mouse moved while left click held, ignoring link hover", .{});
|
log.debug("mouse moved while left click held, ignoring link hover", .{});
|
||||||
break :link null;
|
break :link .{ null, false };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const link = (try self.linkAtPos(pos)) orelse break :link null;
|
const link = (try self.linkAtPos(pos)) orelse break :link .{ null, false };
|
||||||
switch (link[0]) {
|
switch (link[0]) {
|
||||||
.open => {
|
.open => {
|
||||||
const str = try self.io.terminal.screen.selectionString(alloc, .{
|
const str = try self.io.terminal.screen.selectionString(alloc, .{
|
||||||
.sel = link[1],
|
.sel = link[1],
|
||||||
.trim = false,
|
.trim = false,
|
||||||
});
|
});
|
||||||
break :link .{ .url = str };
|
break :link .{
|
||||||
|
.{ .url = str },
|
||||||
|
self.config.link_previews == .true,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
._open_osc8 => {
|
._open_osc8 => {
|
||||||
@ -1276,9 +1281,14 @@ fn mouseRefreshLinks(
|
|||||||
const pin = link[1].start();
|
const pin = link[1].start();
|
||||||
const uri = self.osc8URI(pin) orelse {
|
const uri = self.osc8URI(pin) orelse {
|
||||||
log.warn("failed to get URI for OSC8 hyperlink", .{});
|
log.warn("failed to get URI for OSC8 hyperlink", .{});
|
||||||
break :link null;
|
break :link .{ null, false };
|
||||||
|
};
|
||||||
|
break :link .{
|
||||||
|
.{
|
||||||
|
.url = uri,
|
||||||
|
},
|
||||||
|
self.config.link_previews != .false,
|
||||||
};
|
};
|
||||||
break :link .{ .url = uri };
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -1294,11 +1304,15 @@ fn mouseRefreshLinks(
|
|||||||
.mouse_shape,
|
.mouse_shape,
|
||||||
.pointer,
|
.pointer,
|
||||||
);
|
);
|
||||||
_ = try self.rt_app.performAction(
|
|
||||||
.{ .surface = self },
|
if (preview) {
|
||||||
.mouse_over_link,
|
_ = try self.rt_app.performAction(
|
||||||
link,
|
.{ .surface = self },
|
||||||
);
|
.mouse_over_link,
|
||||||
|
link,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
try self.queueRender();
|
try self.queueRender();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -3710,7 +3724,7 @@ fn processLinks(self: *Surface, pos: apprt.CursorPos) !bool {
|
|||||||
.trim = false,
|
.trim = false,
|
||||||
});
|
});
|
||||||
defer self.alloc.free(str);
|
defer self.alloc.free(str);
|
||||||
try internal_os.open(self.alloc, .unknown, str);
|
try self.openUrl(.{ .kind = .unknown, .url = str });
|
||||||
},
|
},
|
||||||
|
|
||||||
._open_osc8 => {
|
._open_osc8 => {
|
||||||
@ -3718,13 +3732,35 @@ fn processLinks(self: *Surface, pos: apprt.CursorPos) !bool {
|
|||||||
log.warn("failed to get URI for OSC8 hyperlink", .{});
|
log.warn("failed to get URI for OSC8 hyperlink", .{});
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
try internal_os.open(self.alloc, .unknown, uri);
|
try self.openUrl(.{ .kind = .unknown, .url = uri });
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn openUrl(
|
||||||
|
self: *Surface,
|
||||||
|
action: apprt.action.OpenUrl,
|
||||||
|
) !void {
|
||||||
|
// If the apprt handles it then we're done.
|
||||||
|
if (try self.rt_app.performAction(
|
||||||
|
.{ .surface = self },
|
||||||
|
.open_url,
|
||||||
|
action,
|
||||||
|
)) return;
|
||||||
|
|
||||||
|
// apprt didn't handle it, fallback to our simple cross-platform
|
||||||
|
// URL opener. We log a warning because we want well-behaved
|
||||||
|
// apprts to handle this themselves.
|
||||||
|
log.warn("apprt did not handle open URL action, falling back to default opener", .{});
|
||||||
|
try internal_os.open(
|
||||||
|
self.alloc,
|
||||||
|
action.kind,
|
||||||
|
action.url,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Return the URI for an OSC8 hyperlink at the given position or null
|
/// Return the URI for an OSC8 hyperlink at the given position or null
|
||||||
/// if there is no hyperlink.
|
/// if there is no hyperlink.
|
||||||
fn osc8URI(self: *Surface, pin: terminal.Pin) ?[]const u8 {
|
fn osc8URI(self: *Surface, pin: terminal.Pin) ?[]const u8 {
|
||||||
@ -4443,6 +4479,18 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
|
|||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.copy_title_to_clipboard => {
|
||||||
|
const title = self.rt_surface.getTitle() orelse return false;
|
||||||
|
if (title.len == 0) return false;
|
||||||
|
|
||||||
|
self.rt_surface.setClipboardString(title, .standard, false) catch |err| {
|
||||||
|
log.err("error copying title to clipboard err={}", .{err});
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
.paste_from_clipboard => try self.startClipboardRequest(
|
.paste_from_clipboard => try self.startClipboardRequest(
|
||||||
.standard,
|
.standard,
|
||||||
.{ .paste = {} },
|
.{ .paste = {} },
|
||||||
@ -4484,6 +4532,14 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
|
|||||||
try self.setFontSize(size);
|
try self.setFontSize(size);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.set_font_size => |points| {
|
||||||
|
log.debug("set font size={d}", .{points});
|
||||||
|
|
||||||
|
var size = self.font_size;
|
||||||
|
size.points = std.math.clamp(points, 1.0, 255.0);
|
||||||
|
try self.setFontSize(size);
|
||||||
|
},
|
||||||
|
|
||||||
.prompt_surface_title => return try self.rt_app.performAction(
|
.prompt_surface_title => return try self.rt_app.performAction(
|
||||||
.{ .surface = self },
|
.{ .surface = self },
|
||||||
.prompt_title,
|
.prompt_title,
|
||||||
@ -4923,7 +4979,7 @@ fn writeScreenFile(
|
|||||||
defer self.alloc.free(pathZ);
|
defer self.alloc.free(pathZ);
|
||||||
try self.rt_surface.setClipboardString(pathZ, .standard, false);
|
try self.rt_surface.setClipboardString(pathZ, .standard, false);
|
||||||
},
|
},
|
||||||
.open => try internal_os.open(self.alloc, .text, path),
|
.open => try self.openUrl(.{ .kind = .text, .url = path }),
|
||||||
.paste => self.io.queueMessage(try termio.Message.writeReq(
|
.paste => self.io.queueMessage(try termio.Message.writeReq(
|
||||||
self.alloc,
|
self.alloc,
|
||||||
path,
|
path,
|
||||||
|
@ -267,6 +267,11 @@ pub const Action = union(Key) {
|
|||||||
|
|
||||||
check_for_updates,
|
check_for_updates,
|
||||||
|
|
||||||
|
/// Open a URL using the native OS mechanisms. On macOS this might be `open`
|
||||||
|
/// or on Linux this might be `xdg-open`. The exact mechanism is up to the
|
||||||
|
/// apprt.
|
||||||
|
open_url: OpenUrl,
|
||||||
|
|
||||||
/// Sync with: ghostty_action_tag_e
|
/// Sync with: ghostty_action_tag_e
|
||||||
pub const Key = enum(c_int) {
|
pub const Key = enum(c_int) {
|
||||||
quit,
|
quit,
|
||||||
@ -317,6 +322,7 @@ pub const Action = union(Key) {
|
|||||||
undo,
|
undo,
|
||||||
redo,
|
redo,
|
||||||
check_for_updates,
|
check_for_updates,
|
||||||
|
open_url,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Sync with: ghostty_action_u
|
/// Sync with: ghostty_action_u
|
||||||
@ -357,7 +363,11 @@ pub const Action = union(Key) {
|
|||||||
// For ABI compatibility, we expect that this is our union size.
|
// For ABI compatibility, we expect that this is our union size.
|
||||||
// At the time of writing, we don't promise ABI compatibility
|
// At the time of writing, we don't promise ABI compatibility
|
||||||
// so we can change this but I want to be aware of it.
|
// so we can change this but I want to be aware of it.
|
||||||
assert(@sizeOf(CValue) == 16);
|
assert(@sizeOf(CValue) == switch (@sizeOf(usize)) {
|
||||||
|
4 => 16,
|
||||||
|
8 => 24,
|
||||||
|
else => unreachable,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the value type for the given key.
|
/// Returns the value type for the given key.
|
||||||
@ -614,3 +624,44 @@ pub const ConfigChange = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Open a URL
|
||||||
|
pub const OpenUrl = struct {
|
||||||
|
/// The type of data that the URL refers to.
|
||||||
|
kind: Kind,
|
||||||
|
|
||||||
|
/// The URL.
|
||||||
|
url: []const u8,
|
||||||
|
|
||||||
|
/// The type of the data at the URL to open. This is used as a hint to
|
||||||
|
/// potentially open the URL in a different way.
|
||||||
|
///
|
||||||
|
/// Sync with: ghostty_action_open_url_kind_e
|
||||||
|
pub const Kind = enum(c_int) {
|
||||||
|
/// The type is unknown. This is the default and apprts should
|
||||||
|
/// open the URL in the most generic way possible. For example,
|
||||||
|
/// on macOS this would be the equivalent of `open` or on Linux
|
||||||
|
/// this would be `xdg-open`.
|
||||||
|
unknown,
|
||||||
|
|
||||||
|
/// The URL is known to be a text file. In this case, the apprt
|
||||||
|
/// should try to open the URL in a text editor or viewer or
|
||||||
|
/// some equivalent, if possible.
|
||||||
|
text,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sync with: ghostty_action_open_url_s
|
||||||
|
pub const C = extern struct {
|
||||||
|
kind: Kind,
|
||||||
|
url: [*]const u8,
|
||||||
|
len: usize,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn cval(self: OpenUrl) C {
|
||||||
|
return .{
|
||||||
|
.kind = self.kind,
|
||||||
|
.url = self.url.ptr,
|
||||||
|
.len = self.url.len,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@ -884,7 +884,7 @@ pub const Surface = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove this so that running `ghostty` within Ghostty works.
|
// Remove this so that running `ghostty` within Ghostty works.
|
||||||
env.remove("GHOSTTY_MAC_APP");
|
env.remove("GHOSTTY_MAC_LAUNCH_SOURCE");
|
||||||
|
|
||||||
// If we were launched from the desktop then we want to
|
// If we were launched from the desktop then we want to
|
||||||
// remove the LANGUAGE env var so that we don't inherit
|
// remove the LANGUAGE env var so that we don't inherit
|
||||||
|
@ -496,7 +496,7 @@ pub fn performAction(
|
|||||||
.resize_split => self.resizeSplit(target, value),
|
.resize_split => self.resizeSplit(target, value),
|
||||||
.equalize_splits => self.equalizeSplits(target),
|
.equalize_splits => self.equalizeSplits(target),
|
||||||
.goto_split => return self.gotoSplit(target, value),
|
.goto_split => return self.gotoSplit(target, value),
|
||||||
.open_config => try configpkg.edit.open(self.core_app.alloc),
|
.open_config => return self.openConfig(),
|
||||||
.config_change => self.configChange(target, value.config),
|
.config_change => self.configChange(target, value.config),
|
||||||
.reload_config => try self.reloadConfig(target, value),
|
.reload_config => try self.reloadConfig(target, value),
|
||||||
.inspector => self.controlInspector(target, value),
|
.inspector => self.controlInspector(target, value),
|
||||||
@ -519,6 +519,7 @@ pub fn performAction(
|
|||||||
.secure_input => self.setSecureInput(target, value),
|
.secure_input => self.setSecureInput(target, value),
|
||||||
.ring_bell => try self.ringBell(target),
|
.ring_bell => try self.ringBell(target),
|
||||||
.toggle_command_palette => try self.toggleCommandPalette(target),
|
.toggle_command_palette => try self.toggleCommandPalette(target),
|
||||||
|
.open_url => self.openUrl(value),
|
||||||
|
|
||||||
// Unimplemented
|
// Unimplemented
|
||||||
.close_all_windows,
|
.close_all_windows,
|
||||||
@ -1757,3 +1758,34 @@ fn initActions(self: *App) void {
|
|||||||
action_map.addAction(action.as(gio.Action));
|
action_map.addAction(action.as(gio.Action));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn openConfig(self: *App) !bool {
|
||||||
|
// Get the config file path
|
||||||
|
const alloc = self.core_app.alloc;
|
||||||
|
const path = configpkg.edit.openPath(alloc) catch |err| {
|
||||||
|
log.warn("error getting config file path: {}", .{err});
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
defer alloc.free(path);
|
||||||
|
|
||||||
|
// Open it using openURL. "path" isn't actually a URL but
|
||||||
|
// at the time of writing that works just fine for GTK.
|
||||||
|
self.openUrl(.{ .kind = .text, .url = path });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn openUrl(
|
||||||
|
app: *App,
|
||||||
|
value: apprt.action.OpenUrl,
|
||||||
|
) void {
|
||||||
|
// TODO: use https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.OpenURI.html
|
||||||
|
|
||||||
|
// Fallback to the minimal cross-platform way of opening a URL.
|
||||||
|
// This is always a safe fallback and enables for example Windows
|
||||||
|
// to open URLs (GTK on Windows via WSL is a thing).
|
||||||
|
internal_os.open(
|
||||||
|
app.core_app.alloc,
|
||||||
|
value.kind,
|
||||||
|
value.url,
|
||||||
|
) catch |err| log.warn("unable to open url: {}", .{err});
|
||||||
|
}
|
||||||
|
@ -214,6 +214,7 @@ pub fn init(self: *Window, app: *App) !void {
|
|||||||
{
|
{
|
||||||
const btn = gtk.MenuButton.new();
|
const btn = gtk.MenuButton.new();
|
||||||
btn.as(gtk.Widget).setTooltipText(i18n._("Main Menu"));
|
btn.as(gtk.Widget).setTooltipText(i18n._("Main Menu"));
|
||||||
|
btn.as(gtk.Widget).setCanFocus(0);
|
||||||
btn.setIconName("open-menu-symbolic");
|
btn.setIconName("open-menu-symbolic");
|
||||||
btn.setPopover(self.titlebar_menu.asWidget());
|
btn.setPopover(self.titlebar_menu.asWidget());
|
||||||
_ = gobject.Object.signals.notify.connect(
|
_ = gobject.Object.signals.notify.connect(
|
||||||
@ -253,6 +254,7 @@ pub fn init(self: *Window, app: *App) !void {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
btn.setCanFocus(0);
|
||||||
btn.setFocusOnClick(0);
|
btn.setFocusOnClick(0);
|
||||||
self.headerbar.packEnd(btn);
|
self.headerbar.packEnd(btn);
|
||||||
}
|
}
|
||||||
|
@ -88,6 +88,19 @@ pub fn init(
|
|||||||
|
|
||||||
// Our step to open the resulting Ghostty app.
|
// Our step to open the resulting Ghostty app.
|
||||||
const open = open: {
|
const open = open: {
|
||||||
|
const disable_save_state = RunStep.create(b, "disable save state");
|
||||||
|
disable_save_state.has_side_effects = true;
|
||||||
|
disable_save_state.addArgs(&.{
|
||||||
|
"/usr/libexec/PlistBuddy",
|
||||||
|
"-c",
|
||||||
|
// We'll have to change this to `Set` if we ever put this
|
||||||
|
// into our Info.plist.
|
||||||
|
"Add :NSQuitAlwaysKeepsWindows bool false",
|
||||||
|
b.fmt("{s}/Contents/Info.plist", .{app_path}),
|
||||||
|
});
|
||||||
|
disable_save_state.expectExitCode(0);
|
||||||
|
disable_save_state.step.dependOn(&build.step);
|
||||||
|
|
||||||
const open = RunStep.create(b, "run Ghostty app");
|
const open = RunStep.create(b, "run Ghostty app");
|
||||||
open.has_side_effects = true;
|
open.has_side_effects = true;
|
||||||
open.cwd = b.path("");
|
open.cwd = b.path("");
|
||||||
@ -98,22 +111,17 @@ pub fn init(
|
|||||||
|
|
||||||
// Open depends on the app
|
// Open depends on the app
|
||||||
open.step.dependOn(&build.step);
|
open.step.dependOn(&build.step);
|
||||||
|
open.step.dependOn(&disable_save_state.step);
|
||||||
|
|
||||||
// This overrides our default behavior and forces logs to show
|
// This overrides our default behavior and forces logs to show
|
||||||
// up on stderr (in addition to the centralized macOS log).
|
// up on stderr (in addition to the centralized macOS log).
|
||||||
open.setEnvironmentVariable("GHOSTTY_LOG", "1");
|
open.setEnvironmentVariable("GHOSTTY_LOG", "1");
|
||||||
|
|
||||||
// This is hack so that we can activate the app and bring it to
|
// Configure how we're launching
|
||||||
// the front forcibly even though we're executing directly
|
open.setEnvironmentVariable("GHOSTTY_MAC_LAUNCH_SOURCE", "zig_run");
|
||||||
// via the binary and not launch services.
|
|
||||||
open.setEnvironmentVariable("GHOSTTY_MAC_ACTIVATE", "1");
|
|
||||||
|
|
||||||
if (b.args) |args| {
|
if (b.args) |args| {
|
||||||
open.addArgs(args);
|
open.addArgs(args);
|
||||||
} else {
|
|
||||||
// This tricks the app into thinking it's running from the
|
|
||||||
// app bundle so we don't execute our CLI mode.
|
|
||||||
open.setEnvironmentVariable("GHOSTTY_MAC_APP", "1");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break :open open;
|
break :open open;
|
||||||
|
@ -760,6 +760,9 @@ pub fn gtkDistResources(
|
|||||||
});
|
});
|
||||||
const resources_c = generate_c.addOutputFileArg("ghostty_resources.c");
|
const resources_c = generate_c.addOutputFileArg("ghostty_resources.c");
|
||||||
generate_c.addFileArg(gresource_xml);
|
generate_c.addFileArg(gresource_xml);
|
||||||
|
for (gresource.dependencies) |file| {
|
||||||
|
generate_c.addFileInput(b.path(file));
|
||||||
|
}
|
||||||
|
|
||||||
const generate_h = b.addSystemCommand(&.{
|
const generate_h = b.addSystemCommand(&.{
|
||||||
"glib-compile-resources",
|
"glib-compile-resources",
|
||||||
@ -770,6 +773,9 @@ pub fn gtkDistResources(
|
|||||||
});
|
});
|
||||||
const resources_h = generate_h.addOutputFileArg("ghostty_resources.h");
|
const resources_h = generate_h.addOutputFileArg("ghostty_resources.h");
|
||||||
generate_h.addFileArg(gresource_xml);
|
generate_h.addFileArg(gresource_xml);
|
||||||
|
for (gresource.dependencies) |file| {
|
||||||
|
generate_h.addFileInput(b.path(file));
|
||||||
|
}
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.resources_c = .{
|
.resources_c = .{
|
||||||
|
@ -15,8 +15,6 @@ pub const Options = struct {};
|
|||||||
/// The `version` command is used to display information about Ghostty. Recognized as
|
/// The `version` command is used to display information about Ghostty. Recognized as
|
||||||
/// either `+version` or `--version`.
|
/// either `+version` or `--version`.
|
||||||
pub fn run(alloc: Allocator) !u8 {
|
pub fn run(alloc: Allocator) !u8 {
|
||||||
_ = alloc;
|
|
||||||
|
|
||||||
const stdout = std.io.getStdOut().writer();
|
const stdout = std.io.getStdOut().writer();
|
||||||
const tty = std.io.getStdOut().isTty();
|
const tty = std.io.getStdOut().isTty();
|
||||||
|
|
||||||
@ -34,32 +32,37 @@ pub fn run(alloc: Allocator) !u8 {
|
|||||||
try stdout.print(" - channel: {s}\n", .{@tagName(build_config.release_channel)});
|
try stdout.print(" - channel: {s}\n", .{@tagName(build_config.release_channel)});
|
||||||
|
|
||||||
try stdout.print("Build Config\n", .{});
|
try stdout.print("Build Config\n", .{});
|
||||||
try stdout.print(" - Zig version: {s}\n", .{builtin.zig_version_string});
|
try stdout.print(" - Zig version : {s}\n", .{builtin.zig_version_string});
|
||||||
try stdout.print(" - build mode : {}\n", .{builtin.mode});
|
try stdout.print(" - build mode : {}\n", .{builtin.mode});
|
||||||
try stdout.print(" - app runtime: {}\n", .{build_config.app_runtime});
|
try stdout.print(" - app runtime : {}\n", .{build_config.app_runtime});
|
||||||
try stdout.print(" - font engine: {}\n", .{build_config.font_backend});
|
try stdout.print(" - font engine : {}\n", .{build_config.font_backend});
|
||||||
try stdout.print(" - renderer : {}\n", .{renderer.Renderer});
|
try stdout.print(" - renderer : {}\n", .{renderer.Renderer});
|
||||||
try stdout.print(" - libxev : {s}\n", .{@tagName(xev.backend)});
|
try stdout.print(" - libxev : {s}\n", .{@tagName(xev.backend)});
|
||||||
if (comptime build_config.app_runtime == .gtk) {
|
if (comptime build_config.app_runtime == .gtk) {
|
||||||
try stdout.print(" - desktop env: {s}\n", .{@tagName(internal_os.desktopEnvironment())});
|
if (comptime builtin.os.tag == .linux) {
|
||||||
try stdout.print(" - GTK version:\n", .{});
|
const kernel_info = internal_os.getKernelInfo(alloc);
|
||||||
try stdout.print(" build : {}\n", .{gtk_version.comptime_version});
|
defer if (kernel_info) |k| alloc.free(k);
|
||||||
try stdout.print(" runtime : {}\n", .{gtk_version.getRuntimeVersion()});
|
try stdout.print(" - kernel version: {s}\n", .{kernel_info orelse "Kernel information unavailable"});
|
||||||
try stdout.print(" - libadwaita : enabled\n", .{});
|
}
|
||||||
try stdout.print(" build : {}\n", .{adw_version.comptime_version});
|
try stdout.print(" - desktop env : {s}\n", .{@tagName(internal_os.desktopEnvironment())});
|
||||||
try stdout.print(" runtime : {}\n", .{adw_version.getRuntimeVersion()});
|
try stdout.print(" - GTK version :\n", .{});
|
||||||
|
try stdout.print(" build : {}\n", .{gtk_version.comptime_version});
|
||||||
|
try stdout.print(" runtime : {}\n", .{gtk_version.getRuntimeVersion()});
|
||||||
|
try stdout.print(" - libadwaita : enabled\n", .{});
|
||||||
|
try stdout.print(" build : {}\n", .{adw_version.comptime_version});
|
||||||
|
try stdout.print(" runtime : {}\n", .{adw_version.getRuntimeVersion()});
|
||||||
if (comptime build_options.x11) {
|
if (comptime build_options.x11) {
|
||||||
try stdout.print(" - libX11 : enabled\n", .{});
|
try stdout.print(" - libX11 : enabled\n", .{});
|
||||||
} else {
|
} else {
|
||||||
try stdout.print(" - libX11 : disabled\n", .{});
|
try stdout.print(" - libX11 : disabled\n", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
// We say `libwayland` since it is possible to build Ghostty without
|
// We say `libwayland` since it is possible to build Ghostty without
|
||||||
// Wayland integration but with Wayland-enabled GTK
|
// Wayland integration but with Wayland-enabled GTK
|
||||||
if (comptime build_options.wayland) {
|
if (comptime build_options.wayland) {
|
||||||
try stdout.print(" - libwayland : enabled\n", .{});
|
try stdout.print(" - libwayland : enabled\n", .{});
|
||||||
} else {
|
} else {
|
||||||
try stdout.print(" - libwayland : disabled\n", .{});
|
try stdout.print(" - libwayland : disabled\n", .{});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -14,6 +14,7 @@ pub const entryFormatter = formatter.entryFormatter;
|
|||||||
pub const formatEntry = formatter.formatEntry;
|
pub const formatEntry = formatter.formatEntry;
|
||||||
|
|
||||||
// Field types
|
// Field types
|
||||||
|
pub const BoldColor = Config.BoldColor;
|
||||||
pub const ClipboardAccess = Config.ClipboardAccess;
|
pub const ClipboardAccess = Config.ClipboardAccess;
|
||||||
pub const Command = Config.Command;
|
pub const Command = Config.Command;
|
||||||
pub const ConfirmCloseSurface = Config.ConfirmCloseSurface;
|
pub const ConfirmCloseSurface = Config.ConfirmCloseSurface;
|
||||||
@ -37,6 +38,7 @@ pub const ShellIntegrationFeatures = Config.ShellIntegrationFeatures;
|
|||||||
pub const WindowPaddingColor = Config.WindowPaddingColor;
|
pub const WindowPaddingColor = Config.WindowPaddingColor;
|
||||||
pub const BackgroundImagePosition = Config.BackgroundImagePosition;
|
pub const BackgroundImagePosition = Config.BackgroundImagePosition;
|
||||||
pub const BackgroundImageFit = Config.BackgroundImageFit;
|
pub const BackgroundImageFit = Config.BackgroundImageFit;
|
||||||
|
pub const LinkPreviews = Config.LinkPreviews;
|
||||||
|
|
||||||
// Alternate APIs
|
// Alternate APIs
|
||||||
pub const CAPI = @import("config/CAPI.zig");
|
pub const CAPI = @import("config/CAPI.zig");
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const assert = std.debug.assert;
|
||||||
const cli = @import("../cli.zig");
|
const cli = @import("../cli.zig");
|
||||||
const inputpkg = @import("../input.zig");
|
const inputpkg = @import("../input.zig");
|
||||||
const global = &@import("../global.zig").state;
|
const state = &@import("../global.zig").state;
|
||||||
|
const c = @import("../main_c.zig");
|
||||||
|
|
||||||
const Config = @import("Config.zig");
|
const Config = @import("Config.zig");
|
||||||
const c_get = @import("c_get.zig");
|
const c_get = @import("c_get.zig");
|
||||||
@ -12,14 +14,14 @@ const log = std.log.scoped(.config);
|
|||||||
|
|
||||||
/// Create a new configuration filled with the initial default values.
|
/// Create a new configuration filled with the initial default values.
|
||||||
export fn ghostty_config_new() ?*Config {
|
export fn ghostty_config_new() ?*Config {
|
||||||
const result = global.alloc.create(Config) catch |err| {
|
const result = state.alloc.create(Config) catch |err| {
|
||||||
log.err("error allocating config err={}", .{err});
|
log.err("error allocating config err={}", .{err});
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
result.* = Config.default(global.alloc) catch |err| {
|
result.* = Config.default(state.alloc) catch |err| {
|
||||||
log.err("error creating config err={}", .{err});
|
log.err("error creating config err={}", .{err});
|
||||||
global.alloc.destroy(result);
|
state.alloc.destroy(result);
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -29,20 +31,20 @@ export fn ghostty_config_new() ?*Config {
|
|||||||
export fn ghostty_config_free(ptr: ?*Config) void {
|
export fn ghostty_config_free(ptr: ?*Config) void {
|
||||||
if (ptr) |v| {
|
if (ptr) |v| {
|
||||||
v.deinit();
|
v.deinit();
|
||||||
global.alloc.destroy(v);
|
state.alloc.destroy(v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deep clone the configuration.
|
/// Deep clone the configuration.
|
||||||
export fn ghostty_config_clone(self: *Config) ?*Config {
|
export fn ghostty_config_clone(self: *Config) ?*Config {
|
||||||
const result = global.alloc.create(Config) catch |err| {
|
const result = state.alloc.create(Config) catch |err| {
|
||||||
log.err("error allocating config err={}", .{err});
|
log.err("error allocating config err={}", .{err});
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
result.* = self.clone(global.alloc) catch |err| {
|
result.* = self.clone(state.alloc) catch |err| {
|
||||||
log.err("error cloning config err={}", .{err});
|
log.err("error cloning config err={}", .{err});
|
||||||
global.alloc.destroy(result);
|
state.alloc.destroy(result);
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -51,7 +53,7 @@ export fn ghostty_config_clone(self: *Config) ?*Config {
|
|||||||
|
|
||||||
/// Load the configuration from the CLI args.
|
/// Load the configuration from the CLI args.
|
||||||
export fn ghostty_config_load_cli_args(self: *Config) void {
|
export fn ghostty_config_load_cli_args(self: *Config) void {
|
||||||
self.loadCliArgs(global.alloc) catch |err| {
|
self.loadCliArgs(state.alloc) catch |err| {
|
||||||
log.err("error loading config err={}", .{err});
|
log.err("error loading config err={}", .{err});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -60,7 +62,7 @@ export fn ghostty_config_load_cli_args(self: *Config) void {
|
|||||||
/// is usually done first. The default file locations are locations
|
/// is usually done first. The default file locations are locations
|
||||||
/// such as the home directory.
|
/// such as the home directory.
|
||||||
export fn ghostty_config_load_default_files(self: *Config) void {
|
export fn ghostty_config_load_default_files(self: *Config) void {
|
||||||
self.loadDefaultFiles(global.alloc) catch |err| {
|
self.loadDefaultFiles(state.alloc) catch |err| {
|
||||||
log.err("error loading config err={}", .{err});
|
log.err("error loading config err={}", .{err});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -69,7 +71,7 @@ export fn ghostty_config_load_default_files(self: *Config) void {
|
|||||||
/// file locations in the previously loaded configuration. This will
|
/// file locations in the previously loaded configuration. This will
|
||||||
/// recursively continue to load up to a built-in limit.
|
/// recursively continue to load up to a built-in limit.
|
||||||
export fn ghostty_config_load_recursive_files(self: *Config) void {
|
export fn ghostty_config_load_recursive_files(self: *Config) void {
|
||||||
self.loadRecursiveFiles(global.alloc) catch |err| {
|
self.loadRecursiveFiles(state.alloc) catch |err| {
|
||||||
log.err("error loading config err={}", .{err});
|
log.err("error loading config err={}", .{err});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -122,10 +124,13 @@ export fn ghostty_config_get_diagnostic(self: *Config, idx: u32) Diagnostic {
|
|||||||
return .{ .message = message.ptr };
|
return .{ .message = message.ptr };
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn ghostty_config_open() void {
|
export fn ghostty_config_open_path() c.String {
|
||||||
edit.open(global.alloc) catch |err| {
|
const path = edit.openPath(state.alloc) catch |err| {
|
||||||
log.err("error opening config in editor err={}", .{err});
|
log.err("error opening config in editor err={}", .{err});
|
||||||
|
return .empty;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return .fromSlice(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sync with ghostty_diagnostic_s
|
/// Sync with ghostty_diagnostic_s
|
||||||
|
@ -69,6 +69,10 @@ pub const compatibility = std.StaticStringMap(
|
|||||||
// this behavior. This applies to selection too.
|
// this behavior. This applies to selection too.
|
||||||
.{ "cursor-invert-fg-bg", compatCursorInvertFgBg },
|
.{ "cursor-invert-fg-bg", compatCursorInvertFgBg },
|
||||||
.{ "selection-invert-fg-bg", compatSelectionInvertFgBg },
|
.{ "selection-invert-fg-bg", compatSelectionInvertFgBg },
|
||||||
|
|
||||||
|
// Ghostty 1.2 merged `bold-is-bright` into the new `bold-color`
|
||||||
|
// by setting the value to "bright".
|
||||||
|
.{ "bold-is-bright", compatBoldIsBright },
|
||||||
});
|
});
|
||||||
|
|
||||||
/// The font families to use.
|
/// The font families to use.
|
||||||
@ -435,7 +439,7 @@ pub const compatibility = std.StaticStringMap(
|
|||||||
/// * `hinting` - Enable or disable hinting. Enabled by default.
|
/// * `hinting` - Enable or disable hinting. Enabled by default.
|
||||||
///
|
///
|
||||||
/// * `force-autohint` - Always use the freetype auto-hinter instead of
|
/// * `force-autohint` - Always use the freetype auto-hinter instead of
|
||||||
/// the font's native hinter. Enabled by default.
|
/// the font's native hinter. Disabled by default.
|
||||||
///
|
///
|
||||||
/// * `monochrome` - Instructs renderer to use 1-bit monochrome rendering.
|
/// * `monochrome` - Instructs renderer to use 1-bit monochrome rendering.
|
||||||
/// This will disable anti-aliasing, and probably not look very good unless
|
/// This will disable anti-aliasing, and probably not look very good unless
|
||||||
@ -1046,6 +1050,14 @@ link: RepeatableLink = .{},
|
|||||||
/// `link`). If you want to customize URL matching, use `link` and disable this.
|
/// `link`). If you want to customize URL matching, use `link` and disable this.
|
||||||
@"link-url": bool = true,
|
@"link-url": bool = true,
|
||||||
|
|
||||||
|
/// Show link previews for a matched URL.
|
||||||
|
///
|
||||||
|
/// When true, link previews are shown for all matched URLs. When false, link
|
||||||
|
/// previews are never shown. When set to "osc8", link previews are only shown
|
||||||
|
/// for hyperlinks created with the OSC 8 sequence (in this case, the link text
|
||||||
|
/// can differ from the link destination).
|
||||||
|
@"link-previews": LinkPreviews = .true,
|
||||||
|
|
||||||
/// Whether to start the window in a maximized state. This setting applies
|
/// Whether to start the window in a maximized state. This setting applies
|
||||||
/// to new windows and does not apply to tabs, splits, etc. However, this setting
|
/// to new windows and does not apply to tabs, splits, etc. However, this setting
|
||||||
/// will apply to all new windows, not just the first one.
|
/// will apply to all new windows, not just the first one.
|
||||||
@ -2815,8 +2827,24 @@ else
|
|||||||
/// notifications using certain escape sequences such as OSC 9 or OSC 777.
|
/// notifications using certain escape sequences such as OSC 9 or OSC 777.
|
||||||
@"desktop-notifications": bool = true,
|
@"desktop-notifications": bool = true,
|
||||||
|
|
||||||
/// If `true`, the bold text will use the bright color palette.
|
/// Modifies the color used for bold text in the terminal.
|
||||||
@"bold-is-bright": bool = false,
|
///
|
||||||
|
/// This can be set to a specific color, using the same format as
|
||||||
|
/// `background` or `foreground` (e.g. `#RRGGBB` but other formats
|
||||||
|
/// are also supported; see the aforementioned documentation). If a
|
||||||
|
/// specific color is set, this color will always be used for all
|
||||||
|
/// bold text regardless of the terminal's color scheme.
|
||||||
|
///
|
||||||
|
/// This can also be set to `bright`, which uses the bright color palette
|
||||||
|
/// for bold text. For example, if the text is red, then the bold will
|
||||||
|
/// use the bright red color. The terminal palette is set with `palette`
|
||||||
|
/// but can also be overridden by the terminal application itself using
|
||||||
|
/// escape sequences such as OSC 4. (Since Ghostty 1.2.0, the previous
|
||||||
|
/// configuration `bold-is-bright` is deprecated and replaced by this
|
||||||
|
/// usage).
|
||||||
|
///
|
||||||
|
/// Available since Ghostty 1.2.0.
|
||||||
|
@"bold-color": ?BoldColor = null,
|
||||||
|
|
||||||
/// This will be used to set the `TERM` environment variable.
|
/// This will be used to set the `TERM` environment variable.
|
||||||
/// HACK: We set this with an `xterm` prefix because vim uses that to enable key
|
/// HACK: We set this with an `xterm` prefix because vim uses that to enable key
|
||||||
@ -3921,6 +3949,23 @@ fn compatSelectionInvertFgBg(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compatBoldIsBright(
|
||||||
|
self: *Config,
|
||||||
|
alloc: Allocator,
|
||||||
|
key: []const u8,
|
||||||
|
value_: ?[]const u8,
|
||||||
|
) bool {
|
||||||
|
_ = alloc;
|
||||||
|
assert(std.mem.eql(u8, key, "bold-is-bright"));
|
||||||
|
|
||||||
|
const set = cli.args.parseBool(value_ orelse "t") catch return false;
|
||||||
|
if (set) {
|
||||||
|
self.@"bold-color" = .bright;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a shallow copy of this config. This will share all the memory
|
/// Create a shallow copy of this config. This will share all the memory
|
||||||
/// allocated with the previous config but will have a new arena for
|
/// allocated with the previous config but will have a new arena for
|
||||||
/// any changes or new allocations. The config should have `deinit`
|
/// any changes or new allocations. The config should have `deinit`
|
||||||
@ -4345,6 +4390,12 @@ pub const WindowSubtitle = enum {
|
|||||||
@"working-directory",
|
@"working-directory",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const LinkPreviews = enum {
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
osc8,
|
||||||
|
};
|
||||||
|
|
||||||
/// Color represents a color using RGB.
|
/// Color represents a color using RGB.
|
||||||
///
|
///
|
||||||
/// This is a packed struct so that the C API to read color values just
|
/// This is a packed struct so that the C API to read color values just
|
||||||
@ -4542,6 +4593,58 @@ pub const TerminalColor = union(enum) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Represents color values that can be used for bold. See `bold-color`.
|
||||||
|
pub const BoldColor = union(enum) {
|
||||||
|
color: Color,
|
||||||
|
bright,
|
||||||
|
|
||||||
|
pub fn parseCLI(input_: ?[]const u8) !BoldColor {
|
||||||
|
const input = input_ orelse return error.ValueRequired;
|
||||||
|
if (std.mem.eql(u8, input, "bright")) return .bright;
|
||||||
|
return .{ .color = try Color.parseCLI(input) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Used by Formatter
|
||||||
|
pub fn formatEntry(self: BoldColor, formatter: anytype) !void {
|
||||||
|
switch (self) {
|
||||||
|
.color => try self.color.formatEntry(formatter),
|
||||||
|
.bright => try formatter.formatEntry(
|
||||||
|
[:0]const u8,
|
||||||
|
@tagName(self),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "parseCLI" {
|
||||||
|
const testing = std.testing;
|
||||||
|
|
||||||
|
try testing.expectEqual(
|
||||||
|
BoldColor{ .color = Color{ .r = 78, .g = 42, .b = 132 } },
|
||||||
|
try BoldColor.parseCLI("#4e2a84"),
|
||||||
|
);
|
||||||
|
try testing.expectEqual(
|
||||||
|
BoldColor{ .color = Color{ .r = 0, .g = 0, .b = 0 } },
|
||||||
|
try BoldColor.parseCLI("black"),
|
||||||
|
);
|
||||||
|
try testing.expectEqual(
|
||||||
|
BoldColor.bright,
|
||||||
|
try BoldColor.parseCLI("bright"),
|
||||||
|
);
|
||||||
|
|
||||||
|
try testing.expectError(error.InvalidValue, BoldColor.parseCLI("a"));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "formatConfig" {
|
||||||
|
const testing = std.testing;
|
||||||
|
var buf = std.ArrayList(u8).init(testing.allocator);
|
||||||
|
defer buf.deinit();
|
||||||
|
|
||||||
|
var sc: BoldColor = .bright;
|
||||||
|
try sc.formatEntry(formatterpkg.entryFormatter("a", buf.writer()));
|
||||||
|
try testing.expectEqualSlices(u8, "a = bright\n", buf.items);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pub const ColorList = struct {
|
pub const ColorList = struct {
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
@ -6552,8 +6655,9 @@ pub const RepeatableCommand = struct {
|
|||||||
try list.parseCLI(alloc, "title:Foo,action:ignore");
|
try list.parseCLI(alloc, "title:Foo,action:ignore");
|
||||||
try list.parseCLI(alloc, "title:Bar,description:bobr,action:text:ale bydle");
|
try list.parseCLI(alloc, "title:Bar,description:bobr,action:text:ale bydle");
|
||||||
try list.parseCLI(alloc, "title:Quux,description:boo,action:increase_font_size:2.5");
|
try list.parseCLI(alloc, "title:Quux,description:boo,action:increase_font_size:2.5");
|
||||||
|
try list.parseCLI(alloc, "title:Baz,description:Raspberry Pie,action:set_font_size:3.14");
|
||||||
|
|
||||||
try testing.expectEqual(@as(usize, 3), list.value.items.len);
|
try testing.expectEqual(@as(usize, 4), list.value.items.len);
|
||||||
|
|
||||||
try testing.expectEqual(inputpkg.Binding.Action.ignore, list.value.items[0].action);
|
try testing.expectEqual(inputpkg.Binding.Action.ignore, list.value.items[0].action);
|
||||||
try testing.expectEqualStrings("Foo", list.value.items[0].title);
|
try testing.expectEqualStrings("Foo", list.value.items[0].title);
|
||||||
@ -6570,6 +6674,13 @@ pub const RepeatableCommand = struct {
|
|||||||
try testing.expectEqualStrings("Quux", list.value.items[2].title);
|
try testing.expectEqualStrings("Quux", list.value.items[2].title);
|
||||||
try testing.expectEqualStrings("boo", list.value.items[2].description);
|
try testing.expectEqualStrings("boo", list.value.items[2].description);
|
||||||
|
|
||||||
|
try testing.expectEqual(
|
||||||
|
inputpkg.Binding.Action{ .set_font_size = 3.14 },
|
||||||
|
list.value.items[3].action,
|
||||||
|
);
|
||||||
|
try testing.expectEqualStrings("Baz", list.value.items[3].title);
|
||||||
|
try testing.expectEqualStrings("Raspberry Pie", list.value.items[3].description);
|
||||||
|
|
||||||
try list.parseCLI(alloc, "");
|
try list.parseCLI(alloc, "");
|
||||||
try testing.expectEqual(@as(usize, 0), list.value.items.len);
|
try testing.expectEqual(@as(usize, 0), list.value.items.len);
|
||||||
}
|
}
|
||||||
@ -7105,7 +7216,7 @@ pub const FreetypeLoadFlags = packed struct {
|
|||||||
// for Freetype itself. Ghostty hasn't made any opinionated changes
|
// for Freetype itself. Ghostty hasn't made any opinionated changes
|
||||||
// to these defaults.
|
// to these defaults.
|
||||||
hinting: bool = true,
|
hinting: bool = true,
|
||||||
@"force-autohint": bool = true,
|
@"force-autohint": bool = false,
|
||||||
monochrome: bool = false,
|
monochrome: bool = false,
|
||||||
autohint: bool = true,
|
autohint: bool = true,
|
||||||
};
|
};
|
||||||
@ -8235,3 +8346,23 @@ test "compatibility: removed selection-invert-fg-bg" {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "compatibility: removed bold-is-bright" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
|
||||||
|
{
|
||||||
|
var cfg = try Config.default(alloc);
|
||||||
|
defer cfg.deinit();
|
||||||
|
var it: TestIterator = .{ .data = &.{
|
||||||
|
"--bold-is-bright",
|
||||||
|
} };
|
||||||
|
try cfg.loadIter(alloc, &it);
|
||||||
|
try cfg.finalize();
|
||||||
|
|
||||||
|
try testing.expectEqual(
|
||||||
|
BoldColor.bright,
|
||||||
|
cfg.@"bold-color",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -5,18 +5,19 @@ const Allocator = std.mem.Allocator;
|
|||||||
const ArenaAllocator = std.heap.ArenaAllocator;
|
const ArenaAllocator = std.heap.ArenaAllocator;
|
||||||
const internal_os = @import("../os/main.zig");
|
const internal_os = @import("../os/main.zig");
|
||||||
|
|
||||||
/// Open the configuration in the OS default editor according to the default
|
/// The path to the configuration that should be opened for editing.
|
||||||
/// paths the main config file could be in.
|
|
||||||
///
|
///
|
||||||
/// On Linux, this will open the file at the XDG config path. This is the
|
/// On Linux, this will use the file at the XDG config path. This is the
|
||||||
/// only valid path for Linux so we don't need to check for other paths.
|
/// only valid path for Linux so we don't need to check for other paths.
|
||||||
///
|
///
|
||||||
/// On macOS, both XDG and AppSupport paths are valid. Because Ghostty
|
/// On macOS, both XDG and AppSupport paths are valid. Because Ghostty
|
||||||
/// prioritizes AppSupport over XDG, we will open AppSupport if it exists,
|
/// prioritizes AppSupport over XDG, we will use AppSupport if it exists,
|
||||||
/// followed by XDG if it exists, and finally AppSupport if neither exist.
|
/// followed by XDG if it exists, and finally AppSupport if neither exist.
|
||||||
/// For the existence check, we also prefer non-empty files over empty
|
/// For the existence check, we also prefer non-empty files over empty
|
||||||
/// files.
|
/// files.
|
||||||
pub fn open(alloc_gpa: Allocator) !void {
|
///
|
||||||
|
/// The returned value is allocated using the provided allocator.
|
||||||
|
pub fn openPath(alloc_gpa: Allocator) ![:0]const u8 {
|
||||||
// Use an arena to make memory management easier in here.
|
// Use an arena to make memory management easier in here.
|
||||||
var arena = ArenaAllocator.init(alloc_gpa);
|
var arena = ArenaAllocator.init(alloc_gpa);
|
||||||
defer arena.deinit();
|
defer arena.deinit();
|
||||||
@ -41,7 +42,7 @@ pub fn open(alloc_gpa: Allocator) !void {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
try internal_os.open(alloc_gpa, .text, config_path);
|
return try alloc_gpa.dupeZ(u8, config_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the config path to use for open for the current OS.
|
/// Returns the config path to use for open for the current OS.
|
||||||
|
@ -69,10 +69,14 @@ pub fn deinit(self: *Collection, alloc: Allocator) void {
|
|||||||
if (self.load_options) |*v| v.deinit(alloc);
|
if (self.load_options) |*v| v.deinit(alloc);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const AddError = Allocator.Error || error{
|
pub const AddError =
|
||||||
CollectionFull,
|
Allocator.Error ||
|
||||||
DeferredLoadingUnavailable,
|
AdjustSizeError ||
|
||||||
};
|
error{
|
||||||
|
CollectionFull,
|
||||||
|
DeferredLoadingUnavailable,
|
||||||
|
SetSizeFailed,
|
||||||
|
};
|
||||||
|
|
||||||
/// Add a face to the collection for the given style. This face will be added
|
/// Add a face to the collection for the given style. This face will be added
|
||||||
/// next in priority if others exist already, i.e. it'll be the _last_ to be
|
/// next in priority if others exist already, i.e. it'll be the _last_ to be
|
||||||
@ -81,10 +85,9 @@ pub const AddError = Allocator.Error || error{
|
|||||||
/// If no error is encountered then the collection takes ownership of the face,
|
/// If no error is encountered then the collection takes ownership of the face,
|
||||||
/// in which case face will be deallocated when the collection is deallocated.
|
/// in which case face will be deallocated when the collection is deallocated.
|
||||||
///
|
///
|
||||||
/// If a loaded face is added to the collection, it should be the same
|
/// If a loaded face is added to the collection, its size will be changed to
|
||||||
/// size as all the other faces in the collection. This function will not
|
/// match the size specified in load_options, adjusted for harmonization with
|
||||||
/// verify or modify the size until the size of the entire collection is
|
/// the primary face.
|
||||||
/// changed.
|
|
||||||
pub fn add(
|
pub fn add(
|
||||||
self: *Collection,
|
self: *Collection,
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
@ -103,9 +106,107 @@ pub fn add(
|
|||||||
return error.DeferredLoadingUnavailable;
|
return error.DeferredLoadingUnavailable;
|
||||||
|
|
||||||
try list.append(alloc, face);
|
try list.append(alloc, face);
|
||||||
|
|
||||||
|
var owned: *Entry = list.at(idx);
|
||||||
|
|
||||||
|
// If the face is already loaded, apply font size adjustment
|
||||||
|
// now, otherwise we'll apply it whenever we do load it.
|
||||||
|
if (owned.getLoaded()) |loaded| {
|
||||||
|
if (try self.adjustedSize(loaded)) |opts| {
|
||||||
|
loaded.setSize(opts.faceOptions()) catch return error.SetSizeFailed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return .{ .style = style, .idx = @intCast(idx) };
|
return .{ .style = style, .idx = @intCast(idx) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const AdjustSizeError = font.Face.GetMetricsError;
|
||||||
|
|
||||||
|
// Calculate a size for the provided face that will match it with the primary
|
||||||
|
// font, metrically, to improve consistency with fallback fonts. Right now we
|
||||||
|
// match the font based on the ex height, or the ideograph width if the font
|
||||||
|
// has ideographs in it.
|
||||||
|
//
|
||||||
|
// This returns null if load options is null or if self.load_options is null.
|
||||||
|
//
|
||||||
|
// This is very much like the `font-size-adjust` CSS property in how it works.
|
||||||
|
// ref: https://developer.mozilla.org/en-US/docs/Web/CSS/font-size-adjust
|
||||||
|
//
|
||||||
|
// TODO: In the future, provide config options that allow the user to select
|
||||||
|
// which metric should be matched for fallback fonts, instead of hard
|
||||||
|
// coding it as ex height.
|
||||||
|
pub fn adjustedSize(
|
||||||
|
self: *Collection,
|
||||||
|
face: *Face,
|
||||||
|
) AdjustSizeError!?LoadOptions {
|
||||||
|
const load_options = self.load_options orelse return null;
|
||||||
|
|
||||||
|
// We silently do nothing if we can't get the primary
|
||||||
|
// face, because this might be the primary face itself.
|
||||||
|
const primary_face = self.getFace(.{ .idx = 0 }) catch return null;
|
||||||
|
|
||||||
|
// We do nothing if the primary face and this face are the same.
|
||||||
|
if (@intFromPtr(primary_face) == @intFromPtr(face)) return null;
|
||||||
|
|
||||||
|
const primary_metrics = try primary_face.getMetrics();
|
||||||
|
const face_metrics = try face.getMetrics();
|
||||||
|
|
||||||
|
// We use the ex height to match our font sizes, so that the height of
|
||||||
|
// lower-case letters matches between all fonts in the fallback chain.
|
||||||
|
//
|
||||||
|
// We estimate ex height as 0.75 * cap height if it's not specifically
|
||||||
|
// provided, and we estimate cap height as 0.75 * ascent in the same case.
|
||||||
|
//
|
||||||
|
// If the fallback font has an ic_width we prefer that, for normalization
|
||||||
|
// of CJK font sizes when mixed with latin fonts.
|
||||||
|
//
|
||||||
|
// We estimate the ic_width as twice the cell width if it isn't provided.
|
||||||
|
var primary_cap = primary_metrics.cap_height orelse 0.0;
|
||||||
|
if (primary_cap <= 0) primary_cap = primary_metrics.ascent * 0.75;
|
||||||
|
|
||||||
|
var primary_ex = primary_metrics.ex_height orelse 0.0;
|
||||||
|
if (primary_ex <= 0) primary_ex = primary_cap * 0.75;
|
||||||
|
|
||||||
|
var primary_ic = primary_metrics.ic_width orelse 0.0;
|
||||||
|
if (primary_ic <= 0) primary_ic = primary_metrics.cell_width * 2;
|
||||||
|
|
||||||
|
var face_cap = face_metrics.cap_height orelse 0.0;
|
||||||
|
if (face_cap <= 0) face_cap = face_metrics.ascent * 0.75;
|
||||||
|
|
||||||
|
var face_ex = face_metrics.ex_height orelse 0.0;
|
||||||
|
if (face_ex <= 0) face_ex = face_cap * 0.75;
|
||||||
|
|
||||||
|
var face_ic = face_metrics.ic_width orelse 0.0;
|
||||||
|
if (face_ic <= 0) face_ic = face_metrics.cell_width * 2;
|
||||||
|
|
||||||
|
// If the line height of the scaled font would be larger than
|
||||||
|
// the line height of the primary font, we don't want that, so
|
||||||
|
// we take the minimum between matching the ic/ex and the line
|
||||||
|
// height.
|
||||||
|
//
|
||||||
|
// NOTE: We actually allow the line height to be up to 1.2
|
||||||
|
// times the primary line height because empirically
|
||||||
|
// this is usually fine and is better for CJK.
|
||||||
|
//
|
||||||
|
// TODO: We should probably provide a config option that lets
|
||||||
|
// the user pick what metric to use for size adjustment.
|
||||||
|
const scale = @min(
|
||||||
|
1.2 * primary_metrics.lineHeight() / face_metrics.lineHeight(),
|
||||||
|
if (face_metrics.ic_width != null)
|
||||||
|
primary_ic / face_ic
|
||||||
|
else
|
||||||
|
primary_ex / face_ex,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Make a copy of our load options, set the size to the size of
|
||||||
|
// the provided face, and then multiply that by our scaling factor.
|
||||||
|
var opts = load_options;
|
||||||
|
opts.size = face.size;
|
||||||
|
opts.size.points *= @as(f32, @floatCast(scale));
|
||||||
|
|
||||||
|
return opts;
|
||||||
|
}
|
||||||
|
|
||||||
/// Return the Face represented by a given Index. The returned pointer
|
/// Return the Face represented by a given Index. The returned pointer
|
||||||
/// is only valid as long as this collection is not modified.
|
/// is only valid as long as this collection is not modified.
|
||||||
///
|
///
|
||||||
@ -129,21 +230,38 @@ pub fn getFace(self: *Collection, index: Index) !*Face {
|
|||||||
break :item item;
|
break :item item;
|
||||||
};
|
};
|
||||||
|
|
||||||
return try self.getFaceFromEntry(item);
|
const face = try self.getFaceFromEntry(
|
||||||
|
item,
|
||||||
|
// We only want to adjust the size if this isn't the primary face.
|
||||||
|
index.style != .regular or index.idx > 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
return face;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the face from an entry.
|
/// Get the face from an entry.
|
||||||
///
|
///
|
||||||
/// This entry must not be an alias.
|
/// This entry must not be an alias.
|
||||||
fn getFaceFromEntry(self: *Collection, entry: *Entry) !*Face {
|
fn getFaceFromEntry(
|
||||||
|
self: *Collection,
|
||||||
|
entry: *Entry,
|
||||||
|
/// Whether to adjust the font size to match the primary face after loading.
|
||||||
|
adjust: bool,
|
||||||
|
) !*Face {
|
||||||
assert(entry.* != .alias);
|
assert(entry.* != .alias);
|
||||||
|
|
||||||
return switch (entry.*) {
|
return switch (entry.*) {
|
||||||
inline .deferred, .fallback_deferred => |*d, tag| deferred: {
|
inline .deferred, .fallback_deferred => |*d, tag| deferred: {
|
||||||
const opts = self.load_options orelse
|
const opts = self.load_options orelse
|
||||||
return error.DeferredLoadingUnavailable;
|
return error.DeferredLoadingUnavailable;
|
||||||
const face = try d.load(opts.library, opts.faceOptions());
|
var face = try d.load(opts.library, opts.faceOptions());
|
||||||
d.deinit();
|
d.deinit();
|
||||||
|
|
||||||
|
// If we need to adjust the size, do so.
|
||||||
|
if (adjust) if (try self.adjustedSize(&face)) |new_opts| {
|
||||||
|
try face.setSize(new_opts.faceOptions());
|
||||||
|
};
|
||||||
|
|
||||||
entry.* = switch (tag) {
|
entry.* = switch (tag) {
|
||||||
.deferred => .{ .loaded = face },
|
.deferred => .{ .loaded = face },
|
||||||
.fallback_deferred => .{ .fallback_loaded = face },
|
.fallback_deferred => .{ .fallback_loaded = face },
|
||||||
@ -247,7 +365,7 @@ pub fn completeStyles(
|
|||||||
while (it.next()) |entry| {
|
while (it.next()) |entry| {
|
||||||
// Load our face. If we fail to load it, we just skip it and
|
// Load our face. If we fail to load it, we just skip it and
|
||||||
// continue on to try the next one.
|
// continue on to try the next one.
|
||||||
const face = self.getFaceFromEntry(entry) catch |err| {
|
const face = self.getFaceFromEntry(entry, false) catch |err| {
|
||||||
log.warn("error loading regular entry={d} err={}", .{
|
log.warn("error loading regular entry={d} err={}", .{
|
||||||
it.index - 1,
|
it.index - 1,
|
||||||
err,
|
err,
|
||||||
@ -371,7 +489,7 @@ fn syntheticBold(self: *Collection, entry: *Entry) !Face {
|
|||||||
const opts = self.load_options orelse return error.DeferredLoadingUnavailable;
|
const opts = self.load_options orelse return error.DeferredLoadingUnavailable;
|
||||||
|
|
||||||
// Try to bold it.
|
// Try to bold it.
|
||||||
const regular = try self.getFaceFromEntry(entry);
|
const regular = try self.getFaceFromEntry(entry, false);
|
||||||
const face = try regular.syntheticBold(opts.faceOptions());
|
const face = try regular.syntheticBold(opts.faceOptions());
|
||||||
|
|
||||||
var buf: [256]u8 = undefined;
|
var buf: [256]u8 = undefined;
|
||||||
@ -391,7 +509,7 @@ fn syntheticItalic(self: *Collection, entry: *Entry) !Face {
|
|||||||
const opts = self.load_options orelse return error.DeferredLoadingUnavailable;
|
const opts = self.load_options orelse return error.DeferredLoadingUnavailable;
|
||||||
|
|
||||||
// Try to italicize it.
|
// Try to italicize it.
|
||||||
const regular = try self.getFaceFromEntry(entry);
|
const regular = try self.getFaceFromEntry(entry, false);
|
||||||
const face = try regular.syntheticItalic(opts.faceOptions());
|
const face = try regular.syntheticItalic(opts.faceOptions());
|
||||||
|
|
||||||
var buf: [256]u8 = undefined;
|
var buf: [256]u8 = undefined;
|
||||||
@ -420,9 +538,12 @@ pub fn setSize(self: *Collection, size: DesiredSize) !void {
|
|||||||
while (it.next()) |array| {
|
while (it.next()) |array| {
|
||||||
var entry_it = array.value.iterator(0);
|
var entry_it = array.value.iterator(0);
|
||||||
while (entry_it.next()) |entry| switch (entry.*) {
|
while (entry_it.next()) |entry| switch (entry.*) {
|
||||||
.loaded, .fallback_loaded => |*f| try f.setSize(
|
.loaded,
|
||||||
opts.faceOptions(),
|
.fallback_loaded,
|
||||||
),
|
=> |*f| {
|
||||||
|
const new_opts = try self.adjustedSize(f) orelse opts.*;
|
||||||
|
try f.setSize(new_opts.faceOptions());
|
||||||
|
},
|
||||||
|
|
||||||
// Deferred aren't loaded so we don't need to set their size.
|
// Deferred aren't loaded so we don't need to set their size.
|
||||||
// The size for when they're loaded is set since `opts` changed.
|
// The size for when they're loaded is set since `opts` changed.
|
||||||
@ -549,6 +670,16 @@ pub const Entry = union(enum) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If this face is loaded, or is an alias to a loaded face,
|
||||||
|
/// then this returns the `Face`, otherwise returns null.
|
||||||
|
pub fn getLoaded(self: *Entry) ?*Face {
|
||||||
|
return switch (self.*) {
|
||||||
|
.deferred, .fallback_deferred => null,
|
||||||
|
.loaded, .fallback_loaded => |*face| face,
|
||||||
|
.alias => |v| v.getLoaded(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// True if the entry is deferred.
|
/// True if the entry is deferred.
|
||||||
fn isDeferred(self: Entry) bool {
|
fn isDeferred(self: Entry) bool {
|
||||||
return switch (self) {
|
return switch (self) {
|
||||||
@ -906,12 +1037,13 @@ test "metrics" {
|
|||||||
|
|
||||||
var c = init();
|
var c = init();
|
||||||
defer c.deinit(alloc);
|
defer c.deinit(alloc);
|
||||||
c.load_options = .{ .library = lib };
|
const size: DesiredSize = .{ .points = 12, .xdpi = 96, .ydpi = 96 };
|
||||||
|
c.load_options = .{ .library = lib, .size = size };
|
||||||
|
|
||||||
_ = try c.add(alloc, .regular, .{ .loaded = try .init(
|
_ = try c.add(alloc, .regular, .{ .loaded = try .init(
|
||||||
lib,
|
lib,
|
||||||
testFont,
|
testFont,
|
||||||
.{ .size = .{ .points = 12, .xdpi = 96, .ydpi = 96 } },
|
.{ .size = size },
|
||||||
) });
|
) });
|
||||||
|
|
||||||
try c.updateMetrics();
|
try c.updateMetrics();
|
||||||
@ -958,3 +1090,62 @@ test "metrics" {
|
|||||||
.cursor_height = 34,
|
.cursor_height = 34,
|
||||||
}, c.metrics);
|
}, c.metrics);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Also test CJK fallback sizing, we don't currently have a CJK test font.
|
||||||
|
test "adjusted sizes" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
const testFont = font.embedded.inconsolata;
|
||||||
|
const fallback = font.embedded.monaspace_neon;
|
||||||
|
|
||||||
|
var lib = try Library.init(alloc);
|
||||||
|
defer lib.deinit();
|
||||||
|
|
||||||
|
var c = init();
|
||||||
|
defer c.deinit(alloc);
|
||||||
|
const size: DesiredSize = .{ .points = 12, .xdpi = 96, .ydpi = 96 };
|
||||||
|
c.load_options = .{ .library = lib, .size = size };
|
||||||
|
|
||||||
|
// Add our primary face.
|
||||||
|
_ = try c.add(alloc, .regular, .{ .loaded = try .init(
|
||||||
|
lib,
|
||||||
|
testFont,
|
||||||
|
.{ .size = size },
|
||||||
|
) });
|
||||||
|
|
||||||
|
try c.updateMetrics();
|
||||||
|
|
||||||
|
// Add the fallback face.
|
||||||
|
const fallback_idx = try c.add(alloc, .regular, .{ .loaded = try .init(
|
||||||
|
lib,
|
||||||
|
fallback,
|
||||||
|
.{ .size = size },
|
||||||
|
) });
|
||||||
|
|
||||||
|
// The ex heights should match.
|
||||||
|
{
|
||||||
|
const primary_metrics = try (try c.getFace(.{ .idx = 0 })).getMetrics();
|
||||||
|
const fallback_metrics = try (try c.getFace(fallback_idx)).getMetrics();
|
||||||
|
|
||||||
|
try std.testing.expectApproxEqAbs(
|
||||||
|
primary_metrics.ex_height.?,
|
||||||
|
fallback_metrics.ex_height.?,
|
||||||
|
// We accept anything within half a pixel.
|
||||||
|
0.5,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize should keep that relationship.
|
||||||
|
try c.setSize(.{ .points = 37, .xdpi = 96, .ydpi = 96 });
|
||||||
|
{
|
||||||
|
const primary_metrics = try (try c.getFace(.{ .idx = 0 })).getMetrics();
|
||||||
|
const fallback_metrics = try (try c.getFace(fallback_idx)).getMetrics();
|
||||||
|
|
||||||
|
try std.testing.expectApproxEqAbs(
|
||||||
|
primary_metrics.ex_height.?,
|
||||||
|
fallback_metrics.ex_height.?,
|
||||||
|
// We accept anything within half a pixel.
|
||||||
|
0.5,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -17,9 +17,3 @@ offset_y: i32,
|
|||||||
/// be normalized to be between 0 and 1 prior to use in shaders.
|
/// be normalized to be between 0 and 1 prior to use in shaders.
|
||||||
atlas_x: u32,
|
atlas_x: u32,
|
||||||
atlas_y: u32,
|
atlas_y: u32,
|
||||||
|
|
||||||
/// horizontal position to increase drawing position for strings
|
|
||||||
advance_x: f32,
|
|
||||||
|
|
||||||
/// Whether we drew this glyph ourselves with the sprite font.
|
|
||||||
sprite: bool = false,
|
|
||||||
|
@ -107,6 +107,19 @@ pub const FaceMetrics = struct {
|
|||||||
/// a provided ex height metric or measured from the height of the
|
/// a provided ex height metric or measured from the height of the
|
||||||
/// lowercase x glyph.
|
/// lowercase x glyph.
|
||||||
ex_height: ?f64 = null,
|
ex_height: ?f64 = null,
|
||||||
|
|
||||||
|
/// The width of the character "水" (CJK water ideograph, U+6C34),
|
||||||
|
/// if present. This is used for font size adjustment, to normalize
|
||||||
|
/// the width of CJK fonts mixed with latin fonts.
|
||||||
|
///
|
||||||
|
/// NOTE: IC = Ideograph Character
|
||||||
|
ic_width: ?f64 = null,
|
||||||
|
|
||||||
|
/// Convenience function for getting the line height
|
||||||
|
/// (ascent - descent + line_gap).
|
||||||
|
pub inline fn lineHeight(self: FaceMetrics) f64 {
|
||||||
|
return self.ascent - self.descent + self.line_gap;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Calculate our metrics based on values extracted from a font.
|
/// Calculate our metrics based on values extracted from a font.
|
||||||
|
@ -147,6 +147,9 @@ pub const RenderOptions = struct {
|
|||||||
/// Maximum ratio of width to height when resizing.
|
/// Maximum ratio of width to height when resizing.
|
||||||
max_xy_ratio: ?f64 = null,
|
max_xy_ratio: ?f64 = null,
|
||||||
|
|
||||||
|
/// Maximum number of cells horizontally to use.
|
||||||
|
max_constraint_width: u2 = 2,
|
||||||
|
|
||||||
pub const Size = enum {
|
pub const Size = enum {
|
||||||
/// Don't change the size of this glyph.
|
/// Don't change the size of this glyph.
|
||||||
none,
|
none,
|
||||||
@ -186,16 +189,26 @@ pub const RenderOptions = struct {
|
|||||||
pub fn constrain(
|
pub fn constrain(
|
||||||
self: Constraint,
|
self: Constraint,
|
||||||
glyph: GlyphSize,
|
glyph: GlyphSize,
|
||||||
/// Available width
|
/// Width of one cell.
|
||||||
cell_width: f64,
|
cell_width: f64,
|
||||||
/// Available height
|
/// Height of one cell.
|
||||||
cell_height: f64,
|
cell_height: f64,
|
||||||
|
/// Number of cells horizontally available for this glyph.
|
||||||
|
constraint_width: u2,
|
||||||
) GlyphSize {
|
) GlyphSize {
|
||||||
var g = glyph;
|
var g = glyph;
|
||||||
|
|
||||||
const w = cell_width -
|
const available_width =
|
||||||
self.pad_left * cell_width -
|
cell_width * @as(f64, @floatFromInt(
|
||||||
self.pad_right * cell_width;
|
@min(
|
||||||
|
self.max_constraint_width,
|
||||||
|
constraint_width,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
const w = available_width -
|
||||||
|
self.pad_left * available_width -
|
||||||
|
self.pad_right * available_width;
|
||||||
const h = cell_height -
|
const h = cell_height -
|
||||||
self.pad_top * cell_height -
|
self.pad_top * cell_height -
|
||||||
self.pad_bottom * cell_height;
|
self.pad_bottom * cell_height;
|
||||||
@ -203,7 +216,7 @@ pub const RenderOptions = struct {
|
|||||||
// Subtract padding from the bearings so that our
|
// Subtract padding from the bearings so that our
|
||||||
// alignment and sizing code works correctly. We
|
// alignment and sizing code works correctly. We
|
||||||
// re-add before returning.
|
// re-add before returning.
|
||||||
g.x -= self.pad_left * cell_width;
|
g.x -= self.pad_left * available_width;
|
||||||
g.y -= self.pad_bottom * cell_height;
|
g.y -= self.pad_bottom * cell_height;
|
||||||
|
|
||||||
switch (self.size_horizontal) {
|
switch (self.size_horizontal) {
|
||||||
@ -305,7 +318,7 @@ pub const RenderOptions = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Re-add our padding before returning.
|
// Re-add our padding before returning.
|
||||||
g.x += self.pad_left * cell_width;
|
g.x += self.pad_left * available_width;
|
||||||
g.y += self.pad_bottom * cell_height;
|
g.y += self.pad_bottom * cell_height;
|
||||||
|
|
||||||
return g;
|
return g;
|
||||||
|
@ -31,6 +31,9 @@ pub const Face = struct {
|
|||||||
/// tables).
|
/// tables).
|
||||||
color: ?ColorState = null,
|
color: ?ColorState = null,
|
||||||
|
|
||||||
|
/// The current size this font is set to.
|
||||||
|
size: font.face.DesiredSize,
|
||||||
|
|
||||||
/// True if our build is using Harfbuzz. If we're not, we can avoid
|
/// True if our build is using Harfbuzz. If we're not, we can avoid
|
||||||
/// some Harfbuzz-specific code paths.
|
/// some Harfbuzz-specific code paths.
|
||||||
const harfbuzz_shaper = font.options.backend.hasHarfbuzz();
|
const harfbuzz_shaper = font.options.backend.hasHarfbuzz();
|
||||||
@ -106,6 +109,7 @@ pub const Face = struct {
|
|||||||
.font = ct_font,
|
.font = ct_font,
|
||||||
.hb_font = hb_font,
|
.hb_font = hb_font,
|
||||||
.color = color,
|
.color = color,
|
||||||
|
.size = opts.size,
|
||||||
};
|
};
|
||||||
result.quirks_disable_default_font_features = quirks.disableDefaultFontFeatures(&result);
|
result.quirks_disable_default_font_features = quirks.disableDefaultFontFeatures(&result);
|
||||||
|
|
||||||
@ -333,11 +337,10 @@ pub const Face = struct {
|
|||||||
.offset_y = 0,
|
.offset_y = 0,
|
||||||
.atlas_x = 0,
|
.atlas_x = 0,
|
||||||
.atlas_y = 0,
|
.atlas_y = 0,
|
||||||
.advance_x = 0,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const metrics = opts.grid_metrics;
|
const metrics = opts.grid_metrics;
|
||||||
const cell_width: f64 = @floatFromInt(metrics.cell_width * opts.constraint_width);
|
const cell_width: f64 = @floatFromInt(metrics.cell_width);
|
||||||
const cell_height: f64 = @floatFromInt(metrics.cell_height);
|
const cell_height: f64 = @floatFromInt(metrics.cell_height);
|
||||||
|
|
||||||
const glyph_size = opts.constraint.constrain(
|
const glyph_size = opts.constraint.constrain(
|
||||||
@ -349,6 +352,7 @@ pub const Face = struct {
|
|||||||
},
|
},
|
||||||
cell_width,
|
cell_width,
|
||||||
cell_height,
|
cell_height,
|
||||||
|
opts.constraint_width,
|
||||||
);
|
);
|
||||||
|
|
||||||
const width = glyph_size.width;
|
const width = glyph_size.width;
|
||||||
@ -356,8 +360,16 @@ pub const Face = struct {
|
|||||||
const x = glyph_size.x;
|
const x = glyph_size.x;
|
||||||
const y = glyph_size.y;
|
const y = glyph_size.y;
|
||||||
|
|
||||||
const px_width: u32 = @intFromFloat(@ceil(width));
|
// We have to include the fractional pixels that we won't be offsetting
|
||||||
const px_height: u32 = @intFromFloat(@ceil(height));
|
// in our width and height calculations, that is, we offset by the floor
|
||||||
|
// of the bearings when we render the glyph, meaning there's still a bit
|
||||||
|
// of extra width to the area that's drawn in beyond just the width of
|
||||||
|
// the glyph itself, so we include that extra fraction of a pixel when
|
||||||
|
// calculating the width and height here.
|
||||||
|
const frac_x = rect.origin.x - @floor(rect.origin.x);
|
||||||
|
const frac_y = rect.origin.y - @floor(rect.origin.y);
|
||||||
|
const px_width: u32 = @intFromFloat(@ceil(width + frac_x));
|
||||||
|
const px_height: u32 = @intFromFloat(@ceil(height + frac_y));
|
||||||
|
|
||||||
// Settings that are specific to if we are rendering text or emoji.
|
// Settings that are specific to if we are rendering text or emoji.
|
||||||
const color: struct {
|
const color: struct {
|
||||||
@ -475,26 +487,44 @@ pub const Face = struct {
|
|||||||
// This should be the distance from the left of
|
// This should be the distance from the left of
|
||||||
// the cell to the left of the glyph's bounding box.
|
// the cell to the left of the glyph's bounding box.
|
||||||
const offset_x: i32 = offset_x: {
|
const offset_x: i32 = offset_x: {
|
||||||
var result: i32 = @intFromFloat(@round(x));
|
// If the glyph's advance is narrower than the cell width then we
|
||||||
|
// center the advance of the glyph within the cell width. At first
|
||||||
// If our cell was resized then we adjust our glyph's
|
// I implemented this to proportionally scale the center position
|
||||||
// position relative to the new center. This keeps glyphs
|
// of the glyph but that messes up glyphs that are meant to align
|
||||||
// centered in the cell whether it was made wider or narrower.
|
// vertically with others, so this is a compromise.
|
||||||
if (metrics.original_cell_width) |original_width| {
|
//
|
||||||
const before: i32 = @intCast(original_width);
|
// This makes it so that when the `adjust-cell-width` config is
|
||||||
const after: i32 = @intCast(metrics.cell_width);
|
// used, or when a fallback font with a different advance width
|
||||||
// Increase the offset by half of the difference
|
// is used, we don't get weirdly aligned glyphs.
|
||||||
// between the widths to keep things centered.
|
//
|
||||||
result += @divTrunc(after - before, 2);
|
// We don't do this if the constraint has a horizontal alignment,
|
||||||
|
// since in that case the position was already calculated with the
|
||||||
|
// new cell width in mind.
|
||||||
|
if (opts.constraint.align_horizontal == .none) {
|
||||||
|
var advances: [glyphs.len]macos.graphics.Size = undefined;
|
||||||
|
_ = self.font.getAdvancesForGlyphs(.horizontal, &glyphs, &advances);
|
||||||
|
const advance = advances[0].width;
|
||||||
|
const new_advance =
|
||||||
|
cell_width * @as(f64, @floatFromInt(opts.cell_width orelse 1));
|
||||||
|
// If the original advance is greater than the cell width then
|
||||||
|
// it's possible that this is a ligature or other glyph that is
|
||||||
|
// intended to overflow the cell to one side or the other, and
|
||||||
|
// adjusting the bearings could mess that up, so we just leave
|
||||||
|
// it alone if that's the case.
|
||||||
|
//
|
||||||
|
// We also don't want to do anything if the advance is zero or
|
||||||
|
// less, since this is used for stuff like combining characters.
|
||||||
|
if (advance > new_advance or advance <= 0.0) {
|
||||||
|
break :offset_x @intFromFloat(@ceil(x - frac_x));
|
||||||
|
}
|
||||||
|
break :offset_x @intFromFloat(
|
||||||
|
@ceil(x - frac_x + (new_advance - advance) / 2),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
break :offset_x @intFromFloat(@ceil(x - frac_x));
|
||||||
}
|
}
|
||||||
|
|
||||||
break :offset_x result;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get our advance
|
|
||||||
var advances: [glyphs.len]macos.graphics.Size = undefined;
|
|
||||||
_ = self.font.getAdvancesForGlyphs(.horizontal, &glyphs, &advances);
|
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.width = px_width,
|
.width = px_width,
|
||||||
.height = px_height,
|
.height = px_height,
|
||||||
@ -502,7 +532,6 @@ pub const Face = struct {
|
|||||||
.offset_y = offset_y,
|
.offset_y = offset_y,
|
||||||
.atlas_x = region.x,
|
.atlas_x = region.x,
|
||||||
.atlas_y = region.y,
|
.atlas_y = region.y,
|
||||||
.advance_x = @floatCast(advances[0].width),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -734,6 +763,20 @@ pub const Face = struct {
|
|||||||
break :cell_width max;
|
break :cell_width max;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Measure "水" (CJK water ideograph, U+6C34) for our ic width.
|
||||||
|
const ic_width: ?f64 = ic_width: {
|
||||||
|
const glyph = self.glyphIndex('水') orelse break :ic_width null;
|
||||||
|
|
||||||
|
var advances: [1]macos.graphics.Size = undefined;
|
||||||
|
_ = ct_font.getAdvancesForGlyphs(
|
||||||
|
.horizontal,
|
||||||
|
&.{@intCast(glyph)},
|
||||||
|
&advances,
|
||||||
|
);
|
||||||
|
|
||||||
|
break :ic_width advances[0].width;
|
||||||
|
};
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.cell_width = cell_width,
|
.cell_width = cell_width,
|
||||||
.ascent = ascent,
|
.ascent = ascent,
|
||||||
@ -745,6 +788,7 @@ pub const Face = struct {
|
|||||||
.strikethrough_thickness = strikethrough_thickness,
|
.strikethrough_thickness = strikethrough_thickness,
|
||||||
.cap_height = cap_height,
|
.cap_height = cap_height,
|
||||||
.ex_height = ex_height,
|
.ex_height = ex_height,
|
||||||
|
.ic_width = ic_width,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,6 +59,9 @@ pub const Face = struct {
|
|||||||
bold: bool = false,
|
bold: bool = false,
|
||||||
} = .{},
|
} = .{},
|
||||||
|
|
||||||
|
/// The current size this font is set to.
|
||||||
|
size: font.face.DesiredSize,
|
||||||
|
|
||||||
/// Initialize a new font face with the given source in-memory.
|
/// Initialize a new font face with the given source in-memory.
|
||||||
pub fn initFile(
|
pub fn initFile(
|
||||||
lib: Library,
|
lib: Library,
|
||||||
@ -107,6 +110,7 @@ pub const Face = struct {
|
|||||||
.hb_font = hb_font,
|
.hb_font = hb_font,
|
||||||
.ft_mutex = ft_mutex,
|
.ft_mutex = ft_mutex,
|
||||||
.load_flags = opts.freetype_load_flags,
|
.load_flags = opts.freetype_load_flags,
|
||||||
|
.size = opts.size,
|
||||||
};
|
};
|
||||||
result.quirks_disable_default_font_features = quirks.disableDefaultFontFeatures(&result);
|
result.quirks_disable_default_font_features = quirks.disableDefaultFontFeatures(&result);
|
||||||
|
|
||||||
@ -203,6 +207,7 @@ pub const Face = struct {
|
|||||||
/// for clearing any glyph caches, font atlas data, etc.
|
/// for clearing any glyph caches, font atlas data, etc.
|
||||||
pub fn setSize(self: *Face, opts: font.face.Options) !void {
|
pub fn setSize(self: *Face, opts: font.face.Options) !void {
|
||||||
try setSize_(self.face, opts.size);
|
try setSize_(self.face, opts.size);
|
||||||
|
self.size = opts.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setSize_(face: freetype.Face, size: font.face.DesiredSize) !void {
|
fn setSize_(face: freetype.Face, size: font.face.DesiredSize) !void {
|
||||||
@ -348,7 +353,7 @@ pub const Face = struct {
|
|||||||
|
|
||||||
// use options from config
|
// use options from config
|
||||||
.no_hinting = !do_hinting,
|
.no_hinting = !do_hinting,
|
||||||
.force_autohint = !self.load_flags.@"force-autohint",
|
.force_autohint = self.load_flags.@"force-autohint",
|
||||||
.no_autohint = !self.load_flags.autohint,
|
.no_autohint = !self.load_flags.autohint,
|
||||||
|
|
||||||
// NO_SVG set to true because we don't currently support rendering
|
// NO_SVG set to true because we don't currently support rendering
|
||||||
@ -373,7 +378,6 @@ pub const Face = struct {
|
|||||||
.offset_y = 0,
|
.offset_y = 0,
|
||||||
.atlas_x = 0,
|
.atlas_x = 0,
|
||||||
.atlas_y = 0,
|
.atlas_y = 0,
|
||||||
.advance_x = 0,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// For synthetic bold, we embolden the glyph.
|
// For synthetic bold, we embolden the glyph.
|
||||||
@ -390,7 +394,7 @@ pub const Face = struct {
|
|||||||
// Next we need to apply any constraints.
|
// Next we need to apply any constraints.
|
||||||
const metrics = opts.grid_metrics;
|
const metrics = opts.grid_metrics;
|
||||||
|
|
||||||
const cell_width: f64 = @floatFromInt(metrics.cell_width * opts.constraint_width);
|
const cell_width: f64 = @floatFromInt(metrics.cell_width);
|
||||||
const cell_height: f64 = @floatFromInt(metrics.cell_height);
|
const cell_height: f64 = @floatFromInt(metrics.cell_height);
|
||||||
|
|
||||||
const glyph_x: f64 = f26dot6ToF64(glyph.*.metrics.horiBearingX);
|
const glyph_x: f64 = f26dot6ToF64(glyph.*.metrics.horiBearingX);
|
||||||
@ -405,6 +409,7 @@ pub const Face = struct {
|
|||||||
},
|
},
|
||||||
cell_width,
|
cell_width,
|
||||||
cell_height,
|
cell_height,
|
||||||
|
opts.constraint_width,
|
||||||
);
|
);
|
||||||
|
|
||||||
const width = glyph_size.width;
|
const width = glyph_size.width;
|
||||||
@ -638,20 +643,40 @@ pub const Face = struct {
|
|||||||
// This should be the distance from the left of
|
// This should be the distance from the left of
|
||||||
// the cell to the left of the glyph's bounding box.
|
// the cell to the left of the glyph's bounding box.
|
||||||
const offset_x: i32 = offset_x: {
|
const offset_x: i32 = offset_x: {
|
||||||
var result: i32 = @intFromFloat(@floor(x));
|
// If the glyph's advance is narrower than the cell width then we
|
||||||
|
// center the advance of the glyph within the cell width. At first
|
||||||
// If our cell was resized then we adjust our glyph's
|
// I implemented this to proportionally scale the center position
|
||||||
// position relative to the new center. This keeps glyphs
|
// of the glyph but that messes up glyphs that are meant to align
|
||||||
// centered in the cell whether it was made wider or narrower.
|
// vertically with others, so this is a compromise.
|
||||||
if (metrics.original_cell_width) |original_width| {
|
//
|
||||||
const before: i32 = @intCast(original_width);
|
// This makes it so that when the `adjust-cell-width` config is
|
||||||
const after: i32 = @intCast(metrics.cell_width);
|
// used, or when a fallback font with a different advance width
|
||||||
// Increase the offset by half of the difference
|
// is used, we don't get weirdly aligned glyphs.
|
||||||
// between the widths to keep things centered.
|
//
|
||||||
result += @divTrunc(after - before, 2);
|
// We don't do this if the constraint has a horizontal alignment,
|
||||||
|
// since in that case the position was already calculated with the
|
||||||
|
// new cell width in mind.
|
||||||
|
if (opts.constraint.align_horizontal == .none) {
|
||||||
|
const advance = f26dot6ToFloat(glyph.*.advance.x);
|
||||||
|
const new_advance =
|
||||||
|
cell_width * @as(f64, @floatFromInt(opts.cell_width orelse 1));
|
||||||
|
// If the original advance is greater than the cell width then
|
||||||
|
// it's possible that this is a ligature or other glyph that is
|
||||||
|
// intended to overflow the cell to one side or the other, and
|
||||||
|
// adjusting the bearings could mess that up, so we just leave
|
||||||
|
// it alone if that's the case.
|
||||||
|
//
|
||||||
|
// We also don't want to do anything if the advance is zero or
|
||||||
|
// less, since this is used for stuff like combining characters.
|
||||||
|
if (advance > new_advance or advance <= 0.0) {
|
||||||
|
break :offset_x @intFromFloat(@floor(x));
|
||||||
|
}
|
||||||
|
break :offset_x @intFromFloat(
|
||||||
|
@floor(x + (new_advance - advance) / 2),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
break :offset_x @intFromFloat(@floor(x));
|
||||||
}
|
}
|
||||||
|
|
||||||
break :offset_x result;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return Glyph{
|
return Glyph{
|
||||||
@ -661,7 +686,6 @@ pub const Face = struct {
|
|||||||
.offset_y = offset_y,
|
.offset_y = offset_y,
|
||||||
.atlas_x = region.x,
|
.atlas_x = region.x,
|
||||||
.atlas_y = region.y,
|
.atlas_y = region.y,
|
||||||
.advance_x = f26dot6ToFloat(glyph.*.advance.x),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -832,7 +856,7 @@ pub const Face = struct {
|
|||||||
while (c < 127) : (c += 1) {
|
while (c < 127) : (c += 1) {
|
||||||
if (face.getCharIndex(c)) |glyph_index| {
|
if (face.getCharIndex(c)) |glyph_index| {
|
||||||
if (face.loadGlyph(glyph_index, .{
|
if (face.loadGlyph(glyph_index, .{
|
||||||
.render = true,
|
.render = false,
|
||||||
.no_svg = true,
|
.no_svg = true,
|
||||||
})) {
|
})) {
|
||||||
max = @max(
|
max = @max(
|
||||||
@ -870,7 +894,7 @@ pub const Face = struct {
|
|||||||
defer self.ft_mutex.unlock();
|
defer self.ft_mutex.unlock();
|
||||||
if (face.getCharIndex('H')) |glyph_index| {
|
if (face.getCharIndex('H')) |glyph_index| {
|
||||||
if (face.loadGlyph(glyph_index, .{
|
if (face.loadGlyph(glyph_index, .{
|
||||||
.render = true,
|
.render = false,
|
||||||
.no_svg = true,
|
.no_svg = true,
|
||||||
})) {
|
})) {
|
||||||
break :cap f26dot6ToF64(face.handle.*.glyph.*.metrics.height);
|
break :cap f26dot6ToF64(face.handle.*.glyph.*.metrics.height);
|
||||||
@ -883,7 +907,7 @@ pub const Face = struct {
|
|||||||
defer self.ft_mutex.unlock();
|
defer self.ft_mutex.unlock();
|
||||||
if (face.getCharIndex('x')) |glyph_index| {
|
if (face.getCharIndex('x')) |glyph_index| {
|
||||||
if (face.loadGlyph(glyph_index, .{
|
if (face.loadGlyph(glyph_index, .{
|
||||||
.render = true,
|
.render = false,
|
||||||
.no_svg = true,
|
.no_svg = true,
|
||||||
})) {
|
})) {
|
||||||
break :ex f26dot6ToF64(face.handle.*.glyph.*.metrics.height);
|
break :ex f26dot6ToF64(face.handle.*.glyph.*.metrics.height);
|
||||||
@ -894,6 +918,21 @@ pub const Face = struct {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Measure "水" (CJK water ideograph, U+6C34) for our ic width.
|
||||||
|
const ic_width: ?f64 = ic_width: {
|
||||||
|
self.ft_mutex.lock();
|
||||||
|
defer self.ft_mutex.unlock();
|
||||||
|
|
||||||
|
const glyph = face.getCharIndex('水') orelse break :ic_width null;
|
||||||
|
|
||||||
|
face.loadGlyph(glyph, .{
|
||||||
|
.render = false,
|
||||||
|
.no_svg = true,
|
||||||
|
}) catch break :ic_width null;
|
||||||
|
|
||||||
|
break :ic_width f26dot6ToF64(face.handle.*.glyph.*.advance.x);
|
||||||
|
};
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.cell_width = cell_width,
|
.cell_width = cell_width,
|
||||||
|
|
||||||
@ -909,6 +948,7 @@ pub const Face = struct {
|
|||||||
|
|
||||||
.cap_height = cap_height,
|
.cap_height = cap_height,
|
||||||
.ex_height = ex_height,
|
.ex_height = ex_height,
|
||||||
|
.ic_width = ic_width,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,7 +235,6 @@ pub const Face = struct {
|
|||||||
.offset_y = 0,
|
.offset_y = 0,
|
||||||
.atlas_x = region.x,
|
.atlas_x = region.x,
|
||||||
.atlas_y = region.y,
|
.atlas_y = region.y,
|
||||||
.advance_x = 0,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//! This is a generate file, produced by nerd_font_codegen.py
|
//! This is a generated file, produced by nerd_font_codegen.py
|
||||||
//! DO NOT EDIT BY HAND!
|
//! DO NOT EDIT BY HAND!
|
||||||
//!
|
//!
|
||||||
//! This file provides info extracted from the nerd fonts patcher script,
|
//! This file provides info extracted from the nerd fonts patcher script,
|
||||||
@ -13,6 +13,7 @@ pub fn getConstraint(cp: u21) Constraint {
|
|||||||
=> .{
|
=> .{
|
||||||
.size_horizontal = .stretch,
|
.size_horizontal = .stretch,
|
||||||
.size_vertical = .stretch,
|
.size_vertical = .stretch,
|
||||||
|
.max_constraint_width = 1,
|
||||||
.align_horizontal = .center,
|
.align_horizontal = .center,
|
||||||
.align_vertical = .center,
|
.align_vertical = .center,
|
||||||
.pad_left = -0.02,
|
.pad_left = -0.02,
|
||||||
@ -24,24 +25,29 @@ pub fn getConstraint(cp: u21) Constraint {
|
|||||||
=> .{
|
=> .{
|
||||||
.size_horizontal = .cover,
|
.size_horizontal = .cover,
|
||||||
.size_vertical = .fit,
|
.size_vertical = .fit,
|
||||||
|
.max_constraint_width = 1,
|
||||||
.align_horizontal = .center,
|
.align_horizontal = .center,
|
||||||
.align_vertical = .center,
|
.align_vertical = .center,
|
||||||
.pad_left = 0.1,
|
.pad_left = 0.1,
|
||||||
.pad_right = 0.1,
|
.pad_right = 0.1,
|
||||||
.pad_top = 0.01,
|
.pad_top = 0.1,
|
||||||
.pad_bottom = 0.01,
|
.pad_bottom = 0.1,
|
||||||
},
|
},
|
||||||
0x276c...0x2771,
|
0x276c...0x2771,
|
||||||
=> .{
|
=> .{
|
||||||
.size_horizontal = .cover,
|
.size_horizontal = .cover,
|
||||||
.size_vertical = .fit,
|
.size_vertical = .fit,
|
||||||
|
.max_constraint_width = 1,
|
||||||
.align_horizontal = .center,
|
.align_horizontal = .center,
|
||||||
.align_vertical = .center,
|
.align_vertical = .center,
|
||||||
|
.pad_top = 0.15,
|
||||||
|
.pad_bottom = 0.15,
|
||||||
},
|
},
|
||||||
0xe0b0,
|
0xe0b0,
|
||||||
=> .{
|
=> .{
|
||||||
.size_horizontal = .stretch,
|
.size_horizontal = .stretch,
|
||||||
.size_vertical = .stretch,
|
.size_vertical = .stretch,
|
||||||
|
.max_constraint_width = 1,
|
||||||
.align_horizontal = .start,
|
.align_horizontal = .start,
|
||||||
.align_vertical = .center,
|
.align_vertical = .center,
|
||||||
.pad_left = -0.06,
|
.pad_left = -0.06,
|
||||||
@ -54,6 +60,7 @@ pub fn getConstraint(cp: u21) Constraint {
|
|||||||
=> .{
|
=> .{
|
||||||
.size_horizontal = .stretch,
|
.size_horizontal = .stretch,
|
||||||
.size_vertical = .stretch,
|
.size_vertical = .stretch,
|
||||||
|
.max_constraint_width = 1,
|
||||||
.align_horizontal = .start,
|
.align_horizontal = .start,
|
||||||
.align_vertical = .center,
|
.align_vertical = .center,
|
||||||
.max_xy_ratio = 0.7,
|
.max_xy_ratio = 0.7,
|
||||||
@ -62,6 +69,7 @@ pub fn getConstraint(cp: u21) Constraint {
|
|||||||
=> .{
|
=> .{
|
||||||
.size_horizontal = .stretch,
|
.size_horizontal = .stretch,
|
||||||
.size_vertical = .stretch,
|
.size_vertical = .stretch,
|
||||||
|
.max_constraint_width = 1,
|
||||||
.align_horizontal = .end,
|
.align_horizontal = .end,
|
||||||
.align_vertical = .center,
|
.align_vertical = .center,
|
||||||
.pad_left = -0.06,
|
.pad_left = -0.06,
|
||||||
@ -74,6 +82,7 @@ pub fn getConstraint(cp: u21) Constraint {
|
|||||||
=> .{
|
=> .{
|
||||||
.size_horizontal = .stretch,
|
.size_horizontal = .stretch,
|
||||||
.size_vertical = .stretch,
|
.size_vertical = .stretch,
|
||||||
|
.max_constraint_width = 1,
|
||||||
.align_horizontal = .end,
|
.align_horizontal = .end,
|
||||||
.align_vertical = .center,
|
.align_vertical = .center,
|
||||||
.max_xy_ratio = 0.7,
|
.max_xy_ratio = 0.7,
|
||||||
@ -82,6 +91,7 @@ pub fn getConstraint(cp: u21) Constraint {
|
|||||||
=> .{
|
=> .{
|
||||||
.size_horizontal = .stretch,
|
.size_horizontal = .stretch,
|
||||||
.size_vertical = .stretch,
|
.size_vertical = .stretch,
|
||||||
|
.max_constraint_width = 1,
|
||||||
.align_horizontal = .start,
|
.align_horizontal = .start,
|
||||||
.align_vertical = .center,
|
.align_vertical = .center,
|
||||||
.pad_left = -0.06,
|
.pad_left = -0.06,
|
||||||
@ -94,6 +104,7 @@ pub fn getConstraint(cp: u21) Constraint {
|
|||||||
=> .{
|
=> .{
|
||||||
.size_horizontal = .stretch,
|
.size_horizontal = .stretch,
|
||||||
.size_vertical = .stretch,
|
.size_vertical = .stretch,
|
||||||
|
.max_constraint_width = 1,
|
||||||
.align_horizontal = .start,
|
.align_horizontal = .start,
|
||||||
.align_vertical = .center,
|
.align_vertical = .center,
|
||||||
.max_xy_ratio = 0.5,
|
.max_xy_ratio = 0.5,
|
||||||
@ -102,6 +113,7 @@ pub fn getConstraint(cp: u21) Constraint {
|
|||||||
=> .{
|
=> .{
|
||||||
.size_horizontal = .stretch,
|
.size_horizontal = .stretch,
|
||||||
.size_vertical = .stretch,
|
.size_vertical = .stretch,
|
||||||
|
.max_constraint_width = 1,
|
||||||
.align_horizontal = .end,
|
.align_horizontal = .end,
|
||||||
.align_vertical = .center,
|
.align_vertical = .center,
|
||||||
.pad_left = -0.06,
|
.pad_left = -0.06,
|
||||||
@ -114,6 +126,7 @@ pub fn getConstraint(cp: u21) Constraint {
|
|||||||
=> .{
|
=> .{
|
||||||
.size_horizontal = .stretch,
|
.size_horizontal = .stretch,
|
||||||
.size_vertical = .stretch,
|
.size_vertical = .stretch,
|
||||||
|
.max_constraint_width = 1,
|
||||||
.align_horizontal = .end,
|
.align_horizontal = .end,
|
||||||
.align_vertical = .center,
|
.align_vertical = .center,
|
||||||
.max_xy_ratio = 0.5,
|
.max_xy_ratio = 0.5,
|
||||||
@ -123,6 +136,7 @@ pub fn getConstraint(cp: u21) Constraint {
|
|||||||
=> .{
|
=> .{
|
||||||
.size_horizontal = .stretch,
|
.size_horizontal = .stretch,
|
||||||
.size_vertical = .stretch,
|
.size_vertical = .stretch,
|
||||||
|
.max_constraint_width = 1,
|
||||||
.align_horizontal = .start,
|
.align_horizontal = .start,
|
||||||
.align_vertical = .center,
|
.align_vertical = .center,
|
||||||
.pad_left = -0.05,
|
.pad_left = -0.05,
|
||||||
@ -135,6 +149,7 @@ pub fn getConstraint(cp: u21) Constraint {
|
|||||||
=> .{
|
=> .{
|
||||||
.size_horizontal = .stretch,
|
.size_horizontal = .stretch,
|
||||||
.size_vertical = .stretch,
|
.size_vertical = .stretch,
|
||||||
|
.max_constraint_width = 1,
|
||||||
.align_horizontal = .start,
|
.align_horizontal = .start,
|
||||||
.align_vertical = .center,
|
.align_vertical = .center,
|
||||||
},
|
},
|
||||||
@ -143,6 +158,7 @@ pub fn getConstraint(cp: u21) Constraint {
|
|||||||
=> .{
|
=> .{
|
||||||
.size_horizontal = .stretch,
|
.size_horizontal = .stretch,
|
||||||
.size_vertical = .stretch,
|
.size_vertical = .stretch,
|
||||||
|
.max_constraint_width = 1,
|
||||||
.align_horizontal = .end,
|
.align_horizontal = .end,
|
||||||
.align_vertical = .center,
|
.align_vertical = .center,
|
||||||
.pad_left = -0.05,
|
.pad_left = -0.05,
|
||||||
@ -155,6 +171,7 @@ pub fn getConstraint(cp: u21) Constraint {
|
|||||||
=> .{
|
=> .{
|
||||||
.size_horizontal = .stretch,
|
.size_horizontal = .stretch,
|
||||||
.size_vertical = .stretch,
|
.size_vertical = .stretch,
|
||||||
|
.max_constraint_width = 1,
|
||||||
.align_horizontal = .end,
|
.align_horizontal = .end,
|
||||||
.align_vertical = .center,
|
.align_vertical = .center,
|
||||||
},
|
},
|
||||||
@ -204,8 +221,8 @@ pub fn getConstraint(cp: u21) Constraint {
|
|||||||
.align_vertical = .center,
|
.align_vertical = .center,
|
||||||
.pad_left = 0.03,
|
.pad_left = 0.03,
|
||||||
.pad_right = 0.03,
|
.pad_right = 0.03,
|
||||||
.pad_top = 0.01,
|
.pad_top = 0.03,
|
||||||
.pad_bottom = 0.01,
|
.pad_bottom = 0.03,
|
||||||
.max_xy_ratio = 0.86,
|
.max_xy_ratio = 0.86,
|
||||||
},
|
},
|
||||||
0xe0c5,
|
0xe0c5,
|
||||||
@ -216,8 +233,8 @@ pub fn getConstraint(cp: u21) Constraint {
|
|||||||
.align_vertical = .center,
|
.align_vertical = .center,
|
||||||
.pad_left = 0.03,
|
.pad_left = 0.03,
|
||||||
.pad_right = 0.03,
|
.pad_right = 0.03,
|
||||||
.pad_top = 0.01,
|
.pad_top = 0.03,
|
||||||
.pad_bottom = 0.01,
|
.pad_bottom = 0.03,
|
||||||
.max_xy_ratio = 0.86,
|
.max_xy_ratio = 0.86,
|
||||||
},
|
},
|
||||||
0xe0c6,
|
0xe0c6,
|
||||||
@ -228,8 +245,8 @@ pub fn getConstraint(cp: u21) Constraint {
|
|||||||
.align_vertical = .center,
|
.align_vertical = .center,
|
||||||
.pad_left = 0.03,
|
.pad_left = 0.03,
|
||||||
.pad_right = 0.03,
|
.pad_right = 0.03,
|
||||||
.pad_top = 0.01,
|
.pad_top = 0.03,
|
||||||
.pad_bottom = 0.01,
|
.pad_bottom = 0.03,
|
||||||
.max_xy_ratio = 0.78,
|
.max_xy_ratio = 0.78,
|
||||||
},
|
},
|
||||||
0xe0c7,
|
0xe0c7,
|
||||||
@ -240,8 +257,8 @@ pub fn getConstraint(cp: u21) Constraint {
|
|||||||
.align_vertical = .center,
|
.align_vertical = .center,
|
||||||
.pad_left = 0.03,
|
.pad_left = 0.03,
|
||||||
.pad_right = 0.03,
|
.pad_right = 0.03,
|
||||||
.pad_top = 0.01,
|
.pad_top = 0.03,
|
||||||
.pad_bottom = 0.01,
|
.pad_bottom = 0.03,
|
||||||
.max_xy_ratio = 0.78,
|
.max_xy_ratio = 0.78,
|
||||||
},
|
},
|
||||||
0xe0cc,
|
0xe0cc,
|
||||||
@ -285,6 +302,7 @@ pub fn getConstraint(cp: u21) Constraint {
|
|||||||
=> .{
|
=> .{
|
||||||
.size_horizontal = .stretch,
|
.size_horizontal = .stretch,
|
||||||
.size_vertical = .stretch,
|
.size_vertical = .stretch,
|
||||||
|
.max_constraint_width = 1,
|
||||||
.align_horizontal = .start,
|
.align_horizontal = .start,
|
||||||
.align_vertical = .center,
|
.align_vertical = .center,
|
||||||
.pad_left = -0.02,
|
.pad_left = -0.02,
|
||||||
@ -297,6 +315,7 @@ pub fn getConstraint(cp: u21) Constraint {
|
|||||||
=> .{
|
=> .{
|
||||||
.size_horizontal = .stretch,
|
.size_horizontal = .stretch,
|
||||||
.size_vertical = .stretch,
|
.size_vertical = .stretch,
|
||||||
|
.max_constraint_width = 1,
|
||||||
.align_horizontal = .end,
|
.align_horizontal = .end,
|
||||||
.align_vertical = .center,
|
.align_vertical = .center,
|
||||||
.pad_left = -0.02,
|
.pad_left = -0.02,
|
||||||
@ -309,6 +328,7 @@ pub fn getConstraint(cp: u21) Constraint {
|
|||||||
=> .{
|
=> .{
|
||||||
.size_horizontal = .stretch,
|
.size_horizontal = .stretch,
|
||||||
.size_vertical = .stretch,
|
.size_vertical = .stretch,
|
||||||
|
.max_constraint_width = 1,
|
||||||
.align_horizontal = .start,
|
.align_horizontal = .start,
|
||||||
.align_vertical = .center,
|
.align_vertical = .center,
|
||||||
.pad_left = -0.05,
|
.pad_left = -0.05,
|
||||||
@ -321,6 +341,7 @@ pub fn getConstraint(cp: u21) Constraint {
|
|||||||
=> .{
|
=> .{
|
||||||
.size_horizontal = .stretch,
|
.size_horizontal = .stretch,
|
||||||
.size_vertical = .stretch,
|
.size_vertical = .stretch,
|
||||||
|
.max_constraint_width = 1,
|
||||||
.align_horizontal = .end,
|
.align_horizontal = .end,
|
||||||
.align_vertical = .center,
|
.align_vertical = .center,
|
||||||
.pad_left = -0.05,
|
.pad_left = -0.05,
|
||||||
|
@ -1,102 +1,124 @@
|
|||||||
"""
|
"""
|
||||||
This file is mostly vibe coded because I don't like Python. It extracts the
|
This file extracts the patch sets from the nerd fonts font patcher file in order to
|
||||||
patch sets from the nerd fonts font patcher file in order to extract scaling
|
extract scaling rules and attributes for different codepoint ranges which it then
|
||||||
rules and attributes for different codepoint ranges which it then codegens
|
codegens in to a Zig file with a function that switches over codepoints and returns the
|
||||||
in to a Zig file with a function that switches over codepoints and returns
|
attributes and scaling rules.
|
||||||
the attributes and scaling rules.
|
|
||||||
|
|
||||||
This does include an `eval` call! This is spooky, but we trust
|
This does include an `eval` call! This is spooky, but we trust the nerd fonts code to
|
||||||
the nerd fonts code to be safe and not malicious or anything.
|
be safe and not malicious or anything.
|
||||||
|
|
||||||
|
This script requires Python 3.12 or greater.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import ast
|
import ast
|
||||||
import math
|
import math
|
||||||
from pathlib import Path
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
from contextlib import suppress
|
||||||
|
from pathlib import Path
|
||||||
|
from types import SimpleNamespace
|
||||||
|
from typing import Literal, TypedDict, cast
|
||||||
|
|
||||||
|
type PatchSetAttributes = dict[Literal["default"] | int, PatchSetAttributeEntry]
|
||||||
|
type AttributeHash = tuple[str | None, str | None, str, float, float, float]
|
||||||
|
type ResolvedSymbol = PatchSetAttributes | PatchSetScaleRules | int | None
|
||||||
|
|
||||||
|
|
||||||
|
class PatchSetScaleRules(TypedDict):
|
||||||
|
ShiftMode: str
|
||||||
|
ScaleGroups: list[list[int] | range]
|
||||||
|
|
||||||
|
|
||||||
|
class PatchSetAttributeEntry(TypedDict):
|
||||||
|
align: str
|
||||||
|
valign: str
|
||||||
|
stretch: str
|
||||||
|
params: dict[str, float | bool]
|
||||||
|
|
||||||
|
|
||||||
|
class PatchSet(TypedDict):
|
||||||
|
SymStart: int
|
||||||
|
SymEnd: int
|
||||||
|
SrcStart: int | None
|
||||||
|
ScaleRules: PatchSetScaleRules | None
|
||||||
|
Attributes: PatchSetAttributes
|
||||||
|
|
||||||
|
|
||||||
class PatchSetExtractor(ast.NodeVisitor):
|
class PatchSetExtractor(ast.NodeVisitor):
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
self.symbol_table = {}
|
self.symbol_table: dict[str, ast.expr] = {}
|
||||||
self.patch_set_values = []
|
self.patch_set_values: list[PatchSet] = []
|
||||||
|
|
||||||
def visit_ClassDef(self, node):
|
def visit_ClassDef(self, node: ast.ClassDef) -> None:
|
||||||
if node.name == "font_patcher":
|
if node.name != "font_patcher":
|
||||||
for item in node.body:
|
return
|
||||||
if isinstance(item, ast.FunctionDef) and item.name == "setup_patch_set":
|
for item in node.body:
|
||||||
self.visit_setup_patch_set(item)
|
if isinstance(item, ast.FunctionDef) and item.name == "setup_patch_set":
|
||||||
|
self.visit_setup_patch_set(item)
|
||||||
|
|
||||||
def visit_setup_patch_set(self, node):
|
def visit_setup_patch_set(self, node: ast.FunctionDef) -> None:
|
||||||
# First pass: gather variable assignments
|
# First pass: gather variable assignments
|
||||||
for stmt in node.body:
|
for stmt in node.body:
|
||||||
if isinstance(stmt, ast.Assign):
|
match stmt:
|
||||||
# Store simple variable assignments in the symbol table
|
case ast.Assign(targets=[ast.Name(id=symbol)]):
|
||||||
if len(stmt.targets) == 1 and isinstance(stmt.targets[0], ast.Name):
|
# Store simple variable assignments in the symbol table
|
||||||
var_name = stmt.targets[0].id
|
self.symbol_table[symbol] = stmt.value
|
||||||
self.symbol_table[var_name] = stmt.value
|
|
||||||
|
|
||||||
# Second pass: process self.patch_set
|
# Second pass: process self.patch_set
|
||||||
for stmt in node.body:
|
for stmt in node.body:
|
||||||
if isinstance(stmt, ast.Assign):
|
if not isinstance(stmt, ast.Assign):
|
||||||
for target in stmt.targets:
|
continue
|
||||||
if isinstance(target, ast.Attribute) and target.attr == "patch_set":
|
for target in stmt.targets:
|
||||||
if isinstance(stmt.value, ast.List):
|
if (
|
||||||
for elt in stmt.value.elts:
|
isinstance(target, ast.Attribute)
|
||||||
if isinstance(elt, ast.Dict):
|
and target.attr == "patch_set"
|
||||||
self.process_patch_entry(elt)
|
and isinstance(stmt.value, ast.List)
|
||||||
|
):
|
||||||
|
for elt in stmt.value.elts:
|
||||||
|
if isinstance(elt, ast.Dict):
|
||||||
|
self.process_patch_entry(elt)
|
||||||
|
|
||||||
def resolve_symbol(self, node):
|
def resolve_symbol(self, node: ast.expr) -> ResolvedSymbol:
|
||||||
"""Resolve named variables to their actual values from the symbol table."""
|
"""Resolve named variables to their actual values from the symbol table."""
|
||||||
if isinstance(node, ast.Name) and node.id in self.symbol_table:
|
if isinstance(node, ast.Name) and node.id in self.symbol_table:
|
||||||
return self.safe_literal_eval(self.symbol_table[node.id])
|
return self.safe_literal_eval(self.symbol_table[node.id])
|
||||||
return self.safe_literal_eval(node)
|
return self.safe_literal_eval(node)
|
||||||
|
|
||||||
def safe_literal_eval(self, node):
|
def safe_literal_eval(self, node: ast.expr) -> ResolvedSymbol:
|
||||||
"""Try to evaluate or stringify an AST node."""
|
"""Try to evaluate or stringify an AST node."""
|
||||||
try:
|
try:
|
||||||
return ast.literal_eval(node)
|
return ast.literal_eval(node)
|
||||||
except Exception:
|
except ValueError:
|
||||||
# Spooky eval! But we trust nerd fonts to be safe...
|
# Spooky eval! But we trust nerd fonts to be safe...
|
||||||
if hasattr(ast, "unparse"):
|
if hasattr(ast, "unparse"):
|
||||||
return eval(
|
return eval(
|
||||||
ast.unparse(node), {"box_keep": True}, {"self": SpoofSelf()}
|
ast.unparse(node),
|
||||||
|
{"box_keep": True},
|
||||||
|
{"self": SimpleNamespace(args=SimpleNamespace(careful=True))},
|
||||||
)
|
)
|
||||||
else:
|
msg = f"<cannot eval: {type(node).__name__}>"
|
||||||
return f"<cannot eval: {type(node).__name__}>"
|
raise ValueError(msg) from None
|
||||||
|
|
||||||
def process_patch_entry(self, dict_node):
|
def process_patch_entry(self, dict_node: ast.Dict) -> None:
|
||||||
entry = {}
|
entry = {}
|
||||||
|
disallowed_key_nodes = frozenset({"Enabled", "Name", "Filename", "Exact"})
|
||||||
for key_node, value_node in zip(dict_node.keys, dict_node.values):
|
for key_node, value_node in zip(dict_node.keys, dict_node.values):
|
||||||
if isinstance(key_node, ast.Constant) and key_node.value in (
|
if (
|
||||||
"Enabled",
|
isinstance(key_node, ast.Constant)
|
||||||
"Name",
|
and key_node.value not in disallowed_key_nodes
|
||||||
"Filename",
|
|
||||||
"Exact",
|
|
||||||
):
|
):
|
||||||
continue
|
key = ast.literal_eval(cast("ast.Constant", key_node))
|
||||||
key = ast.literal_eval(key_node)
|
entry[key] = self.resolve_symbol(value_node)
|
||||||
value = self.resolve_symbol(value_node)
|
self.patch_set_values.append(cast("PatchSet", entry))
|
||||||
entry[key] = value
|
|
||||||
self.patch_set_values.append(entry)
|
|
||||||
|
|
||||||
|
|
||||||
def extract_patch_set_values(source_code):
|
def extract_patch_set_values(source_code: str) -> list[PatchSet]:
|
||||||
tree = ast.parse(source_code)
|
tree = ast.parse(source_code)
|
||||||
extractor = PatchSetExtractor()
|
extractor = PatchSetExtractor()
|
||||||
extractor.visit(tree)
|
extractor.visit(tree)
|
||||||
return extractor.patch_set_values
|
return extractor.patch_set_values
|
||||||
|
|
||||||
|
|
||||||
# We have to spoof `self` and `self.args` for the eval.
|
def parse_alignment(val: str) -> str | None:
|
||||||
class SpoofArgs:
|
|
||||||
careful = True
|
|
||||||
|
|
||||||
|
|
||||||
class SpoofSelf:
|
|
||||||
args = SpoofArgs()
|
|
||||||
|
|
||||||
|
|
||||||
def parse_alignment(val):
|
|
||||||
return {
|
return {
|
||||||
"l": ".start",
|
"l": ".start",
|
||||||
"r": ".end",
|
"r": ".end",
|
||||||
@ -105,28 +127,24 @@ def parse_alignment(val):
|
|||||||
}.get(val, ".none")
|
}.get(val, ".none")
|
||||||
|
|
||||||
|
|
||||||
def get_param(d, key, default):
|
def attr_key(attr: PatchSetAttributeEntry) -> AttributeHash:
|
||||||
return float(d.get(key, default))
|
|
||||||
|
|
||||||
|
|
||||||
def attr_key(attr):
|
|
||||||
"""Convert attributes to a hashable key for grouping."""
|
"""Convert attributes to a hashable key for grouping."""
|
||||||
stretch = attr.get("stretch", "")
|
params = attr.get("params", {})
|
||||||
return (
|
return (
|
||||||
parse_alignment(attr.get("align", "")),
|
parse_alignment(attr.get("align", "")),
|
||||||
parse_alignment(attr.get("valign", "")),
|
parse_alignment(attr.get("valign", "")),
|
||||||
stretch,
|
attr.get("stretch", ""),
|
||||||
float(attr.get("params", {}).get("overlap", 0.0)),
|
float(params.get("overlap", 0.0)),
|
||||||
float(attr.get("params", {}).get("xy-ratio", -1.0)),
|
float(params.get("xy-ratio", -1.0)),
|
||||||
float(attr.get("params", {}).get("ypadding", 0.0)),
|
float(params.get("ypadding", 0.0)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def coalesce_codepoints_to_ranges(codepoints):
|
def coalesce_codepoints_to_ranges(codepoints: list[int]) -> list[tuple[int, int]]:
|
||||||
"""Convert a sorted list of integers to a list of single values and ranges."""
|
"""Convert a sorted list of integers to a list of single values and ranges."""
|
||||||
ranges = []
|
ranges: list[tuple[int, int]] = []
|
||||||
cp_iter = iter(sorted(codepoints))
|
cp_iter = iter(sorted(codepoints))
|
||||||
try:
|
with suppress(StopIteration):
|
||||||
start = prev = next(cp_iter)
|
start = prev = next(cp_iter)
|
||||||
for cp in cp_iter:
|
for cp in cp_iter:
|
||||||
if cp == prev + 1:
|
if cp == prev + 1:
|
||||||
@ -135,88 +153,96 @@ def coalesce_codepoints_to_ranges(codepoints):
|
|||||||
ranges.append((start, prev))
|
ranges.append((start, prev))
|
||||||
start = prev = cp
|
start = prev = cp
|
||||||
ranges.append((start, prev))
|
ranges.append((start, prev))
|
||||||
except StopIteration:
|
|
||||||
pass
|
|
||||||
return ranges
|
return ranges
|
||||||
|
|
||||||
|
|
||||||
def emit_zig_entry_multikey(codepoints, attr):
|
def emit_zig_entry_multikey(codepoints: list[int], attr: PatchSetAttributeEntry) -> str:
|
||||||
align = parse_alignment(attr.get("align", ""))
|
align = parse_alignment(attr.get("align", ""))
|
||||||
valign = parse_alignment(attr.get("valign", ""))
|
valign = parse_alignment(attr.get("valign", ""))
|
||||||
stretch = attr.get("stretch", "")
|
stretch = attr.get("stretch", "")
|
||||||
params = attr.get("params", {})
|
params = attr.get("params", {})
|
||||||
|
|
||||||
overlap = get_param(params, "overlap", 0.0)
|
overlap = params.get("overlap", 0.0)
|
||||||
xy_ratio = get_param(params, "xy-ratio", -1.0)
|
xy_ratio = params.get("xy-ratio", -1.0)
|
||||||
y_padding = get_param(params, "ypadding", 0.0)
|
y_padding = params.get("ypadding", 0.0)
|
||||||
|
|
||||||
ranges = coalesce_codepoints_to_ranges(codepoints)
|
ranges = coalesce_codepoints_to_ranges(codepoints)
|
||||||
keys = "\n".join(
|
keys = "\n".join(
|
||||||
f" 0x{start:x}...0x{end:x}," if start != end else f" 0x{start:x},"
|
f" {start:#x}...{end:#x}," if start != end else f" {start:#x},"
|
||||||
for start, end in ranges
|
for start, end in ranges
|
||||||
)
|
)
|
||||||
|
|
||||||
s = f"""{keys}
|
s = f"{keys}\n => .{{\n"
|
||||||
=> .{{\n"""
|
|
||||||
|
|
||||||
# These translations don't quite capture the way
|
# These translations don't quite capture the way
|
||||||
# the actual patcher does scaling, but they're a
|
# the actual patcher does scaling, but they're a
|
||||||
# good enough compromise.
|
# good enough compromise.
|
||||||
if ("xy" in stretch):
|
if "xy" in stretch:
|
||||||
s += " .size_horizontal = .stretch,\n"
|
s += " .size_horizontal = .stretch,\n"
|
||||||
s += " .size_vertical = .stretch,\n"
|
s += " .size_vertical = .stretch,\n"
|
||||||
elif ("!" in stretch):
|
elif "!" in stretch:
|
||||||
s += " .size_horizontal = .cover,\n"
|
s += " .size_horizontal = .cover,\n"
|
||||||
s += " .size_vertical = .fit,\n"
|
s += " .size_vertical = .fit,\n"
|
||||||
elif ("^" in stretch):
|
elif "^" in stretch:
|
||||||
s += " .size_horizontal = .cover,\n"
|
s += " .size_horizontal = .cover,\n"
|
||||||
s += " .size_vertical = .cover,\n"
|
s += " .size_vertical = .cover,\n"
|
||||||
else:
|
else:
|
||||||
s += " .size_horizontal = .fit,\n"
|
s += " .size_horizontal = .fit,\n"
|
||||||
s += " .size_vertical = .fit,\n"
|
s += " .size_vertical = .fit,\n"
|
||||||
|
|
||||||
if (align is not None):
|
# There are two cases where we want to limit the constraint width to 1:
|
||||||
|
# - If there's a `1` in the stretch mode string.
|
||||||
|
# - If the stretch mode is `xy` and there's not an explicit `2`.
|
||||||
|
if "1" in stretch or ("xy" in stretch and "2" not in stretch):
|
||||||
|
s += " .max_constraint_width = 1,\n"
|
||||||
|
|
||||||
|
if align is not None:
|
||||||
s += f" .align_horizontal = {align},\n"
|
s += f" .align_horizontal = {align},\n"
|
||||||
if (valign is not None):
|
if valign is not None:
|
||||||
s += f" .align_vertical = {valign},\n"
|
s += f" .align_vertical = {valign},\n"
|
||||||
|
|
||||||
if (overlap != 0.0):
|
# `overlap` and `ypadding` are mutually exclusive,
|
||||||
|
# this is asserted in the nerd fonts patcher itself.
|
||||||
|
if overlap:
|
||||||
pad = -overlap
|
pad = -overlap
|
||||||
s += f" .pad_left = {pad},\n"
|
s += f" .pad_left = {pad},\n"
|
||||||
s += f" .pad_right = {pad},\n"
|
s += f" .pad_right = {pad},\n"
|
||||||
v_pad = y_padding - math.copysign(min(0.01, abs(overlap)), overlap)
|
# In the nerd fonts patcher, overlap values
|
||||||
|
# are capped at 0.01 in the vertical direction.
|
||||||
|
v_pad = -min(0.01, overlap)
|
||||||
s += f" .pad_top = {v_pad},\n"
|
s += f" .pad_top = {v_pad},\n"
|
||||||
s += f" .pad_bottom = {v_pad},\n"
|
s += f" .pad_bottom = {v_pad},\n"
|
||||||
|
elif y_padding:
|
||||||
|
s += f" .pad_top = {y_padding / 2},\n"
|
||||||
|
s += f" .pad_bottom = {y_padding / 2},\n"
|
||||||
|
|
||||||
if (xy_ratio > 0):
|
if xy_ratio > 0:
|
||||||
s += f" .max_xy_ratio = {xy_ratio},\n"
|
s += f" .max_xy_ratio = {xy_ratio},\n"
|
||||||
|
|
||||||
s += " },"
|
s += " },"
|
||||||
|
|
||||||
return s
|
return s
|
||||||
|
|
||||||
def generate_zig_switch_arms(patch_set):
|
|
||||||
entries = {}
|
def generate_zig_switch_arms(patch_sets: list[PatchSet]) -> str:
|
||||||
for entry in patch_set:
|
entries: dict[int, PatchSetAttributeEntry] = {}
|
||||||
|
for entry in patch_sets:
|
||||||
attributes = entry["Attributes"]
|
attributes = entry["Attributes"]
|
||||||
|
|
||||||
for cp in range(entry["SymStart"], entry["SymEnd"] + 1):
|
for cp in range(entry["SymStart"], entry["SymEnd"] + 1):
|
||||||
entries[cp] = attributes["default"]
|
entries[cp] = attributes["default"]
|
||||||
|
|
||||||
for k, v in attributes.items():
|
entries |= {k: v for k, v in attributes.items() if isinstance(k, int)}
|
||||||
if isinstance(k, int):
|
|
||||||
entries[k] = v
|
|
||||||
|
|
||||||
del entries[0]
|
del entries[0]
|
||||||
|
|
||||||
# Group codepoints by attribute key
|
# Group codepoints by attribute key
|
||||||
grouped = defaultdict(list)
|
grouped = defaultdict[AttributeHash, list[int]](list)
|
||||||
for cp, attr in entries.items():
|
for cp, attr in entries.items():
|
||||||
grouped[attr_key(attr)].append(cp)
|
grouped[attr_key(attr)].append(cp)
|
||||||
|
|
||||||
# Emit zig switch arms
|
# Emit zig switch arms
|
||||||
result = []
|
result: list[str] = []
|
||||||
for _, codepoints in sorted(grouped.items(), key=lambda x: x[1]):
|
for codepoints in sorted(grouped.values()):
|
||||||
# Use one of the attrs in the group to emit the value
|
# Use one of the attrs in the group to emit the value
|
||||||
attr = entries[codepoints[0]]
|
attr = entries[codepoints[0]]
|
||||||
result.append(emit_zig_entry_multikey(codepoints, attr))
|
result.append(emit_zig_entry_multikey(codepoints, attr))
|
||||||
@ -225,23 +251,16 @@ def generate_zig_switch_arms(patch_set):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
path = (
|
project_root = Path(__file__).resolve().parents[2]
|
||||||
Path(__file__).resolve().parent
|
|
||||||
/ ".."
|
|
||||||
/ ".."
|
|
||||||
/ "vendor"
|
|
||||||
/ "nerd-fonts"
|
|
||||||
/ "font-patcher.py"
|
|
||||||
)
|
|
||||||
with open(path, "r", encoding="utf-8") as f:
|
|
||||||
source = f.read()
|
|
||||||
|
|
||||||
|
patcher_path = project_root / "vendor" / "nerd-fonts" / "font-patcher.py"
|
||||||
|
source = patcher_path.read_text(encoding="utf-8")
|
||||||
patch_set = extract_patch_set_values(source)
|
patch_set = extract_patch_set_values(source)
|
||||||
|
|
||||||
out_path = Path(__file__).resolve().parent / "nerd_font_attributes.zig"
|
out_path = project_root / "src" / "font" / "nerd_font_attributes.zig"
|
||||||
|
|
||||||
with open(out_path, "w", encoding="utf-8") as f:
|
with out_path.open("w", encoding="utf-8") as f:
|
||||||
f.write("""//! This is a generate file, produced by nerd_font_codegen.py
|
f.write("""//! This is a generated file, produced by nerd_font_codegen.py
|
||||||
//! DO NOT EDIT BY HAND!
|
//! DO NOT EDIT BY HAND!
|
||||||
//!
|
//!
|
||||||
//! This file provides info extracted from the nerd fonts patcher script,
|
//! This file provides info extracted from the nerd fonts patcher script,
|
||||||
@ -254,6 +273,4 @@ pub fn getConstraint(cp: u21) Constraint {
|
|||||||
return switch (cp) {
|
return switch (cp) {
|
||||||
""")
|
""")
|
||||||
f.write(generate_zig_switch_arms(patch_set))
|
f.write(generate_zig_switch_arms(patch_set))
|
||||||
f.write("\n")
|
f.write("\n else => .none,\n };\n}\n")
|
||||||
|
|
||||||
f.write(" else => .none,\n };\n}\n")
|
|
||||||
|
@ -195,7 +195,6 @@ pub fn renderGlyph(
|
|||||||
.offset_y = 0,
|
.offset_y = 0,
|
||||||
.atlas_x = 0,
|
.atlas_x = 0,
|
||||||
.atlas_y = 0,
|
.atlas_y = 0,
|
||||||
.advance_x = 0,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const metrics = self.metrics;
|
const metrics = self.metrics;
|
||||||
@ -227,8 +226,6 @@ pub fn renderGlyph(
|
|||||||
.offset_y = @as(i32, @intCast(region.height +| canvas.clip_bottom)) - @as(i32, @intCast(padding_y)),
|
.offset_y = @as(i32, @intCast(region.height +| canvas.clip_bottom)) - @as(i32, @intCast(padding_y)),
|
||||||
.atlas_x = region.x,
|
.atlas_x = region.x,
|
||||||
.atlas_y = region.y,
|
.atlas_y = region.y,
|
||||||
.advance_x = @floatFromInt(width),
|
|
||||||
.sprite = true,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,24 +140,7 @@ pub const Canvas = struct {
|
|||||||
const region_height = sfc_height -| self.clip_top -| self.clip_bottom;
|
const region_height = sfc_height -| self.clip_top -| self.clip_bottom;
|
||||||
|
|
||||||
// Allocate our texture atlas region
|
// Allocate our texture atlas region
|
||||||
const region = region: {
|
const region = try atlas.reserve(alloc, region_width, region_height);
|
||||||
// Reserve a region with a 1px margin on the bottom and right edges
|
|
||||||
// so that we can avoid interpolation between adjacent glyphs during
|
|
||||||
// texture sampling.
|
|
||||||
var region = try atlas.reserve(
|
|
||||||
alloc,
|
|
||||||
region_width + 1,
|
|
||||||
region_height + 1,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Modify the region to remove the margin so that we write to the
|
|
||||||
// non-zero location. The data in an Altlas is always initialized
|
|
||||||
// to zero (Atlas.clear) so we don't need to worry about zero-ing
|
|
||||||
// that.
|
|
||||||
region.width -= 1;
|
|
||||||
region.height -= 1;
|
|
||||||
break :region region;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (region.width > 0 and region.height > 0) {
|
if (region.width > 0 and region.height > 0) {
|
||||||
const buffer: []u8 = @ptrCast(self.sfc.image_surface_alpha8.buf);
|
const buffer: []u8 = @ptrCast(self.sfc.image_surface_alpha8.buf);
|
||||||
|
@ -281,6 +281,10 @@ pub const Action = union(enum) {
|
|||||||
/// If there is a URL under the cursor, copy it to the default clipboard.
|
/// If there is a URL under the cursor, copy it to the default clipboard.
|
||||||
copy_url_to_clipboard,
|
copy_url_to_clipboard,
|
||||||
|
|
||||||
|
/// Copy the terminal title to the clipboard. If the terminal title is not
|
||||||
|
/// set or is empty this has no effect.
|
||||||
|
copy_title_to_clipboard,
|
||||||
|
|
||||||
/// Increase the font size by the specified amount in points (pt).
|
/// Increase the font size by the specified amount in points (pt).
|
||||||
///
|
///
|
||||||
/// For example, `increase_font_size:1.5` will increase the font size
|
/// For example, `increase_font_size:1.5` will increase the font size
|
||||||
@ -296,6 +300,12 @@ pub const Action = union(enum) {
|
|||||||
/// Reset the font size to the original configured size.
|
/// Reset the font size to the original configured size.
|
||||||
reset_font_size,
|
reset_font_size,
|
||||||
|
|
||||||
|
/// Set the font size to the specified size in points (pt).
|
||||||
|
///
|
||||||
|
/// For example, `set_font_size:14.5` will set the font size
|
||||||
|
/// to 14.5 points.
|
||||||
|
set_font_size: f32,
|
||||||
|
|
||||||
/// Clear the screen and all scrollback.
|
/// Clear the screen and all scrollback.
|
||||||
clear_screen,
|
clear_screen,
|
||||||
|
|
||||||
@ -999,11 +1009,13 @@ pub const Action = union(enum) {
|
|||||||
.reset,
|
.reset,
|
||||||
.copy_to_clipboard,
|
.copy_to_clipboard,
|
||||||
.copy_url_to_clipboard,
|
.copy_url_to_clipboard,
|
||||||
|
.copy_title_to_clipboard,
|
||||||
.paste_from_clipboard,
|
.paste_from_clipboard,
|
||||||
.paste_from_selection,
|
.paste_from_selection,
|
||||||
.increase_font_size,
|
.increase_font_size,
|
||||||
.decrease_font_size,
|
.decrease_font_size,
|
||||||
.reset_font_size,
|
.reset_font_size,
|
||||||
|
.set_font_size,
|
||||||
.prompt_surface_title,
|
.prompt_surface_title,
|
||||||
.clear_screen,
|
.clear_screen,
|
||||||
.select_all,
|
.select_all,
|
||||||
@ -3065,6 +3077,7 @@ test "set: getEvent codepoint case folding" {
|
|||||||
try testing.expect(action == null);
|
try testing.expect(action == null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test "Action: clone" {
|
test "Action: clone" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
var arena = std.heap.ArenaAllocator.init(testing.allocator);
|
var arena = std.heap.ArenaAllocator.init(testing.allocator);
|
||||||
@ -3083,3 +3096,42 @@ test "Action: clone" {
|
|||||||
try testing.expect(b == .text);
|
try testing.expect(b == .text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "parse: increase_font_size" {
|
||||||
|
const testing = std.testing;
|
||||||
|
|
||||||
|
{
|
||||||
|
const binding = try parseSingle("a=increase_font_size:1.5");
|
||||||
|
try testing.expect(binding.action == .increase_font_size);
|
||||||
|
try testing.expectEqual(1.5, binding.action.increase_font_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "parse: decrease_font_size" {
|
||||||
|
const testing = std.testing;
|
||||||
|
|
||||||
|
{
|
||||||
|
const binding = try parseSingle("a=decrease_font_size:2.5");
|
||||||
|
try testing.expect(binding.action == .decrease_font_size);
|
||||||
|
try testing.expectEqual(2.5, binding.action.decrease_font_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "parse: reset_font_size" {
|
||||||
|
const testing = std.testing;
|
||||||
|
|
||||||
|
{
|
||||||
|
const binding = try parseSingle("a=reset_font_size");
|
||||||
|
try testing.expect(binding.action == .reset_font_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "parse: set_font_size" {
|
||||||
|
const testing = std.testing;
|
||||||
|
|
||||||
|
{
|
||||||
|
const binding = try parseSingle("a=set_font_size:13.5");
|
||||||
|
try testing.expect(binding.action == .set_font_size);
|
||||||
|
try testing.expectEqual(13.5, binding.action.set_font_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const Action = @import("Binding.zig").Action;
|
const Action = @import("Binding.zig").Action;
|
||||||
@ -131,6 +132,12 @@ fn actionCommands(action: Action.Key) []const Command {
|
|||||||
.description = "Copy the URL under the cursor to the clipboard.",
|
.description = "Copy the URL under the cursor to the clipboard.",
|
||||||
}},
|
}},
|
||||||
|
|
||||||
|
.copy_title_to_clipboard => comptime &.{.{
|
||||||
|
.action = .copy_title_to_clipboard,
|
||||||
|
.title = "Copy Terminal Title to Clipboard",
|
||||||
|
.description = "Copy the terminal title to the clipboard. If the terminal title is not set this has no effect.",
|
||||||
|
}},
|
||||||
|
|
||||||
.paste_from_clipboard => comptime &.{.{
|
.paste_from_clipboard => comptime &.{.{
|
||||||
.action = .paste_from_clipboard,
|
.action = .paste_from_clipboard,
|
||||||
.title = "Paste from Clipboard",
|
.title = "Paste from Clipboard",
|
||||||
@ -460,6 +467,7 @@ fn actionCommands(action: Action.Key) []const Command {
|
|||||||
.esc,
|
.esc,
|
||||||
.text,
|
.text,
|
||||||
.cursor_key,
|
.cursor_key,
|
||||||
|
.set_font_size,
|
||||||
.scroll_page_fractional,
|
.scroll_page_fractional,
|
||||||
.scroll_page_lines,
|
.scroll_page_lines,
|
||||||
.adjust_selection,
|
.adjust_selection,
|
||||||
|
@ -19,7 +19,12 @@ const internal_os = @import("os/main.zig");
|
|||||||
|
|
||||||
// Some comptime assertions that our C API depends on.
|
// Some comptime assertions that our C API depends on.
|
||||||
comptime {
|
comptime {
|
||||||
assert(apprt.runtime == apprt.embedded);
|
// We allow tests to reference this file because we unit test
|
||||||
|
// some of the C API. At runtime though we should never get these
|
||||||
|
// functions unless we are building libghostty.
|
||||||
|
if (!builtin.is_test) {
|
||||||
|
assert(apprt.runtime == apprt.embedded);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Global options so we can log. This is identical to main.
|
/// Global options so we can log. This is identical to main.
|
||||||
@ -29,7 +34,9 @@ comptime {
|
|||||||
// These structs need to be referenced so the `export` functions
|
// These structs need to be referenced so the `export` functions
|
||||||
// are truly exported by the C API lib.
|
// are truly exported by the C API lib.
|
||||||
_ = @import("config.zig").CAPI;
|
_ = @import("config.zig").CAPI;
|
||||||
_ = apprt.runtime.CAPI;
|
if (@hasDecl(apprt.runtime, "CAPI")) {
|
||||||
|
_ = apprt.runtime.CAPI;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ghostty_info_s
|
/// ghostty_info_s
|
||||||
@ -46,17 +53,29 @@ const Info = extern struct {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Initialize ghostty global state. It is possible to have more than
|
/// ghostty_string_s
|
||||||
/// one global state but it has zero practical benefit.
|
pub const String = extern struct {
|
||||||
export fn ghostty_init() c_int {
|
ptr: ?[*]const u8,
|
||||||
|
len: usize,
|
||||||
|
|
||||||
|
pub const empty: String = .{
|
||||||
|
.ptr = null,
|
||||||
|
.len = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn fromSlice(slice: []const u8) String {
|
||||||
|
return .{
|
||||||
|
.ptr = slice.ptr,
|
||||||
|
.len = slice.len,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Initialize ghostty global state.
|
||||||
|
export fn ghostty_init(argc: usize, argv: [*][*:0]u8) c_int {
|
||||||
assert(builtin.link_libc);
|
assert(builtin.link_libc);
|
||||||
|
|
||||||
// Since in the lib we don't go through start.zig, we need
|
std.os.argv = argv[0..argc];
|
||||||
// to populate argv so that inspecting std.os.argv doesn't
|
|
||||||
// touch uninitialized memory.
|
|
||||||
var argv: [0][*:0]u8 = .{};
|
|
||||||
std.os.argv = &argv;
|
|
||||||
|
|
||||||
state.init() catch |err| {
|
state.init() catch |err| {
|
||||||
std.log.err("failed to initialize ghostty error={}", .{err});
|
std.log.err("failed to initialize ghostty error={}", .{err});
|
||||||
return 1;
|
return 1;
|
||||||
@ -65,15 +84,17 @@ export fn ghostty_init() c_int {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is the entrypoint for the CLI version of Ghostty. This
|
/// Runs an action if it is specified. If there is no action this returns
|
||||||
/// is mutually exclusive to ghostty_init. Do NOT run ghostty_init
|
/// false. If there is an action then this doesn't return.
|
||||||
/// if you are going to run this. This will not return.
|
export fn ghostty_cli_try_action() void {
|
||||||
export fn ghostty_cli_main(argc: usize, argv: [*][*:0]u8) noreturn {
|
const action = state.action orelse return;
|
||||||
std.os.argv = argv[0..argc];
|
std.log.info("executing CLI action={}", .{action});
|
||||||
main.main() catch |err| {
|
posix.exit(action.run(state.alloc) catch |err| {
|
||||||
std.log.err("failed to run ghostty error={}", .{err});
|
std.log.err("CLI action failed error={}", .{err});
|
||||||
posix.exit(1);
|
posix.exit(1);
|
||||||
};
|
});
|
||||||
|
|
||||||
|
posix.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return metadata about Ghostty, such as version, build mode, etc.
|
/// Return metadata about Ghostty, such as version, build mode, etc.
|
||||||
@ -99,3 +120,8 @@ export fn ghostty_info() Info {
|
|||||||
export fn ghostty_translate(msgid: [*:0]const u8) [*:0]const u8 {
|
export fn ghostty_translate(msgid: [*:0]const u8) [*:0]const u8 {
|
||||||
return internal_os.i18n._(msgid);
|
return internal_os.i18n._(msgid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Free a string allocated by Ghostty.
|
||||||
|
export fn ghostty_string_free(str: String) void {
|
||||||
|
state.alloc.free(str.ptr.?[0..str.len]);
|
||||||
|
}
|
||||||
|
@ -24,8 +24,15 @@ pub fn launchedFromDesktop() bool {
|
|||||||
// This special case is so that if we launch the app via the
|
// This special case is so that if we launch the app via the
|
||||||
// app bundle (i.e. via open) then we still treat it as if it
|
// app bundle (i.e. via open) then we still treat it as if it
|
||||||
// was launched from the desktop.
|
// was launched from the desktop.
|
||||||
if (build_config.artifact == .lib and
|
if (build_config.artifact == .lib) lib: {
|
||||||
posix.getenv("GHOSTTY_MAC_APP") != null) break :macos true;
|
const env = "GHOSTTY_MAC_LAUNCH_SOURCE";
|
||||||
|
const source = posix.getenv(env) orelse break :lib;
|
||||||
|
|
||||||
|
// Source can be "app", "cli", or "zig_run". We assume
|
||||||
|
// its the desktop only if its "app". We may want to do
|
||||||
|
// "zig_run" but at the moment there's no reason.
|
||||||
|
if (std.mem.eql(u8, source, "app")) break :macos true;
|
||||||
|
}
|
||||||
|
|
||||||
break :macos c.getppid() == 1;
|
break :macos c.getppid() == 1;
|
||||||
},
|
},
|
||||||
|
@ -49,6 +49,7 @@ pub const locales = [_][:0]const u8{
|
|||||||
"ca_ES.UTF-8",
|
"ca_ES.UTF-8",
|
||||||
"bg_BG.UTF-8",
|
"bg_BG.UTF-8",
|
||||||
"ga_IE.UTF-8",
|
"ga_IE.UTF-8",
|
||||||
|
"he_IL.UTF-8",
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Set for faster membership lookup of locales.
|
/// Set for faster membership lookup of locales.
|
||||||
|
27
src/os/kernel_info.zig
Normal file
27
src/os/kernel_info.zig
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
|
||||||
|
pub fn getKernelInfo(alloc: std.mem.Allocator) ?[]const u8 {
|
||||||
|
if (comptime builtin.os.tag != .linux) return null;
|
||||||
|
const path = "/proc/sys/kernel/osrelease";
|
||||||
|
var file = std.fs.openFileAbsolute(path, .{}) catch return null;
|
||||||
|
defer file.close();
|
||||||
|
|
||||||
|
// 128 bytes should be enough to hold the kernel information
|
||||||
|
const kernel_info = file.readToEndAlloc(alloc, 128) catch return null;
|
||||||
|
defer alloc.free(kernel_info);
|
||||||
|
return alloc.dupe(u8, std.mem.trim(u8, kernel_info, &std.ascii.whitespace)) catch return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
test "read /proc/sys/kernel/osrelease" {
|
||||||
|
if (comptime builtin.os.tag != .linux) return null;
|
||||||
|
const allocator = std.testing.allocator;
|
||||||
|
|
||||||
|
const kernel_info = try getKernelInfo(allocator);
|
||||||
|
defer allocator.free(kernel_info);
|
||||||
|
|
||||||
|
// Since we can't hardcode the info in tests, just check
|
||||||
|
// if something was read from the file
|
||||||
|
try std.testing.expect(kernel_info.len > 0);
|
||||||
|
try std.testing.expect(!std.mem.eql(u8, kernel_info, ""));
|
||||||
|
}
|
@ -14,6 +14,7 @@ const openpkg = @import("open.zig");
|
|||||||
const pipepkg = @import("pipe.zig");
|
const pipepkg = @import("pipe.zig");
|
||||||
const resourcesdir = @import("resourcesdir.zig");
|
const resourcesdir = @import("resourcesdir.zig");
|
||||||
const systemd = @import("systemd.zig");
|
const systemd = @import("systemd.zig");
|
||||||
|
const kernelInfo = @import("kernel_info.zig");
|
||||||
|
|
||||||
// Namespaces
|
// Namespaces
|
||||||
pub const args = @import("args.zig");
|
pub const args = @import("args.zig");
|
||||||
@ -58,6 +59,7 @@ pub const pipe = pipepkg.pipe;
|
|||||||
pub const resourcesDir = resourcesdir.resourcesDir;
|
pub const resourcesDir = resourcesdir.resourcesDir;
|
||||||
pub const ResourcesDir = resourcesdir.ResourcesDir;
|
pub const ResourcesDir = resourcesdir.ResourcesDir;
|
||||||
pub const ShellEscapeWriter = shell.ShellEscapeWriter;
|
pub const ShellEscapeWriter = shell.ShellEscapeWriter;
|
||||||
|
pub const getKernelInfo = kernelInfo.getKernelInfo;
|
||||||
|
|
||||||
test {
|
test {
|
||||||
_ = i18n;
|
_ = i18n;
|
||||||
|
@ -1,24 +1,23 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
const apprt = @import("../apprt.zig");
|
||||||
|
|
||||||
const log = std.log.scoped(.@"os-open");
|
const log = std.log.scoped(.@"os-open");
|
||||||
|
|
||||||
/// The type of the data at the URL to open. This is used as a hint
|
|
||||||
/// to potentially open the URL in a different way.
|
|
||||||
pub const Type = enum {
|
|
||||||
text,
|
|
||||||
unknown,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Open a URL in the default handling application.
|
/// Open a URL in the default handling application.
|
||||||
///
|
///
|
||||||
/// Any output on stderr is logged as a warning in the application logs.
|
/// Any output on stderr is logged as a warning in the application logs.
|
||||||
/// Output on stdout is ignored. The allocator is used to buffer the
|
/// Output on stdout is ignored. The allocator is used to buffer the
|
||||||
/// log output and may allocate from another thread.
|
/// log output and may allocate from another thread.
|
||||||
|
///
|
||||||
|
/// This function is purposely simple for the sake of providing
|
||||||
|
/// some portable way to open URLs. If you are implementing an
|
||||||
|
/// apprt for Ghostty, you should consider doing something special-cased
|
||||||
|
/// for your platform.
|
||||||
pub fn open(
|
pub fn open(
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
typ: Type,
|
kind: apprt.action.OpenUrl.Kind,
|
||||||
url: []const u8,
|
url: []const u8,
|
||||||
) !void {
|
) !void {
|
||||||
var exe: std.process.Child = switch (builtin.os.tag) {
|
var exe: std.process.Child = switch (builtin.os.tag) {
|
||||||
@ -33,7 +32,7 @@ pub fn open(
|
|||||||
),
|
),
|
||||||
|
|
||||||
.macos => .init(
|
.macos => .init(
|
||||||
switch (typ) {
|
switch (kind) {
|
||||||
.text => &.{ "open", "-t", url },
|
.text => &.{ "open", "-t", url },
|
||||||
.unknown => &.{ "open", url },
|
.unknown => &.{ "open", url },
|
||||||
},
|
},
|
||||||
|
@ -356,6 +356,10 @@ pub inline fn textureOptions(self: OpenGL) Texture.Options {
|
|||||||
.format = .rgba,
|
.format = .rgba,
|
||||||
.internal_format = .srgba,
|
.internal_format = .srgba,
|
||||||
.target = .@"2D",
|
.target = .@"2D",
|
||||||
|
.min_filter = .linear,
|
||||||
|
.mag_filter = .linear,
|
||||||
|
.wrap_s = .clamp_to_edge,
|
||||||
|
.wrap_t = .clamp_to_edge,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -388,6 +392,16 @@ pub inline fn imageTextureOptions(
|
|||||||
.format = format.toPixelFormat(),
|
.format = format.toPixelFormat(),
|
||||||
.internal_format = if (srgb) .srgba else .rgba,
|
.internal_format = if (srgb) .srgba else .rgba,
|
||||||
.target = .@"2D",
|
.target = .@"2D",
|
||||||
|
// TODO: Generate mipmaps for image textures and use
|
||||||
|
// linear_mipmap_linear filtering so that they
|
||||||
|
// look good even when scaled way down.
|
||||||
|
.min_filter = .linear,
|
||||||
|
.mag_filter = .linear,
|
||||||
|
// TODO: Separate out background image options, use
|
||||||
|
// repeating coordinate modes so we don't have
|
||||||
|
// to do the modulus in the shader.
|
||||||
|
.wrap_s = .clamp_to_edge,
|
||||||
|
.wrap_t = .clamp_to_edge,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -409,6 +423,10 @@ pub fn initAtlasTexture(
|
|||||||
.format = format,
|
.format = format,
|
||||||
.internal_format = internal_format,
|
.internal_format = internal_format,
|
||||||
.target = .Rectangle,
|
.target = .Rectangle,
|
||||||
|
.min_filter = .nearest,
|
||||||
|
.mag_filter = .nearest,
|
||||||
|
.wrap_s = .clamp_to_edge,
|
||||||
|
.wrap_t = .clamp_to_edge,
|
||||||
},
|
},
|
||||||
atlas.size,
|
atlas.size,
|
||||||
atlas.size,
|
atlas.size,
|
||||||
|
@ -519,7 +519,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
|||||||
foreground: terminal.color.RGB,
|
foreground: terminal.color.RGB,
|
||||||
selection_background: ?configpkg.Config.TerminalColor,
|
selection_background: ?configpkg.Config.TerminalColor,
|
||||||
selection_foreground: ?configpkg.Config.TerminalColor,
|
selection_foreground: ?configpkg.Config.TerminalColor,
|
||||||
bold_is_bright: bool,
|
bold_color: ?configpkg.BoldColor,
|
||||||
min_contrast: f32,
|
min_contrast: f32,
|
||||||
padding_color: configpkg.WindowPaddingColor,
|
padding_color: configpkg.WindowPaddingColor,
|
||||||
custom_shaders: configpkg.RepeatablePath,
|
custom_shaders: configpkg.RepeatablePath,
|
||||||
@ -580,7 +580,8 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
|||||||
|
|
||||||
.background = config.background.toTerminalRGB(),
|
.background = config.background.toTerminalRGB(),
|
||||||
.foreground = config.foreground.toTerminalRGB(),
|
.foreground = config.foreground.toTerminalRGB(),
|
||||||
.bold_is_bright = config.@"bold-is-bright",
|
.bold_color = config.@"bold-color",
|
||||||
|
|
||||||
.min_contrast = @floatCast(config.@"minimum-contrast"),
|
.min_contrast = @floatCast(config.@"minimum-contrast"),
|
||||||
.padding_color = config.@"window-padding-color",
|
.padding_color = config.@"window-padding-color",
|
||||||
|
|
||||||
@ -2540,10 +2541,11 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
|||||||
// the cell style (SGR), before applying any additional
|
// the cell style (SGR), before applying any additional
|
||||||
// configuration, inversions, selections, etc.
|
// configuration, inversions, selections, etc.
|
||||||
const bg_style = style.bg(cell, color_palette);
|
const bg_style = style.bg(cell, color_palette);
|
||||||
const fg_style = style.fg(
|
const fg_style = style.fg(.{
|
||||||
color_palette,
|
.default = self.foreground_color orelse self.default_foreground_color,
|
||||||
self.config.bold_is_bright,
|
.palette = color_palette,
|
||||||
) orelse self.foreground_color orelse self.default_foreground_color;
|
.bold = self.config.bold_color,
|
||||||
|
});
|
||||||
|
|
||||||
// The final background color for the cell.
|
// The final background color for the cell.
|
||||||
const bg = bg: {
|
const bg = bg: {
|
||||||
@ -2801,10 +2803,11 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
|||||||
.@"cell-background",
|
.@"cell-background",
|
||||||
=> |_, tag| {
|
=> |_, tag| {
|
||||||
const sty = screen.cursor.page_pin.style(screen.cursor.page_cell);
|
const sty = screen.cursor.page_pin.style(screen.cursor.page_cell);
|
||||||
const fg_style = sty.fg(
|
const fg_style = sty.fg(.{
|
||||||
color_palette,
|
.default = self.foreground_color orelse self.default_foreground_color,
|
||||||
self.config.bold_is_bright,
|
.palette = color_palette,
|
||||||
) orelse self.foreground_color orelse self.default_foreground_color;
|
.bold = self.config.bold_color,
|
||||||
|
});
|
||||||
const bg_style = sty.bg(
|
const bg_style = sty.bg(
|
||||||
screen.cursor.page_cell,
|
screen.cursor.page_cell,
|
||||||
color_palette,
|
color_palette,
|
||||||
@ -2852,7 +2855,11 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const sty = screen.cursor.page_pin.style(screen.cursor.page_cell);
|
const sty = screen.cursor.page_pin.style(screen.cursor.page_cell);
|
||||||
const fg_style = sty.fg(color_palette, self.config.bold_is_bright) orelse self.foreground_color orelse self.default_foreground_color;
|
const fg_style = sty.fg(.{
|
||||||
|
.default = self.foreground_color orelse self.default_foreground_color,
|
||||||
|
.palette = color_palette,
|
||||||
|
.bold = self.config.bold_color,
|
||||||
|
});
|
||||||
const bg_style = sty.bg(screen.cursor.page_cell, color_palette) orelse self.background_color orelse self.default_background_color;
|
const bg_style = sty.bg(screen.cursor.page_cell, color_palette) orelse self.background_color orelse self.default_background_color;
|
||||||
|
|
||||||
break :blk switch (txt) {
|
break :blk switch (txt) {
|
||||||
|
@ -16,6 +16,10 @@ pub const Options = struct {
|
|||||||
format: gl.Texture.Format,
|
format: gl.Texture.Format,
|
||||||
internal_format: gl.Texture.InternalFormat,
|
internal_format: gl.Texture.InternalFormat,
|
||||||
target: gl.Texture.Target,
|
target: gl.Texture.Target,
|
||||||
|
min_filter: gl.Texture.MinFilter,
|
||||||
|
mag_filter: gl.Texture.MagFilter,
|
||||||
|
wrap_s: gl.Texture.Wrap,
|
||||||
|
wrap_t: gl.Texture.Wrap,
|
||||||
};
|
};
|
||||||
|
|
||||||
texture: gl.Texture,
|
texture: gl.Texture,
|
||||||
@ -48,10 +52,10 @@ pub fn init(
|
|||||||
{
|
{
|
||||||
const texbind = tex.bind(opts.target) catch return error.OpenGLFailed;
|
const texbind = tex.bind(opts.target) catch return error.OpenGLFailed;
|
||||||
defer texbind.unbind();
|
defer texbind.unbind();
|
||||||
texbind.parameter(.WrapS, gl.c.GL_CLAMP_TO_EDGE) catch return error.OpenGLFailed;
|
texbind.parameter(.WrapS, @intFromEnum(opts.wrap_s)) catch return error.OpenGLFailed;
|
||||||
texbind.parameter(.WrapT, gl.c.GL_CLAMP_TO_EDGE) catch return error.OpenGLFailed;
|
texbind.parameter(.WrapT, @intFromEnum(opts.wrap_t)) catch return error.OpenGLFailed;
|
||||||
texbind.parameter(.MinFilter, gl.c.GL_LINEAR) catch return error.OpenGLFailed;
|
texbind.parameter(.MinFilter, @intFromEnum(opts.min_filter)) catch return error.OpenGLFailed;
|
||||||
texbind.parameter(.MagFilter, gl.c.GL_LINEAR) catch return error.OpenGLFailed;
|
texbind.parameter(.MagFilter, @intFromEnum(opts.mag_filter)) catch return error.OpenGLFailed;
|
||||||
texbind.image2D(
|
texbind.image2D(
|
||||||
0,
|
0,
|
||||||
opts.internal_format,
|
opts.internal_format,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
|
const configpkg = @import("../config.zig");
|
||||||
const color = @import("color.zig");
|
const color = @import("color.zig");
|
||||||
const sgr = @import("sgr.zig");
|
const sgr = @import("sgr.zig");
|
||||||
const page = @import("page.zig");
|
const page = @import("page.zig");
|
||||||
@ -115,24 +116,68 @@ pub const Style = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the fg color for a cell with this style given the palette.
|
pub const Fg = struct {
|
||||||
|
/// The default color to use if the style doesn't specify a
|
||||||
|
/// foreground color and no configuration options override
|
||||||
|
/// it.
|
||||||
|
default: color.RGB,
|
||||||
|
|
||||||
|
/// The current color palette. Required to map palette indices to
|
||||||
|
/// real color values.
|
||||||
|
palette: *const color.Palette,
|
||||||
|
|
||||||
|
/// If specified, the color to use for bold text.
|
||||||
|
bold: ?configpkg.BoldColor = null,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Returns the fg color for a cell with this style given the palette
|
||||||
|
/// and various configuration options.
|
||||||
pub fn fg(
|
pub fn fg(
|
||||||
self: Style,
|
self: Style,
|
||||||
palette: *const color.Palette,
|
opts: Fg,
|
||||||
bold_is_bright: bool,
|
) color.RGB {
|
||||||
) ?color.RGB {
|
// Note we don't pull the bold check to the top-level here because
|
||||||
|
// we don't want to duplicate the conditional multiple times since
|
||||||
|
// certain colors require more checks (e.g. `bold_is_bright`).
|
||||||
|
|
||||||
return switch (self.fg_color) {
|
return switch (self.fg_color) {
|
||||||
.none => null,
|
.none => default: {
|
||||||
.palette => |idx| palette: {
|
if (self.flags.bold) {
|
||||||
if (bold_is_bright and self.flags.bold) {
|
if (opts.bold) |bold| switch (bold) {
|
||||||
const bright_offset = @intFromEnum(color.Name.bright_black);
|
.bright => {},
|
||||||
if (idx < bright_offset)
|
.color => |v| break :default v.toTerminalRGB(),
|
||||||
break :palette palette[idx + bright_offset];
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
break :palette palette[idx];
|
break :default opts.default;
|
||||||
|
},
|
||||||
|
|
||||||
|
.palette => |idx| palette: {
|
||||||
|
if (self.flags.bold) {
|
||||||
|
if (opts.bold) |bold| switch (bold) {
|
||||||
|
.color => |v| break :palette v.toTerminalRGB(),
|
||||||
|
.bright => {
|
||||||
|
const bright_offset = @intFromEnum(color.Name.bright_black);
|
||||||
|
if (idx < bright_offset) {
|
||||||
|
break :palette opts.palette[idx + bright_offset];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
break :palette opts.palette[idx];
|
||||||
|
},
|
||||||
|
|
||||||
|
.rgb => |rgb| rgb: {
|
||||||
|
if (self.flags.bold and rgb.eql(opts.default)) {
|
||||||
|
if (opts.bold) |bold| switch (bold) {
|
||||||
|
.color => |v| break :rgb v.toTerminalRGB(),
|
||||||
|
.bright => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
break :rgb rgb;
|
||||||
},
|
},
|
||||||
.rgb => |rgb| rgb,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user