chore: import upstream snapshot with attribution
Test / build-libghostty-vt (x86_64-windows-gnu) (push) Has been cancelled
Test / build-libghostty-vt-macos (aarch64-ios) (push) Has been cancelled
Test / build-libghostty-vt-macos (aarch64-macos) (push) Has been cancelled
Test / build-libghostty-vt-macos (x86_64-macos) (push) Has been cancelled
Test / build-nix (namespace-profile-ghostty-md-arm64) (push) Has been cancelled
Test / build-dist (push) Has been cancelled
Test / GTK x11=true wayland=true (push) Has been cancelled
Test / Build -Dsimd=false (push) Has been cancelled
Test / Build -Dsimd=true (push) Has been cancelled
Test / Build -Dsentry=false (push) Has been cancelled
Test / Build -Dsentry=true (push) Has been cancelled
Test / zig-fmt (push) Has been cancelled
Test / GitHub Actions Pins (push) Has been cancelled
Test / prettier (push) Has been cancelled
Test / swiftlint (push) Has been cancelled
Test / alejandra (push) Has been cancelled
Test / typos (push) Has been cancelled
Nix / Required Checks: Nix (push) Has been cancelled
Nix / check-zig-cache-hash (push) Has been cancelled
Test / skip (push) Has been cancelled
Test / Required Checks: Test (push) Has been cancelled
Test / build-bench (push) Has been cancelled
Test / list-examples (push) Has been cancelled
Test / Example ${{ matrix.dir }} (Windows) (push) Has been cancelled
Test / build-cmake (push) Has been cancelled
Test / test-lib-vt-pkgconfig (push) Has been cancelled
Test / build-flatpak (push) Has been cancelled
Test / build-snap (push) Has been cancelled
Test / build-libghostty-vt (aarch64-linux) (push) Has been cancelled
Test / build-libghostty-vt (aarch64-macos) (push) Has been cancelled
Test / build-libghostty-vt (wasm32-freestanding) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-linux) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-linux-musl) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-macos) (push) Has been cancelled
Test / build-libghostty-vt-android (aarch64-linux-android) (push) Has been cancelled
Test / build-libghostty-vt-android (arm-linux-androideabi) (push) Has been cancelled
Test / build-libghostty-vt-android (x86_64-linux-android) (push) Has been cancelled
Test / build-libghostty-vt-windows (push) Has been cancelled
Test / build-libghostty-windows-gnu (push) Has been cancelled
Test / build-linux (namespace-profile-ghostty-md) (push) Has been cancelled
Test / build-linux (namespace-profile-ghostty-md-arm64) (push) Has been cancelled
Test / build-linux-libghostty (push) Has been cancelled
Test / build-nix (namespace-profile-ghostty-md) (push) Has been cancelled
Test / build-dist-lib-vt (push) Has been cancelled
Test / trigger-snap (push) Has been cancelled
Test / trigger-flatpak (push) Has been cancelled
Test / build-macos (push) Has been cancelled
Test / build-macos-freetype (push) Has been cancelled
Test / test (push) Has been cancelled
Test / test-lib-vt (push) Has been cancelled
Test / GTK x11=false wayland=false (push) Has been cancelled
Test / GTK x11=true wayland=false (push) Has been cancelled
Test / GTK x11=false wayland=true (push) Has been cancelled
Test / test-macos (push) Has been cancelled
Test / test-windows (push) Has been cancelled
Test / Build -Di18n=false (push) Has been cancelled
Test / Build -Di18n=true (push) Has been cancelled
Test / Build test/fuzz-libghostty (push) Has been cancelled
Test / shellcheck (push) Has been cancelled
Test / translations (push) Has been cancelled
Test / blueprint-compiler (push) Has been cancelled
Test / Test pkg/wuffs (push) Has been cancelled
Test / Test build on Debian 13 (push) Has been cancelled
Test / valgrind (push) Has been cancelled
Test / Example ${{ matrix.dir }} (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:02:48 +08:00
commit bd44b5aa08
5770 changed files with 506778 additions and 0 deletions
+33
View File
@@ -0,0 +1,33 @@
# Packages
This folder contains packages written for and used by Ghostty that could
potentially be useful for other projects. These are in-tree with Ghostty
because I don't want to maintain them as separate projects (i.e. get
dedicated issues, PRs, etc.). If you want to use them, you can copy and
paste them into your project.
## License
**This license only applies to the contents of the `pkg` folder within
the Ghostty project. This license does not apply to the rest of the
Ghostty project.**
Copyright © 2024 Mitchell Hashimoto, Ghostty contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the “Software”), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+23
View File
@@ -0,0 +1,23 @@
Based on zig-afl-kit: https://github.com/kristoff-it/zig-afl-kit
MIT License
Copyright (c) 2024 Loris Cro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+141
View File
@@ -0,0 +1,141 @@
#include <limits.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
// AFL++ fuzzer harness for Zig fuzz targets.
//
// This file is the C "glue" that connects AFL++'s runtime to Zig-defined
// fuzz test functions. We can't use AFL++'s compiler wrappers (afl-clang,
// afl-gcc) because the code under test is compiled with Zig, so we manually
// expand the AFL macros (__AFL_INIT, __AFL_LOOP, __AFL_FUZZ_INIT, etc.) and
// wire up the sanitizer coverage symbols ourselves.
// To ensure checks are not optimized out it is recommended to disable
// code optimization for the fuzzer harness main()
#pragma clang optimize off
#pragma GCC optimize("O0")
// Zig-exported entry points. zig_fuzz_init() performs one-time setup and
// zig_fuzz_test() runs one fuzz iteration on the given input buffer.
// The Zig object should export these.
void zig_fuzz_init();
void zig_fuzz_test(unsigned char*, size_t);
// Linker-provided symbols marking the boundaries of the __sancov_guards
// section. These must be declared extern so the linker provides the actual
// section boundaries from the instrumented code, rather than creating new
// variables that shadow them. On macOS (Mach-O), the linker uses a different
// naming convention for section boundaries than Linux (ELF), so we use asm
// labels to reference them.
#ifdef __APPLE__
extern uint32_t __start___sancov_guards __asm(
"section$start$__DATA$__sancov_guards");
extern uint32_t __stop___sancov_guards __asm(
"section$end$__DATA$__sancov_guards");
#else
extern uint32_t __start___sancov_guards;
extern uint32_t __stop___sancov_guards;
#endif
// Provided by afl-compiler-rt; initializes the guard array used by
// SanitizerCoverage's trace-pc-guard instrumentation mode.
void __sanitizer_cov_trace_pc_guard_init(uint32_t*, uint32_t*);
// Stubs for sanitizer coverage callbacks that the Zig-compiled code references
// but AFL's runtime (afl-compiler-rt) does not provide. Without these, linking
// would fail with undefined symbol errors.
__attribute__((visibility("default"))) __attribute__((
tls_model("initial-exec"))) _Thread_local uintptr_t __sancov_lowest_stack;
void __sanitizer_cov_trace_pc_indir() {}
void __sanitizer_cov_8bit_counters_init() {}
void __sanitizer_cov_pcs_init() {}
// Manual expansion of __AFL_FUZZ_INIT().
//
// Enables shared-memory fuzzing: AFL++ writes test cases directly into
// shared memory (__afl_fuzz_ptr) instead of passing them via stdin, which
// is much faster. When not running under AFL++ (e.g. standalone execution),
// __afl_fuzz_ptr will be NULL and we fall back to reading from stdin into
// __afl_fuzz_alt (a 1 MB static buffer).
int __afl_sharedmem_fuzzing = 1;
extern __attribute__((visibility("default"))) unsigned int* __afl_fuzz_len;
extern __attribute__((visibility("default"))) unsigned char* __afl_fuzz_ptr;
unsigned char __afl_fuzz_alt[1048576];
unsigned char* __afl_fuzz_alt_ptr = __afl_fuzz_alt;
int main(int argc, char** argv) {
// Tell AFL's coverage runtime about our guard section so it can track
// which edges in the instrumented Zig code have been hit.
__sanitizer_cov_trace_pc_guard_init(&__start___sancov_guards,
&__stop___sancov_guards);
// Manual expansion of __AFL_INIT() — deferred fork server mode.
//
// The magic string "##SIG_AFL_DEFER_FORKSRV##" is embedded in the binary
// so AFL++'s tooling can detect that this harness uses deferred fork
// server initialization. The `volatile` + `used` attributes prevent the
// compiler/linker from stripping it. We then call __afl_manual_init() to
// start the fork server at this point (after our setup) rather than at
// the very beginning of main().
static volatile const char* _A __attribute__((used, unused));
_A = (const char*)"##SIG_AFL_DEFER_FORKSRV##";
#ifdef __APPLE__
__attribute__((visibility("default"))) void _I(void) __asm__(
"___afl_manual_init");
#else
__attribute__((visibility("default"))) void _I(void) __asm__(
"__afl_manual_init");
#endif
_I();
zig_fuzz_init();
// Manual expansion of __AFL_FUZZ_TESTCASE_BUF.
// Use shared memory buffer if available, otherwise fall back to the
// static buffer (for standalone/non-AFL execution).
unsigned char* buf = __afl_fuzz_ptr ? __afl_fuzz_ptr : __afl_fuzz_alt_ptr;
// Manual expansion of __AFL_LOOP(UINT_MAX) — persistent mode loop.
//
// Persistent mode keeps the process alive across many test cases instead
// of fork()'ing for each one, dramatically improving throughput. The magic
// string "##SIG_AFL_PERSISTENT##" signals to AFL++ that this binary
// supports persistent mode. __afl_persistent_loop() returns non-zero
// while there are more inputs to process.
//
// When connected to AFL++, we loop UINT_MAX times (essentially forever,
// AFL will restart us periodically). When running standalone, we loop
// once so the harness can be used for manual testing/reproduction.
while (({
static volatile const char* _B __attribute__((used, unused));
_B = (const char*)"##SIG_AFL_PERSISTENT##";
extern __attribute__((visibility("default"))) int __afl_connected;
#ifdef __APPLE__
__attribute__((visibility("default"))) int _L(unsigned int) __asm__(
"___afl_persistent_loop");
#else
__attribute__((visibility("default"))) int _L(unsigned int) __asm__(
"__afl_persistent_loop");
#endif
_L(__afl_connected ? UINT_MAX : 1);
})) {
// Manual expansion of __AFL_FUZZ_TESTCASE_LEN.
// In shared-memory mode, the length is provided directly by AFL++.
// In standalone mode, we read from stdin into the fallback buffer.
int len =
__afl_fuzz_ptr ? *__afl_fuzz_len
: (*__afl_fuzz_len = read(0, __afl_fuzz_alt_ptr, 1048576)) == 0xffffffff
? 0
: *__afl_fuzz_len;
if (len >= 0) {
zig_fuzz_test(buf, len);
}
}
return 0;
}
+66
View File
@@ -0,0 +1,66 @@
const std = @import("std");
const builtin = @import("builtin");
/// Creates a build step that produces an AFL++-instrumented fuzzing
/// executable.
///
/// Returns a `LazyPath` to the resulting fuzzing executable.
pub fn addInstrumentedExe(
b: *std.Build,
obj: *std.Build.Step.Compile,
) std.Build.LazyPath {
// Force the build system to produce the binary artifact even though we
// only consume the LLVM bitcode below. Without this, the dependency
// tracking doesn't wire up correctly.
_ = obj.getEmittedBin();
const pkg = b.dependencyFromBuildZig(
@This(),
.{},
);
const afl_cc = b.addSystemCommand(&.{
b.findProgram(&.{"afl-cc"}, &.{}) catch
@panic("Could not find 'afl-cc', which is required to build"),
"-O3",
});
if (builtin.target.os.tag.isDarwin()) {
// Apple's newer ld asserts on the custom section names emitted by
// AFL's LLVM instrumentation when linking our Zig-produced bitcode.
// lld links the same inputs without issue.
afl_cc.addArg("-fuse-ld=lld");
}
afl_cc.addArg("-o");
const fuzz_exe = afl_cc.addOutputFileArg(obj.name);
afl_cc.addFileArg(pkg.path("afl.c"));
afl_cc.addFileArg(obj.getEmittedLlvmBc());
return fuzz_exe;
}
/// Creates a run step that invokes `afl-fuzz` with the given instrumented
/// executable, input corpus directory, and output directory.
///
/// Returns the `Run` step so callers can wire it into a build step.
pub fn addFuzzerRun(
b: *std.Build,
exe: std.Build.LazyPath,
corpus_dir: std.Build.LazyPath,
output_dir: std.Build.LazyPath,
) *std.Build.Step.Run {
const run = b.addSystemCommand(&.{
b.findProgram(&.{"afl-fuzz"}, &.{}) catch
@panic("Could not find 'afl-fuzz', which is required to run"),
"-i",
});
run.addDirectoryArg(corpus_dir);
run.addArgs(&.{"-o"});
run.addDirectoryArg(output_dir);
run.addArgs(&.{"--"});
run.addFileArg(exe);
return run;
}
// Required so `zig build` works although it does nothing.
pub fn build(b: *std.Build) !void {
_ = b;
}
+11
View File
@@ -0,0 +1,11 @@
.{
.name = .afl_plus_plus,
.fingerprint = 0x465bc4bebb188f16,
.version = "0.1.0",
.dependencies = .{},
.paths = .{
"build.zig",
"build.zig.zon",
"afl.c",
},
}
+207
View File
@@ -0,0 +1,207 @@
const std = @import("std");
const builtin = @import("builtin");
pub fn build(_: *std.Build) !void {}
// Configure the step to point to the Android NDK for libc and include
// paths. This requires the Android NDK installed in the system and
// setting the appropriate environment variables or installing the NDK
// in the default location.
//
// The environment variables can be set as follows:
// - `ANDROID_NDK_HOME`: Directly points to the NDK path, including the version.
// - `ANDROID_HOME` or `ANDROID_SDK_ROOT`: Points to the Android SDK path;
// latest available NDK will be automatically selected.
//
// NB: This is a workaround until zig natively supports bionic
// cross-compilation (ziglang/zig#23906).
pub fn addPaths(b: *std.Build, step: *std.Build.Step.Compile) !void {
const Cache = struct {
const Key = struct {
arch: std.Target.Cpu.Arch,
abi: std.Target.Abi,
api_level: u32,
};
var map: std.AutoHashMapUnmanaged(Key, ?struct {
libc: std.Build.LazyPath,
cpp_include: std.Build.LazyPath,
lib: std.Build.LazyPath,
}) = .empty;
};
const target = step.rootModuleTarget();
const gop = try Cache.map.getOrPut(b.allocator, .{
.arch = target.cpu.arch,
.abi = target.abi,
.api_level = target.os.version_range.linux.android,
});
if (!gop.found_existing) {
const ndk_path = findNDKPath(b) orelse return error.AndroidNDKNotFound;
const ndk_triple = ndkTriple(target) orelse {
gop.value_ptr.* = null;
return error.AndroidNDKUnsupportedTarget;
};
const host = hostTag() orelse {
gop.value_ptr.* = null;
return error.AndroidNDKUnsupportedHost;
};
const sysroot = b.pathJoin(&.{
ndk_path,
"toolchains",
"llvm",
"prebuilt",
host,
"sysroot",
});
const include_dir = b.pathJoin(&.{
sysroot,
"usr",
"include",
});
const sys_include_dir = b.pathJoin(&.{
sysroot,
"usr",
"include",
ndk_triple,
});
const c_runtime_dir = b.pathJoin(&.{
sysroot,
"usr",
"lib",
ndk_triple,
b.fmt("{d}", .{target.os.version_range.linux.android}),
});
const lib = b.pathJoin(&.{
sysroot,
"usr",
"lib",
ndk_triple,
});
const cpp_include = b.pathJoin(&.{
sysroot,
"usr",
"include",
"c++",
"v1",
});
const libc_txt = b.fmt(
\\include_dir={s}
\\sys_include_dir={s}
\\crt_dir={s}
\\msvc_lib_dir=
\\kernel32_lib_dir=
\\gcc_dir=
, .{ include_dir, sys_include_dir, c_runtime_dir });
const wf = b.addWriteFiles();
const libc_path = wf.add("libc.txt", libc_txt);
gop.value_ptr.* = .{
.libc = libc_path,
.cpp_include = .{ .cwd_relative = cpp_include },
.lib = .{ .cwd_relative = lib },
};
}
const value = gop.value_ptr.* orelse return error.AndroidNDKNotFound;
step.setLibCFile(value.libc);
step.root_module.addSystemIncludePath(value.cpp_include);
step.root_module.addLibraryPath(value.lib);
}
fn findNDKPath(b: *std.Build) ?[]const u8 {
// Check if user has set the environment variable for the NDK path.
if (std.process.getEnvVarOwned(b.allocator, "ANDROID_NDK_HOME") catch null) |value| {
if (value.len == 0) return null;
var dir = std.fs.openDirAbsolute(value, .{}) catch return null;
defer dir.close();
return value;
}
// Check the common environment variables for the Android SDK path and look for the NDK inside it.
inline for (.{ "ANDROID_HOME", "ANDROID_SDK_ROOT" }) |env| {
if (std.process.getEnvVarOwned(b.allocator, env) catch null) |sdk| {
if (sdk.len > 0) {
if (findLatestNDK(b, sdk)) |ndk| return ndk;
}
}
}
// As a fallback, we assume the most common/default SDK path based on the OS.
const home = std.process.getEnvVarOwned(
b.allocator,
if (builtin.os.tag == .windows) "LOCALAPPDATA" else "HOME",
) catch return null;
const default_sdk_path = b.pathJoin(
&.{
home,
switch (builtin.os.tag) {
.linux => "Android/sdk",
.macos => "Library/Android/Sdk",
.windows => "Android/Sdk",
else => return null,
},
},
);
return findLatestNDK(b, default_sdk_path);
}
fn findLatestNDK(b: *std.Build, sdk_path: []const u8) ?[]const u8 {
const ndk_dir = b.pathJoin(&.{ sdk_path, "ndk" });
var dir = std.fs.openDirAbsolute(ndk_dir, .{ .iterate = true }) catch return null;
defer dir.close();
var latest_: ?struct {
name: []const u8,
version: std.SemanticVersion,
} = null;
var iterator = dir.iterate();
while (iterator.next() catch null) |file| {
if (file.kind != .directory) continue;
const version = std.SemanticVersion.parse(file.name) catch continue;
if (latest_) |latest| {
if (version.order(latest.version) != .gt) continue;
}
latest_ = .{
.name = file.name,
.version = version,
};
}
const latest = latest_ orelse return null;
return b.pathJoin(&.{ sdk_path, "ndk", latest.name });
}
fn hostTag() ?[]const u8 {
return switch (builtin.os.tag) {
.linux => "linux-x86_64",
// All darwin hosts use the same prebuilt binaries
// (https://developer.android.com/ndk/guides/other_build_systems).
.macos => "darwin-x86_64",
.windows => "windows-x86_64",
else => null,
};
}
// We must map the target architecture to the corresponding NDK triple following the NDK
// documentation: https://android.googlesource.com/platform/ndk/+/master/docs/BuildSystemMaintainers.md#architectures
fn ndkTriple(target: std.Target) ?[]const u8 {
return switch (target.cpu.arch) {
.arm => "arm-linux-androideabi",
.aarch64 => "aarch64-linux-android",
.x86 => "i686-linux-android",
.x86_64 => "x86_64-linux-android",
else => null,
};
}
+10
View File
@@ -0,0 +1,10 @@
.{
.name = .android_ndk,
.version = "0.0.2",
.fingerprint = 0xee68d62c5a97b68b,
.dependencies = .{},
.paths = .{
"build.zig",
"build.zig.zon",
},
}
+157
View File
@@ -0,0 +1,157 @@
const std = @import("std");
const builtin = @import("builtin");
pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
_ = target;
_ = optimize;
}
/// Setup the step to point to the proper Apple SDK for libc and
/// frameworks. When running on a Darwin host, this uses the native
/// SDK installed on the system via `xcrun`. When cross-compiling from
/// a non-Darwin host, it falls back to Zig's bundled Darwin headers.
pub fn addPaths(
b: *std.Build,
step: *std.Build.Step.Compile,
) !void {
// The cache. This always uses b.allocator and never frees memory
// (which is idiomatic for a Zig build exe). We cache the libc txt
// file we create because it is expensive to generate (subprocesses).
const Cache = struct {
const Key = struct {
arch: std.Target.Cpu.Arch,
os: std.Target.Os.Tag,
abi: std.Target.Abi,
};
const Value = union(enum) {
native: struct {
libc: std.Build.LazyPath,
framework: []const u8,
system_include: []const u8,
library: []const u8,
},
cross: struct {
libc: std.Build.LazyPath,
},
};
var map: std.AutoHashMapUnmanaged(Key, ?Value) = .{};
};
const target = step.rootModuleTarget();
const gop = try Cache.map.getOrPut(b.allocator, .{
.arch = target.cpu.arch,
.os = target.os.tag,
.abi = target.abi,
});
if (!gop.found_existing) init: {
if (comptime builtin.os.tag.isDarwin()) darwin: {
// Detect our SDK using the "findNative" Zig stdlib function.
// This is really important because it forces using `xcrun` to
// find the SDK path.
const libc = std.zig.LibCInstallation.findNative(.{
.allocator = b.allocator,
.target = &step.rootModuleTarget(),
.verbose = false,
}) catch break :darwin;
// Render the file compatible with the `--libc` Zig flag.
var stream: std.io.Writer.Allocating = .init(b.allocator);
defer stream.deinit();
try libc.render(&stream.writer);
// Create a temporary file to store the libc path because
// `--libc` expects a file path.
const wf = b.addWriteFiles();
const path = wf.add("libc.txt", stream.written());
// Determine our framework path. Zig has a bug where it doesn't
// parse this from the libc txt file for `-framework` flags:
// https://github.com/ziglang/zig/issues/24024
const framework_path = framework: {
const down1 = std.fs.path.dirname(libc.sys_include_dir.?).?;
const down2 = std.fs.path.dirname(down1).?;
break :framework try std.fs.path.join(b.allocator, &.{
down2,
"System",
"Library",
"Frameworks",
});
};
const library_path = library: {
const down1 = std.fs.path.dirname(libc.sys_include_dir.?).?;
break :library try std.fs.path.join(b.allocator, &.{
down1,
"lib",
});
};
gop.value_ptr.* = .{ .native = .{
.libc = path,
.framework = framework_path,
.system_include = libc.sys_include_dir.?,
.library = library_path,
} };
break :init;
}
// Cross-compiling to Darwin from a non-Darwin host.
// Zig only bundles macOS headers, so for other Apple platforms
// we leave the value as null to produce a descriptive error.
if (target.os.tag != .macos) {
gop.value_ptr.* = null;
break :init;
}
// Fall back to Zig's bundled Darwin headers for libc resolution.
const zig_lib_path = b.graph.zig_lib_directory.path.?;
const include_dir = b.pathJoin(&.{
zig_lib_path, "libc", "include", "any-macos-any",
});
const wf = b.addWriteFiles();
const path = wf.add("libc.txt", b.fmt(
\\include_dir={s}
\\sys_include_dir={s}
\\crt_dir=
\\msvc_lib_dir=
\\kernel32_lib_dir=
\\gcc_dir=
\\
, .{ include_dir, include_dir }));
gop.value_ptr.* = .{ .cross = .{ .libc = path } };
}
const value = gop.value_ptr.* orelse return switch (target.os.tag) {
// Return a more descriptive error. Before we just returned the
// generic error but this was confusing a lot of community members.
// It costs us nothing in the build script to return something better.
.macos => error.XcodeMacOSSDKNotFound,
.ios => error.XcodeiOSSDKNotFound,
.tvos => error.XcodeTVOSSDKNotFound,
.watchos => error.XcodeWatchOSSDKNotFound,
else => error.XcodeAppleSDKNotFound,
};
switch (value) {
.native => |native| {
step.setLibCFile(native.libc);
// This is only necessary until this bug is fixed:
// https://github.com/ziglang/zig/issues/24024
step.root_module.addSystemFrameworkPath(.{ .cwd_relative = native.framework });
step.root_module.addSystemIncludePath(.{ .cwd_relative = native.system_include });
step.root_module.addLibraryPath(.{ .cwd_relative = native.library });
},
.cross => |cross| {
step.setLibCFile(cross.libc);
},
}
}
+7
View File
@@ -0,0 +1,7 @@
.{
.name = .apple_sdk,
.version = "0.1.0",
.dependencies = .{},
.fingerprint = 0xdde52860f7c464d2,
.paths = .{""},
}
+159
View File
@@ -0,0 +1,159 @@
const std = @import("std");
pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const lib = b.addLibrary(.{
.name = "breakpad",
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
}),
.linkage = .static,
});
lib.linkLibCpp();
lib.addIncludePath(b.path("vendor"));
if (target.result.os.tag.isDarwin()) {
const apple_sdk = @import("apple_sdk");
try apple_sdk.addPaths(b, lib);
}
var flags: std.ArrayList([]const u8) = .empty;
defer flags.deinit(b.allocator);
if (b.lazyDependency("breakpad", .{})) |upstream| {
lib.addIncludePath(upstream.path("src"));
lib.addCSourceFiles(.{
.root = upstream.path(""),
.files = common,
.flags = flags.items,
});
if (target.result.os.tag.isDarwin()) {
lib.addCSourceFiles(.{
.root = upstream.path(""),
.files = common_apple,
.flags = flags.items,
});
lib.addCSourceFiles(.{
.root = upstream.path(""),
.files = client_apple,
.flags = flags.items,
});
switch (target.result.os.tag) {
.macos => {
lib.addCSourceFiles(.{
.root = upstream.path(""),
.files = common_mac,
.flags = flags.items,
});
lib.addCSourceFiles(.{
.root = upstream.path(""),
.files = client_mac,
.flags = flags.items,
});
},
.ios => lib.addCSourceFiles(.{
.root = upstream.path(""),
.files = client_ios,
.flags = flags.items,
}),
else => {},
}
}
if (target.result.os.tag == .linux) {
lib.addCSourceFiles(.{
.root = upstream.path(""),
.files = common_linux,
.flags = flags.items,
});
lib.addCSourceFiles(.{
.root = upstream.path(""),
.files = client_linux,
.flags = flags.items,
});
}
lib.installHeadersDirectory(
upstream.path("src"),
"",
.{ .include_extensions = &.{".h"} },
);
}
b.installArtifact(lib);
}
const common: []const []const u8 = &.{
"src/common/convert_UTF.cc",
"src/common/md5.cc",
"src/common/string_conversion.cc",
};
const common_linux: []const []const u8 = &.{
"src/common/linux/elf_core_dump.cc",
"src/common/linux/elfutils.cc",
"src/common/linux/file_id.cc",
"src/common/linux/guid_creator.cc",
"src/common/linux/linux_libc_support.cc",
"src/common/linux/memory_mapped_file.cc",
"src/common/linux/safe_readlink.cc",
"src/common/linux/scoped_pipe.cc",
"src/common/linux/scoped_tmpfile.cc",
"src/common/linux/breakpad_getcontext.S",
};
const common_apple: []const []const u8 = &.{
"src/common/mac/arch_utilities.cc",
"src/common/mac/file_id.cc",
"src/common/mac/macho_id.cc",
"src/common/mac/macho_utilities.cc",
"src/common/mac/macho_walker.cc",
"src/common/mac/string_utilities.cc",
};
const common_mac: []const []const u8 = &.{
"src/common/mac/MachIPC.mm",
"src/common/mac/bootstrap_compat.cc",
};
const client_linux: []const []const u8 = &.{
"src/client/minidump_file_writer.cc",
"src/client/linux/crash_generation/crash_generation_client.cc",
"src/client/linux/crash_generation/crash_generation_server.cc",
"src/client/linux/dump_writer_common/thread_info.cc",
"src/client/linux/dump_writer_common/ucontext_reader.cc",
"src/client/linux/handler/exception_handler.cc",
"src/client/linux/handler/minidump_descriptor.cc",
"src/client/linux/log/log.cc",
"src/client/linux/microdump_writer/microdump_writer.cc",
"src/client/linux/minidump_writer/linux_core_dumper.cc",
"src/client/linux/minidump_writer/linux_dumper.cc",
"src/client/linux/minidump_writer/linux_ptrace_dumper.cc",
"src/client/linux/minidump_writer/minidump_writer.cc",
"src/client/linux/minidump_writer/pe_file.cc",
};
const client_apple: []const []const u8 = &.{
"src/client/minidump_file_writer.cc",
"src/client/mac/handler/breakpad_nlist_64.cc",
"src/client/mac/handler/dynamic_images.cc",
"src/client/mac/handler/minidump_generator.cc",
};
const client_mac: []const []const u8 = &.{
"src/client/mac/handler/exception_handler.cc",
"src/client/mac/crash_generation/crash_generation_client.cc",
};
const client_ios: []const []const u8 = &.{
"src/client/ios/exception_handler_no_mach.cc",
"src/client/ios/handler/ios_exception_minidump_generator.mm",
"src/client/mac/crash_generation/ConfigFile.mm",
"src/client/mac/handler/protected_memory_allocator.cc",
};
+15
View File
@@ -0,0 +1,15 @@
.{
.name = .breakpad,
.version = "0.1.0",
.fingerprint = 0xfe9f9e4c76d5f962,
.paths = .{""},
.dependencies = .{
.breakpad = .{
.url = "https://deps.files.ghostty.org/breakpad-b99f444ba5f6b98cac261cbb391d8766b34a5918.tar.gz",
.hash = "N-V-__8AALw2uwF_03u4JRkZwRLc3Y9hakkYV7NKRR9-RIZJ",
.lazy = true,
},
.apple_sdk = .{ .path = "../apple-sdk" },
},
}
File diff suppressed because it is too large Load Diff
+216
View File
@@ -0,0 +1,216 @@
const std = @import("std");
const NativeTargetInfo = std.zig.system.NativeTargetInfo;
pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const freetype = b.option(bool, "freetype", "Use Freetype") orelse false;
const backend_opengl3 = b.option(bool, "backend-opengl3", "OpenGL3 backend") orelse false;
const backend_metal = b.option(bool, "backend-metal", "Metal backend") orelse false;
const backend_osx = b.option(bool, "backend-osx", "OSX backend") orelse false;
// Build options
const options = b.addOptions();
options.addOption(bool, "freetype", freetype);
options.addOption(bool, "backend_opengl3", backend_opengl3);
options.addOption(bool, "backend_metal", backend_metal);
options.addOption(bool, "backend_osx", backend_osx);
// Main static lib
const lib = b.addLibrary(.{
.name = "dcimgui",
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
}),
.linkage = .static,
});
lib.linkLibC();
// On MSVC, we must not use linkLibCpp because Zig unconditionally
// passes -nostdinc++ and then adds its bundled libc++/libc++abi
// include paths, which conflict with MSVC's own C++ runtime headers.
// The MSVC SDK include directories (added via linkLibC) contain
// both C and C++ headers, so linkLibCpp is not needed.
if (target.result.abi != .msvc) {
lib.linkLibCpp();
}
b.installArtifact(lib);
// Zig module
const mod = b.addModule("dcimgui", .{
.root_source_file = b.path("main.zig"),
.target = target,
.optimize = optimize,
});
mod.addOptions("build_options", options);
mod.linkLibrary(lib);
// We need to add proper Apple SDKs to find stdlib headers
if (target.result.os.tag.isDarwin()) {
if (!target.query.isNative()) {
try @import("apple_sdk").addPaths(b, lib);
}
}
// Flags for C compilation, common to all.
var flags: std.ArrayList([]const u8) = .empty;
defer flags.deinit(b.allocator);
try flags.appendSlice(b.allocator, &.{
"-DIMGUI_HAS_DOCK=1",
"-DIMGUI_USE_WCHAR32=1",
"-DIMGUI_DISABLE_OBSOLETE_FUNCTIONS=1",
});
if (target.result.abi == .msvc) {
try flags.appendSlice(b.allocator, &.{
"-fno-sanitize=undefined",
"-fno-sanitize-trap=undefined",
});
}
if (freetype) try flags.appendSlice(b.allocator, &.{
"-DIMGUI_ENABLE_FREETYPE=1",
});
if (backend_opengl3) try flags.appendSlice(b.allocator, &.{
"-DZIGPKG_IMGUI_ENABLE_OPENGL3=1",
});
if (target.result.os.tag == .windows) {
try flags.appendSlice(b.allocator, &.{
"-DIMGUI_IMPL_API=extern\t\"C\"\t__declspec(dllexport)",
});
} else {
try flags.appendSlice(b.allocator, &.{
"-DIMGUI_IMPL_API=extern\t\"C\"",
});
}
if (target.result.os.tag == .freebsd or target.result.abi == .musl) {
try flags.append(b.allocator, "-fPIC");
}
// Add the core Dear Imgui source files
if (b.lazyDependency("imgui", .{})) |upstream| {
lib.addIncludePath(upstream.path(""));
lib.addCSourceFiles(.{
.root = upstream.path(""),
.files = &.{
"imgui_demo.cpp",
"imgui_draw.cpp",
"imgui_tables.cpp",
"imgui_widgets.cpp",
"imgui.cpp",
},
.flags = flags.items,
});
lib.installHeadersDirectory(
upstream.path(""),
"",
.{ .include_extensions = &.{".h"} },
);
if (freetype) {
lib.addCSourceFile(.{
.file = upstream.path("misc/freetype/imgui_freetype.cpp"),
.flags = flags.items,
});
if (b.systemIntegrationOption("freetype", .{})) {
lib.linkSystemLibrary2("freetype2", dynamic_link_opts);
} else {
const freetype_dep = b.dependency("freetype", .{
.target = target,
.optimize = optimize,
.@"enable-libpng" = true,
});
lib.linkLibrary(freetype_dep.artifact("freetype"));
if (freetype_dep.builder.lazyDependency(
"freetype",
.{},
)) |freetype_upstream| {
mod.addIncludePath(freetype_upstream.path("include"));
}
}
}
if (backend_metal) {
lib.addCSourceFiles(.{
.root = upstream.path("backends"),
.files = &.{"imgui_impl_metal.mm"},
.flags = flags.items,
});
lib.installHeadersDirectory(
upstream.path("backends"),
"",
.{ .include_extensions = &.{"imgui_impl_metal.h"} },
);
}
if (backend_osx) {
lib.addCSourceFiles(.{
.root = upstream.path("backends"),
.files = &.{"imgui_impl_osx.mm"},
.flags = flags.items,
});
lib.installHeadersDirectory(
upstream.path("backends"),
"",
.{ .include_extensions = &.{"imgui_impl_osx.h"} },
);
}
if (backend_opengl3) {
lib.addCSourceFiles(.{
.root = upstream.path("backends"),
.files = &.{"imgui_impl_opengl3.cpp"},
.flags = flags.items,
});
lib.installHeadersDirectory(
upstream.path("backends"),
"",
.{ .include_extensions = &.{"imgui_impl_opengl3.h"} },
);
}
}
// Add the C bindings
if (b.lazyDependency("bindings", .{})) |upstream| {
lib.addIncludePath(upstream.path(""));
lib.addCSourceFiles(.{
.root = upstream.path(""),
.files = &.{
"dcimgui.cpp",
"dcimgui_internal.cpp",
},
.flags = flags.items,
});
lib.addCSourceFiles(.{
.root = b.path(""),
.files = &.{"ext.cpp"},
.flags = flags.items,
});
lib.installHeadersDirectory(
upstream.path(""),
"",
.{ .include_extensions = &.{".h"} },
);
}
const test_exe = b.addTest(.{
.name = "test",
.root_module = b.createModule(.{
.root_source_file = b.path("main.zig"),
.target = target,
.optimize = optimize,
}),
});
test_exe.root_module.addOptions("build_options", options);
test_exe.linkLibrary(lib);
const tests_run = b.addRunArtifact(test_exe);
const test_step = b.step("test", "Run tests");
test_step.dependOn(&tests_run.step);
}
// For dynamic linking, we prefer dynamic linking and to search by
// mode first. Mode first will search all paths for a dynamic library
// before falling back to static.
const dynamic_link_opts: std.Build.Module.LinkSystemLibraryOptions = .{
.preferred_link_mode = .dynamic,
.search_strategy = .mode_first,
};
+26
View File
@@ -0,0 +1,26 @@
.{
.name = .dcimgui,
.version = "1.92.5", // -docking branch
.fingerprint = 0x1a25797442c6324f,
.paths = .{""},
.dependencies = .{
// The bindings and imgui versions below must match exactly.
.bindings = .{
// https://github.com/dearimgui/dear_bindings
.url = "https://deps.files.ghostty.org/DearBindings_v0.17_ImGui_v1.92.5-docking.tar.gz",
.hash = "N-V-__8AANT61wB--nJ95Gj_ctmzAtcjloZ__hRqNw5lC1Kr",
.lazy = true,
},
.imgui = .{
// https://github.com/ocornut/imgui
.url = "https://github.com/ocornut/imgui/archive/refs/tags/v1.92.5-docking.tar.gz",
.hash = "N-V-__8AAEbOfQBnvcFcCX2W5z7tDaN8vaNZGamEQtNOe0UI",
.lazy = true,
},
.apple_sdk = .{ .path = "../apple-sdk" },
.freetype = .{ .path = "../freetype" },
},
}
+53
View File
@@ -0,0 +1,53 @@
#include "imgui.h"
// This file contains custom extensions for functionality that isn't
// properly supported by Dear Bindings yet. Namely:
// https://github.com/dearimgui/dear_bindings/issues/55
// Wrap this in a namespace to keep it separate from the C++ API
namespace cimgui
{
#include "dcimgui.h"
}
extern "C"
{
CIMGUI_API void ImFontConfig_ImFontConfig(cimgui::ImFontConfig* self)
{
static_assert(sizeof(cimgui::ImFontConfig) == sizeof(::ImFontConfig), "ImFontConfig size mismatch");
static_assert(alignof(cimgui::ImFontConfig) == alignof(::ImFontConfig), "ImFontConfig alignment mismatch");
::ImFontConfig defaults;
*reinterpret_cast<::ImFontConfig*>(self) = defaults;
}
CIMGUI_API void ImGuiStyle_ImGuiStyle(cimgui::ImGuiStyle* self)
{
static_assert(sizeof(cimgui::ImGuiStyle) == sizeof(::ImGuiStyle), "ImGuiStyle size mismatch");
static_assert(alignof(cimgui::ImGuiStyle) == alignof(::ImGuiStyle), "ImGuiStyle alignment mismatch");
::ImGuiStyle defaults;
*reinterpret_cast<::ImGuiStyle*>(self) = defaults;
}
// Perform the OpenGL3 backend shutdown and then zero out the imgl3w
// function pointer table. ImGui_ImplOpenGL3_Shutdown() calls
// imgl3wShutdown() which dlcloses the GL library handles but does not
// zero out the function pointers. A subsequent ImGui_ImplOpenGL3_Init()
// sees the stale (non-null) pointers, skips loader re-initialization,
// and crashes when calling through them. Zeroing the table forces the
// next Init to reload the GL function pointers via imgl3wInit().
#ifndef IMGUI_DISABLE
#if __has_include("backends/imgui_impl_opengl3.h")
#ifdef ZIGPKG_IMGUI_ENABLE_OPENGL3
#include "backends/imgui_impl_opengl3.h"
#include "backends/imgui_impl_opengl3_loader.h"
CIMGUI_API void ImGui_ImplOpenGL3_ShutdownWithLoaderCleanup()
{
::ImGui_ImplOpenGL3_Shutdown();
memset(&imgl3wProcs, 0, sizeof(imgl3wProcs));
}
#endif // ZIGPKG_IMGUI_ENABLE_OPENGL3
#endif // __has_include("backends/imgui_impl_opengl3.h")
#endif // IMGUI_DISABLE
}
+77
View File
@@ -0,0 +1,77 @@
pub const build_options = @import("build_options");
pub const c = @cImport({
// This is set during the build so it also has to be set
// during import time to get the right types. Without this
// you get stack size mismatches on some structs.
@cDefine("IMGUI_USE_WCHAR32", "1");
@cDefine("IMGUI_HAS_DOCK", "1");
@cInclude("dcimgui.h");
});
// OpenGL3 backend
pub extern fn ImGui_ImplOpenGL3_Init(glsl_version: ?[*:0]const u8) callconv(.c) bool;
pub extern fn ImGui_ImplOpenGL3_Shutdown() callconv(.c) void;
pub extern fn ImGui_ImplOpenGL3_NewFrame() callconv(.c) void;
pub extern fn ImGui_ImplOpenGL3_RenderDrawData(draw_data: *c.ImDrawData) callconv(.c) void;
// Extension: shutdown the OpenGL3 backend and zero out the imgl3w function
// pointer table so a subsequent Init can re-initialize the loader.
pub extern fn ImGui_ImplOpenGL3_ShutdownWithLoaderCleanup() callconv(.c) void;
// Metal backend
pub extern fn ImGui_ImplMetal_Init(device: *anyopaque) callconv(.c) bool;
pub extern fn ImGui_ImplMetal_Shutdown() callconv(.c) void;
pub extern fn ImGui_ImplMetal_NewFrame(render_pass_descriptor: *anyopaque) callconv(.c) void;
pub extern fn ImGui_ImplMetal_RenderDrawData(draw_data: *c.ImDrawData, command_buffer: *anyopaque, command_encoder: *anyopaque) callconv(.c) void;
// OSX
pub extern fn ImGui_ImplOSX_Init(*anyopaque) callconv(.c) bool;
pub extern fn ImGui_ImplOSX_Shutdown() callconv(.c) void;
pub extern fn ImGui_ImplOSX_NewFrame(*anyopaque) callconv(.c) void;
// Internal API types and functions from dcimgui_internal.h
// We declare these manually because the internal header contains bitfields
// that Zig's cImport cannot translate.
pub const ImGuiDockNodeFlagsPrivate = struct {
pub const DockSpace: c.ImGuiDockNodeFlags = 1 << 10;
pub const CentralNode: c.ImGuiDockNodeFlags = 1 << 11;
pub const NoTabBar: c.ImGuiDockNodeFlags = 1 << 12;
pub const HiddenTabBar: c.ImGuiDockNodeFlags = 1 << 13;
pub const NoWindowMenuButton: c.ImGuiDockNodeFlags = 1 << 14;
pub const NoCloseButton: c.ImGuiDockNodeFlags = 1 << 15;
pub const NoResizeX: c.ImGuiDockNodeFlags = 1 << 16;
pub const NoResizeY: c.ImGuiDockNodeFlags = 1 << 17;
pub const DockedWindowsInFocusRoute: c.ImGuiDockNodeFlags = 1 << 18;
pub const NoDockingSplitOther: c.ImGuiDockNodeFlags = 1 << 19;
pub const NoDockingOverMe: c.ImGuiDockNodeFlags = 1 << 20;
pub const NoDockingOverOther: c.ImGuiDockNodeFlags = 1 << 21;
pub const NoDockingOverEmpty: c.ImGuiDockNodeFlags = 1 << 22;
};
pub extern fn ImGui_DockBuilderDockWindow(window_name: [*:0]const u8, node_id: c.ImGuiID) callconv(.c) void;
pub extern fn ImGui_DockBuilderGetNode(node_id: c.ImGuiID) callconv(.c) ?*anyopaque;
pub extern fn ImGui_DockBuilderGetCentralNode(node_id: c.ImGuiID) callconv(.c) ?*anyopaque;
pub extern fn ImGui_DockBuilderAddNode() callconv(.c) c.ImGuiID;
pub extern fn ImGui_DockBuilderAddNodeEx(node_id: c.ImGuiID, flags: c.ImGuiDockNodeFlags) callconv(.c) c.ImGuiID;
pub extern fn ImGui_DockBuilderRemoveNode(node_id: c.ImGuiID) callconv(.c) void;
pub extern fn ImGui_DockBuilderRemoveNodeDockedWindows(node_id: c.ImGuiID) callconv(.c) void;
pub extern fn ImGui_DockBuilderRemoveNodeDockedWindowsEx(node_id: c.ImGuiID, clear_settings_refs: bool) callconv(.c) void;
pub extern fn ImGui_DockBuilderRemoveNodeChildNodes(node_id: c.ImGuiID) callconv(.c) void;
pub extern fn ImGui_DockBuilderSetNodePos(node_id: c.ImGuiID, pos: c.ImVec2) callconv(.c) void;
pub extern fn ImGui_DockBuilderSetNodeSize(node_id: c.ImGuiID, size: c.ImVec2) callconv(.c) void;
pub extern fn ImGui_DockBuilderSplitNode(node_id: c.ImGuiID, split_dir: c.ImGuiDir, size_ratio_for_node_at_dir: f32, out_id_at_dir: *c.ImGuiID, out_id_at_opposite_dir: *c.ImGuiID) callconv(.c) c.ImGuiID;
pub extern fn ImGui_DockBuilderCopyDockSpace(src_dockspace_id: c.ImGuiID, dst_dockspace_id: c.ImGuiID, in_window_remap_pairs: *c.ImVector_const_charPtr) callconv(.c) void;
pub extern fn ImGui_DockBuilderCopyNode(src_node_id: c.ImGuiID, dst_node_id: c.ImGuiID, out_node_remap_pairs: *c.ImVector_ImGuiID) callconv(.c) void;
pub extern fn ImGui_DockBuilderCopyWindowSettings(src_name: [*:0]const u8, dst_name: [*:0]const u8) callconv(.c) void;
pub extern fn ImGui_DockBuilderFinish(node_id: c.ImGuiID) callconv(.c) void;
// Extension functions from ext.cpp
pub const ext = struct {
pub extern fn ImFontConfig_ImFontConfig(self: *c.ImFontConfig) callconv(.c) void;
pub extern fn ImGuiStyle_ImGuiStyle(self: *c.ImGuiStyle) callconv(.c) void;
};
test {
_ = c;
}
+298
View File
@@ -0,0 +1,298 @@
const std = @import("std");
const NativeTargetInfo = std.zig.system.NativeTargetInfo;
pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const libxml2_enabled = b.option(bool, "enable-libxml2", "Build libxml2") orelse true;
const libxml2_iconv_enabled = b.option(
bool,
"enable-libxml2-iconv",
"Build libxml2 with iconv",
) orelse (target.result.os.tag != .windows);
const freetype_enabled = b.option(bool, "enable-freetype", "Build freetype") orelse true;
const module = b.addModule("fontconfig", .{
.root_source_file = b.path("main.zig"),
.target = target,
.optimize = optimize,
});
// For dynamic linking, we prefer dynamic linking and to search by
// mode first. Mode first will search all paths for a dynamic library
// before falling back to static.
const dynamic_link_opts: std.Build.Module.LinkSystemLibraryOptions = .{
.preferred_link_mode = .dynamic,
.search_strategy = .mode_first,
};
const test_exe = b.addTest(.{
.name = "test",
.root_module = b.createModule(.{
.root_source_file = b.path("main.zig"),
.target = target,
.optimize = optimize,
}),
});
const tests_run = b.addRunArtifact(test_exe);
const test_step = b.step("test", "Run tests");
test_step.dependOn(&tests_run.step);
if (b.systemIntegrationOption("fontconfig", .{})) {
module.linkSystemLibrary("fontconfig", dynamic_link_opts);
test_exe.linkSystemLibrary2("fontconfig", dynamic_link_opts);
} else {
const lib = try buildLib(b, module, .{
.target = target,
.optimize = optimize,
.libxml2_enabled = libxml2_enabled,
.libxml2_iconv_enabled = libxml2_iconv_enabled,
.freetype_enabled = freetype_enabled,
.dynamic_link_opts = dynamic_link_opts,
});
test_exe.linkLibrary(lib);
}
}
fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Build.Step.Compile {
const target = options.target;
const optimize = options.optimize;
const libxml2_enabled = options.libxml2_enabled;
const libxml2_iconv_enabled = options.libxml2_iconv_enabled;
const freetype_enabled = options.freetype_enabled;
const lib = b.addLibrary(.{
.name = "fontconfig",
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
}),
.linkage = .static,
});
lib.linkLibC();
if (target.result.os.tag != .windows) {
lib.linkSystemLibrary("pthread");
}
lib.addIncludePath(b.path("override/include"));
module.addIncludePath(b.path("override/include"));
var flags: std.ArrayList([]const u8) = .empty;
defer flags.deinit(b.allocator);
try flags.appendSlice(b.allocator, &.{
"-DHAVE_DIRENT_H",
"-DHAVE_FCNTL_H",
"-DHAVE_STDLIB_H",
"-DHAVE_STRING_H",
"-DHAVE_UNISTD_H",
"-DHAVE_SYS_PARAM_H",
"-DHAVE_MKSTEMP",
//"-DHAVE_GETPROGNAME",
//"-DHAVE_GETEXECNAME",
"-DHAVE_RAND",
//"-DHAVE_RANDOM_R",
"-DHAVE_VPRINTF",
"-DHAVE_FT_GET_BDF_PROPERTY",
"-DHAVE_FT_GET_PS_FONT_INFO",
"-DHAVE_FT_HAS_PS_GLYPH_NAMES",
"-DHAVE_FT_GET_X11_FONT_FORMAT",
"-DHAVE_FT_DONE_MM_VAR",
"-DHAVE_POSIX_FADVISE",
//"-DHAVE_STRUCT_STATVFS_F_BASETYPE",
// "-DHAVE_STRUCT_STATVFS_F_FSTYPENAME",
// "-DHAVE_STRUCT_STATFS_F_FLAGS",
// "-DHAVE_STRUCT_STATFS_F_FSTYPENAME",
// "-DHAVE_STRUCT_DIRENT_D_TYPE",
"-DFLEXIBLE_ARRAY_MEMBER",
"-DHAVE_STDATOMIC_PRIMITIVES",
"-DFC_GPERF_SIZE_T=size_t",
// Default errors that fontconfig can't handle
"-Wno-implicit-function-declaration",
"-Wno-int-conversion",
// https://gitlab.freedesktop.org/fontconfig/fontconfig/-/merge_requests/231
"-fno-sanitize=undefined",
"-fno-sanitize-trap=undefined",
});
switch (target.result.ptrBitWidth()) {
32 => try flags.appendSlice(b.allocator, &.{
"-DSIZEOF_VOID_P=4",
"-DALIGNOF_VOID_P=4",
}),
64 => try flags.appendSlice(b.allocator, &.{
"-DSIZEOF_VOID_P=8",
"-DALIGNOF_VOID_P=8",
}),
else => @panic("unsupported arch"),
}
if (target.result.os.tag == .windows) {
try flags.appendSlice(b.allocator, &.{
"-DFC_CACHEDIR=\"LOCAL_APPDATA_FONTCONFIG_CACHE\"",
"-DFC_TEMPLATEDIR=\"c:/share/fontconfig/conf.avail\"",
"-DCONFIGDIR=\"c:/etc/fonts/conf.d\"",
"-DFC_DEFAULT_FONTS=\"\\t<dir>WINDOWSFONTDIR</dir>\\n\\t<dir>WINDOWSUSERFONTDIR</dir>\\n\"",
});
} else {
try flags.appendSlice(b.allocator, &.{
"-DHAVE_FSTATFS",
"-DHAVE_FSTATVFS",
"-DHAVE_GETOPT",
"-DHAVE_GETOPT_LONG",
"-DHAVE_LINK",
"-DHAVE_LRAND48",
"-DHAVE_LSTAT",
"-DHAVE_MKDTEMP",
"-DHAVE_MKOSTEMP",
"-DHAVE__MKTEMP_S",
"-DHAVE_MMAP",
"-DHAVE_PTHREAD",
"-DHAVE_RANDOM",
"-DHAVE_RAND_R",
"-DHAVE_READLINK",
"-DHAVE_SYS_MOUNT_H",
"-DHAVE_SYS_STATVFS_H",
"-DFC_CACHEDIR=\"/var/cache/fontconfig\"",
"-DFC_DEFAULT_FONTS=\"<dir>/usr/share/fonts</dir><dir>/usr/local/share/fonts</dir>\"",
});
if (target.result.os.tag == .freebsd) {
try flags.appendSlice(b.allocator, &.{
"-DFC_TEMPLATEDIR=\"/usr/local/etc/fonts/conf.avail\"",
"-DFONTCONFIG_PATH=\"/usr/local/etc/fonts\"",
"-DCONFIGDIR=\"/usr/local/etc/fonts/conf.d\"",
});
} else {
try flags.appendSlice(b.allocator, &.{
"-DFC_TEMPLATEDIR=\"/usr/share/fontconfig/conf.avail\"",
"-DFONTCONFIG_PATH=\"/etc/fonts\"",
"-DCONFIGDIR=\"/usr/local/fontconfig/conf.d\"",
});
}
if (target.result.os.tag == .linux) {
try flags.appendSlice(b.allocator, &.{
"-DHAVE_SYS_STATFS_H",
"-DHAVE_SYS_VFS_H",
});
}
}
const dynamic_link_opts = options.dynamic_link_opts;
// Freetype2
_ = b.systemIntegrationOption("freetype", .{}); // So it shows up in help
if (freetype_enabled) {
if (b.systemIntegrationOption("freetype", .{})) {
lib.linkSystemLibrary2("freetype2", dynamic_link_opts);
} else {
if (b.lazyDependency(
"freetype",
.{ .target = target, .optimize = optimize },
)) |freetype_dep| {
lib.linkLibrary(freetype_dep.artifact("freetype"));
}
}
}
// Libxml2
_ = b.systemIntegrationOption("libxml2", .{}); // So it shows up in help
if (libxml2_enabled) {
try flags.appendSlice(b.allocator, &.{
"-DENABLE_LIBXML2",
"-DLIBXML_STATIC",
"-DLIBXML_PUSH_ENABLED",
});
if (target.result.os.tag == .windows) {
// NOTE: this should be defined on all targets
try flags.appendSlice(b.allocator, &.{
"-Werror=implicit-function-declaration",
});
}
if (b.systemIntegrationOption("libxml2", .{})) {
lib.linkSystemLibrary2("libxml-2.0", dynamic_link_opts);
} else {
if (b.lazyDependency("libxml2", .{
.target = target,
.optimize = optimize,
.iconv = libxml2_iconv_enabled,
})) |libxml2_dep| {
lib.linkLibrary(libxml2_dep.artifact("xml2"));
}
}
}
if (b.lazyDependency("fontconfig", .{})) |upstream| {
lib.addIncludePath(upstream.path(""));
module.addIncludePath(upstream.path(""));
lib.addCSourceFiles(.{
.root = upstream.path(""),
.files = srcs,
.flags = flags.items,
});
lib.installHeadersDirectory(
upstream.path("fontconfig"),
"fontconfig",
.{ .include_extensions = &.{".h"} },
);
}
b.installArtifact(lib);
return lib;
}
const headers = &.{
"fontconfig/fontconfig.h",
"fontconfig/fcprivate.h",
"fontconfig/fcfreetype.h",
};
const srcs: []const []const u8 = &.{
"src/fcatomic.c",
"src/fccache.c",
"src/fccfg.c",
"src/fccharset.c",
"src/fccompat.c",
"src/fcdbg.c",
"src/fcdefault.c",
"src/fcdir.c",
"src/fcformat.c",
"src/fcfreetype.c",
"src/fcfs.c",
"src/fcptrlist.c",
"src/fchash.c",
"src/fcinit.c",
"src/fclang.c",
"src/fclist.c",
"src/fcmatch.c",
"src/fcmatrix.c",
"src/fcname.c",
"src/fcobjs.c",
"src/fcpat.c",
"src/fcrange.c",
"src/fcserialize.c",
"src/fcstat.c",
"src/fcstr.c",
"src/fcweight.c",
"src/fcxml.c",
"src/ftglue.c",
};
+16
View File
@@ -0,0 +1,16 @@
.{
.name = .fontconfig,
.version = "2.14.2",
.fingerprint = 0x4a79a5a40c6d6d8,
.paths = .{""},
.dependencies = .{
.fontconfig = .{
.url = "https://deps.files.ghostty.org/fontconfig-2.14.2.tar.gz",
.hash = "N-V-__8AAIrfdwARSa-zMmxWwFuwpXf1T3asIN7s5jqi9c1v",
.lazy = true,
},
.freetype = .{ .path = "../freetype", .lazy = true },
.libxml2 = .{ .path = "../libxml2", .lazy = true },
},
}
+3
View File
@@ -0,0 +1,3 @@
pub const c = @cImport({
@cInclude("fontconfig/fontconfig.h");
});
+40
View File
@@ -0,0 +1,40 @@
const std = @import("std");
const assert = std.debug.assert;
const c = @import("c.zig").c;
pub const CharSet = opaque {
pub fn create() *CharSet {
return @ptrCast(c.FcCharSetCreate());
}
pub fn destroy(self: *CharSet) void {
c.FcCharSetDestroy(self.cval());
}
pub fn addChar(self: *CharSet, cp: u32) bool {
return c.FcCharSetAddChar(self.cval(), cp) == c.FcTrue;
}
pub fn hasChar(self: *const CharSet, cp: u32) bool {
return c.FcCharSetHasChar(self.cvalConst(), cp) == c.FcTrue;
}
pub inline fn cval(self: *CharSet) *c.struct__FcCharSet {
return @ptrCast(self);
}
pub inline fn cvalConst(self: *const CharSet) *const c.struct__FcCharSet {
return @ptrCast(self);
}
};
test "create" {
const testing = std.testing;
var fs = CharSet.create();
defer fs.destroy();
try testing.expect(!fs.hasChar(0x20));
try testing.expect(fs.addChar(0x20));
try testing.expect(fs.hasChar(0x20));
}
+139
View File
@@ -0,0 +1,139 @@
const std = @import("std");
const c = @import("c.zig").c;
const Error = @import("main.zig").Error;
pub const Weight = enum(c_uint) {
thin = c.FC_WEIGHT_THIN,
extralight = c.FC_WEIGHT_EXTRALIGHT,
light = c.FC_WEIGHT_LIGHT,
demilight = c.FC_WEIGHT_DEMILIGHT,
book = c.FC_WEIGHT_BOOK,
regular = c.FC_WEIGHT_REGULAR,
medium = c.FC_WEIGHT_MEDIUM,
demibold = c.FC_WEIGHT_DEMIBOLD,
bold = c.FC_WEIGHT_BOLD,
extrabold = c.FC_WEIGHT_EXTRABOLD,
black = c.FC_WEIGHT_BLACK,
extrablack = c.FC_WEIGHT_EXTRABLACK,
};
pub const Slant = enum(c_uint) {
roman = c.FC_SLANT_ROMAN,
italic = c.FC_SLANT_ITALIC,
oblique = c.FC_SLANT_OBLIQUE,
};
pub const Spacing = enum(c_uint) {
proportional = c.FC_PROPORTIONAL,
dual = c.FC_DUAL,
mono = c.FC_MONO,
charcell = c.FC_CHARCELL,
};
pub const Property = enum {
family,
style,
slant,
weight,
size,
aspect,
pixel_size,
spacing,
foundry,
antialias,
hinting,
hint_style,
vertical_layout,
autohint,
global_advance,
width,
file,
index,
ft_face,
rasterizer,
outline,
scalable,
color,
variable,
scale,
symbol,
dpi,
rgba,
minspace,
source,
charset,
lang,
fontversion,
fullname,
familylang,
stylelang,
fullnamelang,
capability,
embolden,
embedded_bitmap,
decorative,
lcd_filter,
font_features,
font_variations,
namelang,
prgname,
hash,
postscript_name,
font_has_hint,
order,
pub fn cval(self: Property) [:0]const u8 {
@setEvalBranchQuota(10_000);
inline for (@typeInfo(Property).@"enum".fields) |field| {
if (self == @field(Property, field.name)) {
// Build our string in a comptime context so it is a binary
// constant and not stack allocated.
return comptime name: {
// Replace _ with ""
var buf: [field.name.len]u8 = undefined;
const count = std.mem.replace(u8, field.name, "_", "", &buf);
const replaced = buf[0 .. field.name.len - count];
// Build our string
var name: [replaced.len:0]u8 = undefined;
@memcpy(&name, replaced);
name[replaced.len] = 0;
const final = name;
break :name &final;
};
}
}
unreachable;
}
test "cval" {
const testing = std.testing;
try testing.expectEqualStrings("family", Property.family.cval());
try testing.expectEqualStrings("pixelsize", Property.pixel_size.cval());
}
};
pub const Result = enum(c_uint) {
match = c.FcResultMatch,
no_match = c.FcResultNoMatch,
type_mismatch = c.FcResultTypeMismatch,
no_id = c.FcResultNoId,
out_of_memory = c.FcResultOutOfMemory,
pub fn toError(self: Result) Error!void {
return switch (self) {
.match => {},
.no_match => Error.FontconfigNoMatch,
.type_mismatch => Error.FontconfigTypeMismatch,
.no_id => Error.FontconfigNoId,
.out_of_memory => Error.OutOfMemory,
};
}
};
pub const MatchKind = enum(c_uint) {
pattern = c.FcMatchPattern,
font = c.FcMatchFont,
scan = c.FcMatchScan,
};
+61
View File
@@ -0,0 +1,61 @@
const std = @import("std");
const c = @import("c.zig").c;
const Error = @import("main.zig").Error;
const CharSet = @import("char_set.zig").CharSet;
const FontSet = @import("font_set.zig").FontSet;
const ObjectSet = @import("object_set.zig").ObjectSet;
const Pattern = @import("pattern.zig").Pattern;
const Result = @import("main.zig").Result;
const MatchKind = @import("main.zig").MatchKind;
pub const Config = opaque {
pub fn destroy(self: *Config) void {
c.FcConfigDestroy(@ptrCast(self));
}
pub fn fontList(self: *Config, pat: *Pattern, os: *ObjectSet) *FontSet {
return @ptrCast(c.FcFontList(self.cval(), pat.cval(), os.cval()));
}
pub fn fontSort(
self: *Config,
pat: *Pattern,
trim: bool,
charset: ?[*]*CharSet,
) FontSortResult {
var result: FontSortResult = undefined;
result.fs = @ptrCast(c.FcFontSort(
self.cval(),
pat.cval(),
if (trim) c.FcTrue else c.FcFalse,
@ptrCast(charset),
@ptrCast(&result.result),
));
return result;
}
pub fn fontRenderPrepare(self: *Config, pat: *Pattern, font: *Pattern) Error!*Pattern {
return @as(
?*Pattern,
@ptrCast(c.FcFontRenderPrepare(self.cval(), pat.cval(), font.cval())),
) orelse Error.FontconfigFailed;
}
pub fn substituteWithPat(self: *Config, pat: *Pattern, kind: MatchKind) bool {
return c.FcConfigSubstitute(
self.cval(),
pat.cval(),
@intFromEnum(kind),
) == c.FcTrue;
}
pub inline fn cval(self: *Config) *c.struct__FcConfig {
return @ptrCast(self);
}
};
pub const FontSortResult = struct {
result: Result,
fs: *FontSet,
};
+7
View File
@@ -0,0 +1,7 @@
pub const Error = error{
OutOfMemory,
FontconfigFailed,
FontconfigNoMatch,
FontconfigTypeMismatch,
FontconfigNoId,
};
+44
View File
@@ -0,0 +1,44 @@
const std = @import("std");
const assert = std.debug.assert;
const c = @import("c.zig").c;
const Pattern = @import("pattern.zig").Pattern;
pub const FontSet = opaque {
pub fn create() *FontSet {
return @ptrCast(c.FcFontSetCreate());
}
pub fn destroy(self: *FontSet) void {
c.FcFontSetDestroy(self.cval());
}
pub fn fonts(self: *FontSet) []*Pattern {
const empty: [0]*Pattern = undefined;
const s = self.cval();
if (s.fonts == null) return &empty;
const ptr: [*]*Pattern = @ptrCast(@alignCast(s.fonts));
const len: usize = @intCast(s.nfont);
return ptr[0..len];
}
pub fn add(self: *FontSet, pat: *Pattern) bool {
return c.FcFontSetAdd(self.cval(), pat.cval()) == c.FcTrue;
}
pub fn print(self: *FontSet) void {
c.FcFontSetPrint(self.cval());
}
pub inline fn cval(self: *FontSet) *c.struct__FcFontSet {
return @ptrCast(@alignCast(self));
}
};
test "create" {
const testing = std.testing;
var fs = FontSet.create();
defer fs.destroy();
try testing.expectEqual(@as(usize, 0), fs.fonts().len);
}
+43
View File
@@ -0,0 +1,43 @@
const std = @import("std");
const c = @import("c.zig").c;
const Config = @import("config.zig").Config;
pub fn init() bool {
return c.FcInit() == c.FcTrue;
}
pub fn fini() void {
c.FcFini();
}
pub fn initLoadConfig() *Config {
return @ptrCast(c.FcInitLoadConfig());
}
pub fn initLoadConfigAndFonts() *Config {
return @ptrCast(c.FcInitLoadConfigAndFonts());
}
pub fn version() u32 {
return @intCast(c.FcGetVersion());
}
test "version" {
const testing = std.testing;
try testing.expect(version() > 0);
}
test "init" {
try std.testing.expect(init());
defer fini();
}
test "initLoadConfig" {
var config = initLoadConfig();
defer config.destroy();
}
test "initLoadConfigAndFonts" {
var config = initLoadConfigAndFonts();
defer config.destroy();
}
+61
View File
@@ -0,0 +1,61 @@
const std = @import("std");
const assert = std.debug.assert;
const c = @import("c.zig").c;
pub const LangSet = opaque {
pub fn create() *LangSet {
return @ptrCast(c.FcLangSetCreate());
}
pub fn destroy(self: *LangSet) void {
c.FcLangSetDestroy(self.cval());
}
pub fn addLang(self: *LangSet, lang: [:0]const u8) bool {
return c.FcLangSetAdd(self.cval(), lang.ptr) == c.FcTrue;
}
pub fn hasLang(self: *const LangSet, lang: [:0]const u8) bool {
return c.FcLangSetHasLang(self.cvalConst(), lang.ptr) == c.FcLangEqual;
}
pub inline fn cval(self: *LangSet) *c.struct__FcLangSet {
return @ptrCast(self);
}
pub inline fn cvalConst(self: *const LangSet) *const c.struct__FcLangSet {
return @ptrCast(self);
}
};
test "create" {
const testing = std.testing;
var fs = LangSet.create();
defer fs.destroy();
try testing.expect(!fs.hasLang("und-zsye"));
}
test "hasLang exact match" {
const testing = std.testing;
// Test exact match: langset with "en-US" should return true for "en-US"
var fs = LangSet.create();
defer fs.destroy();
try testing.expect(fs.addLang("en-US"));
try testing.expect(fs.hasLang("en-US"));
// Test exact match: langset with "und-zsye" should return true for "und-zsye"
var fs_emoji = LangSet.create();
defer fs_emoji.destroy();
try testing.expect(fs_emoji.addLang("und-zsye"));
try testing.expect(fs_emoji.hasLang("und-zsye"));
// Test mismatch: langset with "en-US" should return false for "fr"
try testing.expect(!fs.hasLang("fr"));
// Test partial match: langset with "en-US" should return false for "en-GB"
// (different territory, but we only want exact matches)
try testing.expect(!fs.hasLang("en-GB"));
}
+45
View File
@@ -0,0 +1,45 @@
const initpkg = @import("init.zig");
const char_set = @import("char_set.zig");
const common = @import("common.zig");
const config = @import("config.zig");
const errorpkg = @import("error.zig");
const font_set = @import("font_set.zig");
const lang_set = @import("lang_set.zig");
const matrix = @import("matrix.zig");
const object_set = @import("object_set.zig");
const pattern = @import("pattern.zig");
const range = @import("range.zig");
const value = @import("value.zig");
pub const c = @import("c.zig").c;
pub const init = initpkg.init;
pub const fini = initpkg.fini;
pub const initLoadConfig = initpkg.initLoadConfig;
pub const initLoadConfigAndFonts = initpkg.initLoadConfigAndFonts;
pub const version = initpkg.version;
pub const CharSet = char_set.CharSet;
pub const Weight = common.Weight;
pub const Slant = common.Slant;
pub const Spacing = common.Spacing;
pub const Property = common.Property;
pub const Result = common.Result;
pub const MatchKind = common.MatchKind;
pub const Config = config.Config;
pub const Error = errorpkg.Error;
pub const FontSet = font_set.FontSet;
pub const LangSet = lang_set.LangSet;
pub const Matrix = matrix.Matrix;
pub const ObjectSet = object_set.ObjectSet;
pub const Pattern = pattern.Pattern;
pub const Range = range.Range;
pub const Type = value.Type;
pub const Value = value.Value;
pub const ValueBinding = value.ValueBinding;
test {
@import("std").testing.refAllDecls(@This());
}
test {
_ = @import("test.zig");
}
+10
View File
@@ -0,0 +1,10 @@
const std = @import("std");
const assert = std.debug.assert;
const c = @import("c.zig").c;
pub const Matrix = extern struct {
xx: f64,
xy: f64,
yx: f64,
yy: f64,
};
+30
View File
@@ -0,0 +1,30 @@
const std = @import("std");
const c = @import("c.zig").c;
const Property = @import("main.zig").Property;
pub const ObjectSet = opaque {
pub fn create() *ObjectSet {
return @ptrCast(c.FcObjectSetCreate());
}
pub fn destroy(self: *ObjectSet) void {
c.FcObjectSetDestroy(self.cval());
}
pub fn add(self: *ObjectSet, p: Property) bool {
return c.FcObjectSetAdd(self.cval(), p.cval().ptr) == c.FcTrue;
}
pub inline fn cval(self: *ObjectSet) *c.struct__FcObjectSet {
return @ptrCast(@alignCast(self));
}
};
test "create" {
const testing = std.testing;
var os = ObjectSet.create();
defer os.destroy();
try testing.expect(os.add(.family));
}
@@ -0,0 +1,347 @@
/* ANSI-C code produced by gperf version 3.1 */
/* Command-line: gperf --pic -m 100 fcobjshash.gperf */
/* Computed positions: -k'3,5' */
#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
&& ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
&& (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
&& ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
&& ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
&& ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
&& ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
&& ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
&& ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
&& ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
&& ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
&& ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
&& ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
&& ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
&& ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
&& ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
&& ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
&& ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
&& ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
&& ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
&& ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
&& ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
&& ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
/* The character set is not based on ISO-646. */
#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gperf@gnu.org>."
#endif
#line 1 "fcobjshash.gperf"
#line 13 "fcobjshash.gperf"
struct FcObjectTypeInfo {
int name;
int id;
};
#include <string.h>
/* maximum key range = 59, duplicates = 0 */
#ifdef __GNUC__
__inline
#else
#ifdef __cplusplus
inline
#endif
#endif
static unsigned int
FcObjectTypeHash (register const char *str, register size_t len)
{
static const unsigned char asso_values[] =
{
66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 4, 10, 32,
21, 29, 28, 49, 14, 4, 66, 66, 5, 31,
18, 22, 27, 66, 15, 9, 8, 23, 23, 13,
23, 16, 4, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66
};
register unsigned int hval = len;
switch (hval)
{
default:
hval += asso_values[(unsigned char)str[4]];
/*FALLTHROUGH*/
case 4:
case 3:
hval += asso_values[(unsigned char)str[2]];
break;
}
return hval;
}
struct FcObjectTypeNamePool_t
{
char FcObjectTypeNamePool_str7[sizeof("dpi")];
char FcObjectTypeNamePool_str8[sizeof("size")];
char FcObjectTypeNamePool_str9[sizeof("file")];
char FcObjectTypeNamePool_str13[sizeof("hash")];
char FcObjectTypeNamePool_str14[sizeof("rgba")];
char FcObjectTypeNamePool_str15[sizeof("spacing")];
char FcObjectTypeNamePool_str16[sizeof("scalable")];
char FcObjectTypeNamePool_str17[sizeof("slant")];
char FcObjectTypeNamePool_str18[sizeof("matrix")];
char FcObjectTypeNamePool_str19[sizeof("outline")];
char FcObjectTypeNamePool_str20[sizeof("charset")];
char FcObjectTypeNamePool_str21[sizeof("antialias")];
char FcObjectTypeNamePool_str22[sizeof("lang")];
char FcObjectTypeNamePool_str23[sizeof("embolden")];
char FcObjectTypeNamePool_str24[sizeof("weight")];
char FcObjectTypeNamePool_str25[sizeof("color")];
char FcObjectTypeNamePool_str26[sizeof("charwidth")];
char FcObjectTypeNamePool_str27[sizeof("variable")];
char FcObjectTypeNamePool_str28[sizeof("charheight")];
char FcObjectTypeNamePool_str29[sizeof("hinting")];
char FcObjectTypeNamePool_str30[sizeof("autohint")];
char FcObjectTypeNamePool_str31[sizeof("fullname")];
char FcObjectTypeNamePool_str32[sizeof("postscriptname")];
char FcObjectTypeNamePool_str33[sizeof("verticallayout")];
char FcObjectTypeNamePool_str34[sizeof("lcdfilter")];
char FcObjectTypeNamePool_str35[sizeof("fullnamelang")];
char FcObjectTypeNamePool_str36[sizeof("hintstyle")];
char FcObjectTypeNamePool_str37[sizeof("pixelsize")];
char FcObjectTypeNamePool_str38[sizeof("scale")];
char FcObjectTypeNamePool_str39[sizeof("globaladvance")];
char FcObjectTypeNamePool_str40[sizeof("width")];
char FcObjectTypeNamePool_str41[sizeof("order")];
char FcObjectTypeNamePool_str42[sizeof("family")];
char FcObjectTypeNamePool_str43[sizeof("fonthashint")];
char FcObjectTypeNamePool_str44[sizeof("namelang")];
char FcObjectTypeNamePool_str45[sizeof("embeddedbitmap")];
char FcObjectTypeNamePool_str46[sizeof("familylang")];
char FcObjectTypeNamePool_str47[sizeof("capability")];
char FcObjectTypeNamePool_str48[sizeof("rasterizer")];
char FcObjectTypeNamePool_str49[sizeof("index")];
char FcObjectTypeNamePool_str50[sizeof("style")];
char FcObjectTypeNamePool_str51[sizeof("foundry")];
char FcObjectTypeNamePool_str52[sizeof("fontversion")];
char FcObjectTypeNamePool_str53[sizeof("minspace")];
char FcObjectTypeNamePool_str54[sizeof("stylelang")];
char FcObjectTypeNamePool_str55[sizeof("fontvariations")];
char FcObjectTypeNamePool_str56[sizeof("fontformat")];
char FcObjectTypeNamePool_str57[sizeof("decorative")];
char FcObjectTypeNamePool_str58[sizeof("fontfeatures")];
char FcObjectTypeNamePool_str59[sizeof("symbol")];
char FcObjectTypeNamePool_str60[sizeof("prgname")];
char FcObjectTypeNamePool_str65[sizeof("aspect")];
};
static const struct FcObjectTypeNamePool_t FcObjectTypeNamePool_contents =
{
"dpi",
"size",
"file",
"hash",
"rgba",
"spacing",
"scalable",
"slant",
"matrix",
"outline",
"charset",
"antialias",
"lang",
"embolden",
"weight",
"color",
"charwidth",
"variable",
"charheight",
"hinting",
"autohint",
"fullname",
"postscriptname",
"verticallayout",
"lcdfilter",
"fullnamelang",
"hintstyle",
"pixelsize",
"scale",
"globaladvance",
"width",
"order",
"family",
"fonthashint",
"namelang",
"embeddedbitmap",
"familylang",
"capability",
"rasterizer",
"index",
"style",
"foundry",
"fontversion",
"minspace",
"stylelang",
"fontvariations",
"fontformat",
"decorative",
"fontfeatures",
"symbol",
"prgname",
"aspect"
};
#define FcObjectTypeNamePool ((const char *) &FcObjectTypeNamePool_contents)
const struct FcObjectTypeInfo *
FcObjectTypeLookup (register const char *str, register size_t len)
{
enum
{
TOTAL_KEYWORDS = 52,
MIN_WORD_LENGTH = 3,
MAX_WORD_LENGTH = 14,
MIN_HASH_VALUE = 7,
MAX_HASH_VALUE = 65
};
static const struct FcObjectTypeInfo wordlist[] =
{
{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
#line 43 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str7,FC_DPI_OBJECT},
#line 27 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str8,FC_SIZE_OBJECT},
#line 38 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str9,FC_FILE_OBJECT},
{-1}, {-1}, {-1},
#line 62 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str13,FC_HASH_OBJECT},
#line 44 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str14,FC_RGBA_OBJECT},
#line 30 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str15,FC_SPACING_OBJECT},
#line 42 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str16,FC_SCALABLE_OBJECT},
#line 24 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str17,FC_SLANT_OBJECT},
#line 49 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str18,FC_MATRIX_OBJECT},
#line 41 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str19,FC_OUTLINE_OBJECT},
#line 50 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str20,FC_CHARSET_OBJECT},
#line 32 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str21,FC_ANTIALIAS_OBJECT},
#line 51 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str22,FC_LANG_OBJECT},
#line 55 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str23,FC_EMBOLDEN_OBJECT},
#line 25 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str24,FC_WEIGHT_OBJECT},
#line 64 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str25,FC_COLOR_OBJECT},
#line 47 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str26,FC_CHARWIDTH_OBJECT},
#line 67 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str27,FC_VARIABLE_OBJECT},
#line 48 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str28,FC_CHAR_HEIGHT_OBJECT},
#line 34 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str29,FC_HINTING_OBJECT},
#line 36 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str30,FC_AUTOHINT_OBJECT},
#line 22 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str31,FC_FULLNAME_OBJECT},
#line 63 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str32,FC_POSTSCRIPT_NAME_OBJECT},
#line 35 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str33,FC_VERTICAL_LAYOUT_OBJECT},
#line 58 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str34,FC_LCD_FILTER_OBJECT},
#line 23 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str35,FC_FULLNAMELANG_OBJECT},
#line 33 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str36,FC_HINT_STYLE_OBJECT},
#line 29 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str37,FC_PIXEL_SIZE_OBJECT},
#line 45 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str38,FC_SCALE_OBJECT},
#line 37 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str39,FC_GLOBAL_ADVANCE_OBJECT},
#line 26 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str40,FC_WIDTH_OBJECT},
#line 69 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str41,FC_ORDER_OBJECT},
#line 18 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str42,FC_FAMILY_OBJECT},
#line 68 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str43,FC_FONT_HAS_HINT_OBJECT},
#line 59 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str44,FC_NAMELANG_OBJECT},
#line 56 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str45,FC_EMBEDDED_BITMAP_OBJECT},
#line 19 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str46,FC_FAMILYLANG_OBJECT},
#line 53 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str47,FC_CAPABILITY_OBJECT},
#line 40 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str48,FC_RASTERIZER_OBJECT},
#line 39 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str49,FC_INDEX_OBJECT},
#line 20 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str50,FC_STYLE_OBJECT},
#line 31 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str51,FC_FOUNDRY_OBJECT},
#line 52 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str52,FC_FONTVERSION_OBJECT},
#line 46 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str53,FC_MINSPACE_OBJECT},
#line 21 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str54,FC_STYLELANG_OBJECT},
#line 66 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str55,FC_FONT_VARIATIONS_OBJECT},
#line 54 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str56,FC_FONTFORMAT_OBJECT},
#line 57 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str57,FC_DECORATIVE_OBJECT},
#line 60 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str58,FC_FONT_FEATURES_OBJECT},
#line 65 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str59,FC_SYMBOL_OBJECT},
#line 61 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str60,FC_PRGNAME_OBJECT},
{-1}, {-1}, {-1}, {-1},
#line 28 "fcobjshash.gperf"
{(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str65,FC_ASPECT_OBJECT}
};
if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
{
register unsigned int key = FcObjectTypeHash (str, len);
if (key <= MAX_HASH_VALUE)
{
register int o = wordlist[key].name;
if (o >= 0)
{
register const char *s = o + FcObjectTypeNamePool;
if (*str == *s && !strcmp (str + 1, s + 1))
return &wordlist[key];
}
}
}
return 0;
}
+168
View File
@@ -0,0 +1,168 @@
const std = @import("std");
const assert = std.debug.assert;
const c = @import("c.zig").c;
const Error = @import("main.zig").Error;
const ObjectSet = @import("main.zig").ObjectSet;
const Property = @import("main.zig").Property;
const Result = @import("main.zig").Result;
const Value = @import("main.zig").Value;
const ValueBinding = @import("main.zig").ValueBinding;
const Weight = @import("main.zig").Weight;
pub const Pattern = opaque {
pub fn create() *Pattern {
return @ptrCast(c.FcPatternCreate());
}
pub fn parse(str: [:0]const u8) *Pattern {
return @ptrCast(c.FcNameParse(str.ptr));
}
pub fn destroy(self: *Pattern) void {
c.FcPatternDestroy(self.cval());
}
pub fn defaultSubstitute(self: *Pattern) void {
c.FcDefaultSubstitute(self.cval());
}
pub fn add(self: *Pattern, prop: Property, value: Value, append: bool) bool {
return c.FcPatternAdd(
self.cval(),
prop.cval().ptr,
value.cval(),
if (append) c.FcTrue else c.FcFalse,
) == c.FcTrue;
}
pub fn get(self: *Pattern, prop: Property, id: u32) Error!Value {
var val: c.struct__FcValue = undefined;
try @as(Result, @enumFromInt(c.FcPatternGet(
self.cval(),
prop.cval().ptr,
@intCast(id),
&val,
))).toError();
return .init(&val);
}
pub fn delete(self: *Pattern, prop: Property) bool {
return c.FcPatternDel(self.cval(), prop.cval()) == c.FcTrue;
}
pub fn filter(self: *Pattern, os: *const ObjectSet) *Pattern {
return @ptrCast(c.FcPatternFilter(self.cval(), os.cval()));
}
pub fn objectIterator(self: *Pattern) ObjectIterator {
return .{ .pat = self.cval(), .iter = null };
}
pub fn print(self: *Pattern) void {
c.FcPatternPrint(self.cval());
}
pub inline fn cval(self: *Pattern) *c.struct__FcPattern {
return @ptrCast(self);
}
pub const ObjectIterator = struct {
pat: *c.struct__FcPattern,
iter: ?c.struct__FcPatternIter,
/// Move to the next object, returns true if there is another
/// object and false otherwise. If this is the first call, this
/// will be the first object.
pub fn next(self: *ObjectIterator) bool {
// Null means our first iterator
if (self.iter == null) {
// If we have no objects, do not create iterator
if (c.FcPatternObjectCount(self.pat) == 0) return false;
var iter: c.struct__FcPatternIter = undefined;
c.FcPatternIterStart(
self.pat,
&iter,
);
assert(c.FcPatternIterIsValid(self.pat, &iter) == c.FcTrue);
self.iter = iter;
// Return right away because the fontconfig iterator pattern
// is do/while.
return true;
}
return c.FcPatternIterNext(self.pat, @ptrCast(&self.iter)) == c.FcTrue;
}
pub fn object(self: *ObjectIterator) []const u8 {
return std.mem.sliceTo(c.FcPatternIterGetObject(
self.pat,
&self.iter.?,
), 0);
}
pub fn valueLen(self: *ObjectIterator) usize {
return @intCast(c.FcPatternIterValueCount(self.pat, &self.iter.?));
}
pub fn valueIterator(self: *ObjectIterator) ValueIterator {
return .{
.pat = self.pat,
.iter = &self.iter.?,
.max = c.FcPatternIterValueCount(self.pat, &self.iter.?),
};
}
};
pub const ValueIterator = struct {
pat: *c.struct__FcPattern,
iter: *c.struct__FcPatternIter,
max: c_int,
id: c_int = 0,
pub const Entry = struct {
result: Result,
value: Value,
binding: ValueBinding,
};
pub fn next(self: *ValueIterator) ?Entry {
if (self.id >= self.max) return null;
var value: c.struct__FcValue = undefined;
var binding: c.FcValueBinding = undefined;
const result = c.FcPatternIterGetValue(self.pat, self.iter, self.id, &value, &binding);
self.id += 1;
return Entry{
.result = @enumFromInt(result),
.binding = @enumFromInt(binding),
.value = .init(&value),
};
}
};
};
test "create" {
const testing = std.testing;
var pat = Pattern.create();
defer pat.destroy();
try testing.expect(pat.add(.family, .{ .string = "monospace" }, false));
try testing.expect(pat.add(.weight, .{ .integer = @intFromEnum(Weight.bold) }, false));
{
const val = try pat.get(.family, 0);
try testing.expect(val == .string);
try testing.expectEqualStrings("monospace", val.string);
}
}
test "name parse" {
var pat = Pattern.parse(":monospace");
defer pat.destroy();
pat.defaultSubstitute();
}
+13
View File
@@ -0,0 +1,13 @@
const std = @import("std");
const assert = std.debug.assert;
const c = @import("c.zig").c;
pub const Range = opaque {
pub fn destroy(self: *Range) void {
c.FcRangeDestroy(self.cval());
}
pub inline fn cval(self: *Range) *c.struct__FcRange {
return @ptrCast(self);
}
};
+66
View File
@@ -0,0 +1,66 @@
const std = @import("std");
const fontconfig = @import("main.zig");
test "fc-list" {
const testing = std.testing;
var cfg = fontconfig.initLoadConfigAndFonts();
defer cfg.destroy();
var pat = fontconfig.Pattern.create();
defer pat.destroy();
var os = fontconfig.ObjectSet.create();
defer os.destroy();
var fs = cfg.fontList(pat, os);
defer fs.destroy();
// Note: this is environmental, but in general we expect all our
// testing environments to have at least one font.
try testing.expect(fs.fonts().len > 0);
}
test "fc-match" {
const testing = std.testing;
var cfg = fontconfig.initLoadConfigAndFonts();
defer cfg.destroy();
var pat = fontconfig.Pattern.create();
errdefer pat.destroy();
try testing.expect(cfg.substituteWithPat(pat, .pattern));
pat.defaultSubstitute();
const result = cfg.fontSort(pat, false, null);
errdefer result.fs.destroy();
var fs = fontconfig.FontSet.create();
defer fs.destroy();
defer for (fs.fonts()) |font| font.destroy();
{
const fonts = result.fs.fonts();
try testing.expect(fonts.len > 0);
for (fonts) |font| {
const pat_prep = try cfg.fontRenderPrepare(pat, font);
try testing.expect(fs.add(pat_prep));
}
result.fs.destroy();
pat.destroy();
}
{
for (fs.fonts()) |font| {
var it = font.objectIterator();
while (it.next()) {
try testing.expect(it.object().len > 0);
try testing.expect(it.valueLen() > 0);
var value_it = it.valueIterator();
while (value_it.next()) |entry| {
try testing.expect(entry.value != .unknown);
}
}
}
}
}
+76
View File
@@ -0,0 +1,76 @@
const std = @import("std");
const assert = std.debug.assert;
const c = @import("c.zig").c;
const CharSet = @import("main.zig").CharSet;
const LangSet = @import("main.zig").LangSet;
const Matrix = @import("main.zig").Matrix;
const Range = @import("main.zig").Range;
pub const Type = enum(c_int) {
unknown = c.FcTypeUnknown,
void = c.FcTypeVoid,
integer = c.FcTypeInteger,
double = c.FcTypeDouble,
string = c.FcTypeString,
bool = c.FcTypeBool,
matrix = c.FcTypeMatrix,
char_set = c.FcTypeCharSet,
ft_face = c.FcTypeFTFace,
lang_set = c.FcTypeLangSet,
range = c.FcTypeRange,
};
pub const Value = union(Type) {
unknown: void,
void: void,
integer: i32,
double: f64,
string: [:0]const u8,
bool: bool,
matrix: *const Matrix,
char_set: *const CharSet,
ft_face: *anyopaque,
lang_set: *const LangSet,
range: *const Range,
pub fn init(cvalue: *c.struct__FcValue) Value {
return switch (@as(Type, @enumFromInt(cvalue.type))) {
.unknown => .{ .unknown = {} },
.void => .{ .void = {} },
.string => .{ .string = std.mem.sliceTo(cvalue.u.s, 0) },
.integer => .{ .integer = @intCast(cvalue.u.i) },
.double => .{ .double = cvalue.u.d },
.bool => .{ .bool = cvalue.u.b == c.FcTrue },
.matrix => .{ .matrix = @ptrCast(cvalue.u.m) },
.char_set => .{ .char_set = @ptrCast(cvalue.u.c) },
.ft_face => .{ .ft_face = @ptrCast(cvalue.u.f) },
.lang_set => .{ .lang_set = @ptrCast(cvalue.u.l) },
.range => .{ .range = @ptrCast(cvalue.u.r) },
};
}
pub fn cval(self: Value) c.struct__FcValue {
return .{
.type = @intFromEnum(std.meta.activeTag(self)),
.u = switch (self) {
.unknown => undefined,
.void => undefined,
.integer => |v| .{ .i = @intCast(v) },
.double => |v| .{ .d = v },
.string => |v| .{ .s = v.ptr },
.bool => |v| .{ .b = if (v) c.FcTrue else c.FcFalse },
.matrix => |v| .{ .m = @ptrCast(v) },
.char_set => |v| .{ .c = @ptrCast(v) },
.ft_face => |v| .{ .f = v },
.lang_set => |v| .{ .l = @ptrCast(v) },
.range => |v| .{ .r = @ptrCast(v) },
},
};
}
};
pub const ValueBinding = enum(c_int) {
weak = c.FcValueBindingWeak,
strong = c.FcValueBindingStrong,
same = c.FcValueBindingSame,
};
+92
View File
@@ -0,0 +1,92 @@
const Library = @This();
const std = @import("std");
const c = @import("c.zig").c;
const Face = @import("face.zig").Face;
const errors = @import("errors.zig");
const Error = errors.Error;
const intToError = errors.intToError;
handle: c.FT_Library,
/// Initialize a new FreeType library object. The set of modules that are
/// registered by this function is determined at build time.
pub fn init() Error!Library {
var res = Library{ .handle = undefined };
try intToError(c.FT_Init_FreeType(&res.handle));
return res;
}
/// Destroy a given FreeType library object and all of its children,
/// including resources, drivers, faces, sizes, etc.
pub fn deinit(self: Library) void {
_ = c.FT_Done_FreeType(self.handle);
}
/// Return the version of the FreeType library being used. This is useful when
/// dynamically linking to the library, since one cannot use the macros
/// FREETYPE_MAJOR, FREETYPE_MINOR, and FREETYPE_PATCH.
pub fn version(self: Library) Version {
var v: Version = undefined;
c.FT_Library_Version(self.handle, &v.major, &v.minor, &v.patch);
return v;
}
/// Call FT_New_Face to open a font from a file.
pub fn initFace(self: Library, path: [:0]const u8, index: i32) Error!Face {
var face: Face = undefined;
try intToError(c.FT_New_Face(
self.handle,
path.ptr,
index,
&face.handle,
));
return face;
}
/// Call FT_Open_Face to open a font that has been loaded into memory.
pub fn initMemoryFace(self: Library, data: []const u8, index: i32) Error!Face {
var face: Face = undefined;
try intToError(c.FT_New_Memory_Face(
self.handle,
data.ptr,
@intCast(data.len),
index,
&face.handle,
));
return face;
}
/// Call when you're done with a loaded MM var.
pub fn doneMMVar(self: Library, mm: *c.FT_MM_Var) void {
_ = c.FT_Done_MM_Var(self.handle, mm);
}
pub const Version = struct {
major: i32,
minor: i32,
patch: i32,
/// Convert the version to a string. The buffer should be able to
/// accommodate the size, recommended to be at least 8 chars wide.
/// The returned slice will be a slice of buf that contains the full
/// version string.
pub fn toString(self: Version, buf: []u8) ![]const u8 {
return try std.fmt.bufPrint(buf, "{d}.{d}.{d}", .{
self.major, self.minor, self.patch,
});
}
};
test "basics" {
const testing = std.testing;
var lib = try init();
defer lib.deinit();
const vsn = lib.version();
try testing.expect(vsn.major > 1);
var buf: [32]u8 = undefined;
_ = try vsn.toString(&buf);
}
+219
View File
@@ -0,0 +1,219 @@
const std = @import("std");
pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const libpng_enabled = b.option(bool, "enable-libpng", "Build libpng") orelse false;
const module = b.addModule("freetype", .{
.root_source_file = b.path("main.zig"),
.target = target,
.optimize = optimize,
});
// For dynamic linking, we prefer dynamic linking and to search by
// mode first. Mode first will search all paths for a dynamic library
// before falling back to static.
const dynamic_link_opts: std.Build.Module.LinkSystemLibraryOptions = .{
.preferred_link_mode = .dynamic,
.search_strategy = .mode_first,
};
var test_exe: ?*std.Build.Step.Compile = null;
if (target.query.isNative()) {
test_exe = b.addTest(.{
.name = "test",
.root_module = b.createModule(.{
.root_source_file = b.path("main.zig"),
.target = target,
.optimize = optimize,
}),
});
const tests_run = b.addRunArtifact(test_exe.?);
const test_step = b.step("test", "Run tests");
test_step.dependOn(&tests_run.step);
}
module.addIncludePath(b.path(""));
if (b.systemIntegrationOption("freetype", .{})) {
module.linkSystemLibrary("freetype2", dynamic_link_opts);
if (test_exe) |exe| {
exe.linkSystemLibrary2("freetype2", dynamic_link_opts);
}
} else {
const lib = try buildLib(b, module, .{
.target = target,
.optimize = optimize,
.libpng_enabled = libpng_enabled,
.dynamic_link_opts = dynamic_link_opts,
});
if (test_exe) |exe| {
exe.linkLibrary(lib);
}
}
}
fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Build.Step.Compile {
const target = options.target;
const optimize = options.optimize;
const libpng_enabled = options.libpng_enabled;
const lib = b.addLibrary(.{
.name = "freetype",
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
}),
.linkage = .static,
});
lib.linkLibC();
if (target.result.os.tag.isDarwin()) {
const apple_sdk = @import("apple_sdk");
try apple_sdk.addPaths(b, lib);
}
var flags: std.ArrayList([]const u8) = .empty;
defer flags.deinit(b.allocator);
try flags.appendSlice(b.allocator, &.{
"-DFT2_BUILD_LIBRARY",
"-DFT_CONFIG_OPTION_SYSTEM_ZLIB=1",
"-fno-sanitize=undefined",
});
if (target.result.os.tag != .windows) {
try flags.appendSlice(b.allocator, &.{
"-DHAVE_UNISTD_H",
"-DHAVE_FCNTL_H",
});
}
if (target.result.os.tag == .freebsd or target.result.abi == .musl) {
try flags.append(b.allocator, "-fPIC");
}
const dynamic_link_opts = options.dynamic_link_opts;
// Zlib
if (b.systemIntegrationOption("zlib", .{})) {
lib.linkSystemLibrary2("zlib", dynamic_link_opts);
} else {
const zlib_dep = b.dependency("zlib", .{ .target = target, .optimize = optimize });
lib.linkLibrary(zlib_dep.artifact("z"));
}
// Libpng
_ = b.systemIntegrationOption("libpng", .{}); // So it shows up in help
if (libpng_enabled) {
try flags.append(b.allocator, "-DFT_CONFIG_OPTION_USE_PNG=1");
if (b.systemIntegrationOption("libpng", .{})) {
lib.linkSystemLibrary2("libpng", dynamic_link_opts);
} else {
const libpng_dep = b.dependency(
"libpng",
.{ .target = target, .optimize = optimize },
);
lib.linkLibrary(libpng_dep.artifact("png"));
}
}
if (b.lazyDependency("freetype", .{})) |upstream| {
lib.addIncludePath(upstream.path("include"));
module.addIncludePath(upstream.path("include"));
lib.addCSourceFiles(.{
.root = upstream.path(""),
.files = srcs,
.flags = flags.items,
});
switch (target.result.os.tag) {
.linux => lib.addCSourceFile(.{
.file = upstream.path("builds/unix/ftsystem.c"),
.flags = flags.items,
}),
.windows => lib.addCSourceFile(.{
.file = upstream.path("builds/windows/ftsystem.c"),
.flags = flags.items,
}),
else => lib.addCSourceFile(.{
.file = upstream.path("src/base/ftsystem.c"),
.flags = flags.items,
}),
}
switch (target.result.os.tag) {
.windows => {
lib.addCSourceFile(.{
.file = upstream.path("builds/windows/ftdebug.c"),
.flags = flags.items,
});
lib.addWin32ResourceFile(.{
.file = upstream.path("src/base/ftver.rc"),
});
},
else => lib.addCSourceFile(.{
.file = upstream.path("src/base/ftdebug.c"),
.flags = flags.items,
}),
}
lib.installHeader(b.path("freetype-zig.h"), "freetype-zig.h");
lib.installHeadersDirectory(
upstream.path("include"),
"",
.{ .include_extensions = &.{".h"} },
);
}
b.installArtifact(lib);
return lib;
}
const srcs: []const []const u8 = &.{
"src/autofit/autofit.c",
"src/base/ftbase.c",
"src/base/ftbbox.c",
"src/base/ftbdf.c",
"src/base/ftbitmap.c",
"src/base/ftcid.c",
"src/base/ftfstype.c",
"src/base/ftgasp.c",
"src/base/ftglyph.c",
"src/base/ftgxval.c",
"src/base/ftinit.c",
"src/base/ftmm.c",
"src/base/ftotval.c",
"src/base/ftpatent.c",
"src/base/ftpfr.c",
"src/base/ftstroke.c",
"src/base/ftsynth.c",
"src/base/fttype1.c",
"src/base/ftwinfnt.c",
"src/bdf/bdf.c",
"src/bzip2/ftbzip2.c",
"src/cache/ftcache.c",
"src/cff/cff.c",
"src/cid/type1cid.c",
"src/gzip/ftgzip.c",
"src/lzw/ftlzw.c",
"src/pcf/pcf.c",
"src/pfr/pfr.c",
"src/psaux/psaux.c",
"src/pshinter/pshinter.c",
"src/psnames/psnames.c",
"src/raster/raster.c",
"src/sdf/sdf.c",
"src/sfnt/sfnt.c",
"src/smooth/smooth.c",
"src/svg/svg.c",
"src/truetype/truetype.c",
"src/type1/type1.c",
"src/type42/type42.c",
"src/winfonts/winfnt.c",
};
+18
View File
@@ -0,0 +1,18 @@
.{
.name = .freetype,
.version = "2.13.2",
.fingerprint = 0xac2059b6f7bbfe0a,
.paths = .{""},
.dependencies = .{
// freetype/freetype
.freetype = .{
.url = "https://deps.files.ghostty.org/freetype-1220b81f6ecfb3fd222f76cf9106fecfa6554ab07ec7fdc4124b9bb063ae2adf969d.tar.gz",
.hash = "N-V-__8AAKLKpwC4H27Ps_0iL3bPkQb-z6ZVSrB-x_3EEkub",
.lazy = true,
},
.apple_sdk = .{ .path = "../apple-sdk" },
.libpng = .{ .path = "../libpng" },
.zlib = .{ .path = "../zlib" },
},
}
+3
View File
@@ -0,0 +1,3 @@
pub const c = @cImport({
@cInclude("freetype-zig.h");
});
+8
View File
@@ -0,0 +1,8 @@
const std = @import("std");
const c = @import("c.zig").c;
/// Compute (a*b)/0x10000 with maximum accuracy. Its main use is to multiply
/// a given value by a 16.16 fixed-point factor.
pub fn mulFix(a: i32, b: i32) i32 {
return @intCast(c.FT_MulFix(a, b));
}
+295
View File
@@ -0,0 +1,295 @@
const c = @import("c.zig").c;
// Thanks to Mach (https://github.com/hexops/mach) for this work, I didn't
// do this manually. I wrote the other Freetype bindings by hand but this
// one was... too tedius.
pub const Error = error{
CannotOpenResource,
UnknownFileFormat,
InvalidFileFormat,
InvalidVersion,
LowerModuleVersion,
InvalidArgument,
UnimplementedFeature,
InvalidTable,
InvalidOffset,
ArrayTooLarge,
MissingModule,
MissingProperty,
InvalidGlyphIndex,
InvalidCharacterCode,
InvalidGlyphFormat,
CannotRenderGlyph,
InvalidOutline,
InvalidComposite,
TooManyHints,
InvalidPixelSize,
InvalidHandle,
InvalidLibraryHandle,
InvalidDriverHandle,
InvalidFaceHandle,
InvalidSizeHandle,
InvalidSlotHandle,
InvalidCharMapHandle,
InvalidCacheHandle,
InvalidStreamHandle,
TooManyDrivers,
TooManyExtensions,
OutOfMemory,
UnlistedObject,
CannotOpenStream,
InvalidStreamSeek,
InvalidStreamSkip,
InvalidStreamRead,
InvalidStreamOperation,
InvalidFrameOperation,
NestedFrameAccess,
InvalidFrameRead,
RasterUninitialized,
RasterCorrupted,
RasterOverflow,
RasterNegativeHeight,
TooManyCaches,
InvalidOpcode,
TooFewArguments,
StackOverflow,
CodeOverflow,
BadArgument,
DivideByZero,
InvalidReference,
DebugOpCode,
ENDFInExecStream,
NestedDEFS,
InvalidCodeRange,
ExecutionTooLong,
TooManyFunctionDefs,
TooManyInstructionDefs,
TableMissing,
HorizHeaderMissing,
LocationsMissing,
NameTableMissing,
CMapTableMissing,
HmtxTableMissing,
PostTableMissing,
InvalidHorizMetrics,
InvalidCharMapFormat,
InvalidPPem,
InvalidVertMetrics,
CouldNotFindContext,
InvalidPostTableFormat,
InvalidPostTable,
Syntax,
StackUnderflow,
Ignore,
NoUnicodeGlyphName,
MissingStartfontField,
MissingFontField,
MissingSizeField,
MissingFontboundingboxField,
MissingCharsField,
MissingStartcharField,
MissingEncodingField,
MissingBbxField,
BbxTooBig,
CorruptedFontHeader,
CorruptedFontGlyphs,
UnknownFreetypeError,
};
pub fn intToError(err: c_int) Error!void {
return switch (err) {
c.FT_Err_Ok => {},
c.FT_Err_Cannot_Open_Resource => Error.CannotOpenResource,
c.FT_Err_Unknown_File_Format => Error.UnknownFileFormat,
c.FT_Err_Invalid_File_Format => Error.InvalidFileFormat,
c.FT_Err_Invalid_Version => Error.InvalidVersion,
c.FT_Err_Lower_Module_Version => Error.LowerModuleVersion,
c.FT_Err_Invalid_Argument => Error.InvalidArgument,
c.FT_Err_Unimplemented_Feature => Error.UnimplementedFeature,
c.FT_Err_Invalid_Table => Error.InvalidTable,
c.FT_Err_Invalid_Offset => Error.InvalidOffset,
c.FT_Err_Array_Too_Large => Error.ArrayTooLarge,
c.FT_Err_Missing_Module => Error.MissingModule,
c.FT_Err_Missing_Property => Error.MissingProperty,
c.FT_Err_Invalid_Glyph_Index => Error.InvalidGlyphIndex,
c.FT_Err_Invalid_Character_Code => Error.InvalidCharacterCode,
c.FT_Err_Invalid_Glyph_Format => Error.InvalidGlyphFormat,
c.FT_Err_Cannot_Render_Glyph => Error.CannotRenderGlyph,
c.FT_Err_Invalid_Outline => Error.InvalidOutline,
c.FT_Err_Invalid_Composite => Error.InvalidComposite,
c.FT_Err_Too_Many_Hints => Error.TooManyHints,
c.FT_Err_Invalid_Pixel_Size => Error.InvalidPixelSize,
c.FT_Err_Invalid_Handle => Error.InvalidHandle,
c.FT_Err_Invalid_Library_Handle => Error.InvalidLibraryHandle,
c.FT_Err_Invalid_Driver_Handle => Error.InvalidDriverHandle,
c.FT_Err_Invalid_Face_Handle => Error.InvalidFaceHandle,
c.FT_Err_Invalid_Size_Handle => Error.InvalidSizeHandle,
c.FT_Err_Invalid_Slot_Handle => Error.InvalidSlotHandle,
c.FT_Err_Invalid_CharMap_Handle => Error.InvalidCharMapHandle,
c.FT_Err_Invalid_Cache_Handle => Error.InvalidCacheHandle,
c.FT_Err_Invalid_Stream_Handle => Error.InvalidStreamHandle,
c.FT_Err_Too_Many_Drivers => Error.TooManyDrivers,
c.FT_Err_Too_Many_Extensions => Error.TooManyExtensions,
c.FT_Err_Out_Of_Memory => Error.OutOfMemory,
c.FT_Err_Unlisted_Object => Error.UnlistedObject,
c.FT_Err_Cannot_Open_Stream => Error.CannotOpenStream,
c.FT_Err_Invalid_Stream_Seek => Error.InvalidStreamSeek,
c.FT_Err_Invalid_Stream_Skip => Error.InvalidStreamSkip,
c.FT_Err_Invalid_Stream_Read => Error.InvalidStreamRead,
c.FT_Err_Invalid_Stream_Operation => Error.InvalidStreamOperation,
c.FT_Err_Invalid_Frame_Operation => Error.InvalidFrameOperation,
c.FT_Err_Nested_Frame_Access => Error.NestedFrameAccess,
c.FT_Err_Invalid_Frame_Read => Error.InvalidFrameRead,
c.FT_Err_Raster_Uninitialized => Error.RasterUninitialized,
c.FT_Err_Raster_Corrupted => Error.RasterCorrupted,
c.FT_Err_Raster_Overflow => Error.RasterOverflow,
c.FT_Err_Raster_Negative_Height => Error.RasterNegativeHeight,
c.FT_Err_Too_Many_Caches => Error.TooManyCaches,
c.FT_Err_Invalid_Opcode => Error.InvalidOpcode,
c.FT_Err_Too_Few_Arguments => Error.TooFewArguments,
c.FT_Err_Stack_Overflow => Error.StackOverflow,
c.FT_Err_Code_Overflow => Error.CodeOverflow,
c.FT_Err_Bad_Argument => Error.BadArgument,
c.FT_Err_Divide_By_Zero => Error.DivideByZero,
c.FT_Err_Invalid_Reference => Error.InvalidReference,
c.FT_Err_Debug_OpCode => Error.DebugOpCode,
c.FT_Err_ENDF_In_Exec_Stream => Error.ENDFInExecStream,
c.FT_Err_Nested_DEFS => Error.NestedDEFS,
c.FT_Err_Invalid_CodeRange => Error.InvalidCodeRange,
c.FT_Err_Execution_Too_Long => Error.ExecutionTooLong,
c.FT_Err_Too_Many_Function_Defs => Error.TooManyFunctionDefs,
c.FT_Err_Too_Many_Instruction_Defs => Error.TooManyInstructionDefs,
c.FT_Err_Table_Missing => Error.TableMissing,
c.FT_Err_Horiz_Header_Missing => Error.HorizHeaderMissing,
c.FT_Err_Locations_Missing => Error.LocationsMissing,
c.FT_Err_Name_Table_Missing => Error.NameTableMissing,
c.FT_Err_CMap_Table_Missing => Error.CMapTableMissing,
c.FT_Err_Hmtx_Table_Missing => Error.HmtxTableMissing,
c.FT_Err_Post_Table_Missing => Error.PostTableMissing,
c.FT_Err_Invalid_Horiz_Metrics => Error.InvalidHorizMetrics,
c.FT_Err_Invalid_CharMap_Format => Error.InvalidCharMapFormat,
c.FT_Err_Invalid_PPem => Error.InvalidPPem,
c.FT_Err_Invalid_Vert_Metrics => Error.InvalidVertMetrics,
c.FT_Err_Could_Not_Find_Context => Error.CouldNotFindContext,
c.FT_Err_Invalid_Post_Table_Format => Error.InvalidPostTableFormat,
c.FT_Err_Invalid_Post_Table => Error.InvalidPostTable,
c.FT_Err_Syntax_Error => Error.Syntax,
c.FT_Err_Stack_Underflow => Error.StackUnderflow,
c.FT_Err_Ignore => Error.Ignore,
c.FT_Err_No_Unicode_Glyph_Name => Error.NoUnicodeGlyphName,
c.FT_Err_Missing_Startfont_Field => Error.MissingStartfontField,
c.FT_Err_Missing_Font_Field => Error.MissingFontField,
c.FT_Err_Missing_Size_Field => Error.MissingSizeField,
c.FT_Err_Missing_Fontboundingbox_Field => Error.MissingFontboundingboxField,
c.FT_Err_Missing_Chars_Field => Error.MissingCharsField,
c.FT_Err_Missing_Startchar_Field => Error.MissingStartcharField,
c.FT_Err_Missing_Encoding_Field => Error.MissingEncodingField,
c.FT_Err_Missing_Bbx_Field => Error.MissingBbxField,
c.FT_Err_Bbx_Too_Big => Error.BbxTooBig,
c.FT_Err_Corrupted_Font_Header => Error.CorruptedFontHeader,
c.FT_Err_Corrupted_Font_Glyphs => Error.CorruptedFontGlyphs,
else => Error.UnknownFreetypeError,
};
}
pub fn errorToInt(err: Error) c_int {
return switch (err) {
Error.CannotOpenResource => c.FT_Err_Cannot_Open_Resource,
Error.UnknownFileFormat => c.FT_Err_Unknown_File_Format,
Error.InvalidFileFormat => c.FT_Err_Invalid_File_Format,
Error.InvalidVersion => c.FT_Err_Invalid_Version,
Error.LowerModuleVersion => c.FT_Err_Lower_Module_Version,
Error.InvalidArgument => c.FT_Err_Invalid_Argument,
Error.UnimplementedFeature => c.FT_Err_Unimplemented_Feature,
Error.InvalidTable => c.FT_Err_Invalid_Table,
Error.InvalidOffset => c.FT_Err_Invalid_Offset,
Error.ArrayTooLarge => c.FT_Err_Array_Too_Large,
Error.MissingModule => c.FT_Err_Missing_Module,
Error.MissingProperty => c.FT_Err_Missing_Property,
Error.InvalidGlyphIndex => c.FT_Err_Invalid_Glyph_Index,
Error.InvalidCharacterCode => c.FT_Err_Invalid_Character_Code,
Error.InvalidGlyphFormat => c.FT_Err_Invalid_Glyph_Format,
Error.CannotRenderGlyph => c.FT_Err_Cannot_Render_Glyph,
Error.InvalidOutline => c.FT_Err_Invalid_Outline,
Error.InvalidComposite => c.FT_Err_Invalid_Composite,
Error.TooManyHints => c.FT_Err_Too_Many_Hints,
Error.InvalidPixelSize => c.FT_Err_Invalid_Pixel_Size,
Error.InvalidHandle => c.FT_Err_Invalid_Handle,
Error.InvalidLibraryHandle => c.FT_Err_Invalid_Library_Handle,
Error.InvalidDriverHandle => c.FT_Err_Invalid_Driver_Handle,
Error.InvalidFaceHandle => c.FT_Err_Invalid_Face_Handle,
Error.InvalidSizeHandle => c.FT_Err_Invalid_Size_Handle,
Error.InvalidSlotHandle => c.FT_Err_Invalid_Slot_Handle,
Error.InvalidCharMapHandle => c.FT_Err_Invalid_CharMap_Handle,
Error.InvalidCacheHandle => c.FT_Err_Invalid_Cache_Handle,
Error.InvalidStreamHandle => c.FT_Err_Invalid_Stream_Handle,
Error.TooManyDrivers => c.FT_Err_Too_Many_Drivers,
Error.TooManyExtensions => c.FT_Err_Too_Many_Extensions,
Error.OutOfMemory => c.FT_Err_Out_Of_Memory,
Error.UnlistedObject => c.FT_Err_Unlisted_Object,
Error.CannotOpenStream => c.FT_Err_Cannot_Open_Stream,
Error.InvalidStreamSeek => c.FT_Err_Invalid_Stream_Seek,
Error.InvalidStreamSkip => c.FT_Err_Invalid_Stream_Skip,
Error.InvalidStreamRead => c.FT_Err_Invalid_Stream_Read,
Error.InvalidStreamOperation => c.FT_Err_Invalid_Stream_Operation,
Error.InvalidFrameOperation => c.FT_Err_Invalid_Frame_Operation,
Error.NestedFrameAccess => c.FT_Err_Nested_Frame_Access,
Error.InvalidFrameRead => c.FT_Err_Invalid_Frame_Read,
Error.RasterUninitialized => c.FT_Err_Raster_Uninitialized,
Error.RasterCorrupted => c.FT_Err_Raster_Corrupted,
Error.RasterOverflow => c.FT_Err_Raster_Overflow,
Error.RasterNegativeHeight => c.FT_Err_Raster_Negative_Height,
Error.TooManyCaches => c.FT_Err_Too_Many_Caches,
Error.InvalidOpcode => c.FT_Err_Invalid_Opcode,
Error.TooFewArguments => c.FT_Err_Too_Few_Arguments,
Error.StackOverflow => c.FT_Err_Stack_Overflow,
Error.CodeOverflow => c.FT_Err_Code_Overflow,
Error.BadArgument => c.FT_Err_Bad_Argument,
Error.DivideByZero => c.FT_Err_Divide_By_Zero,
Error.InvalidReference => c.FT_Err_Invalid_Reference,
Error.DebugOpCode => c.FT_Err_Debug_OpCode,
Error.ENDFInExecStream => c.FT_Err_ENDF_In_Exec_Stream,
Error.NestedDEFS => c.FT_Err_Nested_DEFS,
Error.InvalidCodeRange => c.FT_Err_Invalid_CodeRange,
Error.ExecutionTooLong => c.FT_Err_Execution_Too_Long,
Error.TooManyFunctionDefs => c.FT_Err_Too_Many_Function_Defs,
Error.TooManyInstructionDefs => c.FT_Err_Too_Many_Instruction_Defs,
Error.TableMissing => c.FT_Err_Table_Missing,
Error.HorizHeaderMissing => c.FT_Err_Horiz_Header_Missing,
Error.LocationsMissing => c.FT_Err_Locations_Missing,
Error.NameTableMissing => c.FT_Err_Name_Table_Missing,
Error.CMapTableMissing => c.FT_Err_CMap_Table_Missing,
Error.HmtxTableMissing => c.FT_Err_Hmtx_Table_Missing,
Error.PostTableMissing => c.FT_Err_Post_Table_Missing,
Error.InvalidHorizMetrics => c.FT_Err_Invalid_Horiz_Metrics,
Error.InvalidCharMapFormat => c.FT_Err_Invalid_CharMap_Format,
Error.InvalidPPem => c.FT_Err_Invalid_PPem,
Error.InvalidVertMetrics => c.FT_Err_Invalid_Vert_Metrics,
Error.CouldNotFindContext => c.FT_Err_Could_Not_Find_Context,
Error.InvalidPostTableFormat => c.FT_Err_Invalid_Post_Table_Format,
Error.InvalidPostTable => c.FT_Err_Invalid_Post_Table,
Error.Syntax => c.FT_Err_Syntax_Error,
Error.StackUnderflow => c.FT_Err_Stack_Underflow,
Error.Ignore => c.FT_Err_Ignore,
Error.NoUnicodeGlyphName => c.FT_Err_No_Unicode_Glyph_Name,
Error.MissingStartfontField => c.FT_Err_Missing_Startfont_Field,
Error.MissingFontField => c.FT_Err_Missing_Font_Field,
Error.MissingSizeField => c.FT_Err_Missing_Size_Field,
Error.MissingFontboundingboxField => c.FT_Err_Missing_Fontboundingbox_Field,
Error.MissingCharsField => c.FT_Err_Missing_Chars_Field,
Error.MissingStartcharField => c.FT_Err_Missing_Startchar_Field,
Error.MissingEncodingField => c.FT_Err_Missing_Encoding_Field,
Error.MissingBbxField => c.FT_Err_Missing_Bbx_Field,
Error.BbxTooBig => c.FT_Err_Bbx_Too_Big,
Error.CorruptedFontHeader => c.FT_Err_Corrupted_Font_Header,
Error.CorruptedFontGlyphs => c.FT_Err_Corrupted_Font_Glyphs,
};
}
test "error conversion" {
const expectError = @import("std").testing.expectError;
try intToError(c.FT_Err_Ok);
try expectError(Error.OutOfMemory, intToError(c.FT_Err_Out_Of_Memory));
}
+385
View File
@@ -0,0 +1,385 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const c = @import("c.zig").c;
const errors = @import("errors.zig");
const Library = @import("Library.zig");
const Tag = @import("tag.zig").Tag;
const Error = errors.Error;
const intToError = errors.intToError;
pub const Face = struct {
handle: c.FT_Face,
pub fn deinit(self: Face) void {
_ = c.FT_Done_Face(self.handle);
}
/// Increment the counter of the face.
pub fn ref(self: Face) void {
_ = c.FT_Reference_Face(self.handle);
}
/// A macro that returns true whenever a face object contains some
/// embedded bitmaps. See the available_sizes field of the FT_FaceRec structure.
pub fn hasFixedSizes(self: Face) bool {
return c.FT_HAS_FIXED_SIZES(self.handle);
}
/// A macro that returns true whenever a face object contains tables for
/// color glyphs.
pub fn hasColor(self: Face) bool {
return c.FT_HAS_COLOR(self.handle);
}
/// A macro that returns true whenever a face object contains an sbix
/// OpenType table and outline glyphs.
pub fn hasSBIX(self: Face) bool {
return c.FT_HAS_SBIX(self.handle);
}
/// A macro that returns true whenever a face object contains some
/// multiple masters.
pub fn hasMultipleMasters(self: Face) bool {
return c.FT_HAS_MULTIPLE_MASTERS(self.handle);
}
/// A macro that returns true whenever a face object contains a scalable
/// font face (true for TrueType, Type 1, Type 42, CID, OpenType/CFF,
/// and PFR font formats).
pub fn isScalable(self: Face) bool {
return c.FT_IS_SCALABLE(self.handle);
}
/// Select a given charmap by its encoding tag (as listed in freetype.h).
pub fn selectCharmap(self: Face, encoding: Encoding) Error!void {
return intToError(c.FT_Select_Charmap(self.handle, @intCast(@intFromEnum(encoding))));
}
/// Call FT_Request_Size to request the nominal size (in points).
pub fn setCharSize(
self: Face,
char_width: i32,
char_height: i32,
horz_resolution: u16,
vert_resolution: u16,
) Error!void {
return intToError(c.FT_Set_Char_Size(
self.handle,
char_width,
char_height,
horz_resolution,
vert_resolution,
));
}
/// Select a bitmap strike. To be more precise, this function sets the
/// scaling factors of the active FT_Size object in a face so that bitmaps
/// from this particular strike are taken by FT_Load_Glyph and friends.
pub fn selectSize(self: Face, idx: i32) Error!void {
return intToError(c.FT_Select_Size(self.handle, idx));
}
/// Return the glyph index of a given character code. This function uses
/// the currently selected charmap to do the mapping.
pub fn getCharIndex(self: Face, char: u32) ?u32 {
const i = c.FT_Get_Char_Index(self.handle, char);
return if (i == 0) null else i;
}
/// Load a glyph into the glyph slot of a face object.
pub fn loadGlyph(self: Face, glyph_index: u32, load_flags: LoadFlags) Error!void {
return intToError(c.FT_Load_Glyph(
self.handle,
glyph_index,
@bitCast(load_flags),
));
}
/// Convert a given glyph image to a bitmap.
pub fn renderGlyph(self: Face, render_mode: RenderMode) Error!void {
return intToError(c.FT_Render_Glyph(
self.handle.*.glyph,
@intCast(@intFromEnum(render_mode)),
));
}
/// Return a pointer to a given SFNT table stored within a face.
pub fn getSfntTable(self: Face, comptime tag: SfntTag) ?*tag.DataType() {
return @ptrCast(@alignCast(c.FT_Get_Sfnt_Table(
self.handle,
@intFromEnum(tag),
)));
}
/// Retrieve the number of name strings in the SFNT name table.
pub fn getSfntNameCount(self: Face) usize {
return @intCast(c.FT_Get_Sfnt_Name_Count(self.handle));
}
/// Retrieve a string of the SFNT name table for a given index.
pub fn getSfntName(self: Face, i: usize) Error!c.FT_SfntName {
var name: c.FT_SfntName = undefined;
const res = c.FT_Get_Sfnt_Name(self.handle, @intCast(i), &name);
return if (intToError(res)) |_| name else |err| err;
}
/// Load any SFNT font table into client memory.
pub fn loadSfntTable(
self: Face,
alloc: Allocator,
tag: Tag,
) (Allocator.Error || Error)!?[]u8 {
const tag_c: c_ulong = @intCast(@as(u32, @bitCast(tag)));
// Get the length of the table in bytes
var len: c_ulong = 0;
var res = c.FT_Load_Sfnt_Table(self.handle, tag_c, 0, null, &len);
_ = intToError(res) catch |err| return err;
// If our length is zero we don't have a table.
if (len == 0) return null;
// Allocate a buffer to hold the table and load it
const buf = try alloc.alloc(u8, len);
errdefer alloc.free(buf);
res = c.FT_Load_Sfnt_Table(self.handle, tag_c, 0, buf.ptr, &len);
_ = intToError(res) catch |err| return err;
return buf;
}
/// Check whether a given SFNT table is available in a face.
pub fn hasSfntTable(self: Face, tag: Tag) bool {
const tag_c: c_ulong = @intCast(@as(u32, @bitCast(tag)));
var len: c_ulong = 0;
const res = c.FT_Load_Sfnt_Table(self.handle, tag_c, 0, null, &len);
_ = intToError(res) catch return false;
return len != 0;
}
/// Retrieve the font variation descriptor for a font.
pub fn getMMVar(self: Face) Error!*c.FT_MM_Var {
var result: *c.FT_MM_Var = undefined;
const res = c.FT_Get_MM_Var(self.handle, @ptrCast(&result));
return if (intToError(res)) |_| result else |err| err;
}
/// Get the design coordinates of the currently selected interpolated font.
pub fn getVarDesignCoordinates(self: Face, coords: []c.FT_Fixed) Error!void {
const res = c.FT_Get_Var_Design_Coordinates(
self.handle,
@intCast(coords.len),
coords.ptr,
);
return intToError(res);
}
/// Choose an interpolated font design through design coordinates.
pub fn setVarDesignCoordinates(self: Face, coords: []c.FT_Fixed) Error!void {
const res = c.FT_Set_Var_Design_Coordinates(
self.handle,
@intCast(coords.len),
coords.ptr,
);
return intToError(res);
}
/// Set the transformation that is applied to glyph images when they are
/// loaded into a glyph slot through FT_Load_Glyph.
pub fn setTransform(
self: Face,
matrix: ?*const c.FT_Matrix,
delta: ?*const c.FT_Vector,
) void {
c.FT_Set_Transform(
self.handle,
@ptrCast(@constCast(matrix)),
@ptrCast(@constCast(delta)),
);
}
};
/// An enumeration to specify indices of SFNT tables loaded and parsed by
/// FreeType during initialization of an SFNT font. Used in the
/// FT_Get_Sfnt_Table API function.
pub const SfntTag = enum(c_int) {
head = c.FT_SFNT_HEAD,
maxp = c.FT_SFNT_MAXP,
os2 = c.FT_SFNT_OS2,
hhea = c.FT_SFNT_HHEA,
vhea = c.FT_SFNT_VHEA,
post = c.FT_SFNT_POST,
pclt = c.FT_SFNT_PCLT,
/// The data type for a given sfnt tag.
pub fn DataType(comptime self: SfntTag) type {
return switch (self) {
.os2 => c.TT_OS2,
.head => c.TT_Header,
.post => c.TT_Postscript,
.hhea => c.TT_HoriHeader,
else => unreachable, // As-needed...
};
}
};
/// An enumeration to specify character sets supported by charmaps. Used in the
/// FT_Select_Charmap API function.
pub const Encoding = enum(u31) {
none = c.FT_ENCODING_NONE,
ms_symbol = c.FT_ENCODING_MS_SYMBOL,
unicode = c.FT_ENCODING_UNICODE,
sjis = c.FT_ENCODING_SJIS,
prc = c.FT_ENCODING_PRC,
big5 = c.FT_ENCODING_BIG5,
wansung = c.FT_ENCODING_WANSUNG,
johab = c.FT_ENCODING_JOHAB,
adobe_standard = c.FT_ENCODING_ADOBE_STANDARD,
adobe_expert = c.FT_ENCODING_ADOBE_EXPERT,
adobe_custom = c.FT_ENCODING_ADOBE_CUSTOM,
adobe_latin_1 = c.FT_ENCODING_ADOBE_LATIN_1,
old_latin_2 = c.FT_ENCODING_OLD_LATIN_2,
apple_roman = c.FT_ENCODING_APPLE_ROMAN,
};
/// https://freetype.org/freetype2/docs/reference/ft2-glyph_retrieval.html#ft_render_mode
pub const RenderMode = enum(c_uint) {
normal = c.FT_RENDER_MODE_NORMAL,
light = c.FT_RENDER_MODE_LIGHT,
mono = c.FT_RENDER_MODE_MONO,
lcd = c.FT_RENDER_MODE_LCD,
lcd_v = c.FT_RENDER_MODE_LCD_V,
sdf = c.FT_RENDER_MODE_SDF,
};
/// A collection of flags for FT_Load_Glyph that indicate
/// what kind of operations to perform during glyph loading.
///
/// Some of these flags are not included in the official FreeType
/// documentation, but are nevertheless present and named in the
/// header, so the names have been copied from there.
pub const LoadFlags = packed struct(c_int) {
no_scale: bool = false,
no_hinting: bool = false,
render: bool = false,
no_bitmap: bool = false,
vertical_layout: bool = false,
force_autohint: bool = false,
crop_bitmap: bool = false,
pedantic: bool = false,
advance_only: bool = false,
ignore_global_advance_width: bool = false,
no_recurse: bool = false,
ignore_transform: bool = false,
monochrome: bool = false,
linear_design: bool = false,
sbits_only: bool = false,
no_autohint: bool = false,
target: Target = .normal,
color: bool = false,
compute_metrics: bool = false,
bitmap_metrics_only: bool = false,
svg_only: bool = false,
no_svg: bool = false,
_padding: u7 = 0,
pub const Target = enum(u4) {
normal = 0,
light = 1,
mono = 2,
lcd = 3,
lcd_v = 4,
};
test "bitcast" {
const testing = std.testing;
const cval: i32 = c.FT_LOAD_RENDER | c.FT_LOAD_PEDANTIC | c.FT_LOAD_COLOR;
const flags = @as(LoadFlags, @bitCast(cval));
try testing.expect(!flags.no_hinting);
try testing.expect(flags.render);
try testing.expect(flags.pedantic);
try testing.expect(flags.color);
// Verify bit alignment (for bit 9)
const cval2: i32 = c.FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
const flags2 = @as(LoadFlags, @bitCast(cval2));
try testing.expect(flags2.ignore_global_advance_width);
try testing.expect(!flags2.no_recurse);
}
test "all flags individually" {
const testing = std.testing;
try testing.expectEqual(
c.FT_LOAD_DEFAULT,
@as(c_int, @bitCast(LoadFlags{})),
);
inline for ([_]struct { c_int, []const u8 }{
.{ c.FT_LOAD_NO_SCALE, "no_scale" },
.{ c.FT_LOAD_NO_HINTING, "no_hinting" },
.{ c.FT_LOAD_RENDER, "render" },
.{ c.FT_LOAD_NO_BITMAP, "no_bitmap" },
.{ c.FT_LOAD_VERTICAL_LAYOUT, "vertical_layout" },
.{ c.FT_LOAD_FORCE_AUTOHINT, "force_autohint" },
.{ c.FT_LOAD_CROP_BITMAP, "crop_bitmap" },
.{ c.FT_LOAD_PEDANTIC, "pedantic" },
.{ c.FT_LOAD_ADVANCE_ONLY, "advance_only" },
.{ c.FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH, "ignore_global_advance_width" },
.{ c.FT_LOAD_NO_RECURSE, "no_recurse" },
.{ c.FT_LOAD_IGNORE_TRANSFORM, "ignore_transform" },
.{ c.FT_LOAD_MONOCHROME, "monochrome" },
.{ c.FT_LOAD_LINEAR_DESIGN, "linear_design" },
.{ c.FT_LOAD_SBITS_ONLY, "sbits_only" },
.{ c.FT_LOAD_NO_AUTOHINT, "no_autohint" },
.{ c.FT_LOAD_COLOR, "color" },
.{ c.FT_LOAD_COMPUTE_METRICS, "compute_metrics" },
.{ c.FT_LOAD_BITMAP_METRICS_ONLY, "bitmap_metrics_only" },
.{ c.FT_LOAD_SVG_ONLY, "svg_only" },
.{ c.FT_LOAD_NO_SVG, "no_svg" },
}) |pair| {
var flags: LoadFlags = .{};
@field(flags, pair[1]) = true;
try testing.expectEqual(pair[0], @as(c_int, @bitCast(flags)));
}
}
test "all load targets" {
const testing = std.testing;
inline for ([_]struct { c_int, Target }{
.{ c.FT_LOAD_TARGET_NORMAL, .normal },
.{ c.FT_LOAD_TARGET_LIGHT, .light },
.{ c.FT_LOAD_TARGET_MONO, .mono },
.{ c.FT_LOAD_TARGET_LCD, .lcd },
.{ c.FT_LOAD_TARGET_LCD_V, .lcd_v },
}) |pair| {
const flags: LoadFlags = .{ .target = pair[1] };
try testing.expectEqual(pair[0], @as(c_int, @bitCast(flags)));
}
}
};
test "loading memory font" {
const testing = std.testing;
const font_data = @import("test.zig").font_regular;
var lib = try Library.init();
defer lib.deinit();
var face = try lib.initMemoryFace(font_data, 0);
defer face.deinit();
// Try APIs
try face.selectCharmap(.unicode);
try testing.expect(!face.hasFixedSizes());
try face.setCharSize(12, 0, 0, 0);
// Try loading
const idx = face.getCharIndex('A').?;
try face.loadGlyph(idx, .{});
// Try getting a truetype table
const os2 = face.getSfntTable(.os2);
try testing.expect(os2 != null);
}
+9
View File
@@ -0,0 +1,9 @@
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_TRUETYPE_TABLES_H
#include <freetype/ftmm.h>
#include <freetype/ftoutln.h>
#include <freetype/ftsnames.h>
#include <freetype/ttnameid.h>
#include <freetype/ftbitmap.h>
#include <freetype/ftbbox.h>
+18
View File
@@ -0,0 +1,18 @@
const computations = @import("computations.zig");
const errors = @import("errors.zig");
const face = @import("face.zig");
const tag = @import("tag.zig");
pub const c = @import("c.zig").c;
pub const testing = @import("test.zig");
pub const Library = @import("Library.zig");
pub const Error = errors.Error;
pub const Face = face.Face;
pub const LoadFlags = face.LoadFlags;
pub const Tag = tag.Tag;
pub const mulFix = computations.mulFix;
test {
@import("std").testing.refAllDecls(@This());
}
Binary file not shown.
+17
View File
@@ -0,0 +1,17 @@
/// FT_Tag
pub const Tag = packed struct(u32) {
d: u8,
c: u8,
b: u8,
a: u8,
pub fn init(v: *const [4]u8) Tag {
return .{ .a = v[0], .b = v[1], .c = v[2], .d = v[3] };
}
/// Converts the ID to a string. The return value is only valid
/// for the lifetime of the self pointer.
pub fn str(self: Tag) [4]u8 {
return .{ self.a, self.b, self.c, self.d };
}
};
+1
View File
@@ -0,0 +1 @@
pub const font_regular = @embedFile("res/FiraCode-Regular.ttf");
+169
View File
@@ -0,0 +1,169 @@
const std = @import("std");
pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const module = b.addModule("glslang", .{
.root_source_file = b.path("main.zig"),
.target = target,
.optimize = optimize,
});
const upstream = b.lazyDependency("glslang", .{});
const lib = try buildGlslang(b, upstream, target, optimize);
b.installArtifact(lib);
if (upstream) |v| module.addIncludePath(v.path(""));
module.addIncludePath(b.path("override"));
if (target.query.isNative()) {
const test_exe = b.addTest(.{
.name = "test",
.root_module = b.createModule(.{
.root_source_file = b.path("main.zig"),
.target = target,
.optimize = optimize,
}),
});
test_exe.linkLibrary(lib);
const tests_run = b.addRunArtifact(test_exe);
const test_step = b.step("test", "Run tests");
test_step.dependOn(&tests_run.step);
// Uncomment this if we're debugging tests
// b.installArtifact(test_exe);
}
}
fn buildGlslang(
b: *std.Build,
upstream_: ?*std.Build.Dependency,
target: std.Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode,
) !*std.Build.Step.Compile {
const lib = b.addLibrary(.{
.name = "glslang",
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
}),
.linkage = .static,
});
lib.linkLibC();
// On MSVC, we must not use linkLibCpp because Zig unconditionally
// passes -nostdinc++ and then adds its bundled libc++/libc++abi
// include paths, which conflict with MSVC's own C++ runtime headers.
// The MSVC SDK include directories (added via linkLibC) contain
// both C and C++ headers, so linkLibCpp is not needed.
if (target.result.abi != .msvc) {
lib.linkLibCpp();
}
if (upstream_) |upstream| lib.addIncludePath(upstream.path(""));
lib.addIncludePath(b.path("override"));
if (target.result.os.tag.isDarwin()) {
const apple_sdk = @import("apple_sdk");
try apple_sdk.addPaths(b, lib);
}
var flags: std.ArrayList([]const u8) = .empty;
defer flags.deinit(b.allocator);
try flags.appendSlice(b.allocator, &.{
"-fno-sanitize=undefined",
"-fno-sanitize-trap=undefined",
});
// MSVC requires explicit std specification otherwise C++17 features
// like std::variant, std::filesystem, and inline variables are
// guarded behind _HAS_CXX17.
try flags.append(b.allocator, "-std=c++17");
if (target.result.os.tag == .freebsd or target.result.abi == .musl) {
try flags.append(b.allocator, "-fPIC");
}
if (upstream_) |upstream| {
lib.addCSourceFiles(.{
.root = upstream.path(""),
.flags = flags.items,
.files = &.{
// GenericCodeGen
"glslang/GenericCodeGen/CodeGen.cpp",
"glslang/GenericCodeGen/Link.cpp",
// MachineIndependent
//"MachineIndependent/glslang.y",
"glslang/MachineIndependent/glslang_tab.cpp",
"glslang/MachineIndependent/attribute.cpp",
"glslang/MachineIndependent/Constant.cpp",
"glslang/MachineIndependent/iomapper.cpp",
"glslang/MachineIndependent/InfoSink.cpp",
"glslang/MachineIndependent/Initialize.cpp",
"glslang/MachineIndependent/IntermTraverse.cpp",
"glslang/MachineIndependent/Intermediate.cpp",
"glslang/MachineIndependent/ParseContextBase.cpp",
"glslang/MachineIndependent/ParseHelper.cpp",
"glslang/MachineIndependent/PoolAlloc.cpp",
"glslang/MachineIndependent/RemoveTree.cpp",
"glslang/MachineIndependent/Scan.cpp",
"glslang/MachineIndependent/ShaderLang.cpp",
"glslang/MachineIndependent/SpirvIntrinsics.cpp",
"glslang/MachineIndependent/SymbolTable.cpp",
"glslang/MachineIndependent/Versions.cpp",
"glslang/MachineIndependent/intermOut.cpp",
"glslang/MachineIndependent/limits.cpp",
"glslang/MachineIndependent/linkValidate.cpp",
"glslang/MachineIndependent/parseConst.cpp",
"glslang/MachineIndependent/reflection.cpp",
"glslang/MachineIndependent/preprocessor/Pp.cpp",
"glslang/MachineIndependent/preprocessor/PpAtom.cpp",
"glslang/MachineIndependent/preprocessor/PpContext.cpp",
"glslang/MachineIndependent/preprocessor/PpScanner.cpp",
"glslang/MachineIndependent/preprocessor/PpTokens.cpp",
"glslang/MachineIndependent/propagateNoContraction.cpp",
// C Interface
"glslang/CInterface/glslang_c_interface.cpp",
// ResourceLimits
"glslang/ResourceLimits/ResourceLimits.cpp",
"glslang/ResourceLimits/resource_limits_c.cpp",
// SPIRV
"SPIRV/GlslangToSpv.cpp",
"SPIRV/InReadableOrder.cpp",
"SPIRV/Logger.cpp",
"SPIRV/SpvBuilder.cpp",
"SPIRV/SpvPostProcess.cpp",
"SPIRV/doc.cpp",
"SPIRV/disassemble.cpp",
"SPIRV/CInterface/spirv_c_interface.cpp",
},
});
if (target.result.os.tag != .windows) {
lib.addCSourceFiles(.{
.root = upstream.path(""),
.flags = flags.items,
.files = &.{
"glslang/OSDependent/Unix/ossource.cpp",
},
});
} else {
lib.addCSourceFiles(.{
.root = upstream.path(""),
.flags = flags.items,
.files = &.{
"glslang/OSDependent/Windows/ossource.cpp",
},
});
}
lib.installHeadersDirectory(
upstream.path(""),
"",
.{ .include_extensions = &.{".h"} },
);
}
return lib;
}
+16
View File
@@ -0,0 +1,16 @@
.{
.name = .glslang,
.version = "14.2.0",
.fingerprint = 0x274a35558e2e504,
.paths = .{""},
.dependencies = .{
// KhronosGroup/glslang
.glslang = .{
.url = "https://deps.files.ghostty.org/glslang-12201278a1a05c0ce0b6eb6026c65cd3e9247aa041b1c260324bf29cee559dd23ba1.tar.gz",
.hash = "N-V-__8AABzkUgISeKGgXAzgtutgJsZc0-kkeqBBscJgMkvy",
.lazy = true,
},
.apple_sdk = .{ .path = "../apple-sdk" },
},
}
+4
View File
@@ -0,0 +1,4 @@
pub const c = @cImport({
@cInclude("glslang/Include/glslang_c_interface.h");
@cInclude("glslang/Public/resource_limits_c.h");
});
+9
View File
@@ -0,0 +1,9 @@
const c = @import("c.zig").c;
pub fn init() !void {
if (c.glslang_initialize_process() == 0) return error.GlslangInitFailed;
}
pub fn finalize() void {
c.glslang_finalize_process();
}
+15
View File
@@ -0,0 +1,15 @@
const initpkg = @import("init.zig");
const program = @import("program.zig");
const shader = @import("shader.zig");
pub const c = @import("c.zig").c;
pub const testing = @import("test.zig");
pub const init = initpkg.init;
pub const finalize = initpkg.finalize;
pub const Program = program.Program;
pub const Shader = shader.Shader;
test {
@import("std").testing.refAllDecls(@This());
}
+62
View File
@@ -0,0 +1,62 @@
// Copyright (C) 2020 The Khronos Group Inc.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
//
// Neither the name of The Khronos Group Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#ifndef GLSLANG_BUILD_INFO
#define GLSLANG_BUILD_INFO
#define GLSLANG_VERSION_MAJOR 13
#define GLSLANG_VERSION_MINOR 1
#define GLSLANG_VERSION_PATCH 1
#define GLSLANG_VERSION_FLAVOR ""
#define GLSLANG_VERSION_GREATER_THAN(major, minor, patch) \
((GLSLANG_VERSION_MAJOR) > (major) || ((major) == GLSLANG_VERSION_MAJOR && \
((GLSLANG_VERSION_MINOR) > (minor) || ((minor) == GLSLANG_VERSION_MINOR && \
(GLSLANG_VERSION_PATCH) > (patch)))))
#define GLSLANG_VERSION_GREATER_OR_EQUAL_TO(major, minor, patch) \
((GLSLANG_VERSION_MAJOR) > (major) || ((major) == GLSLANG_VERSION_MAJOR && \
((GLSLANG_VERSION_MINOR) > (minor) || ((minor) == GLSLANG_VERSION_MINOR && \
(GLSLANG_VERSION_PATCH >= (patch))))))
#define GLSLANG_VERSION_LESS_THAN(major, minor, patch) \
((GLSLANG_VERSION_MAJOR) < (major) || ((major) == GLSLANG_VERSION_MAJOR && \
((GLSLANG_VERSION_MINOR) < (minor) || ((minor) == GLSLANG_VERSION_MINOR && \
(GLSLANG_VERSION_PATCH) < (patch)))))
#define GLSLANG_VERSION_LESS_OR_EQUAL_TO(major, minor, patch) \
((GLSLANG_VERSION_MAJOR) < (major) || ((major) == GLSLANG_VERSION_MAJOR && \
((GLSLANG_VERSION_MINOR) < (minor) || ((minor) == GLSLANG_VERSION_MINOR && \
(GLSLANG_VERSION_PATCH <= (patch))))))
#endif // GLSLANG_BUILD_INFO
+60
View File
@@ -0,0 +1,60 @@
const std = @import("std");
const c = @import("c.zig").c;
const testlib = @import("test.zig");
const Shader = @import("shader.zig").Shader;
pub const Program = opaque {
pub fn create() !*Program {
if (c.glslang_program_create()) |ptr| return @ptrCast(ptr);
return error.OutOfMemory;
}
pub fn delete(self: *Program) void {
c.glslang_program_delete(@ptrCast(self));
}
pub fn addShader(self: *Program, shader: *Shader) void {
c.glslang_program_add_shader(@ptrCast(self), @ptrCast(shader));
}
pub fn link(self: *Program, messages: c_int) !void {
if (c.glslang_program_link(@ptrCast(self), messages) != 0) return;
return error.GlslangFailed;
}
pub fn spirvGenerate(self: *Program, stage: c.glslang_stage_t) void {
c.glslang_program_SPIRV_generate(@ptrCast(self), stage);
}
pub fn spirvGetSize(self: *Program) usize {
return @intCast(c.glslang_program_SPIRV_get_size(@ptrCast(self)));
}
pub fn spirvGet(self: *Program, buf: []u32) void {
c.glslang_program_SPIRV_get(@ptrCast(self), buf.ptr);
}
pub fn spirvGetPtr(self: *Program) ![*]u32 {
return @ptrCast(c.glslang_program_SPIRV_get_ptr(@ptrCast(self)));
}
pub fn spirvGetMessages(self: *Program) ![:0]const u8 {
const ptr = c.glslang_program_SPIRV_get_messages(@ptrCast(self));
return std.mem.sliceTo(ptr, 0);
}
pub fn getInfoLog(self: *Program) ![:0]const u8 {
const ptr = c.glslang_program_get_info_log(@ptrCast(self));
return std.mem.sliceTo(ptr, 0);
}
pub fn getDebugInfoLog(self: *Program) ![:0]const u8 {
const ptr = c.glslang_program_get_info_debug_log(@ptrCast(self));
return std.mem.sliceTo(ptr, 0);
}
};
test {
var program = try Program.create();
defer program.delete();
}
+58
View File
@@ -0,0 +1,58 @@
const std = @import("std");
const c = @import("c.zig").c;
const testlib = @import("test.zig");
pub const Shader = opaque {
pub fn create(input: *const c.glslang_input_t) !*Shader {
if (c.glslang_shader_create(input)) |ptr| return @ptrCast(ptr);
return error.OutOfMemory;
}
pub fn delete(self: *Shader) void {
c.glslang_shader_delete(@ptrCast(self));
}
pub fn preprocess(self: *Shader, input: *const c.glslang_input_t) !void {
if (c.glslang_shader_preprocess(@ptrCast(self), input) == 0)
return error.GlslangFailed;
}
pub fn parse(self: *Shader, input: *const c.glslang_input_t) !void {
if (c.glslang_shader_parse(@ptrCast(self), input) == 0)
return error.GlslangFailed;
}
pub fn getInfoLog(self: *Shader) ![:0]const u8 {
const ptr = c.glslang_shader_get_info_log(@ptrCast(self));
return std.mem.sliceTo(ptr, 0);
}
pub fn getDebugInfoLog(self: *Shader) ![:0]const u8 {
const ptr = c.glslang_shader_get_info_debug_log(@ptrCast(self));
return std.mem.sliceTo(ptr, 0);
}
};
test {
const input: c.glslang_input_t = .{
.language = c.GLSLANG_SOURCE_GLSL,
.stage = c.GLSLANG_STAGE_FRAGMENT,
.client = c.GLSLANG_CLIENT_VULKAN,
.client_version = c.GLSLANG_TARGET_VULKAN_1_2,
.target_language = c.GLSLANG_TARGET_SPV,
.target_language_version = c.GLSLANG_TARGET_SPV_1_5,
.code = @embedFile("test/simple.frag"),
.default_version = 100,
.default_profile = c.GLSLANG_NO_PROFILE,
.force_default_version_and_profile = 0,
.forward_compatible = 0,
.messages = c.GLSLANG_MSG_DEFAULT_BIT,
.resource = c.glslang_default_resource(),
};
try testlib.ensureInit();
const shader = try Shader.create(&input);
defer shader.delete();
try shader.preprocess(&input);
try shader.parse(&input);
}
+10
View File
@@ -0,0 +1,10 @@
const glslang = @import("main.zig");
var initialized: bool = false;
/// Call this function before any other tests in this package to ensure that
/// the glslang library is initialized.
pub fn ensureInit() !void {
if (initialized) return;
try glslang.init();
}
+56
View File
@@ -0,0 +1,56 @@
#version 430 core
layout(binding = 0) uniform Globals {
uniform vec3 iResolution;
uniform float iTime;
uniform float iTimeDelta;
uniform float iFrameRate;
uniform int iFrame;
uniform float iChannelTime[4];
uniform vec3 iChannelResolution[4];
uniform vec4 iMouse;
uniform vec4 iDate;
uniform float iSampleRate;
};
layout(binding = 0) uniform sampler2D iChannel0;
layout(binding = 1) uniform sampler2D iChannel1;
layout(binding = 2) uniform sampler2D iChannel2;
layout(binding = 3) uniform sampler2D iChannel3;
layout(location = 0) in vec4 gl_FragCoord;
layout(location = 0) out vec4 _fragColor;
#define texture2D texture
void mainImage( out vec4 fragColor, in vec2 fragCoord );
void main() { mainImage (_fragColor, gl_FragCoord.xy); }
#define t iTime
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = ( fragCoord - .5*iResolution.xy) / iResolution.y;
vec3 col = vec3(0.);
float a = atan(uv.y,uv.x);
float r = 0.5*length(uv);
float counter = 100.;
a = 4.*a+20.*r+50.*cos(r)*cos(.1*t)+abs(a*r);
float f = 0.02*abs(cos(a))/(r*r);
vec2 v = vec2(0.);
for(float i=0.;i<counter;i++){
v = mat2(v,-v.y,v.x) * v + vec2(2.*f+cos(0.5*t*(exp(-0.2* r))),-cos(t*r*r)*cos(0.5*t));
if(length(v)>2.){
counter = i;
break;
}
}
col=vec3(min(0.9,1.2*exp(-pow(f,0.45)*counter)));
fragColor = min(0.9,1.2*exp(-pow(f,0.45)*counter) )
* ( 0.7 + 0.3* cos(10.*r - 2.*t -vec4(.7,1.4,2.1,0) ) );
}
+127
View File
@@ -0,0 +1,127 @@
const std = @import("std");
const version = @import("build.zig.zon").version;
const dynamic_link_opts: std.Build.Module.LinkSystemLibraryOptions = .{
.preferred_link_mode = .dynamic,
.search_strategy = .mode_first,
};
pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
// Zig API
const module = b.addModule("gtk4-layer-shell", .{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
// Needs the gtk.h header
module.linkSystemLibrary("gtk4", dynamic_link_opts);
if (b.systemIntegrationOption("gtk4-layer-shell", .{})) {
module.linkSystemLibrary("gtk4-layer-shell-0", dynamic_link_opts);
} else {
_ = try buildLib(b, module, .{
.target = target,
.optimize = optimize,
});
}
}
fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Build.Step.Compile {
const lib_version = try std.SemanticVersion.parse(version);
const target = options.target;
const optimize = options.optimize;
// Shared library
const lib = b.addLibrary(.{
.name = "gtk4-layer-shell",
.linkage = .dynamic,
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
}),
});
b.installArtifact(lib);
// We need to call both lazy dependencies to tell Zig we need both
const upstream_ = b.lazyDependency("gtk4_layer_shell", .{});
const wayland_protocols_ = b.lazyDependency("wayland_protocols", .{});
const upstream = upstream_ orelse return lib;
const wayland_protocols = wayland_protocols_ orelse return lib;
lib.linkLibC();
lib.addIncludePath(upstream.path("include"));
lib.addIncludePath(upstream.path("src"));
module.addIncludePath(upstream.path("include"));
// GTK
lib.linkSystemLibrary2("gtk4", dynamic_link_opts);
// Wayland headers and source files
{
const protocols = [_]struct { []const u8, std.Build.LazyPath }{
.{
"wlr-layer-shell-unstable-v1",
upstream.path("protocol/wlr-layer-shell-unstable-v1.xml"),
},
.{
"xdg-shell",
wayland_protocols.path("stable/xdg-shell/xdg-shell.xml"),
},
// Even though we don't use session lock, we still need its headers
.{
"ext-session-lock-v1",
wayland_protocols.path("staging/ext-session-lock/ext-session-lock-v1.xml"),
},
};
const wf = b.addWriteFiles();
for (protocols) |protocol| {
const name, const xml = protocol;
const header_scanner = b.addSystemCommand(&.{ "wayland-scanner", "client-header" });
header_scanner.addFileArg(xml);
_ = wf.addCopyFile(
header_scanner.addOutputFileArg(name),
b.fmt("{s}-client.h", .{name}),
);
const source_scanner = b.addSystemCommand(&.{ "wayland-scanner", "private-code" });
source_scanner.addFileArg(xml);
const source = source_scanner.addOutputFileArg(b.fmt("{s}.c", .{name}));
lib.addCSourceFile(.{ .file = source });
}
lib.addIncludePath(wf.getDirectory());
}
lib.installHeadersDirectory(
upstream.path("include"),
"",
.{ .include_extensions = &.{".h"} },
);
// Certain files relating to session lock were removed as we don't use them
const srcs: []const []const u8 = &.{
"gtk4-layer-shell.c",
"layer-surface.c",
"libwayland-shim.c",
"registry.c",
"stolen-from-libwayland.c",
"stubbed-surface.c",
"xdg-surface-server.c",
};
lib.addCSourceFiles(.{
.root = upstream.path("src"),
.files = srcs,
.flags = &.{
b.fmt("-DGTK_LAYER_SHELL_MAJOR={}", .{lib_version.major}),
b.fmt("-DGTK_LAYER_SHELL_MINOR={}", .{lib_version.minor}),
b.fmt("-DGTK_LAYER_SHELL_MICRO={}", .{lib_version.patch}),
},
});
return lib;
}
+18
View File
@@ -0,0 +1,18 @@
.{
.name = .gtk4_layer_shell,
.version = "1.1.0",
.fingerprint = 0x4b96f9483c6feeb1,
.paths = .{""},
.dependencies = .{
.gtk4_layer_shell = .{
.url = "https://deps.files.ghostty.org/gtk4-layer-shell-1.1.0.tar.gz",
.hash = "N-V-__8AALiNBAA-_0gprYr92CjrMj1I5bqNu0TSJOnjFNSr",
.lazy = true,
},
.wayland_protocols = .{
.url = "https://deps.files.ghostty.org/wayland-protocols-258d8f88f2c8c25a830c6316f87d23ce1a0f12d9.tar.gz",
.hash = "N-V-__8AAKw-DAAaV8bOAAGqA0-oD7o-HNIlPFYKRXSPT03S",
.lazy = true,
},
},
}
+71
View File
@@ -0,0 +1,71 @@
const std = @import("std");
const c = @cImport({
@cInclude("gtk4-layer-shell.h");
});
const gdk = @import("gdk");
const gtk = @import("gtk");
pub const ShellLayer = enum(c_uint) {
background = c.GTK_LAYER_SHELL_LAYER_BACKGROUND,
bottom = c.GTK_LAYER_SHELL_LAYER_BOTTOM,
top = c.GTK_LAYER_SHELL_LAYER_TOP,
overlay = c.GTK_LAYER_SHELL_LAYER_OVERLAY,
};
pub const ShellEdge = enum(c_uint) {
left = c.GTK_LAYER_SHELL_EDGE_LEFT,
right = c.GTK_LAYER_SHELL_EDGE_RIGHT,
top = c.GTK_LAYER_SHELL_EDGE_TOP,
bottom = c.GTK_LAYER_SHELL_EDGE_BOTTOM,
};
pub const KeyboardMode = enum(c_uint) {
none = c.GTK_LAYER_SHELL_KEYBOARD_MODE_NONE,
exclusive = c.GTK_LAYER_SHELL_KEYBOARD_MODE_EXCLUSIVE,
on_demand = c.GTK_LAYER_SHELL_KEYBOARD_MODE_ON_DEMAND,
};
pub fn isSupported() bool {
return c.gtk_layer_is_supported() != 0;
}
pub fn getProtocolVersion() c_uint {
return c.gtk_layer_get_protocol_version();
}
pub fn getLibraryVersion() std.SemanticVersion {
return .{
.major = c.gtk_layer_get_major_version(),
.minor = c.gtk_layer_get_minor_version(),
.patch = c.gtk_layer_get_micro_version(),
};
}
pub fn initForWindow(window: *gtk.Window) void {
c.gtk_layer_init_for_window(@ptrCast(window));
}
pub fn setLayer(window: *gtk.Window, layer: ShellLayer) void {
c.gtk_layer_set_layer(@ptrCast(window), @intFromEnum(layer));
}
pub fn setAnchor(window: *gtk.Window, edge: ShellEdge, anchor_to_edge: bool) void {
c.gtk_layer_set_anchor(@ptrCast(window), @intFromEnum(edge), @intFromBool(anchor_to_edge));
}
pub fn setMargin(window: *gtk.Window, edge: ShellEdge, margin_size: c_int) void {
c.gtk_layer_set_margin(@ptrCast(window), @intFromEnum(edge), margin_size);
}
pub fn setKeyboardMode(window: *gtk.Window, mode: KeyboardMode) void {
c.gtk_layer_set_keyboard_mode(@ptrCast(window), @intFromEnum(mode));
}
pub fn setMonitor(window: *gtk.Window, monitor: ?*gdk.Monitor) void {
c.gtk_layer_set_monitor(@ptrCast(window), @ptrCast(monitor));
}
pub fn setNamespace(window: *gtk.Window, name: [:0]const u8) void {
c.gtk_layer_set_namespace(@ptrCast(window), name.ptr);
}
+126
View File
@@ -0,0 +1,126 @@
const std = @import("std");
const c = @import("c.zig").c;
const Error = @import("errors.zig").Error;
/// Data type holding the memory modes available to client programs.
///
/// Regarding these various memory-modes:
///
/// - In no case shall the HarfBuzz client modify memory that is passed to
/// HarfBuzz in a blob. If there is any such possibility,
/// HB_MEMORY_MODE_DUPLICATE should be used such that HarfBuzz makes a
/// copy immediately,
///
/// - Use HB_MEMORY_MODE_READONLY otherwise, unless you really really really
/// know what you are doing,
///
/// - HB_MEMORY_MODE_WRITABLE is appropriate if you really made a copy of
/// data solely for the purpose of passing to HarfBuzz and doing that
/// just once (no reuse!),
///
/// - If the font is mmap()ed, it's okay to use
/// HB_MEMORY_READONLY_MAY_MAKE_WRITABLE , however, using that mode
/// correctly is very tricky. Use HB_MEMORY_MODE_READONLY instead.
pub const MemoryMode = enum(u2) {
/// HarfBuzz immediately makes a copy of the data.
duplicate = c.HB_MEMORY_MODE_DUPLICATE,
/// HarfBuzz client will never modify the data, and HarfBuzz will never
/// modify the data.
readonly = c.HB_MEMORY_MODE_READONLY,
/// HarfBuzz client made a copy of the data solely for HarfBuzz, so
/// HarfBuzz may modify the data.
writable = c.HB_MEMORY_MODE_WRITABLE,
/// See above
readonly_may_make_writable = c.HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE,
};
/// Blobs wrap a chunk of binary data to handle lifecycle management of data
/// while it is passed between client and HarfBuzz. Blobs are primarily
/// used to create font faces, but also to access font face tables, as well as
/// pass around other binary data.
pub const Blob = struct {
handle: *c.hb_blob_t,
/// Creates a new "blob" object wrapping data . The mode parameter is used
/// to negotiate ownership and lifecycle of data .
///
/// Note that this function returns a freshly-allocated empty blob even
/// if length is zero. This is in contrast to hb_blob_create(), which
/// returns the singleton empty blob (as returned by hb_blob_get_empty())
/// if length is zero.
pub fn create(data: []const u8, mode: MemoryMode) Error!Blob {
const handle = c.hb_blob_create_or_fail(
data.ptr,
@intCast(data.len),
@intFromEnum(mode),
null,
null,
) orelse return Error.HarfbuzzFailed;
return Blob{ .handle = handle };
}
/// Decreases the reference count on blob , and if it reaches zero,
/// destroys blob , freeing all memory, possibly calling the
/// destroy-callback the blob was created for if it has not been
/// called already.
pub fn destroy(self: *Blob) void {
c.hb_blob_destroy(self.handle);
}
/// Attaches a user-data key/data pair to the specified blob.
pub fn setUserData(
self: Blob,
comptime T: type,
key: ?*anyopaque,
ptr: ?*T,
comptime destroycb: ?*const fn (?*T) callconv(.c) void,
replace: bool,
) bool {
const Callback = struct {
pub fn callback(data: ?*anyopaque) callconv(.c) void {
@call(.{ .modifier = .always_inline }, destroycb, .{
@as(?*T, @ptrCast(@alignCast(data))),
});
}
};
return c.hb_blob_set_user_data(
self.handle,
@ptrCast(key),
ptr,
if (destroycb != null) Callback.callback else null,
if (replace) 1 else 0,
) > 0;
}
/// Fetches the user data associated with the specified key, attached to
/// the specified font-functions structure.
pub fn getUserData(
self: Blob,
comptime T: type,
key: ?*anyopaque,
) ?*T {
const opt = c.hb_blob_get_user_data(self.handle, @ptrCast(key));
if (opt) |ptr|
return @ptrCast(@alignCast(ptr))
else
return null;
}
};
test {
const testing = std.testing;
const data = "hello";
var blob = try Blob.create(data, .readonly);
defer blob.destroy();
var userdata: u8 = 127;
var key: u8 = 0;
try testing.expect(blob.setUserData(u8, &key, &userdata, null, false));
try testing.expect(blob.getUserData(u8, &key).?.* == 127);
}
+373
View File
@@ -0,0 +1,373 @@
const std = @import("std");
const c = @import("c.zig").c;
const common = @import("common.zig");
const Error = @import("errors.zig").Error;
const Direction = common.Direction;
const Script = common.Script;
const Language = common.Language;
/// Buffers serve a dual role in HarfBuzz; before shaping, they hold the
/// input characters that are passed to hb_shape(), and after shaping they
/// hold the output glyphs.
pub const Buffer = struct {
handle: *c.hb_buffer_t,
/// Creates a new hb_buffer_t with all properties to defaults.
pub fn create() Error!Buffer {
const handle = c.hb_buffer_create() orelse return Error.HarfbuzzFailed;
return Buffer{ .handle = handle };
}
/// Deallocate the buffer . Decreases the reference count on buffer by one.
/// If the result is zero, then buffer and all associated resources are
/// freed. See hb_buffer_reference().
pub fn destroy(self: *Buffer) void {
c.hb_buffer_destroy(self.handle);
}
/// Resets the buffer to its initial status, as if it was just newly
/// created with hb_buffer_create().
pub fn reset(self: Buffer) void {
c.hb_buffer_reset(self.handle);
}
/// Returns the number of items in the buffer.
pub fn getLength(self: Buffer) u32 {
return c.hb_buffer_get_length(self.handle);
}
/// Sets the type of buffer contents. Buffers are either empty, contain
/// characters (before shaping), or contain glyphs (the result of shaping).
pub fn setContentType(self: Buffer, ct: ContentType) void {
c.hb_buffer_set_content_type(self.handle, @intFromEnum(ct));
}
/// Fetches the type of buffer contents. Buffers are either empty, contain
/// characters (before shaping), or contain glyphs (the result of shaping).
pub fn getContentType(self: Buffer) ContentType {
return @enumFromInt(c.hb_buffer_get_content_type(self.handle));
}
/// Appends a character with the Unicode value of codepoint to buffer,
/// and gives it the initial cluster value of cluster . Clusters can be
/// any thing the client wants, they are usually used to refer to the
/// index of the character in the input text stream and are output in
/// hb_glyph_info_t.cluster field.
///
/// This function does not check the validity of codepoint, it is up to
/// the caller to ensure it is a valid Unicode code point.
pub fn add(self: Buffer, cp: u32, cluster: u32) void {
c.hb_buffer_add(self.handle, cp, cluster);
}
/// Appends characters from text array to buffer . The item_offset is the
/// position of the first character from text that will be appended, and
/// item_length is the number of character. When shaping part of a larger
/// text (e.g. a run of text from a paragraph), instead of passing just
/// the substring corresponding to the run, it is preferable to pass the
/// whole paragraph and specify the run start and length as item_offset and
/// item_length , respectively, to give HarfBuzz the full context to be
/// able, for example, to do cross-run Arabic shaping or properly handle
/// combining marks at stat of run.
///
/// This function does not check the validity of text , it is up to the
/// caller to ensure it contains a valid Unicode code points.
pub fn addCodepoints(self: Buffer, text: []const u32) void {
c.hb_buffer_add_codepoints(
self.handle,
text.ptr,
@intCast(text.len),
0,
@intCast(text.len),
);
}
/// See hb_buffer_add_codepoints().
///
/// Replaces invalid UTF-32 characters with the buffer replacement code
/// point, see hb_buffer_set_replacement_codepoint().
pub fn addUTF32(self: Buffer, text: []const u32) void {
c.hb_buffer_add_utf32(
self.handle,
text.ptr,
@intCast(text.len),
0,
@intCast(text.len),
);
}
/// See hb_buffer_add_codepoints().
///
/// Replaces invalid UTF-16 characters with the buffer replacement code
/// point, see hb_buffer_set_replacement_codepoint().
pub fn addUTF16(self: Buffer, text: []const u16) void {
c.hb_buffer_add_utf16(
self.handle,
text.ptr,
@intCast(text.len),
0,
@intCast(text.len),
);
}
/// See hb_buffer_add_codepoints().
///
/// Replaces invalid UTF-8 characters with the buffer replacement code
/// point, see hb_buffer_set_replacement_codepoint().
pub fn addUTF8(self: Buffer, text: []const u8) void {
c.hb_buffer_add_utf8(
self.handle,
text.ptr,
@intCast(text.len),
0,
@intCast(text.len),
);
}
/// Similar to hb_buffer_add_codepoints(), but allows only access to first
/// 256 Unicode code points that can fit in 8-bit strings.
pub fn addLatin1(self: Buffer, text: []const u8) void {
c.hb_buffer_add_latin1(
self.handle,
text.ptr,
@intCast(text.len),
0,
@intCast(text.len),
);
}
/// Set the text flow direction of the buffer. No shaping can happen
/// without setting buffer direction, and it controls the visual direction
/// for the output glyphs; for RTL direction the glyphs will be reversed.
/// Many layout features depend on the proper setting of the direction,
/// for example, reversing RTL text before shaping, then shaping with LTR
/// direction is not the same as keeping the text in logical order and
/// shaping with RTL direction.
pub fn setDirection(self: Buffer, dir: Direction) void {
c.hb_buffer_set_direction(self.handle, @intFromEnum(dir));
}
/// See hb_buffer_set_direction()
pub fn getDirection(self: Buffer) Direction {
return @enumFromInt(c.hb_buffer_get_direction(self.handle));
}
/// Sets the script of buffer to script.
///
/// Script is crucial for choosing the proper shaping behaviour for
/// scripts that require it (e.g. Arabic) and the which OpenType features
/// defined in the font to be applied.
///
/// You can pass one of the predefined hb_script_t values, or use
/// hb_script_from_string() or hb_script_from_iso15924_tag() to get the
/// corresponding script from an ISO 15924 script tag.
pub fn setScript(self: Buffer, script: Script) void {
c.hb_buffer_set_script(self.handle, @intFromEnum(script));
}
/// See hb_buffer_set_script()
pub fn getScript(self: Buffer) Script {
return @enumFromInt(c.hb_buffer_get_script(self.handle));
}
/// Sets the language of buffer to language .
///
/// Languages are crucial for selecting which OpenType feature to apply to
/// the buffer which can result in applying language-specific behaviour.
/// Languages are orthogonal to the scripts, and though they are related,
/// they are different concepts and should not be confused with each other.
///
/// Use hb_language_from_string() to convert from BCP 47 language tags to
/// hb_language_t.
pub fn setLanguage(self: Buffer, language: Language) void {
c.hb_buffer_set_language(self.handle, language.handle);
}
/// See hb_buffer_set_language()
pub fn getLanguage(self: Buffer) Language {
return Language{ .handle = c.hb_buffer_get_language(self.handle) };
}
/// Returns buffer glyph information array. Returned pointer is valid as
/// long as buffer contents are not modified.
pub fn getGlyphInfos(self: Buffer) []GlyphInfo {
var length: u32 = 0;
const ptr: [*c]GlyphInfo = @ptrCast(c.hb_buffer_get_glyph_infos(self.handle, &length));
return ptr[0..length];
}
/// Returns buffer glyph position array. Returned pointer is valid as
/// long as buffer contents are not modified.
///
/// If buffer did not have positions before, the positions will be
/// initialized to zeros, unless this function is called from within a
/// buffer message callback (see hb_buffer_set_message_func()), in which
/// case NULL is returned.
pub fn getGlyphPositions(self: Buffer) ?[]GlyphPosition {
var length: u32 = 0;
if (c.hb_buffer_get_glyph_positions(self.handle, &length)) |positions| {
const ptr: [*]GlyphPosition = @ptrCast(positions);
return ptr[0..length];
}
return null;
}
/// Sets unset buffer segment properties based on buffer Unicode contents.
/// If buffer is not empty, it must have content type
/// HB_BUFFER_CONTENT_TYPE_UNICODE.
///
/// If buffer script is not set (ie. is HB_SCRIPT_INVALID), it will be set
/// to the Unicode script of the first character in the buffer that has a
/// script other than HB_SCRIPT_COMMON, HB_SCRIPT_INHERITED, and
/// HB_SCRIPT_UNKNOWN.
///
/// Next, if buffer direction is not set (ie. is HB_DIRECTION_INVALID), it
/// will be set to the natural horizontal direction of the buffer script as
/// returned by hb_script_get_horizontal_direction(). If
/// hb_script_get_horizontal_direction() returns HB_DIRECTION_INVALID,
/// then HB_DIRECTION_LTR is used.
///
/// Finally, if buffer language is not set (ie. is HB_LANGUAGE_INVALID), it
/// will be set to the process's default language as returned by
/// hb_language_get_default(). This may change in the future by taking
/// buffer script into consideration when choosing a language. Note that
/// hb_language_get_default() is NOT threadsafe the first time it is
/// called. See documentation for that function for details.
pub fn guessSegmentProperties(self: Buffer) void {
c.hb_buffer_guess_segment_properties(self.handle);
}
/// Sets the cluster level of a buffer. The `ClusterLevel` dictates one
/// aspect of how HarfBuzz will treat non-base characters during shaping.
pub fn setClusterLevel(self: Buffer, level: ClusterLevel) void {
c.hb_buffer_set_cluster_level(self.handle, @intFromEnum(level));
}
};
/// The type of hb_buffer_t contents.
pub const ContentType = enum(u2) {
/// Initial value for new buffer.
invalid = c.HB_BUFFER_CONTENT_TYPE_INVALID,
/// The buffer contains input characters (before shaping).
unicode = c.HB_BUFFER_CONTENT_TYPE_UNICODE,
/// The buffer contains output glyphs (after shaping).
glyphs = c.HB_BUFFER_CONTENT_TYPE_GLYPHS,
};
/// Data type for holding HarfBuzz's clustering behavior options. The cluster
/// level dictates one aspect of how HarfBuzz will treat non-base characters
/// during shaping.
pub const ClusterLevel = enum(u2) {
/// In `monotone_graphemes`, non-base characters are merged into the
/// cluster of the base character that precedes them. There is also cluster
/// merging every time the clusters will otherwise become non-monotone.
/// This is the default cluster level.
monotone_graphemes = c.HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES,
/// In `monotone_characters`, non-base characters are initially assigned
/// their own cluster values, which are not merged into preceding base
/// clusters. This allows HarfBuzz to perform additional operations like
/// reorder sequences of adjacent marks. The output is still monotone, but
/// the cluster values are more granular.
monotone_characters = c.HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS,
/// In `characters`, non-base characters are assigned their own cluster
/// values, which are not merged into preceding base clusters. Moreover,
/// the cluster values are not merged into monotone order. This is the most
/// granular cluster level, and it is useful for clients that need to know
/// the exact cluster values of each character, but is harder to use for
/// clients, since clusters might appear in any order.
characters = c.HB_BUFFER_CLUSTER_LEVEL_CHARACTERS,
/// In `graphemes`, non-base characters are merged into the cluster of the
/// base character that precedes them. This is similar to the Unicode
/// Grapheme Cluster algorithm, but it is not exactly the same. The output
/// is not forced to be monotone. This is useful for clients that want to
/// use HarfBuzz as a cheap implementation of the Unicode Grapheme Cluster
/// algorithm.
graphemes = c.HB_BUFFER_CLUSTER_LEVEL_GRAPHEMES,
};
/// The hb_glyph_info_t is the structure that holds information about the
/// glyphs and their relation to input text.
pub const GlyphInfo = extern struct {
/// either a Unicode code point (before shaping) or a glyph index (after shaping).
codepoint: u32,
_mask: u32,
/// the index of the character in the original text that corresponds to
/// this hb_glyph_info_t, or whatever the client passes to hb_buffer_add().
/// More than one hb_glyph_info_t can have the same cluster value, if they
/// resulted from the same character (e.g. one to many glyph substitution),
/// and when more than one character gets merged in the same glyph (e.g.
/// many to one glyph substitution) the hb_glyph_info_t will have the
/// smallest cluster value of them. By default some characters are merged
/// into the same cluster (e.g. combining marks have the same cluster as
/// their bases) even if they are separate glyphs, hb_buffer_set_cluster_level()
/// allow selecting more fine-grained cluster handling.
cluster: u32,
_var1: u32,
_var2: u32,
};
/// The hb_glyph_position_t is the structure that holds the positions of the
/// glyph in both horizontal and vertical directions. All positions in
/// hb_glyph_position_t are relative to the current point.
pub const GlyphPosition = extern struct {
/// how much the line advances after drawing this glyph when setting text
/// in horizontal direction.
x_advance: i32,
/// how much the line advances after drawing this glyph when setting text
/// in vertical direction.
y_advance: i32,
/// how much the glyph moves on the X-axis before drawing it, this should
/// not affect how much the line advances.
x_offset: i32,
/// how much the glyph moves on the Y-axis before drawing it, this should
/// not affect how much the line advances.
y_offset: i32,
_var: u32,
};
test "create" {
const testing = std.testing;
var buffer = try Buffer.create();
defer buffer.destroy();
buffer.reset();
// Content type
buffer.setContentType(.unicode);
try testing.expectEqual(ContentType.unicode, buffer.getContentType());
// Try add functions
buffer.add('🥹', 27);
var utf32 = [_]u32{ 'A', 'B', 'C' };
var utf16 = [_]u16{ 'A', 'B', 'C' };
var utf8 = [_]u8{ 'A', 'B', 'C' };
buffer.addCodepoints(&utf32);
buffer.addUTF32(&utf32);
buffer.addUTF16(&utf16);
buffer.addUTF8(&utf8);
buffer.addLatin1(&utf8);
// Guess properties first
buffer.guessSegmentProperties();
// Try to set properties
buffer.setDirection(.ltr);
try testing.expectEqual(Direction.ltr, buffer.getDirection());
buffer.setScript(.arabic);
try testing.expectEqual(Script.arabic, buffer.getScript());
buffer.setLanguage(Language.fromString("en"));
}
+194
View File
@@ -0,0 +1,194 @@
const std = @import("std");
const apple_sdk = @import("apple_sdk");
pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const coretext_enabled = b.option(bool, "enable-coretext", "Build coretext") orelse false;
const freetype_enabled = b.option(bool, "enable-freetype", "Build freetype") orelse true;
const freetype = b.dependency("freetype", .{
.target = target,
.optimize = optimize,
.@"enable-libpng" = true,
});
const macos = b.dependency("macos", .{ .target = target, .optimize = optimize });
const module = harfbuzz: {
const module = b.addModule("harfbuzz", .{
.root_source_file = b.path("main.zig"),
.target = target,
.optimize = optimize,
.imports = &.{
.{ .name = "freetype", .module = freetype.module("freetype") },
.{ .name = "macos", .module = macos.module("macos") },
},
});
const options = b.addOptions();
options.addOption(bool, "coretext", coretext_enabled);
options.addOption(bool, "freetype", freetype_enabled);
module.addOptions("build_options", options);
break :harfbuzz module;
};
// For dynamic linking, we prefer dynamic linking and to search by
// mode first. Mode first will search all paths for a dynamic library
// before falling back to static.
const dynamic_link_opts: std.Build.Module.LinkSystemLibraryOptions = .{
.preferred_link_mode = .dynamic,
.search_strategy = .mode_first,
};
const test_exe = b.addTest(.{
.name = "test",
.root_module = b.createModule(.{
.root_source_file = b.path("main.zig"),
.target = target,
.optimize = optimize,
}),
});
{
var it = module.import_table.iterator();
while (it.next()) |entry| test_exe.root_module.addImport(entry.key_ptr.*, entry.value_ptr.*);
if (b.systemIntegrationOption("freetype", .{})) {
test_exe.linkSystemLibrary2("freetype2", dynamic_link_opts);
} else {
test_exe.linkLibrary(freetype.artifact("freetype"));
}
const tests_run = b.addRunArtifact(test_exe);
const test_step = b.step("test", "Run tests");
test_step.dependOn(&tests_run.step);
}
if (b.systemIntegrationOption("harfbuzz", .{})) {
module.linkSystemLibrary("harfbuzz", dynamic_link_opts);
test_exe.linkSystemLibrary2("harfbuzz", dynamic_link_opts);
} else {
const lib = try buildLib(b, module, .{
.target = target,
.optimize = optimize,
.coretext_enabled = coretext_enabled,
.freetype_enabled = freetype_enabled,
.dynamic_link_opts = dynamic_link_opts,
});
test_exe.linkLibrary(lib);
}
}
fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Build.Step.Compile {
const target = options.target;
const optimize = options.optimize;
const coretext_enabled = options.coretext_enabled;
const freetype_enabled = options.freetype_enabled;
const freetype = b.dependency("freetype", .{
.target = target,
.optimize = optimize,
.@"enable-libpng" = true,
});
const lib = b.addLibrary(.{
.name = "harfbuzz",
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
}),
.linkage = .static,
});
lib.linkLibC();
// On MSVC, we must not use linkLibCpp because Zig unconditionally
// passes -nostdinc++ and then adds its bundled libc++/libc++abi
// include paths, which conflict with MSVC's own C++ runtime headers.
// The MSVC SDK include directories (added via linkLibC) contain
// both C and C++ headers, so linkLibCpp is not needed.
if (target.result.abi != .msvc) {
lib.linkLibCpp();
}
if (target.result.os.tag.isDarwin()) {
try apple_sdk.addPaths(b, lib);
}
const dynamic_link_opts = options.dynamic_link_opts;
var flags: std.ArrayList([]const u8) = .empty;
defer flags.deinit(b.allocator);
try flags.appendSlice(b.allocator, &.{
"-DHAVE_STDBOOL_H",
});
// Disable ubsan for MSVC: Zig's ubsan runtime cannot be bundled
// on Windows (LNK4229), leaving __ubsan_handle_* unresolved when
// the static archive is consumed by an external linker.
if (target.result.abi == .msvc) {
try flags.appendSlice(b.allocator, &.{
"-fno-sanitize=undefined",
"-fno-sanitize-trap=undefined",
});
}
if (target.result.os.tag != .windows) {
try flags.appendSlice(b.allocator, &.{
"-DHAVE_UNISTD_H",
"-DHAVE_SYS_MMAN_H",
"-DHAVE_PTHREAD=1",
});
}
// Freetype
_ = b.systemIntegrationOption("freetype", .{}); // So it shows up in help
if (freetype_enabled) {
try flags.appendSlice(b.allocator, &.{
"-DHAVE_FREETYPE=1",
// Let's just assume a new freetype
"-DHAVE_FT_GET_VAR_BLEND_COORDINATES=1",
"-DHAVE_FT_SET_VAR_BLEND_COORDINATES=1",
"-DHAVE_FT_DONE_MM_VAR=1",
"-DHAVE_FT_GET_TRANSFORM=1",
});
if (b.systemIntegrationOption("freetype", .{})) {
lib.linkSystemLibrary2("freetype2", dynamic_link_opts);
module.linkSystemLibrary("freetype2", dynamic_link_opts);
} else {
lib.linkLibrary(freetype.artifact("freetype"));
if (freetype.builder.lazyDependency(
"freetype",
.{},
)) |freetype_dep| {
module.addIncludePath(freetype_dep.path("include"));
}
}
}
if (coretext_enabled) {
try flags.appendSlice(b.allocator, &.{"-DHAVE_CORETEXT=1"});
lib.linkFramework("CoreText");
module.linkFramework("CoreText", .{});
}
if (b.lazyDependency("harfbuzz", .{})) |upstream| {
lib.addIncludePath(upstream.path("src"));
module.addIncludePath(upstream.path("src"));
lib.addCSourceFile(.{
.file = upstream.path("src/harfbuzz.cc"),
.flags = flags.items,
});
lib.installHeadersDirectory(
upstream.path("src"),
"",
.{ .include_extensions = &.{".h"} },
);
}
b.installArtifact(lib);
return lib;
}
+18
View File
@@ -0,0 +1,18 @@
.{
.name = .harfbuzz,
.version = "11.0.0",
.fingerprint = 0xbd60917cd18865d8,
.paths = .{""},
.dependencies = .{
// harfbuzz/harfbuzz
.harfbuzz = .{
.url = "https://deps.files.ghostty.org/harfbuzz-11.0.0.tar.xz",
.hash = "N-V-__8AAG02ugUcWec-Ndp-i7JTsJ0dgF8nnJRUInkGLG7G",
.lazy = true,
},
.freetype = .{ .path = "../freetype" },
.macos = .{ .path = "../macos" },
.apple_sdk = .{ .path = "../apple-sdk" },
},
}
+8
View File
@@ -0,0 +1,8 @@
const builtin = @import("builtin");
const build_options = @import("build_options");
pub const c = @cImport({
@cInclude("hb.h");
if (build_options.freetype) @cInclude("hb-ft.h");
if (build_options.coretext) @cInclude("hb-coretext.h");
});
+247
View File
@@ -0,0 +1,247 @@
const std = @import("std");
const c = @import("c.zig").c;
/// The direction of a text segment or buffer.
///
/// A segment can also be tested for horizontal or vertical orientation
/// (irrespective of specific direction) with HB_DIRECTION_IS_HORIZONTAL()
/// or HB_DIRECTION_IS_VERTICAL().
pub const Direction = enum(u3) {
invalid = c.HB_DIRECTION_INVALID,
ltr = c.HB_DIRECTION_LTR,
rtl = c.HB_DIRECTION_RTL,
ttb = c.HB_DIRECTION_TTB,
bit = c.HB_DIRECTION_BTT,
};
/// Data type for scripts. Each hb_script_t's value is an hb_tag_t
/// corresponding to the four-letter values defined by ISO 15924.
///
/// See also the Script (sc) property of the Unicode Character Database.
pub const Script = enum(u31) {
common = c.HB_SCRIPT_COMMON,
inherited = c.HB_SCRIPT_INHERITED,
unknown = c.HB_SCRIPT_UNKNOWN,
arabic = c.HB_SCRIPT_ARABIC,
armenian = c.HB_SCRIPT_ARMENIAN,
bengali = c.HB_SCRIPT_BENGALI,
cyrillic = c.HB_SCRIPT_CYRILLIC,
devanagari = c.HB_SCRIPT_DEVANAGARI,
georgian = c.HB_SCRIPT_GEORGIAN,
greek = c.HB_SCRIPT_GREEK,
gujarati = c.HB_SCRIPT_GUJARATI,
gurmukhi = c.HB_SCRIPT_GURMUKHI,
hangul = c.HB_SCRIPT_HANGUL,
han = c.HB_SCRIPT_HAN,
hebrew = c.HB_SCRIPT_HEBREW,
hiragana = c.HB_SCRIPT_HIRAGANA,
kannada = c.HB_SCRIPT_KANNADA,
katakana = c.HB_SCRIPT_KATAKANA,
lao = c.HB_SCRIPT_LAO,
latin = c.HB_SCRIPT_LATIN,
malayalam = c.HB_SCRIPT_MALAYALAM,
oriya = c.HB_SCRIPT_ORIYA,
tamil = c.HB_SCRIPT_TAMIL,
telugu = c.HB_SCRIPT_TELUGU,
thai = c.HB_SCRIPT_THAI,
tibetan = c.HB_SCRIPT_TIBETAN,
bopomofo = c.HB_SCRIPT_BOPOMOFO,
braille = c.HB_SCRIPT_BRAILLE,
canadian_syllabics = c.HB_SCRIPT_CANADIAN_SYLLABICS,
cherokee = c.HB_SCRIPT_CHEROKEE,
ethiopic = c.HB_SCRIPT_ETHIOPIC,
khmer = c.HB_SCRIPT_KHMER,
mongolian = c.HB_SCRIPT_MONGOLIAN,
myanmar = c.HB_SCRIPT_MYANMAR,
ogham = c.HB_SCRIPT_OGHAM,
runic = c.HB_SCRIPT_RUNIC,
sinhala = c.HB_SCRIPT_SINHALA,
syriac = c.HB_SCRIPT_SYRIAC,
thaana = c.HB_SCRIPT_THAANA,
yi = c.HB_SCRIPT_YI,
deseret = c.HB_SCRIPT_DESERET,
gothic = c.HB_SCRIPT_GOTHIC,
old_italic = c.HB_SCRIPT_OLD_ITALIC,
buhid = c.HB_SCRIPT_BUHID,
hanunoo = c.HB_SCRIPT_HANUNOO,
tagalog = c.HB_SCRIPT_TAGALOG,
tagbanwa = c.HB_SCRIPT_TAGBANWA,
cypriot = c.HB_SCRIPT_CYPRIOT,
limbu = c.HB_SCRIPT_LIMBU,
linear_b = c.HB_SCRIPT_LINEAR_B,
osmanya = c.HB_SCRIPT_OSMANYA,
shavian = c.HB_SCRIPT_SHAVIAN,
tai_le = c.HB_SCRIPT_TAI_LE,
ugaritic = c.HB_SCRIPT_UGARITIC,
buginese = c.HB_SCRIPT_BUGINESE,
coptic = c.HB_SCRIPT_COPTIC,
glagolitic = c.HB_SCRIPT_GLAGOLITIC,
kharoshthi = c.HB_SCRIPT_KHAROSHTHI,
new_tai_lue = c.HB_SCRIPT_NEW_TAI_LUE,
old_persian = c.HB_SCRIPT_OLD_PERSIAN,
syloti_nagri = c.HB_SCRIPT_SYLOTI_NAGRI,
tifinagh = c.HB_SCRIPT_TIFINAGH,
balinese = c.HB_SCRIPT_BALINESE,
cuneiform = c.HB_SCRIPT_CUNEIFORM,
nko = c.HB_SCRIPT_NKO,
phags_pa = c.HB_SCRIPT_PHAGS_PA,
phoenician = c.HB_SCRIPT_PHOENICIAN,
carian = c.HB_SCRIPT_CARIAN,
cham = c.HB_SCRIPT_CHAM,
kayah_li = c.HB_SCRIPT_KAYAH_LI,
lepcha = c.HB_SCRIPT_LEPCHA,
lycian = c.HB_SCRIPT_LYCIAN,
lydian = c.HB_SCRIPT_LYDIAN,
ol_chiki = c.HB_SCRIPT_OL_CHIKI,
rejang = c.HB_SCRIPT_REJANG,
saurashtra = c.HB_SCRIPT_SAURASHTRA,
sundanese = c.HB_SCRIPT_SUNDANESE,
vai = c.HB_SCRIPT_VAI,
avestan = c.HB_SCRIPT_AVESTAN,
bamum = c.HB_SCRIPT_BAMUM,
egyptian_hieroglyphs = c.HB_SCRIPT_EGYPTIAN_HIEROGLYPHS,
imperial_aramaic = c.HB_SCRIPT_IMPERIAL_ARAMAIC,
inscriptional_pahlavi = c.HB_SCRIPT_INSCRIPTIONAL_PAHLAVI,
inscriptional_parthian = c.HB_SCRIPT_INSCRIPTIONAL_PARTHIAN,
javanese = c.HB_SCRIPT_JAVANESE,
kaithi = c.HB_SCRIPT_KAITHI,
lisu = c.HB_SCRIPT_LISU,
meetei_mayek = c.HB_SCRIPT_MEETEI_MAYEK,
old_south_arabian = c.HB_SCRIPT_OLD_SOUTH_ARABIAN,
old_turkic = c.HB_SCRIPT_OLD_TURKIC,
samaritan = c.HB_SCRIPT_SAMARITAN,
tai_tham = c.HB_SCRIPT_TAI_THAM,
tai_viet = c.HB_SCRIPT_TAI_VIET,
batak = c.HB_SCRIPT_BATAK,
brahmi = c.HB_SCRIPT_BRAHMI,
mandaic = c.HB_SCRIPT_MANDAIC,
chakma = c.HB_SCRIPT_CHAKMA,
meroitic_cursive = c.HB_SCRIPT_MEROITIC_CURSIVE,
meroitic_hieroglyphs = c.HB_SCRIPT_MEROITIC_HIEROGLYPHS,
miao = c.HB_SCRIPT_MIAO,
sharada = c.HB_SCRIPT_SHARADA,
sora_sompeng = c.HB_SCRIPT_SORA_SOMPENG,
takri = c.HB_SCRIPT_TAKRI,
bassa_vah = c.HB_SCRIPT_BASSA_VAH,
caucasian_albanian = c.HB_SCRIPT_CAUCASIAN_ALBANIAN,
duployan = c.HB_SCRIPT_DUPLOYAN,
elbasan = c.HB_SCRIPT_ELBASAN,
grantha = c.HB_SCRIPT_GRANTHA,
khojki = c.HB_SCRIPT_KHOJKI,
khudawadi = c.HB_SCRIPT_KHUDAWADI,
linear_a = c.HB_SCRIPT_LINEAR_A,
mahajani = c.HB_SCRIPT_MAHAJANI,
manichaean = c.HB_SCRIPT_MANICHAEAN,
mende_kikakui = c.HB_SCRIPT_MENDE_KIKAKUI,
modi = c.HB_SCRIPT_MODI,
mro = c.HB_SCRIPT_MRO,
nabataean = c.HB_SCRIPT_NABATAEAN,
old_north_arabian = c.HB_SCRIPT_OLD_NORTH_ARABIAN,
old_permic = c.HB_SCRIPT_OLD_PERMIC,
pahawh_hmong = c.HB_SCRIPT_PAHAWH_HMONG,
palmyrene = c.HB_SCRIPT_PALMYRENE,
pau_cin_hau = c.HB_SCRIPT_PAU_CIN_HAU,
psalter_pahlavi = c.HB_SCRIPT_PSALTER_PAHLAVI,
siddham = c.HB_SCRIPT_SIDDHAM,
tirhuta = c.HB_SCRIPT_TIRHUTA,
warang_citi = c.HB_SCRIPT_WARANG_CITI,
ahom = c.HB_SCRIPT_AHOM,
anatolian_hieroglyphs = c.HB_SCRIPT_ANATOLIAN_HIEROGLYPHS,
hatran = c.HB_SCRIPT_HATRAN,
multani = c.HB_SCRIPT_MULTANI,
old_hungarian = c.HB_SCRIPT_OLD_HUNGARIAN,
signwriting = c.HB_SCRIPT_SIGNWRITING,
adlam = c.HB_SCRIPT_ADLAM,
bhaiksuki = c.HB_SCRIPT_BHAIKSUKI,
marchen = c.HB_SCRIPT_MARCHEN,
osage = c.HB_SCRIPT_OSAGE,
tangut = c.HB_SCRIPT_TANGUT,
newa = c.HB_SCRIPT_NEWA,
masaram_gondi = c.HB_SCRIPT_MASARAM_GONDI,
nushu = c.HB_SCRIPT_NUSHU,
soyombo = c.HB_SCRIPT_SOYOMBO,
zanabazar_square = c.HB_SCRIPT_ZANABAZAR_SQUARE,
dogra = c.HB_SCRIPT_DOGRA,
gunjala_gondi = c.HB_SCRIPT_GUNJALA_GONDI,
hanifi_rohingya = c.HB_SCRIPT_HANIFI_ROHINGYA,
makasar = c.HB_SCRIPT_MAKASAR,
medefaidrin = c.HB_SCRIPT_MEDEFAIDRIN,
old_sogdian = c.HB_SCRIPT_OLD_SOGDIAN,
sogdian = c.HB_SCRIPT_SOGDIAN,
elymaic = c.HB_SCRIPT_ELYMAIC,
nandinagari = c.HB_SCRIPT_NANDINAGARI,
nyiakeng_puachue_hmong = c.HB_SCRIPT_NYIAKENG_PUACHUE_HMONG,
wancho = c.HB_SCRIPT_WANCHO,
chorasmian = c.HB_SCRIPT_CHORASMIAN,
dives_akuru = c.HB_SCRIPT_DIVES_AKURU,
khitan_small_script = c.HB_SCRIPT_KHITAN_SMALL_SCRIPT,
yezidi = c.HB_SCRIPT_YEZIDI,
cypro_minoan = c.HB_SCRIPT_CYPRO_MINOAN,
old_uyghur = c.HB_SCRIPT_OLD_UYGHUR,
tangsa = c.HB_SCRIPT_TANGSA,
toto = c.HB_SCRIPT_TOTO,
vithkuqi = c.HB_SCRIPT_VITHKUQI,
math = c.HB_SCRIPT_MATH,
invalid = c.HB_SCRIPT_INVALID,
};
/// Data type for languages. Each hb_language_t corresponds to a BCP 47
/// language tag.
pub const Language = struct {
handle: c.hb_language_t,
/// Converts str representing a BCP 47 language tag to the corresponding
/// hb_language_t.
pub fn fromString(str: []const u8) Language {
return .{
.handle = c.hb_language_from_string(str.ptr, @intCast(str.len)),
};
}
/// Converts an hb_language_t to a string.
pub fn toString(self: Language) [:0]const u8 {
return std.mem.span(@as(
[*:0]const u8,
@ptrCast(c.hb_language_to_string(self.handle)),
));
}
/// Fetch the default language from current locale.
pub fn getDefault() Language {
return .{ .handle = c.hb_language_get_default() };
}
};
/// The hb_feature_t is the structure that holds information about requested
/// feature application. The feature will be applied with the given value to
/// all glyphs which are in clusters between start (inclusive) and end
/// (exclusive). Setting start to HB_FEATURE_GLOBAL_START and end to
/// HB_FEATURE_GLOBAL_END specifies that the feature always applies to the
/// entire buffer.
pub const Feature = extern struct {
tag: c.hb_tag_t,
value: u32,
start: c_uint,
end: c_uint,
pub fn fromString(str: []const u8) ?Feature {
var f: c.hb_feature_t = undefined;
return if (c.hb_feature_from_string(
str.ptr,
@intCast(str.len),
&f,
) > 0)
@bitCast(f)
else
null;
}
pub fn toString(self: *Feature, buf: []u8) void {
c.hb_feature_to_string(self, buf.ptr, @intCast(buf.len));
}
};
test "feature from string" {
const testing = std.testing;
try testing.expect(Feature.fromString("dlig") != null);
}
+30
View File
@@ -0,0 +1,30 @@
const macos = @import("macos");
const std = @import("std");
const c = @import("c.zig").c;
const Face = @import("face.zig").Face;
const Font = @import("font.zig").Font;
const Error = @import("errors.zig").Error;
// Use custom extern functions so that the proper CoreText structs are used
// without a ptrcast.
extern fn hb_coretext_font_create(ct_face: *macos.text.Font) ?*c.hb_font_t;
/// Creates an hb_font_t font object from the specified CTFontRef.
pub fn createFont(face: *macos.text.Font) Error!Font {
const handle = hb_coretext_font_create(face) orelse return Error.HarfbuzzFailed;
return Font{ .handle = handle };
}
test {
if (!@hasDecl(c, "hb_coretext_font_create")) return error.SkipZigTest;
const name = try macos.foundation.String.createWithBytes("Monaco", .utf8, false);
defer name.release();
const desc = try macos.text.FontDescriptor.createWithNameAndSize(name, 12);
defer desc.release();
const font = try macos.text.Font.createWithFontDescriptor(desc, 12);
defer font.release();
var hb_font = try createFont(font);
defer hb_font.destroy();
}
+5
View File
@@ -0,0 +1,5 @@
pub const Error = error{
/// Not very descriptive but harfbuzz doesn't actually have error
/// codes so the best we can do!
HarfbuzzFailed,
};
+17
View File
@@ -0,0 +1,17 @@
const std = @import("std");
const c = @import("c.zig").c;
/// A font face is an object that represents a single face from within a font family.
///
/// More precisely, a font face represents a single face in a binary font file.
/// Font faces are typically built from a binary blob and a face index.
/// Font faces are used to create fonts.
pub const Face = struct {
handle: *c.hb_face_t,
/// Decreases the reference count on a face object. When the reference
/// count reaches zero, the face is destroyed, freeing all memory.
pub fn destroy(self: *Face) void {
c.hb_face_destroy(self.handle);
}
};
+28
View File
@@ -0,0 +1,28 @@
const std = @import("std");
const c = @import("c.zig").c;
const Face = @import("face.zig").Face;
const Error = @import("errors.zig").Error;
pub const Font = struct {
handle: *c.hb_font_t,
/// Constructs a new font object from the specified face.
pub fn create(face: Face) Error!Font {
const handle = c.hb_font_create(face.handle) orelse return Error.HarfbuzzFailed;
return Font{ .handle = handle };
}
/// Decreases the reference count on the given font object. When the
/// reference count reaches zero, the font is destroyed, freeing all memory.
pub fn destroy(self: *Font) void {
c.hb_font_destroy(self.handle);
}
pub fn setScale(self: *Font, x: u32, y: u32) void {
c.hb_font_set_scale(
self.handle,
@intCast(x),
@intCast(y),
);
}
};
+78
View File
@@ -0,0 +1,78 @@
const freetype = @import("freetype");
const std = @import("std");
const c = @import("c.zig").c;
const Face = @import("face.zig").Face;
const Font = @import("font.zig").Font;
const Error = @import("errors.zig").Error;
// Use custom extern functions so that the proper freetype structs are used
// without a ptrcast. These are only needed when interacting with freetype
// C structs.
extern fn hb_ft_face_create_referenced(ft_face: freetype.c.FT_Face) ?*c.hb_face_t;
extern fn hb_ft_font_create_referenced(ft_face: freetype.c.FT_Face) ?*c.hb_font_t;
extern fn hb_ft_font_get_face(font: ?*c.hb_font_t) freetype.c.FT_Face;
/// Creates an hb_face_t face object from the specified FT_Face.
///
/// This is the preferred variant of the hb_ft_face_create* function
/// family, because it calls FT_Reference_Face() on ft_face , ensuring
/// that ft_face remains alive as long as the resulting hb_face_t face
/// object remains alive. Also calls FT_Done_Face() when the hb_face_t
/// face object is destroyed.
///
/// Use this version unless you know you have good reasons not to.
pub fn createFace(face: freetype.c.FT_Face) Error!Face {
const handle = hb_ft_face_create_referenced(face) orelse return Error.HarfbuzzFailed;
return Face{ .handle = handle };
}
/// Creates an hb_font_t font object from the specified FT_Face.
pub fn createFont(face: freetype.c.FT_Face) Error!Font {
const handle = hb_ft_font_create_referenced(face) orelse return Error.HarfbuzzFailed;
return Font{ .handle = handle };
}
/// Configures the font-functions structure of the specified hb_font_t font
/// object to use FreeType font functions.
///
/// In particular, you can use this function to configure an existing
/// hb_face_t face object for use with FreeType font functions even if that
/// hb_face_t face object was initially created with hb_face_create(), and
/// therefore was not initially configured to use FreeType font functions.
///
/// An hb_face_t face object created with hb_ft_face_create() is preconfigured
/// for FreeType font functions and does not require this function to be used.
pub fn setFontFuncs(font: Font) void {
c.hb_ft_font_set_funcs(font.handle);
}
test {
if (!@hasDecl(c, "hb_ft_font_create_referenced")) return error.SkipZigTest;
const testing = std.testing;
const testFont = freetype.testing.font_regular;
const ftc = freetype.c;
const ftok = ftc.FT_Err_Ok;
var ft_lib: ftc.FT_Library = undefined;
if (ftc.FT_Init_FreeType(&ft_lib) != ftok)
return error.FreeTypeInitFailed;
defer _ = ftc.FT_Done_FreeType(ft_lib);
var ft_face: ftc.FT_Face = undefined;
try testing.expect(ftc.FT_New_Memory_Face(
ft_lib,
testFont,
@intCast(testFont.len),
0,
&ft_face,
) == ftok);
defer _ = ftc.FT_Done_Face(ft_face);
var face = try createFace(ft_face);
defer face.destroy();
var font = try createFont(ft_face);
defer font.destroy();
setFontFuncs(font);
}
+31
View File
@@ -0,0 +1,31 @@
const blob = @import("blob.zig");
const buffer = @import("buffer.zig");
const common = @import("common.zig");
const errors = @import("errors.zig");
const face = @import("face.zig");
const font = @import("font.zig");
const shapepkg = @import("shape.zig");
const versionpkg = @import("version.zig");
pub const c = @import("c.zig").c;
pub const freetype = @import("freetype.zig");
pub const coretext = @import("coretext.zig");
pub const MemoryMode = blob.MemoryMode;
pub const Blob = blob.Blob;
pub const Buffer = buffer.Buffer;
pub const GlyphPosition = buffer.GlyphPosition;
pub const Direction = common.Direction;
pub const Script = common.Script;
pub const Language = common.Language;
pub const Feature = common.Feature;
pub const Face = face.Face;
pub const Font = font.Font;
pub const shape = shapepkg.shape;
pub const Version = versionpkg.Version;
pub const version = versionpkg.version;
pub const versionAtLeast = versionpkg.versionAtLeast;
pub const versionString = versionpkg.versionString;
test {
@import("std").testing.refAllDecls(@This());
}
+27
View File
@@ -0,0 +1,27 @@
const std = @import("std");
const c = @import("c.zig").c;
const Font = @import("font.zig").Font;
const Buffer = @import("buffer.zig").Buffer;
const Feature = @import("common.zig").Feature;
/// Shapes buffer using font turning its Unicode characters content to
/// positioned glyphs. If features is not NULL, it will be used to control
/// the features applied during shaping. If two features have the same tag
/// but overlapping ranges the value of the feature with the higher index
/// takes precedence.
pub fn shape(font: Font, buf: Buffer, features: ?[]const Feature) void {
const hb_feats: [*c]const c.hb_feature_t = feats: {
if (features) |fs| {
if (fs.len > 0) break :feats @ptrCast(fs.ptr);
}
break :feats null;
};
c.hb_shape(
font.handle,
buf.handle,
hb_feats,
if (features) |f| @intCast(f.len) else 0,
);
}
+47
View File
@@ -0,0 +1,47 @@
const std = @import("std");
const c = @import("c.zig").c;
pub const Version = struct {
major: u32,
minor: u32,
micro: u32,
};
/// Returns library version as three integer components.
pub fn version() Version {
var major: c_uint = 0;
var minor: c_uint = 0;
var micro: c_uint = 0;
c.hb_version(&major, &minor, &micro);
return .{ .major = major, .minor = minor, .micro = micro };
}
/// Tests the library version against a minimum value, as three integer components.
pub fn versionAtLeast(vsn: Version) bool {
return c.hb_version_atleast(
vsn.major,
vsn.minor,
vsn.micro,
) > 0;
}
/// Returns library version as a string with three components.
pub fn versionString() [:0]const u8 {
const res = c.hb_version_string();
return std.mem.sliceTo(res, 0);
}
test {
const testing = std.testing;
// Should be able to get the version
const vsn = version();
try testing.expect(vsn.major > 0);
// Should be at least version 1
try testing.expect(versionAtLeast(.{
.major = 1,
.minor = 0,
.micro = 0,
}));
}
+138
View File
@@ -0,0 +1,138 @@
const std = @import("std");
pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const upstream_ = b.lazyDependency("highway", .{});
const module = b.addModule("highway", .{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
const lib = b.addLibrary(.{
.name = "highway",
.root_module = b.createModule(.{
.root_source_file = b.path("src/detect.zig"),
.target = target,
.optimize = optimize,
}),
.linkage = .static,
});
// Our highway package is free of libc at runtime (uses no symbols)
// but does require libc headers at compile time.
lib.linkLibC();
lib.addIncludePath(b.path("src/cpp"));
if (upstream_) |upstream| {
lib.addIncludePath(upstream.path(""));
module.addIncludePath(upstream.path(""));
}
if (target.result.abi.isAndroid()) {
const android_ndk = @import("android_ndk");
try android_ndk.addPaths(b, lib);
}
// Mainly for iOS simulators, but we add for all Darwin target for
// consistency.
if (target.result.os.tag.isDarwin()) {
const apple_sdk = @import("apple_sdk");
try apple_sdk.addPaths(b, lib);
}
var flags: std.ArrayList([]const u8) = .empty;
defer flags.deinit(b.allocator);
try flags.appendSlice(b.allocator, &.{
// Highway can avoid libc++ entirely as long as all users compile
// against the headers with the same define.
"-DHWY_NO_LIBCXX",
// Avoid changing binaries based on the current time and date.
"-Wno-builtin-macro-redefined",
"-D__DATE__=\"redacted\"",
"-D__TIMESTAMP__=\"redacted\"",
"-D__TIME__=\"redacted\"",
// Optimizations
"-fmerge-all-constants",
// Warnings
"-Wall",
"-Wextra",
// These are not included in Wall nor Wextra:
"-Wconversion",
"-Wsign-conversion",
"-Wvla",
"-Wnon-virtual-dtor",
"-Wfloat-overflow-conversion",
"-Wfloat-zero-conversion",
"-Wfor-loop-analysis",
"-Wgnu-redeclared-enum",
"-Winfinite-recursion",
"-Wself-assign",
"-Wstring-conversion",
"-Wtautological-overlap-compare",
"-Wthread-safety-analysis",
"-Wundefined-func-template",
"-fno-cxx-exceptions",
"-fno-slp-vectorize",
"-fno-vectorize",
// Fixes linker issues for release builds missing ubsanitizer symbols
"-fno-sanitize=undefined",
"-fno-sanitize-trap=undefined",
});
if (target.result.os.tag == .freebsd or target.result.os.tag == .linux) {
try flags.append(b.allocator, "-fPIC");
lib.root_module.pic = true;
}
if (target.result.os.tag != .windows) {
try flags.appendSlice(b.allocator, &.{
"-fmath-errno",
"-fno-exceptions",
});
}
lib.addCSourceFiles(.{ .flags = flags.items, .files = &.{
"src/cpp/abort.cc",
"src/cpp/per_target.cc",
"src/cpp/targets.cpp",
} });
if (upstream_) |upstream| {
lib.installHeadersDirectory(
upstream.path("hwy"),
"hwy",
.{ .include_extensions = &.{".h"} },
);
}
b.installArtifact(lib);
{
const test_exe = b.addTest(.{
.name = "test",
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
}),
});
test_exe.linkLibrary(lib);
var it = module.import_table.iterator();
while (it.next()) |entry| test_exe.root_module.addImport(entry.key_ptr.*, entry.value_ptr.*);
const tests_run = b.addRunArtifact(test_exe);
const test_step = b.step("test", "Run tests");
test_step.dependOn(&tests_run.step);
}
}
+17
View File
@@ -0,0 +1,17 @@
.{
.name = .highway,
.version = "1.2.0",
.fingerprint = 0xdbcf1a7425023274,
.paths = .{""},
.dependencies = .{
// google/highway
.highway = .{
.url = "https://deps.files.ghostty.org/highway-66486a10623fa0d72fe91260f96c892e41aceb06.tar.gz",
.hash = "N-V-__8AAGmZhABbsPJLfbqrh6JTHsXhY6qCaLAQyx25e0XE",
.lazy = true,
},
.android_ndk = .{ .path = "../android-ndk" },
.apple_sdk = .{ .path = "../apple-sdk" },
},
}
+70
View File
@@ -0,0 +1,70 @@
// Copyright 2019 Google LLC
// Copyright 2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: BSD-3-Clause
// Vendored from google/highway hwy/abort.cc at commit:
// 66486a10623fa0d72fe91260f96c892e41aceb06
//
// Local modifications:
// - Removed stdio/stdlib/string/sanitizer-backed formatting and logging paths
// so this file no longer pulls in libc/libc++ symbols.
// - Replaced std::atomic storage with compiler atomics on plain function
// pointers to preserve thread-safe handler installation without libc++.
// - Kept only the Warn/Abort symbol surface Highway's runtime dispatch needs,
// with a trap-only fallback when no abort handler is installed.
//
// Why:
// - Ghostty only needs Highway's runtime dispatch support here, not its
// formatted stderr diagnostics.
// - Keeping this translation unit libc/libc++ free lets pkg/highway build as a
// small vendored shim around Zig-driven target detection.
#include "hwy/abort.h"
#include "hwy/base.h"
namespace hwy {
namespace {
WarnFunc g_warn_func = nullptr;
AbortFunc g_abort_func = nullptr;
} // namespace
HWY_DLLEXPORT WarnFunc& GetWarnFunc() {
return g_warn_func;
}
HWY_DLLEXPORT AbortFunc& GetAbortFunc() {
return g_abort_func;
}
HWY_DLLEXPORT WarnFunc SetWarnFunc(WarnFunc func) {
return __atomic_exchange_n(&g_warn_func, func, __ATOMIC_SEQ_CST);
}
HWY_DLLEXPORT AbortFunc SetAbortFunc(AbortFunc func) {
return __atomic_exchange_n(&g_abort_func, func, __ATOMIC_SEQ_CST);
}
HWY_DLLEXPORT void HWY_FORMAT(3, 4)
Warn(const char* file, int line, const char* format, ...) {
WarnFunc handler = __atomic_load_n(&g_warn_func, __ATOMIC_SEQ_CST);
if (handler != nullptr) {
handler(file, line, format);
}
}
HWY_DLLEXPORT HWY_NORETURN void HWY_FORMAT(3, 4)
Abort(const char* file, int line, const char* format, ...) {
AbortFunc handler = __atomic_load_n(&g_abort_func, __ATOMIC_SEQ_CST);
if (handler != nullptr) {
handler(file, line, format);
}
__builtin_trap();
}
} // namespace hwy
+91
View File
@@ -0,0 +1,91 @@
// Copyright 2022 Google LLC
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Vendored from google/highway hwy/per_target.cc at commit:
// 66486a10623fa0d72fe91260f96c892e41aceb06
//
// Local modifications:
// - Changed HWY_TARGET_INCLUDE from the upstream path to the local vendored
// filename so Highway's multi-pass include machinery resolves this copy.
// - Left the implementation otherwise identical to upstream.
//
// Why:
// - Ghostty vendors only the specific Highway .cc files it needs in this
// directory, so the original source-relative include path no longer exists.
// - Keeping the logic unchanged aside from the include path reduces fork
// maintenance cost while still allowing a minimal vendored source set.
// Enable all targets so that calling Have* does not call into a null pointer.
#ifndef HWY_COMPILE_ALL_ATTAINABLE
#define HWY_COMPILE_ALL_ATTAINABLE
#endif
#include "hwy/per_target.h"
#include <stddef.h>
#include <stdint.h>
#undef HWY_TARGET_INCLUDE
#define HWY_TARGET_INCLUDE "per_target.cc"
#include "hwy/foreach_target.h" // IWYU pragma: keep
#include "hwy/highway.h"
HWY_BEFORE_NAMESPACE();
namespace hwy {
namespace HWY_NAMESPACE {
namespace {
int64_t GetTarget() { return HWY_TARGET; }
size_t GetVectorBytes() { return Lanes(ScalableTag<uint8_t>()); }
bool GetHaveInteger64() { return HWY_HAVE_INTEGER64 != 0; }
bool GetHaveFloat16() { return HWY_HAVE_FLOAT16 != 0; }
bool GetHaveFloat64() { return HWY_HAVE_FLOAT64 != 0; }
} // namespace
// NOLINTNEXTLINE(google-readability-namespace-comments)
} // namespace HWY_NAMESPACE
} // namespace hwy
HWY_AFTER_NAMESPACE();
#if HWY_ONCE
namespace hwy {
namespace {
HWY_EXPORT(GetTarget);
HWY_EXPORT(GetVectorBytes);
HWY_EXPORT(GetHaveInteger64);
HWY_EXPORT(GetHaveFloat16);
HWY_EXPORT(GetHaveFloat64);
} // namespace
HWY_DLLEXPORT int64_t DispatchedTarget() {
return HWY_DYNAMIC_DISPATCH(GetTarget)();
}
HWY_DLLEXPORT size_t VectorBytes() {
return HWY_DYNAMIC_DISPATCH(GetVectorBytes)();
}
HWY_DLLEXPORT bool HaveInteger64() {
return HWY_DYNAMIC_DISPATCH(GetHaveInteger64)();
}
HWY_DLLEXPORT bool HaveFloat16() {
return HWY_DYNAMIC_DISPATCH(GetHaveFloat16)();
}
HWY_DLLEXPORT bool HaveFloat64() {
return HWY_DYNAMIC_DISPATCH(GetHaveFloat64)();
}
} // namespace hwy
#endif // HWY_ONCE
+79
View File
@@ -0,0 +1,79 @@
// Vendored from google/highway hwy/targets.cc at commit:
// 66486a10623fa0d72fe91260f96c892e41aceb06
//
// Local modifications:
// - Dropped upstream CPU feature probing and platform-specific detection code
// in favor of Ghostty's Zig-provided ghostty_hwy_detect_targets().
// - Removed the HWY_WARN baseline-mismatch diagnostic path so this file does
// not depend on libc-backed formatting/logging.
// - Kept only the chosen-target bookkeeping and runtime dispatch state that
// Highway's HWY_DYNAMIC_DISPATCH machinery needs.
// - Added hwy_supported_targets() as a small C shim for Zig to query the final
// supported target mask.
//
// Why:
// - Ghostty wants a minimal vendored Highway runtime that avoids direct libc
// usage and lets Zig own target detection policy.
// - Narrowing this file to dispatch state makes the local fork easier to audit
// and maintain than carrying upstream's full platform detection surface.
#include "hwy/targets.h"
namespace hwy {
extern "C" int64_t ghostty_hwy_detect_targets();
// Vendored from Highway's hwy/targets.cc. Ghostty provides target detection in
// Zig, so this TU only retains the runtime dispatch/chosen-target state.
static int64_t DetectTargets() {
int64_t bits = HWY_SCALAR | HWY_EMU128;
#if (HWY_ARCH_X86 || HWY_ARCH_ARM || HWY_ARCH_PPC || HWY_ARCH_S390X || \
HWY_ARCH_RISCV || HWY_ARCH_LOONGARCH) && \
HWY_HAVE_RUNTIME_DISPATCH
bits |= ghostty_hwy_detect_targets();
#else
bits |= HWY_ENABLED_BASELINE;
#endif
return bits;
}
// When running tests, this value can be set to the mocked supported targets
// mask. Only written to from a single thread before the test starts.
static int64_t supported_targets_for_test_ = 0;
// Mask of targets disabled at runtime with DisableTargets.
static int64_t supported_mask_ = LimitsMax<int64_t>();
HWY_DLLEXPORT void DisableTargets(int64_t disabled_targets) {
supported_mask_ = static_cast<int64_t>(~disabled_targets);
GetChosenTarget().DeInit();
}
HWY_DLLEXPORT void SetSupportedTargetsForTest(int64_t targets) {
supported_targets_for_test_ = targets;
GetChosenTarget().DeInit();
}
HWY_DLLEXPORT int64_t SupportedTargets() {
int64_t targets = supported_targets_for_test_;
if (HWY_LIKELY(targets == 0)) {
targets = DetectTargets();
GetChosenTarget().Update(targets);
}
targets &= supported_mask_;
return targets == 0 ? HWY_STATIC_TARGET : targets;
}
HWY_DLLEXPORT ChosenTarget& GetChosenTarget() {
static ChosenTarget chosen_target;
return chosen_target;
}
} // namespace hwy
extern "C" int64_t hwy_supported_targets() {
return hwy::SupportedTargets();
}
+49
View File
@@ -0,0 +1,49 @@
const builtin = @import("builtin");
const HwyTargets = @import("targets.zig").Targets;
const x86 = @import("detect/x86.zig");
const aarch64_darwin = @import("detect/aarch64_darwin.zig");
const aarch64_linux = @import("detect/aarch64_linux.zig");
const ppc = @import("detect/ppc.zig");
const s390x = @import("detect/s390x.zig");
const riscv = @import("detect/riscv.zig");
const loongarch = @import("detect/loongarch.zig");
/// Detect Highway targets at runtime using minimal, direct CPU feature
/// probing.
///
/// Previous versions called std.zig.system.resolveTargetQuery which
/// drags in the full Zig target/CPU model tables for every architecture,
/// bloating the binary by ~300 KB and causing code-layout regressions in
/// unrelated hot paths (icache / branch-predictor pressure).
///
/// This version uses only inline assembly (CPUID on x86, MRS on AArch64)
/// and lightweight syscalls (sysctlbyname on Darwin, getauxval on Linux),
/// so it adds no data tables and no std.Target dependency.
pub export fn ghostty_hwy_detect_targets() callconv(.c) i64 {
return switch (builtin.cpu.arch) {
.x86_64, .x86 => x86.detect(),
.aarch64, .aarch64_be => detectAarch64(),
.powerpc, .powerpc64, .powerpc64le => ppc.detect(),
.s390x => s390x.detect(),
.riscv32, .riscv64 => riscv.detect(),
.loongarch32, .loongarch64 => loongarch.detect(),
else => 0,
};
}
fn detectAarch64() i64 {
var t: HwyTargets = .{};
// All AArch64 implementations have NEON.
t.neon_without_aes = true;
if (comptime builtin.os.tag.isDarwin()) {
return aarch64_darwin.detect(&t);
} else if (comptime builtin.os.tag == .linux) {
return aarch64_linux.detect(&t);
}
// Other OS: return baseline NEON.
return @bitCast(t);
}
+33
View File
@@ -0,0 +1,33 @@
const HwyTargets = @import("../targets.zig").Targets;
pub fn detect(t: *HwyTargets) i64 {
// All Apple Silicon has AES.
t.neon = true;
// Every Apple chip from A11 (2017) onward has FP16 + DotProd.
// BF16 arrived with M2 / A15 (ARM_BLIZZARD_AVALANCHE, 2022).
// We probe hw.optional.arm.FEAT_BF16 to be precise.
const has_bf16 = darwinSysctlBool("hw.optional.arm.FEAT_BF16");
if (has_bf16) {
t.neon_bf16 = true;
}
// Apple Silicon does not support SVE.
return @bitCast(t.*);
}
fn darwinSysctlBool(comptime name: [:0]const u8) bool {
var value: c_int = 0;
var len: usize = @sizeOf(c_int);
const rc = sysctlbyname(name.ptr, &value, &len, null, 0);
return rc == 0 and value != 0;
}
// We can rely on libc for macOS because libsystem is always available.
extern "c" fn sysctlbyname(
name: [*:0]const u8,
oldp: ?*anyopaque,
oldlenp: ?*usize,
newp: ?*const anyopaque,
newlen: usize,
) c_int;
+65
View File
@@ -0,0 +1,65 @@
const HwyTargets = @import("../targets.zig").Targets;
const linux = @import("linux.zig");
pub fn detect(t: *HwyTargets) i64 {
// Linux exposes AArch64 features via getauxval(AT_HWCAP / AT_HWCAP2).
const AT_HWCAP: usize = 16;
const AT_HWCAP2: usize = 26;
const hwcap = linux.getauxval(AT_HWCAP);
const hwcap2 = linux.getauxval(AT_HWCAP2);
// Bit positions from Linux UAPI asm/hwcap.h
const HWCAP_AES: usize = 1 << 3;
const HWCAP_FPHP: usize = 1 << 9; // FEAT_FP16
const HWCAP_ASIMDDP: usize = 1 << 20; // DotProd
const HWCAP_SVE: usize = 1 << 22;
const HWCAP2_BF16: usize = 1 << 14;
const HWCAP2_SVE2: usize = 1 << 1;
const HWCAP2_SVEAES: usize = 1 << 2;
if (hwcap & HWCAP_AES != 0) {
t.neon = true;
if (hwcap & HWCAP_FPHP != 0 and
hwcap & HWCAP_ASIMDDP != 0 and
hwcap2 & HWCAP2_BF16 != 0)
{
t.neon_bf16 = true;
}
}
if (hwcap & HWCAP_SVE != 0) {
const vec_bytes = sveVectorBytes();
if (vec_bytes >= 32) {
t.sve = true;
if (vec_bytes == 32) {
t.sve_256 = true;
}
}
if (hwcap2 & HWCAP2_SVE2 != 0 and hwcap2 & HWCAP2_SVEAES != 0) {
if (vec_bytes >= 32) {
t.sve2 = true;
} else if (vec_bytes == 16) {
t.sve2_128 = true;
}
}
}
return @bitCast(t.*);
}
fn sveVectorBytes() usize {
// PR_SVE_GET_VL returns the SVE vector length in the lower 16 bits.
const PR_SVE_GET_VL: i32 = 51;
const ret = linux.prctl(PR_SVE_GET_VL, 0, 0, 0, 0);
const signed: isize = @bitCast(ret);
if (signed >= 0) {
return ret & 0xFFFF;
}
// prctl failed: assume 128-bit (NEON-width, conservative).
return 16;
}
+10
View File
@@ -0,0 +1,10 @@
/// Reads from the ELF auxiliary vector (set by the kernel at process
/// start). Does not call into libc.
pub inline fn getauxval(key: usize) usize {
return @import("std").os.linux.getauxval(key);
}
/// Direct syscall wrapper for prctl(2).
pub inline fn prctl(option: i32, a2: usize, a3: usize, a4: usize, a5: usize) usize {
return @import("std").os.linux.prctl(option, a2, a3, a4, a5);
}
+26
View File
@@ -0,0 +1,26 @@
const builtin = @import("builtin");
const HwyTargets = @import("../targets.zig").Targets;
const linux = @import("linux.zig");
pub fn detect() i64 {
var t: HwyTargets = .{};
if (comptime builtin.os.tag != .linux) return @bitCast(t);
const AT_HWCAP: usize = 16;
const hwcap = linux.getauxval(AT_HWCAP);
// From Linux arch/loongarch/include/uapi/asm/hwcap.h
const HWCAP_LSX: usize = 1 << 4;
const HWCAP_LASX: usize = 1 << 5;
if (hwcap & HWCAP_LSX != 0) {
t.lsx = true;
if (hwcap & HWCAP_LASX != 0) {
t.lasx = true;
}
}
return @bitCast(t);
}
+43
View File
@@ -0,0 +1,43 @@
const builtin = @import("builtin");
const HwyTargets = @import("../targets.zig").Targets;
const linux = @import("linux.zig");
pub fn detect() i64 {
var t: HwyTargets = .{};
if (comptime builtin.os.tag != .linux) return @bitCast(t);
const AT_HWCAP: usize = 16;
const AT_HWCAP2: usize = 26;
const hwcap = linux.getauxval(AT_HWCAP);
const hwcap2 = linux.getauxval(AT_HWCAP2);
// From Linux arch/powerpc/include/uapi/asm/cputable.h
const PPC_FEATURE_HAS_ALTIVEC: usize = 0x10000000;
const PPC_FEATURE_HAS_VSX: usize = 0x00000080;
const PPC_FEATURE2_ARCH_2_07: usize = 0x80000000; // POWER8
const PPC_FEATURE2_VEC_CRYPTO: usize = 0x02000000;
const PPC_FEATURE2_ARCH_3_00: usize = 0x00800000; // POWER9
const PPC_FEATURE2_ARCH_3_1: usize = 0x00040000; // POWER10
const PPC_FEATURE2_MMA: usize = 0x00020000;
if (hwcap & PPC_FEATURE_HAS_ALTIVEC != 0 and
hwcap & PPC_FEATURE_HAS_VSX != 0 and
hwcap2 & PPC_FEATURE2_ARCH_2_07 != 0 and
hwcap2 & PPC_FEATURE2_VEC_CRYPTO != 0)
{
t.ppc8 = true;
if (hwcap2 & PPC_FEATURE2_ARCH_3_00 != 0) {
t.ppc9 = true;
if (hwcap2 & PPC_FEATURE2_ARCH_3_1 != 0 and
hwcap2 & PPC_FEATURE2_MMA != 0)
{
t.ppc10 = true;
}
}
}
return @bitCast(t);
}
+22
View File
@@ -0,0 +1,22 @@
const builtin = @import("builtin");
const HwyTargets = @import("../targets.zig").Targets;
const linux = @import("linux.zig");
pub fn detect() i64 {
var t: HwyTargets = .{};
if (comptime builtin.os.tag != .linux) return @bitCast(t);
const AT_HWCAP: usize = 16;
const hwcap = linux.getauxval(AT_HWCAP);
// ISA extension bit for 'V' (vector).
// Letter-based bits: bit position = letter - 'A'.
const HWCAP_V: usize = 1 << ('V' - 'A');
if (hwcap & HWCAP_V != 0) {
t.rvv = true;
}
return @bitCast(t);
}
+29
View File
@@ -0,0 +1,29 @@
const builtin = @import("builtin");
const HwyTargets = @import("../targets.zig").Targets;
const linux = @import("linux.zig");
pub fn detect() i64 {
var t: HwyTargets = .{};
if (comptime builtin.os.tag != .linux) return @bitCast(t);
const AT_HWCAP: usize = 16;
const hwcap = linux.getauxval(AT_HWCAP);
// From Linux arch/s390/include/asm/elf.h
const HWCAP_VX: usize = 1 << 11;
const HWCAP_VXE: usize = 1 << 13; // z14
const HWCAP_VXE2: usize = 1 << 15; // z15
if (hwcap & HWCAP_VX != 0) {
if (hwcap & HWCAP_VXE != 0) {
t.z14 = true;
if (hwcap & HWCAP_VXE2 != 0) {
t.z15 = true;
}
}
}
return @bitCast(t);
}
+166
View File
@@ -0,0 +1,166 @@
const builtin = @import("builtin");
const HwyTargets = @import("../targets.zig").Targets;
const CpuidResult = struct { eax: u32, ebx: u32, ecx: u32, edx: u32 };
fn cpuid(leaf: u32, subleaf: u32) CpuidResult {
var eax: u32 = undefined;
var ebx: u32 = undefined;
var ecx: u32 = undefined;
var edx: u32 = undefined;
asm volatile ("cpuid"
: [_] "={eax}" (eax),
[_] "={ebx}" (ebx),
[_] "={ecx}" (ecx),
[_] "={edx}" (edx),
: [_] "{eax}" (leaf),
[_] "{ecx}" (subleaf),
);
return .{ .eax = eax, .ebx = ebx, .ecx = ecx, .edx = edx };
}
inline fn bit(val: u32, comptime pos: u5) bool {
return (val >> pos) & 1 != 0;
}
pub fn detect() i64 {
var t: HwyTargets = .{};
// x86_64 always has SSE2.
if (comptime builtin.cpu.arch == .x86_64) {
t.sse2 = true;
}
const leaf0 = cpuid(0, 0);
const max_leaf = leaf0.eax;
if (max_leaf < 1) return @bitCast(t);
const leaf1 = cpuid(1, 0);
// -- SSE2 on 32-bit x86 -------------------------------------------------
if (comptime builtin.cpu.arch == .x86) {
if (bit(leaf1.edx, 25) and bit(leaf1.edx, 26)) {
t.sse2 = true;
}
}
// -- SSSE3 ---------------------------------------------------------------
if (bit(leaf1.ecx, 0) and // SSE3
bit(leaf1.ecx, 9)) // SSSE3
{
t.ssse3 = true;
}
// -- SSE4 ----------------------------------------------------------------
if (bit(leaf1.ecx, 19) and // SSE4.1
bit(leaf1.ecx, 20) and // SSE4.2
bit(leaf1.ecx, 1) and // PCLMUL
bit(leaf1.ecx, 25)) // AES
{
t.sse4 = true;
}
// Check XSAVE / AVX OS support before enabling any AVX-dependent target.
const has_xsave = bit(leaf1.ecx, 27);
const has_avx_bit = bit(leaf1.ecx, 28);
const xcr0: u32 = if (has_xsave and has_avx_bit) asm volatile ("xgetbv"
: [_] "={eax}" (-> u32),
: [_] "{ecx}" (@as(u32, 0)),
: .{ .edx = true }) else 0;
const has_avx_save = (xcr0 & 0x6) == 0x6; // SSE + AVX state
// Darwin lazily saves AVX-512 context on first use.
const has_avx512_save = if (comptime builtin.os.tag.isDarwin())
true
else
(xcr0 & 0xE0) == 0xE0; // opmask + zmm_hi256 + hi16_zmm
// -- AVX2 ----------------------------------------------------------------
if (has_avx_save and max_leaf >= 7) {
const leaf7 = cpuid(7, 0);
if (bit(leaf7.ebx, 5) and // AVX2
bit(leaf1.ecx, 12) and // FMA
bit(leaf1.ecx, 29)) // F16C
{
// Also need LZCNT (extended leaf), BMI, BMI2.
const leaf_ext = cpuid(0x80000001, 0);
if (bit(leaf_ext.ecx, 5) and // LZCNT
bit(leaf7.ebx, 3) and // BMI
bit(leaf7.ebx, 8)) // BMI2
{
t.avx2 = true;
}
}
// -- AVX-512 ---------------------------------------------------------
if (has_avx512_save) {
if (bit(leaf7.ebx, 16) and // AVX512F
bit(leaf7.ebx, 31) and // AVX512VL
bit(leaf7.ebx, 17) and // AVX512DQ
bit(leaf7.ebx, 30) and // AVX512BW
bit(leaf7.ebx, 28)) // AVX512CD
{
t.avx3 = true;
}
if (bit(leaf7.ecx, 11) and // AVX512VNNI
bit(leaf7.ecx, 10) and // VPCLMULQDQ (AVX save ok)
bit(leaf7.ecx, 1) and // AVX512VBMI
bit(leaf7.ecx, 6) and // AVX512VBMI2
bit(leaf7.ecx, 9) and // VAES (AVX save ok)
bit(leaf7.ecx, 14) and // AVX512VPOPCNTDQ
bit(leaf7.ecx, 12) and // AVX512BITALG
bit(leaf7.ecx, 8)) // GFNI
{
t.avx3_dl = true;
}
// AVX512BF16 is in leaf 7 sub-1.
if (t.avx3_dl and leaf7.eax >= 1) {
const leaf7_1 = cpuid(7, 1);
if (bit(leaf7_1.eax, 5)) { // AVX512BF16
if (isAMD()) {
t.avx3_zen4 = true;
}
}
if (bit(leaf7.edx, 23) and // AVX512FP16
bit(leaf7_1.eax, 5)) // AVX512BF16
{
t.avx3_spr = true;
}
} else if (bit(leaf7.edx, 23)) { // AVX512FP16 without sub-leaf
// Can't check BF16 without sub-leaf support, skip avx3_spr.
}
}
// -- AVX10 -----------------------------------------------------------
if (max_leaf >= 7 and cpuid(7, 0).eax >= 1) {
const leaf7_1 = cpuid(7, 1);
if (bit(leaf7_1.edx, 19)) { // AVX10.1-256
if (max_leaf >= 0x24) {
const leaf24 = cpuid(0x24, 0);
if (bit(leaf24.ebx, 18)) { // AVX10.1-512
t.avx3_spr = true;
t.avx3_dl = true;
t.avx3 = true;
}
}
// AVX10.2 detection would require a leaf we can't
// reliably check yet; leave for future.
}
}
}
return @bitCast(t);
}
fn isAMD() bool {
const leaf0 = cpuid(0, 0);
// "Auth" "enti" "cAMD"
return leaf0.ebx == 0x68747541 and
leaf0.ecx == 0x444d4163 and
leaf0.edx == 0x69746e65;
}
+12
View File
@@ -0,0 +1,12 @@
extern "c" fn hwy_supported_targets() i64;
pub const Targets = @import("targets.zig").Targets;
pub fn supported_targets() Targets {
return @bitCast(hwy_supported_targets());
}
test {
_ = supported_targets();
_ = @import("runtime_detect.zig");
}
+109
View File
@@ -0,0 +1,109 @@
const assert = @import("std").debug.assert;
pub const Targets = packed struct(i64) {
// x86_64
_reserved_0_2: u3 = 0,
avx10_2_512: bool = false,
avx3_spr: bool = false,
avx10_2: bool = false,
avx3_zen4: bool = false,
avx3_dl: bool = false,
avx3: bool = false,
avx2: bool = false,
_reserved_10: u1 = 0,
sse4: bool = false,
ssse3: bool = false,
_reserved_13: u1 = 0,
sse2: bool = false,
_reserved_15_17: u3 = 0,
// aarch64
sve2_128: bool = false,
sve_256: bool = false,
_reserved_20_22: u3 = 0,
sve2: bool = false,
sve: bool = false,
_reserved_25: u1 = 0,
neon_bf16: bool = false,
_reserved_27: u1 = 0,
neon: bool = false,
neon_without_aes: bool = false,
_reserved_30_36: u7 = 0,
// risc-v
rvv: bool = false,
_reserved_38_39: u2 = 0,
// LoongArch
lasx: bool = false,
lsx: bool = false,
_reserved_42_46: u5 = 0,
// IBM Power
ppc10: bool = false,
ppc9: bool = false,
ppc8: bool = false,
z15: bool = false,
z14: bool = false,
_reserved_52_57: u6 = 0,
// WebAssembly
wasm_emu256: bool = false,
wasm: bool = false,
_reserved_60: u1 = 0,
// Emulation
emu128: bool = false,
scalar: bool = false,
_reserved_63: u1 = 0,
fn bitPos(comptime field_name: []const u8) comptime_int {
return @bitOffsetOf(Targets, field_name);
}
// Verify at comptime that each flag field matches its Highway bit constant.
comptime {
// x86
assert(bitPos("avx10_2_512") == 3);
assert(bitPos("avx3_spr") == 4);
assert(bitPos("avx10_2") == 5);
assert(bitPos("avx3_zen4") == 6);
assert(bitPos("avx3_dl") == 7);
assert(bitPos("avx3") == 8);
assert(bitPos("avx2") == 9);
assert(bitPos("sse4") == 11);
assert(bitPos("ssse3") == 12);
assert(bitPos("sse2") == 14);
// aarch64
assert(bitPos("sve2_128") == 18);
assert(bitPos("sve_256") == 19);
assert(bitPos("sve2") == 23);
assert(bitPos("sve") == 24);
assert(bitPos("neon_bf16") == 26);
assert(bitPos("neon") == 28);
assert(bitPos("neon_without_aes") == 29);
// risc-v
assert(bitPos("rvv") == 37);
// LoongArch
assert(bitPos("lasx") == 40);
assert(bitPos("lsx") == 41);
// IBM Power
assert(bitPos("ppc10") == 47);
assert(bitPos("ppc9") == 48);
assert(bitPos("ppc8") == 49);
assert(bitPos("z15") == 50);
assert(bitPos("z14") == 51);
// WebAssembly
assert(bitPos("wasm_emu256") == 58);
assert(bitPos("wasm") == 59);
// Emulation
assert(bitPos("emu128") == 61);
assert(bitPos("scalar") == 62);
}
};
+104
View File
@@ -0,0 +1,104 @@
//! Provides libintl for macOS.
//!
//! IMPORTANT: This is only for macOS. We could support other platforms
//! if/when we need to but generally Linux provides libintl in libc.
//! Windows we'll have to figure out when we get there.
//!
//! Since this is only for macOS, there's a lot of hardcoded stuff
//! here that assumes macOS. For example, I generated the config.h
//! on my own machine (a Mac) and then copied it here. This isn't
//! ideal since we should do the same detection that gettext's configure
//! script does, but its quite a bit of work to do that.
//!
//! UPGRADING: If you need to upgrade gettext, then the only thing to
//! really watch out for is the xlocale.h include we added manually
//! at the end of config.h. The comment there notes why. When we upgrade
//! we should audit our config.h and make sure we add that back (if we
//! have to).
const std = @import("std");
pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
var flags: std.ArrayList([]const u8) = .empty;
defer flags.deinit(b.allocator);
try flags.appendSlice(b.allocator, &.{
"-DHAVE_CONFIG_H",
"-DLOCALEDIR=\"\"",
});
{
const lib = b.addLibrary(.{
.name = "intl",
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
}),
.linkage = .static,
});
lib.linkLibC();
lib.addIncludePath(b.path(""));
if (target.result.os.tag.isDarwin()) {
const apple_sdk = @import("apple_sdk");
try apple_sdk.addPaths(b, lib);
}
if (b.lazyDependency("gettext", .{})) |upstream| {
lib.addIncludePath(upstream.path("gettext-runtime/intl"));
lib.addIncludePath(upstream.path("gettext-runtime/intl/gnulib-lib"));
lib.addCSourceFiles(.{
.root = upstream.path("gettext-runtime/intl"),
.files = srcs,
.flags = flags.items,
});
}
lib.installHeader(b.path("libintl.h"), "libintl.h");
b.installArtifact(lib);
}
}
const srcs: []const []const u8 = &.{
"bindtextdom.c",
"dcgettext.c",
"dcigettext.c",
"dcngettext.c",
"dgettext.c",
"dngettext.c",
"explodename.c",
"finddomain.c",
"gettext.c",
"hash-string.c",
"intl-compat.c",
"l10nflist.c",
"langprefs.c",
"loadmsgcat.c",
"localealias.c",
"log.c",
"ngettext.c",
"plural-exp.c",
"plural.c",
"setlocale.c",
"textdomain.c",
"version.c",
"compat.c",
// There's probably a better way to detect that we need these, but
// these are hardcoded for now for macOS.
"gnulib-lib/getlocalename_l-unsafe.c",
"gnulib-lib/localename.c",
"gnulib-lib/localename-environ.c",
"gnulib-lib/localename-unsafe.c",
"gnulib-lib/setlocale-lock.c",
"gnulib-lib/setlocale_null.c",
"gnulib-lib/setlocale_null-unlocked.c",
// Not needed for macOS, but we might need them for other platforms.
// If we expand this to support other platforms, we should uncomment
// these.
// "osdep.c",
// "printf.c",
};
+15
View File
@@ -0,0 +1,15 @@
.{
.name = .libintl,
.version = "0.24.0",
.fingerprint = 0x16434c723ba7278a,
.paths = .{""},
.dependencies = .{
.gettext = .{
.url = "https://deps.files.ghostty.org/gettext-0.24.tar.gz",
.hash = "N-V-__8AADcZkgn4cMhTUpIz6mShCKyqqB-NBtf_S2bHaTC-",
.lazy = true,
},
.apple_sdk = .{ .path = "../apple-sdk" },
},
}
+2370
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -0,0 +1 @@
#include "libintl.h"
+1168
View File
File diff suppressed because it is too large Load Diff
+97
View File
@@ -0,0 +1,97 @@
const std = @import("std");
pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const lib = b.addLibrary(.{
.name = "png",
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
}),
.linkage = .static,
});
lib.linkLibC();
if (target.result.os.tag == .linux) {
lib.linkSystemLibrary("m");
}
if (target.result.os.tag.isDarwin()) {
const apple_sdk = @import("apple_sdk");
try apple_sdk.addPaths(b, lib);
}
// For dynamic linking, we prefer dynamic linking and to search by
// mode first. Mode first will search all paths for a dynamic library
// before falling back to static.
const dynamic_link_opts: std.Build.Module.LinkSystemLibraryOptions = .{
.preferred_link_mode = .dynamic,
.search_strategy = .mode_first,
};
if (b.systemIntegrationOption("zlib", .{})) {
lib.linkSystemLibrary2("zlib", dynamic_link_opts);
} else {
if (b.lazyDependency(
"zlib",
.{ .target = target, .optimize = optimize },
)) |zlib_dep| {
lib.linkLibrary(zlib_dep.artifact("z"));
lib.addIncludePath(b.path(""));
}
if (b.lazyDependency("libpng", .{})) |upstream| {
lib.addIncludePath(upstream.path(""));
}
}
if (b.lazyDependency("libpng", .{})) |upstream| {
var flags: std.ArrayList([]const u8) = .empty;
defer flags.deinit(b.allocator);
try flags.appendSlice(b.allocator, &.{
"-DPNG_ARM_NEON_OPT=0",
"-DPNG_POWERPC_VSX_OPT=0",
"-DPNG_INTEL_SSE_OPT=0",
"-DPNG_MIPS_MSA_OPT=0",
});
if (target.result.abi == .msvc) {
try flags.appendSlice(b.allocator, &.{
"-fno-sanitize=undefined",
"-fno-sanitize-trap=undefined",
});
}
lib.addCSourceFiles(.{
.root = upstream.path(""),
.files = srcs,
.flags = flags.items,
});
lib.installHeader(b.path("pnglibconf.h"), "pnglibconf.h");
lib.installHeadersDirectory(
upstream.path(""),
"",
.{ .include_extensions = &.{".h"} },
);
}
b.installArtifact(lib);
}
const srcs: []const []const u8 = &.{
"png.c",
"pngerror.c",
"pngget.c",
"pngmem.c",
"pngpread.c",
"pngread.c",
"pngrio.c",
"pngrtran.c",
"pngrutil.c",
"pngset.c",
"pngtrans.c",
"pngwio.c",
"pngwrite.c",
"pngwtran.c",
"pngwutil.c",
};
+17
View File
@@ -0,0 +1,17 @@
.{
.name = .libpng,
.version = "1.6.43",
.fingerprint = 0xb7a09eb437ca8a79,
.paths = .{""},
.dependencies = .{
// glennrp/libpng
.libpng = .{
.url = "https://deps.files.ghostty.org/libpng-1220aa013f0c83da3fb64ea6d327f9173fa008d10e28bc9349eac3463457723b1c66.tar.gz",
.hash = "N-V-__8AAJrvXQCqAT8Mg9o_tk6m0yf5Fz-gCNEOKLyTSerD",
.lazy = true,
},
.zlib = .{ .path = "../zlib", .lazy = true },
.apple_sdk = .{ .path = "../apple-sdk" },
},
}

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