diff options
| author | Can Celik <ogulcancelik@gmail.com> | 2026-04-04 19:22:04 +0300 |
|---|---|---|
| committer | Can Celik <ogulcancelik@gmail.com> | 2026-04-05 01:23:06 +0300 |
| commit | 41dd733ca7123c9ac4bfc6c781697a660e95b052 (patch) | |
| tree | 3fc91815ca139ef2e0b281bba0abda4ef15175f4 /vendor/libghostty-vt/pkg/harfbuzz | |
| parent | 55c138263e190a1e79d4b997bfb9397306c0ec1f (diff) | |
feat: finish ghostty pane backend integration
Diffstat (limited to 'vendor/libghostty-vt/pkg/harfbuzz')
| -rw-r--r-- | vendor/libghostty-vt/pkg/harfbuzz/blob.zig | 126 | ||||
| -rw-r--r-- | vendor/libghostty-vt/pkg/harfbuzz/buffer.zig | 373 | ||||
| -rw-r--r-- | vendor/libghostty-vt/pkg/harfbuzz/build.zig | 185 | ||||
| -rw-r--r-- | vendor/libghostty-vt/pkg/harfbuzz/build.zig.zon | 18 | ||||
| -rw-r--r-- | vendor/libghostty-vt/pkg/harfbuzz/c.zig | 8 | ||||
| -rw-r--r-- | vendor/libghostty-vt/pkg/harfbuzz/common.zig | 247 | ||||
| -rw-r--r-- | vendor/libghostty-vt/pkg/harfbuzz/coretext.zig | 30 | ||||
| -rw-r--r-- | vendor/libghostty-vt/pkg/harfbuzz/errors.zig | 5 | ||||
| -rw-r--r-- | vendor/libghostty-vt/pkg/harfbuzz/face.zig | 17 | ||||
| -rw-r--r-- | vendor/libghostty-vt/pkg/harfbuzz/font.zig | 28 | ||||
| -rw-r--r-- | vendor/libghostty-vt/pkg/harfbuzz/freetype.zig | 78 | ||||
| -rw-r--r-- | vendor/libghostty-vt/pkg/harfbuzz/main.zig | 31 | ||||
| -rw-r--r-- | vendor/libghostty-vt/pkg/harfbuzz/shape.zig | 27 | ||||
| -rw-r--r-- | vendor/libghostty-vt/pkg/harfbuzz/version.zig | 47 |
14 files changed, 1220 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/pkg/harfbuzz/blob.zig b/vendor/libghostty-vt/pkg/harfbuzz/blob.zig new file mode 100644 index 00000000..9472e4c7 --- /dev/null +++ b/vendor/libghostty-vt/pkg/harfbuzz/blob.zig @@ -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); +} diff --git a/vendor/libghostty-vt/pkg/harfbuzz/buffer.zig b/vendor/libghostty-vt/pkg/harfbuzz/buffer.zig new file mode 100644 index 00000000..b97c1bef --- /dev/null +++ b/vendor/libghostty-vt/pkg/harfbuzz/buffer.zig @@ -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")); +} diff --git a/vendor/libghostty-vt/pkg/harfbuzz/build.zig b/vendor/libghostty-vt/pkg/harfbuzz/build.zig new file mode 100644 index 00000000..6d8f3be7 --- /dev/null +++ b/vendor/libghostty-vt/pkg/harfbuzz/build.zig @@ -0,0 +1,185 @@ +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", + }); + 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; +} diff --git a/vendor/libghostty-vt/pkg/harfbuzz/build.zig.zon b/vendor/libghostty-vt/pkg/harfbuzz/build.zig.zon new file mode 100644 index 00000000..b7d155e9 --- /dev/null +++ b/vendor/libghostty-vt/pkg/harfbuzz/build.zig.zon @@ -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" }, + }, +} diff --git a/vendor/libghostty-vt/pkg/harfbuzz/c.zig b/vendor/libghostty-vt/pkg/harfbuzz/c.zig new file mode 100644 index 00000000..49e87dce --- /dev/null +++ b/vendor/libghostty-vt/pkg/harfbuzz/c.zig @@ -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"); +}); diff --git a/vendor/libghostty-vt/pkg/harfbuzz/common.zig b/vendor/libghostty-vt/pkg/harfbuzz/common.zig new file mode 100644 index 00000000..840ff586 --- /dev/null +++ b/vendor/libghostty-vt/pkg/harfbuzz/common.zig @@ -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); +} diff --git a/vendor/libghostty-vt/pkg/harfbuzz/coretext.zig b/vendor/libghostty-vt/pkg/harfbuzz/coretext.zig new file mode 100644 index 00000000..73042624 --- /dev/null +++ b/vendor/libghostty-vt/pkg/harfbuzz/coretext.zig @@ -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(); +} diff --git a/vendor/libghostty-vt/pkg/harfbuzz/errors.zig b/vendor/libghostty-vt/pkg/harfbuzz/errors.zig new file mode 100644 index 00000000..44a4d055 --- /dev/null +++ b/vendor/libghostty-vt/pkg/harfbuzz/errors.zig @@ -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, +}; diff --git a/vendor/libghostty-vt/pkg/harfbuzz/face.zig b/vendor/libghostty-vt/pkg/harfbuzz/face.zig new file mode 100644 index 00000000..5ffad5d3 --- /dev/null +++ b/vendor/libghostty-vt/pkg/harfbuzz/face.zig @@ -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); + } +}; diff --git a/vendor/libghostty-vt/pkg/harfbuzz/font.zig b/vendor/libghostty-vt/pkg/harfbuzz/font.zig new file mode 100644 index 00000000..13544fb9 --- /dev/null +++ b/vendor/libghostty-vt/pkg/harfbuzz/font.zig @@ -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), + ); + } +}; diff --git a/vendor/libghostty-vt/pkg/harfbuzz/freetype.zig b/vendor/libghostty-vt/pkg/harfbuzz/freetype.zig new file mode 100644 index 00000000..d90d3f83 --- /dev/null +++ b/vendor/libghostty-vt/pkg/harfbuzz/freetype.zig @@ -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); +} diff --git a/vendor/libghostty-vt/pkg/harfbuzz/main.zig b/vendor/libghostty-vt/pkg/harfbuzz/main.zig new file mode 100644 index 00000000..08a4f9c2 --- /dev/null +++ b/vendor/libghostty-vt/pkg/harfbuzz/main.zig @@ -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()); +} diff --git a/vendor/libghostty-vt/pkg/harfbuzz/shape.zig b/vendor/libghostty-vt/pkg/harfbuzz/shape.zig new file mode 100644 index 00000000..b49020c3 --- /dev/null +++ b/vendor/libghostty-vt/pkg/harfbuzz/shape.zig @@ -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, + ); +} diff --git a/vendor/libghostty-vt/pkg/harfbuzz/version.zig b/vendor/libghostty-vt/pkg/harfbuzz/version.zig new file mode 100644 index 00000000..dbe25b52 --- /dev/null +++ b/vendor/libghostty-vt/pkg/harfbuzz/version.zig @@ -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, + })); +} |
