Merge remote-tracking branch 'origin/main' into gh-1972-clickable-file-paths
3
.gitattributes
vendored
@ -1,3 +1,6 @@
|
|||||||
|
build.zig.zon.nix linguist-generated=true
|
||||||
|
build.zig.zon.txt linguist-generated=true
|
||||||
|
build.zig.zon2json-lock linguist-generated=true
|
||||||
vendor/** linguist-vendored
|
vendor/** linguist-vendored
|
||||||
website/** linguist-documentation
|
website/** linguist-documentation
|
||||||
pkg/breakpad/vendor/** linguist-vendored
|
pkg/breakpad/vendor/** linguist-vendored
|
||||||
|
29
.github/workflows/nix.yml
vendored
@ -1,6 +1,31 @@
|
|||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
name: Nix
|
name: Nix
|
||||||
jobs:
|
jobs:
|
||||||
|
required:
|
||||||
|
name: "Required Checks: Nix"
|
||||||
|
runs-on: namespace-profile-ghostty-sm
|
||||||
|
needs:
|
||||||
|
- check-zig-cache-hash
|
||||||
|
steps:
|
||||||
|
- id: status
|
||||||
|
name: Determine status
|
||||||
|
run: |
|
||||||
|
results=$(tr -d '\n' <<< '${{ toJSON(needs.*.result) }}')
|
||||||
|
if ! grep -q -v -E '(failure|cancelled)' <<< "$results"; then
|
||||||
|
result="failed"
|
||||||
|
else
|
||||||
|
result="success"
|
||||||
|
fi
|
||||||
|
{
|
||||||
|
echo "result=${result}"
|
||||||
|
echo "results=${results}"
|
||||||
|
} | tee -a "$GITHUB_OUTPUT"
|
||||||
|
- if: always() && steps.status.outputs.result != 'success'
|
||||||
|
name: Check for failed status
|
||||||
|
run: |
|
||||||
|
echo "One or more required build workflows failed: ${{ steps.status.outputs.results }}"
|
||||||
|
exit 1
|
||||||
|
|
||||||
check-zig-cache-hash:
|
check-zig-cache-hash:
|
||||||
if: github.repository == 'ghostty-org/ghostty'
|
if: github.repository == 'ghostty-org/ghostty'
|
||||||
runs-on: namespace-profile-ghostty-sm
|
runs-on: namespace-profile-ghostty-sm
|
||||||
@ -25,5 +50,5 @@ jobs:
|
|||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
useDaemon: false # sometimes fails on short jobs
|
useDaemon: false # sometimes fails on short jobs
|
||||||
- name: Check Zig cache hash
|
- name: Check Zig cache
|
||||||
run: nix develop -c ./nix/build-support/check-zig-cache-hash.sh
|
run: nix develop -c ./nix/build-support/check-zig-cache.sh
|
||||||
|
74
.github/workflows/publish-tag.yml
vendored
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: "Version to deploy (format: vX.Y.Z)"
|
||||||
|
required: true
|
||||||
|
|
||||||
|
name: Publish Tagged Release
|
||||||
|
|
||||||
|
# We must only run one release workflow at a time to prevent corrupting
|
||||||
|
# our release artifacts.
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
setup:
|
||||||
|
runs-on: namespace-profile-ghostty-sm
|
||||||
|
outputs:
|
||||||
|
version: ${{ steps.extract_version.outputs.version }}
|
||||||
|
steps:
|
||||||
|
- name: Validate Version Input
|
||||||
|
run: |
|
||||||
|
if [[ ! "${{ github.event.inputs.version }}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||||
|
echo "Error: Version must follow the format vX.Y.Z (e.g., v1.0.0)."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Version is valid: ${{ github.event.inputs.version }}"
|
||||||
|
|
||||||
|
- name: Exract the Version
|
||||||
|
id: extract_version
|
||||||
|
run: |
|
||||||
|
VERSION=${{ github.event.inputs.version }}
|
||||||
|
VERSION=${VERSION#v}
|
||||||
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
upload:
|
||||||
|
needs: [setup]
|
||||||
|
runs-on: namespace-profile-ghostty-sm
|
||||||
|
env:
|
||||||
|
GHOSTTY_VERSION: ${{ needs.setup.outputs.version }}
|
||||||
|
steps:
|
||||||
|
- name: Validate Release Files
|
||||||
|
run: |
|
||||||
|
BASE="https://release.files.ghostty.org/${GHOSTTY_VERSION}"
|
||||||
|
curl -I -s -o /dev/null -w "%{http_code}" "${BASE}/ghostty-${GHOSTTY_VERSION}.tar.gz" | grep -q "^200$" || exit 1
|
||||||
|
curl -I -s -o /dev/null -w "%{http_code}" "${BASE}/ghostty-${GHOSTTY_VERSION}.tar.gz.minisig" | grep -q "^200$" || exit 1
|
||||||
|
curl -I -s -o /dev/null -w "%{http_code}" "${BASE}/ghostty-source.tar.gz" | grep -q "^200$" || exit 1
|
||||||
|
curl -I -s -o /dev/null -w "%{http_code}" "${BASE}/ghostty-source.tar.gz.minisig" | grep -q "^200$" || exit 1
|
||||||
|
curl -I -s -o /dev/null -w "%{http_code}" "${BASE}/ghostty-macos-universal.zip" | grep -q "^200$" || exit 1
|
||||||
|
curl -I -s -o /dev/null -w "%{http_code}" "${BASE}/ghostty-macos-universal-dsym.zip" | grep -q "^200$" || exit 1
|
||||||
|
curl -I -s -o /dev/null -w "%{http_code}" "${BASE}/Ghostty.dmg" | grep -q "^200$" || exit 1
|
||||||
|
curl -I -s -o /dev/null -w "%{http_code}" "${BASE}/appcast-staged.xml" | grep -q "^200$" || exit 1
|
||||||
|
|
||||||
|
- name: Download Staged Appcast
|
||||||
|
run: |
|
||||||
|
curl -L https://release.files.ghostty.org/${GHOSTTY_VERSION}/appcast-staged.xml > appcast-staged.xml
|
||||||
|
mv appcast-staged.xml appcast.xml
|
||||||
|
|
||||||
|
- name: Upload Appcast
|
||||||
|
run: |
|
||||||
|
rm -rf blob
|
||||||
|
mkdir blob
|
||||||
|
mv appcast.xml blob/appcast.xml
|
||||||
|
- name: Upload Appcast to R2
|
||||||
|
uses: ryand56/r2-upload-action@latest
|
||||||
|
with:
|
||||||
|
r2-account-id: ${{ secrets.CF_R2_RELEASE_ACCOUNT_ID }}
|
||||||
|
r2-access-key-id: ${{ secrets.CF_R2_RELEASE_AWS_KEY }}
|
||||||
|
r2-secret-access-key: ${{ secrets.CF_R2_RELEASE_SECRET_KEY }}
|
||||||
|
r2-bucket: ghostty-release
|
||||||
|
source-dir: blob
|
||||||
|
destination-dir: ./
|
2
.github/workflows/release-pr.yml
vendored
@ -68,7 +68,7 @@ jobs:
|
|||||||
# Setup Sparkle
|
# Setup Sparkle
|
||||||
- name: Setup Sparkle
|
- name: Setup Sparkle
|
||||||
env:
|
env:
|
||||||
SPARKLE_VERSION: 2.6.3
|
SPARKLE_VERSION: 2.6.4
|
||||||
run: |
|
run: |
|
||||||
mkdir -p .action/sparkle
|
mkdir -p .action/sparkle
|
||||||
cd .action/sparkle
|
cd .action/sparkle
|
||||||
|
21
.github/workflows/release-tag.yml
vendored
@ -7,6 +7,7 @@ on:
|
|||||||
upload:
|
upload:
|
||||||
description: "Upload final artifacts to R2"
|
description: "Upload final artifacts to R2"
|
||||||
default: false
|
default: false
|
||||||
|
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- "v[0-9]+.[0-9]+.[0-9]+"
|
- "v[0-9]+.[0-9]+.[0-9]+"
|
||||||
@ -135,7 +136,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Setup Sparkle
|
- name: Setup Sparkle
|
||||||
env:
|
env:
|
||||||
SPARKLE_VERSION: 2.6.3
|
SPARKLE_VERSION: 2.6.4
|
||||||
run: |
|
run: |
|
||||||
mkdir -p .action/sparkle
|
mkdir -p .action/sparkle
|
||||||
cd .action/sparkle
|
cd .action/sparkle
|
||||||
@ -297,7 +298,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Setup Sparkle
|
- name: Setup Sparkle
|
||||||
env:
|
env:
|
||||||
SPARKLE_VERSION: 2.6.3
|
SPARKLE_VERSION: 2.6.4
|
||||||
run: |
|
run: |
|
||||||
mkdir -p .action/sparkle
|
mkdir -p .action/sparkle
|
||||||
cd .action/sparkle
|
cd .action/sparkle
|
||||||
@ -367,6 +368,7 @@ jobs:
|
|||||||
mv ghostty-macos-universal.zip blob/${GHOSTTY_VERSION}/ghostty-macos-universal.zip
|
mv ghostty-macos-universal.zip blob/${GHOSTTY_VERSION}/ghostty-macos-universal.zip
|
||||||
mv ghostty-macos-universal-dsym.zip blob/${GHOSTTY_VERSION}/ghostty-macos-universal-dsym.zip
|
mv ghostty-macos-universal-dsym.zip blob/${GHOSTTY_VERSION}/ghostty-macos-universal-dsym.zip
|
||||||
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
|
||||||
- name: Upload to R2
|
- name: Upload to R2
|
||||||
uses: ryand56/r2-upload-action@latest
|
uses: ryand56/r2-upload-action@latest
|
||||||
with:
|
with:
|
||||||
@ -376,18 +378,3 @@ jobs:
|
|||||||
r2-bucket: ghostty-release
|
r2-bucket: ghostty-release
|
||||||
source-dir: blob
|
source-dir: blob
|
||||||
destination-dir: ./
|
destination-dir: ./
|
||||||
|
|
||||||
- name: Prep Appcast
|
|
||||||
run: |
|
|
||||||
rm -rf blob
|
|
||||||
mkdir blob
|
|
||||||
mv appcast.xml blob/appcast.xml
|
|
||||||
- name: Upload Appcast to R2
|
|
||||||
uses: ryand56/r2-upload-action@latest
|
|
||||||
with:
|
|
||||||
r2-account-id: ${{ secrets.CF_R2_RELEASE_ACCOUNT_ID }}
|
|
||||||
r2-access-key-id: ${{ secrets.CF_R2_RELEASE_AWS_KEY }}
|
|
||||||
r2-secret-access-key: ${{ secrets.CF_R2_RELEASE_SECRET_KEY }}
|
|
||||||
r2-bucket: ghostty-release
|
|
||||||
source-dir: blob
|
|
||||||
destination-dir: ./
|
|
||||||
|
2
.github/workflows/release-tip.yml
vendored
@ -164,7 +164,7 @@ jobs:
|
|||||||
# Setup Sparkle
|
# Setup Sparkle
|
||||||
- name: Setup Sparkle
|
- name: Setup Sparkle
|
||||||
env:
|
env:
|
||||||
SPARKLE_VERSION: 2.6.3
|
SPARKLE_VERSION: 2.6.4
|
||||||
run: |
|
run: |
|
||||||
mkdir -p .action/sparkle
|
mkdir -p .action/sparkle
|
||||||
cd .action/sparkle
|
cd .action/sparkle
|
||||||
|
160
.github/workflows/test.yml
vendored
@ -6,6 +6,47 @@ on:
|
|||||||
name: Test
|
name: Test
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
required:
|
||||||
|
name: "Required Checks: Test"
|
||||||
|
runs-on: namespace-profile-ghostty-sm
|
||||||
|
needs:
|
||||||
|
- build
|
||||||
|
- build-bench
|
||||||
|
- build-linux-libghostty
|
||||||
|
- build-nix
|
||||||
|
- build-snap
|
||||||
|
- build-macos
|
||||||
|
- build-macos-matrix
|
||||||
|
- build-windows
|
||||||
|
- test
|
||||||
|
- test-gtk
|
||||||
|
- test-sentry-linux
|
||||||
|
- test-macos
|
||||||
|
- prettier
|
||||||
|
- alejandra
|
||||||
|
- typos
|
||||||
|
- test-pkg-linux
|
||||||
|
- test-debian-12
|
||||||
|
steps:
|
||||||
|
- id: status
|
||||||
|
name: Determine status
|
||||||
|
run: |
|
||||||
|
results=$(tr -d '\n' <<< '${{ toJSON(needs.*.result) }}')
|
||||||
|
if ! grep -q -v -E '(failure|cancelled)' <<< "$results"; then
|
||||||
|
result="failed"
|
||||||
|
else
|
||||||
|
result="success"
|
||||||
|
fi
|
||||||
|
{
|
||||||
|
echo "result=${result}"
|
||||||
|
echo "results=${results}"
|
||||||
|
} | tee -a "$GITHUB_OUTPUT"
|
||||||
|
- if: always() && steps.status.outputs.result != 'success'
|
||||||
|
name: Check for failed status
|
||||||
|
run: |
|
||||||
|
echo "One or more required build workflows failed: ${{ steps.status.outputs.results }}"
|
||||||
|
exit 1
|
||||||
|
|
||||||
build:
|
build:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
@ -163,10 +204,14 @@ jobs:
|
|||||||
- name: XCode Select
|
- name: XCode Select
|
||||||
run: sudo xcode-select -s /Applications/Xcode_16.0.app
|
run: sudo xcode-select -s /Applications/Xcode_16.0.app
|
||||||
|
|
||||||
|
- name: get the Zig deps
|
||||||
|
id: deps
|
||||||
|
run: nix build -L .#deps && echo "deps=$(readlink ./result)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
# GhosttyKit is the framework that is built from Zig for our native
|
# GhosttyKit is the framework that is built from Zig for our native
|
||||||
# Mac app to access.
|
# Mac app to access.
|
||||||
- name: Build GhosttyKit
|
- name: Build GhosttyKit
|
||||||
run: nix develop -c zig build
|
run: nix develop -c zig build --system ${{ steps.deps.outputs.deps }}
|
||||||
|
|
||||||
# The native app is built with native XCode tooling. This also does
|
# The native app is built with native XCode tooling. This also does
|
||||||
# codesigning. IMPORTANT: this must NOT run in a Nix environment.
|
# codesigning. IMPORTANT: this must NOT run in a Nix environment.
|
||||||
@ -199,35 +244,65 @@ jobs:
|
|||||||
- name: XCode Select
|
- name: XCode Select
|
||||||
run: sudo xcode-select -s /Applications/Xcode_16.0.app
|
run: sudo xcode-select -s /Applications/Xcode_16.0.app
|
||||||
|
|
||||||
|
- name: get the Zig deps
|
||||||
|
id: deps
|
||||||
|
run: nix build -L .#deps && echo "deps=$(readlink ./result)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Test All
|
- name: Test All
|
||||||
run: |
|
run: |
|
||||||
# OpenGL
|
# OpenGL
|
||||||
nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=freetype
|
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=freetype
|
||||||
nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext
|
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext
|
||||||
nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_freetype
|
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_freetype
|
||||||
nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_harfbuzz
|
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_harfbuzz
|
||||||
nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_noshape
|
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_noshape
|
||||||
|
|
||||||
# Metal
|
# Metal
|
||||||
nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=freetype
|
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=freetype
|
||||||
nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext
|
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext
|
||||||
nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_freetype
|
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_freetype
|
||||||
nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_harfbuzz
|
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_harfbuzz
|
||||||
nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_noshape
|
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_noshape
|
||||||
|
|
||||||
- name: Build All
|
- name: Build All
|
||||||
run: |
|
run: |
|
||||||
nix develop -c zig build -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=freetype
|
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=freetype
|
||||||
nix develop -c zig build -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext
|
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext
|
||||||
nix develop -c zig build -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_freetype
|
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_freetype
|
||||||
nix develop -c zig build -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_harfbuzz
|
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_harfbuzz
|
||||||
nix develop -c zig build -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_noshape
|
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_noshape
|
||||||
|
|
||||||
nix develop -c zig build -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=freetype
|
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=freetype
|
||||||
nix develop -c zig build -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext
|
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext
|
||||||
nix develop -c zig build -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_freetype
|
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_freetype
|
||||||
nix develop -c zig build -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_harfbuzz
|
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_harfbuzz
|
||||||
nix develop -c zig build -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_noshape
|
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_noshape
|
||||||
|
|
||||||
|
build-snap:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os:
|
||||||
|
[namespace-profile-ghostty-snap, namespace-profile-ghostty-snap-arm64]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
needs: test
|
||||||
|
env:
|
||||||
|
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
||||||
|
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
fetch-tags: true
|
||||||
|
- name: Setup Cache
|
||||||
|
uses: namespacelabs/nscloud-cache-action@v1.2.0
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
/nix
|
||||||
|
/zig
|
||||||
|
- run: sudo apt install -y udev
|
||||||
|
- run: sudo systemctl start systemd-udevd
|
||||||
|
- uses: snapcore/action-build@v1
|
||||||
|
|
||||||
build-windows:
|
build-windows:
|
||||||
runs-on: windows-2022
|
runs-on: windows-2022
|
||||||
@ -247,10 +322,10 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
# Get the zig version from build.zig so that it only needs to be updated
|
# Get the zig version from build.zig so that it only needs to be updated
|
||||||
$fileContent = Get-Content -Path "build.zig" -Raw
|
$fileContent = Get-Content -Path "build.zig" -Raw
|
||||||
$pattern = 'const required_zig = "(.*?)";'
|
$pattern = 'buildpkg\.requireZig\("(.*?)"\);'
|
||||||
$zigVersion = [regex]::Match($fileContent, $pattern).Groups[1].Value
|
$zigVersion = [regex]::Match($fileContent, $pattern).Groups[1].Value
|
||||||
Write-Output $version
|
|
||||||
$version = "zig-windows-x86_64-$zigVersion"
|
$version = "zig-windows-x86_64-$zigVersion"
|
||||||
|
Write-Output $version
|
||||||
$uri = "https://ziglang.org/download/$zigVersion/$version.zip"
|
$uri = "https://ziglang.org/download/$zigVersion/$version.zip"
|
||||||
Invoke-WebRequest -Uri "$uri" -OutFile ".\zig-windows.zip"
|
Invoke-WebRequest -Uri "$uri" -OutFile ".\zig-windows.zip"
|
||||||
Expand-Archive -Path ".\zig-windows.zip" -DestinationPath ".\" -Force
|
Expand-Archive -Path ".\zig-windows.zip" -DestinationPath ".\" -Force
|
||||||
@ -327,7 +402,7 @@ jobs:
|
|||||||
run: nix develop -c zig build -Dapp-runtime=none test
|
run: nix develop -c zig build -Dapp-runtime=none test
|
||||||
|
|
||||||
- name: Test GTK Build
|
- name: Test GTK Build
|
||||||
run: nix develop -c zig build -Dapp-runtime=gtk -Dgtk-adwaita=true -Demit-docs
|
run: nix develop -c zig build -Dapp-runtime=gtk -Demit-docs
|
||||||
|
|
||||||
- name: Test GLFW Build
|
- name: Test GLFW Build
|
||||||
run: nix develop -c zig build -Dapp-runtime=glfw
|
run: nix develop -c zig build -Dapp-runtime=glfw
|
||||||
@ -340,9 +415,9 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
adwaita: ["true", "false"]
|
|
||||||
x11: ["true", "false"]
|
x11: ["true", "false"]
|
||||||
name: GTK adwaita=${{ matrix.adwaita }} x11=${{ matrix.x11 }}
|
wayland: ["true", "false"]
|
||||||
|
name: GTK x11=${{ matrix.x11 }} wayland=${{ matrix.wayland }}
|
||||||
runs-on: namespace-profile-ghostty-sm
|
runs-on: namespace-profile-ghostty-sm
|
||||||
needs: test
|
needs: test
|
||||||
env:
|
env:
|
||||||
@ -373,8 +448,8 @@ jobs:
|
|||||||
nix develop -c \
|
nix develop -c \
|
||||||
zig build \
|
zig build \
|
||||||
-Dapp-runtime=gtk \
|
-Dapp-runtime=gtk \
|
||||||
-Dgtk-adwaita=${{ matrix.adwaita }} \
|
-Dgtk-x11=${{ matrix.x11 }} \
|
||||||
-Dgtk-x11=${{ matrix.x11 }}
|
-Dgtk-wayland=${{ matrix.wayland }}
|
||||||
|
|
||||||
test-sentry-linux:
|
test-sentry-linux:
|
||||||
strategy:
|
strategy:
|
||||||
@ -430,8 +505,12 @@ jobs:
|
|||||||
- name: XCode Select
|
- name: XCode Select
|
||||||
run: sudo xcode-select -s /Applications/Xcode_16.0.app
|
run: sudo xcode-select -s /Applications/Xcode_16.0.app
|
||||||
|
|
||||||
|
- name: get the Zig deps
|
||||||
|
id: deps
|
||||||
|
run: nix build -L .#deps && echo "deps=$(readlink ./result)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: test
|
- name: test
|
||||||
run: nix develop -c zig build test
|
run: nix develop -c zig build test --system ${{ steps.deps.outputs.deps }}
|
||||||
|
|
||||||
prettier:
|
prettier:
|
||||||
if: github.repository == 'ghostty-org/ghostty'
|
if: github.repository == 'ghostty-org/ghostty'
|
||||||
@ -548,3 +627,26 @@ jobs:
|
|||||||
- name: Test ${{ matrix.pkg }} Build
|
- name: Test ${{ matrix.pkg }} Build
|
||||||
run: |
|
run: |
|
||||||
nix develop -c sh -c "cd pkg/${{ matrix.pkg }} ; zig build test"
|
nix develop -c sh -c "cd pkg/${{ matrix.pkg }} ; zig build test"
|
||||||
|
|
||||||
|
test-debian-12:
|
||||||
|
name: Test build on Debian 12
|
||||||
|
runs-on: namespace-profile-ghostty-sm
|
||||||
|
needs: test
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install and configure Namespace CLI
|
||||||
|
uses: namespacelabs/nscloud-setup@v0
|
||||||
|
|
||||||
|
- name: Configure Namespace powered Buildx
|
||||||
|
uses: namespacelabs/nscloud-setup-buildx-action@v0
|
||||||
|
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: src/build/docker/debian/Dockerfile
|
||||||
|
build-args: |
|
||||||
|
DISTRO_VERSION=12
|
||||||
|
ZIG_VERSION=0.13.0
|
||||||
|
8
.github/workflows/update-colorschemes.yml
vendored
@ -48,14 +48,14 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
# Only proceed if build.zig.zon has changed
|
# Only proceed if build.zig.zon has changed
|
||||||
if ! git diff --exit-code build.zig.zon; then
|
if ! git diff --exit-code build.zig.zon; then
|
||||||
nix develop -c ./nix/build-support/check-zig-cache-hash.sh --update
|
nix develop -c ./nix/build-support/check-zig-cache.sh --update
|
||||||
nix develop -c ./nix/build-support/check-zig-cache-hash.sh
|
nix develop -c ./nix/build-support/check-zig-cache.sh
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Verify the build still works. We choose an arbitrary build type
|
# Verify the build still works. We choose an arbitrary build type
|
||||||
# as a canary instead of testing all build types.
|
# as a canary instead of testing all build types.
|
||||||
- name: Test Build
|
- name: Test Build
|
||||||
run: nix develop -c zig build -Dapp-runtime=gtk -Dgtk-adwaita=true
|
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@v7
|
||||||
@ -66,7 +66,7 @@ jobs:
|
|||||||
commit-message: "deps: Update iTerm2 color schemes"
|
commit-message: "deps: Update iTerm2 color schemes"
|
||||||
add-paths: |
|
add-paths: |
|
||||||
build.zig.zon
|
build.zig.zon
|
||||||
nix/zigCacheHash.nix
|
build.zig.zon.nix
|
||||||
body: |
|
body: |
|
||||||
Upstream revision: https://github.com/mbadolato/iTerm2-Color-Schemes/tree/${{ steps.zig_fetch.outputs.upstream_rev }}
|
Upstream revision: https://github.com/mbadolato/iTerm2-Color-Schemes/tree/${{ steps.zig_fetch.outputs.upstream_rev }}
|
||||||
labels: dependencies
|
labels: dependencies
|
||||||
|
1
.gitignore
vendored
@ -17,3 +17,4 @@ test/cases/**/*.actual.png
|
|||||||
glad.zip
|
glad.zip
|
||||||
/Box_test.ppm
|
/Box_test.ppm
|
||||||
/Box_test_diff.ppm
|
/Box_test_diff.ppm
|
||||||
|
/ghostty.qcow2
|
||||||
|
149
CODEOWNERS
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
# This file documents the subsystem maintainers of the Ghostty project
|
||||||
|
# along with the responsibilities of a maintainer and how one can become
|
||||||
|
# a maintainer.
|
||||||
|
#
|
||||||
|
# Ghostty follows a subsystem maintainer model where distinguished
|
||||||
|
# contributors (with mutual agreement) are designated as maintainers of a
|
||||||
|
# specific subset of the project. A subsystem maintainer has more privileges
|
||||||
|
# and authority over a specific part of the project than a regular
|
||||||
|
# contributor and deference is given to them when making decisions about
|
||||||
|
# their subsystem.
|
||||||
|
#
|
||||||
|
# Ultimately Ghostty has a BDFL (Benevolent Dictator For Life) model
|
||||||
|
# currently with @mitchellh as the BDFL. The BDFL has the final say in all
|
||||||
|
# decisions and may override a maintainer's decision if necessary. I like to
|
||||||
|
# say its a BDFLFN (Benevolent Dictator For Life "For Now") model because
|
||||||
|
# long term I'd like to see the project be more community driven. But for
|
||||||
|
# now, early in its life, we're going with this model.
|
||||||
|
#
|
||||||
|
# ## Privileges
|
||||||
|
#
|
||||||
|
# - Authority to approve or reject pull requests in their subsystem.
|
||||||
|
# - Authority to moderate issues and discussions in their subsystem.
|
||||||
|
# - Authority to make roadmap and design decisions about their subsystem
|
||||||
|
# with input only from other subsystem maintainers.
|
||||||
|
#
|
||||||
|
# In all scenarios, the BDFL doesn't need to be consulted for decisions
|
||||||
|
# but may revert or override decisions if necessary. The expectation is
|
||||||
|
# that maintainers will be trusted to make the right decisions for their
|
||||||
|
# subsystem and this will be rare.
|
||||||
|
#
|
||||||
|
# ## Responsibilities
|
||||||
|
#
|
||||||
|
# Subsystem maintainership is a voluntary role and maintainers are not
|
||||||
|
# expected to dedicate any amount of time to the project. However, if a
|
||||||
|
# maintainer is inactive for a long period of time, they may be removed from
|
||||||
|
# the maintainers list to avoid bitrot or outdated information.
|
||||||
|
#
|
||||||
|
# Maintainers are expected to be exemplary members of the community and
|
||||||
|
# should be respectful, helpful, and professional in all interactions.
|
||||||
|
# This is both in regards to the community at large as well as other
|
||||||
|
# subsystem maintainers as well as @mitchellh.
|
||||||
|
#
|
||||||
|
# As technical leaders, maintainers are expected to be mindful about
|
||||||
|
# breaking changes, performance, user impact, and other technical
|
||||||
|
# considerations in their subsystem. They should be considerate of large
|
||||||
|
# changes and should be able to justify their decisions.
|
||||||
|
#
|
||||||
|
# Notably, maintainers have NO OBLIGATION to review pull requests or issues
|
||||||
|
# in their subsystem. They have full discretion to review or not review
|
||||||
|
# anything they want. This isn't a job! It is a role of trust and authority
|
||||||
|
# and the expectation is that maintainers will use their best judgement.
|
||||||
|
#
|
||||||
|
# ## Becoming a Maintainer
|
||||||
|
#
|
||||||
|
# Maintainer candidates are noticed and proposed by the community. Anyone
|
||||||
|
# may propose themselves or someone else as a maintainer. The BDFL along
|
||||||
|
# with existing maintainers will discuss and decide.
|
||||||
|
#
|
||||||
|
# Generally, we want to see consistent high quality contributions to a
|
||||||
|
# specific subsystem before considering someone as a maintainer. There isn't
|
||||||
|
# an exact number of contributions or time period required but generally
|
||||||
|
# we're looking for an order of a dozen or more contributions over a period of
|
||||||
|
# months, at least.
|
||||||
|
#
|
||||||
|
# # Subsystem List
|
||||||
|
#
|
||||||
|
# The subsystems don't fully cover the entirety of the Ghostty project but
|
||||||
|
# are created organically as experts in certain areas emerge. If you feel
|
||||||
|
# you are an expert in a certain area and would like to be a maintainer,
|
||||||
|
# please reach out to @mitchellh on Discord.
|
||||||
|
#
|
||||||
|
# (Alphabetical order)
|
||||||
|
#
|
||||||
|
# - @ghostty-org/font - All things font related including discovery,
|
||||||
|
# rasterization, shaping, coloring, etc.
|
||||||
|
#
|
||||||
|
# - @ghostty-org/gtk - Anything GTK-related in the project, primarily
|
||||||
|
# the GTK apprt. Also includes X11/Wayland integrations and general
|
||||||
|
# Linux support.
|
||||||
|
#
|
||||||
|
# - @ghostty-org/macos - The Ghostty macOS app and any macOS-specific
|
||||||
|
# features, configurations, etc.
|
||||||
|
#
|
||||||
|
# - @ghostty-org/renderer - Ghostty rendering subsystem, including the
|
||||||
|
# rendering abstractions as well as specific renderers like OpenGL
|
||||||
|
# and Metal.
|
||||||
|
#
|
||||||
|
# - @ghostty-org/shell - Ghostty shell integration, including shell
|
||||||
|
# completions, shell detection, and any other shell interactions.
|
||||||
|
#
|
||||||
|
# - @ghostty-org/terminal - The terminal emulator subsystem, including
|
||||||
|
# subprocess management and pty handling, escape sequence parsing,
|
||||||
|
# key encoding, etc.
|
||||||
|
#
|
||||||
|
# ## Outside of Ghostty
|
||||||
|
#
|
||||||
|
# Other "subsystems" exist outside of Ghostty and will not be represented
|
||||||
|
# in this CODEOWNERS file:
|
||||||
|
#
|
||||||
|
# - @ghostty-org/discord-bot - Maintainers of the Ghostty Discord bot.
|
||||||
|
#
|
||||||
|
# - @ghostty-org/website - Maintainers of the Ghostty website.
|
||||||
|
|
||||||
|
# Font
|
||||||
|
/src/font/ @ghostty-org/font
|
||||||
|
/pkg/fontconfig/ @ghostty-org/font
|
||||||
|
/pkg/freetype/ @ghostty-org/font
|
||||||
|
/pkg/harfbuzz/ @ghostty-org/font
|
||||||
|
|
||||||
|
# GTK
|
||||||
|
/src/apprt/gtk/ @ghostty-org/gtk
|
||||||
|
/src/os/cgroup.zig @ghostty-org/gtk
|
||||||
|
/src/os/flatpak.zig @ghostty-org/gtk
|
||||||
|
/dist/linux/ @ghostty-org/gtk
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
#
|
||||||
|
# This includes libghostty because the macOS apprt is built on top of
|
||||||
|
# libghostty and often requires or is impacted by changes to libghostty.
|
||||||
|
# macOS subsystem maintainers are expected to only work on libghostty
|
||||||
|
# insofar as it impacts the macOS apprt.
|
||||||
|
/include/ghostty.h @ghostty-org/macos
|
||||||
|
/src/apprt/embedded.zig @ghostty-org/macos
|
||||||
|
/src/os/cf_release_thread.zig @ghostty-org/macos
|
||||||
|
/src/os/macos.zig @ghostty-org/macos
|
||||||
|
/macos/ @ghostty-org/macos
|
||||||
|
/dist/macos/ @ghostty-org/macos
|
||||||
|
/pkg/apple-sdk/ @ghostty-org/macos
|
||||||
|
/pkg/macos/ @ghostty-org/macos
|
||||||
|
|
||||||
|
# Renderer
|
||||||
|
/src/renderer.zig @ghostty-org/renderer
|
||||||
|
/src/renderer/ @ghostty-org/renderer
|
||||||
|
/pkg/glslang/ @ghostty-org/renderer
|
||||||
|
/pkg/opengl/ @ghostty-org/renderer
|
||||||
|
/pkg/spirv-cross/ @ghostty-org/renderer
|
||||||
|
/pkg/wuffs/ @ghostty-org/renderer
|
||||||
|
|
||||||
|
# Shell
|
||||||
|
/src/shell-integration/ @ghostty-org/shell
|
||||||
|
/src/termio/shell-integration.zig @ghostty-org/shell
|
||||||
|
|
||||||
|
# Terminal
|
||||||
|
/src/simd/ @ghostty-org/terminal
|
||||||
|
/src/terminal/ @ghostty-org/terminal
|
||||||
|
/src/terminfo/ @ghostty-org/terminal
|
||||||
|
/src/unicode/ @ghostty-org/terminal
|
||||||
|
/src/Surface.zig @ghostty-org/terminal
|
||||||
|
/src/surface_mouse.zig @ghostty-org/terminal
|
180
CONTRIBUTING.md
@ -77,3 +77,183 @@ pull request will be accepted with a high degree of certainty.
|
|||||||
> **Pull requests are NOT a place to discuss feature design.** Please do
|
> **Pull requests are NOT a place to discuss feature design.** Please do
|
||||||
> not open a WIP pull request to discuss a feature. Instead, use a discussion
|
> not open a WIP pull request to discuss a feature. Instead, use a discussion
|
||||||
> and link to your branch.
|
> and link to your branch.
|
||||||
|
|
||||||
|
# Developer Guide
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
>
|
||||||
|
> **The remainder of this file is dedicated to developers actively
|
||||||
|
> working on Ghostty.** If you're a user reporting an issue, you can
|
||||||
|
> ignore the rest of this document.
|
||||||
|
|
||||||
|
## Input Stack Testing
|
||||||
|
|
||||||
|
The input stack is the part of the codebase that starts with a
|
||||||
|
key event and ends with text encoding being sent to the pty (it
|
||||||
|
does not include _rendering_ the text, which is part of the
|
||||||
|
font or rendering stack).
|
||||||
|
|
||||||
|
If you modify any part of the input stack, you must manually verify
|
||||||
|
all the following input cases work properly. We unfortunately do
|
||||||
|
not automate this in any way, but if we can do that one day that'd
|
||||||
|
save a LOT of grief and time.
|
||||||
|
|
||||||
|
Note: this list may not be exhaustive, I'm still working on it.
|
||||||
|
|
||||||
|
### Linux IME
|
||||||
|
|
||||||
|
IME (Input Method Editors) are a common source of bugs in the input stack,
|
||||||
|
especially on Linux since there are multiple different IME systems
|
||||||
|
interacting with different windowing systems and application frameworks
|
||||||
|
all written by different organizations.
|
||||||
|
|
||||||
|
The following matrix should be tested to ensure that all IME input works
|
||||||
|
properly:
|
||||||
|
|
||||||
|
1. Wayland, X11
|
||||||
|
2. ibus, fcitx, none
|
||||||
|
3. Dead key input (e.g. Spanish), CJK (e.g. Japanese), Emoji, Unicode Hex
|
||||||
|
4. ibus versions: 1.5.29, 1.5.30, 1.5.31 (each exhibit slightly different behaviors)
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
>
|
||||||
|
> This is a **work in progress**. I'm still working on this list and it
|
||||||
|
> is not complete. As I find more test cases, I will add them here.
|
||||||
|
|
||||||
|
#### Dead Key Input
|
||||||
|
|
||||||
|
Set your keyboard layout to "Spanish" (or another layout that uses dead keys).
|
||||||
|
|
||||||
|
1. Launch Ghostty
|
||||||
|
2. Press `'`
|
||||||
|
3. Press `a`
|
||||||
|
4. Verify that `á` is displayed
|
||||||
|
|
||||||
|
Note that the dead key may or may not show a preedit state visually.
|
||||||
|
For ibus and fcitx it does but for the "none" case it does not. Importantly,
|
||||||
|
the text should be correct when it is sent to the pty.
|
||||||
|
|
||||||
|
We should also test canceling dead key input:
|
||||||
|
|
||||||
|
1. Launch Ghostty
|
||||||
|
2. Press `'`
|
||||||
|
3. Press escape
|
||||||
|
4. Press `a`
|
||||||
|
5. Verify that `a` is displayed (no diacritic)
|
||||||
|
|
||||||
|
#### CJK Input
|
||||||
|
|
||||||
|
Configure fcitx or ibus with a keyboard layout like Japanese or Mozc. The
|
||||||
|
exact layout doesn't matter.
|
||||||
|
|
||||||
|
1. Launch Ghostty
|
||||||
|
2. Press `Ctrl+Shift` to switch to "Hiragana"
|
||||||
|
3. On a US physical layout, type: `konn`, you should see `こん` in preedit.
|
||||||
|
4. Press `Enter`
|
||||||
|
5. Verify that `こん` is displayed in the terminal.
|
||||||
|
|
||||||
|
We should also test switching input methods while preedit is active, which
|
||||||
|
should commit the text:
|
||||||
|
|
||||||
|
1. Launch Ghostty
|
||||||
|
2. Press `Ctrl+Shift` to switch to "Hiragana"
|
||||||
|
3. On a US physical layout, type: `konn`, you should see `こん` in preedit.
|
||||||
|
4. Press `Ctrl+Shift` to switch to another layout (any)
|
||||||
|
5. Verify that `こん` is displayed in the terminal as committed text.
|
||||||
|
|
||||||
|
## Nix Virtual Machines
|
||||||
|
|
||||||
|
Several Nix virtual machine definitions are provided by the project for testing
|
||||||
|
and developing Ghostty against multiple different Linux desktop environments.
|
||||||
|
|
||||||
|
Running these requires a working Nix installation, either Nix on your
|
||||||
|
favorite Linux distribution, NixOS, or macOS with nix-darwin installed. Further
|
||||||
|
requirements for macOS are detailed below.
|
||||||
|
|
||||||
|
VMs should only be run on your local desktop and then powered off when not in
|
||||||
|
use, which will discard any changes to the VM.
|
||||||
|
|
||||||
|
The VM definitions provide minimal software "out of the box" but additional
|
||||||
|
software can be installed by using standard Nix mechanisms like `nix run nixpkgs#<package>`.
|
||||||
|
|
||||||
|
### Linux
|
||||||
|
|
||||||
|
1. Check out the Ghostty source and change to the directory.
|
||||||
|
2. Run `nix run .#<vmtype>`. `<vmtype>` can be any of the VMs defined in the
|
||||||
|
`nix/vm` directory (without the `.nix` suffix) excluding any file prefixed
|
||||||
|
with `common` or `create`.
|
||||||
|
3. The VM will build and then launch. Depending on the speed of your system, this
|
||||||
|
can take a while, but eventually you should get a new VM window.
|
||||||
|
4. The Ghostty source directory should be mounted to `/tmp/shared` in the VM. Depending
|
||||||
|
on what UID and GID of the user that you launched the VM as, `/tmp/shared` _may_ be
|
||||||
|
writable by the VM user, so be careful!
|
||||||
|
|
||||||
|
### macOS
|
||||||
|
|
||||||
|
1. To run the VMs on macOS you will need to enable the Linux builder in your `nix-darwin`
|
||||||
|
config. This _should_ be as simple as adding `nix.linux-builder.enable=true` to your
|
||||||
|
configuration and then rebuilding. See [this](https://nixcademy.com/posts/macos-linux-builder/)
|
||||||
|
blog post for more information about the Linux builder and how to tune the performance.
|
||||||
|
2. Once the Linux builder has been enabled, you should be able to follow the Linux instructions
|
||||||
|
above to launch a VM.
|
||||||
|
|
||||||
|
### Custom VMs
|
||||||
|
|
||||||
|
To easily create a custom VM without modifying the Ghostty source, create a new
|
||||||
|
directory, then create a file called `flake.nix` with the following text in the
|
||||||
|
new directory.
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "nixpkgs/nixpkgs-unstable";
|
||||||
|
ghostty.url = "github:ghostty-org/ghostty";
|
||||||
|
};
|
||||||
|
outputs = {
|
||||||
|
nixpkgs,
|
||||||
|
ghostty,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
|
nixosConfigurations.custom-vm = ghostty.create-gnome-vm {
|
||||||
|
nixpkgs = nixpkgs;
|
||||||
|
system = "x86_64-linux";
|
||||||
|
overlay = ghostty.overlays.releasefast;
|
||||||
|
# module = ./configuration.nix # also works
|
||||||
|
module = {pkgs, ...}: {
|
||||||
|
environment.systemPackages = [
|
||||||
|
pkgs.btop
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The custom VM can then be run with a command like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
nix run .#nixosConfigurations.custom-vm.config.system.build.vm
|
||||||
|
```
|
||||||
|
|
||||||
|
A file named `ghostty.qcow2` will be created that is used to persist any changes
|
||||||
|
made in the VM. To "reset" the VM to default delete the file and it will be
|
||||||
|
recreated the next time you run the VM.
|
||||||
|
|
||||||
|
### Contributing new VM definitions
|
||||||
|
|
||||||
|
#### VM Acceptance Criteria
|
||||||
|
|
||||||
|
We welcome the contribution of new VM definitions, as long as they meet the following criteria:
|
||||||
|
|
||||||
|
1. The should be different enough from existing VM definitions that they represent a distinct
|
||||||
|
user (and developer) experience.
|
||||||
|
2. There's a significant Ghostty user population that uses a similar environment.
|
||||||
|
3. The VMs can be built using only packages from the current stable NixOS release.
|
||||||
|
|
||||||
|
#### VM Definition Criteria
|
||||||
|
|
||||||
|
1. VMs should be as minimal as possible so that they build and launch quickly.
|
||||||
|
Additional software can be added at runtime with a command like `nix run nixpkgs#<package name>`.
|
||||||
|
2. VMs should not expose any services to the network, or run any remote access
|
||||||
|
software like SSH daemons, VNC or RDP.
|
||||||
|
3. VMs should auto-login using the "ghostty" user.
|
||||||
|
14
PACKAGING.md
@ -23,13 +23,6 @@ https://release.files.ghostty.org/VERSION/ghostty-VERSION.tar.gz
|
|||||||
https://release.files.ghostty.org/VERSION/ghostty-VERSION.tar.gz.minisig
|
https://release.files.ghostty.org/VERSION/ghostty-VERSION.tar.gz.minisig
|
||||||
```
|
```
|
||||||
|
|
||||||
> [!NOTE]
|
|
||||||
>
|
|
||||||
> **Version 1.0.0 the filename is `ghostty-source.tar.gz`.** Future
|
|
||||||
> versions will use the `ghostty-VERSION.tar.gz` format since it is more
|
|
||||||
> typical for source tarballs. But for version 1.0.0, the filename is
|
|
||||||
> `ghostty-source.tar.gz`.
|
|
||||||
|
|
||||||
Signature files are signed with
|
Signature files are signed with
|
||||||
[minisign](https://jedisct1.github.io/minisign/)
|
[minisign](https://jedisct1.github.io/minisign/)
|
||||||
using the following public key:
|
using the following public key:
|
||||||
@ -88,6 +81,13 @@ for system packages which separate a build and install step, since the
|
|||||||
install step can then be done with a `mv` or `cp` command (from `/tmp/ghostty`
|
install step can then be done with a `mv` or `cp` command (from `/tmp/ghostty`
|
||||||
to wherever the package manager expects it).
|
to wherever the package manager expects it).
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
>
|
||||||
|
> **Version 1.1.1 and 1.1.2 are missing `fetch-zig-cache.sh`.** This was
|
||||||
|
> an oversight on the release process. You can use the script from version
|
||||||
|
> 1.1.0 to fetch the Zig cache for these versions. Future versions will
|
||||||
|
> restore the script.
|
||||||
|
|
||||||
### Build Options
|
### Build Options
|
||||||
|
|
||||||
Ghostty uses the Zig build system. You can see all available build options by
|
Ghostty uses the Zig build system. You can see all available build options by
|
||||||
|
16
build.zig
@ -3,21 +3,7 @@ const builtin = @import("builtin");
|
|||||||
const buildpkg = @import("src/build/main.zig");
|
const buildpkg = @import("src/build/main.zig");
|
||||||
|
|
||||||
comptime {
|
comptime {
|
||||||
// This is the required Zig version for building this project. We allow
|
buildpkg.requireZig("0.13.0");
|
||||||
// any patch version but the major and minor must match exactly.
|
|
||||||
const required_zig = "0.13.0";
|
|
||||||
|
|
||||||
// Fail compilation if the current Zig version doesn't meet requirements.
|
|
||||||
const current_vsn = builtin.zig_version;
|
|
||||||
const required_vsn = std.SemanticVersion.parse(required_zig) catch unreachable;
|
|
||||||
if (current_vsn.major != required_vsn.major or
|
|
||||||
current_vsn.minor != required_vsn.minor)
|
|
||||||
{
|
|
||||||
@compileError(std.fmt.comptimePrint(
|
|
||||||
"Your Zig version v{} does not meet the required build version of v{}",
|
|
||||||
.{ current_vsn, required_vsn },
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(b: *std.Build) !void {
|
pub fn build(b: *std.Build) !void {
|
||||||
|
@ -1,32 +1,39 @@
|
|||||||
.{
|
.{
|
||||||
.name = "ghostty",
|
.name = "ghostty",
|
||||||
.version = "1.0.2",
|
.version = "1.1.3",
|
||||||
.paths = .{""},
|
.paths = .{""},
|
||||||
.dependencies = .{
|
.dependencies = .{
|
||||||
// Zig libs
|
// Zig libs
|
||||||
|
|
||||||
.libxev = .{
|
.libxev = .{
|
||||||
.url = "https://github.com/mitchellh/libxev/archive/db6a52bafadf00360e675fefa7926e8e6c0e9931.tar.gz",
|
// mitchellh/libxev
|
||||||
.hash = "12206029de146b685739f69b10a6f08baee86b3d0a5f9a659fa2b2b66c9602078bbf",
|
.url = "https://deps.files.ghostty.org/libxev-1220ebf88622c4d502dc59e71347e4d28c47e033f11b59aff774ae5787565c40999c.tar.gz",
|
||||||
|
.hash = "1220ebf88622c4d502dc59e71347e4d28c47e033f11b59aff774ae5787565c40999c",
|
||||||
},
|
},
|
||||||
.mach_glfw = .{
|
.mach_glfw = .{
|
||||||
.url = "https://github.com/mitchellh/mach-glfw/archive/37c2995f31abcf7e8378fba68ddcf4a3faa02de0.tar.gz",
|
// mitchellh/mach-glfw
|
||||||
|
.url = "https://deps.files.ghostty.org/mach_glfw-12206ed982e709e565d536ce930701a8c07edfd2cfdce428683f3f2a601d37696a62.tar.gz",
|
||||||
.hash = "12206ed982e709e565d536ce930701a8c07edfd2cfdce428683f3f2a601d37696a62",
|
.hash = "12206ed982e709e565d536ce930701a8c07edfd2cfdce428683f3f2a601d37696a62",
|
||||||
.lazy = true,
|
.lazy = true,
|
||||||
},
|
},
|
||||||
.vaxis = .{
|
.vaxis = .{
|
||||||
.url = "git+https://github.com/rockorager/libvaxis/?ref=main#6d729a2dc3b934818dffe06d2ba3ce02841ed74b",
|
// rockorager/libvaxis
|
||||||
.hash = "12200df4ebeaed45de26cb2c9f3b6f3746d8013b604e035dae658f86f586c8c91d2f",
|
.url = "git+https://github.com/rockorager/libvaxis#2237a7059eae99e9f132dd5acd1555e49d6c7d93",
|
||||||
|
.hash = "1220f5aec880d4f430cc1597ede88f1530da69e39a4986080e976b0c7b919c2ebfeb",
|
||||||
},
|
},
|
||||||
.z2d = .{
|
.z2d = .{
|
||||||
.url = "git+https://github.com/vancluever/z2d?ref=v0.4.0#4638bb02a9dc41cc2fb811f092811f6a951c752a",
|
// vancluever/z2d
|
||||||
|
.url = "https://deps.files.ghostty.org/z2d-12201f0d542e7541cf492a001d4d0d0155c92f58212fbcb0d224e95edeba06b5416a.tar.gz",
|
||||||
.hash = "12201f0d542e7541cf492a001d4d0d0155c92f58212fbcb0d224e95edeba06b5416a",
|
.hash = "12201f0d542e7541cf492a001d4d0d0155c92f58212fbcb0d224e95edeba06b5416a",
|
||||||
},
|
},
|
||||||
.zig_objc = .{
|
.zig_objc = .{
|
||||||
.url = "https://github.com/mitchellh/zig-objc/archive/9b8ba849b0f58fe207ecd6ab7c147af55b17556e.tar.gz",
|
// mitchellh/zig-objc
|
||||||
|
.url = "https://deps.files.ghostty.org/zig_objc-1220e17e64ef0ef561b3e4b9f3a96a2494285f2ec31c097721bf8c8677ec4415c634.tar.gz",
|
||||||
.hash = "1220e17e64ef0ef561b3e4b9f3a96a2494285f2ec31c097721bf8c8677ec4415c634",
|
.hash = "1220e17e64ef0ef561b3e4b9f3a96a2494285f2ec31c097721bf8c8677ec4415c634",
|
||||||
},
|
},
|
||||||
.zig_js = .{
|
.zig_js = .{
|
||||||
.url = "https://github.com/mitchellh/zig-js/archive/d0b8b0a57c52fbc89f9d9fecba75ca29da7dd7d1.tar.gz",
|
// mitchellh/zig-js
|
||||||
|
.url = "https://deps.files.ghostty.org/zig_js-12205a66d423259567764fa0fc60c82be35365c21aeb76c5a7dc99698401f4f6fefc.tar.gz",
|
||||||
.hash = "12205a66d423259567764fa0fc60c82be35365c21aeb76c5a7dc99698401f4f6fefc",
|
.hash = "12205a66d423259567764fa0fc60c82be35365c21aeb76c5a7dc99698401f4f6fefc",
|
||||||
},
|
},
|
||||||
.ziglyph = .{
|
.ziglyph = .{
|
||||||
@ -34,13 +41,20 @@
|
|||||||
.hash = "12207831bce7d4abce57b5a98e8f3635811cfefd160bca022eb91fe905d36a02cf25",
|
.hash = "12207831bce7d4abce57b5a98e8f3635811cfefd160bca022eb91fe905d36a02cf25",
|
||||||
},
|
},
|
||||||
.zig_wayland = .{
|
.zig_wayland = .{
|
||||||
.url = "https://codeberg.org/ifreund/zig-wayland/archive/a5e2e9b6a6d7fba638ace4d4b24a3b576a02685b.tar.gz",
|
// codeberg ifreund/zig-wayland
|
||||||
.hash = "1220d41b23ae70e93355bb29dac1c07aa6aeb92427a2dffc4375e94b4de18111248c",
|
.url = "https://deps.files.ghostty.org/zig-wayland-fbfe3b4ac0b472a27b1f1a67405436c58cbee12d.tar.gz",
|
||||||
|
.hash = "12209ca054cb1919fa276e328967f10b253f7537c4136eb48f3332b0f7cf661cad38",
|
||||||
},
|
},
|
||||||
.zf = .{
|
.zf = .{
|
||||||
.url = "git+https://github.com/natecraddock/zf/?ref=main#ed99ca18b02dda052e20ba467e90b623c04690dd",
|
// natecraddock/zf
|
||||||
|
.url = "https://deps.files.ghostty.org/zf-1220edc3b8d8bedbb50555947987e5e8e2f93871ca3c8e8d4cc8f1377c15b5dd35e8.tar.gz",
|
||||||
.hash = "1220edc3b8d8bedbb50555947987e5e8e2f93871ca3c8e8d4cc8f1377c15b5dd35e8",
|
.hash = "1220edc3b8d8bedbb50555947987e5e8e2f93871ca3c8e8d4cc8f1377c15b5dd35e8",
|
||||||
},
|
},
|
||||||
|
.gobject = .{
|
||||||
|
// ianprime0509/zig-gobject
|
||||||
|
.url = "https://deps.files.ghostty.org/gobject-12208d70ee791d7ef7e16e1c3c9c1127b57f1ed066a24f87d57fc9f730c5dc394b9d.tar.zst",
|
||||||
|
.hash = "12208d70ee791d7ef7e16e1c3c9c1127b57f1ed066a24f87d57fc9f730c5dc394b9d",
|
||||||
|
},
|
||||||
|
|
||||||
// C libs
|
// C libs
|
||||||
.cimgui = .{ .path = "./pkg/cimgui" },
|
.cimgui = .{ .path = "./pkg/cimgui" },
|
||||||
@ -72,15 +86,15 @@
|
|||||||
.hash = "12201a57c6ce0001aa034fa80fba3e1cd2253c560a45748f4f4dd21ff23b491cddef",
|
.hash = "12201a57c6ce0001aa034fa80fba3e1cd2253c560a45748f4f4dd21ff23b491cddef",
|
||||||
},
|
},
|
||||||
.plasma_wayland_protocols = .{
|
.plasma_wayland_protocols = .{
|
||||||
.url = "git+https://github.com/KDE/plasma-wayland-protocols?ref=main#db525e8f9da548cffa2ac77618dd0fbe7f511b86",
|
.url = "https://deps.files.ghostty.org/plasma_wayland_protocols-12207e0851c12acdeee0991e893e0132fc87bb763969a585dc16ecca33e88334c566.tar.gz",
|
||||||
.hash = "12207e0851c12acdeee0991e893e0132fc87bb763969a585dc16ecca33e88334c566",
|
.hash = "12207e0851c12acdeee0991e893e0132fc87bb763969a585dc16ecca33e88334c566",
|
||||||
},
|
},
|
||||||
|
|
||||||
// Other
|
// Other
|
||||||
.apple_sdk = .{ .path = "./pkg/apple-sdk" },
|
.apple_sdk = .{ .path = "./pkg/apple-sdk" },
|
||||||
.iterm2_themes = .{
|
.iterm2_themes = .{
|
||||||
.url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/4762ad5bd6d3906e28babdc2bda8a967d63a63be.tar.gz",
|
.url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/efb1bb1843500a751eb30afa58fe48a6bec8952c.tar.gz",
|
||||||
.hash = "1220a263b22113273d01bd33e3c06b8119cb2f63b4e5d414a85d88e3aa95bb68a2de",
|
.hash = "1220a1dbe41bc69aacf75026a7158812198ea265fb9cac64dcb91cd31f3b1b8c1f92",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
390
build.zig.zon.nix
generated
Normal file
@ -0,0 +1,390 @@
|
|||||||
|
# generated by zon2nix (https://github.com/Cloudef/zig2nix)
|
||||||
|
{
|
||||||
|
lib,
|
||||||
|
linkFarm,
|
||||||
|
fetchurl,
|
||||||
|
fetchgit,
|
||||||
|
runCommandLocal,
|
||||||
|
zig,
|
||||||
|
name ? "zig-packages",
|
||||||
|
}:
|
||||||
|
with builtins;
|
||||||
|
with lib; let
|
||||||
|
unpackZigArtifact = {
|
||||||
|
name,
|
||||||
|
artifact,
|
||||||
|
}:
|
||||||
|
runCommandLocal name
|
||||||
|
{
|
||||||
|
nativeBuildInputs = [zig];
|
||||||
|
}
|
||||||
|
''
|
||||||
|
hash="$(zig fetch --global-cache-dir "$TMPDIR" ${artifact})"
|
||||||
|
mv "$TMPDIR/p/$hash" "$out"
|
||||||
|
chmod 755 "$out"
|
||||||
|
'';
|
||||||
|
|
||||||
|
fetchZig = {
|
||||||
|
name,
|
||||||
|
url,
|
||||||
|
hash,
|
||||||
|
}: let
|
||||||
|
artifact = fetchurl {inherit url hash;};
|
||||||
|
in
|
||||||
|
unpackZigArtifact {inherit name artifact;};
|
||||||
|
|
||||||
|
fetchGitZig = {
|
||||||
|
name,
|
||||||
|
url,
|
||||||
|
hash,
|
||||||
|
}: let
|
||||||
|
parts = splitString "#" url;
|
||||||
|
url_base = elemAt parts 0;
|
||||||
|
url_without_query = elemAt (splitString "?" url_base) 0;
|
||||||
|
rev_base = elemAt parts 1;
|
||||||
|
rev =
|
||||||
|
if match "^[a-fA-F0-9]{40}$" rev_base != null
|
||||||
|
then rev_base
|
||||||
|
else "refs/heads/${rev_base}";
|
||||||
|
in
|
||||||
|
fetchgit {
|
||||||
|
inherit name rev hash;
|
||||||
|
url = url_without_query;
|
||||||
|
deepClone = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchZigArtifact = {
|
||||||
|
name,
|
||||||
|
url,
|
||||||
|
hash,
|
||||||
|
}: let
|
||||||
|
parts = splitString "://" url;
|
||||||
|
proto = elemAt parts 0;
|
||||||
|
path = elemAt parts 1;
|
||||||
|
fetcher = {
|
||||||
|
"git+http" = fetchGitZig {
|
||||||
|
inherit name hash;
|
||||||
|
url = "http://${path}";
|
||||||
|
};
|
||||||
|
"git+https" = fetchGitZig {
|
||||||
|
inherit name hash;
|
||||||
|
url = "https://${path}";
|
||||||
|
};
|
||||||
|
http = fetchZig {
|
||||||
|
inherit name hash;
|
||||||
|
url = "http://${path}";
|
||||||
|
};
|
||||||
|
https = fetchZig {
|
||||||
|
inherit name hash;
|
||||||
|
url = "https://${path}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
fetcher.${proto};
|
||||||
|
in
|
||||||
|
linkFarm name [
|
||||||
|
{
|
||||||
|
name = "1220ebf88622c4d502dc59e71347e4d28c47e033f11b59aff774ae5787565c40999c";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "libxev";
|
||||||
|
url = "https://deps.files.ghostty.org/libxev-1220ebf88622c4d502dc59e71347e4d28c47e033f11b59aff774ae5787565c40999c.tar.gz";
|
||||||
|
hash = "sha256-VHP90NTytIZ8UZsYRKOOxN490/I6yv6ec40sP8y5MJ8=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "12206ed982e709e565d536ce930701a8c07edfd2cfdce428683f3f2a601d37696a62";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "mach_glfw";
|
||||||
|
url = "https://deps.files.ghostty.org/mach_glfw-12206ed982e709e565d536ce930701a8c07edfd2cfdce428683f3f2a601d37696a62.tar.gz";
|
||||||
|
hash = "sha256-HhXIvWUS8/CHWY4VXPG2ZEo+we8XOn3o5rYJCQ1n8Nk=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "1220736fa4ba211162c7a0e46cc8fe04d95921927688bff64ab5da7420d098a7272d";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "glfw";
|
||||||
|
url = "https://github.com/mitchellh/glfw/archive/b552c6ec47326b94015feddb36058ea567b87159.tar.gz";
|
||||||
|
hash = "sha256-IeBVAOQmtyFqVxzuXPek1onuPwIamcOyYtxqKpPEQjU=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "12202adbfecdad671d585c9a5bfcbd5cdf821726779430047742ce1bf94ad67d19cb";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "xcode_frameworks";
|
||||||
|
url = "https://github.com/mitchellh/xcode-frameworks/archive/69801c154c39d7ae6129ea1ba8fe1afe00585fc8.tar.gz";
|
||||||
|
hash = "sha256-mP/I2coL57UJm/3+4Q8sPAgQwk8V4zM+S4VBBTrX2To=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "122004bfd4c519dadfb8e6281a42fc34fd1aa15aea654ea8a492839046f9894fa2cf";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "vulkan_headers";
|
||||||
|
url = "https://github.com/mitchellh/vulkan-headers/archive/04c8a0389d5a0236a96312988017cd4ce27d8041.tar.gz";
|
||||||
|
hash = "sha256-K+zrRudgHFukOM6En1StRYRMNYkeRk+qHTXvrXaG+FU=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "1220b3164434d2ec9db146a40bf3a30f490590d68fa8529776a3138074f0da2c11ca";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "wayland_headers";
|
||||||
|
url = "https://github.com/mitchellh/wayland-headers/archive/5f991515a29f994d87b908115a2ab0b899474bd1.tar.gz";
|
||||||
|
hash = "sha256-uFilLZinKkZt6RdVTV3lUmJpzpswDdFva22FvwU/XQI=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "122089c326186c84aa2fd034b16abc38f3ebf4862d9ae106dc1847ac44f557b36465";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "x11_headers";
|
||||||
|
url = "https://github.com/mitchellh/x11-headers/archive/2ffbd62d82ff73ec929dd8de802bc95effa0ef88.tar.gz";
|
||||||
|
hash = "sha256-EhV2bmTY/OMYN1wEul35gD0hQgS/Al262jO3pVr0O+c=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "1220f5aec880d4f430cc1597ede88f1530da69e39a4986080e976b0c7b919c2ebfeb";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "vaxis";
|
||||||
|
url = "git+https://github.com/rockorager/libvaxis#2237a7059eae99e9f132dd5acd1555e49d6c7d93";
|
||||||
|
hash = "sha256-ZzLNJOsXzyBhkdUhbET30RoU2T9xKYsBUQz2NAjK/G8=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "1220dd654ef941fc76fd96f9ec6adadf83f69b9887a0d3f4ee5ac0a1a3e11be35cf5";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "zigimg";
|
||||||
|
url = "git+https://github.com/zigimg/zigimg#3a667bdb3d7f0955a5a51c8468eac83210c1439e";
|
||||||
|
hash = "sha256-oLf3YH3yeg4ikVO/GahMCDRMTU31AHkfSnF4rt7xTKo=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "122055beff332830a391e9895c044d33b15ea21063779557024b46169fb1984c6e40";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "zg";
|
||||||
|
url = "https://codeberg.org/atman/zg/archive/v0.13.2.tar.gz";
|
||||||
|
hash = "sha256-2x9hT7bYq9KJYWLVOf21a+QvTG/F7HWT+YK15IMRzNY=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "12201f0d542e7541cf492a001d4d0d0155c92f58212fbcb0d224e95edeba06b5416a";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "z2d";
|
||||||
|
url = "https://deps.files.ghostty.org/z2d-12201f0d542e7541cf492a001d4d0d0155c92f58212fbcb0d224e95edeba06b5416a.tar.gz";
|
||||||
|
hash = "sha256-P0UJ54RO/vVyDa+UkBl+QEOjzoMMEFSOTexQP/uBXfc=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "1220e17e64ef0ef561b3e4b9f3a96a2494285f2ec31c097721bf8c8677ec4415c634";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "zig_objc";
|
||||||
|
url = "https://deps.files.ghostty.org/zig_objc-1220e17e64ef0ef561b3e4b9f3a96a2494285f2ec31c097721bf8c8677ec4415c634.tar.gz";
|
||||||
|
hash = "sha256-H+HIbh2T23uzrsg9/1/vl9Ir1HCAa2pzeTx6zktJH9Q=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "12205a66d423259567764fa0fc60c82be35365c21aeb76c5a7dc99698401f4f6fefc";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "zig_js";
|
||||||
|
url = "https://deps.files.ghostty.org/zig_js-12205a66d423259567764fa0fc60c82be35365c21aeb76c5a7dc99698401f4f6fefc.tar.gz";
|
||||||
|
hash = "sha256-fyNeCVbC9UAaKJY6JhAZlT0A479M/AKYMPIWEZbDWD0=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "12207831bce7d4abce57b5a98e8f3635811cfefd160bca022eb91fe905d36a02cf25";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "ziglyph";
|
||||||
|
url = "https://deps.files.ghostty.org/ziglyph-b89d43d1e3fb01b6074bc1f7fc980324b04d26a5.tar.gz";
|
||||||
|
hash = "sha256-cse98+Ft8QUjX+P88yyYfaxJOJGQ9M7Ymw7jFxDz89k=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "12209ca054cb1919fa276e328967f10b253f7537c4136eb48f3332b0f7cf661cad38";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "zig_wayland";
|
||||||
|
url = "https://deps.files.ghostty.org/zig-wayland-fbfe3b4ac0b472a27b1f1a67405436c58cbee12d.tar.gz";
|
||||||
|
hash = "sha256-RtAystqK/GRYIquTK1KfD7rRSCrfuzAvCD1Z9DE1ldc=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "1220edc3b8d8bedbb50555947987e5e8e2f93871ca3c8e8d4cc8f1377c15b5dd35e8";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "zf";
|
||||||
|
url = "https://deps.files.ghostty.org/zf-1220edc3b8d8bedbb50555947987e5e8e2f93871ca3c8e8d4cc8f1377c15b5dd35e8.tar.gz";
|
||||||
|
hash = "sha256-/oLryY3VQfjbtQi+UP+n6FJTVA/YxIetjO+6Ovrh6/E=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "1220c72c1697dd9008461ead702997a15d8a1c5810247f02e7983b9f74c6c6e4c087";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "vaxis";
|
||||||
|
url = "git+https://github.com/rockorager/libvaxis/?ref=main#dc0a228a5544988d4a920cfb40be9cd28db41423";
|
||||||
|
hash = "sha256-QWN4jOrA91KlbqmeEHHJ4HTnCC9nmfxt8DHUXJpAzLI=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "12208d70ee791d7ef7e16e1c3c9c1127b57f1ed066a24f87d57fc9f730c5dc394b9d";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "gobject";
|
||||||
|
url = "https://deps.files.ghostty.org/gobject-12208d70ee791d7ef7e16e1c3c9c1127b57f1ed066a24f87d57fc9f730c5dc394b9d.tar.zst";
|
||||||
|
hash = "sha256-UU97kNv/bZzQPKz1djhEDLapLguvfBpFfWVb6FthtcI=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "12202cdac858abc52413a6c6711d5026d2d3c8e13f95ca2c327eade0736298bb021f";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "wayland";
|
||||||
|
url = "https://deps.files.ghostty.org/wayland-9cb3d7aa9dc995ffafdbdef7ab86a949d0fb0e7d.tar.gz";
|
||||||
|
hash = "sha256-6kGR1o5DdnflHzqs3ieCmBAUTpMdOXoyfcYDXiw5xQ0=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "12201a57c6ce0001aa034fa80fba3e1cd2253c560a45748f4f4dd21ff23b491cddef";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "wayland_protocols";
|
||||||
|
url = "https://deps.files.ghostty.org/wayland-protocols-258d8f88f2c8c25a830c6316f87d23ce1a0f12d9.tar.gz";
|
||||||
|
hash = "sha256-XO3K3egbdeYPI+XoO13SuOtO+5+Peb16NH0UiusFMPg=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "12207e0851c12acdeee0991e893e0132fc87bb763969a585dc16ecca33e88334c566";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "plasma_wayland_protocols";
|
||||||
|
url = "https://deps.files.ghostty.org/plasma_wayland_protocols-12207e0851c12acdeee0991e893e0132fc87bb763969a585dc16ecca33e88334c566.tar.gz";
|
||||||
|
hash = "sha256-XFi6IUrNjmvKNCbcCLAixGqN2Zeymhs+KLrfccIN9EE=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "1220a1dbe41bc69aacf75026a7158812198ea265fb9cac64dcb91cd31f3b1b8c1f92";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "iterm2_themes";
|
||||||
|
url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/efb1bb1843500a751eb30afa58fe48a6bec8952c.tar.gz";
|
||||||
|
hash = "sha256-ZdVc1mmLwF45PZiqL/j/l7MO2O6hZ11lqIToGFdHiEU=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "imgui";
|
||||||
|
url = "https://deps.files.ghostty.org/imgui-1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402.tar.gz";
|
||||||
|
hash = "sha256-oF/QHgTPEat4Hig4fGIdLkIPHmBEyOJ6JeYD6pnveGA=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "1220b81f6ecfb3fd222f76cf9106fecfa6554ab07ec7fdc4124b9bb063ae2adf969d";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "freetype";
|
||||||
|
url = "https://deps.files.ghostty.org/freetype-1220b81f6ecfb3fd222f76cf9106fecfa6554ab07ec7fdc4124b9bb063ae2adf969d.tar.gz";
|
||||||
|
hash = "sha256-QnIB9dUVFnDQXB9bRb713aHy592XHvVPD+qqf/0quQw=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "1220aa013f0c83da3fb64ea6d327f9173fa008d10e28bc9349eac3463457723b1c66";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "libpng";
|
||||||
|
url = "https://deps.files.ghostty.org/libpng-1220aa013f0c83da3fb64ea6d327f9173fa008d10e28bc9349eac3463457723b1c66.tar.gz";
|
||||||
|
hash = "sha256-/syVtGzwXo4/yKQUdQ4LparQDYnp/fF16U/wQcrxoDo=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "zlib";
|
||||||
|
url = "https://deps.files.ghostty.org/zlib-1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb.tar.gz";
|
||||||
|
hash = "sha256-F+iIY/NgBnKrSRgvIXKBtvxNPHYr3jYZNeQ2qVIU0Fw=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "12201149afb3326c56c05bb0a577f54f76ac20deece63aa2f5cd6ff31a4fa4fcb3b7";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "fontconfig";
|
||||||
|
url = "https://deps.files.ghostty.org/fontconfig-2.14.2.tar.gz";
|
||||||
|
hash = "sha256-O6LdkhWHGKzsXKrxpxYEO1qgVcJ7CB2RSvPMtA3OilU=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "122032442d95c3b428ae8e526017fad881e7dc78eab4d558e9a58a80bfbd65a64f7d";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "libxml2";
|
||||||
|
url = "https://github.com/GNOME/libxml2/archive/refs/tags/v2.11.5.tar.gz";
|
||||||
|
hash = "sha256-bCgFni4+60K1tLFkieORamNGwQladP7jvGXNxdiaYhU=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "1220b8588f106c996af10249bfa092c6fb2f35fbacb1505ef477a0b04a7dd1063122";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "harfbuzz";
|
||||||
|
url = "https://deps.files.ghostty.org/harfbuzz-1220b8588f106c996af10249bfa092c6fb2f35fbacb1505ef477a0b04a7dd1063122.tar.gz";
|
||||||
|
hash = "sha256-nxygiYE7BZRK0c6MfgGCEwJtNdybq0gKIeuHaDg5ZVY=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "12205c83b8311a24b1d5ae6d21640df04f4b0726e314337c043cde1432758cbe165b";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "highway";
|
||||||
|
url = "https://deps.files.ghostty.org/highway-12205c83b8311a24b1d5ae6d21640df04f4b0726e314337c043cde1432758cbe165b.tar.gz";
|
||||||
|
hash = "sha256-NUqLRTm1iOcLmOxwhEJz4/J0EwLEw3e8xOgbPRhm98k=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "1220c15e72eadd0d9085a8af134904d9a0f5dfcbed5f606ad60edc60ebeccd9706bb";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "oniguruma";
|
||||||
|
url = "https://deps.files.ghostty.org/oniguruma-1220c15e72eadd0d9085a8af134904d9a0f5dfcbed5f606ad60edc60ebeccd9706bb.tar.gz";
|
||||||
|
hash = "sha256-ABqhIC54RI9MC/GkjHblVodrNvFtks4yB+zP1h2Z8qA=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "1220446be831adcca918167647c06c7b825849fa3fba5f22da394667974537a9c77e";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "sentry";
|
||||||
|
url = "https://deps.files.ghostty.org/sentry-1220446be831adcca918167647c06c7b825849fa3fba5f22da394667974537a9c77e.tar.gz";
|
||||||
|
hash = "sha256-KsZJfMjWGo0xCT5HrduMmyxFsWsHBbszSoNbZCPDGN8=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "12207fd37bb8251919c112dcdd8f616a491857b34a451f7e4486490077206dc2a1ea";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "breakpad";
|
||||||
|
url = "https://github.com/getsentry/breakpad/archive/b99f444ba5f6b98cac261cbb391d8766b34a5918.tar.gz";
|
||||||
|
hash = "sha256-bMqYlD0amQdmzvYQd8Ca/1k4Bj/heh7+EijlQSttatk=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "1220d4d18426ca72fc2b7e56ce47273149815501d0d2395c2a98c726b31ba931e641";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "utfcpp";
|
||||||
|
url = "https://deps.files.ghostty.org/utfcpp-1220d4d18426ca72fc2b7e56ce47273149815501d0d2395c2a98c726b31ba931e641.tar.gz";
|
||||||
|
hash = "sha256-/8ZooxDndgfTk/PBizJxXyI9oerExNbgV5oR345rWc8=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "122037b39d577ec2db3fd7b2130e7b69ef6cc1807d68607a7c232c958315d381b5cd";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "wuffs";
|
||||||
|
url = "https://deps.files.ghostty.org/wuffs-122037b39d577ec2db3fd7b2130e7b69ef6cc1807d68607a7c232c958315d381b5cd.tar.gz";
|
||||||
|
hash = "sha256-nkzSCr6W5sTG7enDBXEIhgEm574uLD41UVR2wlC+HBM=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "12207ff340169c7d40c570b4b6a97db614fe47e0d83b5801a932dcd44917424c8806";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "pixels";
|
||||||
|
url = "https://deps.files.ghostty.org/pixels-12207ff340169c7d40c570b4b6a97db614fe47e0d83b5801a932dcd44917424c8806.tar.gz";
|
||||||
|
hash = "sha256-Veg7FtCRCCUCvxSb9FfzH0IJLFmCZQ4/+657SIcb8Ro=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "12201278a1a05c0ce0b6eb6026c65cd3e9247aa041b1c260324bf29cee559dd23ba1";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "glslang";
|
||||||
|
url = "https://deps.files.ghostty.org/glslang-12201278a1a05c0ce0b6eb6026c65cd3e9247aa041b1c260324bf29cee559dd23ba1.tar.gz";
|
||||||
|
hash = "sha256-FKLtu1Ccs+UamlPj9eQ12/WXFgS0uDPmPmB26MCpl7U=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "1220fb3b5586e8be67bc3feb34cbe749cf42a60d628d2953632c2f8141302748c8da";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "spirv_cross";
|
||||||
|
url = "https://deps.files.ghostty.org/spirv_cross-1220fb3b5586e8be67bc3feb34cbe749cf42a60d628d2953632c2f8141302748c8da.tar.gz";
|
||||||
|
hash = "sha256-tStvz8Ref6abHwahNiwVVHNETizAmZVVaxVsU7pmV+M=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
]
|
38
build.zig.zon.txt
generated
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
git+https://github.com/rockorager/libvaxis#2237a7059eae99e9f132dd5acd1555e49d6c7d93
|
||||||
|
git+https://github.com/rockorager/libvaxis/?ref=main#dc0a228a5544988d4a920cfb40be9cd28db41423
|
||||||
|
git+https://github.com/zigimg/zigimg#3a667bdb3d7f0955a5a51c8468eac83210c1439e
|
||||||
|
https://codeberg.org/atman/zg/archive/v0.13.2.tar.gz
|
||||||
|
https://deps.files.ghostty.org/fontconfig-2.14.2.tar.gz
|
||||||
|
https://deps.files.ghostty.org/freetype-1220b81f6ecfb3fd222f76cf9106fecfa6554ab07ec7fdc4124b9bb063ae2adf969d.tar.gz
|
||||||
|
https://deps.files.ghostty.org/glslang-12201278a1a05c0ce0b6eb6026c65cd3e9247aa041b1c260324bf29cee559dd23ba1.tar.gz
|
||||||
|
https://deps.files.ghostty.org/gobject-12208d70ee791d7ef7e16e1c3c9c1127b57f1ed066a24f87d57fc9f730c5dc394b9d.tar.zst
|
||||||
|
https://deps.files.ghostty.org/harfbuzz-1220b8588f106c996af10249bfa092c6fb2f35fbacb1505ef477a0b04a7dd1063122.tar.gz
|
||||||
|
https://deps.files.ghostty.org/highway-12205c83b8311a24b1d5ae6d21640df04f4b0726e314337c043cde1432758cbe165b.tar.gz
|
||||||
|
https://deps.files.ghostty.org/imgui-1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402.tar.gz
|
||||||
|
https://deps.files.ghostty.org/libpng-1220aa013f0c83da3fb64ea6d327f9173fa008d10e28bc9349eac3463457723b1c66.tar.gz
|
||||||
|
https://deps.files.ghostty.org/libxev-1220ebf88622c4d502dc59e71347e4d28c47e033f11b59aff774ae5787565c40999c.tar.gz
|
||||||
|
https://deps.files.ghostty.org/mach_glfw-12206ed982e709e565d536ce930701a8c07edfd2cfdce428683f3f2a601d37696a62.tar.gz
|
||||||
|
https://deps.files.ghostty.org/oniguruma-1220c15e72eadd0d9085a8af134904d9a0f5dfcbed5f606ad60edc60ebeccd9706bb.tar.gz
|
||||||
|
https://deps.files.ghostty.org/pixels-12207ff340169c7d40c570b4b6a97db614fe47e0d83b5801a932dcd44917424c8806.tar.gz
|
||||||
|
https://deps.files.ghostty.org/plasma_wayland_protocols-12207e0851c12acdeee0991e893e0132fc87bb763969a585dc16ecca33e88334c566.tar.gz
|
||||||
|
https://deps.files.ghostty.org/sentry-1220446be831adcca918167647c06c7b825849fa3fba5f22da394667974537a9c77e.tar.gz
|
||||||
|
https://deps.files.ghostty.org/spirv_cross-1220fb3b5586e8be67bc3feb34cbe749cf42a60d628d2953632c2f8141302748c8da.tar.gz
|
||||||
|
https://deps.files.ghostty.org/utfcpp-1220d4d18426ca72fc2b7e56ce47273149815501d0d2395c2a98c726b31ba931e641.tar.gz
|
||||||
|
https://deps.files.ghostty.org/wayland-9cb3d7aa9dc995ffafdbdef7ab86a949d0fb0e7d.tar.gz
|
||||||
|
https://deps.files.ghostty.org/wayland-protocols-258d8f88f2c8c25a830c6316f87d23ce1a0f12d9.tar.gz
|
||||||
|
https://deps.files.ghostty.org/wuffs-122037b39d577ec2db3fd7b2130e7b69ef6cc1807d68607a7c232c958315d381b5cd.tar.gz
|
||||||
|
https://deps.files.ghostty.org/z2d-12201f0d542e7541cf492a001d4d0d0155c92f58212fbcb0d224e95edeba06b5416a.tar.gz
|
||||||
|
https://deps.files.ghostty.org/zf-1220edc3b8d8bedbb50555947987e5e8e2f93871ca3c8e8d4cc8f1377c15b5dd35e8.tar.gz
|
||||||
|
https://deps.files.ghostty.org/zig-wayland-fbfe3b4ac0b472a27b1f1a67405436c58cbee12d.tar.gz
|
||||||
|
https://deps.files.ghostty.org/zig_js-12205a66d423259567764fa0fc60c82be35365c21aeb76c5a7dc99698401f4f6fefc.tar.gz
|
||||||
|
https://deps.files.ghostty.org/zig_objc-1220e17e64ef0ef561b3e4b9f3a96a2494285f2ec31c097721bf8c8677ec4415c634.tar.gz
|
||||||
|
https://deps.files.ghostty.org/ziglyph-b89d43d1e3fb01b6074bc1f7fc980324b04d26a5.tar.gz
|
||||||
|
https://deps.files.ghostty.org/zlib-1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb.tar.gz
|
||||||
|
https://github.com/GNOME/libxml2/archive/refs/tags/v2.11.5.tar.gz
|
||||||
|
https://github.com/getsentry/breakpad/archive/b99f444ba5f6b98cac261cbb391d8766b34a5918.tar.gz
|
||||||
|
https://github.com/mbadolato/iTerm2-Color-Schemes/archive/db227d159adc265818f2e898da0f70ef8d7b580e.tar.gz
|
||||||
|
https://github.com/mitchellh/glfw/archive/b552c6ec47326b94015feddb36058ea567b87159.tar.gz
|
||||||
|
https://github.com/mitchellh/vulkan-headers/archive/04c8a0389d5a0236a96312988017cd4ce27d8041.tar.gz
|
||||||
|
https://github.com/mitchellh/wayland-headers/archive/5f991515a29f994d87b908115a2ab0b899474bd1.tar.gz
|
||||||
|
https://github.com/mitchellh/x11-headers/archive/2ffbd62d82ff73ec929dd8de802bc95effa0ef88.tar.gz
|
||||||
|
https://github.com/mitchellh/xcode-frameworks/archive/69801c154c39d7ae6129ea1ba8fe1afe00585fc8.tar.gz
|
192
build.zig.zon2json-lock
generated
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
{
|
||||||
|
"1220ebf88622c4d502dc59e71347e4d28c47e033f11b59aff774ae5787565c40999c": {
|
||||||
|
"name": "libxev",
|
||||||
|
"url": "https://deps.files.ghostty.org/libxev-1220ebf88622c4d502dc59e71347e4d28c47e033f11b59aff774ae5787565c40999c.tar.gz",
|
||||||
|
"hash": "sha256-VHP90NTytIZ8UZsYRKOOxN490/I6yv6ec40sP8y5MJ8="
|
||||||
|
},
|
||||||
|
"12206ed982e709e565d536ce930701a8c07edfd2cfdce428683f3f2a601d37696a62": {
|
||||||
|
"name": "mach_glfw",
|
||||||
|
"url": "https://deps.files.ghostty.org/mach_glfw-12206ed982e709e565d536ce930701a8c07edfd2cfdce428683f3f2a601d37696a62.tar.gz",
|
||||||
|
"hash": "sha256-HhXIvWUS8/CHWY4VXPG2ZEo+we8XOn3o5rYJCQ1n8Nk="
|
||||||
|
},
|
||||||
|
"1220736fa4ba211162c7a0e46cc8fe04d95921927688bff64ab5da7420d098a7272d": {
|
||||||
|
"name": "glfw",
|
||||||
|
"url": "https://github.com/mitchellh/glfw/archive/b552c6ec47326b94015feddb36058ea567b87159.tar.gz",
|
||||||
|
"hash": "sha256-IeBVAOQmtyFqVxzuXPek1onuPwIamcOyYtxqKpPEQjU="
|
||||||
|
},
|
||||||
|
"12202adbfecdad671d585c9a5bfcbd5cdf821726779430047742ce1bf94ad67d19cb": {
|
||||||
|
"name": "xcode_frameworks",
|
||||||
|
"url": "https://github.com/mitchellh/xcode-frameworks/archive/69801c154c39d7ae6129ea1ba8fe1afe00585fc8.tar.gz",
|
||||||
|
"hash": "sha256-mP/I2coL57UJm/3+4Q8sPAgQwk8V4zM+S4VBBTrX2To="
|
||||||
|
},
|
||||||
|
"122004bfd4c519dadfb8e6281a42fc34fd1aa15aea654ea8a492839046f9894fa2cf": {
|
||||||
|
"name": "vulkan_headers",
|
||||||
|
"url": "https://github.com/mitchellh/vulkan-headers/archive/04c8a0389d5a0236a96312988017cd4ce27d8041.tar.gz",
|
||||||
|
"hash": "sha256-K+zrRudgHFukOM6En1StRYRMNYkeRk+qHTXvrXaG+FU="
|
||||||
|
},
|
||||||
|
"1220b3164434d2ec9db146a40bf3a30f490590d68fa8529776a3138074f0da2c11ca": {
|
||||||
|
"name": "wayland_headers",
|
||||||
|
"url": "https://github.com/mitchellh/wayland-headers/archive/5f991515a29f994d87b908115a2ab0b899474bd1.tar.gz",
|
||||||
|
"hash": "sha256-uFilLZinKkZt6RdVTV3lUmJpzpswDdFva22FvwU/XQI="
|
||||||
|
},
|
||||||
|
"122089c326186c84aa2fd034b16abc38f3ebf4862d9ae106dc1847ac44f557b36465": {
|
||||||
|
"name": "x11_headers",
|
||||||
|
"url": "https://github.com/mitchellh/x11-headers/archive/2ffbd62d82ff73ec929dd8de802bc95effa0ef88.tar.gz",
|
||||||
|
"hash": "sha256-EhV2bmTY/OMYN1wEul35gD0hQgS/Al262jO3pVr0O+c="
|
||||||
|
},
|
||||||
|
"1220f5aec880d4f430cc1597ede88f1530da69e39a4986080e976b0c7b919c2ebfeb": {
|
||||||
|
"name": "vaxis",
|
||||||
|
"url": "git+https://github.com/rockorager/libvaxis#2237a7059eae99e9f132dd5acd1555e49d6c7d93",
|
||||||
|
"hash": "sha256-ZzLNJOsXzyBhkdUhbET30RoU2T9xKYsBUQz2NAjK/G8="
|
||||||
|
},
|
||||||
|
"1220dd654ef941fc76fd96f9ec6adadf83f69b9887a0d3f4ee5ac0a1a3e11be35cf5": {
|
||||||
|
"name": "zigimg",
|
||||||
|
"url": "git+https://github.com/zigimg/zigimg#3a667bdb3d7f0955a5a51c8468eac83210c1439e",
|
||||||
|
"hash": "sha256-oLf3YH3yeg4ikVO/GahMCDRMTU31AHkfSnF4rt7xTKo="
|
||||||
|
},
|
||||||
|
"122055beff332830a391e9895c044d33b15ea21063779557024b46169fb1984c6e40": {
|
||||||
|
"name": "zg",
|
||||||
|
"url": "https://codeberg.org/atman/zg/archive/v0.13.2.tar.gz",
|
||||||
|
"hash": "sha256-2x9hT7bYq9KJYWLVOf21a+QvTG/F7HWT+YK15IMRzNY="
|
||||||
|
},
|
||||||
|
"12201f0d542e7541cf492a001d4d0d0155c92f58212fbcb0d224e95edeba06b5416a": {
|
||||||
|
"name": "z2d",
|
||||||
|
"url": "https://deps.files.ghostty.org/z2d-12201f0d542e7541cf492a001d4d0d0155c92f58212fbcb0d224e95edeba06b5416a.tar.gz",
|
||||||
|
"hash": "sha256-P0UJ54RO/vVyDa+UkBl+QEOjzoMMEFSOTexQP/uBXfc="
|
||||||
|
},
|
||||||
|
"1220e17e64ef0ef561b3e4b9f3a96a2494285f2ec31c097721bf8c8677ec4415c634": {
|
||||||
|
"name": "zig_objc",
|
||||||
|
"url": "https://deps.files.ghostty.org/zig_objc-1220e17e64ef0ef561b3e4b9f3a96a2494285f2ec31c097721bf8c8677ec4415c634.tar.gz",
|
||||||
|
"hash": "sha256-H+HIbh2T23uzrsg9/1/vl9Ir1HCAa2pzeTx6zktJH9Q="
|
||||||
|
},
|
||||||
|
"12205a66d423259567764fa0fc60c82be35365c21aeb76c5a7dc99698401f4f6fefc": {
|
||||||
|
"name": "zig_js",
|
||||||
|
"url": "https://deps.files.ghostty.org/zig_js-12205a66d423259567764fa0fc60c82be35365c21aeb76c5a7dc99698401f4f6fefc.tar.gz",
|
||||||
|
"hash": "sha256-fyNeCVbC9UAaKJY6JhAZlT0A479M/AKYMPIWEZbDWD0="
|
||||||
|
},
|
||||||
|
"12207831bce7d4abce57b5a98e8f3635811cfefd160bca022eb91fe905d36a02cf25": {
|
||||||
|
"name": "ziglyph",
|
||||||
|
"url": "https://deps.files.ghostty.org/ziglyph-b89d43d1e3fb01b6074bc1f7fc980324b04d26a5.tar.gz",
|
||||||
|
"hash": "sha256-cse98+Ft8QUjX+P88yyYfaxJOJGQ9M7Ymw7jFxDz89k="
|
||||||
|
},
|
||||||
|
"12209ca054cb1919fa276e328967f10b253f7537c4136eb48f3332b0f7cf661cad38": {
|
||||||
|
"name": "zig_wayland",
|
||||||
|
"url": "https://deps.files.ghostty.org/zig-wayland-fbfe3b4ac0b472a27b1f1a67405436c58cbee12d.tar.gz",
|
||||||
|
"hash": "sha256-RtAystqK/GRYIquTK1KfD7rRSCrfuzAvCD1Z9DE1ldc="
|
||||||
|
},
|
||||||
|
"1220edc3b8d8bedbb50555947987e5e8e2f93871ca3c8e8d4cc8f1377c15b5dd35e8": {
|
||||||
|
"name": "zf",
|
||||||
|
"url": "https://deps.files.ghostty.org/zf-1220edc3b8d8bedbb50555947987e5e8e2f93871ca3c8e8d4cc8f1377c15b5dd35e8.tar.gz",
|
||||||
|
"hash": "sha256-/oLryY3VQfjbtQi+UP+n6FJTVA/YxIetjO+6Ovrh6/E="
|
||||||
|
},
|
||||||
|
"1220c72c1697dd9008461ead702997a15d8a1c5810247f02e7983b9f74c6c6e4c087": {
|
||||||
|
"name": "vaxis",
|
||||||
|
"url": "git+https://github.com/rockorager/libvaxis/?ref=main#dc0a228a5544988d4a920cfb40be9cd28db41423",
|
||||||
|
"hash": "sha256-QWN4jOrA91KlbqmeEHHJ4HTnCC9nmfxt8DHUXJpAzLI="
|
||||||
|
},
|
||||||
|
"12208d70ee791d7ef7e16e1c3c9c1127b57f1ed066a24f87d57fc9f730c5dc394b9d": {
|
||||||
|
"name": "gobject",
|
||||||
|
"url": "https://deps.files.ghostty.org/gobject-12208d70ee791d7ef7e16e1c3c9c1127b57f1ed066a24f87d57fc9f730c5dc394b9d.tar.zst",
|
||||||
|
"hash": "sha256-UU97kNv/bZzQPKz1djhEDLapLguvfBpFfWVb6FthtcI="
|
||||||
|
},
|
||||||
|
"12202cdac858abc52413a6c6711d5026d2d3c8e13f95ca2c327eade0736298bb021f": {
|
||||||
|
"name": "wayland",
|
||||||
|
"url": "https://deps.files.ghostty.org/wayland-9cb3d7aa9dc995ffafdbdef7ab86a949d0fb0e7d.tar.gz",
|
||||||
|
"hash": "sha256-6kGR1o5DdnflHzqs3ieCmBAUTpMdOXoyfcYDXiw5xQ0="
|
||||||
|
},
|
||||||
|
"12201a57c6ce0001aa034fa80fba3e1cd2253c560a45748f4f4dd21ff23b491cddef": {
|
||||||
|
"name": "wayland_protocols",
|
||||||
|
"url": "https://deps.files.ghostty.org/wayland-protocols-258d8f88f2c8c25a830c6316f87d23ce1a0f12d9.tar.gz",
|
||||||
|
"hash": "sha256-XO3K3egbdeYPI+XoO13SuOtO+5+Peb16NH0UiusFMPg="
|
||||||
|
},
|
||||||
|
"12207e0851c12acdeee0991e893e0132fc87bb763969a585dc16ecca33e88334c566": {
|
||||||
|
"name": "plasma_wayland_protocols",
|
||||||
|
"url": "https://deps.files.ghostty.org/plasma_wayland_protocols-12207e0851c12acdeee0991e893e0132fc87bb763969a585dc16ecca33e88334c566.tar.gz",
|
||||||
|
"hash": "sha256-XFi6IUrNjmvKNCbcCLAixGqN2Zeymhs+KLrfccIN9EE="
|
||||||
|
},
|
||||||
|
"12203d2647e5daf36a9c85b969e03f422540786ce9ea624eb4c26d204fe1f46218f3": {
|
||||||
|
"name": "iterm2_themes",
|
||||||
|
"url": "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/db227d159adc265818f2e898da0f70ef8d7b580e.tar.gz",
|
||||||
|
"hash": "sha256-Iyf7U4rpvNkPX4AOEbYSYGte5+SjRwsWD2luOn1Hz8U="
|
||||||
|
},
|
||||||
|
"1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402": {
|
||||||
|
"name": "imgui",
|
||||||
|
"url": "https://deps.files.ghostty.org/imgui-1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402.tar.gz",
|
||||||
|
"hash": "sha256-oF/QHgTPEat4Hig4fGIdLkIPHmBEyOJ6JeYD6pnveGA="
|
||||||
|
},
|
||||||
|
"1220b81f6ecfb3fd222f76cf9106fecfa6554ab07ec7fdc4124b9bb063ae2adf969d": {
|
||||||
|
"name": "freetype",
|
||||||
|
"url": "https://deps.files.ghostty.org/freetype-1220b81f6ecfb3fd222f76cf9106fecfa6554ab07ec7fdc4124b9bb063ae2adf969d.tar.gz",
|
||||||
|
"hash": "sha256-QnIB9dUVFnDQXB9bRb713aHy592XHvVPD+qqf/0quQw="
|
||||||
|
},
|
||||||
|
"1220aa013f0c83da3fb64ea6d327f9173fa008d10e28bc9349eac3463457723b1c66": {
|
||||||
|
"name": "libpng",
|
||||||
|
"url": "https://deps.files.ghostty.org/libpng-1220aa013f0c83da3fb64ea6d327f9173fa008d10e28bc9349eac3463457723b1c66.tar.gz",
|
||||||
|
"hash": "sha256-/syVtGzwXo4/yKQUdQ4LparQDYnp/fF16U/wQcrxoDo="
|
||||||
|
},
|
||||||
|
"1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb": {
|
||||||
|
"name": "zlib",
|
||||||
|
"url": "https://deps.files.ghostty.org/zlib-1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb.tar.gz",
|
||||||
|
"hash": "sha256-F+iIY/NgBnKrSRgvIXKBtvxNPHYr3jYZNeQ2qVIU0Fw="
|
||||||
|
},
|
||||||
|
"12201149afb3326c56c05bb0a577f54f76ac20deece63aa2f5cd6ff31a4fa4fcb3b7": {
|
||||||
|
"name": "fontconfig",
|
||||||
|
"url": "https://deps.files.ghostty.org/fontconfig-2.14.2.tar.gz",
|
||||||
|
"hash": "sha256-O6LdkhWHGKzsXKrxpxYEO1qgVcJ7CB2RSvPMtA3OilU="
|
||||||
|
},
|
||||||
|
"122032442d95c3b428ae8e526017fad881e7dc78eab4d558e9a58a80bfbd65a64f7d": {
|
||||||
|
"name": "libxml2",
|
||||||
|
"url": "https://github.com/GNOME/libxml2/archive/refs/tags/v2.11.5.tar.gz",
|
||||||
|
"hash": "sha256-bCgFni4+60K1tLFkieORamNGwQladP7jvGXNxdiaYhU="
|
||||||
|
},
|
||||||
|
"1220b8588f106c996af10249bfa092c6fb2f35fbacb1505ef477a0b04a7dd1063122": {
|
||||||
|
"name": "harfbuzz",
|
||||||
|
"url": "https://deps.files.ghostty.org/harfbuzz-1220b8588f106c996af10249bfa092c6fb2f35fbacb1505ef477a0b04a7dd1063122.tar.gz",
|
||||||
|
"hash": "sha256-nxygiYE7BZRK0c6MfgGCEwJtNdybq0gKIeuHaDg5ZVY="
|
||||||
|
},
|
||||||
|
"12205c83b8311a24b1d5ae6d21640df04f4b0726e314337c043cde1432758cbe165b": {
|
||||||
|
"name": "highway",
|
||||||
|
"url": "https://deps.files.ghostty.org/highway-12205c83b8311a24b1d5ae6d21640df04f4b0726e314337c043cde1432758cbe165b.tar.gz",
|
||||||
|
"hash": "sha256-NUqLRTm1iOcLmOxwhEJz4/J0EwLEw3e8xOgbPRhm98k="
|
||||||
|
},
|
||||||
|
"1220c15e72eadd0d9085a8af134904d9a0f5dfcbed5f606ad60edc60ebeccd9706bb": {
|
||||||
|
"name": "oniguruma",
|
||||||
|
"url": "https://deps.files.ghostty.org/oniguruma-1220c15e72eadd0d9085a8af134904d9a0f5dfcbed5f606ad60edc60ebeccd9706bb.tar.gz",
|
||||||
|
"hash": "sha256-ABqhIC54RI9MC/GkjHblVodrNvFtks4yB+zP1h2Z8qA="
|
||||||
|
},
|
||||||
|
"1220446be831adcca918167647c06c7b825849fa3fba5f22da394667974537a9c77e": {
|
||||||
|
"name": "sentry",
|
||||||
|
"url": "https://deps.files.ghostty.org/sentry-1220446be831adcca918167647c06c7b825849fa3fba5f22da394667974537a9c77e.tar.gz",
|
||||||
|
"hash": "sha256-KsZJfMjWGo0xCT5HrduMmyxFsWsHBbszSoNbZCPDGN8="
|
||||||
|
},
|
||||||
|
"12207fd37bb8251919c112dcdd8f616a491857b34a451f7e4486490077206dc2a1ea": {
|
||||||
|
"name": "breakpad",
|
||||||
|
"url": "https://github.com/getsentry/breakpad/archive/b99f444ba5f6b98cac261cbb391d8766b34a5918.tar.gz",
|
||||||
|
"hash": "sha256-bMqYlD0amQdmzvYQd8Ca/1k4Bj/heh7+EijlQSttatk="
|
||||||
|
},
|
||||||
|
"1220d4d18426ca72fc2b7e56ce47273149815501d0d2395c2a98c726b31ba931e641": {
|
||||||
|
"name": "utfcpp",
|
||||||
|
"url": "https://deps.files.ghostty.org/utfcpp-1220d4d18426ca72fc2b7e56ce47273149815501d0d2395c2a98c726b31ba931e641.tar.gz",
|
||||||
|
"hash": "sha256-/8ZooxDndgfTk/PBizJxXyI9oerExNbgV5oR345rWc8="
|
||||||
|
},
|
||||||
|
"122037b39d577ec2db3fd7b2130e7b69ef6cc1807d68607a7c232c958315d381b5cd": {
|
||||||
|
"name": "wuffs",
|
||||||
|
"url": "https://deps.files.ghostty.org/wuffs-122037b39d577ec2db3fd7b2130e7b69ef6cc1807d68607a7c232c958315d381b5cd.tar.gz",
|
||||||
|
"hash": "sha256-nkzSCr6W5sTG7enDBXEIhgEm574uLD41UVR2wlC+HBM="
|
||||||
|
},
|
||||||
|
"12207ff340169c7d40c570b4b6a97db614fe47e0d83b5801a932dcd44917424c8806": {
|
||||||
|
"name": "pixels",
|
||||||
|
"url": "https://deps.files.ghostty.org/pixels-12207ff340169c7d40c570b4b6a97db614fe47e0d83b5801a932dcd44917424c8806.tar.gz",
|
||||||
|
"hash": "sha256-Veg7FtCRCCUCvxSb9FfzH0IJLFmCZQ4/+657SIcb8Ro="
|
||||||
|
},
|
||||||
|
"12201278a1a05c0ce0b6eb6026c65cd3e9247aa041b1c260324bf29cee559dd23ba1": {
|
||||||
|
"name": "glslang",
|
||||||
|
"url": "https://deps.files.ghostty.org/glslang-12201278a1a05c0ce0b6eb6026c65cd3e9247aa041b1c260324bf29cee559dd23ba1.tar.gz",
|
||||||
|
"hash": "sha256-FKLtu1Ccs+UamlPj9eQ12/WXFgS0uDPmPmB26MCpl7U="
|
||||||
|
},
|
||||||
|
"1220fb3b5586e8be67bc3feb34cbe749cf42a60d628d2953632c2f8141302748c8da": {
|
||||||
|
"name": "spirv_cross",
|
||||||
|
"url": "https://deps.files.ghostty.org/spirv_cross-1220fb3b5586e8be67bc3feb34cbe749cf42a60d628d2953632c2f8141302748c8da.tar.gz",
|
||||||
|
"hash": "sha256-tStvz8Ref6abHwahNiwVVHNETizAmZVVaxVsU7pmV+M="
|
||||||
|
}
|
||||||
|
}
|
1
dist/linux/app.desktop
vendored
@ -7,6 +7,7 @@ Icon=com.mitchellh.ghostty
|
|||||||
Categories=System;TerminalEmulator;
|
Categories=System;TerminalEmulator;
|
||||||
Keywords=terminal;tty;pty;
|
Keywords=terminal;tty;pty;
|
||||||
StartupNotify=true
|
StartupNotify=true
|
||||||
|
StartupWMClass=com.mitchellh.ghostty
|
||||||
Terminal=false
|
Terminal=false
|
||||||
Actions=new-window;
|
Actions=new-window;
|
||||||
X-GNOME-UsesNotifications=true
|
X-GNOME-UsesNotifications=true
|
||||||
|
0
dist/linux/ghostty_dolphin.desktop
vendored
Normal file → Executable file
97
dist/linux/ghostty_nautilus.py
vendored
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
# Adapted from wezterm: https://github.com/wez/wezterm/blob/main/assets/wezterm-nautilus.py
|
||||||
|
# original copyright notice:
|
||||||
|
#
|
||||||
|
# Copyright (C) 2022 Sebastian Wiesner <sebastian@swsnr.de>
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
from os.path import isdir
|
||||||
|
from gi import require_version
|
||||||
|
from gi.repository import Nautilus, GObject, Gio, GLib
|
||||||
|
|
||||||
|
|
||||||
|
class OpenInGhosttyAction(GObject.GObject, Nautilus.MenuProvider):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
session = Gio.bus_get_sync(Gio.BusType.SESSION, None)
|
||||||
|
self._systemd = None
|
||||||
|
# Check if the this system runs under systemd, per sd_booted(3)
|
||||||
|
if isdir('/run/systemd/system/'):
|
||||||
|
self._systemd = Gio.DBusProxy.new_sync(session,
|
||||||
|
Gio.DBusProxyFlags.NONE,
|
||||||
|
None,
|
||||||
|
"org.freedesktop.systemd1",
|
||||||
|
"/org/freedesktop/systemd1",
|
||||||
|
"org.freedesktop.systemd1.Manager", None)
|
||||||
|
|
||||||
|
def _open_terminal(self, path):
|
||||||
|
cmd = ['ghostty', f'--working-directory={path}', '--gtk-single-instance=false']
|
||||||
|
child = Gio.Subprocess.new(cmd, Gio.SubprocessFlags.NONE)
|
||||||
|
if self._systemd:
|
||||||
|
# Move new terminal into a dedicated systemd scope to make systemd
|
||||||
|
# track the terminal separately; in particular this makes systemd
|
||||||
|
# keep a separate CPU and memory account for the terminal which in turn
|
||||||
|
# ensures that oomd doesn't take nautilus down if a process in
|
||||||
|
# ghostty consumes a lot of memory.
|
||||||
|
pid = int(child.get_identifier())
|
||||||
|
props = [("PIDs", GLib.Variant('au', [pid])),
|
||||||
|
('CollectMode', GLib.Variant('s', 'inactive-or-failed'))]
|
||||||
|
name = 'app-nautilus-com.mitchellh.ghostty-{}.scope'.format(pid)
|
||||||
|
args = GLib.Variant('(ssa(sv)a(sa(sv)))', (name, 'fail', props, []))
|
||||||
|
self._systemd.call_sync('StartTransientUnit', args,
|
||||||
|
Gio.DBusCallFlags.NO_AUTO_START, 500, None)
|
||||||
|
|
||||||
|
def _menu_item_activated(self, _menu, paths):
|
||||||
|
for path in paths:
|
||||||
|
self._open_terminal(path)
|
||||||
|
|
||||||
|
def _make_item(self, name, paths):
|
||||||
|
item = Nautilus.MenuItem(name=name, label='Open in Ghostty',
|
||||||
|
icon='com.mitchellh.ghostty')
|
||||||
|
item.connect('activate', self._menu_item_activated, paths)
|
||||||
|
return item
|
||||||
|
|
||||||
|
def _paths_to_open(self, files):
|
||||||
|
paths = []
|
||||||
|
for file in files:
|
||||||
|
location = file.get_location() if file.is_directory() else file.get_parent_location()
|
||||||
|
path = location.get_path()
|
||||||
|
if path and path not in paths:
|
||||||
|
paths.append(path)
|
||||||
|
if 10 < len(paths):
|
||||||
|
# Let's not open anything if the user selected a lot of directories,
|
||||||
|
# to avoid accidentally spamming their desktop with dozends of
|
||||||
|
# new windows or tabs. Ten is a totally arbitrary limit :)
|
||||||
|
return []
|
||||||
|
else:
|
||||||
|
return paths
|
||||||
|
|
||||||
|
def get_file_items(self, *args):
|
||||||
|
# Nautilus 3.0 API passes args (window, files), 4.0 API just passes files
|
||||||
|
files = args[0] if len(args) == 1 else args[1]
|
||||||
|
paths = self._paths_to_open(files)
|
||||||
|
if paths:
|
||||||
|
return [self._make_item(name='GhosttyNautilus::open_in_ghostty', paths=paths)]
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def get_background_items(self, *args):
|
||||||
|
# Nautilus 3.0 API passes args (window, file), 4.0 API just passes file
|
||||||
|
file = args[0] if len(args) == 1 else args[1]
|
||||||
|
paths = self._paths_to_open([file])
|
||||||
|
if paths:
|
||||||
|
return [self._make_item(name='GhosttyNautilus::open_folder_in_ghostty', paths=paths)]
|
||||||
|
else:
|
||||||
|
return []
|
7
dist/macos/update_appcast_tag.py
vendored
@ -21,6 +21,7 @@ from datetime import datetime, timezone
|
|||||||
|
|
||||||
now = datetime.now(timezone.utc)
|
now = datetime.now(timezone.utc)
|
||||||
version = os.environ["GHOSTTY_VERSION"]
|
version = os.environ["GHOSTTY_VERSION"]
|
||||||
|
version_dash = version.replace('.', '-')
|
||||||
build = os.environ["GHOSTTY_BUILD"]
|
build = os.environ["GHOSTTY_BUILD"]
|
||||||
commit = os.environ["GHOSTTY_COMMIT"]
|
commit = os.environ["GHOSTTY_COMMIT"]
|
||||||
commit_long = os.environ["GHOSTTY_COMMIT_LONG"]
|
commit_long = os.environ["GHOSTTY_COMMIT_LONG"]
|
||||||
@ -82,6 +83,8 @@ elem = ET.SubElement(item, "sparkle:shortVersionString")
|
|||||||
elem.text = f"{version}"
|
elem.text = f"{version}"
|
||||||
elem = ET.SubElement(item, "sparkle:minimumSystemVersion")
|
elem = ET.SubElement(item, "sparkle:minimumSystemVersion")
|
||||||
elem.text = "13.0.0"
|
elem.text = "13.0.0"
|
||||||
|
elem = ET.SubElement(item, "sparkle:fullReleaseNotesLink")
|
||||||
|
elem.text = f"https://ghostty.org/docs/install/release-notes/{version_dash}"
|
||||||
elem = ET.SubElement(item, "description")
|
elem = ET.SubElement(item, "description")
|
||||||
elem.text = f"""
|
elem.text = f"""
|
||||||
<h1>Ghostty v{version}</h1>
|
<h1>Ghostty v{version}</h1>
|
||||||
@ -91,8 +94,8 @@ on {now.strftime('%Y-%m-%d')}.
|
|||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
We don't currently generate release notes for auto-updates.
|
We don't currently generate release notes for auto-updates.
|
||||||
You can view the complete changelog and release notes on
|
You can view the complete changelog and release notes
|
||||||
the <a href="https://ghostty.org">Ghostty website</a>.
|
at <a href="https://ghostty.org/docs/install/release-notes/{version_dash}">ghostty.org/docs/install/release-notes/{version_dash}</a>.
|
||||||
</p>
|
</p>
|
||||||
"""
|
"""
|
||||||
elem = ET.SubElement(item, "enclosure")
|
elem = ET.SubElement(item, "enclosure")
|
||||||
|
62
flake.lock
generated
@ -3,11 +3,11 @@
|
|||||||
"flake-compat": {
|
"flake-compat": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1696426674,
|
"lastModified": 1733328505,
|
||||||
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
"narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=",
|
||||||
"owner": "edolstra",
|
"owner": "edolstra",
|
||||||
"repo": "flake-compat",
|
"repo": "flake-compat",
|
||||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
"rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -21,11 +21,11 @@
|
|||||||
"systems": "systems"
|
"systems": "systems"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1705309234,
|
"lastModified": 1731533236,
|
||||||
"narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "flake-utils",
|
"repo": "flake-utils",
|
||||||
"rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -36,11 +36,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs-stable": {
|
"nixpkgs-stable": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1733423277,
|
"lastModified": 1738255539,
|
||||||
"narHash": "sha256-TxabjxEgkNbCGFRHgM/b9yZWlBj60gUOUnRT/wbVQR8=",
|
"narHash": "sha256-hP2eOqhIO/OILW+3moNWO4GtdJFYCqAe9yJZgvlCoDQ=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "e36963a147267afc055f7cf65225958633e536bf",
|
"rev": "c3511a3b53b482aa7547c9d1626fd7310c1de1c5",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -52,11 +52,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs-unstable": {
|
"nixpkgs-unstable": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1733229606,
|
"lastModified": 1738136902,
|
||||||
"narHash": "sha256-FLYY5M0rpa5C2QAE3CKLYAM6TwbKicdRK6qNrSHlNrE=",
|
"narHash": "sha256-pUvLijVGARw4u793APze3j6mU1Zwdtz7hGkGGkD87qw=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "566e53c2ad750c84f6d31f9ccb9d00f823165550",
|
"rev": "9a5db3142ce450045840cc8d832b13b8a2018e0c",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -69,9 +69,11 @@
|
|||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-compat": "flake-compat",
|
"flake-compat": "flake-compat",
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
"nixpkgs-stable": "nixpkgs-stable",
|
"nixpkgs-stable": "nixpkgs-stable",
|
||||||
"nixpkgs-unstable": "nixpkgs-unstable",
|
"nixpkgs-unstable": "nixpkgs-unstable",
|
||||||
"zig": "zig"
|
"zig": "zig",
|
||||||
|
"zig2nix": "zig2nix"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"systems": {
|
"systems": {
|
||||||
@ -92,17 +94,19 @@
|
|||||||
"zig": {
|
"zig": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-compat": [],
|
"flake-compat": [],
|
||||||
"flake-utils": "flake-utils",
|
"flake-utils": [
|
||||||
|
"flake-utils"
|
||||||
|
],
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"nixpkgs-stable"
|
"nixpkgs-stable"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1717848532,
|
"lastModified": 1738239110,
|
||||||
"narHash": "sha256-d+xIUvSTreHl8pAmU1fnmkfDTGQYCn2Rb/zOwByxS2M=",
|
"narHash": "sha256-Y5i9mQ++dyIQr+zEPNy+KIbc5wjPmfllBrag3cHZgcE=",
|
||||||
"owner": "mitchellh",
|
"owner": "mitchellh",
|
||||||
"repo": "zig-overlay",
|
"repo": "zig-overlay",
|
||||||
"rev": "02fc5cc555fc14fda40c42d7c3250efa43812b43",
|
"rev": "1a8fb6f3a04724519436355564b95fce5e272504",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -110,6 +114,30 @@
|
|||||||
"repo": "zig-overlay",
|
"repo": "zig-overlay",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"zig2nix": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": [
|
||||||
|
"flake-utils"
|
||||||
|
],
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs-stable"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1738263917,
|
||||||
|
"narHash": "sha256-j/3fwe2pEOquHabP/puljOKwAZFjIE9gXZqA91sC48M=",
|
||||||
|
"owner": "jcollie",
|
||||||
|
"repo": "zig2nix",
|
||||||
|
"rev": "c311d8e77a6ee0d995f40a6e10a89a3a4ab04f9a",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "jcollie",
|
||||||
|
"ref": "c311d8e77a6ee0d995f40a6e10a89a3a4ab04f9a",
|
||||||
|
"repo": "zig2nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"root": "root",
|
"root": "root",
|
||||||
|
106
flake.nix
@ -8,6 +8,7 @@
|
|||||||
# glibc versions used by our dependencies from Nix are compatible with the
|
# glibc versions used by our dependencies from Nix are compatible with the
|
||||||
# system glibc that the user is building for.
|
# system glibc that the user is building for.
|
||||||
nixpkgs-stable.url = "github:nixos/nixpkgs/release-24.11";
|
nixpkgs-stable.url = "github:nixos/nixpkgs/release-24.11";
|
||||||
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
|
||||||
# Used for shell.nix
|
# Used for shell.nix
|
||||||
flake-compat = {
|
flake-compat = {
|
||||||
@ -19,9 +20,18 @@
|
|||||||
url = "github:mitchellh/zig-overlay";
|
url = "github:mitchellh/zig-overlay";
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.follows = "nixpkgs-stable";
|
nixpkgs.follows = "nixpkgs-stable";
|
||||||
|
flake-utils.follows = "flake-utils";
|
||||||
flake-compat.follows = "";
|
flake-compat.follows = "";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
zig2nix = {
|
||||||
|
url = "github:jcollie/zig2nix?ref=c311d8e77a6ee0d995f40a6e10a89a3a4ab04f9a";
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.follows = "nixpkgs-stable";
|
||||||
|
flake-utils.follows = "flake-utils";
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = {
|
outputs = {
|
||||||
@ -29,40 +39,86 @@
|
|||||||
nixpkgs-unstable,
|
nixpkgs-unstable,
|
||||||
nixpkgs-stable,
|
nixpkgs-stable,
|
||||||
zig,
|
zig,
|
||||||
|
zig2nix,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
builtins.foldl' nixpkgs-stable.lib.recursiveUpdate {} (builtins.map (system: let
|
builtins.foldl' nixpkgs-stable.lib.recursiveUpdate {} (
|
||||||
pkgs-stable = nixpkgs-stable.legacyPackages.${system};
|
builtins.map (
|
||||||
pkgs-unstable = nixpkgs-unstable.legacyPackages.${system};
|
system: let
|
||||||
in {
|
pkgs-stable = nixpkgs-stable.legacyPackages.${system};
|
||||||
devShell.${system} = pkgs-stable.callPackage ./nix/devShell.nix {
|
pkgs-unstable = nixpkgs-unstable.legacyPackages.${system};
|
||||||
zig = zig.packages.${system}."0.13.0";
|
in {
|
||||||
wraptest = pkgs-stable.callPackage ./nix/wraptest.nix {};
|
devShell.${system} = pkgs-stable.callPackage ./nix/devShell.nix {
|
||||||
};
|
zig = zig.packages.${system}."0.13.0";
|
||||||
|
wraptest = pkgs-stable.callPackage ./nix/wraptest.nix {};
|
||||||
|
zig2nix = zig2nix;
|
||||||
|
};
|
||||||
|
|
||||||
packages.${system} = let
|
packages.${system} = let
|
||||||
mkArgs = optimize: {
|
mkArgs = optimize: {
|
||||||
inherit optimize;
|
inherit optimize;
|
||||||
|
|
||||||
revision = self.shortRev or self.dirtyShortRev or "dirty";
|
revision = self.shortRev or self.dirtyShortRev or "dirty";
|
||||||
};
|
};
|
||||||
in rec {
|
in rec {
|
||||||
ghostty-debug = pkgs-stable.callPackage ./nix/package.nix (mkArgs "Debug");
|
deps = pkgs-stable.callPackage ./build.zig.zon.nix {};
|
||||||
ghostty-releasesafe = pkgs-stable.callPackage ./nix/package.nix (mkArgs "ReleaseSafe");
|
ghostty-debug = pkgs-stable.callPackage ./nix/package.nix (mkArgs "Debug");
|
||||||
ghostty-releasefast = pkgs-stable.callPackage ./nix/package.nix (mkArgs "ReleaseFast");
|
ghostty-releasesafe = pkgs-stable.callPackage ./nix/package.nix (mkArgs "ReleaseSafe");
|
||||||
|
ghostty-releasefast = pkgs-stable.callPackage ./nix/package.nix (mkArgs "ReleaseFast");
|
||||||
|
|
||||||
ghostty = ghostty-releasefast;
|
ghostty = ghostty-releasefast;
|
||||||
default = ghostty;
|
default = ghostty;
|
||||||
};
|
};
|
||||||
|
|
||||||
formatter.${system} = pkgs-stable.alejandra;
|
formatter.${system} = pkgs-stable.alejandra;
|
||||||
|
|
||||||
# Our supported systems are the same supported systems as the Zig binaries.
|
apps.${system} = let
|
||||||
}) (builtins.attrNames zig.packages))
|
runVM = (
|
||||||
|
module: let
|
||||||
|
vm = import ./nix/vm/create.nix {
|
||||||
|
inherit system module;
|
||||||
|
nixpkgs = nixpkgs-stable;
|
||||||
|
overlay = self.overlays.debug;
|
||||||
|
};
|
||||||
|
program = pkgs-stable.writeShellScript "run-ghostty-vm" ''
|
||||||
|
SHARED_DIR=$(pwd)
|
||||||
|
export SHARED_DIR
|
||||||
|
|
||||||
|
${pkgs-stable.lib.getExe vm.config.system.build.vm} "$@"
|
||||||
|
'';
|
||||||
|
in {
|
||||||
|
type = "app";
|
||||||
|
program = "${program}";
|
||||||
|
}
|
||||||
|
);
|
||||||
|
in {
|
||||||
|
wayland-cinnamon = runVM ./nix/vm/wayland-cinnamon.nix;
|
||||||
|
wayland-gnome = runVM ./nix/vm/wayland-gnome.nix;
|
||||||
|
wayland-plasma6 = runVM ./nix/vm/wayland-plasma6.nix;
|
||||||
|
x11-cinnamon = runVM ./nix/vm/x11-cinnamon.nix;
|
||||||
|
x11-gnome = runVM ./nix/vm/x11-gnome.nix;
|
||||||
|
x11-plasma6 = runVM ./nix/vm/x11-plasma6.nix;
|
||||||
|
x11-xfce = runVM ./nix/vm/x11-xfce.nix;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
# Our supported systems are the same supported systems as the Zig binaries.
|
||||||
|
) (builtins.attrNames zig.packages)
|
||||||
|
)
|
||||||
// {
|
// {
|
||||||
overlays.default = final: prev: {
|
overlays = {
|
||||||
ghostty = self.packages.${prev.system}.default;
|
default = self.overlays.releasefast;
|
||||||
|
releasefast = final: prev: {
|
||||||
|
ghostty = self.packages.${prev.system}.ghostty-releasefast;
|
||||||
|
};
|
||||||
|
debug = final: prev: {
|
||||||
|
ghostty = self.packages.${prev.system}.ghostty-debug;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
create-vm = import ./nix/vm/create.nix;
|
||||||
|
create-cinnamon-vm = import ./nix/vm/create-cinnamon.nix;
|
||||||
|
create-gnome-vm = import ./nix/vm/create-gnome.nix;
|
||||||
|
create-plasma6-vm = import ./nix/vm/create-plasma6.nix;
|
||||||
|
create-xfce-vm = import ./nix/vm/create-xfce.nix;
|
||||||
};
|
};
|
||||||
|
|
||||||
nixConfig = {
|
nixConfig = {
|
||||||
|
@ -159,7 +159,7 @@ typedef enum {
|
|||||||
GHOSTTY_KEY_EQUAL,
|
GHOSTTY_KEY_EQUAL,
|
||||||
GHOSTTY_KEY_LEFT_BRACKET, // [
|
GHOSTTY_KEY_LEFT_BRACKET, // [
|
||||||
GHOSTTY_KEY_RIGHT_BRACKET, // ]
|
GHOSTTY_KEY_RIGHT_BRACKET, // ]
|
||||||
GHOSTTY_KEY_BACKSLASH, // /
|
GHOSTTY_KEY_BACKSLASH, // \
|
||||||
|
|
||||||
// control
|
// control
|
||||||
GHOSTTY_KEY_UP,
|
GHOSTTY_KEY_UP,
|
||||||
@ -412,6 +412,7 @@ typedef enum {
|
|||||||
GHOSTTY_FULLSCREEN_NATIVE,
|
GHOSTTY_FULLSCREEN_NATIVE,
|
||||||
GHOSTTY_FULLSCREEN_NON_NATIVE,
|
GHOSTTY_FULLSCREEN_NON_NATIVE,
|
||||||
GHOSTTY_FULLSCREEN_NON_NATIVE_VISIBLE_MENU,
|
GHOSTTY_FULLSCREEN_NON_NATIVE_VISIBLE_MENU,
|
||||||
|
GHOSTTY_FULLSCREEN_NON_NATIVE_PADDED_NOTCH,
|
||||||
} ghostty_action_fullscreen_e;
|
} ghostty_action_fullscreen_e;
|
||||||
|
|
||||||
// apprt.action.SecureInput
|
// apprt.action.SecureInput
|
||||||
@ -562,8 +563,10 @@ typedef enum {
|
|||||||
GHOSTTY_ACTION_QUIT,
|
GHOSTTY_ACTION_QUIT,
|
||||||
GHOSTTY_ACTION_NEW_WINDOW,
|
GHOSTTY_ACTION_NEW_WINDOW,
|
||||||
GHOSTTY_ACTION_NEW_TAB,
|
GHOSTTY_ACTION_NEW_TAB,
|
||||||
|
GHOSTTY_ACTION_CLOSE_TAB,
|
||||||
GHOSTTY_ACTION_NEW_SPLIT,
|
GHOSTTY_ACTION_NEW_SPLIT,
|
||||||
GHOSTTY_ACTION_CLOSE_ALL_WINDOWS,
|
GHOSTTY_ACTION_CLOSE_ALL_WINDOWS,
|
||||||
|
GHOSTTY_ACTION_TOGGLE_MAXIMIZE,
|
||||||
GHOSTTY_ACTION_TOGGLE_FULLSCREEN,
|
GHOSTTY_ACTION_TOGGLE_FULLSCREEN,
|
||||||
GHOSTTY_ACTION_TOGGLE_TAB_OVERVIEW,
|
GHOSTTY_ACTION_TOGGLE_TAB_OVERVIEW,
|
||||||
GHOSTTY_ACTION_TOGGLE_WINDOW_DECORATIONS,
|
GHOSTTY_ACTION_TOGGLE_WINDOW_DECORATIONS,
|
||||||
@ -583,6 +586,7 @@ typedef enum {
|
|||||||
GHOSTTY_ACTION_RENDER_INSPECTOR,
|
GHOSTTY_ACTION_RENDER_INSPECTOR,
|
||||||
GHOSTTY_ACTION_DESKTOP_NOTIFICATION,
|
GHOSTTY_ACTION_DESKTOP_NOTIFICATION,
|
||||||
GHOSTTY_ACTION_SET_TITLE,
|
GHOSTTY_ACTION_SET_TITLE,
|
||||||
|
GHOSTTY_ACTION_PROMPT_TITLE,
|
||||||
GHOSTTY_ACTION_PWD,
|
GHOSTTY_ACTION_PWD,
|
||||||
GHOSTTY_ACTION_MOUSE_SHAPE,
|
GHOSTTY_ACTION_MOUSE_SHAPE,
|
||||||
GHOSTTY_ACTION_MOUSE_VISIBILITY,
|
GHOSTTY_ACTION_MOUSE_VISIBILITY,
|
||||||
@ -642,7 +646,7 @@ typedef void (*ghostty_runtime_write_clipboard_cb)(void*,
|
|||||||
ghostty_clipboard_e,
|
ghostty_clipboard_e,
|
||||||
bool);
|
bool);
|
||||||
typedef void (*ghostty_runtime_close_surface_cb)(void*, bool);
|
typedef void (*ghostty_runtime_close_surface_cb)(void*, bool);
|
||||||
typedef void (*ghostty_runtime_action_cb)(ghostty_app_t,
|
typedef bool (*ghostty_runtime_action_cb)(ghostty_app_t,
|
||||||
ghostty_target_s,
|
ghostty_target_s,
|
||||||
ghostty_action_s);
|
ghostty_action_s);
|
||||||
|
|
||||||
|
12
macos/Assets.xcassets/Alternate Icons/BlueprintImage.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "macOS-AppIcon-1024px.png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
macos/Assets.xcassets/Alternate Icons/BlueprintImage.imageset/macOS-AppIcon-1024px.png
vendored
Normal file
After Width: | Height: | Size: 434 KiB |
12
macos/Assets.xcassets/Alternate Icons/ChalkboardImage.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "macOS-AppIcon-1024px.png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
macos/Assets.xcassets/Alternate Icons/ChalkboardImage.imageset/macOS-AppIcon-1024px.png
vendored
Normal file
After Width: | Height: | Size: 576 KiB |
6
macos/Assets.xcassets/Alternate Icons/Contents.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
12
macos/Assets.xcassets/Alternate Icons/GlassImage.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "macOS-AppIcon-1024px.png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
macos/Assets.xcassets/Alternate Icons/GlassImage.imageset/macOS-AppIcon-1024px.png
vendored
Normal file
After Width: | Height: | Size: 515 KiB |
12
macos/Assets.xcassets/Alternate Icons/HolographicImage.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "macOS-AppIcon-1024px.png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
macos/Assets.xcassets/Alternate Icons/HolographicImage.imageset/macOS-AppIcon-1024px.png
vendored
Normal file
After Width: | Height: | Size: 588 KiB |
12
macos/Assets.xcassets/Alternate Icons/MicrochipImage.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "macOS-AppIcon-1024px.png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
macos/Assets.xcassets/Alternate Icons/MicrochipImage.imageset/macOS-AppIcon-1024px.png
vendored
Normal file
After Width: | Height: | Size: 630 KiB |
12
macos/Assets.xcassets/Alternate Icons/PaperImage.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "macOS-AppIcon-1024px.png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
macos/Assets.xcassets/Alternate Icons/PaperImage.imageset/macOS-AppIcon-1024px.png
vendored
Normal file
After Width: | Height: | Size: 335 KiB |
12
macos/Assets.xcassets/Alternate Icons/RetroImage.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "macOS-AppIcon-1024px.png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
macos/Assets.xcassets/Alternate Icons/RetroImage.imageset/macOS-AppIcon-1024px.png
vendored
Normal file
After Width: | Height: | Size: 1.0 MiB |
12
macos/Assets.xcassets/Alternate Icons/XrayImage.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "macOS-AppIcon-1024px.png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
macos/Assets.xcassets/Alternate Icons/XrayImage.imageset/macOS-AppIcon-1024px.png
vendored
Normal file
After Width: | Height: | Size: 443 KiB |
@ -69,9 +69,13 @@
|
|||||||
A59FB5CF2AE0DB50009128F3 /* InspectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59FB5CE2AE0DB50009128F3 /* InspectorView.swift */; };
|
A59FB5CF2AE0DB50009128F3 /* InspectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59FB5CE2AE0DB50009128F3 /* InspectorView.swift */; };
|
||||||
A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59FB5D02AE0DEA7009128F3 /* MetalView.swift */; };
|
A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59FB5D02AE0DEA7009128F3 /* MetalView.swift */; };
|
||||||
A5A1F8852A489D6800D1E8BC /* terminfo in Resources */ = {isa = PBXBuildFile; fileRef = A5A1F8842A489D6800D1E8BC /* terminfo */; };
|
A5A1F8852A489D6800D1E8BC /* terminfo in Resources */ = {isa = PBXBuildFile; fileRef = A5A1F8842A489D6800D1E8BC /* terminfo */; };
|
||||||
|
A5A2A3CA2D4445E30033CF96 /* Dock.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5A2A3C92D4445E20033CF96 /* Dock.swift */; };
|
||||||
|
A5A2A3CC2D444ABB0033CF96 /* NSApplication+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5A2A3CB2D444AB80033CF96 /* NSApplication+Extension.swift */; };
|
||||||
A5A6F72A2CC41B8900B232A5 /* Xcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5A6F7292CC41B8700B232A5 /* Xcode.swift */; };
|
A5A6F72A2CC41B8900B232A5 /* Xcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5A6F7292CC41B8700B232A5 /* Xcode.swift */; };
|
||||||
|
A5AEB1652D5BE7D000513529 /* LastWindowPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5AEB1642D5BE7BF00513529 /* LastWindowPosition.swift */; };
|
||||||
A5B30539299BEAAB0047F10C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A5B30538299BEAAB0047F10C /* Assets.xcassets */; };
|
A5B30539299BEAAB0047F10C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A5B30538299BEAAB0047F10C /* Assets.xcassets */; };
|
||||||
A5CA378C2D2A4DEB00931030 /* KeyboardLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CA378B2D2A4DE800931030 /* KeyboardLayout.swift */; };
|
A5CA378C2D2A4DEB00931030 /* KeyboardLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CA378B2D2A4DE800931030 /* KeyboardLayout.swift */; };
|
||||||
|
A5CA378E2D31D6C300931030 /* Weak.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CA378D2D31D6C100931030 /* Weak.swift */; };
|
||||||
A5CBD0562C9E65B80017A1AE /* DraggableWindowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CBD0552C9E65A50017A1AE /* DraggableWindowView.swift */; };
|
A5CBD0562C9E65B80017A1AE /* DraggableWindowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CBD0552C9E65A50017A1AE /* DraggableWindowView.swift */; };
|
||||||
A5CBD0582C9F30960017A1AE /* Cursor.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CBD0572C9F30860017A1AE /* Cursor.swift */; };
|
A5CBD0582C9F30960017A1AE /* Cursor.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CBD0572C9F30860017A1AE /* Cursor.swift */; };
|
||||||
A5CBD0592C9F37B10017A1AE /* Backport.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CEAFFE29C2410700646FDA /* Backport.swift */; };
|
A5CBD0592C9F37B10017A1AE /* Backport.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CEAFFE29C2410700646FDA /* Backport.swift */; };
|
||||||
@ -102,6 +106,7 @@
|
|||||||
C159E89D2B69A2EF00FDFE9C /* OSColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C159E81C2B66A06B00FDFE9C /* OSColor+Extension.swift */; };
|
C159E89D2B69A2EF00FDFE9C /* OSColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C159E81C2B66A06B00FDFE9C /* OSColor+Extension.swift */; };
|
||||||
C1F26EA72B738B9900404083 /* NSView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1F26EA62B738B9900404083 /* NSView+Extension.swift */; };
|
C1F26EA72B738B9900404083 /* NSView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1F26EA62B738B9900404083 /* NSView+Extension.swift */; };
|
||||||
C1F26EE92B76CBFC00404083 /* VibrantLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = C1F26EE82B76CBFC00404083 /* VibrantLayer.m */; };
|
C1F26EE92B76CBFC00404083 /* VibrantLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = C1F26EE82B76CBFC00404083 /* VibrantLayer.m */; };
|
||||||
|
CFBB5FEA2D231E5000FD62EE /* QuickTerminalSpaceBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFBB5FE92D231E5000FD62EE /* QuickTerminalSpaceBehavior.swift */; };
|
||||||
FC5218FA2D10FFCE004C93E0 /* zsh in Resources */ = {isa = PBXBuildFile; fileRef = FC5218F92D10FFC7004C93E0 /* zsh */; };
|
FC5218FA2D10FFCE004C93E0 /* zsh in Resources */ = {isa = PBXBuildFile; fileRef = FC5218F92D10FFC7004C93E0 /* zsh */; };
|
||||||
FC9ABA9C2D0F53F80020D4C8 /* bash-completion in Resources */ = {isa = PBXBuildFile; fileRef = FC9ABA9B2D0F538D0020D4C8 /* bash-completion */; };
|
FC9ABA9C2D0F53F80020D4C8 /* bash-completion in Resources */ = {isa = PBXBuildFile; fileRef = FC9ABA9B2D0F538D0020D4C8 /* bash-completion */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
@ -161,11 +166,15 @@
|
|||||||
A59FB5CE2AE0DB50009128F3 /* InspectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorView.swift; sourceTree = "<group>"; };
|
A59FB5CE2AE0DB50009128F3 /* InspectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorView.swift; sourceTree = "<group>"; };
|
||||||
A59FB5D02AE0DEA7009128F3 /* MetalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetalView.swift; sourceTree = "<group>"; };
|
A59FB5D02AE0DEA7009128F3 /* MetalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetalView.swift; sourceTree = "<group>"; };
|
||||||
A5A1F8842A489D6800D1E8BC /* terminfo */ = {isa = PBXFileReference; lastKnownFileType = folder; name = terminfo; path = "../zig-out/share/terminfo"; sourceTree = "<group>"; };
|
A5A1F8842A489D6800D1E8BC /* terminfo */ = {isa = PBXFileReference; lastKnownFileType = folder; name = terminfo; path = "../zig-out/share/terminfo"; sourceTree = "<group>"; };
|
||||||
|
A5A2A3C92D4445E20033CF96 /* Dock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dock.swift; sourceTree = "<group>"; };
|
||||||
|
A5A2A3CB2D444AB80033CF96 /* NSApplication+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSApplication+Extension.swift"; sourceTree = "<group>"; };
|
||||||
A5A6F7292CC41B8700B232A5 /* Xcode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Xcode.swift; sourceTree = "<group>"; };
|
A5A6F7292CC41B8700B232A5 /* Xcode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Xcode.swift; sourceTree = "<group>"; };
|
||||||
|
A5AEB1642D5BE7BF00513529 /* LastWindowPosition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LastWindowPosition.swift; sourceTree = "<group>"; };
|
||||||
A5B30531299BEAAA0047F10C /* Ghostty.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Ghostty.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
A5B30531299BEAAA0047F10C /* Ghostty.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Ghostty.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
A5B30538299BEAAB0047F10C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
A5B30538299BEAAB0047F10C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
A5B3053D299BEAAB0047F10C /* Ghostty.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Ghostty.entitlements; sourceTree = "<group>"; };
|
A5B3053D299BEAAB0047F10C /* Ghostty.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Ghostty.entitlements; sourceTree = "<group>"; };
|
||||||
A5CA378B2D2A4DE800931030 /* KeyboardLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardLayout.swift; sourceTree = "<group>"; };
|
A5CA378B2D2A4DE800931030 /* KeyboardLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardLayout.swift; sourceTree = "<group>"; };
|
||||||
|
A5CA378D2D31D6C100931030 /* Weak.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Weak.swift; sourceTree = "<group>"; };
|
||||||
A5CBD0552C9E65A50017A1AE /* DraggableWindowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraggableWindowView.swift; sourceTree = "<group>"; };
|
A5CBD0552C9E65A50017A1AE /* DraggableWindowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraggableWindowView.swift; sourceTree = "<group>"; };
|
||||||
A5CBD0572C9F30860017A1AE /* Cursor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cursor.swift; sourceTree = "<group>"; };
|
A5CBD0572C9F30860017A1AE /* Cursor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cursor.swift; sourceTree = "<group>"; };
|
||||||
A5CBD05B2CA0C5C70017A1AE /* QuickTerminal.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = QuickTerminal.xib; sourceTree = "<group>"; };
|
A5CBD05B2CA0C5C70017A1AE /* QuickTerminal.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = QuickTerminal.xib; sourceTree = "<group>"; };
|
||||||
@ -198,6 +207,7 @@
|
|||||||
C1F26EE72B76CBFC00404083 /* VibrantLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VibrantLayer.h; sourceTree = "<group>"; };
|
C1F26EE72B76CBFC00404083 /* VibrantLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VibrantLayer.h; sourceTree = "<group>"; };
|
||||||
C1F26EE82B76CBFC00404083 /* VibrantLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VibrantLayer.m; sourceTree = "<group>"; };
|
C1F26EE82B76CBFC00404083 /* VibrantLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VibrantLayer.m; sourceTree = "<group>"; };
|
||||||
C1F26EEA2B76CC2400404083 /* ghostty-bridging-header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ghostty-bridging-header.h"; sourceTree = "<group>"; };
|
C1F26EEA2B76CC2400404083 /* ghostty-bridging-header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ghostty-bridging-header.h"; sourceTree = "<group>"; };
|
||||||
|
CFBB5FE92D231E5000FD62EE /* QuickTerminalSpaceBehavior.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickTerminalSpaceBehavior.swift; sourceTree = "<group>"; };
|
||||||
FC5218F92D10FFC7004C93E0 /* zsh */ = {isa = PBXFileReference; lastKnownFileType = folder; name = zsh; path = "../zig-out/share/zsh"; sourceTree = "<group>"; };
|
FC5218F92D10FFC7004C93E0 /* zsh */ = {isa = PBXFileReference; lastKnownFileType = folder; name = zsh; path = "../zig-out/share/zsh"; sourceTree = "<group>"; };
|
||||||
FC9ABA9B2D0F538D0020D4C8 /* bash-completion */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "bash-completion"; path = "../zig-out/share/bash-completion"; sourceTree = "<group>"; };
|
FC9ABA9B2D0F538D0020D4C8 /* bash-completion */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "bash-completion"; path = "../zig-out/share/bash-completion"; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
@ -262,11 +272,13 @@
|
|||||||
A534263D2A7DCBB000EBB7A2 /* Helpers */ = {
|
A534263D2A7DCBB000EBB7A2 /* Helpers */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
A5AEB1642D5BE7BF00513529 /* LastWindowPosition.swift */,
|
||||||
A5A6F7292CC41B8700B232A5 /* Xcode.swift */,
|
A5A6F7292CC41B8700B232A5 /* Xcode.swift */,
|
||||||
A5CEAFFE29C2410700646FDA /* Backport.swift */,
|
A5CEAFFE29C2410700646FDA /* Backport.swift */,
|
||||||
A5333E1B2B5A1CE3008AEFF7 /* CrossKit.swift */,
|
A5333E1B2B5A1CE3008AEFF7 /* CrossKit.swift */,
|
||||||
A5CBD0572C9F30860017A1AE /* Cursor.swift */,
|
A5CBD0572C9F30860017A1AE /* Cursor.swift */,
|
||||||
A5D0AF3C2B37804400D21823 /* CodableBridge.swift */,
|
A5D0AF3C2B37804400D21823 /* CodableBridge.swift */,
|
||||||
|
A5A2A3C92D4445E20033CF96 /* Dock.swift */,
|
||||||
A52FFF582CAA4FF1000C6A5B /* Fullscreen.swift */,
|
A52FFF582CAA4FF1000C6A5B /* Fullscreen.swift */,
|
||||||
A59630962AEE163600D64628 /* HostingWindow.swift */,
|
A59630962AEE163600D64628 /* HostingWindow.swift */,
|
||||||
A5CA378B2D2A4DE800931030 /* KeyboardLayout.swift */,
|
A5CA378B2D2A4DE800931030 /* KeyboardLayout.swift */,
|
||||||
@ -274,12 +286,14 @@
|
|||||||
A5CBD0552C9E65A50017A1AE /* DraggableWindowView.swift */,
|
A5CBD0552C9E65A50017A1AE /* DraggableWindowView.swift */,
|
||||||
C159E81C2B66A06B00FDFE9C /* OSColor+Extension.swift */,
|
C159E81C2B66A06B00FDFE9C /* OSColor+Extension.swift */,
|
||||||
A599CDAF2CF103F20049FA26 /* NSAppearance+Extension.swift */,
|
A599CDAF2CF103F20049FA26 /* NSAppearance+Extension.swift */,
|
||||||
|
A5A2A3CB2D444AB80033CF96 /* NSApplication+Extension.swift */,
|
||||||
A54B0CEA2D0CFB4A00CBEFF8 /* NSImage+Extension.swift */,
|
A54B0CEA2D0CFB4A00CBEFF8 /* NSImage+Extension.swift */,
|
||||||
A52FFF5C2CAB4D05000C6A5B /* NSScreen+Extension.swift */,
|
A52FFF5C2CAB4D05000C6A5B /* NSScreen+Extension.swift */,
|
||||||
C1F26EA62B738B9900404083 /* NSView+Extension.swift */,
|
C1F26EA62B738B9900404083 /* NSView+Extension.swift */,
|
||||||
AEE8B3442B9AA39600260C5E /* NSPasteboard+Extension.swift */,
|
AEE8B3442B9AA39600260C5E /* NSPasteboard+Extension.swift */,
|
||||||
A5985CD62C320C4500C57AD3 /* String+Extension.swift */,
|
A5985CD62C320C4500C57AD3 /* String+Extension.swift */,
|
||||||
A5CC36142C9CDA03004D6760 /* View+Extension.swift */,
|
A5CC36142C9CDA03004D6760 /* View+Extension.swift */,
|
||||||
|
A5CA378D2D31D6C100931030 /* Weak.swift */,
|
||||||
C1F26EE72B76CBFC00404083 /* VibrantLayer.h */,
|
C1F26EE72B76CBFC00404083 /* VibrantLayer.h */,
|
||||||
C1F26EE82B76CBFC00404083 /* VibrantLayer.m */,
|
C1F26EE82B76CBFC00404083 /* VibrantLayer.m */,
|
||||||
A5CEAFDA29B8005900646FDA /* SplitView */,
|
A5CEAFDA29B8005900646FDA /* SplitView */,
|
||||||
@ -448,6 +462,7 @@
|
|||||||
children = (
|
children = (
|
||||||
A5CBD05B2CA0C5C70017A1AE /* QuickTerminal.xib */,
|
A5CBD05B2CA0C5C70017A1AE /* QuickTerminal.xib */,
|
||||||
A5CBD05D2CA0C5E70017A1AE /* QuickTerminalController.swift */,
|
A5CBD05D2CA0C5E70017A1AE /* QuickTerminalController.swift */,
|
||||||
|
CFBB5FE92D231E5000FD62EE /* QuickTerminalSpaceBehavior.swift */,
|
||||||
A5CBD0632CA122E70017A1AE /* QuickTerminalPosition.swift */,
|
A5CBD0632CA122E70017A1AE /* QuickTerminalPosition.swift */,
|
||||||
A52FFF562CA90481000C6A5B /* QuickTerminalScreen.swift */,
|
A52FFF562CA90481000C6A5B /* QuickTerminalScreen.swift */,
|
||||||
A5CBD05F2CA0C9080017A1AE /* QuickTerminalWindow.swift */,
|
A5CBD05F2CA0C9080017A1AE /* QuickTerminalWindow.swift */,
|
||||||
@ -611,11 +626,13 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
A5AEB1652D5BE7D000513529 /* LastWindowPosition.swift in Sources */,
|
||||||
A59630A42AF059BB00D64628 /* Ghostty.SplitNode.swift in Sources */,
|
A59630A42AF059BB00D64628 /* Ghostty.SplitNode.swift in Sources */,
|
||||||
A514C8D62B54A16400493A16 /* Ghostty.Config.swift in Sources */,
|
A514C8D62B54A16400493A16 /* Ghostty.Config.swift in Sources */,
|
||||||
A54B0CEB2D0CFB4C00CBEFF8 /* NSImage+Extension.swift in Sources */,
|
A54B0CEB2D0CFB4C00CBEFF8 /* NSImage+Extension.swift in Sources */,
|
||||||
A54D786C2CA7978E001B19B1 /* BaseTerminalController.swift in Sources */,
|
A54D786C2CA7978E001B19B1 /* BaseTerminalController.swift in Sources */,
|
||||||
A59FB5CF2AE0DB50009128F3 /* InspectorView.swift in Sources */,
|
A59FB5CF2AE0DB50009128F3 /* InspectorView.swift in Sources */,
|
||||||
|
CFBB5FEA2D231E5000FD62EE /* QuickTerminalSpaceBehavior.swift in Sources */,
|
||||||
A54B0CE92D0CECD100CBEFF8 /* ColorizedGhosttyIconView.swift in Sources */,
|
A54B0CE92D0CECD100CBEFF8 /* ColorizedGhosttyIconView.swift in Sources */,
|
||||||
A5D0AF3D2B37804400D21823 /* CodableBridge.swift in Sources */,
|
A5D0AF3D2B37804400D21823 /* CodableBridge.swift in Sources */,
|
||||||
A5D0AF3B2B36A1DE00D21823 /* TerminalRestorable.swift in Sources */,
|
A5D0AF3B2B36A1DE00D21823 /* TerminalRestorable.swift in Sources */,
|
||||||
@ -628,6 +645,7 @@
|
|||||||
A5CBD0602CA0C90A0017A1AE /* QuickTerminalWindow.swift in Sources */,
|
A5CBD0602CA0C90A0017A1AE /* QuickTerminalWindow.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 */,
|
||||||
|
A5A2A3CA2D4445E30033CF96 /* Dock.swift in Sources */,
|
||||||
A51BFC222B2FB6B400E92F16 /* AboutView.swift in Sources */,
|
A51BFC222B2FB6B400E92F16 /* AboutView.swift in Sources */,
|
||||||
A5278A9B2AA05B2600CD3039 /* Ghostty.Input.swift in Sources */,
|
A5278A9B2AA05B2600CD3039 /* Ghostty.Input.swift in Sources */,
|
||||||
A5CBD0562C9E65B80017A1AE /* DraggableWindowView.swift in Sources */,
|
A5CBD0562C9E65B80017A1AE /* DraggableWindowView.swift in Sources */,
|
||||||
@ -643,12 +661,14 @@
|
|||||||
A5A6F72A2CC41B8900B232A5 /* Xcode.swift in Sources */,
|
A5A6F72A2CC41B8900B232A5 /* Xcode.swift in Sources */,
|
||||||
A52FFF5B2CAA54B1000C6A5B /* FullscreenMode+Extension.swift in Sources */,
|
A52FFF5B2CAA54B1000C6A5B /* FullscreenMode+Extension.swift in Sources */,
|
||||||
A5333E222B5A2128008AEFF7 /* SurfaceView_AppKit.swift in Sources */,
|
A5333E222B5A2128008AEFF7 /* SurfaceView_AppKit.swift in Sources */,
|
||||||
|
A5CA378E2D31D6C300931030 /* Weak.swift in Sources */,
|
||||||
A5CDF1952AAFA19600513312 /* ConfigurationErrorsView.swift in Sources */,
|
A5CDF1952AAFA19600513312 /* ConfigurationErrorsView.swift in Sources */,
|
||||||
A55B7BBC29B6FC330055DE60 /* SurfaceView.swift in Sources */,
|
A55B7BBC29B6FC330055DE60 /* SurfaceView.swift in Sources */,
|
||||||
A5333E1C2B5A1CE3008AEFF7 /* CrossKit.swift in Sources */,
|
A5333E1C2B5A1CE3008AEFF7 /* CrossKit.swift in Sources */,
|
||||||
A59444F729A2ED5200725BBA /* SettingsView.swift in Sources */,
|
A59444F729A2ED5200725BBA /* SettingsView.swift in Sources */,
|
||||||
A56D58862ACDDB4100508D2C /* Ghostty.Shell.swift in Sources */,
|
A56D58862ACDDB4100508D2C /* Ghostty.Shell.swift in Sources */,
|
||||||
A5985CD72C320C4500C57AD3 /* String+Extension.swift in Sources */,
|
A5985CD72C320C4500C57AD3 /* String+Extension.swift in Sources */,
|
||||||
|
A5A2A3CC2D444ABB0033CF96 /* NSApplication+Extension.swift in Sources */,
|
||||||
A59630A22AF0415000D64628 /* Ghostty.TerminalSplit.swift in Sources */,
|
A59630A22AF0415000D64628 /* Ghostty.TerminalSplit.swift in Sources */,
|
||||||
A5FEB3002ABB69450068369E /* main.swift in Sources */,
|
A5FEB3002ABB69450068369E /* main.swift in Sources */,
|
||||||
A55B7BB829B6F53A0055DE60 /* Package.swift in Sources */,
|
A55B7BB829B6F53A0055DE60 /* Package.swift in Sources */,
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/sparkle-project/Sparkle",
|
"location" : "https://github.com/sparkle-project/Sparkle",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "b456fd404954a9e13f55aa0c88cd5a40b8399638",
|
"revision" : "0ef1ee0220239b3776f433314515fd849025673f",
|
||||||
"version" : "2.6.3"
|
"version" : "2.6.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -30,15 +30,18 @@ class AppDelegate: NSObject,
|
|||||||
@IBOutlet private var menuSplitRight: NSMenuItem?
|
@IBOutlet private var menuSplitRight: NSMenuItem?
|
||||||
@IBOutlet private var menuSplitDown: NSMenuItem?
|
@IBOutlet private var menuSplitDown: NSMenuItem?
|
||||||
@IBOutlet private var menuClose: NSMenuItem?
|
@IBOutlet private var menuClose: NSMenuItem?
|
||||||
|
@IBOutlet private var menuCloseTab: NSMenuItem?
|
||||||
@IBOutlet private var menuCloseWindow: NSMenuItem?
|
@IBOutlet private var menuCloseWindow: NSMenuItem?
|
||||||
@IBOutlet private var menuCloseAllWindows: NSMenuItem?
|
@IBOutlet private var menuCloseAllWindows: NSMenuItem?
|
||||||
|
|
||||||
@IBOutlet private var menuCopy: NSMenuItem?
|
@IBOutlet private var menuCopy: NSMenuItem?
|
||||||
@IBOutlet private var menuPaste: NSMenuItem?
|
@IBOutlet private var menuPaste: NSMenuItem?
|
||||||
|
@IBOutlet private var menuPasteSelection: NSMenuItem?
|
||||||
@IBOutlet private var menuSelectAll: NSMenuItem?
|
@IBOutlet private var menuSelectAll: NSMenuItem?
|
||||||
|
|
||||||
@IBOutlet private var menuToggleVisibility: NSMenuItem?
|
@IBOutlet private var menuToggleVisibility: NSMenuItem?
|
||||||
@IBOutlet private var menuToggleFullScreen: NSMenuItem?
|
@IBOutlet private var menuToggleFullScreen: NSMenuItem?
|
||||||
|
@IBOutlet private var menuBringAllToFront: NSMenuItem?
|
||||||
@IBOutlet private var menuZoomSplit: NSMenuItem?
|
@IBOutlet private var menuZoomSplit: NSMenuItem?
|
||||||
@IBOutlet private var menuPreviousSplit: NSMenuItem?
|
@IBOutlet private var menuPreviousSplit: NSMenuItem?
|
||||||
@IBOutlet private var menuNextSplit: NSMenuItem?
|
@IBOutlet private var menuNextSplit: NSMenuItem?
|
||||||
@ -50,6 +53,7 @@ class AppDelegate: NSObject,
|
|||||||
@IBOutlet private var menuIncreaseFontSize: NSMenuItem?
|
@IBOutlet private var menuIncreaseFontSize: NSMenuItem?
|
||||||
@IBOutlet private var menuDecreaseFontSize: NSMenuItem?
|
@IBOutlet private var menuDecreaseFontSize: NSMenuItem?
|
||||||
@IBOutlet private var menuResetFontSize: NSMenuItem?
|
@IBOutlet private var menuResetFontSize: NSMenuItem?
|
||||||
|
@IBOutlet private var menuChangeTitle: NSMenuItem?
|
||||||
@IBOutlet private var menuQuickTerminal: NSMenuItem?
|
@IBOutlet private var menuQuickTerminal: NSMenuItem?
|
||||||
@IBOutlet private var menuTerminalInspector: NSMenuItem?
|
@IBOutlet private var menuTerminalInspector: NSMenuItem?
|
||||||
|
|
||||||
@ -90,10 +94,8 @@ class AppDelegate: NSObject,
|
|||||||
return ProcessInfo.processInfo.systemUptime - applicationLaunchTime
|
return ProcessInfo.processInfo.systemUptime - applicationLaunchTime
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tracks whether the application is currently visible. This can be gamed, i.e. if a user manually
|
/// Tracks the windows that we hid for toggleVisibility.
|
||||||
/// brings each window one by one to the front. But at worst its off by one set of toggles and this
|
private var hiddenState: ToggleVisibilityState? = nil
|
||||||
/// makes our logic very easy.
|
|
||||||
private var isVisible: Bool = true
|
|
||||||
|
|
||||||
/// The observer for the app appearance.
|
/// The observer for the app appearance.
|
||||||
private var appearanceObserver: NSKeyValueObservation? = nil
|
private var appearanceObserver: NSKeyValueObservation? = nil
|
||||||
@ -217,15 +219,20 @@ class AppDelegate: NSObject,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func applicationDidBecomeActive(_ notification: Notification) {
|
func applicationDidBecomeActive(_ notification: Notification) {
|
||||||
guard !applicationHasBecomeActive else { return }
|
// If we're back manually then clear the hidden state because macOS handles it.
|
||||||
applicationHasBecomeActive = true
|
self.hiddenState = nil
|
||||||
|
|
||||||
// Let's launch our first window. We only do this if we have no other windows. It
|
// First launch stuff
|
||||||
// is possible to have other windows in a few scenarios:
|
if (!applicationHasBecomeActive) {
|
||||||
// - if we're opening a URL since `application(_:openFile:)` is called before this.
|
applicationHasBecomeActive = true
|
||||||
// - if we're restoring from persisted state
|
|
||||||
if terminalManager.windows.count == 0 && derivedConfig.initialWindow {
|
// Let's launch our first window. We only do this if we have no other windows. It
|
||||||
terminalManager.newWindow()
|
// is possible to have other windows in a few scenarios:
|
||||||
|
// - if we're opening a URL since `application(_:openFile:)` is called before this.
|
||||||
|
// - if we're restoring from persisted state
|
||||||
|
if terminalManager.windows.count == 0 && derivedConfig.initialWindow {
|
||||||
|
terminalManager.newWindow()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,7 +247,13 @@ class AppDelegate: NSObject,
|
|||||||
// This probably isn't fully safe. The isEmpty check above is aspirational, it doesn't
|
// This probably isn't fully safe. The isEmpty check above is aspirational, it doesn't
|
||||||
// quite work with SwiftUI because windows are retained on close. So instead we check
|
// quite work with SwiftUI because windows are retained on close. So instead we check
|
||||||
// if there are any that are visible. I'm guessing this breaks under certain scenarios.
|
// if there are any that are visible. I'm guessing this breaks under certain scenarios.
|
||||||
if (windows.allSatisfy { !$0.isVisible }) { return .terminateNow }
|
//
|
||||||
|
// NOTE(mitchellh): I don't think we need this check at all anymore. I'm keeping it
|
||||||
|
// here because I don't want to remove it in a patch release cycle but we should
|
||||||
|
// target removing it soon.
|
||||||
|
if (self.quickController == nil && windows.allSatisfy { !$0.isVisible }) {
|
||||||
|
return .terminateNow
|
||||||
|
}
|
||||||
|
|
||||||
// If the user is shutting down, restarting, or logging out, we don't confirm quit.
|
// If the user is shutting down, restarting, or logging out, we don't confirm quit.
|
||||||
why: if let event = NSAppleEventManager.shared().currentAppleEvent {
|
why: if let event = NSAppleEventManager.shared().currentAppleEvent {
|
||||||
@ -346,6 +359,7 @@ class AppDelegate: NSObject,
|
|||||||
syncMenuShortcut(config, action: "new_window", menuItem: self.menuNewWindow)
|
syncMenuShortcut(config, action: "new_window", menuItem: self.menuNewWindow)
|
||||||
syncMenuShortcut(config, action: "new_tab", menuItem: self.menuNewTab)
|
syncMenuShortcut(config, action: "new_tab", menuItem: self.menuNewTab)
|
||||||
syncMenuShortcut(config, action: "close_surface", menuItem: self.menuClose)
|
syncMenuShortcut(config, action: "close_surface", menuItem: self.menuClose)
|
||||||
|
syncMenuShortcut(config, action: "close_tab", menuItem: self.menuCloseTab)
|
||||||
syncMenuShortcut(config, action: "close_window", menuItem: self.menuCloseWindow)
|
syncMenuShortcut(config, action: "close_window", menuItem: self.menuCloseWindow)
|
||||||
syncMenuShortcut(config, action: "close_all_windows", menuItem: self.menuCloseAllWindows)
|
syncMenuShortcut(config, action: "close_all_windows", menuItem: self.menuCloseAllWindows)
|
||||||
syncMenuShortcut(config, action: "new_split:right", menuItem: self.menuSplitRight)
|
syncMenuShortcut(config, action: "new_split:right", menuItem: self.menuSplitRight)
|
||||||
@ -353,6 +367,7 @@ class AppDelegate: NSObject,
|
|||||||
|
|
||||||
syncMenuShortcut(config, action: "copy_to_clipboard", menuItem: self.menuCopy)
|
syncMenuShortcut(config, action: "copy_to_clipboard", menuItem: self.menuCopy)
|
||||||
syncMenuShortcut(config, action: "paste_from_clipboard", menuItem: self.menuPaste)
|
syncMenuShortcut(config, action: "paste_from_clipboard", menuItem: self.menuPaste)
|
||||||
|
syncMenuShortcut(config, action: "paste_from_selection", menuItem: self.menuPasteSelection)
|
||||||
syncMenuShortcut(config, action: "select_all", menuItem: self.menuSelectAll)
|
syncMenuShortcut(config, action: "select_all", menuItem: self.menuSelectAll)
|
||||||
|
|
||||||
syncMenuShortcut(config, action: "toggle_split_zoom", menuItem: self.menuZoomSplit)
|
syncMenuShortcut(config, action: "toggle_split_zoom", menuItem: self.menuZoomSplit)
|
||||||
@ -371,6 +386,7 @@ class AppDelegate: NSObject,
|
|||||||
syncMenuShortcut(config, action: "increase_font_size:1", menuItem: self.menuIncreaseFontSize)
|
syncMenuShortcut(config, action: "increase_font_size:1", menuItem: self.menuIncreaseFontSize)
|
||||||
syncMenuShortcut(config, action: "decrease_font_size:1", menuItem: self.menuDecreaseFontSize)
|
syncMenuShortcut(config, action: "decrease_font_size:1", menuItem: self.menuDecreaseFontSize)
|
||||||
syncMenuShortcut(config, action: "reset_font_size", menuItem: self.menuResetFontSize)
|
syncMenuShortcut(config, action: "reset_font_size", menuItem: self.menuResetFontSize)
|
||||||
|
syncMenuShortcut(config, action: "change_title_prompt", menuItem: self.menuChangeTitle)
|
||||||
syncMenuShortcut(config, action: "toggle_quick_terminal", menuItem: self.menuQuickTerminal)
|
syncMenuShortcut(config, action: "toggle_quick_terminal", menuItem: self.menuQuickTerminal)
|
||||||
syncMenuShortcut(config, action: "toggle_visibility", menuItem: self.menuToggleVisibility)
|
syncMenuShortcut(config, action: "toggle_visibility", menuItem: self.menuToggleVisibility)
|
||||||
syncMenuShortcut(config, action: "inspector:toggle", menuItem: self.menuTerminalInspector)
|
syncMenuShortcut(config, action: "inspector:toggle", menuItem: self.menuTerminalInspector)
|
||||||
@ -424,7 +440,7 @@ class AppDelegate: NSObject,
|
|||||||
// If we have a main window then we don't process any of the keys
|
// If we have a main window then we don't process any of the keys
|
||||||
// because we let it capture and propagate.
|
// because we let it capture and propagate.
|
||||||
guard NSApp.mainWindow == nil else { return event }
|
guard NSApp.mainWindow == nil else { return event }
|
||||||
|
|
||||||
// If this event as-is would result in a key binding then we send it.
|
// If this event as-is would result in a key binding then we send it.
|
||||||
if let app = ghostty.app,
|
if let app = ghostty.app,
|
||||||
ghostty_app_key_is_binding(
|
ghostty_app_key_is_binding(
|
||||||
@ -440,26 +456,26 @@ class AppDelegate: NSObject,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this event would be handled by our menu then we do nothing.
|
// If this event would be handled by our menu then we do nothing.
|
||||||
if let mainMenu = NSApp.mainMenu,
|
if let mainMenu = NSApp.mainMenu,
|
||||||
mainMenu.performKeyEquivalent(with: event) {
|
mainMenu.performKeyEquivalent(with: event) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we reach this point then we try to process the key event
|
// If we reach this point then we try to process the key event
|
||||||
// through the Ghostty key mechanism.
|
// through the Ghostty key mechanism.
|
||||||
|
|
||||||
// Ghostty must be loaded
|
// Ghostty must be loaded
|
||||||
guard let ghostty = self.ghostty.app else { return event }
|
guard let ghostty = self.ghostty.app else { return event }
|
||||||
|
|
||||||
// Build our event input and call ghostty
|
// Build our event input and call ghostty
|
||||||
if (ghostty_app_key(ghostty, event.ghosttyKeyEvent(GHOSTTY_ACTION_PRESS))) {
|
if (ghostty_app_key(ghostty, event.ghosttyKeyEvent(GHOSTTY_ACTION_PRESS))) {
|
||||||
// The key was used so we want to stop it from going to our Mac app
|
// The key was used so we want to stop it from going to our Mac app
|
||||||
Ghostty.logger.debug("local key event handled event=\(event)")
|
Ghostty.logger.debug("local key event handled event=\(event)")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return event
|
return event
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -517,6 +533,15 @@ class AppDelegate: NSObject,
|
|||||||
// AppKit mutex on the appearance.
|
// AppKit mutex on the appearance.
|
||||||
DispatchQueue.main.async { self.syncAppearance(config: config) }
|
DispatchQueue.main.async { self.syncAppearance(config: config) }
|
||||||
|
|
||||||
|
// Decide whether to hide/unhide app from dock and app switcher
|
||||||
|
switch (config.macosHidden) {
|
||||||
|
case .never:
|
||||||
|
NSApp.setActivationPolicy(.regular)
|
||||||
|
|
||||||
|
case .always:
|
||||||
|
NSApp.setActivationPolicy(.accessory)
|
||||||
|
}
|
||||||
|
|
||||||
// If we have configuration errors, we need to show them.
|
// If we have configuration errors, we need to show them.
|
||||||
let c = ConfigurationErrorsController.sharedInstance
|
let c = ConfigurationErrorsController.sharedInstance
|
||||||
c.errors = config.errors
|
c.errors = config.errors
|
||||||
@ -550,6 +575,30 @@ class AppDelegate: NSObject,
|
|||||||
self.appIcon = nil
|
self.appIcon = nil
|
||||||
break
|
break
|
||||||
|
|
||||||
|
case .blueprint:
|
||||||
|
self.appIcon = NSImage(named: "BlueprintImage")!
|
||||||
|
|
||||||
|
case .chalkboard:
|
||||||
|
self.appIcon = NSImage(named: "ChalkboardImage")!
|
||||||
|
|
||||||
|
case .glass:
|
||||||
|
self.appIcon = NSImage(named: "GlassImage")!
|
||||||
|
|
||||||
|
case .holographic:
|
||||||
|
self.appIcon = NSImage(named: "HolographicImage")!
|
||||||
|
|
||||||
|
case .microchip:
|
||||||
|
self.appIcon = NSImage(named: "MicrochipImage")!
|
||||||
|
|
||||||
|
case .paper:
|
||||||
|
self.appIcon = NSImage(named: "PaperImage")!
|
||||||
|
|
||||||
|
case .retro:
|
||||||
|
self.appIcon = NSImage(named: "RetroImage")!
|
||||||
|
|
||||||
|
case .xray:
|
||||||
|
self.appIcon = NSImage(named: "XrayImage")!
|
||||||
|
|
||||||
case .customStyle:
|
case .customStyle:
|
||||||
guard let ghostColor = config.macosIconGhostColor else { break }
|
guard let ghostColor = config.macosIconGhostColor else { break }
|
||||||
guard let screenColors = config.macosIconScreenColor else { break }
|
guard let screenColors = config.macosIconScreenColor else { break }
|
||||||
@ -702,21 +751,35 @@ class AppDelegate: NSObject,
|
|||||||
|
|
||||||
/// Toggles visibility of all Ghosty Terminal windows. When hidden, activates Ghostty as the frontmost application
|
/// Toggles visibility of all Ghosty Terminal windows. When hidden, activates Ghostty as the frontmost application
|
||||||
@IBAction func toggleVisibility(_ sender: Any) {
|
@IBAction func toggleVisibility(_ sender: Any) {
|
||||||
// We only care about terminal windows.
|
// If we have focus, then we hide all windows.
|
||||||
for window in NSApp.windows.filter({ $0.windowController is BaseTerminalController }) {
|
if NSApp.isActive {
|
||||||
if isVisible {
|
// Toggle visibility doesn't do anything if the focused window is native
|
||||||
window.orderOut(nil)
|
// fullscreen. This is only relevant if Ghostty is active.
|
||||||
} else {
|
guard let keyWindow = NSApp.keyWindow,
|
||||||
window.makeKeyAndOrderFront(nil)
|
!keyWindow.styleMask.contains(.fullScreen) else { return }
|
||||||
}
|
|
||||||
|
// Keep track of our hidden state to restore properly
|
||||||
|
self.hiddenState = .init()
|
||||||
|
NSApp.hide(nil)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// After bringing them all to front we make sure our app is active too.
|
// If we're not active, we want to become active
|
||||||
if !isVisible {
|
NSApp.activate(ignoringOtherApps: true)
|
||||||
|
|
||||||
|
// Bring all windows to the front. Note: we don't use NSApp.unhide because
|
||||||
|
// that will unhide ALL hidden windows. We want to only bring forward the
|
||||||
|
// ones that we hid.
|
||||||
|
hiddenState?.restore()
|
||||||
|
hiddenState = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func bringAllToFront(_ sender: Any) {
|
||||||
|
if !NSApp.isActive {
|
||||||
NSApp.activate(ignoringOtherApps: true)
|
NSApp.activate(ignoringOtherApps: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
isVisible.toggle()
|
NSApplication.shared.arrangeInFront(sender)
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct DerivedConfig {
|
private struct DerivedConfig {
|
||||||
@ -736,4 +799,33 @@ class AppDelegate: NSObject,
|
|||||||
self.quickTerminalPosition = config.quickTerminalPosition
|
self.quickTerminalPosition = config.quickTerminalPosition
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct ToggleVisibilityState {
|
||||||
|
let hiddenWindows: [Weak<NSWindow>]
|
||||||
|
let keyWindow: Weak<NSWindow>?
|
||||||
|
|
||||||
|
init() {
|
||||||
|
// We need to know the key window so that we can bring focus back to the
|
||||||
|
// right window if it was hidden.
|
||||||
|
self.keyWindow = if let keyWindow = NSApp.keyWindow {
|
||||||
|
.init(keyWindow)
|
||||||
|
} else {
|
||||||
|
nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to keep track of the windows that were visible because we only
|
||||||
|
// want to bring back these windows if we remove the toggle.
|
||||||
|
//
|
||||||
|
// We also ignore fullscreen windows because they don't hide anyways.
|
||||||
|
self.hiddenWindows = NSApp.windows.filter {
|
||||||
|
$0.isVisible &&
|
||||||
|
!$0.styleMask.contains(.fullScreen)
|
||||||
|
}.map { Weak($0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
func restore() {
|
||||||
|
hiddenWindows.forEach { $0.value?.orderFrontRegardless() }
|
||||||
|
keyWindow?.value?.makeKey()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="23094" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="23504" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="macosx"/>
|
<deployment identifier="macosx"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23094"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23504"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<objects>
|
<objects>
|
||||||
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
||||||
@ -14,9 +14,12 @@
|
|||||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||||
<customObject id="bbz-4X-AYv" userLabel="AppDelegate" customClass="AppDelegate" customModule="Ghostty" customModuleProvider="target">
|
<customObject id="bbz-4X-AYv" userLabel="AppDelegate" customClass="AppDelegate" customModule="Ghostty" customModuleProvider="target">
|
||||||
<connections>
|
<connections>
|
||||||
|
<outlet property="menuBringAllToFront" destination="LE2-aR-0XJ" id="AP9-oK-60V"/>
|
||||||
|
<outlet property="menuChangeTitle" destination="24I-xg-qIq" id="kg6-kT-jNL"/>
|
||||||
<outlet property="menuCheckForUpdates" destination="GEA-5y-yzH" id="0nV-Tf-nJQ"/>
|
<outlet property="menuCheckForUpdates" destination="GEA-5y-yzH" id="0nV-Tf-nJQ"/>
|
||||||
<outlet property="menuClose" destination="DVo-aG-piG" id="R3t-0C-aSU"/>
|
<outlet property="menuClose" destination="DVo-aG-piG" id="R3t-0C-aSU"/>
|
||||||
<outlet property="menuCloseAllWindows" destination="yKr-Vi-Yqw" id="Zet-Ir-zbm"/>
|
<outlet property="menuCloseAllWindows" destination="yKr-Vi-Yqw" id="Zet-Ir-zbm"/>
|
||||||
|
<outlet property="menuCloseTab" destination="Obb-Mk-j8J" id="Gda-L0-gdz"/>
|
||||||
<outlet property="menuCloseWindow" destination="W5w-UZ-crk" id="6ff-BT-ENV"/>
|
<outlet property="menuCloseWindow" destination="W5w-UZ-crk" id="6ff-BT-ENV"/>
|
||||||
<outlet property="menuCopy" destination="Jqf-pv-Zcu" id="bKd-1C-oy9"/>
|
<outlet property="menuCopy" destination="Jqf-pv-Zcu" id="bKd-1C-oy9"/>
|
||||||
<outlet property="menuDecreaseFontSize" destination="kzb-SZ-dOA" id="Y1B-Vh-6Z2"/>
|
<outlet property="menuDecreaseFontSize" destination="kzb-SZ-dOA" id="Y1B-Vh-6Z2"/>
|
||||||
@ -31,6 +34,7 @@
|
|||||||
<outlet property="menuNextSplit" destination="bD7-ei-wKU" id="LeT-xw-eh4"/>
|
<outlet property="menuNextSplit" destination="bD7-ei-wKU" id="LeT-xw-eh4"/>
|
||||||
<outlet property="menuOpenConfig" destination="BOF-NM-1cW" id="Nze-Go-glw"/>
|
<outlet property="menuOpenConfig" destination="BOF-NM-1cW" id="Nze-Go-glw"/>
|
||||||
<outlet property="menuPaste" destination="i27-pK-umN" id="ICc-X2-gV3"/>
|
<outlet property="menuPaste" destination="i27-pK-umN" id="ICc-X2-gV3"/>
|
||||||
|
<outlet property="menuPasteSelection" destination="akq-ov-Jjh" id="GS8-aQ-hVw"/>
|
||||||
<outlet property="menuPreviousSplit" destination="Lic-px-1wg" id="Rto-CG-yRe"/>
|
<outlet property="menuPreviousSplit" destination="Lic-px-1wg" id="Rto-CG-yRe"/>
|
||||||
<outlet property="menuQuickTerminal" destination="1pv-LF-NBJ" id="glN-5B-IGi"/>
|
<outlet property="menuQuickTerminal" destination="1pv-LF-NBJ" id="glN-5B-IGi"/>
|
||||||
<outlet property="menuQuit" destination="4sb-4s-VLi" id="qYN-S1-6UW"/>
|
<outlet property="menuQuit" destination="4sb-4s-VLi" id="qYN-S1-6UW"/>
|
||||||
@ -154,6 +158,12 @@
|
|||||||
<action selector="close:" target="-1" id="tTZ-2b-Mbm"/>
|
<action selector="close:" target="-1" id="tTZ-2b-Mbm"/>
|
||||||
</connections>
|
</connections>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
|
<menuItem title="Close Tab" id="Obb-Mk-j8J">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="closeTab:" target="-1" id="UBb-Bd-nkj"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
<menuItem title="Close Window" id="W5w-UZ-crk">
|
<menuItem title="Close Window" id="W5w-UZ-crk">
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
<connections>
|
<connections>
|
||||||
@ -185,6 +195,12 @@
|
|||||||
<action selector="paste:" target="-1" id="ZKe-2B-mel"/>
|
<action selector="paste:" target="-1" id="ZKe-2B-mel"/>
|
||||||
</connections>
|
</connections>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
|
<menuItem title="Paste Selection" id="akq-ov-Jjh">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="pasteSelection:" target="-1" id="vo3-Rf-Udb"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
<menuItem title="Select All" id="q2h-lq-e4r">
|
<menuItem title="Select All" id="q2h-lq-e4r">
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
<connections>
|
<connections>
|
||||||
@ -218,6 +234,13 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
<menuItem isSeparatorItem="YES" id="L3L-I8-sqk"/>
|
<menuItem isSeparatorItem="YES" id="L3L-I8-sqk"/>
|
||||||
|
<menuItem title="Change Title..." id="24I-xg-qIq">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="changeTitle:" target="-1" id="XuL-QB-Q9l"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem isSeparatorItem="YES" id="Vkj-tP-dMZ"/>
|
||||||
<menuItem title="Quick Terminal" id="1pv-LF-NBJ">
|
<menuItem title="Quick Terminal" id="1pv-LF-NBJ">
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
<connections>
|
<connections>
|
||||||
@ -256,12 +279,6 @@
|
|||||||
<action selector="toggleGhosttyFullScreen:" target="-1" id="QB9-7R-xyc"/>
|
<action selector="toggleGhosttyFullScreen:" target="-1" id="QB9-7R-xyc"/>
|
||||||
</connections>
|
</connections>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
|
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
|
||||||
<connections>
|
|
||||||
<action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
|
|
||||||
</connections>
|
|
||||||
</menuItem>
|
|
||||||
<menuItem title="Show/Hide All Terminals" id="DOX-wA-ilh">
|
<menuItem title="Show/Hide All Terminals" id="DOX-wA-ilh">
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
<connections>
|
<connections>
|
||||||
@ -356,6 +373,13 @@
|
|||||||
</items>
|
</items>
|
||||||
</menu>
|
</menu>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
|
<menuItem isSeparatorItem="YES" id="dgt-Tx-d4e"/>
|
||||||
|
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
</items>
|
</items>
|
||||||
</menu>
|
</menu>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
|
@ -3,6 +3,12 @@ import Cocoa
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import GhosttyKit
|
import GhosttyKit
|
||||||
|
|
||||||
|
// This is a Apple's private function that we need to call to get the active space.
|
||||||
|
@_silgen_name("CGSGetActiveSpace")
|
||||||
|
func CGSGetActiveSpace(_ cid: Int) -> size_t
|
||||||
|
@_silgen_name("CGSMainConnectionID")
|
||||||
|
func CGSMainConnectionID() -> Int
|
||||||
|
|
||||||
/// Controller for the "quick" terminal.
|
/// Controller for the "quick" terminal.
|
||||||
class QuickTerminalController: BaseTerminalController {
|
class QuickTerminalController: BaseTerminalController {
|
||||||
override var windowNibName: NSNib.Name? { "QuickTerminal" }
|
override var windowNibName: NSNib.Name? { "QuickTerminal" }
|
||||||
@ -18,6 +24,12 @@ class QuickTerminalController: BaseTerminalController {
|
|||||||
/// application to the front.
|
/// application to the front.
|
||||||
private var previousApp: NSRunningApplication? = nil
|
private var previousApp: NSRunningApplication? = nil
|
||||||
|
|
||||||
|
// The active space when the quick terminal was last shown.
|
||||||
|
private var previousActiveSpace: size_t = 0
|
||||||
|
|
||||||
|
/// Non-nil if we have hidden dock state.
|
||||||
|
private var hiddenDock: HiddenDock? = nil
|
||||||
|
|
||||||
/// The configuration derived from the Ghostty config so we don't need to rely on references.
|
/// The configuration derived from the Ghostty config so we don't need to rely on references.
|
||||||
private var derivedConfig: DerivedConfig
|
private var derivedConfig: DerivedConfig
|
||||||
|
|
||||||
@ -32,6 +44,11 @@ class QuickTerminalController: BaseTerminalController {
|
|||||||
|
|
||||||
// Setup our notifications for behaviors
|
// Setup our notifications for behaviors
|
||||||
let center = NotificationCenter.default
|
let center = NotificationCenter.default
|
||||||
|
center.addObserver(
|
||||||
|
self,
|
||||||
|
selector: #selector(applicationWillTerminate(_:)),
|
||||||
|
name: NSApplication.willTerminateNotification,
|
||||||
|
object: nil)
|
||||||
center.addObserver(
|
center.addObserver(
|
||||||
self,
|
self,
|
||||||
selector: #selector(onToggleFullscreen),
|
selector: #selector(onToggleFullscreen),
|
||||||
@ -52,6 +69,9 @@ class QuickTerminalController: BaseTerminalController {
|
|||||||
// Remove all of our notificationcenter subscriptions
|
// Remove all of our notificationcenter subscriptions
|
||||||
let center = NotificationCenter.default
|
let center = NotificationCenter.default
|
||||||
center.removeObserver(self)
|
center.removeObserver(self)
|
||||||
|
|
||||||
|
// Make sure we restore our hidden dock
|
||||||
|
hiddenDock = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: NSWindowController
|
// MARK: NSWindowController
|
||||||
@ -87,6 +107,17 @@ class QuickTerminalController: BaseTerminalController {
|
|||||||
|
|
||||||
// MARK: NSWindowDelegate
|
// MARK: NSWindowDelegate
|
||||||
|
|
||||||
|
override func windowDidBecomeKey(_ notification: Notification) {
|
||||||
|
super.windowDidBecomeKey(notification)
|
||||||
|
|
||||||
|
// If we're not visible we don't care to run the logic below. It only
|
||||||
|
// applies if we can be seen.
|
||||||
|
guard visible else { return }
|
||||||
|
|
||||||
|
// Re-hide the dock if we were hiding it before.
|
||||||
|
hiddenDock?.hide()
|
||||||
|
}
|
||||||
|
|
||||||
override func windowDidResignKey(_ notification: Notification) {
|
override func windowDidResignKey(_ notification: Notification) {
|
||||||
super.windowDidResignKey(notification)
|
super.windowDidResignKey(notification)
|
||||||
|
|
||||||
@ -107,8 +138,32 @@ class QuickTerminalController: BaseTerminalController {
|
|||||||
self.previousApp = nil
|
self.previousApp = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if (derivedConfig.quickTerminalAutoHide) {
|
// Regardless of autohide, we always want to bring the dock back
|
||||||
animateOut()
|
// when we lose focus.
|
||||||
|
hiddenDock?.restore()
|
||||||
|
|
||||||
|
if derivedConfig.quickTerminalAutoHide {
|
||||||
|
switch derivedConfig.quickTerminalSpaceBehavior {
|
||||||
|
case .remain:
|
||||||
|
// If we lose focus on the active space, then we can animate out
|
||||||
|
animateOut()
|
||||||
|
|
||||||
|
case .move:
|
||||||
|
let currentActiveSpace = CGSGetActiveSpace(CGSMainConnectionID())
|
||||||
|
if previousActiveSpace == currentActiveSpace {
|
||||||
|
// We haven't moved spaces. We lost focus to another app on the
|
||||||
|
// current space. Animate out.
|
||||||
|
animateOut()
|
||||||
|
} else {
|
||||||
|
// We've moved to a different space. Bring the quick terminal back
|
||||||
|
// into view.
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.window?.makeKeyAndOrderFront(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.previousActiveSpace = currentActiveSpace
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,6 +218,9 @@ class QuickTerminalController: BaseTerminalController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set previous active space
|
||||||
|
self.previousActiveSpace = CGSGetActiveSpace(CGSMainConnectionID())
|
||||||
|
|
||||||
// Animate the window in
|
// Animate the window in
|
||||||
animateWindowIn(window: window, from: position)
|
animateWindowIn(window: window, from: position)
|
||||||
|
|
||||||
@ -198,8 +256,29 @@ class QuickTerminalController: BaseTerminalController {
|
|||||||
// Move our window off screen to the top
|
// Move our window off screen to the top
|
||||||
position.setInitial(in: window, on: screen)
|
position.setInitial(in: window, on: screen)
|
||||||
|
|
||||||
|
// We need to set our window level to a high value. In testing, only
|
||||||
|
// popUpMenu and above do what we want. This gets it above the menu bar
|
||||||
|
// and lets us render off screen.
|
||||||
|
window.level = .popUpMenu
|
||||||
|
|
||||||
// Move it to the visible position since animation requires this
|
// Move it to the visible position since animation requires this
|
||||||
window.makeKeyAndOrderFront(nil)
|
DispatchQueue.main.async {
|
||||||
|
window.makeKeyAndOrderFront(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If our dock position would conflict with our target location then
|
||||||
|
// we autohide the dock.
|
||||||
|
if position.conflictsWithDock(on: screen) {
|
||||||
|
if (hiddenDock == nil) {
|
||||||
|
hiddenDock = .init()
|
||||||
|
}
|
||||||
|
|
||||||
|
hiddenDock?.hide()
|
||||||
|
} else {
|
||||||
|
// Ensure we don't have any hidden dock if we don't conflict.
|
||||||
|
// The deinit will restore.
|
||||||
|
hiddenDock = nil
|
||||||
|
}
|
||||||
|
|
||||||
// Run the animation that moves our window into the proper place and makes
|
// Run the animation that moves our window into the proper place and makes
|
||||||
// it visible.
|
// it visible.
|
||||||
@ -211,8 +290,16 @@ class QuickTerminalController: BaseTerminalController {
|
|||||||
// There is a very minor delay here so waiting at least an event loop tick
|
// There is a very minor delay here so waiting at least an event loop tick
|
||||||
// keeps us safe from the view not being on the window.
|
// keeps us safe from the view not being on the window.
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
// If we canceled our animation in we do nothing
|
// If we canceled our animation clean up some state.
|
||||||
guard self.visible else { return }
|
guard self.visible else {
|
||||||
|
self.hiddenDock = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// After animating in, we reset the window level to a value that
|
||||||
|
// is above other windows but not as high as popUpMenu. This allows
|
||||||
|
// things like IME dropdowns to appear properly.
|
||||||
|
window.level = .floating
|
||||||
|
|
||||||
// Now that the window is visible, sync our appearance. This function
|
// Now that the window is visible, sync our appearance. This function
|
||||||
// requires the window is visible.
|
// requires the window is visible.
|
||||||
@ -276,6 +363,17 @@ class QuickTerminalController: BaseTerminalController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func animateWindowOut(window: NSWindow, to position: QuickTerminalPosition) {
|
private func animateWindowOut(window: NSWindow, to position: QuickTerminalPosition) {
|
||||||
|
// If we hid the dock then we unhide it.
|
||||||
|
hiddenDock = nil
|
||||||
|
|
||||||
|
// If the window isn't on our active space then we don't animate, we just
|
||||||
|
// hide it.
|
||||||
|
if !window.isOnActiveSpace {
|
||||||
|
self.previousApp = nil
|
||||||
|
window.orderOut(self)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// We always animate out to whatever screen the window is actually on.
|
// We always animate out to whatever screen the window is actually on.
|
||||||
guard let screen = window.screen ?? NSScreen.main else { return }
|
guard let screen = window.screen ?? NSScreen.main else { return }
|
||||||
|
|
||||||
@ -297,6 +395,11 @@ class QuickTerminalController: BaseTerminalController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We need to set our window level to a high value. In testing, only
|
||||||
|
// popUpMenu and above do what we want. This gets it above the menu bar
|
||||||
|
// and lets us render off screen.
|
||||||
|
window.level = .popUpMenu
|
||||||
|
|
||||||
NSAnimationContext.runAnimationGroup({ context in
|
NSAnimationContext.runAnimationGroup({ context in
|
||||||
context.duration = derivedConfig.quickTerminalAnimationDuration
|
context.duration = derivedConfig.quickTerminalAnimationDuration
|
||||||
context.timingFunction = .init(name: .easeIn)
|
context.timingFunction = .init(name: .easeIn)
|
||||||
@ -311,23 +414,13 @@ class QuickTerminalController: BaseTerminalController {
|
|||||||
private func syncAppearance() {
|
private func syncAppearance() {
|
||||||
guard let window else { return }
|
guard let window else { return }
|
||||||
|
|
||||||
|
// Change the collection behavior of the window depending on the configuration.
|
||||||
|
window.collectionBehavior = derivedConfig.quickTerminalSpaceBehavior.collectionBehavior
|
||||||
|
|
||||||
// If our window is not visible, then no need to sync the appearance yet.
|
// If our window is not visible, then no need to sync the appearance yet.
|
||||||
// Some APIs such as window blur have no effect unless the window is visible.
|
// Some APIs such as window blur have no effect unless the window is visible.
|
||||||
guard window.isVisible else { return }
|
guard window.isVisible else { return }
|
||||||
|
|
||||||
// Terminals typically operate in sRGB color space and macOS defaults
|
|
||||||
// to "native" which is typically P3. There is a lot more resources
|
|
||||||
// covered in this GitHub issue: https://github.com/mitchellh/ghostty/pull/376
|
|
||||||
// Ghostty defaults to sRGB but this can be overridden.
|
|
||||||
switch (self.derivedConfig.windowColorspace) {
|
|
||||||
case "display-p3":
|
|
||||||
window.colorSpace = .displayP3
|
|
||||||
case "srgb":
|
|
||||||
fallthrough
|
|
||||||
default:
|
|
||||||
window.colorSpace = .sRGB
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have window transparency then set it transparent. Otherwise set it opaque.
|
// If we have window transparency then set it transparent. Otherwise set it opaque.
|
||||||
if (self.derivedConfig.backgroundOpacity < 1) {
|
if (self.derivedConfig.backgroundOpacity < 1) {
|
||||||
window.isOpaque = false
|
window.isOpaque = false
|
||||||
@ -368,6 +461,13 @@ class QuickTerminalController: BaseTerminalController {
|
|||||||
|
|
||||||
// MARK: Notifications
|
// MARK: Notifications
|
||||||
|
|
||||||
|
@objc private func applicationWillTerminate(_ notification: Notification) {
|
||||||
|
// If the application is going to terminate we want to make sure we
|
||||||
|
// restore any global dock state. I think deinit should be called which
|
||||||
|
// would call this anyways but I can't be sure so I will do this too.
|
||||||
|
hiddenDock = nil
|
||||||
|
}
|
||||||
|
|
||||||
@objc private func onToggleFullscreen(notification: SwiftUI.Notification) {
|
@objc private func onToggleFullscreen(notification: SwiftUI.Notification) {
|
||||||
guard let target = notification.object as? Ghostty.SurfaceView else { return }
|
guard let target = notification.object as? Ghostty.SurfaceView else { return }
|
||||||
guard target == self.focusedSurface else { return }
|
guard target == self.focusedSurface else { return }
|
||||||
@ -396,14 +496,14 @@ class QuickTerminalController: BaseTerminalController {
|
|||||||
let quickTerminalScreen: QuickTerminalScreen
|
let quickTerminalScreen: QuickTerminalScreen
|
||||||
let quickTerminalAnimationDuration: Double
|
let quickTerminalAnimationDuration: Double
|
||||||
let quickTerminalAutoHide: Bool
|
let quickTerminalAutoHide: Bool
|
||||||
let windowColorspace: String
|
let quickTerminalSpaceBehavior: QuickTerminalSpaceBehavior
|
||||||
let backgroundOpacity: Double
|
let backgroundOpacity: Double
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
self.quickTerminalScreen = .main
|
self.quickTerminalScreen = .main
|
||||||
self.quickTerminalAnimationDuration = 0.2
|
self.quickTerminalAnimationDuration = 0.2
|
||||||
self.quickTerminalAutoHide = true
|
self.quickTerminalAutoHide = true
|
||||||
self.windowColorspace = ""
|
self.quickTerminalSpaceBehavior = .move
|
||||||
self.backgroundOpacity = 1.0
|
self.backgroundOpacity = 1.0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -411,10 +511,39 @@ class QuickTerminalController: BaseTerminalController {
|
|||||||
self.quickTerminalScreen = config.quickTerminalScreen
|
self.quickTerminalScreen = config.quickTerminalScreen
|
||||||
self.quickTerminalAnimationDuration = config.quickTerminalAnimationDuration
|
self.quickTerminalAnimationDuration = config.quickTerminalAnimationDuration
|
||||||
self.quickTerminalAutoHide = config.quickTerminalAutoHide
|
self.quickTerminalAutoHide = config.quickTerminalAutoHide
|
||||||
self.windowColorspace = config.windowColorspace
|
self.quickTerminalSpaceBehavior = config.quickTerminalSpaceBehavior
|
||||||
self.backgroundOpacity = config.backgroundOpacity
|
self.backgroundOpacity = config.backgroundOpacity
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Hides the dock globally (not just NSApp). This is only used if the quick terminal is
|
||||||
|
/// in a conflicting position with the dock.
|
||||||
|
private class HiddenDock {
|
||||||
|
let previousAutoHide: Bool
|
||||||
|
private var hidden: Bool = false
|
||||||
|
|
||||||
|
init() {
|
||||||
|
previousAutoHide = Dock.autoHideEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
restore()
|
||||||
|
}
|
||||||
|
|
||||||
|
func hide() {
|
||||||
|
guard !hidden else { return }
|
||||||
|
NSApp.acquirePresentationOption(.autoHideDock)
|
||||||
|
Dock.autoHideEnabled = true
|
||||||
|
hidden = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func restore() {
|
||||||
|
guard hidden else { return }
|
||||||
|
NSApp.releasePresentationOption(.autoHideDock)
|
||||||
|
Dock.autoHideEnabled = previousAutoHide
|
||||||
|
hidden = false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Notification.Name {
|
extension Notification.Name {
|
||||||
|
@ -69,7 +69,7 @@ enum QuickTerminalPosition : String {
|
|||||||
finalSize.width = screen.frame.width
|
finalSize.width = screen.frame.width
|
||||||
|
|
||||||
case .left, .right:
|
case .left, .right:
|
||||||
finalSize.height = screen.frame.height
|
finalSize.height = screen.visibleFrame.height
|
||||||
|
|
||||||
case .center:
|
case .center:
|
||||||
finalSize.width = screen.frame.width / 2
|
finalSize.width = screen.frame.width / 2
|
||||||
@ -118,4 +118,22 @@ enum QuickTerminalPosition : String {
|
|||||||
return .init(x: screen.visibleFrame.origin.x + (screen.visibleFrame.width - window.frame.width) / 2, y: screen.visibleFrame.origin.y + (screen.visibleFrame.height - window.frame.height) / 2)
|
return .init(x: screen.visibleFrame.origin.x + (screen.visibleFrame.width - window.frame.width) / 2, y: screen.visibleFrame.origin.y + (screen.visibleFrame.height - window.frame.height) / 2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func conflictsWithDock(on screen: NSScreen) -> Bool {
|
||||||
|
// Screen must have a dock for it to conflict
|
||||||
|
guard screen.hasDock else { return false }
|
||||||
|
|
||||||
|
// Get the dock orientation for this screen
|
||||||
|
guard let orientation = Dock.orientation else { return false }
|
||||||
|
|
||||||
|
// Depending on the orientation of the dock, we conflict if our quick terminal
|
||||||
|
// would potentially "hit" the dock. In the future we should probably consider
|
||||||
|
// the frame of the quick terminal.
|
||||||
|
return switch (orientation) {
|
||||||
|
case .top: self == .top || self == .left || self == .right
|
||||||
|
case .bottom: self == .bottom || self == .left || self == .right
|
||||||
|
case .left: self == .top || self == .bottom
|
||||||
|
case .right: self == .top || self == .bottom
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
import Foundation
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
enum QuickTerminalSpaceBehavior {
|
||||||
|
case remain
|
||||||
|
case move
|
||||||
|
|
||||||
|
init?(fromGhosttyConfig string: String) {
|
||||||
|
switch (string) {
|
||||||
|
case "move":
|
||||||
|
self = .move
|
||||||
|
|
||||||
|
case "remain":
|
||||||
|
self = .remain
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var collectionBehavior: NSWindow.CollectionBehavior {
|
||||||
|
let commonBehavior: [NSWindow.CollectionBehavior] = [
|
||||||
|
.ignoresCycle,
|
||||||
|
.fullScreenAuxiliary
|
||||||
|
]
|
||||||
|
|
||||||
|
switch (self) {
|
||||||
|
case .move:
|
||||||
|
// We want this to move the window to the active space.
|
||||||
|
return NSWindow.CollectionBehavior([.canJoinAllSpaces] + commonBehavior)
|
||||||
|
case .remain:
|
||||||
|
// We want this to remain the window in the current space.
|
||||||
|
return NSWindow.CollectionBehavior([.moveToActiveSpace] + commonBehavior)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
class QuickTerminalWindow: NSWindow {
|
class QuickTerminalWindow: NSPanel {
|
||||||
// Both of these must be true for windows without decorations to be able to
|
// Both of these must be true for windows without decorations to be able to
|
||||||
// still become key/main and receive events.
|
// still become key/main and receive events.
|
||||||
override var canBecomeKey: Bool { return true }
|
override var canBecomeKey: Bool { return true }
|
||||||
@ -26,22 +26,7 @@ class QuickTerminalWindow: NSWindow {
|
|||||||
// window remains resizable.
|
// window remains resizable.
|
||||||
self.styleMask.remove(.titled)
|
self.styleMask.remove(.titled)
|
||||||
|
|
||||||
// We need to set our window level to a high value. In testing, only
|
// We don't want to activate the owning app when quick terminal is triggered.
|
||||||
// popUpMenu and above do what we want. This gets it above the menu bar
|
self.styleMask.insert(.nonactivatingPanel)
|
||||||
// and lets us render off screen.
|
|
||||||
self.level = .popUpMenu
|
|
||||||
|
|
||||||
// This plus the level above was what was needed for the animation to work,
|
|
||||||
// because it gets the window off screen properly. Plus we add some fields
|
|
||||||
// we just want the behavior of.
|
|
||||||
self.collectionBehavior = [
|
|
||||||
// We want this to be part of every space because it is a singleton.
|
|
||||||
.canJoinAllSpaces,
|
|
||||||
|
|
||||||
// We don't want to be part of command-tilde
|
|
||||||
.ignoresCycle,
|
|
||||||
|
|
||||||
// We never support fullscreen
|
|
||||||
.fullScreenNone]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -389,9 +389,9 @@ class BaseTerminalController: NSWindowController,
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (request) {
|
switch (request) {
|
||||||
case .osc_52_write:
|
case let .osc_52_write(pasteboard):
|
||||||
guard case .confirm = action else { break }
|
guard case .confirm = action else { break }
|
||||||
let pb = NSPasteboard.general
|
let pb = pasteboard ?? NSPasteboard.general
|
||||||
pb.declareTypes([.string], owner: nil)
|
pb.declareTypes([.string], owner: nil)
|
||||||
pb.setString(cc.contents, forType: .string)
|
pb.setString(cc.contents, forType: .string)
|
||||||
case .osc_52_read, .paste:
|
case .osc_52_read, .paste:
|
||||||
@ -452,6 +452,7 @@ class BaseTerminalController: NSWindowController,
|
|||||||
self.alert = nil
|
self.alert = nil
|
||||||
switch (response) {
|
switch (response) {
|
||||||
case .alertFirstButtonReturn:
|
case .alertFirstButtonReturn:
|
||||||
|
alert.window.orderOut(nil)
|
||||||
window.close()
|
window.close()
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -22,7 +22,7 @@ class TerminalController: BaseTerminalController {
|
|||||||
private var restorable: Bool = true
|
private var restorable: Bool = true
|
||||||
|
|
||||||
/// The configuration derived from the Ghostty config so we don't need to rely on references.
|
/// The configuration derived from the Ghostty config so we don't need to rely on references.
|
||||||
private var derivedConfig: DerivedConfig
|
private(set) var derivedConfig: DerivedConfig
|
||||||
|
|
||||||
/// The notification cancellable for focused surface property changes.
|
/// The notification cancellable for focused surface property changes.
|
||||||
private var surfaceAppearanceCancellables: Set<AnyCancellable> = []
|
private var surfaceAppearanceCancellables: Set<AnyCancellable> = []
|
||||||
@ -60,6 +60,11 @@ class TerminalController: BaseTerminalController {
|
|||||||
selector: #selector(onGotoTab),
|
selector: #selector(onGotoTab),
|
||||||
name: Ghostty.Notification.ghosttyGotoTab,
|
name: Ghostty.Notification.ghosttyGotoTab,
|
||||||
object: nil)
|
object: nil)
|
||||||
|
center.addObserver(
|
||||||
|
self,
|
||||||
|
selector: #selector(onCloseTab),
|
||||||
|
name: .ghosttyCloseTab,
|
||||||
|
object: nil)
|
||||||
center.addObserver(
|
center.addObserver(
|
||||||
self,
|
self,
|
||||||
selector: #selector(ghosttyConfigDidChange(_:)),
|
selector: #selector(ghosttyConfigDidChange(_:)),
|
||||||
@ -278,9 +283,12 @@ class TerminalController: BaseTerminalController {
|
|||||||
private func setInitialWindowPosition(x: Int16?, y: Int16?, windowDecorations: Bool) {
|
private func setInitialWindowPosition(x: Int16?, y: Int16?, windowDecorations: Bool) {
|
||||||
guard let window else { return }
|
guard let window else { return }
|
||||||
|
|
||||||
// If we don't have both an X and Y we center.
|
// If we don't have an X/Y then we try to use the previously saved window pos.
|
||||||
guard let x, let y else {
|
guard let x, let y else {
|
||||||
window.center()
|
if (!LastWindowPosition.shared.restore(window)) {
|
||||||
|
window.center()
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -310,28 +318,28 @@ class TerminalController: BaseTerminalController {
|
|||||||
window.styleMask = [
|
window.styleMask = [
|
||||||
// We need `titled` in the mask to get the normal window frame
|
// We need `titled` in the mask to get the normal window frame
|
||||||
.titled,
|
.titled,
|
||||||
|
|
||||||
// Full size content view so we can extend
|
// Full size content view so we can extend
|
||||||
// content in to the hidden titlebar's area
|
// content in to the hidden titlebar's area
|
||||||
.fullSizeContentView,
|
.fullSizeContentView,
|
||||||
|
|
||||||
.resizable,
|
.resizable,
|
||||||
.closable,
|
.closable,
|
||||||
.miniaturizable,
|
.miniaturizable,
|
||||||
]
|
]
|
||||||
|
|
||||||
// Hide the title
|
// Hide the title
|
||||||
window.titleVisibility = .hidden
|
window.titleVisibility = .hidden
|
||||||
window.titlebarAppearsTransparent = true
|
window.titlebarAppearsTransparent = true
|
||||||
|
|
||||||
// Hide the traffic lights (window control buttons)
|
// Hide the traffic lights (window control buttons)
|
||||||
window.standardWindowButton(.closeButton)?.isHidden = true
|
window.standardWindowButton(.closeButton)?.isHidden = true
|
||||||
window.standardWindowButton(.miniaturizeButton)?.isHidden = true
|
window.standardWindowButton(.miniaturizeButton)?.isHidden = true
|
||||||
window.standardWindowButton(.zoomButton)?.isHidden = true
|
window.standardWindowButton(.zoomButton)?.isHidden = true
|
||||||
|
|
||||||
// Disallow tabbing if the titlebar is hidden, since that will (should) also hide the tab bar.
|
// Disallow tabbing if the titlebar is hidden, since that will (should) also hide the tab bar.
|
||||||
window.tabbingMode = .disallowed
|
window.tabbingMode = .disallowed
|
||||||
|
|
||||||
// Nuke it from orbit -- hide the titlebar container entirely, just in case. There are
|
// Nuke it from orbit -- hide the titlebar container entirely, just in case. There are
|
||||||
// some operations that appear to bring back the titlebar visibility so this ensures
|
// some operations that appear to bring back the titlebar visibility so this ensures
|
||||||
// it is gone forever.
|
// it is gone forever.
|
||||||
@ -340,7 +348,7 @@ class TerminalController: BaseTerminalController {
|
|||||||
titleBarContainer.isHidden = true
|
titleBarContainer.isHidden = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func windowDidLoad() {
|
override func windowDidLoad() {
|
||||||
super.windowDidLoad()
|
super.windowDidLoad()
|
||||||
guard let window = window as? TerminalWindow else { return }
|
guard let window = window as? TerminalWindow else { return }
|
||||||
@ -361,33 +369,31 @@ class TerminalController: BaseTerminalController {
|
|||||||
// If window decorations are disabled, remove our title
|
// If window decorations are disabled, remove our title
|
||||||
if (!config.windowDecorations) { window.styleMask.remove(.titled) }
|
if (!config.windowDecorations) { window.styleMask.remove(.titled) }
|
||||||
|
|
||||||
// Terminals typically operate in sRGB color space and macOS defaults
|
|
||||||
// to "native" which is typically P3. There is a lot more resources
|
|
||||||
// covered in this GitHub issue: https://github.com/mitchellh/ghostty/pull/376
|
|
||||||
// Ghostty defaults to sRGB but this can be overridden.
|
|
||||||
switch (config.windowColorspace) {
|
|
||||||
case "display-p3":
|
|
||||||
window.colorSpace = .displayP3
|
|
||||||
case "srgb":
|
|
||||||
fallthrough
|
|
||||||
default:
|
|
||||||
window.colorSpace = .sRGB
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have only a single surface (no splits) and that surface requested
|
// If we have only a single surface (no splits) and that surface requested
|
||||||
// an initial size then we set it here now.
|
// an initial size then we set it here now.
|
||||||
if case let .leaf(leaf) = surfaceTree {
|
if case let .leaf(leaf) = surfaceTree {
|
||||||
if let initialSize = leaf.surface.initialSize,
|
if let initialSize = leaf.surface.initialSize,
|
||||||
let screen = window.screen ?? NSScreen.main {
|
let screen = window.screen ?? NSScreen.main {
|
||||||
// Setup our frame. We need to first subtract the views frame so that we can
|
// Get the current frame of the window
|
||||||
// just get the chrome frame so that we only affect the surface view size.
|
|
||||||
var frame = window.frame
|
var frame = window.frame
|
||||||
frame.size.width -= leaf.surface.frame.size.width
|
|
||||||
frame.size.height -= leaf.surface.frame.size.height
|
|
||||||
frame.size.width += min(initialSize.width, screen.frame.width)
|
|
||||||
frame.size.height += min(initialSize.height, screen.frame.height)
|
|
||||||
|
|
||||||
// We have no tabs and we are not a split, so set the initial size of the window.
|
// Calculate the chrome size (window size minus view size)
|
||||||
|
let chromeWidth = frame.size.width - leaf.surface.frame.size.width
|
||||||
|
let chromeHeight = frame.size.height - leaf.surface.frame.size.height
|
||||||
|
|
||||||
|
// Calculate the new width and height, clamping to the screen's size
|
||||||
|
let newWidth = min(initialSize.width + chromeWidth, screen.visibleFrame.width)
|
||||||
|
let newHeight = min(initialSize.height + chromeHeight, screen.visibleFrame.height)
|
||||||
|
|
||||||
|
// Update the frame size while keeping the window's position intact
|
||||||
|
frame.size.width = newWidth
|
||||||
|
frame.size.height = newHeight
|
||||||
|
|
||||||
|
// Ensure the window doesn't go outside the screen boundaries
|
||||||
|
frame.origin.x = max(screen.frame.origin.x, min(frame.origin.x, screen.frame.maxX - newWidth))
|
||||||
|
frame.origin.y = max(screen.frame.origin.y, min(frame.origin.y, screen.frame.maxY - newHeight))
|
||||||
|
|
||||||
|
// Set the updated frame to the window
|
||||||
window.setFrame(frame, display: true)
|
window.setFrame(frame, display: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -487,6 +493,20 @@ class TerminalController: BaseTerminalController {
|
|||||||
override func windowDidMove(_ notification: Notification) {
|
override func windowDidMove(_ notification: Notification) {
|
||||||
super.windowDidMove(notification)
|
super.windowDidMove(notification)
|
||||||
self.fixTabBar()
|
self.fixTabBar()
|
||||||
|
|
||||||
|
// Whenever we move save our last position for the next start.
|
||||||
|
if let window {
|
||||||
|
LastWindowPosition.shared.save(window)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowDidBecomeMain(_ notification: Notification) {
|
||||||
|
// Whenever we get focused, use that as our last window position for
|
||||||
|
// restart. This differs from Terminal.app but matches iTerm2 behavior
|
||||||
|
// and I think its sensible.
|
||||||
|
if let window {
|
||||||
|
LastWindowPosition.shared.save(window)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called when the window will be encoded. We handle the data encoding here in the
|
// Called when the window will be encoded. We handle the data encoding here in the
|
||||||
@ -508,7 +528,50 @@ class TerminalController: BaseTerminalController {
|
|||||||
ghostty.newTab(surface: surface)
|
ghostty.newTab(surface: surface)
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction override func closeWindow(_ sender: Any) {
|
private func confirmClose(
|
||||||
|
window: NSWindow,
|
||||||
|
messageText: String,
|
||||||
|
informativeText: String,
|
||||||
|
completion: @escaping () -> Void
|
||||||
|
) {
|
||||||
|
// If we need confirmation by any, show one confirmation for all windows
|
||||||
|
// in the tab group.
|
||||||
|
let alert = NSAlert()
|
||||||
|
alert.messageText = messageText
|
||||||
|
alert.informativeText = informativeText
|
||||||
|
alert.addButton(withTitle: "Close")
|
||||||
|
alert.addButton(withTitle: "Cancel")
|
||||||
|
alert.alertStyle = .warning
|
||||||
|
alert.beginSheetModal(for: window) { response in
|
||||||
|
if response == .alertFirstButtonReturn {
|
||||||
|
completion()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func closeTab(_ sender: Any?) {
|
||||||
|
guard let window = window else { return }
|
||||||
|
guard window.tabGroup != nil else {
|
||||||
|
// No tabs, no tab group, just perform a normal close.
|
||||||
|
window.performClose(sender)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if surfaceTree?.needsConfirmQuit() ?? false {
|
||||||
|
confirmClose(
|
||||||
|
window: window,
|
||||||
|
messageText: "Close Tab?",
|
||||||
|
informativeText: "The terminal still has a running process. If you close the tab the process will be killed."
|
||||||
|
) {
|
||||||
|
window.close()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
window.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction override func closeWindow(_ sender: Any?) {
|
||||||
guard let window = window else { return }
|
guard let window = window else { return }
|
||||||
guard let tabGroup = window.tabGroup else {
|
guard let tabGroup = window.tabGroup else {
|
||||||
// No tabs, no tab group, just perform a normal close.
|
// No tabs, no tab group, just perform a normal close.
|
||||||
@ -523,47 +586,34 @@ class TerminalController: BaseTerminalController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if any windows require close confirmation.
|
// Check if any windows require close confirmation.
|
||||||
var needsConfirm: Bool = false
|
let needsConfirm = tabGroup.windows.contains { tabWindow in
|
||||||
for tabWindow in tabGroup.windows {
|
guard let controller = tabWindow.windowController as? TerminalController else {
|
||||||
guard let c = tabWindow.windowController as? TerminalController else { continue }
|
return false
|
||||||
if (c.surfaceTree?.needsConfirmQuit() ?? false) {
|
|
||||||
needsConfirm = true
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
return controller.surfaceTree?.needsConfirmQuit() ?? false
|
||||||
}
|
}
|
||||||
|
|
||||||
// If none need confirmation then we can just close all the windows.
|
// If none need confirmation then we can just close all the windows.
|
||||||
if (!needsConfirm) {
|
if !needsConfirm {
|
||||||
for tabWindow in tabGroup.windows {
|
tabGroup.windows.forEach { $0.close() }
|
||||||
tabWindow.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we need confirmation by any, show one confirmation for all windows
|
confirmClose(
|
||||||
// in the tab group.
|
window: window,
|
||||||
let alert = NSAlert()
|
messageText: "Close Window?",
|
||||||
alert.messageText = "Close Window?"
|
informativeText: "All terminal sessions in this window will be terminated."
|
||||||
alert.informativeText = "All terminal sessions in this window will be terminated."
|
) {
|
||||||
alert.addButton(withTitle: "Close Window")
|
tabGroup.windows.forEach { $0.close() }
|
||||||
alert.addButton(withTitle: "Cancel")
|
}
|
||||||
alert.alertStyle = .warning
|
|
||||||
alert.beginSheetModal(for: window, completionHandler: { response in
|
|
||||||
if (response == .alertFirstButtonReturn) {
|
|
||||||
for tabWindow in tabGroup.windows {
|
|
||||||
tabWindow.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func toggleGhosttyFullScreen(_ sender: Any) {
|
@IBAction func toggleGhosttyFullScreen(_ sender: Any?) {
|
||||||
guard let surface = focusedSurface?.surface else { return }
|
guard let surface = focusedSurface?.surface else { return }
|
||||||
ghostty.toggleFullscreen(surface: surface)
|
ghostty.toggleFullscreen(surface: surface)
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func toggleTerminalInspector(_ sender: Any) {
|
@IBAction func toggleTerminalInspector(_ sender: Any?) {
|
||||||
guard let surface = focusedSurface?.surface else { return }
|
guard let surface = focusedSurface?.surface else { return }
|
||||||
ghostty.toggleTerminalInspector(surface: surface)
|
ghostty.toggleTerminalInspector(surface: surface)
|
||||||
}
|
}
|
||||||
@ -659,13 +709,21 @@ class TerminalController: BaseTerminalController {
|
|||||||
// If our index is the same we do nothing
|
// If our index is the same we do nothing
|
||||||
guard finalIndex != selectedIndex else { return }
|
guard finalIndex != selectedIndex else { return }
|
||||||
|
|
||||||
// Get our parent
|
// Get our target window
|
||||||
let parent = tabbedWindows[finalIndex]
|
let targetWindow = tabbedWindows[finalIndex]
|
||||||
|
|
||||||
// Move our current selected window to the proper index
|
// Begin a group of window operations to minimize visual updates
|
||||||
|
NSAnimationContext.beginGrouping()
|
||||||
|
NSAnimationContext.current.duration = 0
|
||||||
|
|
||||||
|
// Remove and re-add the window in the correct position
|
||||||
tabGroup.removeWindow(selectedWindow)
|
tabGroup.removeWindow(selectedWindow)
|
||||||
parent.addTabbedWindow(selectedWindow, ordered: action.amount < 0 ? .below : .above)
|
targetWindow.addTabbedWindow(selectedWindow, ordered: action.amount < 0 ? .below : .above)
|
||||||
selectedWindow.makeKeyAndOrderFront(nil)
|
|
||||||
|
// Ensure our window remains selected
|
||||||
|
selectedWindow.makeKey()
|
||||||
|
|
||||||
|
NSAnimationContext.endGrouping()
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func onGotoTab(notification: SwiftUI.Notification) {
|
@objc private func onGotoTab(notification: SwiftUI.Notification) {
|
||||||
@ -720,6 +778,12 @@ class TerminalController: BaseTerminalController {
|
|||||||
targetWindow.makeKeyAndOrderFront(nil)
|
targetWindow.makeKeyAndOrderFront(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc private func onCloseTab(notification: SwiftUI.Notification) {
|
||||||
|
guard let target = notification.object as? Ghostty.SurfaceView else { return }
|
||||||
|
guard surfaceTree?.contains(view: target) ?? false else { return }
|
||||||
|
closeTab(self)
|
||||||
|
}
|
||||||
|
|
||||||
@objc private func onToggleFullscreen(notification: SwiftUI.Notification) {
|
@objc private func onToggleFullscreen(notification: SwiftUI.Notification) {
|
||||||
guard let target = notification.object as? Ghostty.SurfaceView else { return }
|
guard let target = notification.object as? Ghostty.SurfaceView else { return }
|
||||||
guard target == self.focusedSurface else { return }
|
guard target == self.focusedSurface else { return }
|
||||||
@ -737,7 +801,7 @@ class TerminalController: BaseTerminalController {
|
|||||||
toggleFullscreen(mode: fullscreenMode)
|
toggleFullscreen(mode: fullscreenMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct DerivedConfig {
|
struct DerivedConfig {
|
||||||
let backgroundColor: Color
|
let backgroundColor: Color
|
||||||
let macosTitlebarStyle: String
|
let macosTitlebarStyle: String
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ class TerminalManager {
|
|||||||
// fullscreen for the logic later in this method.
|
// fullscreen for the logic later in this method.
|
||||||
c.toggleFullscreen(mode: .native)
|
c.toggleFullscreen(mode: .native)
|
||||||
|
|
||||||
case .nonNative, .nonNativeVisibleMenu:
|
case .nonNative, .nonNativeVisibleMenu, .nonNativePaddedNotch:
|
||||||
// If we're non-native then we have to do it on a later loop
|
// If we're non-native then we have to do it on a later loop
|
||||||
// so that the content view is setup.
|
// so that the content view is setup.
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
|
@ -10,7 +10,7 @@ protocol TerminalViewDelegate: AnyObject {
|
|||||||
|
|
||||||
/// The title of the terminal should change.
|
/// The title of the terminal should change.
|
||||||
func titleDidChange(to: String)
|
func titleDidChange(to: String)
|
||||||
|
|
||||||
/// The URL of the pwd should change.
|
/// The URL of the pwd should change.
|
||||||
func pwdDidChange(to: URL?)
|
func pwdDidChange(to: URL?)
|
||||||
|
|
||||||
@ -56,15 +56,10 @@ struct TerminalView<ViewModel: TerminalViewModel>: View {
|
|||||||
|
|
||||||
// The title for our window
|
// The title for our window
|
||||||
private var title: String {
|
private var title: String {
|
||||||
var title = "👻"
|
if let surfaceTitle, !surfaceTitle.isEmpty {
|
||||||
|
return surfaceTitle
|
||||||
if let surfaceTitle = surfaceTitle {
|
|
||||||
if (surfaceTitle.count > 0) {
|
|
||||||
title = surfaceTitle
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return "👻"
|
||||||
return title
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The pwd of the focused surface as a URL
|
// The pwd of the focused surface as a URL
|
||||||
@ -72,7 +67,7 @@ struct TerminalView<ViewModel: TerminalViewModel>: View {
|
|||||||
guard let surfacePwd, surfacePwd != "" else { return nil }
|
guard let surfacePwd, surfacePwd != "" else { return nil }
|
||||||
return URL(fileURLWithPath: surfacePwd)
|
return URL(fileURLWithPath: surfacePwd)
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
switch ghostty.readiness {
|
switch ghostty.readiness {
|
||||||
case .loading:
|
case .loading:
|
||||||
|
@ -115,6 +115,21 @@ class TerminalWindow: NSWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We override this so that with the hidden titlebar style the titlebar
|
||||||
|
// area is not draggable.
|
||||||
|
override var contentLayoutRect: CGRect {
|
||||||
|
var rect = super.contentLayoutRect
|
||||||
|
|
||||||
|
// If we are using a hidden titlebar style, the content layout is the
|
||||||
|
// full frame making it so that it is not draggable.
|
||||||
|
if let controller = windowController as? TerminalController,
|
||||||
|
controller.derivedConfig.macosTitlebarStyle == "hidden" {
|
||||||
|
rect.origin.y = 0
|
||||||
|
rect.size.height = self.frame.height
|
||||||
|
}
|
||||||
|
return rect
|
||||||
|
}
|
||||||
|
|
||||||
// The window theme configuration from Ghostty. This is used to control some
|
// The window theme configuration from Ghostty. This is used to control some
|
||||||
// behaviors that don't look quite right in certain situations.
|
// behaviors that don't look quite right in certain situations.
|
||||||
var windowTheme: TerminalWindowTheme?
|
var windowTheme: TerminalWindowTheme?
|
||||||
|
@ -13,6 +13,9 @@ extension FullscreenMode {
|
|||||||
case GHOSTTY_FULLSCREEN_NON_NATIVE_VISIBLE_MENU:
|
case GHOSTTY_FULLSCREEN_NON_NATIVE_VISIBLE_MENU:
|
||||||
.nonNativeVisibleMenu
|
.nonNativeVisibleMenu
|
||||||
|
|
||||||
|
case GHOSTTY_FULLSCREEN_NON_NATIVE_PADDED_NOTCH:
|
||||||
|
.nonNativePaddedNotch
|
||||||
|
|
||||||
default:
|
default:
|
||||||
nil
|
nil
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ extension Ghostty {
|
|||||||
// uses to interface with the application runtime environment.
|
// uses to interface with the application runtime environment.
|
||||||
var runtime_cfg = ghostty_runtime_config_s(
|
var runtime_cfg = ghostty_runtime_config_s(
|
||||||
userdata: Unmanaged.passUnretained(self).toOpaque(),
|
userdata: Unmanaged.passUnretained(self).toOpaque(),
|
||||||
supports_selection_clipboard: false,
|
supports_selection_clipboard: true,
|
||||||
wakeup_cb: { userdata in App.wakeup(userdata) },
|
wakeup_cb: { userdata in App.wakeup(userdata) },
|
||||||
action_cb: { app, target, action in App.action(app!, target: target, action: action) },
|
action_cb: { app, target, action in App.action(app!, target: target, action: action) },
|
||||||
read_clipboard_cb: { userdata, loc, state in App.readClipboard(userdata, location: loc, state: state) },
|
read_clipboard_cb: { userdata, loc, state in App.readClipboard(userdata, location: loc, state: state) },
|
||||||
@ -257,7 +257,7 @@ extension Ghostty {
|
|||||||
// MARK: Ghostty Callbacks (iOS)
|
// MARK: Ghostty Callbacks (iOS)
|
||||||
|
|
||||||
static func wakeup(_ userdata: UnsafeMutableRawPointer?) {}
|
static func wakeup(_ userdata: UnsafeMutableRawPointer?) {}
|
||||||
static func action(_ app: ghostty_app_t, target: ghostty_target_s, action: ghostty_action_s) {}
|
static func action(_ app: ghostty_app_t, target: ghostty_target_s, action: ghostty_action_s) -> Bool { return false }
|
||||||
static func readClipboard(
|
static func readClipboard(
|
||||||
_ userdata: UnsafeMutableRawPointer?,
|
_ userdata: UnsafeMutableRawPointer?,
|
||||||
location: ghostty_clipboard_e,
|
location: ghostty_clipboard_e,
|
||||||
@ -320,13 +320,13 @@ extension Ghostty {
|
|||||||
let surfaceView = self.surfaceUserdata(from: userdata)
|
let surfaceView = self.surfaceUserdata(from: userdata)
|
||||||
guard let surface = surfaceView.surface else { return }
|
guard let surface = surfaceView.surface else { return }
|
||||||
|
|
||||||
// We only support the standard clipboard
|
// Get our pasteboard
|
||||||
if (location != GHOSTTY_CLIPBOARD_STANDARD) {
|
guard let pasteboard = NSPasteboard.ghostty(location) else {
|
||||||
return completeClipboardRequest(surface, data: "", state: state)
|
return completeClipboardRequest(surface, data: "", state: state)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get our string
|
// Get our string
|
||||||
let str = NSPasteboard.general.getOpinionatedStringContents() ?? ""
|
let str = pasteboard.getOpinionatedStringContents() ?? ""
|
||||||
completeClipboardRequest(surface, data: str, state: state)
|
completeClipboardRequest(surface, data: str, state: state)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -364,14 +364,12 @@ extension Ghostty {
|
|||||||
static func writeClipboard(_ userdata: UnsafeMutableRawPointer?, string: UnsafePointer<CChar>?, location: ghostty_clipboard_e, confirm: Bool) {
|
static func writeClipboard(_ userdata: UnsafeMutableRawPointer?, string: UnsafePointer<CChar>?, location: ghostty_clipboard_e, confirm: Bool) {
|
||||||
let surface = self.surfaceUserdata(from: userdata)
|
let surface = self.surfaceUserdata(from: userdata)
|
||||||
|
|
||||||
// We only support the standard clipboard
|
|
||||||
if (location != GHOSTTY_CLIPBOARD_STANDARD) { return }
|
|
||||||
|
|
||||||
|
guard let pasteboard = NSPasteboard.ghostty(location) else { return }
|
||||||
guard let valueStr = String(cString: string!, encoding: .utf8) else { return }
|
guard let valueStr = String(cString: string!, encoding: .utf8) else { return }
|
||||||
if !confirm {
|
if !confirm {
|
||||||
let pb = NSPasteboard.general
|
pasteboard.declareTypes([.string], owner: nil)
|
||||||
pb.declareTypes([.string], owner: nil)
|
pasteboard.setString(valueStr, forType: .string)
|
||||||
pb.setString(valueStr, forType: .string)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -380,7 +378,7 @@ extension Ghostty {
|
|||||||
object: surface,
|
object: surface,
|
||||||
userInfo: [
|
userInfo: [
|
||||||
Notification.ConfirmClipboardStrKey: valueStr,
|
Notification.ConfirmClipboardStrKey: valueStr,
|
||||||
Notification.ConfirmClipboardRequestKey: Ghostty.ClipboardRequest.osc_52_write,
|
Notification.ConfirmClipboardRequestKey: Ghostty.ClipboardRequest.osc_52_write(pasteboard),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -425,7 +423,7 @@ extension Ghostty {
|
|||||||
|
|
||||||
// MARK: Actions (macOS)
|
// MARK: Actions (macOS)
|
||||||
|
|
||||||
static func action(_ app: ghostty_app_t, target: ghostty_target_s, action: ghostty_action_s) {
|
static func action(_ app: ghostty_app_t, target: ghostty_target_s, action: ghostty_action_s) -> Bool {
|
||||||
// Make sure it a target we understand so all our action handlers can assert
|
// Make sure it a target we understand so all our action handlers can assert
|
||||||
switch (target.tag) {
|
switch (target.tag) {
|
||||||
case GHOSTTY_TARGET_APP, GHOSTTY_TARGET_SURFACE:
|
case GHOSTTY_TARGET_APP, GHOSTTY_TARGET_SURFACE:
|
||||||
@ -433,7 +431,7 @@ extension Ghostty {
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
Ghostty.logger.warning("unknown action target=\(target.tag.rawValue)")
|
Ghostty.logger.warning("unknown action target=\(target.tag.rawValue)")
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Action dispatch
|
// Action dispatch
|
||||||
@ -450,17 +448,20 @@ extension Ghostty {
|
|||||||
case GHOSTTY_ACTION_NEW_SPLIT:
|
case GHOSTTY_ACTION_NEW_SPLIT:
|
||||||
newSplit(app, target: target, direction: action.action.new_split)
|
newSplit(app, target: target, direction: action.action.new_split)
|
||||||
|
|
||||||
|
case GHOSTTY_ACTION_CLOSE_TAB:
|
||||||
|
closeTab(app, target: target)
|
||||||
|
|
||||||
case GHOSTTY_ACTION_TOGGLE_FULLSCREEN:
|
case GHOSTTY_ACTION_TOGGLE_FULLSCREEN:
|
||||||
toggleFullscreen(app, target: target, mode: action.action.toggle_fullscreen)
|
toggleFullscreen(app, target: target, mode: action.action.toggle_fullscreen)
|
||||||
|
|
||||||
case GHOSTTY_ACTION_MOVE_TAB:
|
case GHOSTTY_ACTION_MOVE_TAB:
|
||||||
moveTab(app, target: target, move: action.action.move_tab)
|
return moveTab(app, target: target, move: action.action.move_tab)
|
||||||
|
|
||||||
case GHOSTTY_ACTION_GOTO_TAB:
|
case GHOSTTY_ACTION_GOTO_TAB:
|
||||||
gotoTab(app, target: target, tab: action.action.goto_tab)
|
return gotoTab(app, target: target, tab: action.action.goto_tab)
|
||||||
|
|
||||||
case GHOSTTY_ACTION_GOTO_SPLIT:
|
case GHOSTTY_ACTION_GOTO_SPLIT:
|
||||||
gotoSplit(app, target: target, direction: action.action.goto_split)
|
return gotoSplit(app, target: target, direction: action.action.goto_split)
|
||||||
|
|
||||||
case GHOSTTY_ACTION_RESIZE_SPLIT:
|
case GHOSTTY_ACTION_RESIZE_SPLIT:
|
||||||
resizeSplit(app, target: target, resize: action.action.resize_split)
|
resizeSplit(app, target: target, resize: action.action.resize_split)
|
||||||
@ -483,6 +484,9 @@ extension Ghostty {
|
|||||||
case GHOSTTY_ACTION_SET_TITLE:
|
case GHOSTTY_ACTION_SET_TITLE:
|
||||||
setTitle(app, target: target, v: action.action.set_title)
|
setTitle(app, target: target, v: action.action.set_title)
|
||||||
|
|
||||||
|
case GHOSTTY_ACTION_PROMPT_TITLE:
|
||||||
|
return promptTitle(app, target: target)
|
||||||
|
|
||||||
case GHOSTTY_ACTION_PWD:
|
case GHOSTTY_ACTION_PWD:
|
||||||
pwdChanged(app, target: target, v: action.action.pwd)
|
pwdChanged(app, target: target, v: action.action.pwd)
|
||||||
|
|
||||||
@ -540,10 +544,15 @@ extension Ghostty {
|
|||||||
fallthrough
|
fallthrough
|
||||||
case GHOSTTY_ACTION_QUIT_TIMER:
|
case GHOSTTY_ACTION_QUIT_TIMER:
|
||||||
Ghostty.logger.info("known but unimplemented action action=\(action.tag.rawValue)")
|
Ghostty.logger.info("known but unimplemented action action=\(action.tag.rawValue)")
|
||||||
|
return false
|
||||||
default:
|
default:
|
||||||
Ghostty.logger.warning("unknown action action=\(action.tag.rawValue)")
|
Ghostty.logger.warning("unknown action action=\(action.tag.rawValue)")
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we reached here then we assume performed since all unknown actions
|
||||||
|
// are captured in the switch and return false.
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func quit(_ app: ghostty_app_t) {
|
private static func quit(_ app: ghostty_app_t) {
|
||||||
@ -653,6 +662,27 @@ extension Ghostty {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static func closeTab(_ app: ghostty_app_t, target: ghostty_target_s) {
|
||||||
|
switch (target.tag) {
|
||||||
|
case GHOSTTY_TARGET_APP:
|
||||||
|
Ghostty.logger.warning("close tab does nothing with an app target")
|
||||||
|
return
|
||||||
|
|
||||||
|
case GHOSTTY_TARGET_SURFACE:
|
||||||
|
guard let surface = target.target.surface else { return }
|
||||||
|
guard let surfaceView = self.surfaceView(from: surface) else { return }
|
||||||
|
|
||||||
|
NotificationCenter.default.post(
|
||||||
|
name: .ghosttyCloseTab,
|
||||||
|
object: surfaceView
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
default:
|
||||||
|
assertionFailure()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static func toggleFullscreen(
|
private static func toggleFullscreen(
|
||||||
_ app: ghostty_app_t,
|
_ app: ghostty_app_t,
|
||||||
target: ghostty_target_s,
|
target: ghostty_target_s,
|
||||||
@ -694,15 +724,19 @@ extension Ghostty {
|
|||||||
private static func moveTab(
|
private static func moveTab(
|
||||||
_ app: ghostty_app_t,
|
_ app: ghostty_app_t,
|
||||||
target: ghostty_target_s,
|
target: ghostty_target_s,
|
||||||
move: ghostty_action_move_tab_s) {
|
move: ghostty_action_move_tab_s) -> Bool {
|
||||||
switch (target.tag) {
|
switch (target.tag) {
|
||||||
case GHOSTTY_TARGET_APP:
|
case GHOSTTY_TARGET_APP:
|
||||||
Ghostty.logger.warning("move tab does nothing with an app target")
|
Ghostty.logger.warning("move tab does nothing with an app target")
|
||||||
return
|
return false
|
||||||
|
|
||||||
case GHOSTTY_TARGET_SURFACE:
|
case GHOSTTY_TARGET_SURFACE:
|
||||||
guard let surface = target.target.surface else { return }
|
guard let surface = target.target.surface else { return false }
|
||||||
guard let surfaceView = self.surfaceView(from: surface) else { return }
|
guard let surfaceView = self.surfaceView(from: surface) else { return false }
|
||||||
|
|
||||||
|
// See gotoTab for notes on this check.
|
||||||
|
guard (surfaceView.window?.tabGroup?.windows.count ?? 0) > 1 else { return false }
|
||||||
|
|
||||||
NotificationCenter.default.post(
|
NotificationCenter.default.post(
|
||||||
name: .ghosttyMoveTab,
|
name: .ghosttyMoveTab,
|
||||||
object: surfaceView,
|
object: surfaceView,
|
||||||
@ -714,20 +748,27 @@ extension Ghostty {
|
|||||||
default:
|
default:
|
||||||
assertionFailure()
|
assertionFailure()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func gotoTab(
|
private static func gotoTab(
|
||||||
_ app: ghostty_app_t,
|
_ app: ghostty_app_t,
|
||||||
target: ghostty_target_s,
|
target: ghostty_target_s,
|
||||||
tab: ghostty_action_goto_tab_e) {
|
tab: ghostty_action_goto_tab_e) -> Bool {
|
||||||
switch (target.tag) {
|
switch (target.tag) {
|
||||||
case GHOSTTY_TARGET_APP:
|
case GHOSTTY_TARGET_APP:
|
||||||
Ghostty.logger.warning("goto tab does nothing with an app target")
|
Ghostty.logger.warning("goto tab does nothing with an app target")
|
||||||
return
|
return false
|
||||||
|
|
||||||
case GHOSTTY_TARGET_SURFACE:
|
case GHOSTTY_TARGET_SURFACE:
|
||||||
guard let surface = target.target.surface else { return }
|
guard let surface = target.target.surface else { return false }
|
||||||
guard let surfaceView = self.surfaceView(from: surface) else { return }
|
guard let surfaceView = self.surfaceView(from: surface) else { return false }
|
||||||
|
|
||||||
|
// Similar to goto_split (see comment there) about our performability,
|
||||||
|
// we should make this more accurate later.
|
||||||
|
guard (surfaceView.window?.tabGroup?.windows.count ?? 0) > 1 else { return false }
|
||||||
|
|
||||||
NotificationCenter.default.post(
|
NotificationCenter.default.post(
|
||||||
name: Notification.ghosttyGotoTab,
|
name: Notification.ghosttyGotoTab,
|
||||||
object: surfaceView,
|
object: surfaceView,
|
||||||
@ -739,20 +780,31 @@ extension Ghostty {
|
|||||||
default:
|
default:
|
||||||
assertionFailure()
|
assertionFailure()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func gotoSplit(
|
private static func gotoSplit(
|
||||||
_ app: ghostty_app_t,
|
_ app: ghostty_app_t,
|
||||||
target: ghostty_target_s,
|
target: ghostty_target_s,
|
||||||
direction: ghostty_action_goto_split_e) {
|
direction: ghostty_action_goto_split_e) -> Bool {
|
||||||
switch (target.tag) {
|
switch (target.tag) {
|
||||||
case GHOSTTY_TARGET_APP:
|
case GHOSTTY_TARGET_APP:
|
||||||
Ghostty.logger.warning("goto split does nothing with an app target")
|
Ghostty.logger.warning("goto split does nothing with an app target")
|
||||||
return
|
return false
|
||||||
|
|
||||||
case GHOSTTY_TARGET_SURFACE:
|
case GHOSTTY_TARGET_SURFACE:
|
||||||
guard let surface = target.target.surface else { return }
|
guard let surface = target.target.surface else { return false }
|
||||||
guard let surfaceView = self.surfaceView(from: surface) else { return }
|
guard let surfaceView = self.surfaceView(from: surface) else { return false }
|
||||||
|
guard let controller = surfaceView.window?.windowController as? BaseTerminalController else { return false }
|
||||||
|
|
||||||
|
// For now, we return false if the window has no splits and we return
|
||||||
|
// true if the window has ANY splits. This isn't strictly correct because
|
||||||
|
// we should only be returning true if we actually performed the action,
|
||||||
|
// but this handles the most common case of caring about goto_split performability
|
||||||
|
// which is the no-split case.
|
||||||
|
guard controller.surfaceTree?.isSplit ?? false else { return false }
|
||||||
|
|
||||||
NotificationCenter.default.post(
|
NotificationCenter.default.post(
|
||||||
name: Notification.ghosttyFocusSplit,
|
name: Notification.ghosttyFocusSplit,
|
||||||
object: surfaceView,
|
object: surfaceView,
|
||||||
@ -764,6 +816,8 @@ extension Ghostty {
|
|||||||
default:
|
default:
|
||||||
assertionFailure()
|
assertionFailure()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func resizeSplit(
|
private static func resizeSplit(
|
||||||
@ -956,6 +1010,26 @@ extension Ghostty {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static func promptTitle(
|
||||||
|
_ app: ghostty_app_t,
|
||||||
|
target: ghostty_target_s) -> Bool {
|
||||||
|
switch (target.tag) {
|
||||||
|
case GHOSTTY_TARGET_APP:
|
||||||
|
Ghostty.logger.warning("set title prompt does nothing with an app target")
|
||||||
|
return false
|
||||||
|
|
||||||
|
case GHOSTTY_TARGET_SURFACE:
|
||||||
|
guard let surface = target.target.surface else { return false }
|
||||||
|
guard let surfaceView = self.surfaceView(from: surface) else { return false }
|
||||||
|
surfaceView.promptTitle()
|
||||||
|
|
||||||
|
default:
|
||||||
|
assertionFailure()
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
private static func pwdChanged(
|
private static func pwdChanged(
|
||||||
_ app: ghostty_app_t,
|
_ app: ghostty_app_t,
|
||||||
target: ghostty_target_s,
|
target: ghostty_target_s,
|
||||||
|
@ -132,15 +132,6 @@ extension Ghostty {
|
|||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
var windowColorspace: String {
|
|
||||||
guard let config = self.config else { return "" }
|
|
||||||
var v: UnsafePointer<Int8>? = nil
|
|
||||||
let key = "window-colorspace"
|
|
||||||
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return "" }
|
|
||||||
guard let ptr = v else { return "" }
|
|
||||||
return String(cString: ptr)
|
|
||||||
}
|
|
||||||
|
|
||||||
var windowSaveState: String {
|
var windowSaveState: String {
|
||||||
guard let config = self.config else { return "" }
|
guard let config = self.config else { return "" }
|
||||||
var v: UnsafePointer<Int8>? = nil
|
var v: UnsafePointer<Int8>? = nil
|
||||||
@ -174,11 +165,14 @@ extension Ghostty {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var windowDecorations: Bool {
|
var windowDecorations: Bool {
|
||||||
guard let config = self.config else { return true }
|
let defaultValue = true
|
||||||
var v = false;
|
guard let config = self.config else { return defaultValue }
|
||||||
|
var v: UnsafePointer<Int8>? = nil
|
||||||
let key = "window-decoration"
|
let key = "window-decoration"
|
||||||
_ = ghostty_config_get(config, &v, key, UInt(key.count))
|
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return defaultValue }
|
||||||
return v;
|
guard let ptr = v else { return defaultValue }
|
||||||
|
let str = String(cString: ptr)
|
||||||
|
return WindowDecoration(rawValue: str)?.enabled() ?? defaultValue
|
||||||
}
|
}
|
||||||
|
|
||||||
var windowTheme: String? {
|
var windowTheme: String? {
|
||||||
@ -222,6 +216,8 @@ extension Ghostty {
|
|||||||
.nonNative
|
.nonNative
|
||||||
case "visible-menu":
|
case "visible-menu":
|
||||||
.nonNativeVisibleMenu
|
.nonNativeVisibleMenu
|
||||||
|
case "padded-notch":
|
||||||
|
.nonNativePaddedNotch
|
||||||
default:
|
default:
|
||||||
defaultValue
|
defaultValue
|
||||||
}
|
}
|
||||||
@ -306,6 +302,16 @@ extension Ghostty {
|
|||||||
return buffer.map { .init(ghostty: $0) }
|
return buffer.map { .init(ghostty: $0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var macosHidden: MacHidden {
|
||||||
|
guard let config = self.config else { return .never }
|
||||||
|
var v: UnsafePointer<Int8>? = nil
|
||||||
|
let key = "macos-hidden"
|
||||||
|
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return .never }
|
||||||
|
guard let ptr = v else { return .never }
|
||||||
|
let str = String(cString: ptr)
|
||||||
|
return MacHidden(rawValue: str) ?? .never
|
||||||
|
}
|
||||||
|
|
||||||
var focusFollowsMouse : Bool {
|
var focusFollowsMouse : Bool {
|
||||||
guard let config = self.config else { return false }
|
guard let config = self.config else { return false }
|
||||||
var v = false;
|
var v = false;
|
||||||
@ -345,7 +351,7 @@ extension Ghostty {
|
|||||||
var backgroundBlurRadius: Int {
|
var backgroundBlurRadius: Int {
|
||||||
guard let config = self.config else { return 1 }
|
guard let config = self.config else { return 1 }
|
||||||
var v: Int = 0
|
var v: Int = 0
|
||||||
let key = "background-blur-radius"
|
let key = "background-blur"
|
||||||
_ = ghostty_config_get(config, &v, key, UInt(key.count))
|
_ = ghostty_config_get(config, &v, key, UInt(key.count))
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
@ -431,6 +437,16 @@ extension Ghostty {
|
|||||||
_ = ghostty_config_get(config, &v, key, UInt(key.count))
|
_ = ghostty_config_get(config, &v, key, UInt(key.count))
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var quickTerminalSpaceBehavior: QuickTerminalSpaceBehavior {
|
||||||
|
guard let config = self.config else { return .move }
|
||||||
|
var v: UnsafePointer<Int8>? = nil
|
||||||
|
let key = "quick-terminal-space-behavior"
|
||||||
|
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return .move }
|
||||||
|
guard let ptr = v else { return .move }
|
||||||
|
let str = String(cString: ptr)
|
||||||
|
return QuickTerminalSpaceBehavior(fromGhosttyConfig: str) ?? .move
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
var resizeOverlay: ResizeOverlay {
|
var resizeOverlay: ResizeOverlay {
|
||||||
@ -510,6 +526,11 @@ extension Ghostty.Config {
|
|||||||
case download
|
case download
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum MacHidden : String {
|
||||||
|
case never
|
||||||
|
case always
|
||||||
|
}
|
||||||
|
|
||||||
enum ResizeOverlay : String {
|
enum ResizeOverlay : String {
|
||||||
case always
|
case always
|
||||||
case never
|
case never
|
||||||
@ -553,4 +574,18 @@ extension Ghostty.Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum WindowDecoration: String {
|
||||||
|
case none
|
||||||
|
case client
|
||||||
|
case server
|
||||||
|
case auto
|
||||||
|
|
||||||
|
func enabled() -> Bool {
|
||||||
|
switch self {
|
||||||
|
case .client, .server, .auto: return true
|
||||||
|
case .none: return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,15 @@ extension Ghostty {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if the tree is split.
|
||||||
|
var isSplit: Bool {
|
||||||
|
return if case .leaf = self {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func topLeft() -> SurfaceView {
|
func topLeft() -> SurfaceView {
|
||||||
switch (self) {
|
switch (self) {
|
||||||
case .leaf(let leaf):
|
case .leaf(let leaf):
|
||||||
@ -120,14 +129,7 @@ extension Ghostty {
|
|||||||
|
|
||||||
/// Returns true if the split tree contains the given view.
|
/// Returns true if the split tree contains the given view.
|
||||||
func contains(view: SurfaceView) -> Bool {
|
func contains(view: SurfaceView) -> Bool {
|
||||||
switch (self) {
|
return leaf(for: view) != nil
|
||||||
case .leaf(let leaf):
|
|
||||||
return leaf.surface == view
|
|
||||||
|
|
||||||
case .split(let container):
|
|
||||||
return container.topLeft.contains(view: view) ||
|
|
||||||
container.bottomRight.contains(view: view)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find a surface view by UUID.
|
/// Find a surface view by UUID.
|
||||||
@ -164,6 +166,22 @@ extension Ghostty {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the node for the given view if its in the tree.
|
||||||
|
func leaf(for view: SurfaceView) -> Leaf? {
|
||||||
|
switch (self) {
|
||||||
|
case .leaf(let leaf):
|
||||||
|
if leaf.surface == view {
|
||||||
|
return leaf
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
case .split(let container):
|
||||||
|
return container.topLeft.leaf(for: view) ??
|
||||||
|
container.bottomRight.leaf(for: view)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Sequence
|
// MARK: - Sequence
|
||||||
|
|
||||||
func makeIterator() -> IndexingIterator<[Leaf]> {
|
func makeIterator() -> IndexingIterator<[Leaf]> {
|
||||||
|
@ -205,6 +205,7 @@ extension Ghostty {
|
|||||||
alert.beginSheetModal(for: window, completionHandler: { response in
|
alert.beginSheetModal(for: window, completionHandler: { response in
|
||||||
switch (response) {
|
switch (response) {
|
||||||
case .alertFirstButtonReturn:
|
case .alertFirstButtonReturn:
|
||||||
|
alert.window.orderOut(nil)
|
||||||
node = nil
|
node = nil
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -159,7 +159,7 @@ extension Ghostty {
|
|||||||
case osc_52_read
|
case osc_52_read
|
||||||
|
|
||||||
/// An application is attempting to write to the clipboard using OSC 52
|
/// An application is attempting to write to the clipboard using OSC 52
|
||||||
case osc_52_write
|
case osc_52_write(OSPasteboard?)
|
||||||
|
|
||||||
/// The text to show in the clipboard confirmation prompt for a given request type
|
/// The text to show in the clipboard confirmation prompt for a given request type
|
||||||
func text() -> String {
|
func text() -> String {
|
||||||
@ -188,7 +188,7 @@ extension Ghostty {
|
|||||||
case GHOSTTY_CLIPBOARD_REQUEST_OSC_52_READ:
|
case GHOSTTY_CLIPBOARD_REQUEST_OSC_52_READ:
|
||||||
return .osc_52_read
|
return .osc_52_read
|
||||||
case GHOSTTY_CLIPBOARD_REQUEST_OSC_52_WRITE:
|
case GHOSTTY_CLIPBOARD_REQUEST_OSC_52_WRITE:
|
||||||
return .osc_52_write
|
return .osc_52_write(nil)
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -198,6 +198,14 @@ extension Ghostty {
|
|||||||
/// macos-icon
|
/// macos-icon
|
||||||
enum MacOSIcon: String {
|
enum MacOSIcon: String {
|
||||||
case official
|
case official
|
||||||
|
case blueprint
|
||||||
|
case chalkboard
|
||||||
|
case glass
|
||||||
|
case holographic
|
||||||
|
case microchip
|
||||||
|
case paper
|
||||||
|
case retro
|
||||||
|
case xray
|
||||||
case customStyle = "custom-style"
|
case customStyle = "custom-style"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,6 +244,9 @@ extension Notification.Name {
|
|||||||
/// Goto tab. Has tab index in the userinfo.
|
/// Goto tab. Has tab index in the userinfo.
|
||||||
static let ghosttyMoveTab = Notification.Name("com.mitchellh.ghostty.moveTab")
|
static let ghosttyMoveTab = Notification.Name("com.mitchellh.ghostty.moveTab")
|
||||||
static let GhosttyMoveTabKey = ghosttyMoveTab.rawValue
|
static let GhosttyMoveTabKey = ghosttyMoveTab.rawValue
|
||||||
|
|
||||||
|
/// Close tab
|
||||||
|
static let ghosttyCloseTab = Notification.Name("com.mitchellh.ghostty.closeTab")
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: I am moving all of these to Notification.Name extensions over time. This
|
// NOTE: I am moving all of these to Notification.Name extensions over time. This
|
||||||
|
@ -92,22 +92,6 @@ extension Ghostty {
|
|||||||
windowFocus = false
|
windowFocus = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onDrop(of: [.fileURL], isTargeted: nil) { providers in
|
|
||||||
providers.forEach { provider in
|
|
||||||
_ = provider.loadObject(ofClass: URL.self) { url, _ in
|
|
||||||
guard let url = url else { return }
|
|
||||||
let path = Shell.escape(url.path)
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
surfaceView.insertText(
|
|
||||||
path,
|
|
||||||
replacementRange: NSMakeRange(0, 0)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// If our geo size changed then we show the resize overlay as configured.
|
// If our geo size changed then we show the resize overlay as configured.
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import AppKit
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import CoreText
|
import CoreText
|
||||||
import UserNotifications
|
import UserNotifications
|
||||||
@ -12,7 +13,14 @@ extension Ghostty {
|
|||||||
// The current title of the surface as defined by the pty. This can be
|
// The current title of the surface as defined by the pty. This can be
|
||||||
// changed with escape codes. This is public because the callbacks go
|
// changed with escape codes. This is public because the callbacks go
|
||||||
// to the app level and it is set from there.
|
// to the app level and it is set from there.
|
||||||
@Published private(set) var title: String = "👻"
|
@Published private(set) var title: String = "" {
|
||||||
|
didSet {
|
||||||
|
if !title.isEmpty {
|
||||||
|
titleFallbackTimer?.invalidate()
|
||||||
|
titleFallbackTimer = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The current pwd of the surface as defined by the pty. This can be
|
// The current pwd of the surface as defined by the pty. This can be
|
||||||
// changed with escape codes.
|
// changed with escape codes.
|
||||||
@ -113,6 +121,14 @@ extension Ghostty {
|
|||||||
// A small delay that is introduced before a title change to avoid flickers
|
// A small delay that is introduced before a title change to avoid flickers
|
||||||
private var titleChangeTimer: Timer?
|
private var titleChangeTimer: Timer?
|
||||||
|
|
||||||
|
// A timer to fallback to ghost emoji if no title is set within the grace period
|
||||||
|
private var titleFallbackTimer: Timer?
|
||||||
|
|
||||||
|
// This is the title from the terminal. This is nil if we're currently using
|
||||||
|
// the terminal title as the main title property. If the title is set manually
|
||||||
|
// by the user, this is set to the prior value (which may be empty, but non-nil).
|
||||||
|
private var titleFromTerminal: String?
|
||||||
|
|
||||||
/// Event monitor (see individual events for why)
|
/// Event monitor (see individual events for why)
|
||||||
private var eventMonitor: Any? = nil
|
private var eventMonitor: Any? = nil
|
||||||
|
|
||||||
@ -139,6 +155,13 @@ extension Ghostty {
|
|||||||
// can do SOMETHING.
|
// can do SOMETHING.
|
||||||
super.init(frame: NSMakeRect(0, 0, 800, 600))
|
super.init(frame: NSMakeRect(0, 0, 800, 600))
|
||||||
|
|
||||||
|
// Set a timer to show the ghost emoji after 500ms if no title is set
|
||||||
|
titleFallbackTimer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { [weak self] _ in
|
||||||
|
if let self = self, self.title.isEmpty {
|
||||||
|
self.title = "👻"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Before we initialize the surface we want to register our notifications
|
// Before we initialize the surface we want to register our notifications
|
||||||
// so there is no window where we can't receive them.
|
// so there is no window where we can't receive them.
|
||||||
let center = NotificationCenter.default
|
let center = NotificationCenter.default
|
||||||
@ -213,6 +236,9 @@ extension Ghostty {
|
|||||||
|
|
||||||
ghostty_surface_set_color_scheme(surface, scheme)
|
ghostty_surface_set_color_scheme(surface, scheme)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The UTTypes that can be dragged onto this view.
|
||||||
|
registerForDraggedTypes(Array(Self.dropTypes))
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder: NSCoder) {
|
||||||
@ -359,6 +385,45 @@ extension Ghostty {
|
|||||||
NSCursor.setHiddenUntilMouseMoves(!visible)
|
NSCursor.setHiddenUntilMouseMoves(!visible)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the title by prompting the user.
|
||||||
|
func promptTitle() {
|
||||||
|
// Create an alert dialog
|
||||||
|
let alert = NSAlert()
|
||||||
|
alert.messageText = "Change Terminal Title"
|
||||||
|
alert.informativeText = "Leave blank to restore the default."
|
||||||
|
alert.alertStyle = .informational
|
||||||
|
|
||||||
|
// Add a text field to the alert
|
||||||
|
let textField = NSTextField(frame: NSRect(x: 0, y: 0, width: 250, height: 24))
|
||||||
|
textField.stringValue = title
|
||||||
|
alert.accessoryView = textField
|
||||||
|
|
||||||
|
// Add buttons
|
||||||
|
alert.addButton(withTitle: "OK")
|
||||||
|
alert.addButton(withTitle: "Cancel")
|
||||||
|
|
||||||
|
let response = alert.runModal()
|
||||||
|
|
||||||
|
// Check if the user clicked "OK"
|
||||||
|
if response == .alertFirstButtonReturn {
|
||||||
|
// Get the input text
|
||||||
|
let newTitle = textField.stringValue
|
||||||
|
|
||||||
|
if newTitle.isEmpty {
|
||||||
|
// Empty means that user wants the title to be set automatically
|
||||||
|
// We also need to reload the config for the "title" property to be
|
||||||
|
// used again by this tab.
|
||||||
|
let prevTitle = titleFromTerminal ?? "👻"
|
||||||
|
titleFromTerminal = nil
|
||||||
|
setTitle(prevTitle)
|
||||||
|
} else {
|
||||||
|
// Set the title and prevent it from being changed automatically
|
||||||
|
titleFromTerminal = title
|
||||||
|
title = newTitle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func setTitle(_ title: String) {
|
func setTitle(_ title: String) {
|
||||||
// This fixes an issue where very quick changes to the title could
|
// This fixes an issue where very quick changes to the title could
|
||||||
// cause an unpleasant flickering. We set a timer so that we can
|
// cause an unpleasant flickering. We set a timer so that we can
|
||||||
@ -369,6 +434,11 @@ extension Ghostty {
|
|||||||
withTimeInterval: 0.075,
|
withTimeInterval: 0.075,
|
||||||
repeats: false
|
repeats: false
|
||||||
) { [weak self] _ in
|
) { [weak self] _ in
|
||||||
|
// Set the title if it wasn't manually set.
|
||||||
|
guard self?.titleFromTerminal == nil else {
|
||||||
|
self?.titleFromTerminal = title
|
||||||
|
return
|
||||||
|
}
|
||||||
self?.title = title
|
self?.title = title
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1096,6 +1166,8 @@ extension Ghostty {
|
|||||||
menu.addItem(.separator())
|
menu.addItem(.separator())
|
||||||
menu.addItem(withTitle: "Reset Terminal", action: #selector(resetTerminal(_:)), keyEquivalent: "")
|
menu.addItem(withTitle: "Reset Terminal", action: #selector(resetTerminal(_:)), keyEquivalent: "")
|
||||||
menu.addItem(withTitle: "Toggle Terminal Inspector", action: #selector(toggleTerminalInspector(_:)), keyEquivalent: "")
|
menu.addItem(withTitle: "Toggle Terminal Inspector", action: #selector(toggleTerminalInspector(_:)), keyEquivalent: "")
|
||||||
|
menu.addItem(.separator())
|
||||||
|
menu.addItem(withTitle: "Change Title...", action: #selector(changeTitle(_:)), keyEquivalent: "")
|
||||||
|
|
||||||
return menu
|
return menu
|
||||||
}
|
}
|
||||||
@ -1127,6 +1199,14 @@ extension Ghostty {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@IBAction func pasteSelection(_ sender: Any?) {
|
||||||
|
guard let surface = self.surface else { return }
|
||||||
|
let action = "paste_from_selection"
|
||||||
|
if (!ghostty_surface_binding_action(surface, action, UInt(action.count))) {
|
||||||
|
AppDelegate.logger.warning("action failed action=\(action)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@IBAction override func selectAll(_ sender: Any?) {
|
@IBAction override func selectAll(_ sender: Any?) {
|
||||||
guard let surface = self.surface else { return }
|
guard let surface = self.surface else { return }
|
||||||
let action = "select_all"
|
let action = "select_all"
|
||||||
@ -1160,6 +1240,10 @@ extension Ghostty {
|
|||||||
AppDelegate.logger.warning("action failed action=\(action)")
|
AppDelegate.logger.warning("action failed action=\(action)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@IBAction func changeTitle(_ sender: Any) {
|
||||||
|
promptTitle()
|
||||||
|
}
|
||||||
|
|
||||||
/// Show a user notification and associate it with this surface
|
/// Show a user notification and associate it with this surface
|
||||||
func showUserNotification(title: String, body: String) {
|
func showUserNotification(title: String, body: String) {
|
||||||
@ -1448,3 +1532,78 @@ extension Ghostty.SurfaceView: NSServicesMenuRequestor {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: NSMenuItemValidation
|
||||||
|
|
||||||
|
extension Ghostty.SurfaceView: NSMenuItemValidation {
|
||||||
|
func validateMenuItem(_ item: NSMenuItem) -> Bool {
|
||||||
|
switch item.action {
|
||||||
|
case #selector(pasteSelection):
|
||||||
|
let pb = NSPasteboard.ghosttySelection
|
||||||
|
guard let str = pb.getOpinionatedStringContents() else { return false }
|
||||||
|
return !str.isEmpty
|
||||||
|
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: NSDraggingDestination
|
||||||
|
|
||||||
|
extension Ghostty.SurfaceView {
|
||||||
|
static let dropTypes: Set<NSPasteboard.PasteboardType> = [
|
||||||
|
.string,
|
||||||
|
.fileURL,
|
||||||
|
.URL
|
||||||
|
]
|
||||||
|
|
||||||
|
override func draggingEntered(_ sender: any NSDraggingInfo) -> NSDragOperation {
|
||||||
|
guard let types = sender.draggingPasteboard.types else { return [] }
|
||||||
|
|
||||||
|
// If the dragging object contains none of our types then we return none.
|
||||||
|
// This shouldn't happen because AppKit should guarantee that we only
|
||||||
|
// receive types we registered for but its good to check.
|
||||||
|
if Set(types).isDisjoint(with: Self.dropTypes) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
// We use copy to get the proper icon
|
||||||
|
return .copy
|
||||||
|
}
|
||||||
|
|
||||||
|
override func performDragOperation(_ sender: any NSDraggingInfo) -> Bool {
|
||||||
|
let pb = sender.draggingPasteboard
|
||||||
|
|
||||||
|
let content: String?
|
||||||
|
if let url = pb.string(forType: .URL) {
|
||||||
|
// URLs first, they get escaped as-is.
|
||||||
|
content = Ghostty.Shell.escape(url)
|
||||||
|
} else if let urls = pb.readObjects(forClasses: [NSURL.self]) as? [URL],
|
||||||
|
urls.count > 0 {
|
||||||
|
// File URLs next. They get escaped individually and then joined by a
|
||||||
|
// space if there are multiple.
|
||||||
|
content = urls
|
||||||
|
.map { Ghostty.Shell.escape($0.path) }
|
||||||
|
.joined(separator: " ")
|
||||||
|
} else if let str = pb.string(forType: .string) {
|
||||||
|
// Strings are not escaped because they may be copy/pasting a
|
||||||
|
// command they want to execute.
|
||||||
|
content = str
|
||||||
|
} else {
|
||||||
|
content = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if let content {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.insertText(
|
||||||
|
content,
|
||||||
|
replacementRange: NSMakeRange(0, 0)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -10,6 +10,7 @@ import AppKit
|
|||||||
typealias OSView = NSView
|
typealias OSView = NSView
|
||||||
typealias OSColor = NSColor
|
typealias OSColor = NSColor
|
||||||
typealias OSSize = NSSize
|
typealias OSSize = NSSize
|
||||||
|
typealias OSPasteboard = NSPasteboard
|
||||||
|
|
||||||
protocol OSViewRepresentable: NSViewRepresentable where NSViewType == OSViewType {
|
protocol OSViewRepresentable: NSViewRepresentable where NSViewType == OSViewType {
|
||||||
associatedtype OSViewType: NSView
|
associatedtype OSViewType: NSView
|
||||||
@ -34,6 +35,7 @@ import UIKit
|
|||||||
typealias OSView = UIView
|
typealias OSView = UIView
|
||||||
typealias OSColor = UIColor
|
typealias OSColor = UIColor
|
||||||
typealias OSSize = CGSize
|
typealias OSSize = CGSize
|
||||||
|
typealias OSPasteboard = UIPasteboard
|
||||||
|
|
||||||
protocol OSViewRepresentable: UIViewRepresentable {
|
protocol OSViewRepresentable: UIViewRepresentable {
|
||||||
associatedtype OSViewType: UIView
|
associatedtype OSViewType: UIView
|
||||||
|
38
macos/Sources/Helpers/Dock.swift
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import Cocoa
|
||||||
|
|
||||||
|
// Private API to get Dock location
|
||||||
|
@_silgen_name("CoreDockGetOrientationAndPinning")
|
||||||
|
func CoreDockGetOrientationAndPinning(
|
||||||
|
_ outOrientation: UnsafeMutablePointer<Int32>,
|
||||||
|
_ outPinning: UnsafeMutablePointer<Int32>)
|
||||||
|
|
||||||
|
// Private API to get the current Dock auto-hide state
|
||||||
|
@_silgen_name("CoreDockGetAutoHideEnabled")
|
||||||
|
func CoreDockGetAutoHideEnabled() -> Bool
|
||||||
|
|
||||||
|
// Toggles the Dock's auto-hide state
|
||||||
|
@_silgen_name("CoreDockSetAutoHideEnabled")
|
||||||
|
func CoreDockSetAutoHideEnabled(_ flag: Bool)
|
||||||
|
|
||||||
|
enum DockOrientation: Int {
|
||||||
|
case top = 1
|
||||||
|
case bottom = 2
|
||||||
|
case left = 3
|
||||||
|
case right = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
class Dock {
|
||||||
|
/// Returns the orientation of the dock or nil if it can't be determined.
|
||||||
|
static var orientation: DockOrientation? {
|
||||||
|
var orientation: Int32 = 0
|
||||||
|
var pinning: Int32 = 0
|
||||||
|
CoreDockGetOrientationAndPinning(&orientation, &pinning)
|
||||||
|
return .init(rawValue: Int(orientation)) ?? nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the dock autohide.
|
||||||
|
static var autoHideEnabled: Bool {
|
||||||
|
get { return CoreDockGetAutoHideEnabled() }
|
||||||
|
set { CoreDockSetAutoHideEnabled(newValue) }
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ enum FullscreenMode {
|
|||||||
case native
|
case native
|
||||||
case nonNative
|
case nonNative
|
||||||
case nonNativeVisibleMenu
|
case nonNativeVisibleMenu
|
||||||
|
case nonNativePaddedNotch
|
||||||
|
|
||||||
/// Initializes the fullscreen style implementation for the mode. This will not toggle any
|
/// Initializes the fullscreen style implementation for the mode. This will not toggle any
|
||||||
/// fullscreen properties. This may fail if the window isn't configured properly for a given
|
/// fullscreen properties. This may fail if the window isn't configured properly for a given
|
||||||
@ -20,6 +21,9 @@ enum FullscreenMode {
|
|||||||
|
|
||||||
case .nonNativeVisibleMenu:
|
case .nonNativeVisibleMenu:
|
||||||
return NonNativeFullscreenVisibleMenu(window)
|
return NonNativeFullscreenVisibleMenu(window)
|
||||||
|
|
||||||
|
case .nonNativePaddedNotch:
|
||||||
|
return NonNativeFullscreenPaddedNotch(window)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -141,6 +145,7 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle {
|
|||||||
|
|
||||||
struct Properties {
|
struct Properties {
|
||||||
var hideMenu: Bool = true
|
var hideMenu: Bool = true
|
||||||
|
var paddedNotch: Bool = false
|
||||||
}
|
}
|
||||||
|
|
||||||
private var savedState: SavedState?
|
private var savedState: SavedState?
|
||||||
@ -278,6 +283,9 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle {
|
|||||||
// put an #available check, but it was in a bug fix release so I think
|
// put an #available check, but it was in a bug fix release so I think
|
||||||
// if a bug is reported to Ghostty we can just advise the user to
|
// if a bug is reported to Ghostty we can just advise the user to
|
||||||
// update.
|
// update.
|
||||||
|
} else if (properties.paddedNotch) {
|
||||||
|
// We are hiding the menu, we may need to avoid the notch.
|
||||||
|
frame.size.height -= screen.safeAreaInsets.top
|
||||||
}
|
}
|
||||||
|
|
||||||
return frame
|
return frame
|
||||||
@ -307,21 +315,21 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle {
|
|||||||
// MARK: Dock
|
// MARK: Dock
|
||||||
|
|
||||||
private func hideDock() {
|
private func hideDock() {
|
||||||
NSApp.presentationOptions.insert(.autoHideDock)
|
NSApp.acquirePresentationOption(.autoHideDock)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func unhideDock() {
|
private func unhideDock() {
|
||||||
NSApp.presentationOptions.remove(.autoHideDock)
|
NSApp.releasePresentationOption(.autoHideDock)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Menu
|
// MARK: Menu
|
||||||
|
|
||||||
func hideMenu() {
|
func hideMenu() {
|
||||||
NSApp.presentationOptions.insert(.autoHideMenuBar)
|
NSApp.acquirePresentationOption(.autoHideMenuBar)
|
||||||
}
|
}
|
||||||
|
|
||||||
func unhideMenu() {
|
func unhideMenu() {
|
||||||
NSApp.presentationOptions.remove(.autoHideMenuBar)
|
NSApp.releasePresentationOption(.autoHideMenuBar)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The state that must be saved for non-native fullscreen to exit fullscreen.
|
/// The state that must be saved for non-native fullscreen to exit fullscreen.
|
||||||
@ -349,3 +357,7 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle {
|
|||||||
class NonNativeFullscreenVisibleMenu: NonNativeFullscreen {
|
class NonNativeFullscreenVisibleMenu: NonNativeFullscreen {
|
||||||
override var properties: Properties { Properties(hideMenu: false) }
|
override var properties: Properties { Properties(hideMenu: false) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class NonNativeFullscreenPaddedNotch: NonNativeFullscreen {
|
||||||
|
override var properties: Properties { Properties(paddedNotch: true) }
|
||||||
|
}
|
||||||
|
34
macos/Sources/Helpers/LastWindowPosition.swift
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import Cocoa
|
||||||
|
|
||||||
|
/// Manages the persistence and restoration of window positions across app launches.
|
||||||
|
class LastWindowPosition {
|
||||||
|
static let shared = LastWindowPosition()
|
||||||
|
|
||||||
|
private let positionKey = "NSWindowLastPosition"
|
||||||
|
|
||||||
|
func save(_ window: NSWindow) {
|
||||||
|
let origin = window.frame.origin
|
||||||
|
let point = [origin.x, origin.y]
|
||||||
|
UserDefaults.standard.set(point, forKey: positionKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func restore(_ window: NSWindow) -> Bool {
|
||||||
|
guard let points = UserDefaults.standard.array(forKey: positionKey) as? [Double],
|
||||||
|
points.count == 2 else { return false }
|
||||||
|
|
||||||
|
let lastPosition = CGPoint(x: points[0], y: points[1])
|
||||||
|
|
||||||
|
guard let screen = window.screen ?? NSScreen.main else { return false }
|
||||||
|
let visibleFrame = screen.visibleFrame
|
||||||
|
|
||||||
|
var newFrame = window.frame
|
||||||
|
newFrame.origin = lastPosition
|
||||||
|
if !visibleFrame.contains(newFrame.origin) {
|
||||||
|
newFrame.origin.x = max(visibleFrame.minX, min(visibleFrame.maxX - newFrame.width, newFrame.origin.x))
|
||||||
|
newFrame.origin.y = max(visibleFrame.minY, min(visibleFrame.maxY - newFrame.height, newFrame.origin.y))
|
||||||
|
}
|
||||||
|
|
||||||
|
window.setFrame(newFrame, display: true)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
31
macos/Sources/Helpers/NSApplication+Extension.swift
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import Cocoa
|
||||||
|
|
||||||
|
extension NSApplication {
|
||||||
|
private static var presentationOptionCounts: [NSApplication.PresentationOptions.Element: UInt] = [:]
|
||||||
|
|
||||||
|
/// Add a presentation option to the application and main a reference count so that and equal
|
||||||
|
/// number of pops is required to disable it. This is useful so that multiple classes can affect global
|
||||||
|
/// app state without overriding others.
|
||||||
|
func acquirePresentationOption(_ option: NSApplication.PresentationOptions.Element) {
|
||||||
|
Self.presentationOptionCounts[option, default: 0] += 1
|
||||||
|
presentationOptions.insert(option)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See acquirePresentationOption
|
||||||
|
func releasePresentationOption(_ option: NSApplication.PresentationOptions.Element) {
|
||||||
|
guard let value = Self.presentationOptionCounts[option] else { return }
|
||||||
|
guard value > 0 else { return }
|
||||||
|
if (value == 1) {
|
||||||
|
presentationOptions.remove(option)
|
||||||
|
Self.presentationOptionCounts.removeValue(forKey: option)
|
||||||
|
} else {
|
||||||
|
Self.presentationOptionCounts[option] = value - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension NSApplication.PresentationOptions.Element: @retroactive Hashable {
|
||||||
|
public func hash(into hasher: inout Hasher) {
|
||||||
|
hasher.combine(rawValue)
|
||||||
|
}
|
||||||
|
}
|
@ -1,17 +1,39 @@
|
|||||||
import AppKit
|
import AppKit
|
||||||
|
import GhosttyKit
|
||||||
|
|
||||||
extension NSPasteboard {
|
extension NSPasteboard {
|
||||||
|
/// The pasteboard to used for Ghostty selection.
|
||||||
|
static var ghosttySelection: NSPasteboard = {
|
||||||
|
NSPasteboard(name: .init("com.mitchellh.ghostty.selection"))
|
||||||
|
}()
|
||||||
|
|
||||||
/// Gets the contents of the pasteboard as a string following a specific set of semantics.
|
/// Gets the contents of the pasteboard as a string following a specific set of semantics.
|
||||||
/// Does these things in order:
|
/// Does these things in order:
|
||||||
/// - Tries to get the absolute filesystem path of the file in the pasteboard if there is one.
|
/// - Tries to get the absolute filesystem path of the file in the pasteboard if there is one and ensures the file path is properly escaped.
|
||||||
/// - Tries to get any string from the pasteboard.
|
/// - Tries to get any string from the pasteboard.
|
||||||
/// If all of the above fail, returns None.
|
/// If all of the above fail, returns None.
|
||||||
func getOpinionatedStringContents() -> String? {
|
func getOpinionatedStringContents() -> String? {
|
||||||
if let file = self.string(forType: .fileURL) {
|
if let urls = readObjects(forClasses: [NSURL.self]) as? [URL],
|
||||||
if let path = NSURL(string: file)?.path {
|
urls.count > 0 {
|
||||||
return path
|
return urls
|
||||||
}
|
.map { $0.isFileURL ? Ghostty.Shell.escape($0.path) : $0.absoluteString }
|
||||||
|
.joined(separator: " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.string(forType: .string)
|
return self.string(forType: .string)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The pasteboard for the Ghostty enum type.
|
||||||
|
static func ghostty(_ clipboard: ghostty_clipboard_e) -> NSPasteboard? {
|
||||||
|
switch (clipboard) {
|
||||||
|
case GHOSTTY_CLIPBOARD_STANDARD:
|
||||||
|
return Self.general
|
||||||
|
|
||||||
|
case GHOSTTY_CLIPBOARD_SELECTION:
|
||||||
|
return Self.ghosttySelection
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
9
macos/Sources/Helpers/Weak.swift
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/// A wrapper that holds a weak reference to an object. This lets us create native containers
|
||||||
|
/// of weak references.
|
||||||
|
class Weak<T: AnyObject> {
|
||||||
|
weak var value: T?
|
||||||
|
|
||||||
|
init(_ value: T) {
|
||||||
|
self.value = value
|
||||||
|
}
|
||||||
|
}
|
@ -1,63 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# Nothing in this script should fail.
|
|
||||||
set -e
|
|
||||||
|
|
||||||
CACHE_HASH_FILE="$(realpath "$(dirname "$0")/../zigCacheHash.nix")"
|
|
||||||
|
|
||||||
help() {
|
|
||||||
echo ""
|
|
||||||
echo "To fix, please (manually) re-run the script from the repository root,"
|
|
||||||
echo "commit, and push the update:"
|
|
||||||
echo ""
|
|
||||||
echo " ./nix/build-support/check-zig-cache-hash.sh --update"
|
|
||||||
echo " git add nix/zigCacheHash.nix"
|
|
||||||
echo " git commit -m \"nix: update Zig cache hash\""
|
|
||||||
echo " git push"
|
|
||||||
echo ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if [ -f "${CACHE_HASH_FILE}" ]; then
|
|
||||||
OLD_CACHE_HASH="$(nix eval --raw --file "${CACHE_HASH_FILE}")"
|
|
||||||
elif [ "$1" != "--update" ]; then
|
|
||||||
echo -e "\nERROR: Zig cache hash file missing."
|
|
||||||
help
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
ZIG_GLOBAL_CACHE_DIR="$(mktemp --directory --suffix nix-zig-cache)"
|
|
||||||
export ZIG_GLOBAL_CACHE_DIR
|
|
||||||
|
|
||||||
# This is not 100% necessary in CI but is helpful when running locally to keep
|
|
||||||
# a local workstation clean.
|
|
||||||
trap 'rm -rf "${ZIG_GLOBAL_CACHE_DIR}"' EXIT
|
|
||||||
|
|
||||||
# Run Zig and download the cache to the temporary directory.
|
|
||||||
|
|
||||||
sh ./nix/build-support/fetch-zig-cache.sh
|
|
||||||
|
|
||||||
# Now, calculate the hash.
|
|
||||||
ZIG_CACHE_HASH="sha256-$(nix-hash --type sha256 --to-base64 "$(nix-hash --type sha256 "${ZIG_GLOBAL_CACHE_DIR}")")"
|
|
||||||
|
|
||||||
if [ "${OLD_CACHE_HASH}" == "${ZIG_CACHE_HASH}" ]; then
|
|
||||||
echo -e "\nOK: Zig cache store hash unchanged."
|
|
||||||
exit 0
|
|
||||||
elif [ "$1" != "--update" ]; then
|
|
||||||
echo -e "\nERROR: The Zig cache store hash has updated."
|
|
||||||
echo ""
|
|
||||||
echo " * Old hash: ${OLD_CACHE_HASH}"
|
|
||||||
echo " * New hash: ${ZIG_CACHE_HASH}"
|
|
||||||
help
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo -e "\nNew Zig cache store hash: ${ZIG_CACHE_HASH}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Write out the cache file
|
|
||||||
cat > "${CACHE_HASH_FILE}" <<EOS
|
|
||||||
# This file is auto-generated! check build-support/check-zig-cache-hash.sh for
|
|
||||||
# more details.
|
|
||||||
"${ZIG_CACHE_HASH}"
|
|
||||||
EOS
|
|
||||||
|
|
||||||
echo -e "\nOK: Wrote new hash to file: ${CACHE_HASH_FILE}"
|
|
78
nix/build-support/check-zig-cache.sh
Executable file
@ -0,0 +1,78 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# This script checks if the build.zig.zon.nix file is up-to-date.
|
||||||
|
# If the `--update` flag is passed, it will update all necessary
|
||||||
|
# files to be up to date.
|
||||||
|
#
|
||||||
|
# The files owned by this are:
|
||||||
|
#
|
||||||
|
# - build.zig.zon.nix
|
||||||
|
# - build.zig.zon.txt
|
||||||
|
# - build.zig.zon2json-lock
|
||||||
|
#
|
||||||
|
# All of these are auto-generated and should not be edited manually.
|
||||||
|
|
||||||
|
# Nothing in this script should fail.
|
||||||
|
set -e
|
||||||
|
|
||||||
|
WORK_DIR=$(mktemp -d)
|
||||||
|
|
||||||
|
if [[ ! "$WORK_DIR" || ! -d "$WORK_DIR" ]]; then
|
||||||
|
echo "could not create temp dir"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
function cleanup {
|
||||||
|
rm -rf "$WORK_DIR"
|
||||||
|
}
|
||||||
|
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
help() {
|
||||||
|
echo ""
|
||||||
|
echo "To fix, please (manually) re-run the script from the repository root,"
|
||||||
|
echo "commit, and submit a PR with the update:"
|
||||||
|
echo ""
|
||||||
|
echo " ./nix/build-support/check-zig-cache-hash.sh --update"
|
||||||
|
echo " git add build.zig.zon.nix"
|
||||||
|
echo " git commit -m \"nix: update build.zig.zon.nix\""
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
ROOT="$(realpath "$(dirname "$0")/../../")"
|
||||||
|
BUILD_ZIG_ZON="$ROOT/build.zig.zon"
|
||||||
|
BUILD_ZIG_ZON_LOCK="$ROOT/build.zig.zon2json-lock"
|
||||||
|
BUILD_ZIG_ZON_NIX="$ROOT/build.zig.zon.nix"
|
||||||
|
BUILD_ZIG_ZON_TXT="$ROOT/build.zig.zon.txt"
|
||||||
|
|
||||||
|
if [ -f "${BUILD_ZIG_ZON_NIX}" ]; then
|
||||||
|
OLD_HASH=$(sha512sum "${BUILD_ZIG_ZON_NIX}" | awk '{print $1}')
|
||||||
|
elif [ "$1" != "--update" ]; then
|
||||||
|
echo -e "\nERROR: build.zig.zon.nix missing."
|
||||||
|
help
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f "$BUILD_ZIG_ZON_LOCK"
|
||||||
|
zon2nix "$BUILD_ZIG_ZON" > "$WORK_DIR/build.zig.zon.nix"
|
||||||
|
alejandra --quiet "$WORK_DIR/build.zig.zon.nix"
|
||||||
|
|
||||||
|
NEW_HASH=$(sha512sum "$WORK_DIR/build.zig.zon.nix" | awk '{print $1}')
|
||||||
|
|
||||||
|
if [ "${OLD_HASH}" == "${NEW_HASH}" ]; then
|
||||||
|
echo -e "\nOK: build.zig.zon.nix unchanged."
|
||||||
|
exit 0
|
||||||
|
elif [ "$1" != "--update" ]; then
|
||||||
|
echo -e "\nERROR: build.zig.zon.nix needs to be updated."
|
||||||
|
echo ""
|
||||||
|
echo " * Old hash: ${OLD_HASH}"
|
||||||
|
echo " * New hash: ${NEW_HASH}"
|
||||||
|
help
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
jq -r '.[] .url' "$BUILD_ZIG_ZON_LOCK" | sort > "$BUILD_ZIG_ZON_TXT"
|
||||||
|
mv "$WORK_DIR/build.zig.zon.nix" "$BUILD_ZIG_ZON_NIX"
|
||||||
|
echo -e "\nOK: build.zig.zon.nix updated."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
@ -1,32 +1,13 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
set -e
|
# NOTE THIS IS A TEMPORARY SCRIPT TO SUPPORT PACKAGE MAINTAINERS.
|
||||||
|
|
||||||
# Because Zig does not fetch recursive dependencies when you run `zig build
|
|
||||||
# --fetch` (see https://github.com/ziglang/zig/issues/20976) we need to do some
|
|
||||||
# extra work to fetch everything that we actually need to build without Internet
|
|
||||||
# access (such as when building a Nix package).
|
|
||||||
#
|
#
|
||||||
# An example of this happening:
|
# A future Zig version will hopefully fix the issue where
|
||||||
|
# `zig build --fetch` doesn't fetch transitive dependencies[1]. When that
|
||||||
|
# is resolved, we won't need any special machinery for the general use case
|
||||||
|
# at all and packagers can just use `zig build --fetch`.
|
||||||
#
|
#
|
||||||
# error: builder for '/nix/store/cx8qcwrhjmjxik2547fw99v5j6np5san-ghostty-0.1.0.drv' failed with exit code 1;
|
# [1]: https://github.com/ziglang/zig/issues/20976
|
||||||
# la/build/tmp.xgHOheUF7V/p/12208cfdda4d5fdbc81b0c44b82e4d6dba2d4a86bff644a153e026fdfc80f8469133/build.zig.zon:7:20: error: unable to discover remote git server capabilities: TemporaryNameServerFailure
|
|
||||||
# > .url = "git+https://github.com/zigimg/zigimg#3a667bdb3d7f0955a5a51c8468eac83210c1439e",
|
|
||||||
# > ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# > /build/tmp.xgHOheUF7V/p/12208cfdda4d5fdbc81b0c44b82e4d6dba2d4a86bff644a153e026fdfc80f8469133/build.zig.zon:16:20: error: unable to discover remote git server capabilities: TemporaryNameServerFailure
|
|
||||||
# > .url = "git+https://github.com/mitchellh/libxev#f6a672a78436d8efee1aa847a43a900ad773618b",
|
|
||||||
# > ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# >
|
|
||||||
# For full logs, run 'nix log /nix/store/cx8qcwrhjmjxik2547fw99v5j6np5san-ghostty-0.1.0.drv'.
|
|
||||||
#
|
|
||||||
# To update this script, add any failing URLs with a line like this:
|
|
||||||
#
|
|
||||||
# zig fetch <url>
|
|
||||||
#
|
|
||||||
# Periodically old URLs may need to be cleaned out.
|
|
||||||
#
|
|
||||||
# Hopefully when the Zig issue is fixed this script can be eliminated in favor
|
|
||||||
# of a plain `zig build --fetch`.
|
|
||||||
|
|
||||||
if [ -z ${ZIG_GLOBAL_CACHE_DIR+x} ]
|
if [ -z ${ZIG_GLOBAL_CACHE_DIR+x} ]
|
||||||
then
|
then
|
||||||
@ -34,6 +15,13 @@ then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
zig build --fetch
|
# Go through each line of our build.zig.zon.txt and fetch it.
|
||||||
zig fetch git+https://github.com/zigimg/zigimg#3a667bdb3d7f0955a5a51c8468eac83210c1439e
|
SCRIPT_PATH="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
|
||||||
zig fetch git+https://github.com/mitchellh/libxev#f6a672a78436d8efee1aa847a43a900ad773618b
|
ZON_TXT_FILE="$SCRIPT_PATH/../../build.zig.zon.txt"
|
||||||
|
while IFS= read -r url; do
|
||||||
|
echo "Fetching: $url"
|
||||||
|
zig fetch "$url" >/dev/null 2>&1 || {
|
||||||
|
echo "Failed to fetch: $url" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
done < "$ZON_TXT_FILE"
|
||||||
|
30
nix/build-support/update-mirror.sh
Executable file
@ -0,0 +1,30 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# This script generates a directory that can be uploaded to blob
|
||||||
|
# storage to mirror our dependencies. The dependencies are unmodified
|
||||||
|
# so their checksum and content hashes will match.
|
||||||
|
|
||||||
|
set -e # Exit immediately if a command exits with a non-zero status
|
||||||
|
|
||||||
|
SCRIPT_PATH="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
|
||||||
|
INPUT_FILE="$SCRIPT_PATH/../../build.zig.zon2json-lock"
|
||||||
|
OUTPUT_DIR="blob"
|
||||||
|
|
||||||
|
# Ensure the output directory exists
|
||||||
|
mkdir -p "$OUTPUT_DIR"
|
||||||
|
|
||||||
|
# Use jq to iterate over the JSON and download files
|
||||||
|
jq -r 'to_entries[] | "\(.key) \(.value.name) \(.value.url)"' "$INPUT_FILE" | while read -r key name url; do
|
||||||
|
# Skip URLs that don't start with http(s). They aren't necessary for
|
||||||
|
# our mirror.
|
||||||
|
if ! echo "$url" | grep -Eq "^https?://"; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract the file extension from the URL
|
||||||
|
extension=$(echo "$url" | grep -oE '\.[a-z0-9]+(\.[a-z0-9]+)?$')
|
||||||
|
|
||||||
|
filename="${name}-${key}${extension}"
|
||||||
|
echo "$url -> $filename"
|
||||||
|
curl -L -o "$OUTPUT_DIR/$filename" "$url"
|
||||||
|
done
|
@ -14,6 +14,7 @@
|
|||||||
python3,
|
python3,
|
||||||
qemu,
|
qemu,
|
||||||
scdoc,
|
scdoc,
|
||||||
|
snapcraft,
|
||||||
valgrind,
|
valgrind,
|
||||||
#, vulkan-loader # unused
|
#, vulkan-loader # unused
|
||||||
vttest,
|
vttest,
|
||||||
@ -30,7 +31,9 @@
|
|||||||
glib,
|
glib,
|
||||||
glslang,
|
glslang,
|
||||||
gtk4,
|
gtk4,
|
||||||
|
gobject-introspection,
|
||||||
libadwaita,
|
libadwaita,
|
||||||
|
blueprint-compiler,
|
||||||
adwaita-icon-theme,
|
adwaita-icon-theme,
|
||||||
hicolor-icon-theme,
|
hicolor-icon-theme,
|
||||||
harfbuzz,
|
harfbuzz,
|
||||||
@ -47,6 +50,7 @@
|
|||||||
simdutf,
|
simdutf,
|
||||||
zlib,
|
zlib,
|
||||||
alejandra,
|
alejandra,
|
||||||
|
jq,
|
||||||
minisign,
|
minisign,
|
||||||
pandoc,
|
pandoc,
|
||||||
hyperfine,
|
hyperfine,
|
||||||
@ -54,6 +58,8 @@
|
|||||||
wayland,
|
wayland,
|
||||||
wayland-scanner,
|
wayland-scanner,
|
||||||
wayland-protocols,
|
wayland-protocols,
|
||||||
|
zig2nix,
|
||||||
|
system,
|
||||||
}: let
|
}: let
|
||||||
# See package.nix. Keep in sync.
|
# See package.nix. Keep in sync.
|
||||||
rpathLibs =
|
rpathLibs =
|
||||||
@ -83,6 +89,7 @@
|
|||||||
libadwaita
|
libadwaita
|
||||||
gtk4
|
gtk4
|
||||||
glib
|
glib
|
||||||
|
gobject-introspection
|
||||||
wayland
|
wayland
|
||||||
];
|
];
|
||||||
in
|
in
|
||||||
@ -92,6 +99,7 @@ in
|
|||||||
packages =
|
packages =
|
||||||
[
|
[
|
||||||
# For builds
|
# For builds
|
||||||
|
jq
|
||||||
llvmPackages_latest.llvm
|
llvmPackages_latest.llvm
|
||||||
minisign
|
minisign
|
||||||
ncurses
|
ncurses
|
||||||
@ -100,6 +108,7 @@ in
|
|||||||
scdoc
|
scdoc
|
||||||
zig
|
zig
|
||||||
zip
|
zip
|
||||||
|
zig2nix.packages.${system}.zon2nix
|
||||||
|
|
||||||
# For web and wasm stuff
|
# For web and wasm stuff
|
||||||
nodejs
|
nodejs
|
||||||
@ -129,6 +138,7 @@ in
|
|||||||
qemu
|
qemu
|
||||||
|
|
||||||
gdb
|
gdb
|
||||||
|
snapcraft
|
||||||
valgrind
|
valgrind
|
||||||
wraptest
|
wraptest
|
||||||
|
|
||||||
@ -154,9 +164,11 @@ in
|
|||||||
libXrandr
|
libXrandr
|
||||||
|
|
||||||
# Only needed for GTK builds
|
# Only needed for GTK builds
|
||||||
|
blueprint-compiler
|
||||||
libadwaita
|
libadwaita
|
||||||
gtk4
|
gtk4
|
||||||
glib
|
glib
|
||||||
|
gobject-introspection
|
||||||
wayland
|
wayland
|
||||||
wayland-scanner
|
wayland-scanner
|
||||||
wayland-protocols
|
wayland-protocols
|
||||||
|
119
nix/package.nix
@ -2,6 +2,7 @@
|
|||||||
lib,
|
lib,
|
||||||
stdenv,
|
stdenv,
|
||||||
bzip2,
|
bzip2,
|
||||||
|
callPackage,
|
||||||
expat,
|
expat,
|
||||||
fontconfig,
|
fontconfig,
|
||||||
freetype,
|
freetype,
|
||||||
@ -12,7 +13,9 @@
|
|||||||
libGL,
|
libGL,
|
||||||
glib,
|
glib,
|
||||||
gtk4,
|
gtk4,
|
||||||
|
gobject-introspection,
|
||||||
libadwaita,
|
libadwaita,
|
||||||
|
blueprint-compiler,
|
||||||
wrapGAppsHook4,
|
wrapGAppsHook4,
|
||||||
gsettings-desktop-schemas,
|
gsettings-desktop-schemas,
|
||||||
git,
|
git,
|
||||||
@ -40,82 +43,36 @@
|
|||||||
# ultimately acted on and has made its way to a nixpkgs implementation, this
|
# ultimately acted on and has made its way to a nixpkgs implementation, this
|
||||||
# can probably be removed in favor of that.
|
# can probably be removed in favor of that.
|
||||||
zig_hook = zig_0_13.hook.overrideAttrs {
|
zig_hook = zig_0_13.hook.overrideAttrs {
|
||||||
zig_default_flags = "-Dcpu=baseline -Doptimize=${optimize}";
|
zig_default_flags = "-Dcpu=baseline -Doptimize=${optimize} --color off";
|
||||||
};
|
|
||||||
|
|
||||||
# We limit source like this to try and reduce the amount of rebuilds as possible
|
|
||||||
# thus we only provide the source that is needed for the build
|
|
||||||
#
|
|
||||||
# NOTE: as of the current moment only linux files are provided,
|
|
||||||
# since darwin support is not finished
|
|
||||||
src = lib.fileset.toSource {
|
|
||||||
root = ../.;
|
|
||||||
fileset = lib.fileset.intersection (lib.fileset.fromSource (lib.sources.cleanSource ../.)) (
|
|
||||||
lib.fileset.unions [
|
|
||||||
../dist/linux
|
|
||||||
../images
|
|
||||||
../include
|
|
||||||
../pkg
|
|
||||||
../src
|
|
||||||
../vendor
|
|
||||||
../build.zig
|
|
||||||
../build.zig.zon
|
|
||||||
./build-support/fetch-zig-cache.sh
|
|
||||||
]
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
# This hash is the computation of the zigCache fixed-output derivation. This
|
|
||||||
# allows us to use remote package dependencies without breaking the sandbox.
|
|
||||||
#
|
|
||||||
# This will need updating whenever dependencies get updated (e.g. changes are
|
|
||||||
# made to zig.build.zon). If you see that the main build is trying to reach
|
|
||||||
# out to the internet and failing, this is likely the cause. Change this
|
|
||||||
# value back to lib.fakeHash, and re-run. The build failure should emit the
|
|
||||||
# updated hash, which of course, should be validated before updating here.
|
|
||||||
#
|
|
||||||
# (It's also possible that you might see a hash mismatch - without the
|
|
||||||
# network errors - if you don't have a previous instance of the cache
|
|
||||||
# derivation in your store already. If so, just update the value as above.)
|
|
||||||
zigCacheHash = import ./zigCacheHash.nix;
|
|
||||||
|
|
||||||
zigCache = stdenv.mkDerivation {
|
|
||||||
inherit src;
|
|
||||||
name = "ghostty-cache";
|
|
||||||
nativeBuildInputs = [
|
|
||||||
git
|
|
||||||
zig_hook
|
|
||||||
];
|
|
||||||
|
|
||||||
dontConfigure = true;
|
|
||||||
dontUseZigBuild = true;
|
|
||||||
dontUseZigInstall = true;
|
|
||||||
dontFixup = true;
|
|
||||||
|
|
||||||
buildPhase = ''
|
|
||||||
runHook preBuild
|
|
||||||
|
|
||||||
sh ./nix/build-support/fetch-zig-cache.sh
|
|
||||||
|
|
||||||
runHook postBuild
|
|
||||||
'';
|
|
||||||
|
|
||||||
installPhase = ''
|
|
||||||
runHook preInstall
|
|
||||||
|
|
||||||
cp -r --reflink=auto $ZIG_GLOBAL_CACHE_DIR $out
|
|
||||||
|
|
||||||
runHook postInstall
|
|
||||||
'';
|
|
||||||
|
|
||||||
outputHashMode = "recursive";
|
|
||||||
outputHash = zigCacheHash;
|
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
stdenv.mkDerivation (finalAttrs: {
|
stdenv.mkDerivation (finalAttrs: {
|
||||||
pname = "ghostty";
|
pname = "ghostty";
|
||||||
version = "1.0.2";
|
version = "1.1.3";
|
||||||
inherit src;
|
|
||||||
|
# We limit source like this to try and reduce the amount of rebuilds as possible
|
||||||
|
# thus we only provide the source that is needed for the build
|
||||||
|
#
|
||||||
|
# NOTE: as of the current moment only linux files are provided,
|
||||||
|
# since darwin support is not finished
|
||||||
|
src = lib.fileset.toSource {
|
||||||
|
root = ../.;
|
||||||
|
fileset = lib.fileset.intersection (lib.fileset.fromSource (lib.sources.cleanSource ../.)) (
|
||||||
|
lib.fileset.unions [
|
||||||
|
../dist/linux
|
||||||
|
../images
|
||||||
|
../include
|
||||||
|
../pkg
|
||||||
|
../src
|
||||||
|
../vendor
|
||||||
|
../build.zig
|
||||||
|
../build.zig.zon
|
||||||
|
../build.zig.zon.nix
|
||||||
|
]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
deps = callPackage ../build.zig.zon.nix {name = "ghostty-cache-${finalAttrs.version}";};
|
||||||
|
|
||||||
nativeBuildInputs =
|
nativeBuildInputs =
|
||||||
[
|
[
|
||||||
@ -124,7 +81,9 @@ in
|
|||||||
pandoc
|
pandoc
|
||||||
pkg-config
|
pkg-config
|
||||||
zig_hook
|
zig_hook
|
||||||
|
gobject-introspection
|
||||||
wrapGAppsHook4
|
wrapGAppsHook4
|
||||||
|
blueprint-compiler
|
||||||
]
|
]
|
||||||
++ lib.optionals enableWayland [
|
++ lib.optionals enableWayland [
|
||||||
wayland-scanner
|
wayland-scanner
|
||||||
@ -162,13 +121,13 @@ in
|
|||||||
|
|
||||||
dontConfigure = true;
|
dontConfigure = true;
|
||||||
|
|
||||||
zigBuildFlags = "-Dversion-string=${finalAttrs.version}-${revision}-nix -Dgtk-x11=${lib.boolToString enableX11} -Dgtk-wayland=${lib.boolToString enableWayland}";
|
zigBuildFlags = [
|
||||||
|
"--system"
|
||||||
preBuild = ''
|
"${finalAttrs.deps}"
|
||||||
rm -rf $ZIG_GLOBAL_CACHE_DIR
|
"-Dversion-string=${finalAttrs.version}-${revision}-nix"
|
||||||
cp -r --reflink=auto ${zigCache} $ZIG_GLOBAL_CACHE_DIR
|
"-Dgtk-x11=${lib.boolToString enableX11}"
|
||||||
chmod u+rwX -R $ZIG_GLOBAL_CACHE_DIR
|
"-Dgtk-wayland=${lib.boolToString enableWayland}"
|
||||||
'';
|
];
|
||||||
|
|
||||||
outputs = [
|
outputs = [
|
||||||
"out"
|
"out"
|
||||||
@ -202,7 +161,7 @@ in
|
|||||||
'';
|
'';
|
||||||
|
|
||||||
meta = {
|
meta = {
|
||||||
homepage = "https://github.com/ghostty-org/ghostty";
|
homepage = "https://ghostty.org";
|
||||||
license = lib.licenses.mit;
|
license = lib.licenses.mit;
|
||||||
platforms = [
|
platforms = [
|
||||||
"x86_64-linux"
|
"x86_64-linux"
|
||||||
|
18
nix/vm/common-cinnamon.nix
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{...}: {
|
||||||
|
imports = [
|
||||||
|
./common.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
services.xserver = {
|
||||||
|
displayManager = {
|
||||||
|
lightdm = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
desktopManager = {
|
||||||
|
cinnamon = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
136
nix/vm/common-gnome.nix
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
|
imports = [
|
||||||
|
./common.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
services.xserver = {
|
||||||
|
displayManager = {
|
||||||
|
gdm = {
|
||||||
|
enable = true;
|
||||||
|
autoSuspend = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
desktopManager = {
|
||||||
|
gnome = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
environment.systemPackages = [
|
||||||
|
pkgs.gnomeExtensions.no-overview
|
||||||
|
];
|
||||||
|
|
||||||
|
environment.gnome.excludePackages = with pkgs; [
|
||||||
|
atomix
|
||||||
|
baobab
|
||||||
|
cheese
|
||||||
|
epiphany
|
||||||
|
evince
|
||||||
|
file-roller
|
||||||
|
geary
|
||||||
|
gnome-backgrounds
|
||||||
|
gnome-calculator
|
||||||
|
gnome-calendar
|
||||||
|
gnome-clocks
|
||||||
|
gnome-connections
|
||||||
|
gnome-contacts
|
||||||
|
gnome-disk-utility
|
||||||
|
gnome-extension-manager
|
||||||
|
gnome-logs
|
||||||
|
gnome-maps
|
||||||
|
gnome-music
|
||||||
|
gnome-photos
|
||||||
|
gnome-software
|
||||||
|
gnome-system-monitor
|
||||||
|
gnome-text-editor
|
||||||
|
gnome-themes-extra
|
||||||
|
gnome-tour
|
||||||
|
gnome-user-docs
|
||||||
|
gnome-weather
|
||||||
|
hitori
|
||||||
|
iagno
|
||||||
|
loupe
|
||||||
|
nautilus
|
||||||
|
orca
|
||||||
|
seahorse
|
||||||
|
simple-scan
|
||||||
|
snapshot
|
||||||
|
sushi
|
||||||
|
tali
|
||||||
|
totem
|
||||||
|
yelp
|
||||||
|
];
|
||||||
|
|
||||||
|
programs.dconf = {
|
||||||
|
enable = true;
|
||||||
|
profiles.user.databases = [
|
||||||
|
{
|
||||||
|
settings = with lib.gvariant; {
|
||||||
|
"org/gnome/desktop/background" = {
|
||||||
|
picture-uri = "file://${pkgs.ghostty}/share/icons/hicolor/512x512/apps/com.mitchellh.ghostty.png";
|
||||||
|
picture-uri-dark = "file://${pkgs.ghostty}/share/icons/hicolor/512x512/apps/com.mitchellh.ghostty.png";
|
||||||
|
picture-options = "centered";
|
||||||
|
primary-color = "#000000000000";
|
||||||
|
secondary-color = "#000000000000";
|
||||||
|
};
|
||||||
|
"org/gnome/desktop/interface" = {
|
||||||
|
color-scheme = "prefer-dark";
|
||||||
|
};
|
||||||
|
"org/gnome/desktop/notifications" = {
|
||||||
|
show-in-lock-screen = false;
|
||||||
|
};
|
||||||
|
"org/gnome/desktop/screensaver" = {
|
||||||
|
lock-enabled = false;
|
||||||
|
picture-uri = "file://${pkgs.ghostty}/share/icons/hicolor/512x512/apps/com.mitchellh.ghostty.png";
|
||||||
|
picture-options = "centered";
|
||||||
|
primary-color = "#000000000000";
|
||||||
|
secondary-color = "#000000000000";
|
||||||
|
};
|
||||||
|
"org/gnome/desktop/session" = {
|
||||||
|
idle-delay = mkUint32 0;
|
||||||
|
};
|
||||||
|
"org/gnome/shell" = {
|
||||||
|
disable-user-extensions = false;
|
||||||
|
enabled-extensions = builtins.map (x: x.extensionUuid) (
|
||||||
|
lib.filter (p: p ? extensionUuid) config.environment.systemPackages
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
programs.geary.enable = false;
|
||||||
|
|
||||||
|
services.gnome = {
|
||||||
|
gnome-browser-connector.enable = false;
|
||||||
|
gnome-initial-setup.enable = false;
|
||||||
|
gnome-online-accounts.enable = false;
|
||||||
|
gnome-remote-desktop.enable = false;
|
||||||
|
rygel.enable = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
system.activationScripts = {
|
||||||
|
face = {
|
||||||
|
text = ''
|
||||||
|
mkdir -p /var/lib/AccountsService/{icons,users}
|
||||||
|
|
||||||
|
cp ${pkgs.ghostty}/share/icons/hicolor/1024x1024/apps/com.mitchellh.ghostty.png /var/lib/AccountsService/icons/ghostty
|
||||||
|
|
||||||
|
echo -e "[User]\nIcon=/var/lib/AccountsService/icons/ghostty\n" > /var/lib/AccountsService/users/ghostty
|
||||||
|
|
||||||
|
chown root:root /var/lib/AccountsService/users/ghostty
|
||||||
|
chmod 0600 /var/lib/AccountsService/users/ghostty
|
||||||
|
|
||||||
|
chown root:root /var/lib/AccountsService/icons/ghostty
|
||||||
|
chmod 0444 /var/lib/AccountsService/icons/ghostty
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
21
nix/vm/common-plasma6.nix
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{...}: {
|
||||||
|
imports = [
|
||||||
|
./common.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
services = {
|
||||||
|
displayManager = {
|
||||||
|
sddm = {
|
||||||
|
enable = true;
|
||||||
|
wayland = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
desktopManager = {
|
||||||
|
plasma6 = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
18
nix/vm/common-xfce.nix
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{...}: {
|
||||||
|
imports = [
|
||||||
|
./common.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
services.xserver = {
|
||||||
|
displayManager = {
|
||||||
|
lightdm = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
desktopManager = {
|
||||||
|
xfce = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
83
nix/vm/common.nix
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
{pkgs, ...}: {
|
||||||
|
boot.loader.systemd-boot.enable = true;
|
||||||
|
boot.loader.efi.canTouchEfiVariables = true;
|
||||||
|
|
||||||
|
documentation.nixos.enable = false;
|
||||||
|
|
||||||
|
networking.hostName = "ghostty";
|
||||||
|
networking.domain = "mitchellh.com";
|
||||||
|
|
||||||
|
virtualisation.vmVariant = {
|
||||||
|
virtualisation.memorySize = 2048;
|
||||||
|
};
|
||||||
|
|
||||||
|
nix = {
|
||||||
|
settings = {
|
||||||
|
trusted-users = [
|
||||||
|
"root"
|
||||||
|
"ghostty"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
extraOptions = ''
|
||||||
|
experimental-features = nix-command flakes
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
users.mutableUsers = false;
|
||||||
|
|
||||||
|
users.groups.ghostty = {};
|
||||||
|
|
||||||
|
users.users.ghostty = {
|
||||||
|
description = "Ghostty";
|
||||||
|
group = "ghostty";
|
||||||
|
extraGroups = ["wheel"];
|
||||||
|
isNormalUser = true;
|
||||||
|
initialPassword = "ghostty";
|
||||||
|
};
|
||||||
|
|
||||||
|
environment.etc = {
|
||||||
|
"xdg/autostart/com.mitchellh.ghostty.desktop" = {
|
||||||
|
source = "${pkgs.ghostty}/share/applications/com.mitchellh.ghostty.desktop";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
environment.systemPackages = [
|
||||||
|
pkgs.kitty
|
||||||
|
pkgs.fish
|
||||||
|
pkgs.ghostty
|
||||||
|
pkgs.helix
|
||||||
|
pkgs.neovim
|
||||||
|
pkgs.xterm
|
||||||
|
pkgs.zsh
|
||||||
|
];
|
||||||
|
|
||||||
|
security.polkit = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
services.dbus = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
services.displayManager = {
|
||||||
|
autoLogin = {
|
||||||
|
user = "ghostty";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.libinput = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
services.qemuGuest = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
services.spice-vdagentd = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
services.xserver = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
}
|
12
nix/vm/create-cinnamon.nix
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
system,
|
||||||
|
nixpkgs,
|
||||||
|
overlay,
|
||||||
|
module,
|
||||||
|
uid ? 1000,
|
||||||
|
gid ? 1000,
|
||||||
|
}:
|
||||||
|
import ./create.nix {
|
||||||
|
inherit system nixpkgs overlay module uid gid;
|
||||||
|
common = ./common-cinnamon.nix;
|
||||||
|
}
|
12
nix/vm/create-gnome.nix
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
system,
|
||||||
|
nixpkgs,
|
||||||
|
overlay,
|
||||||
|
module,
|
||||||
|
uid ? 1000,
|
||||||
|
gid ? 1000,
|
||||||
|
}:
|
||||||
|
import ./create.nix {
|
||||||
|
inherit system nixpkgs overlay module uid gid;
|
||||||
|
common = ./common-gnome.nix;
|
||||||
|
}
|
12
nix/vm/create-plasma6.nix
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
system,
|
||||||
|
nixpkgs,
|
||||||
|
overlay,
|
||||||
|
module,
|
||||||
|
uid ? 1000,
|
||||||
|
gid ? 1000,
|
||||||
|
}:
|
||||||
|
import ./create.nix {
|
||||||
|
inherit system nixpkgs overlay module uid gid;
|
||||||
|
common = ./common-plasma6.nix;
|
||||||
|
}
|
12
nix/vm/create-xfce.nix
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
system,
|
||||||
|
nixpkgs,
|
||||||
|
overlay,
|
||||||
|
module,
|
||||||
|
uid ? 1000,
|
||||||
|
gid ? 1000,
|
||||||
|
}:
|
||||||
|
import ./create.nix {
|
||||||
|
inherit system nixpkgs overlay module uid gid;
|
||||||
|
common = ./common-xfce.nix;
|
||||||
|
}
|
42
nix/vm/create.nix
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
system,
|
||||||
|
nixpkgs,
|
||||||
|
overlay,
|
||||||
|
module,
|
||||||
|
common ? ./common.nix,
|
||||||
|
uid ? 1000,
|
||||||
|
gid ? 1000,
|
||||||
|
}: let
|
||||||
|
pkgs = import nixpkgs {
|
||||||
|
inherit system;
|
||||||
|
overlays = [
|
||||||
|
overlay
|
||||||
|
];
|
||||||
|
};
|
||||||
|
in
|
||||||
|
nixpkgs.lib.nixosSystem {
|
||||||
|
system = builtins.replaceStrings ["darwin"] ["linux"] system;
|
||||||
|
modules = [
|
||||||
|
{
|
||||||
|
virtualisation.vmVariant = {
|
||||||
|
virtualisation.host.pkgs = pkgs;
|
||||||
|
};
|
||||||
|
|
||||||
|
nixpkgs.overlays = [
|
||||||
|
overlay
|
||||||
|
];
|
||||||
|
|
||||||
|
users.groups.ghostty = {
|
||||||
|
gid = gid;
|
||||||
|
};
|
||||||
|
|
||||||
|
users.users.ghostty = {
|
||||||
|
uid = uid;
|
||||||
|
};
|
||||||
|
|
||||||
|
system.stateVersion = nixpkgs.lib.trivial.release;
|
||||||
|
}
|
||||||
|
common
|
||||||
|
module
|
||||||
|
];
|
||||||
|
}
|
7
nix/vm/wayland-cinnamon.nix
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{...}: {
|
||||||
|
imports = [
|
||||||
|
./common-cinnamon.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
services.displayManager.defaultSession = "cinnamon-wayland";
|
||||||
|
}
|
9
nix/vm/wayland-gnome.nix
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{...}: {
|
||||||
|
imports = [
|
||||||
|
./common-gnome.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
services.displayManager = {
|
||||||
|
defaultSession = "gnome";
|
||||||
|
};
|
||||||
|
}
|
6
nix/vm/wayland-plasma6.nix
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{...}: {
|
||||||
|
imports = [
|
||||||
|
./common-plasma6.nix
|
||||||
|
];
|
||||||
|
services.displayManager.defaultSession = "plasma";
|
||||||
|
}
|
7
nix/vm/x11-cinnamon.nix
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{...}: {
|
||||||
|
imports = [
|
||||||
|
./common-cinnamon.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
services.displayManager.defaultSession = "cinnamon";
|
||||||
|
}
|
9
nix/vm/x11-gnome.nix
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{...}: {
|
||||||
|
imports = [
|
||||||
|
./common-gnome.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
services.displayManager = {
|
||||||
|
defaultSession = "gnome-xorg";
|
||||||
|
};
|
||||||
|
}
|
6
nix/vm/x11-plasma6.nix
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{...}: {
|
||||||
|
imports = [
|
||||||
|
./common-plasma6.nix
|
||||||
|
];
|
||||||
|
services.displayManager.defaultSession = "plasmax11";
|
||||||
|
}
|
7
nix/vm/x11-xfce.nix
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{...}: {
|
||||||
|
imports = [
|
||||||
|
./common-xfce.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
services.displayManager.defaultSession = "xfce";
|
||||||
|
}
|
@ -1,3 +1,3 @@
|
|||||||
# This file is auto-generated! check build-support/check-zig-cache-hash.sh for
|
# This file is auto-generated! check build-support/check-zig-cache-hash.sh for
|
||||||
# more details.
|
# more details.
|
||||||
"sha256-PnfSy793kcVt85q47kWR0xkivXoMOZAAmuUyKO9vqAI="
|
"sha256-S8kS+gO17dl9LJGKL1+kgDUre+vPTmdTvXzgc585Fl8="
|
||||||
|
@ -6,7 +6,8 @@
|
|||||||
// This should be kept in sync with the submodule in the cimgui source
|
// This should be kept in sync with the submodule in the cimgui source
|
||||||
// code in ./vendor/ to be safe that they're compatible.
|
// code in ./vendor/ to be safe that they're compatible.
|
||||||
.imgui = .{
|
.imgui = .{
|
||||||
.url = "https://github.com/ocornut/imgui/archive/e391fe2e66eb1c96b1624ae8444dc64c23146ef4.tar.gz",
|
// ocornut/imgui
|
||||||
|
.url = "https://deps.files.ghostty.org/imgui-1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402.tar.gz",
|
||||||
.hash = "1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402",
|
.hash = "1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -3,8 +3,9 @@
|
|||||||
.version = "2.13.2",
|
.version = "2.13.2",
|
||||||
.paths = .{""},
|
.paths = .{""},
|
||||||
.dependencies = .{
|
.dependencies = .{
|
||||||
|
// freetype/freetype
|
||||||
.freetype = .{
|
.freetype = .{
|
||||||
.url = "https://github.com/freetype/freetype/archive/refs/tags/VER-2-13-2.tar.gz",
|
.url = "https://deps.files.ghostty.org/freetype-1220b81f6ecfb3fd222f76cf9106fecfa6554ab07ec7fdc4124b9bb063ae2adf969d.tar.gz",
|
||||||
.hash = "1220b81f6ecfb3fd222f76cf9106fecfa6554ab07ec7fdc4124b9bb063ae2adf969d",
|
.hash = "1220b81f6ecfb3fd222f76cf9106fecfa6554ab07ec7fdc4124b9bb063ae2adf969d",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -3,8 +3,9 @@
|
|||||||
.version = "14.2.0",
|
.version = "14.2.0",
|
||||||
.paths = .{""},
|
.paths = .{""},
|
||||||
.dependencies = .{
|
.dependencies = .{
|
||||||
|
// KhronosGroup/glslang
|
||||||
.glslang = .{
|
.glslang = .{
|
||||||
.url = "https://github.com/KhronosGroup/glslang/archive/refs/tags/14.2.0.tar.gz",
|
.url = "https://deps.files.ghostty.org/glslang-12201278a1a05c0ce0b6eb6026c65cd3e9247aa041b1c260324bf29cee559dd23ba1.tar.gz",
|
||||||
.hash = "12201278a1a05c0ce0b6eb6026c65cd3e9247aa041b1c260324bf29cee559dd23ba1",
|
.hash = "12201278a1a05c0ce0b6eb6026c65cd3e9247aa041b1c260324bf29cee559dd23ba1",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -3,8 +3,9 @@
|
|||||||
.version = "8.4.0",
|
.version = "8.4.0",
|
||||||
.paths = .{""},
|
.paths = .{""},
|
||||||
.dependencies = .{
|
.dependencies = .{
|
||||||
|
// harfbuzz/harfbuzz
|
||||||
.harfbuzz = .{
|
.harfbuzz = .{
|
||||||
.url = "https://github.com/harfbuzz/harfbuzz/archive/refs/tags/8.4.0.tar.gz",
|
.url = "https://deps.files.ghostty.org/harfbuzz-1220b8588f106c996af10249bfa092c6fb2f35fbacb1505ef477a0b04a7dd1063122.tar.gz",
|
||||||
.hash = "1220b8588f106c996af10249bfa092c6fb2f35fbacb1505ef477a0b04a7dd1063122",
|
.hash = "1220b8588f106c996af10249bfa092c6fb2f35fbacb1505ef477a0b04a7dd1063122",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -3,8 +3,9 @@
|
|||||||
.version = "1.1.0",
|
.version = "1.1.0",
|
||||||
.paths = .{""},
|
.paths = .{""},
|
||||||
.dependencies = .{
|
.dependencies = .{
|
||||||
|
// google/highway
|
||||||
.highway = .{
|
.highway = .{
|
||||||
.url = "https://github.com/google/highway/archive/refs/tags/1.1.0.tar.gz",
|
.url = "https://deps.files.ghostty.org/highway-12205c83b8311a24b1d5ae6d21640df04f4b0726e314337c043cde1432758cbe165b.tar.gz",
|
||||||
.hash = "12205c83b8311a24b1d5ae6d21640df04f4b0726e314337c043cde1432758cbe165b",
|
.hash = "12205c83b8311a24b1d5ae6d21640df04f4b0726e314337c043cde1432758cbe165b",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -3,8 +3,9 @@
|
|||||||
.version = "1.6.43",
|
.version = "1.6.43",
|
||||||
.paths = .{""},
|
.paths = .{""},
|
||||||
.dependencies = .{
|
.dependencies = .{
|
||||||
|
// glennrp/libpng
|
||||||
.libpng = .{
|
.libpng = .{
|
||||||
.url = "https://github.com/glennrp/libpng/archive/refs/tags/v1.6.43.tar.gz",
|
.url = "https://deps.files.ghostty.org/libpng-1220aa013f0c83da3fb64ea6d327f9173fa008d10e28bc9349eac3463457723b1c66.tar.gz",
|
||||||
.hash = "1220aa013f0c83da3fb64ea6d327f9173fa008d10e28bc9349eac3463457723b1c66",
|
.hash = "1220aa013f0c83da3fb64ea6d327f9173fa008d10e28bc9349eac3463457723b1c66",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -18,9 +18,72 @@ pub const ColorSpace = opaque {
|
|||||||
) orelse Allocator.Error.OutOfMemory;
|
) orelse Allocator.Error.OutOfMemory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn createNamed(name: Name) Allocator.Error!*ColorSpace {
|
||||||
|
return @as(
|
||||||
|
?*ColorSpace,
|
||||||
|
@ptrFromInt(@intFromPtr(c.CGColorSpaceCreateWithName(name.cfstring()))),
|
||||||
|
) orelse Allocator.Error.OutOfMemory;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn release(self: *ColorSpace) void {
|
pub fn release(self: *ColorSpace) void {
|
||||||
c.CGColorSpaceRelease(@ptrCast(self));
|
c.CGColorSpaceRelease(@ptrCast(self));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const Name = enum {
|
||||||
|
/// This color space uses the DCI P3 primaries, a D65 white point, and
|
||||||
|
/// the sRGB transfer function.
|
||||||
|
displayP3,
|
||||||
|
/// The Display P3 color space with a linear transfer function and
|
||||||
|
/// extended-range values.
|
||||||
|
extendedLinearDisplayP3,
|
||||||
|
/// The sRGB colorimetry and non-linear transfer function are specified
|
||||||
|
/// in IEC 61966-2-1.
|
||||||
|
sRGB,
|
||||||
|
/// This color space has the same colorimetry as `sRGB`, but uses a
|
||||||
|
/// linear transfer function.
|
||||||
|
linearSRGB,
|
||||||
|
/// This color space has the same colorimetry as `sRGB`, but you can
|
||||||
|
/// encode component values below `0.0` and above `1.0`. Negative values
|
||||||
|
/// are encoded as the signed reflection of the original encoding
|
||||||
|
/// function, as shown in the formula below:
|
||||||
|
/// ```
|
||||||
|
/// extendedTransferFunction(x) = sign(x) * sRGBTransferFunction(abs(x))
|
||||||
|
/// ```
|
||||||
|
extendedSRGB,
|
||||||
|
/// This color space has the same colorimetry as `sRGB`; in addition,
|
||||||
|
/// you may encode component values below `0.0` and above `1.0`.
|
||||||
|
extendedLinearSRGB,
|
||||||
|
/// ...
|
||||||
|
genericGrayGamma2_2,
|
||||||
|
/// ...
|
||||||
|
linearGray,
|
||||||
|
/// This color space has the same colorimetry as `genericGrayGamma2_2`,
|
||||||
|
/// but you can encode component values below `0.0` and above `1.0`.
|
||||||
|
/// Negative values are encoded as the signed reflection of the
|
||||||
|
/// original encoding function, as shown in the formula below:
|
||||||
|
/// ```
|
||||||
|
/// extendedGrayTransferFunction(x) = sign(x) * gamma22Function(abs(x))
|
||||||
|
/// ```
|
||||||
|
extendedGray,
|
||||||
|
/// This color space has the same colorimetry as `linearGray`; in
|
||||||
|
/// addition, you may encode component values below `0.0` and above `1.0`.
|
||||||
|
extendedLinearGray,
|
||||||
|
|
||||||
|
fn cfstring(self: Name) c.CFStringRef {
|
||||||
|
return switch (self) {
|
||||||
|
.displayP3 => c.kCGColorSpaceDisplayP3,
|
||||||
|
.extendedLinearDisplayP3 => c.kCGColorSpaceExtendedLinearDisplayP3,
|
||||||
|
.sRGB => c.kCGColorSpaceSRGB,
|
||||||
|
.extendedSRGB => c.kCGColorSpaceExtendedSRGB,
|
||||||
|
.linearSRGB => c.kCGColorSpaceLinearSRGB,
|
||||||
|
.extendedLinearSRGB => c.kCGColorSpaceExtendedLinearSRGB,
|
||||||
|
.genericGrayGamma2_2 => c.kCGColorSpaceGenericGrayGamma2_2,
|
||||||
|
.extendedGray => c.kCGColorSpaceExtendedGray,
|
||||||
|
.linearGray => c.kCGColorSpaceLinearGray,
|
||||||
|
.extendedLinearGray => c.kCGColorSpaceExtendedLinearGray,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
test {
|
test {
|
||||||
|