mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-07-31 04:39:01 +00:00
fix some 0.16 translation regressions
This commit is contained in:
@@ -785,12 +785,13 @@ typedef enum GHOSTTY_ENUM_TYPED {
|
||||
GHOSTTY_TERMINAL_OPT_KITTY_IMAGE_MEDIUM_FILE = 16,
|
||||
|
||||
/**
|
||||
* Enable or disable Kitty image loading via the temporary file medium.
|
||||
* Enable Kitty image loading via the temporary file medium, restricted to
|
||||
* the provided directory. The string data is copied into the terminal.
|
||||
*
|
||||
* A NULL value pointer is a no-op. Has no effect when Kitty graphics
|
||||
* are disabled at build time.
|
||||
* A NULL value pointer disables the temporary file medium. Has no effect
|
||||
* when Kitty graphics are disabled at build time.
|
||||
*
|
||||
* Input type: bool*
|
||||
* Input type: GhosttyString*
|
||||
*/
|
||||
GHOSTTY_TERMINAL_OPT_KITTY_IMAGE_MEDIUM_TEMP_FILE = 17,
|
||||
|
||||
@@ -1139,12 +1140,13 @@ typedef enum GHOSTTY_ENUM_TYPED {
|
||||
GHOSTTY_TERMINAL_DATA_KITTY_IMAGE_MEDIUM_FILE = 27,
|
||||
|
||||
/**
|
||||
* Whether the temporary file medium is enabled for Kitty image loading
|
||||
* on the active screen.
|
||||
* The directory allowed for Kitty image loading via the temporary file
|
||||
* medium on the active screen. The string is empty when the medium is
|
||||
* disabled.
|
||||
*
|
||||
* Returns GHOSTTY_NO_VALUE when Kitty graphics are disabled at build time.
|
||||
*
|
||||
* Output type: bool *
|
||||
* Output type: GhosttyString *
|
||||
*/
|
||||
GHOSTTY_TERMINAL_DATA_KITTY_IMAGE_MEDIUM_TEMP_FILE = 28,
|
||||
|
||||
|
||||
@@ -236,12 +236,12 @@ fn startPosix(self: *Command, arena: Allocator) !void {
|
||||
var path_expanded_buf: [std.fs.max_path_bytes]u8 = undefined;
|
||||
const PATH = global.environ().getPosix("PATH") orelse "/usr/local/bin:/bin/:/usr/bin";
|
||||
var it = std.mem.tokenizeScalar(u8, PATH, ':');
|
||||
var err: posix.system.E = undefined;
|
||||
var err: posix.system.E = .NOENT;
|
||||
var seen_eacces = false;
|
||||
|
||||
while (it.next()) |search_path| {
|
||||
const path_len = search_path.len + file_slice.len + 1;
|
||||
if (path_expanded_buf.len < path_len + 1) return error.NameTooLong;
|
||||
if (path_expanded_buf.len < path_len + 1) break :execve .NAMETOOLONG;
|
||||
@memcpy(path_expanded_buf[0..search_path.len], search_path);
|
||||
path_expanded_buf[search_path.len] = '/';
|
||||
@memcpy(path_expanded_buf[search_path.len + 1 ..][0..file_slice.len], file_slice);
|
||||
|
||||
@@ -1593,7 +1593,10 @@ pub const Surface = extern struct {
|
||||
pub fn defaultTermioEnv(self: *Self) !std.process.Environ.Map {
|
||||
const app = Application.default();
|
||||
const alloc = app.allocator();
|
||||
var env = try global.environMap();
|
||||
var env = if (internal_os.isFlatpak())
|
||||
std.process.Environ.Map.init(alloc)
|
||||
else
|
||||
try global.environMap();
|
||||
errdefer env.deinit();
|
||||
|
||||
if (app.savedLanguage()) |language| {
|
||||
|
||||
@@ -34,6 +34,7 @@ pub const SVG = struct {
|
||||
const offset = try compat_reader.readerInt(&reader, u32, .big);
|
||||
|
||||
// Seek to the offset to get our document list
|
||||
if (offset > data.len) return error.EndOfStream;
|
||||
reader.seek = offset;
|
||||
|
||||
// Get our document records along with the start/end glyph range.
|
||||
@@ -108,3 +109,13 @@ test "SVG" {
|
||||
try testing.expectEqual(11482, svg.end_glyph_id);
|
||||
try testing.expect(svg.hasGlyph(11482));
|
||||
}
|
||||
|
||||
test "SVG document list offset past end" {
|
||||
const testing = std.testing;
|
||||
const table = [_]u8{
|
||||
0, 0, // Version
|
||||
0, 0, 0, 7, // Document list offset, one byte past end
|
||||
};
|
||||
|
||||
try testing.expectError(error.EndOfStream, SVG.init(&table));
|
||||
}
|
||||
|
||||
@@ -4,27 +4,53 @@ const builtin = @import("builtin");
|
||||
const std = @import("std");
|
||||
const global = @import("../../global.zig");
|
||||
|
||||
pub const ReadToEndAllocError = error{ FileTooBig, BytesReadMismatch } ||
|
||||
std.Io.File.StatError ||
|
||||
pub const ReadToEndAllocError = error{FileTooBig} ||
|
||||
std.Io.File.ReadStreamingError ||
|
||||
std.mem.Allocator.Error;
|
||||
|
||||
/// This is a much simpler `readToEndAlloc` that just pre-allocates the memory
|
||||
/// for the file ahead of time, and errors out if the size is larger than
|
||||
/// `max_bytes`.
|
||||
/// 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 {
|
||||
const size = (try file.stat(global.io())).size;
|
||||
if (size > max_bytes) {
|
||||
return error.FileTooBig;
|
||||
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]);
|
||||
}
|
||||
|
||||
const buf = try alloc.alloc(u8, size);
|
||||
errdefer alloc.free(buf);
|
||||
if (try file.readStreaming(global.io(), &.{buf}) != size) {
|
||||
return error.BytesReadMismatch;
|
||||
}
|
||||
|
||||
return buf;
|
||||
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),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3816,18 +3816,23 @@ pub fn resize(
|
||||
// we just go back to the primary screen. Not great, but best
|
||||
// we can do.
|
||||
tw.check(.alternate_screen_init) catch break :alt;
|
||||
const replacement = self.screens.getInit(alt.io, alloc, .alternate, .{
|
||||
.cols = opts.cols,
|
||||
.rows = opts.rows,
|
||||
.max_scrollback = 0,
|
||||
.kitty_image_storage_limit = if (comptime build_options.kitty_graphics)
|
||||
primary.kitty_images.total_limit
|
||||
else
|
||||
0,
|
||||
.kitty_image_loading_limits = if (comptime build_options.kitty_graphics)
|
||||
primary.kitty_images.image_limits
|
||||
else {},
|
||||
}) catch |init_err| {
|
||||
const replacement = self.screens.getInit(
|
||||
self.io(),
|
||||
alloc,
|
||||
.alternate,
|
||||
.{
|
||||
.cols = opts.cols,
|
||||
.rows = opts.rows,
|
||||
.max_scrollback = 0,
|
||||
.kitty_image_storage_limit = if (comptime build_options.kitty_graphics)
|
||||
primary.kitty_images.total_limit
|
||||
else
|
||||
0,
|
||||
.kitty_image_loading_limits = if (comptime build_options.kitty_graphics)
|
||||
primary.kitty_images.image_limits
|
||||
else {},
|
||||
},
|
||||
) catch |init_err| {
|
||||
log.warn(
|
||||
"alternate screen replacement failed, falling back to primary err={}",
|
||||
.{init_err},
|
||||
|
||||
@@ -150,7 +150,7 @@ pub const LoadingImage = struct {
|
||||
const path = switch (t.medium) {
|
||||
.direct => unreachable, // handled above
|
||||
.file, .temporary_file => path: {
|
||||
const len = std.Io.Dir.realPathFileAbsolute(
|
||||
const len = std.Io.Dir.cwd().realPathFile(
|
||||
io,
|
||||
cmd.data,
|
||||
&abs_buf,
|
||||
@@ -364,7 +364,7 @@ pub const LoadingImage = struct {
|
||||
// The temporary dir is sometimes a symlink. On macOS for
|
||||
// example /tmp is /private/var/...
|
||||
var buf: [std.fs.max_path_bytes]u8 = undefined;
|
||||
const real_dir = buf[0 .. std.Io.Dir.realPathFileAbsolute(
|
||||
const real_dir = buf[0 .. std.Io.Dir.cwd().realPathFile(
|
||||
io,
|
||||
dir,
|
||||
&buf,
|
||||
@@ -912,6 +912,46 @@ test "image load: rgb, not compressed, regular file" {
|
||||
try tmp_dir.dir.access(testing.io, path, .{});
|
||||
}
|
||||
|
||||
test "image load: rgb, not compressed, relative regular file" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const io = testing.io;
|
||||
|
||||
var tmp_dir = testing.tmpDir(.{});
|
||||
defer tmp_dir.cleanup();
|
||||
const data = @embedFile("testdata/image-rgb-none-20x15-2147483647-raw.data");
|
||||
try tmp_dir.dir.writeFile(testing.io, .{
|
||||
.sub_path = "image.data",
|
||||
.data = data,
|
||||
});
|
||||
|
||||
var cmd: command.Command = .{
|
||||
.control = .{ .transmit = .{
|
||||
.format = .rgb,
|
||||
.medium = .file,
|
||||
.compression = .none,
|
||||
.width = 20,
|
||||
.height = 15,
|
||||
.image_id = 31,
|
||||
} },
|
||||
.data = try std.fmt.allocPrint(
|
||||
alloc,
|
||||
".zig-cache/tmp/{s}/image.data",
|
||||
.{tmp_dir.sub_path},
|
||||
),
|
||||
};
|
||||
defer cmd.deinit(alloc);
|
||||
var loading = try LoadingImage.init(io, alloc, &cmd, .{
|
||||
.file = true,
|
||||
.temporary_file = .disabled,
|
||||
.shared_memory = false,
|
||||
});
|
||||
defer loading.deinit(alloc);
|
||||
var img = try loading.complete(alloc);
|
||||
defer img.deinit(alloc);
|
||||
try testing.expect(img.compression == .none);
|
||||
}
|
||||
|
||||
test "image load: png, not compressed, regular file" {
|
||||
if (sys.decode_png == null) return error.SkipZigTest;
|
||||
|
||||
|
||||
@@ -1196,7 +1196,11 @@ const Subprocess = struct {
|
||||
// The gist is that it lets us detect when children
|
||||
// are still alive without blocking so that we can
|
||||
// kill them again.
|
||||
const res_pid = posix.system.waitpid(pid, null, std.c.W.NOHANG);
|
||||
const res_pid = while (true) {
|
||||
const rc = posix.system.waitpid(pid, null, std.c.W.NOHANG);
|
||||
if (posix.errno(rc) == .INTR) continue;
|
||||
break rc;
|
||||
};
|
||||
log.debug("waitpid result={}", .{res_pid});
|
||||
if (res_pid != 0) break;
|
||||
try std.Io.sleep(global.io(), .fromMilliseconds(10), .awake);
|
||||
|
||||
@@ -658,9 +658,11 @@ fn processOutputLocked(self: *Termio, buf: []const u8) void {
|
||||
// HEAVY read load, we don't want to send a ton of these so we
|
||||
// use a timer under the covers
|
||||
const now = std.Io.Timestamp.now(global.io(), .awake);
|
||||
if (self.last_cursor_reset) |last| cursor_reset: {
|
||||
if (last.durationTo(now).toMilliseconds() <= 500) {
|
||||
break :cursor_reset;
|
||||
cursor_reset: {
|
||||
if (self.last_cursor_reset) |last| {
|
||||
if (last.durationTo(now).toMilliseconds() <= 500) {
|
||||
break :cursor_reset;
|
||||
}
|
||||
}
|
||||
|
||||
self.last_cursor_reset = now;
|
||||
|
||||
Reference in New Issue
Block a user