aboutsummaryrefslogtreecommitdiff
path: root/vendor/libghostty-vt/src/lib/compat
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-09-10 21:51:57 +0300
committerGitHub <noreply@github.com>2026-09-10 21:51:57 +0300
commit425c86179791cd32e2d4ce0ae7f269940b4b0663 (patch)
treee52c4383a6d69216cbe2a32638bf90841d7e2189 /vendor/libghostty-vt/src/lib/compat
parent90e947a6e26e02f10ec7f1694c4eb5b78a86406b (diff)
chore: upgrade libghostty and preserve terminal behavior (#3906)
Diffstat (limited to 'vendor/libghostty-vt/src/lib/compat')
-rw-r--r--vendor/libghostty-vt/src/lib/compat/README.md37
-rw-r--r--vendor/libghostty-vt/src/lib/compat/fd.zig83
-rw-r--r--vendor/libghostty-vt/src/lib/compat/file.zig56
-rw-r--r--vendor/libghostty-vt/src/lib/compat/reader.zig64
-rw-r--r--vendor/libghostty-vt/src/lib/compat/testing.zig19
-rw-r--r--vendor/libghostty-vt/src/lib/compat/thread.zig75
6 files changed, 334 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/src/lib/compat/README.md b/vendor/libghostty-vt/src/lib/compat/README.md
new file mode 100644
index 00000000..22f58c10
--- /dev/null
+++ b/vendor/libghostty-vt/src/lib/compat/README.md
@@ -0,0 +1,37 @@
+# Zig Compatibility Libraries
+
+The following directory contains code that either used to exist in the Zig
+standard library, or currently exists in Zig master that has been deemed
+useful to have ahead of a full release.
+
+Code located here should likely be eventually phased out, or moved to a more
+logical location if it's decided it would be useful to keep.
+
+## Licenses
+
+Zig code taken from the standard library is used under the terms of the MIT
+license. The license body follows.
+
+```
+The MIT License (Expat)
+
+Copyright (c) Zig 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.
+```
diff --git a/vendor/libghostty-vt/src/lib/compat/fd.zig b/vendor/libghostty-vt/src/lib/compat/fd.zig
new file mode 100644
index 00000000..3bef7414
--- /dev/null
+++ b/vendor/libghostty-vt/src/lib/compat/fd.zig
@@ -0,0 +1,83 @@
+//! Code taken from 0.15.2 and `std.fs.file`. See README.md for license and
+//! details.
+const std = @import("std");
+
+pub const PipeError = error{
+ SystemFdQuotaExceeded,
+ ProcessFdQuotaExceeded,
+} || std.posix.UnexpectedError;
+
+pub fn close(fd: std.posix.fd_t) void {
+ switch (std.posix.errno(std.posix.system.close(fd))) {
+ .BADF => unreachable, // Always a race condition
+ else => {}, // Includes EINTR, see std.posix source in 0.15.2
+ }
+}
+
+pub fn pipe() PipeError![2]std.posix.fd_t {
+ var fds: [2]std.posix.fd_t = undefined;
+ switch (std.posix.errno(std.posix.system.pipe(&fds))) {
+ .SUCCESS => return fds,
+ .INVAL => unreachable, // Invalid parameters to pipe()
+ .FAULT => unreachable, // Invalid fds pointer
+ .NFILE => return error.SystemFdQuotaExceeded,
+ .MFILE => return error.ProcessFdQuotaExceeded,
+ else => |err| return std.posix.unexpectedErrno(err),
+ }
+}
+
+pub fn pipe2(flags: std.posix.O) PipeError![2]std.posix.fd_t {
+ if (@TypeOf(std.posix.system.pipe2) != void) {
+ var fds: [2]std.posix.fd_t = undefined;
+ switch (std.posix.errno(std.posix.system.pipe2(&fds, flags))) {
+ .SUCCESS => return fds,
+ .INVAL => unreachable, // Invalid flags
+ .FAULT => unreachable, // Invalid fds pointer
+ .NFILE => return error.SystemFdQuotaExceeded,
+ .MFILE => return error.ProcessFdQuotaExceeded,
+ else => |err| return std.posix.unexpectedErrno(err),
+ }
+ }
+
+ const fds: [2]std.posix.fd_t = try pipe();
+ errdefer {
+ close(fds[0]);
+ close(fds[1]);
+ }
+
+ // https://github.com/ziglang/zig/issues/18882
+ if (@as(u32, @bitCast(flags)) == 0)
+ return fds;
+
+ // CLOEXEC is special, it's a file descriptor flag and must be set using
+ // F.SETFD.
+ if (flags.CLOEXEC) {
+ for (fds) |fd| {
+ switch (std.posix.errno(std.posix.system.fcntl(fd, std.posix.F.SETFD, @as(u32, std.posix.FD_CLOEXEC)))) {
+ .SUCCESS => {},
+ .INVAL => unreachable, // Invalid flags
+ .BADF => unreachable, // Always a race condition
+ else => |err| return std.posix.unexpectedErrno(err),
+ }
+ }
+ }
+
+ const new_flags: u32 = f: {
+ var new_flags = flags;
+ new_flags.CLOEXEC = false;
+ break :f @bitCast(new_flags);
+ };
+ // Set every other flag affecting the file status using F.SETFL.
+ if (new_flags != 0) {
+ for (fds) |fd| {
+ switch (std.posix.errno(std.posix.system.fcntl(fd, std.posix.F.SETFL, new_flags))) {
+ .SUCCESS => {},
+ .INVAL => unreachable, // Invalid flags
+ .BADF => unreachable, // Always a race condition
+ else => |err| return std.posix.unexpectedErrno(err),
+ }
+ }
+ }
+
+ return fds;
+}
diff --git a/vendor/libghostty-vt/src/lib/compat/file.zig b/vendor/libghostty-vt/src/lib/compat/file.zig
new file mode 100644
index 00000000..efb31a52
--- /dev/null
+++ b/vendor/libghostty-vt/src/lib/compat/file.zig
@@ -0,0 +1,56 @@
+//! Code taken from 0.15.2 and `std.fs.file`. See README.md for license and
+//! details.
+const builtin = @import("builtin");
+const std = @import("std");
+const global = @import("../../global.zig");
+
+pub const ReadToEndAllocError = error{FileTooBig} ||
+ std.Io.File.ReadStreamingError ||
+ std.mem.Allocator.Error;
+
+/// Read the file from its current position through end-of-stream, returning
+/// `error.FileTooBig` if the result exceeds `max_bytes`.
+///
+/// Caller owns the memory.
+pub fn readToEndAlloc(file: std.Io.File, alloc: std.mem.Allocator, max_bytes: usize) ReadToEndAllocError![]u8 {
+ var read_buf: [4096]u8 = undefined;
+ var result: std.ArrayList(u8) = .empty;
+ defer result.deinit(alloc);
+
+ while (true) {
+ const n = file.readStreaming(global.io(), &.{&read_buf}) catch |err| switch (err) {
+ error.EndOfStream => break,
+ else => |e| return e,
+ };
+ if (n == 0) continue;
+ if (n > max_bytes - result.items.len) return error.FileTooBig;
+ try result.appendSlice(alloc, read_buf[0..n]);
+ }
+
+ return result.toOwnedSlice(alloc);
+}
+
+test "readToEndAlloc reads through EOF and permits exact limit" {
+ const testing = std.testing;
+ const contents = "hello, world";
+
+ var tmp_dir = testing.tmpDir(.{});
+ defer tmp_dir.cleanup();
+ try tmp_dir.dir.writeFile(testing.io, .{
+ .sub_path = "data",
+ .data = contents,
+ });
+
+ const file = try tmp_dir.dir.openFile(testing.io, "data", .{});
+ defer file.close(testing.io);
+ const result = try readToEndAlloc(file, testing.allocator, contents.len);
+ defer testing.allocator.free(result);
+ try testing.expectEqualStrings(contents, result);
+
+ const too_big_file = try tmp_dir.dir.openFile(testing.io, "data", .{});
+ defer too_big_file.close(testing.io);
+ try testing.expectError(
+ error.FileTooBig,
+ readToEndAlloc(too_big_file, testing.allocator, contents.len - 1),
+ );
+}
diff --git a/vendor/libghostty-vt/src/lib/compat/reader.zig b/vendor/libghostty-vt/src/lib/compat/reader.zig
new file mode 100644
index 00000000..28813440
--- /dev/null
+++ b/vendor/libghostty-vt/src/lib/compat/reader.zig
@@ -0,0 +1,64 @@
+//! Code taken from older reader implementations, still may be useful to keep
+//! around in general. See README.md for license and details.
+const std = @import("std");
+
+/// Pulled from old stdlib as this function has been removed.
+pub fn readSkipUntilDelimiterOrEof(reader: *std.Io.Reader, delimiter: u8) std.Io.Reader.Error!void {
+ while (true) {
+ const byte = readByte(reader) catch |err| switch (err) {
+ error.EndOfStream => return,
+ else => |e| return e,
+ };
+ if (byte == delimiter) return;
+ }
+}
+
+/// Pulled from old stdlib as this function has been removed.
+pub fn readStructEndian(
+ reader: *std.Io.Reader,
+ comptime T: type,
+ endian: std.builtin.Endian,
+) std.Io.Reader.Error!T {
+ var result: [1]T = undefined;
+ try reader.readSliceEndian(T, &result, endian);
+ return result[0];
+}
+
+/// Pulled from old stdlib as this function has been removed.
+pub fn readerInt(reader: *std.Io.Reader, comptime T: type, endian: std.builtin.Endian) std.Io.Reader.Error!T {
+ const bytes = try readBytesNoEof(reader, @divExact(@typeInfo(T).int.bits, 8));
+ return std.mem.readInt(T, &bytes, endian);
+}
+
+/// Pulled from old stdlib as this function has been removed.
+pub fn readByteSigned(reader: *std.Io.Reader) std.Io.Reader.Error!i8 {
+ return @as(i8, @bitCast(try readByte(reader)));
+}
+
+/// Pulled from old stdlib as this function has been removed.
+pub fn readByte(reader: *std.Io.Reader) std.Io.Reader.Error!u8 {
+ var result: [1]u8 = undefined;
+ try reader.readSliceAll(&result);
+ return result[0];
+}
+
+/// Pulled from old stdlib as this function has been removed.
+pub fn readSkipBytes(reader: *std.Io.Reader, num_bytes: u64) std.Io.Reader.Error!void {
+ const buf_size = 512;
+
+ var buf: [buf_size]u8 = undefined;
+ var remaining = num_bytes;
+
+ while (remaining > 0) {
+ const amt = @min(remaining, buf_size);
+ try reader.readSliceAll(buf[0..amt]);
+ remaining -= amt;
+ }
+}
+
+/// Pulled from old stdlib as this function has been removed.
+fn readBytesNoEof(reader: *std.Io.Reader, comptime num_bytes: usize) std.Io.Reader.Error![num_bytes]u8 {
+ var bytes: [num_bytes]u8 = undefined;
+ try reader.readSliceAll(&bytes);
+ return bytes;
+}
diff --git a/vendor/libghostty-vt/src/lib/compat/testing.zig b/vendor/libghostty-vt/src/lib/compat/testing.zig
new file mode 100644
index 00000000..e826eb40
--- /dev/null
+++ b/vendor/libghostty-vt/src/lib/compat/testing.zig
@@ -0,0 +1,19 @@
+//! Code taken from 0.15.2 `std.testing`. See README.md for license and
+//! details.
+const builtin = @import("builtin");
+const std = @import("std");
+
+/// Given a type, recursively references all the declarations inside, so that the semantic analyzer sees them.
+/// For deep types, you may use `@setEvalBranchQuota`.
+pub fn refAllDeclsRecursive(comptime T: type) void {
+ if (!builtin.is_test) return;
+ inline for (comptime std.meta.declarations(T)) |decl| {
+ if (@TypeOf(@field(T, decl.name)) == type) {
+ switch (@typeInfo(@field(T, decl.name))) {
+ .@"struct", .@"enum", .@"union", .@"opaque" => refAllDeclsRecursive(@field(T, decl.name)),
+ else => {},
+ }
+ }
+ _ = &@field(T, decl.name);
+ }
+}
diff --git a/vendor/libghostty-vt/src/lib/compat/thread.zig b/vendor/libghostty-vt/src/lib/compat/thread.zig
new file mode 100644
index 00000000..890c02f0
--- /dev/null
+++ b/vendor/libghostty-vt/src/lib/compat/thread.zig
@@ -0,0 +1,75 @@
+//! Code taken from 0.17.0 `std.Io.Condition`. See README.md for license and
+//! details.
+//!
+//! FIXME: Should be able to remove after 0.17.0, see:
+//! https://codeberg.org/ziglang/zig/pulls/31278
+const builtin = @import("builtin");
+const std = @import("std");
+const assert = @import("../../quirks.zig").inlineAssert;
+
+pub const WaitTimeoutError = std.Io.Cancelable || std.Io.Timeout.Error;
+
+/// Blocks until the condition is signaled, canceled, or the provided
+/// timeout expires.
+///
+/// See also:
+/// * `wait`
+/// * `waitUncancelable`
+pub fn waitTimeout(
+ cond: *std.Io.Condition,
+ io: std.Io,
+ mutex: *std.Io.Mutex,
+ timeout: std.Io.Timeout,
+) WaitTimeoutError!void {
+ const deadline = timeout.toDeadline(io);
+
+ var epoch = cond.epoch.load(.acquire); // `.acquire` to ensure ordered before state load
+
+ {
+ const prev_state = cond.state.fetchAdd(.{ .waiters = 1, .signals = 0 }, .monotonic);
+ assert(prev_state.waiters < std.math.maxInt(u16)); // overflow caused by too many waiters
+ }
+
+ mutex.unlock(io);
+ defer mutex.lockUncancelable(io);
+
+ while (true) {
+ const result = io.futexWaitTimeout(u32, &cond.epoch.raw, epoch, deadline);
+
+ epoch = cond.epoch.load(.acquire); // `.acquire` to ensure ordered before `state` load
+
+ // Even on error, try to consume a pending signal first. Otherwise a race might
+ // cause a signal to get stuck in the state with no corresponding waiter.
+ {
+ var prev_state = cond.state.load(.monotonic);
+ while (prev_state.signals > 0) {
+ prev_state = cond.state.cmpxchgWeak(prev_state, .{
+ .waiters = prev_state.waiters - 1,
+ .signals = prev_state.signals - 1,
+ }, .acquire, .monotonic) orelse {
+ // We successfully consumed a signal.
+ return;
+ };
+ }
+ }
+
+ // There are no more signals available; this was a spurious wakeup or an error. If it
+ // was an error, we will remove ourselves as a waiter and return that error. If a
+ // timeout was specified and the deadline has passed, we remove ourselves as a waiter
+ // and return `error.Timeout`. Otherwise, we'll loop back to the futex wait.
+ result catch |err| {
+ const prev_state = cond.state.fetchSub(.{ .waiters = 1, .signals = 0 }, .monotonic);
+ assert(prev_state.waiters > 0); // underflow caused by illegal state
+ return err;
+ };
+ switch (deadline) {
+ .none => {},
+ .deadline => |d| if (d.untilNow(io).raw.nanoseconds >= 0) {
+ const prev_state = cond.state.fetchSub(.{ .waiters = 1, .signals = 0 }, .monotonic);
+ assert(prev_state.waiters > 0); // underflow caused by illegal state
+ return error.Timeout;
+ },
+ .duration => unreachable,
+ }
+ }
+}