mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 16:56:09 +03:00
Merge pull request #1653 from mitchellh/coretext
Enable CoreText font shaper for macOS by default
This commit is contained in:
143
.github/workflows/release-pr.yml
vendored
143
.github/workflows/release-pr.yml
vendored
@ -149,3 +149,146 @@ jobs:
|
|||||||
r2-bucket: ghostty-pr
|
r2-bucket: ghostty-pr
|
||||||
source-dir: blob
|
source-dir: blob
|
||||||
destination-dir: ./
|
destination-dir: ./
|
||||||
|
|
||||||
|
build-macos-debug:
|
||||||
|
runs-on: namespace-profile-ghostty-macos
|
||||||
|
timeout-minutes: 90
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
# Important so that build number generation works
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||||
|
- uses: cachix/install-nix-action@v26
|
||||||
|
with:
|
||||||
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
|
- uses: cachix/cachix-action@v14
|
||||||
|
with:
|
||||||
|
name: ghostty
|
||||||
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
|
|
||||||
|
# Setup Sparkle
|
||||||
|
- name: Setup Sparkle
|
||||||
|
env:
|
||||||
|
SPARKLE_VERSION: 2.5.1
|
||||||
|
run: |
|
||||||
|
mkdir -p .action/sparkle
|
||||||
|
cd .action/sparkle
|
||||||
|
curl -L https://github.com/sparkle-project/Sparkle/releases/download/${SPARKLE_VERSION}/Sparkle-for-Swift-Package-Manager.zip > sparkle.zip
|
||||||
|
unzip sparkle.zip
|
||||||
|
echo "$(pwd)/bin" >> $GITHUB_PATH
|
||||||
|
|
||||||
|
# Load Build Number
|
||||||
|
- name: Build Number
|
||||||
|
run: |
|
||||||
|
echo "GHOSTTY_BUILD=$(git rev-list --count head)" >> $GITHUB_ENV
|
||||||
|
echo "GHOSTTY_COMMIT=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
# GhosttyKit is the framework that is built from Zig for our native
|
||||||
|
# Mac app to access. Build this in release mode.
|
||||||
|
- name: Build GhosttyKit
|
||||||
|
run: nix develop -c zig build -Dstatic=true -Doptimize=Debug
|
||||||
|
|
||||||
|
# The native app is built with native XCode tooling. This also does
|
||||||
|
# codesigning. IMPORTANT: this must NOT run in a Nix environment.
|
||||||
|
# Nix breaks xcodebuild so this has to be run outside.
|
||||||
|
- name: Build Ghostty.app
|
||||||
|
run: cd macos && xcodebuild -target Ghostty -configuration Release
|
||||||
|
|
||||||
|
# We inject the "build number" as simply the number of commits since HEAD.
|
||||||
|
# This will be a monotonically always increasing build number that we use.
|
||||||
|
- name: Update Info.plist
|
||||||
|
env:
|
||||||
|
SPARKLE_KEY_PUB: ${{ secrets.PROD_MACOS_SPARKLE_KEY_PUB }}
|
||||||
|
run: |
|
||||||
|
# Version Info
|
||||||
|
/usr/libexec/PlistBuddy -c "Set :GhosttyCommit $GHOSTTY_COMMIT" "macos/build/Release/Ghostty.app/Contents/Info.plist"
|
||||||
|
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $GHOSTTY_BUILD" "macos/build/Release/Ghostty.app/Contents/Info.plist"
|
||||||
|
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $GHOSTTY_COMMIT" "macos/build/Release/Ghostty.app/Contents/Info.plist"
|
||||||
|
|
||||||
|
# Updater
|
||||||
|
/usr/libexec/PlistBuddy -c "Set :SUPublicEDKey $SPARKLE_KEY_PUB" "macos/build/Release/Ghostty.app/Contents/Info.plist"
|
||||||
|
|
||||||
|
- name: Codesign app bundle
|
||||||
|
env:
|
||||||
|
MACOS_CERTIFICATE: ${{ secrets.PROD_MACOS_CERTIFICATE }}
|
||||||
|
MACOS_CERTIFICATE_PWD: ${{ secrets.PROD_MACOS_CERTIFICATE_PWD }}
|
||||||
|
MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }}
|
||||||
|
MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }}
|
||||||
|
run: |
|
||||||
|
# Turn our base64-encoded certificate back to a regular .p12 file
|
||||||
|
echo $MACOS_CERTIFICATE | base64 --decode > certificate.p12
|
||||||
|
|
||||||
|
# We need to create a new keychain, otherwise using the certificate will prompt
|
||||||
|
# with a UI dialog asking for the certificate password, which we can't
|
||||||
|
# use in a headless CI environment
|
||||||
|
security create-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain
|
||||||
|
security default-keychain -s build.keychain
|
||||||
|
security unlock-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain
|
||||||
|
security import certificate.p12 -k build.keychain -P "$MACOS_CERTIFICATE_PWD" -T /usr/bin/codesign
|
||||||
|
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MACOS_CI_KEYCHAIN_PWD" build.keychain
|
||||||
|
|
||||||
|
# Codesign Sparkle. Some notes here:
|
||||||
|
# - The XPC services aren't used since we don't sandbox Ghostty,
|
||||||
|
# but since they're part of the build, they still need to be
|
||||||
|
# codesigned.
|
||||||
|
# - The binaries in the "Versions" folders need to NOT be symlinks.
|
||||||
|
/usr/bin/codesign --verbose -f -s "$MACOS_CERTIFICATE_NAME" -o runtime "macos/build/Release/Ghostty.app/Contents/Frameworks/Sparkle.framework/Versions/B/XPCServices/Downloader.xpc"
|
||||||
|
/usr/bin/codesign --verbose -f -s "$MACOS_CERTIFICATE_NAME" -o runtime "macos/build/Release/Ghostty.app/Contents/Frameworks/Sparkle.framework/Versions/B/XPCServices/Installer.xpc"
|
||||||
|
/usr/bin/codesign --verbose -f -s "$MACOS_CERTIFICATE_NAME" -o runtime "macos/build/Release/Ghostty.app/Contents/Frameworks/Sparkle.framework/Versions/B/Autoupdate"
|
||||||
|
/usr/bin/codesign --verbose -f -s "$MACOS_CERTIFICATE_NAME" -o runtime "macos/build/Release/Ghostty.app/Contents/Frameworks/Sparkle.framework/Versions/B/Updater.app"
|
||||||
|
/usr/bin/codesign --verbose -f -s "$MACOS_CERTIFICATE_NAME" -o runtime "macos/build/Release/Ghostty.app/Contents/Frameworks/Sparkle.framework"
|
||||||
|
|
||||||
|
# Codesign the app bundle
|
||||||
|
/usr/bin/codesign --verbose -f -s "$MACOS_CERTIFICATE_NAME" -o runtime --entitlements "macos/Ghostty.entitlements" macos/build/Release/Ghostty.app
|
||||||
|
|
||||||
|
- name: "Notarize app bundle"
|
||||||
|
env:
|
||||||
|
PROD_MACOS_NOTARIZATION_APPLE_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_APPLE_ID }}
|
||||||
|
PROD_MACOS_NOTARIZATION_TEAM_ID: ${{ secrets.PROD_MACOS_NOTARIZATION_TEAM_ID }}
|
||||||
|
PROD_MACOS_NOTARIZATION_PWD: ${{ secrets.PROD_MACOS_NOTARIZATION_PWD }}
|
||||||
|
run: |
|
||||||
|
# Store the notarization credentials so that we can prevent a UI password dialog
|
||||||
|
# from blocking the CI
|
||||||
|
echo "Create keychain profile"
|
||||||
|
xcrun notarytool store-credentials "notarytool-profile" --apple-id "$PROD_MACOS_NOTARIZATION_APPLE_ID" --team-id "$PROD_MACOS_NOTARIZATION_TEAM_ID" --password "$PROD_MACOS_NOTARIZATION_PWD"
|
||||||
|
|
||||||
|
# We can't notarize an app bundle directly, but we need to compress it as an archive.
|
||||||
|
# Therefore, we create a zip file containing our app bundle, so that we can send it to the
|
||||||
|
# notarization service
|
||||||
|
echo "Creating temp notarization archive"
|
||||||
|
ditto -c -k --keepParent "macos/build/Release/Ghostty.app" "notarization.zip"
|
||||||
|
|
||||||
|
# Here we send the notarization request to the Apple's Notarization service, waiting for the result.
|
||||||
|
# This typically takes a few seconds inside a CI environment, but it might take more depending on the App
|
||||||
|
# characteristics. Visit the Notarization docs for more information and strategies on how to optimize it if
|
||||||
|
# you're curious
|
||||||
|
echo "Notarize app"
|
||||||
|
xcrun notarytool submit "notarization.zip" --keychain-profile "notarytool-profile" --wait
|
||||||
|
|
||||||
|
# Finally, we need to "attach the staple" to our executable, which will allow our app to be
|
||||||
|
# validated by macOS even when an internet connection is not available.
|
||||||
|
echo "Attach staple"
|
||||||
|
xcrun stapler staple "macos/build/Release/Ghostty.app"
|
||||||
|
|
||||||
|
# Zip up the app
|
||||||
|
- name: Zip App
|
||||||
|
run: cd macos/build/Release && zip -9 -r --symlinks ../../../ghostty-macos-universal-debug.zip Ghostty.app
|
||||||
|
|
||||||
|
# Update Blob Storage
|
||||||
|
- name: Prep R2 Storage
|
||||||
|
run: |
|
||||||
|
mkdir blob
|
||||||
|
mkdir -p blob/${GHOSTTY_BUILD}
|
||||||
|
cp ghostty-macos-universal-debug.zip blob/${GHOSTTY_BUILD}/ghostty-macos-universal-debug.zip
|
||||||
|
- name: Upload to R2
|
||||||
|
uses: ryand56/r2-upload-action@latest
|
||||||
|
with:
|
||||||
|
r2-account-id: ${{ secrets.CF_R2_PR_ACCOUNT_ID }}
|
||||||
|
r2-access-key-id: ${{ secrets.CF_R2_PR_AWS_KEY }}
|
||||||
|
r2-secret-access-key: ${{ secrets.CF_R2_PR_SECRET_KEY }}
|
||||||
|
r2-bucket: ghostty-pr
|
||||||
|
source-dir: blob
|
||||||
|
destination-dir: ./
|
||||||
|
19
.github/workflows/test.yml
vendored
19
.github/workflows/test.yml
vendored
@ -277,6 +277,25 @@ jobs:
|
|||||||
- name: Test Dynamic Build
|
- name: Test Dynamic Build
|
||||||
run: nix develop -c zig build -Dstatic=false
|
run: nix develop -c zig build -Dstatic=false
|
||||||
|
|
||||||
|
test-macos:
|
||||||
|
runs-on: namespace-profile-ghostty-macos
|
||||||
|
needs: test
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||||
|
- uses: cachix/install-nix-action@v26
|
||||||
|
with:
|
||||||
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
|
- uses: cachix/cachix-action@v14
|
||||||
|
with:
|
||||||
|
name: ghostty
|
||||||
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
|
|
||||||
|
- name: test
|
||||||
|
run: nix develop -c zig build test
|
||||||
|
|
||||||
prettier:
|
prettier:
|
||||||
runs-on: namespace-profile-ghostty-sm
|
runs-on: namespace-profile-ghostty-sm
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
|
19
build.zig
19
build.zig
@ -1055,11 +1055,14 @@ fn addDeps(
|
|||||||
"fontconfig",
|
"fontconfig",
|
||||||
fontconfig_dep.module("fontconfig"),
|
fontconfig_dep.module("fontconfig"),
|
||||||
);
|
);
|
||||||
|
if (config.font_backend.hasHarfbuzz()) step.root_module.addImport(
|
||||||
|
"harfbuzz",
|
||||||
|
harfbuzz_dep.module("harfbuzz"),
|
||||||
|
);
|
||||||
step.root_module.addImport("oniguruma", oniguruma_dep.module("oniguruma"));
|
step.root_module.addImport("oniguruma", oniguruma_dep.module("oniguruma"));
|
||||||
step.root_module.addImport("freetype", freetype_dep.module("freetype"));
|
step.root_module.addImport("freetype", freetype_dep.module("freetype"));
|
||||||
step.root_module.addImport("glslang", glslang_dep.module("glslang"));
|
step.root_module.addImport("glslang", glslang_dep.module("glslang"));
|
||||||
step.root_module.addImport("spirv_cross", spirv_cross_dep.module("spirv_cross"));
|
step.root_module.addImport("spirv_cross", spirv_cross_dep.module("spirv_cross"));
|
||||||
step.root_module.addImport("harfbuzz", harfbuzz_dep.module("harfbuzz"));
|
|
||||||
step.root_module.addImport("xev", libxev_dep.module("xev"));
|
step.root_module.addImport("xev", libxev_dep.module("xev"));
|
||||||
step.root_module.addImport("opengl", opengl_dep.module("opengl"));
|
step.root_module.addImport("opengl", opengl_dep.module("opengl"));
|
||||||
step.root_module.addImport("pixman", pixman_dep.module("pixman"));
|
step.root_module.addImport("pixman", pixman_dep.module("pixman"));
|
||||||
@ -1110,7 +1113,6 @@ fn addDeps(
|
|||||||
step.addIncludePath(freetype_dep.path(""));
|
step.addIncludePath(freetype_dep.path(""));
|
||||||
step.linkSystemLibrary2("bzip2", dynamic_link_opts);
|
step.linkSystemLibrary2("bzip2", dynamic_link_opts);
|
||||||
step.linkSystemLibrary2("freetype2", dynamic_link_opts);
|
step.linkSystemLibrary2("freetype2", dynamic_link_opts);
|
||||||
step.linkSystemLibrary2("harfbuzz", dynamic_link_opts);
|
|
||||||
step.linkSystemLibrary2("libpng", dynamic_link_opts);
|
step.linkSystemLibrary2("libpng", dynamic_link_opts);
|
||||||
step.linkSystemLibrary2("oniguruma", dynamic_link_opts);
|
step.linkSystemLibrary2("oniguruma", dynamic_link_opts);
|
||||||
step.linkSystemLibrary2("pixman-1", dynamic_link_opts);
|
step.linkSystemLibrary2("pixman-1", dynamic_link_opts);
|
||||||
@ -1119,6 +1121,9 @@ fn addDeps(
|
|||||||
if (config.font_backend.hasFontconfig()) {
|
if (config.font_backend.hasFontconfig()) {
|
||||||
step.linkSystemLibrary2("fontconfig", dynamic_link_opts);
|
step.linkSystemLibrary2("fontconfig", dynamic_link_opts);
|
||||||
}
|
}
|
||||||
|
if (config.font_backend.hasHarfbuzz()) {
|
||||||
|
step.linkSystemLibrary2("harfbuzz", dynamic_link_opts);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Other dependencies, we may dynamically link
|
// Other dependencies, we may dynamically link
|
||||||
@ -1136,14 +1141,16 @@ fn addDeps(
|
|||||||
step.linkLibrary(freetype_dep.artifact("freetype"));
|
step.linkLibrary(freetype_dep.artifact("freetype"));
|
||||||
try static_libs.append(freetype_dep.artifact("freetype").getEmittedBin());
|
try static_libs.append(freetype_dep.artifact("freetype").getEmittedBin());
|
||||||
|
|
||||||
// Harfbuzz
|
|
||||||
step.linkLibrary(harfbuzz_dep.artifact("harfbuzz"));
|
|
||||||
try static_libs.append(harfbuzz_dep.artifact("harfbuzz").getEmittedBin());
|
|
||||||
|
|
||||||
// Pixman
|
// Pixman
|
||||||
step.linkLibrary(pixman_dep.artifact("pixman"));
|
step.linkLibrary(pixman_dep.artifact("pixman"));
|
||||||
try static_libs.append(pixman_dep.artifact("pixman").getEmittedBin());
|
try static_libs.append(pixman_dep.artifact("pixman").getEmittedBin());
|
||||||
|
|
||||||
|
// Harfbuzz
|
||||||
|
if (config.font_backend.hasHarfbuzz()) {
|
||||||
|
step.linkLibrary(harfbuzz_dep.artifact("harfbuzz"));
|
||||||
|
try static_libs.append(harfbuzz_dep.artifact("harfbuzz").getEmittedBin());
|
||||||
|
}
|
||||||
|
|
||||||
// Only Linux gets fontconfig
|
// Only Linux gets fontconfig
|
||||||
if (config.font_backend.hasFontconfig()) {
|
if (config.font_backend.hasFontconfig()) {
|
||||||
// Fontconfig
|
// Fontconfig
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const base = @import("base.zig");
|
const base = @import("base.zig");
|
||||||
|
const c = @import("c.zig");
|
||||||
const cftype = @import("type.zig");
|
const cftype = @import("type.zig");
|
||||||
const ComparisonResult = base.ComparisonResult;
|
const ComparisonResult = base.ComparisonResult;
|
||||||
const Range = base.Range;
|
const Range = base.Range;
|
||||||
@ -42,6 +43,14 @@ pub const Array = opaque {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub const MutableArray = opaque {
|
pub const MutableArray = opaque {
|
||||||
|
pub fn create() Allocator.Error!*MutableArray {
|
||||||
|
return CFArrayCreateMutable(
|
||||||
|
null,
|
||||||
|
0,
|
||||||
|
&c.kCFTypeArrayCallBacks,
|
||||||
|
) orelse error.OutOfMemory;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn createCopy(array: *Array) Allocator.Error!*MutableArray {
|
pub fn createCopy(array: *Array) Allocator.Error!*MutableArray {
|
||||||
return CFArrayCreateMutableCopy(
|
return CFArrayCreateMutableCopy(
|
||||||
null,
|
null,
|
||||||
@ -54,6 +63,18 @@ pub const MutableArray = opaque {
|
|||||||
cftype.CFRelease(self);
|
cftype.CFRelease(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn appendValue(
|
||||||
|
self: *MutableArray,
|
||||||
|
comptime Elem: type,
|
||||||
|
value: *const Elem,
|
||||||
|
) void {
|
||||||
|
CFArrayAppendValue(self, @constCast(@ptrCast(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn removeValue(self: *MutableArray, idx: usize) void {
|
||||||
|
CFArrayRemoveValueAtIndex(self, idx);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn sortValues(
|
pub fn sortValues(
|
||||||
self: *MutableArray,
|
self: *MutableArray,
|
||||||
comptime Elem: type,
|
comptime Elem: type,
|
||||||
@ -73,12 +94,24 @@ pub const MutableArray = opaque {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "c" fn CFArrayCreateMutable(
|
||||||
|
allocator: ?*anyopaque,
|
||||||
|
capacity: usize,
|
||||||
|
callbacks: ?*const anyopaque,
|
||||||
|
) ?*MutableArray;
|
||||||
extern "c" fn CFArrayCreateMutableCopy(
|
extern "c" fn CFArrayCreateMutableCopy(
|
||||||
allocator: ?*anyopaque,
|
allocator: ?*anyopaque,
|
||||||
capacity: usize,
|
capacity: usize,
|
||||||
array: *Array,
|
array: *Array,
|
||||||
) ?*MutableArray;
|
) ?*MutableArray;
|
||||||
|
extern "c" fn CFArrayAppendValue(
|
||||||
|
*MutableArray,
|
||||||
|
*anyopaque,
|
||||||
|
) void;
|
||||||
|
extern "c" fn CFArrayRemoveValueAtIndex(
|
||||||
|
*MutableArray,
|
||||||
|
usize,
|
||||||
|
) void;
|
||||||
extern "c" fn CFArraySortValues(
|
extern "c" fn CFArraySortValues(
|
||||||
array: *MutableArray,
|
array: *MutableArray,
|
||||||
range: Range,
|
range: Range,
|
||||||
|
@ -6,8 +6,8 @@ const c = @import("c.zig");
|
|||||||
|
|
||||||
pub const Dictionary = opaque {
|
pub const Dictionary = opaque {
|
||||||
pub fn create(
|
pub fn create(
|
||||||
keys: ?[]?*const anyopaque,
|
keys: ?[]const ?*const anyopaque,
|
||||||
values: ?[]?*const anyopaque,
|
values: ?[]const ?*const anyopaque,
|
||||||
) Allocator.Error!*Dictionary {
|
) Allocator.Error!*Dictionary {
|
||||||
if (keys != null or values != null) {
|
if (keys != null or values != null) {
|
||||||
assert(keys != null);
|
assert(keys != null);
|
||||||
@ -17,8 +17,8 @@ pub const Dictionary = opaque {
|
|||||||
|
|
||||||
return @as(?*Dictionary, @ptrFromInt(@intFromPtr(c.CFDictionaryCreate(
|
return @as(?*Dictionary, @ptrFromInt(@intFromPtr(c.CFDictionaryCreate(
|
||||||
null,
|
null,
|
||||||
@ptrCast(if (keys) |slice| slice.ptr else null),
|
@constCast(@ptrCast(if (keys) |slice| slice.ptr else null)),
|
||||||
@ptrCast(if (values) |slice| slice.ptr else null),
|
@constCast(@ptrCast(if (values) |slice| slice.ptr else null)),
|
||||||
@intCast(if (keys) |slice| slice.len else 0),
|
@intCast(if (keys) |slice| slice.len else 0),
|
||||||
&c.kCFTypeDictionaryKeyCallBacks,
|
&c.kCFTypeDictionaryKeyCallBacks,
|
||||||
&c.kCFTypeDictionaryValueCallBacks,
|
&c.kCFTypeDictionaryValueCallBacks,
|
||||||
|
@ -13,8 +13,9 @@ pub const Face = struct {
|
|||||||
/// Our font face
|
/// Our font face
|
||||||
font: *macos.text.Font,
|
font: *macos.text.Font,
|
||||||
|
|
||||||
/// Harfbuzz font corresponding to this face.
|
/// Harfbuzz font corresponding to this face. We only use this
|
||||||
hb_font: harfbuzz.Font,
|
/// if we're using Harfbuzz.
|
||||||
|
hb_font: if (harfbuzz_shaper) harfbuzz.Font else void,
|
||||||
|
|
||||||
/// The presentation for this font.
|
/// The presentation for this font.
|
||||||
presentation: font.Presentation,
|
presentation: font.Presentation,
|
||||||
@ -25,6 +26,10 @@ pub const Face = struct {
|
|||||||
/// Set quirks.disableDefaultFontFeatures
|
/// Set quirks.disableDefaultFontFeatures
|
||||||
quirks_disable_default_font_features: bool = false,
|
quirks_disable_default_font_features: bool = false,
|
||||||
|
|
||||||
|
/// True if our build is using Harfbuzz. If we're not, we can avoid
|
||||||
|
/// some Harfbuzz-specific code paths.
|
||||||
|
const harfbuzz_shaper = font.options.backend.hasHarfbuzz();
|
||||||
|
|
||||||
/// The matrix applied to a regular font to auto-italicize it.
|
/// The matrix applied to a regular font to auto-italicize it.
|
||||||
pub const italic_skew = macos.graphics.AffineTransform{
|
pub const italic_skew = macos.graphics.AffineTransform{
|
||||||
.a = 1,
|
.a = 1,
|
||||||
@ -75,10 +80,6 @@ pub const Face = struct {
|
|||||||
/// Initialize a face with a CTFont. This will take ownership over
|
/// Initialize a face with a CTFont. This will take ownership over
|
||||||
/// the CTFont. This does NOT copy or retain the CTFont.
|
/// the CTFont. This does NOT copy or retain the CTFont.
|
||||||
pub fn initFont(ct_font: *macos.text.Font, opts: font.face.Options) !Face {
|
pub fn initFont(ct_font: *macos.text.Font, opts: font.face.Options) !Face {
|
||||||
var hb_font = try harfbuzz.coretext.createFont(ct_font);
|
|
||||||
errdefer hb_font.destroy();
|
|
||||||
hb_font.setScale(opts.size.pixels(), opts.size.pixels());
|
|
||||||
|
|
||||||
const traits = ct_font.getSymbolicTraits();
|
const traits = ct_font.getSymbolicTraits();
|
||||||
const metrics = metrics: {
|
const metrics = metrics: {
|
||||||
var metrics = try calcMetrics(ct_font);
|
var metrics = try calcMetrics(ct_font);
|
||||||
@ -86,6 +87,13 @@ pub const Face = struct {
|
|||||||
break :metrics metrics;
|
break :metrics metrics;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var hb_font = if (comptime harfbuzz_shaper) font: {
|
||||||
|
var hb_font = try harfbuzz.coretext.createFont(ct_font);
|
||||||
|
hb_font.setScale(opts.size.pixels(), opts.size.pixels());
|
||||||
|
break :font hb_font;
|
||||||
|
} else {};
|
||||||
|
errdefer if (comptime harfbuzz_shaper) hb_font.destroy();
|
||||||
|
|
||||||
var result: Face = .{
|
var result: Face = .{
|
||||||
.font = ct_font,
|
.font = ct_font,
|
||||||
.hb_font = hb_font,
|
.hb_font = hb_font,
|
||||||
@ -144,7 +152,7 @@ pub const Face = struct {
|
|||||||
|
|
||||||
pub fn deinit(self: *Face) void {
|
pub fn deinit(self: *Face) void {
|
||||||
self.font.release();
|
self.font.release();
|
||||||
self.hb_font.destroy();
|
if (comptime harfbuzz_shaper) self.hb_font.destroy();
|
||||||
self.* = undefined;
|
self.* = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,6 +115,19 @@ pub const Backend = enum {
|
|||||||
=> false,
|
=> false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn hasHarfbuzz(self: Backend) bool {
|
||||||
|
return switch (self) {
|
||||||
|
.freetype,
|
||||||
|
.fontconfig_freetype,
|
||||||
|
.coretext_freetype,
|
||||||
|
=> true,
|
||||||
|
|
||||||
|
.coretext,
|
||||||
|
.web_canvas,
|
||||||
|
=> false,
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The styles that a family can take.
|
/// The styles that a family can take.
|
||||||
|
BIN
src/font/res/JetBrainsMonoNerdFont-Regular.ttf
Normal file
BIN
src/font/res/JetBrainsMonoNerdFont-Regular.ttf
Normal file
Binary file not shown.
@ -1,7 +1,7 @@
|
|||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
const options = @import("main.zig").options;
|
const options = @import("main.zig").options;
|
||||||
const harfbuzz = @import("shaper/harfbuzz.zig");
|
pub const harfbuzz = @import("shaper/harfbuzz.zig");
|
||||||
const coretext = @import("shaper/coretext.zig");
|
pub const coretext = @import("shaper/coretext.zig");
|
||||||
pub const web_canvas = @import("shaper/web_canvas.zig");
|
pub const web_canvas = @import("shaper/web_canvas.zig");
|
||||||
pub usingnamespace @import("shaper/run.zig");
|
pub usingnamespace @import("shaper/run.zig");
|
||||||
|
|
||||||
@ -10,12 +10,12 @@ pub const Shaper = switch (options.backend) {
|
|||||||
.freetype,
|
.freetype,
|
||||||
.fontconfig_freetype,
|
.fontconfig_freetype,
|
||||||
.coretext_freetype,
|
.coretext_freetype,
|
||||||
.coretext,
|
|
||||||
=> harfbuzz.Shaper,
|
=> harfbuzz.Shaper,
|
||||||
|
|
||||||
// Has missing features, can't be used yet. See the comments in
|
// Note that coretext_freetype cannot use the coretext
|
||||||
// the coretext.zig file for more details.
|
// shaper because the coretext shaper requests CoreText
|
||||||
//.coretext => coretext.Shaper,
|
// font faces.
|
||||||
|
.coretext => coretext.Shaper,
|
||||||
|
|
||||||
.web_canvas => web_canvas.Shaper,
|
.web_canvas => web_canvas.Shaper,
|
||||||
};
|
};
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -12,6 +12,9 @@ pub const fontEmoji = @embedFile("res/NotoColorEmoji.ttf");
|
|||||||
pub const fontEmojiText = @embedFile("res/NotoEmoji-Regular.ttf");
|
pub const fontEmojiText = @embedFile("res/NotoEmoji-Regular.ttf");
|
||||||
pub const fontVariable = @embedFile("res/Lilex-VF.ttf");
|
pub const fontVariable = @embedFile("res/Lilex-VF.ttf");
|
||||||
|
|
||||||
|
/// Font with nerd fonts embedded.
|
||||||
|
pub const fontNerdFont = @embedFile("res/JetBrainsMonoNerdFont-Regular.ttf");
|
||||||
|
|
||||||
/// Cozette is a unique font because it embeds some emoji characters
|
/// Cozette is a unique font because it embeds some emoji characters
|
||||||
/// but has a text presentation.
|
/// but has a text presentation.
|
||||||
pub const fontCozette = @embedFile("res/CozetteVector.ttf");
|
pub const fontCozette = @embedFile("res/CozetteVector.ttf");
|
||||||
|
@ -256,7 +256,9 @@ pub const GlobalState = struct {
|
|||||||
std.log.info("ghostty build optimize={s}", .{build_config.mode_string});
|
std.log.info("ghostty build optimize={s}", .{build_config.mode_string});
|
||||||
std.log.info("runtime={}", .{build_config.app_runtime});
|
std.log.info("runtime={}", .{build_config.app_runtime});
|
||||||
std.log.info("font_backend={}", .{build_config.font_backend});
|
std.log.info("font_backend={}", .{build_config.font_backend});
|
||||||
std.log.info("dependency harfbuzz={s}", .{harfbuzz.versionString()});
|
if (comptime build_config.font_backend.hasHarfbuzz()) {
|
||||||
|
std.log.info("dependency harfbuzz={s}", .{harfbuzz.versionString()});
|
||||||
|
}
|
||||||
if (comptime build_config.font_backend.hasFontconfig()) {
|
if (comptime build_config.font_backend.hasFontconfig()) {
|
||||||
std.log.info("dependency fontconfig={d}", .{fontconfig.version()});
|
std.log.info("dependency fontconfig={d}", .{fontconfig.version()});
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user