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
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:
@@ -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.
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
.{
|
||||
.name = .afl_plus_plus,
|
||||
.fingerprint = 0x465bc4bebb188f16,
|
||||
.version = "0.1.0",
|
||||
.dependencies = .{},
|
||||
.paths = .{
|
||||
"build.zig",
|
||||
"build.zig.zon",
|
||||
"afl.c",
|
||||
},
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
.{
|
||||
.name = .android_ndk,
|
||||
.version = "0.0.2",
|
||||
.fingerprint = 0xee68d62c5a97b68b,
|
||||
.dependencies = .{},
|
||||
.paths = .{
|
||||
"build.zig",
|
||||
"build.zig.zon",
|
||||
},
|
||||
}
|
||||
@@ -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);
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
.{
|
||||
.name = .apple_sdk,
|
||||
.version = "0.1.0",
|
||||
.dependencies = .{},
|
||||
.fingerprint = 0xdde52860f7c464d2,
|
||||
.paths = .{""},
|
||||
}
|
||||
@@ -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",
|
||||
};
|
||||
@@ -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
@@ -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,
|
||||
};
|
||||
@@ -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" },
|
||||
},
|
||||
}
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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",
|
||||
};
|
||||
@@ -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 },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
pub const c = @cImport({
|
||||
@cInclude("fontconfig/fontconfig.h");
|
||||
});
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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,
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
pub const Error = error{
|
||||
OutOfMemory,
|
||||
FontconfigFailed,
|
||||
FontconfigNoMatch,
|
||||
FontconfigTypeMismatch,
|
||||
FontconfigNoId,
|
||||
};
|
||||
@@ -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 ∅
|
||||
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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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"));
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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",
|
||||
};
|
||||
@@ -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" },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
pub const c = @cImport({
|
||||
@cInclude("freetype-zig.h");
|
||||
});
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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());
|
||||
}
|
||||
Executable
BIN
Binary file not shown.
@@ -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 };
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
pub const font_regular = @embedFile("res/FiraCode-Regular.ttf");
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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" },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
pub const c = @cImport({
|
||||
@cInclude("glslang/Include/glslang_c_interface.h");
|
||||
@cInclude("glslang/Public/resource_limits_c.h");
|
||||
});
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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) ) );
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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"));
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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" },
|
||||
},
|
||||
}
|
||||
@@ -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");
|
||||
});
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
@@ -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),
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
@@ -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, µ);
|
||||
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,
|
||||
}));
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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" },
|
||||
},
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
@@ -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",
|
||||
};
|
||||
@@ -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" },
|
||||
},
|
||||
}
|
||||
Generated
+2370
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
#include "libintl.h"
|
||||
Generated
+1168
File diff suppressed because it is too large
Load Diff
@@ -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",
|
||||
};
|
||||
@@ -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
Reference in New Issue
Block a user