Zig 0.15: zig build GTK exe

pull/9004/head
Mitchell Hashimoto 2025-10-02 14:41:08 -07:00
parent cb295b84a0
commit d59d754e29
No known key found for this signature in database
GPG Key ID: 523D5DC389D273BC
43 changed files with 321 additions and 256 deletions

View File

@ -305,19 +305,19 @@ const DerivedConfig = struct {
// Build all of our links // Build all of our links
const links = links: { const links = links: {
var links = std.ArrayList(Link).init(alloc); var links: std.ArrayList(Link) = .empty;
defer links.deinit(); defer links.deinit(alloc);
for (config.link.links.items) |link| { for (config.link.links.items) |link| {
var regex = try link.oniRegex(); var regex = try link.oniRegex();
errdefer regex.deinit(); errdefer regex.deinit();
try links.append(.{ try links.append(alloc, .{
.regex = regex, .regex = regex,
.action = link.action, .action = link.action,
.highlight = link.highlight, .highlight = link.highlight,
}); });
} }
break :links try links.toOwnedSlice(); break :links try links.toOwnedSlice(alloc);
}; };
errdefer { errdefer {
for (links) |*link| link.regex.deinit(); for (links) |*link| link.regex.deinit();
@ -2493,7 +2493,7 @@ fn maybeHandleBinding(
self.keyboard.bindings = null; self.keyboard.bindings = null;
// Attempt to perform the action // Attempt to perform the action
log.debug("key event binding flags={} action={}", .{ log.debug("key event binding flags={} action={f}", .{
leaf.flags, leaf.flags,
action, action,
}); });
@ -5119,7 +5119,9 @@ fn writeScreenFile(
defer file.close(); defer file.close();
// Screen.dumpString writes byte-by-byte, so buffer it // Screen.dumpString writes byte-by-byte, so buffer it
var buf_writer = std.io.bufferedWriter(file.writer()); var buf: [4096]u8 = undefined;
var file_writer = file.writer(&buf);
var buf_writer = &file_writer.interface;
// Write the scrollback contents. This requires a lock. // Write the scrollback contents. This requires a lock.
{ {
@ -5169,7 +5171,7 @@ fn writeScreenFile(
const br = sel.bottomRight(&self.io.terminal.screen); const br = sel.bottomRight(&self.io.terminal.screen);
try self.io.terminal.screen.dumpString( try self.io.terminal.screen.dumpString(
buf_writer.writer(), buf_writer,
.{ .{
.tl = tl, .tl = tl,
.br = br, .br = br,

View File

@ -578,7 +578,7 @@ pub const SetTitle = struct {
value: @This(), value: @This(),
comptime _: []const u8, comptime _: []const u8,
_: std.fmt.FormatOptions, _: std.fmt.FormatOptions,
writer: anytype, writer: *std.Io.Writer,
) !void { ) !void {
try writer.print("{s}{{ {s} }}", .{ @typeName(@This()), value.title }); try writer.print("{s}{{ {s} }}", .{ @typeName(@This()), value.title });
} }
@ -602,7 +602,7 @@ pub const Pwd = struct {
value: @This(), value: @This(),
comptime _: []const u8, comptime _: []const u8,
_: std.fmt.FormatOptions, _: std.fmt.FormatOptions,
writer: anytype, writer: *std.Io.Writer,
) !void { ) !void {
try writer.print("{s}{{ {s} }}", .{ @typeName(@This()), value.pwd }); try writer.print("{s}{{ {s} }}", .{ @typeName(@This()), value.pwd });
} }
@ -630,7 +630,7 @@ pub const DesktopNotification = struct {
value: @This(), value: @This(),
comptime _: []const u8, comptime _: []const u8,
_: std.fmt.FormatOptions, _: std.fmt.FormatOptions,
writer: anytype, writer: *std.Io.Writer,
) !void { ) !void {
try writer.print("{s}{{ title: {s}, body: {s} }}", .{ try writer.print("{s}{{ title: {s}, body: {s} }}", .{
@typeName(@This()), @typeName(@This()),

View File

@ -27,7 +27,7 @@ pub fn getRuntimeVersion() std.SemanticVersion {
} }
pub fn logVersion() void { pub fn logVersion() void {
log.info("libadwaita version build={} runtime={}", .{ log.info("libadwaita version build={f} runtime={f}", .{
comptime_version, comptime_version,
getRuntimeVersion(), getRuntimeVersion(),
}); });

View File

@ -166,7 +166,7 @@ pub fn main() !void {
/// Generate the icon resources. This works by looking up all the icons /// Generate the icon resources. This works by looking up all the icons
/// specified by `icon_sizes` in `images/icons/`. They are asserted to exist /// specified by `icon_sizes` in `images/icons/`. They are asserted to exist
/// by trying to access the file. /// by trying to access the file.
fn genIcons(writer: anytype) !void { fn genIcons(writer: *std.Io.Writer) !void {
try writer.print( try writer.print(
\\ <gresource prefix="{s}/icons"> \\ <gresource prefix="{s}/icons">
\\ \\
@ -208,7 +208,7 @@ fn genIcons(writer: anytype) !void {
} }
/// Generate the resources at the root prefix. /// Generate the resources at the root prefix.
fn genRoot(writer: anytype) !void { fn genRoot(writer: *std.Io.Writer) !void {
try writer.print( try writer.print(
\\ <gresource prefix="{s}"> \\ <gresource prefix="{s}">
\\ \\
@ -240,7 +240,7 @@ fn genRoot(writer: anytype) !void {
/// assuming these will be /// assuming these will be
fn genUi( fn genUi(
alloc: Allocator, alloc: Allocator,
writer: anytype, writer: *std.Io.Writer,
files: *const std.ArrayListUnmanaged([]const u8), files: *const std.ArrayListUnmanaged([]const u8),
) !void { ) !void {
try writer.print( try writer.print(

View File

@ -50,7 +50,7 @@ pub fn init(
) orelse ""; ) orelse "";
if (!std.mem.eql(u8, original, current)) break :transient current; if (!std.mem.eql(u8, original, current)) break :transient current;
alloc.free(current); alloc.free(current);
std.time.sleep(25 * std.time.ns_per_ms); std.Thread.sleep(25 * std.time.ns_per_ms);
}; };
errdefer alloc.free(transient); errdefer alloc.free(transient);
log.info("transient scope created cgroup={s}", .{transient}); log.info("transient scope created cgroup={s}", .{transient});
@ -101,21 +101,21 @@ fn enableControllers(alloc: Allocator, cgroup: []const u8) !void {
defer alloc.free(raw); defer alloc.free(raw);
// Build our string builder for enabling all controllers // Build our string builder for enabling all controllers
var builder = std.ArrayList(u8).init(alloc); var builder: std.Io.Writer.Allocating = .init(alloc);
defer builder.deinit(); defer builder.deinit();
// Controllers are space-separated // Controllers are space-separated
var it = std.mem.splitScalar(u8, raw, ' '); var it = std.mem.splitScalar(u8, raw, ' ');
while (it.next()) |controller| { while (it.next()) |controller| {
try builder.append('+'); try builder.writer.writeByte('+');
try builder.appendSlice(controller); try builder.writer.writeAll(controller);
if (it.rest().len > 0) try builder.append(' '); if (it.rest().len > 0) try builder.writer.writeByte(' ');
} }
// Enable them all // Enable them all
try internal_os.cgroup.configureControllers( try internal_os.cgroup.configureControllers(
cgroup, cgroup,
builder.items, builder.written(),
); );
} }

View File

@ -1044,7 +1044,9 @@ pub const Application = extern struct {
defer file.close(); defer file.close();
log.info("loading gtk-custom-css path={s}", .{path}); log.info("loading gtk-custom-css path={s}", .{path});
const contents = try file.reader().readAllAlloc( var buf: [4096]u8 = undefined;
var reader = file.reader(&buf);
const contents = try reader.interface.readAlloc(
alloc, alloc,
5 * 1024 * 1024, // 5MB, 5 * 1024 * 1024, // 5MB,
); );
@ -1115,8 +1117,8 @@ pub const Application = extern struct {
// This should really never, never happen. Its not critical enough // This should really never, never happen. Its not critical enough
// to actually crash, but this is a bug somewhere. An accelerator // to actually crash, but this is a bug somewhere. An accelerator
// for a trigger can't possibly be more than 1024 bytes. // for a trigger can't possibly be more than 1024 bytes.
error.NoSpaceLeft => { error.WriteFailed => {
log.warn("accelerator somehow longer than 1024 bytes: {}", .{trigger}); log.warn("accelerator somehow longer than 1024 bytes: {f}", .{trigger});
return; return;
}, },
}; };

View File

@ -485,10 +485,11 @@ const Command = extern struct {
const command = priv.command orelse return null; const command = priv.command orelse return null;
priv.action_key = std.fmt.allocPrintZ( priv.action_key = std.fmt.allocPrintSentinel(
priv.arena.allocator(), priv.arena.allocator(),
"{}", "{f}",
.{command.action}, .{command.action},
0,
) catch null; ) catch null;
return priv.action_key; return priv.action_key;

View File

@ -188,9 +188,9 @@ pub const GlobalShortcuts = extern struct {
// If there isn't space to translate the trigger, then our // If there isn't space to translate the trigger, then our
// buffer might be too small (but 1024 is insane!). In any case // buffer might be too small (but 1024 is insane!). In any case
// we don't want to stop registering globals. // we don't want to stop registering globals.
error.NoSpaceLeft => { error.WriteFailed => {
log.warn( log.warn(
"buffer too small to translate trigger, ignoring={}", "buffer too small to translate trigger, ignoring={f}",
.{entry.key_ptr.*}, .{entry.key_ptr.*},
); );
continue; continue;
@ -257,7 +257,7 @@ pub const GlobalShortcuts = extern struct {
const trigger = entry.key_ptr.*.ptr; const trigger = entry.key_ptr.*.ptr;
const action = std.fmt.bufPrintZ( const action = std.fmt.bufPrintZ(
&action_buf, &action_buf,
"{}", "{f}",
.{entry.value_ptr.*}, .{entry.value_ptr.*},
) catch continue; ) catch continue;

View File

@ -268,7 +268,7 @@ pub const SplitTree = extern struct {
); );
defer new_tree.deinit(); defer new_tree.deinit();
log.debug( log.debug(
"new split at={} direction={} old_tree={} new_tree={}", "new split at={} direction={} old_tree={f} new_tree={f}",
.{ handle, direction, old_tree, &new_tree }, .{ handle, direction, old_tree, &new_tree },
); );

View File

@ -2117,13 +2117,11 @@ pub const Surface = extern struct {
const alloc = Application.default().allocator(); const alloc = Application.default().allocator();
if (ext.gValueHolds(value, gdk.FileList.getGObjectType())) { if (ext.gValueHolds(value, gdk.FileList.getGObjectType())) {
var data = std.ArrayList(u8).init(alloc); var stream: std.Io.Writer.Allocating = .init(alloc);
defer data.deinit(); defer stream.deinit();
var shell_escape_writer: internal_os.ShellEscapeWriter(std.ArrayList(u8).Writer) = .{ var shell_escape_writer: internal_os.ShellEscapeWriter = .init(&stream.writer);
.child_writer = data.writer(), const writer = &shell_escape_writer.writer;
};
const writer = shell_escape_writer.writer();
const list: ?*glib.SList = list: { const list: ?*glib.SList = list: {
const unboxed = value.getBoxed() orelse return 0; const unboxed = value.getBoxed() orelse return 0;
@ -2151,7 +2149,7 @@ pub const Surface = extern struct {
} }
} }
const string = data.toOwnedSliceSentinel(0) catch |err| { const string = stream.toOwnedSliceSentinel(0) catch |err| {
log.err("unable to convert to a slice: {}", .{err}); log.err("unable to convert to a slice: {}", .{err});
return 0; return 0;
}; };
@ -2164,13 +2162,11 @@ pub const Surface = extern struct {
const object = value.getObject() orelse return 0; const object = value.getObject() orelse return 0;
const file = gobject.ext.cast(gio.File, object) orelse return 0; const file = gobject.ext.cast(gio.File, object) orelse return 0;
const path = file.getPath() orelse return 0; const path = file.getPath() orelse return 0;
var data = std.ArrayList(u8).init(alloc); var stream: std.Io.Writer.Allocating = .init(alloc);
defer data.deinit(); defer stream.deinit();
var shell_escape_writer: internal_os.ShellEscapeWriter(std.ArrayList(u8).Writer) = .{ var shell_escape_writer: internal_os.ShellEscapeWriter = .init(&stream.writer);
.child_writer = data.writer(), const writer = &shell_escape_writer.writer;
};
const writer = shell_escape_writer.writer();
writer.writeAll(std.mem.span(path)) catch |err| { writer.writeAll(std.mem.span(path)) catch |err| {
log.err("unable to write path to buffer: {}", .{err}); log.err("unable to write path to buffer: {}", .{err});
return 0; return 0;
@ -2180,7 +2176,7 @@ pub const Surface = extern struct {
return 0; return 0;
}; };
const string = data.toOwnedSliceSentinel(0) catch |err| { const string = stream.toOwnedSliceSentinel(0) catch |err| {
log.err("unable to convert to a slice: {}", .{err}); log.err("unable to convert to a slice: {}", .{err});
return 0; return 0;
}; };

View File

@ -405,22 +405,21 @@ pub const Tab = extern struct {
}; };
// Use an allocator to build up our string as we write it. // Use an allocator to build up our string as we write it.
var buf: std.ArrayList(u8) = .init(Application.default().allocator()); var buf: std.Io.Writer.Allocating = .init(Application.default().allocator());
defer buf.deinit(); defer buf.deinit();
const writer = buf.writer();
// If our bell is ringing, then we prefix the bell icon to the title. // If our bell is ringing, then we prefix the bell icon to the title.
if (bell_ringing and config.@"bell-features".title) { if (bell_ringing and config.@"bell-features".title) {
writer.writeAll("🔔 ") catch {}; buf.writer.writeAll("🔔 ") catch {};
} }
// If we're zoomed, prefix with the magnifying glass emoji. // If we're zoomed, prefix with the magnifying glass emoji.
if (zoomed) { if (zoomed) {
writer.writeAll("🔍 ") catch {}; buf.writer.writeAll("🔍 ") catch {};
} }
writer.writeAll(plain) catch return glib.ext.dupeZ(u8, plain); buf.writer.writeAll(plain) catch return glib.ext.dupeZ(u8, plain);
return glib.ext.dupeZ(u8, buf.items); return glib.ext.dupeZ(u8, buf.written());
} }
const C = Common(Self, Private); const C = Common(Self, Private);

View File

@ -26,7 +26,7 @@ pub fn getRuntimeVersion() std.SemanticVersion {
} }
pub fn logVersion() void { pub fn logVersion() void {
log.info("GTK version build={} runtime={}", .{ log.info("GTK version build={f} runtime={f}", .{
comptime_version, comptime_version,
getRuntimeVersion(), getRuntimeVersion(),
}); });

View File

@ -12,9 +12,8 @@ const winproto = @import("winproto.zig");
pub fn accelFromTrigger( pub fn accelFromTrigger(
buf: []u8, buf: []u8,
trigger: input.Binding.Trigger, trigger: input.Binding.Trigger,
) error{NoSpaceLeft}!?[:0]const u8 { ) error{WriteFailed}!?[:0]const u8 {
var buf_stream = std.io.fixedBufferStream(buf); var writer: std.Io.Writer = .fixed(buf);
const writer = buf_stream.writer();
// Modifiers // Modifiers
if (trigger.mods.shift) try writer.writeAll("<Shift>"); if (trigger.mods.shift) try writer.writeAll("<Shift>");
@ -23,11 +22,11 @@ pub fn accelFromTrigger(
if (trigger.mods.super) try writer.writeAll("<Super>"); if (trigger.mods.super) try writer.writeAll("<Super>");
// Write our key // Write our key
if (!try writeTriggerKey(writer, trigger)) return null; if (!try writeTriggerKey(&writer, trigger)) return null;
// We need to make the string null terminated. // We need to make the string null terminated.
try writer.writeByte(0); try writer.writeByte(0);
const slice = buf_stream.getWritten(); const slice = writer.buffered();
return slice[0 .. slice.len - 1 :0]; return slice[0 .. slice.len - 1 :0];
} }
@ -36,9 +35,8 @@ pub fn accelFromTrigger(
pub fn xdgShortcutFromTrigger( pub fn xdgShortcutFromTrigger(
buf: []u8, buf: []u8,
trigger: input.Binding.Trigger, trigger: input.Binding.Trigger,
) error{NoSpaceLeft}!?[:0]const u8 { ) error{WriteFailed}!?[:0]const u8 {
var buf_stream = std.io.fixedBufferStream(buf); var writer: std.Io.Writer = .fixed(buf);
const writer = buf_stream.writer();
// Modifiers // Modifiers
if (trigger.mods.shift) try writer.writeAll("SHIFT+"); if (trigger.mods.shift) try writer.writeAll("SHIFT+");
@ -52,15 +50,18 @@ pub fn xdgShortcutFromTrigger(
// to *X11's* keysyms (which I assume is a subset of libxkbcommon's). // to *X11's* keysyms (which I assume is a subset of libxkbcommon's).
// I haven't been able to any evidence to back up that assumption but // I haven't been able to any evidence to back up that assumption but
// this works for now // this works for now
if (!try writeTriggerKey(writer, trigger)) return null; if (!try writeTriggerKey(&writer, trigger)) return null;
// We need to make the string null terminated. // We need to make the string null terminated.
try writer.writeByte(0); try writer.writeByte(0);
const slice = buf_stream.getWritten(); const slice = writer.buffered();
return slice[0 .. slice.len - 1 :0]; return slice[0 .. slice.len - 1 :0];
} }
fn writeTriggerKey(writer: anytype, trigger: input.Binding.Trigger) error{NoSpaceLeft}!bool { fn writeTriggerKey(
writer: *std.Io.Writer,
trigger: input.Binding.Trigger,
) error{WriteFailed}!bool {
switch (trigger.key) { switch (trigger.key) {
.physical => |k| { .physical => |k| {
const keyval = keyvalFromKey(k) orelse return false; const keyval = keyvalFromKey(k) orelse return false;

View File

@ -21,6 +21,8 @@ pub fn init(b: *std.Build, cfg: *const Config, deps: *const SharedDeps) !Ghostty
.omit_frame_pointer = cfg.strip, .omit_frame_pointer = cfg.strip,
.unwind_tables = if (cfg.strip) .none else .sync, .unwind_tables = if (cfg.strip) .none else .sync,
}), }),
// Crashes on x86_64 self-hosted on 0.15.1
.use_llvm = true,
}); });
const install_step = b.addInstallArtifact(exe, .{}); const install_step = b.addInstallArtifact(exe, .{});

View File

@ -2,12 +2,15 @@ const std = @import("std");
const gen = @import("mdgen.zig"); const gen = @import("mdgen.zig");
pub fn main() !void { pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
const alloc = gpa.allocator(); const alloc = gpa.allocator();
const writer = std.io.getStdOut().writer(); var buffer: [1024]u8 = undefined;
var stdout_writer = std.fs.File.stdout().writer(&buffer);
const writer = &stdout_writer.interface;
try gen.substitute(alloc, @embedFile("ghostty_1_header.md"), writer); try gen.substitute(alloc, @embedFile("ghostty_1_header.md"), writer);
try gen.genActions(writer); try gen.genActions(writer);
try gen.genConfig(writer, true); try gen.genConfig(writer, true);
try gen.substitute(alloc, @embedFile("ghostty_1_footer.md"), writer); try gen.substitute(alloc, @embedFile("ghostty_1_footer.md"), writer);
try writer.flush();
} }

View File

@ -2,12 +2,15 @@ const std = @import("std");
const gen = @import("mdgen.zig"); const gen = @import("mdgen.zig");
pub fn main() !void { pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
const alloc = gpa.allocator(); const alloc = gpa.allocator();
const output = std.io.getStdOut().writer(); var buffer: [1024]u8 = undefined;
try gen.substitute(alloc, @embedFile("ghostty_5_header.md"), output); var stdout_writer = std.fs.File.stdout().writer(&buffer);
try gen.genConfig(output, false); const writer = &stdout_writer.interface;
try gen.genKeybindActions(output); try gen.substitute(alloc, @embedFile("ghostty_5_header.md"), writer);
try gen.substitute(alloc, @embedFile("ghostty_5_footer.md"), output); try gen.genConfig(writer, false);
try gen.genKeybindActions(writer);
try gen.substitute(alloc, @embedFile("ghostty_5_footer.md"), writer);
try writer.flush();
} }

View File

@ -5,7 +5,7 @@ const Config = @import("../../config/Config.zig");
const Action = @import("../../cli/ghostty.zig").Action; const Action = @import("../../cli/ghostty.zig").Action;
const KeybindAction = @import("../../input/Binding.zig").Action; const KeybindAction = @import("../../input/Binding.zig").Action;
pub fn substitute(alloc: std.mem.Allocator, input: []const u8, writer: anytype) !void { pub fn substitute(alloc: std.mem.Allocator, input: []const u8, writer: *std.Io.Writer) !void {
const output = try alloc.alloc(u8, std.mem.replacementSize( const output = try alloc.alloc(u8, std.mem.replacementSize(
u8, u8,
input, input,
@ -18,7 +18,7 @@ pub fn substitute(alloc: std.mem.Allocator, input: []const u8, writer: anytype)
try writer.writeAll(output); try writer.writeAll(output);
} }
pub fn genConfig(writer: anytype, cli: bool) !void { pub fn genConfig(writer: *std.Io.Writer, cli: bool) !void {
try writer.writeAll( try writer.writeAll(
\\ \\
\\# CONFIGURATION OPTIONS \\# CONFIGURATION OPTIONS
@ -48,7 +48,7 @@ pub fn genConfig(writer: anytype, cli: bool) !void {
} }
} }
pub fn genActions(writer: anytype) !void { pub fn genActions(writer: *std.Io.Writer) !void {
try writer.writeAll( try writer.writeAll(
\\ \\
\\# COMMAND LINE ACTIONS \\# COMMAND LINE ACTIONS
@ -83,7 +83,7 @@ pub fn genActions(writer: anytype) !void {
} }
} }
pub fn genKeybindActions(writer: anytype) !void { pub fn genKeybindActions(writer: *std.Io.Writer) !void {
try writer.writeAll( try writer.writeAll(
\\ \\
\\# KEYBIND ACTIONS \\# KEYBIND ACTIONS

View File

@ -19,18 +19,18 @@ pub const completions = comptimeGenerateBashCompletions();
fn comptimeGenerateBashCompletions() []const u8 { fn comptimeGenerateBashCompletions() []const u8 {
comptime { comptime {
@setEvalBranchQuota(50000); @setEvalBranchQuota(50000);
var counter = std.io.countingWriter(std.io.null_writer); var counter: std.Io.Writer.Discarding = .init(&.{});
try writeBashCompletions(&counter.writer()); try writeBashCompletions(&counter.writer);
var buf: [counter.bytes_written]u8 = undefined; var buf: [counter.count]u8 = undefined;
var stream = std.io.fixedBufferStream(&buf); var writer: std.Io.Writer = .fixed(&buf);
try writeBashCompletions(stream.writer()); try writeBashCompletions(&writer);
const final = buf; const final = buf;
return final[0..stream.getWritten().len]; return final[0..writer.end];
} }
} }
fn writeBashCompletions(writer: anytype) !void { fn writeBashCompletions(writer: *std.Io.Writer) !void {
const pad1 = " "; const pad1 = " ";
const pad2 = pad1 ++ pad1; const pad2 = pad1 ++ pad1;
const pad3 = pad2 ++ pad1; const pad3 = pad2 ++ pad1;

View File

@ -11,18 +11,18 @@ pub const completions = comptimeGenerateCompletions();
fn comptimeGenerateCompletions() []const u8 { fn comptimeGenerateCompletions() []const u8 {
comptime { comptime {
@setEvalBranchQuota(50000); @setEvalBranchQuota(50000);
var counter = std.io.countingWriter(std.io.null_writer); var counter: std.Io.Writer.Discarding = .init(&.{});
try writeCompletions(&counter.writer()); try writeCompletions(&counter.writer);
var buf: [counter.bytes_written]u8 = undefined; var buf: [counter.count]u8 = undefined;
var stream = std.io.fixedBufferStream(&buf); var writer: std.Io.Writer = .fixed(&buf);
try writeCompletions(stream.writer()); try writeCompletions(&writer);
const final = buf; const final = buf;
return final[0..stream.getWritten().len]; return final[0..writer.end];
} }
} }
fn writeCompletions(writer: anytype) !void { fn writeCompletions(writer: *std.Io.Writer) !void {
{ {
try writer.writeAll("set -l commands \""); try writer.writeAll("set -l commands \"");
var count: usize = 0; var count: usize = 0;

View File

@ -72,7 +72,7 @@ fn comptimeGenSyntax() []const u8 {
} }
/// Writes the syntax file to the given writer. /// Writes the syntax file to the given writer.
fn writeSyntax(writer: anytype) !void { fn writeSyntax(writer: *std.Io.Writer) !void {
try writer.writeAll( try writer.writeAll(
\\" Vim syntax file \\" Vim syntax file
\\" Language: Ghostty config file \\" Language: Ghostty config file

View File

@ -12,18 +12,18 @@ const equals_required = "=-:::";
fn comptimeGenerateZshCompletions() []const u8 { fn comptimeGenerateZshCompletions() []const u8 {
comptime { comptime {
@setEvalBranchQuota(50000); @setEvalBranchQuota(50000);
var counter = std.io.countingWriter(std.io.null_writer); var counter: std.Io.Writer.Discarding = .init(&.{});
try writeZshCompletions(&counter.writer()); try writeZshCompletions(&counter.writer);
var buf: [counter.bytes_written]u8 = undefined; var buf: [counter.count]u8 = undefined;
var stream = std.io.fixedBufferStream(&buf); var writer: std.Io.Writer = .fixed(&buf);
try writeZshCompletions(stream.writer()); try writeZshCompletions(&writer);
const final = buf; const final = buf;
return final[0..stream.getWritten().len]; return final[0..writer.end];
} }
} }
fn writeZshCompletions(writer: anytype) !void { fn writeZshCompletions(writer: *std.Io.Writer) !void {
try writer.writeAll( try writer.writeAll(
\\#compdef ghostty \\#compdef ghostty
\\ \\

View File

@ -355,7 +355,7 @@ pub fn clear(self: *Atlas) void {
/// swapped because PPM expects RGB. This would be /// swapped because PPM expects RGB. This would be
/// easy enough to fix so next time someone needs /// easy enough to fix so next time someone needs
/// to debug a color atlas they should fix it. /// to debug a color atlas they should fix it.
pub fn dump(self: Atlas, writer: anytype) !void { pub fn dump(self: Atlas, writer: *std.Io.Writer) !void {
try writer.print( try writer.print(
\\P{c} \\P{c}
\\{d} {d} \\{d} {d}

View File

@ -106,7 +106,7 @@ fn FixedPoint(comptime T: type, int_bits: u64, frac_bits: u64) type {
self: Self, self: Self,
comptime fmt: []const u8, comptime fmt: []const u8,
options: std.fmt.FormatOptions, options: std.fmt.FormatOptions,
writer: anytype, writer: *std.Io.Writer,
) !void { ) !void {
_ = fmt; _ = fmt;
_ = options; _ = options;
@ -176,7 +176,7 @@ pub const SFNT = struct {
self: OffsetSubtable, self: OffsetSubtable,
comptime fmt: []const u8, comptime fmt: []const u8,
options: std.fmt.FormatOptions, options: std.fmt.FormatOptions,
writer: anytype, writer: *std.Io.Writer,
) !void { ) !void {
_ = fmt; _ = fmt;
_ = options; _ = options;
@ -210,7 +210,7 @@ pub const SFNT = struct {
self: TableRecord, self: TableRecord,
comptime fmt: []const u8, comptime fmt: []const u8,
options: std.fmt.FormatOptions, options: std.fmt.FormatOptions,
writer: anytype, writer: *std.Io.Writer,
) !void { ) !void {
_ = fmt; _ = fmt;
_ = options; _ = options;

View File

@ -201,7 +201,7 @@ pub const Feature = struct {
self: Feature, self: Feature,
comptime layout: []const u8, comptime layout: []const u8,
opts: std.fmt.FormatOptions, opts: std.fmt.FormatOptions,
writer: anytype, writer: *std.Io.Writer,
) !void { ) !void {
_ = layout; _ = layout;
_ = opts; _ = opts;
@ -262,7 +262,7 @@ pub const FeatureList = struct {
self: FeatureList, self: FeatureList,
comptime layout: []const u8, comptime layout: []const u8,
opts: std.fmt.FormatOptions, opts: std.fmt.FormatOptions,
writer: anytype, writer: *std.Io.Writer,
) !void { ) !void {
for (self.features.items, 0..) |feature, i| { for (self.features.items, 0..) |feature, i| {
try feature.format(layout, opts, writer); try feature.format(layout, opts, writer);

View File

@ -600,6 +600,7 @@ fn renderModesWindow(self: *Inspector) void {
const t = self.surface.renderer_state.terminal; const t = self.surface.renderer_state.terminal;
inline for (@typeInfo(terminal.Mode).@"enum".fields) |field| { inline for (@typeInfo(terminal.Mode).@"enum".fields) |field| {
@setEvalBranchQuota(6000);
const tag: terminal.modes.ModeTag = @bitCast(@as(terminal.modes.ModeTag.Backing, field.value)); const tag: terminal.modes.ModeTag = @bitCast(@as(terminal.modes.ModeTag.Backing, field.value));
cimgui.c.igTableNextRow(cimgui.c.ImGuiTableRowFlags_None, 0); cimgui.c.igTableNextRow(cimgui.c.ImGuiTableRowFlags_None, 0);

View File

@ -43,9 +43,9 @@ pub const VTEvent = struct {
) !VTEvent { ) !VTEvent {
var md = Metadata.init(alloc); var md = Metadata.init(alloc);
errdefer md.deinit(); errdefer md.deinit();
var buf = std.ArrayList(u8).init(alloc); var buf: std.Io.Writer.Allocating = .init(alloc);
defer buf.deinit(); defer buf.deinit();
try encodeAction(alloc, buf.writer(), &md, action); try encodeAction(alloc, &buf.writer, &md, action);
const str = try buf.toOwnedSliceSentinel(0); const str = try buf.toOwnedSliceSentinel(0);
errdefer alloc.free(str); errdefer alloc.free(str);
@ -115,7 +115,7 @@ pub const VTEvent = struct {
/// Encode a parser action as a string that we show in the logs. /// Encode a parser action as a string that we show in the logs.
fn encodeAction( fn encodeAction(
alloc: Allocator, alloc: Allocator,
writer: anytype, writer: *std.Io.Writer,
md: *Metadata, md: *Metadata,
action: terminal.Parser.Action, action: terminal.Parser.Action,
) !void { ) !void {
@ -125,16 +125,16 @@ pub const VTEvent = struct {
.csi_dispatch => |v| try encodeCSI(writer, v), .csi_dispatch => |v| try encodeCSI(writer, v),
.esc_dispatch => |v| try encodeEsc(writer, v), .esc_dispatch => |v| try encodeEsc(writer, v),
.osc_dispatch => |v| try encodeOSC(alloc, writer, md, v), .osc_dispatch => |v| try encodeOSC(alloc, writer, md, v),
else => try writer.print("{}", .{action}), else => try writer.print("{f}", .{action}),
} }
} }
fn encodePrint(writer: anytype, action: terminal.Parser.Action) !void { fn encodePrint(writer: *std.Io.Writer, action: terminal.Parser.Action) !void {
const ch = action.print; const ch = action.print;
try writer.print("'{u}' (U+{X})", .{ ch, ch }); try writer.print("'{u}' (U+{X})", .{ ch, ch });
} }
fn encodeExecute(writer: anytype, action: terminal.Parser.Action) !void { fn encodeExecute(writer: *std.Io.Writer, action: terminal.Parser.Action) !void {
const ch = action.execute; const ch = action.execute;
switch (ch) { switch (ch) {
0x00 => try writer.writeAll("NUL"), 0x00 => try writer.writeAll("NUL"),
@ -158,7 +158,7 @@ pub const VTEvent = struct {
try writer.print(" (0x{X})", .{ch}); try writer.print(" (0x{X})", .{ch});
} }
fn encodeCSI(writer: anytype, csi: terminal.Parser.Action.CSI) !void { fn encodeCSI(writer: *std.Io.Writer, csi: terminal.Parser.Action.CSI) !void {
for (csi.intermediates) |v| try writer.print("{c} ", .{v}); for (csi.intermediates) |v| try writer.print("{c} ", .{v});
for (csi.params, 0..) |v, i| { for (csi.params, 0..) |v, i| {
if (i != 0) try writer.writeByte(';'); if (i != 0) try writer.writeByte(';');
@ -168,14 +168,14 @@ pub const VTEvent = struct {
try writer.writeByte(csi.final); try writer.writeByte(csi.final);
} }
fn encodeEsc(writer: anytype, esc: terminal.Parser.Action.ESC) !void { fn encodeEsc(writer: *std.Io.Writer, esc: terminal.Parser.Action.ESC) !void {
for (esc.intermediates) |v| try writer.print("{c} ", .{v}); for (esc.intermediates) |v| try writer.print("{c} ", .{v});
try writer.writeByte(esc.final); try writer.writeByte(esc.final);
} }
fn encodeOSC( fn encodeOSC(
alloc: Allocator, alloc: Allocator,
writer: anytype, writer: *std.Io.Writer,
md: *Metadata, md: *Metadata,
osc: terminal.osc.Command, osc: terminal.osc.Command,
) !void { ) !void {
@ -265,10 +265,10 @@ pub const VTEvent = struct {
const s = if (field.type == void) const s = if (field.type == void)
try alloc.dupeZ(u8, tag_name) try alloc.dupeZ(u8, tag_name)
else else
try std.fmt.allocPrintZ(alloc, "{s}={}", .{ try std.fmt.allocPrintSentinel(alloc, "{s}={}", .{
tag_name, tag_name,
@field(value, field.name), @field(value, field.name),
}); }, 0);
try md.put(key, s); try md.put(key, s);
} }
@ -283,7 +283,7 @@ pub const VTEvent = struct {
else => switch (Value) { else => switch (Value) {
u8, u16 => try md.put( u8, u16 => try md.put(
key, key,
try std.fmt.allocPrintZ(alloc, "{}", .{value}), try std.fmt.allocPrintSentinel(alloc, "{}", .{value}, 0),
), ),
[]const u8, []const u8,

View File

@ -33,7 +33,9 @@ pub fn main() !void {
const action = action_ orelse return error.NoAction; const action = action_ orelse return error.NoAction;
// Our output always goes to stdout. // Our output always goes to stdout.
const writer = std.io.getStdOut().writer(); var buffer: [1024]u8 = undefined;
var stdout_writer = std.fs.File.stdout().writer(&buffer);
const writer = &stdout_writer.interface;
switch (action) { switch (action) {
.bash => try writer.writeAll(@import("extra/bash.zig").completions), .bash => try writer.writeAll(@import("extra/bash.zig").completions),
.fish => try writer.writeAll(@import("extra/fish.zig").completions), .fish => try writer.writeAll(@import("extra/fish.zig").completions),

View File

@ -35,7 +35,9 @@ pub fn main() !MainReturn {
// a global is because the C API needs to be able to access this state; // a global is because the C API needs to be able to access this state;
// no other Zig code should EVER access the global state. // no other Zig code should EVER access the global state.
state.init() catch |err| { state.init() catch |err| {
const stderr = std.io.getStdErr().writer(); var buffer: [1024]u8 = undefined;
var stderr_writer = std.fs.File.stderr().writer(&buffer);
const stderr = &stderr_writer.interface;
defer posix.exit(1); defer posix.exit(1);
const ErrSet = @TypeOf(err) || error{Unknown}; const ErrSet = @TypeOf(err) || error{Unknown};
switch (@as(ErrSet, @errorCast(err))) { switch (@as(ErrSet, @errorCast(err))) {
@ -54,6 +56,7 @@ pub fn main() !MainReturn {
else => try stderr.print("invalid CLI invocation err={}\n", .{err}), else => try stderr.print("invalid CLI invocation err={}\n", .{err}),
} }
try stderr.flush();
}; };
defer state.deinit(); defer state.deinit();
const alloc = state.alloc; const alloc = state.alloc;
@ -154,8 +157,12 @@ fn logFn(
.stderr => { .stderr => {
// Always try default to send to stderr // Always try default to send to stderr
const stderr = std.io.getStdErr().writer(); var buffer: [1024]u8 = undefined;
nosuspend stderr.print(level_txt ++ prefix ++ format ++ "\n", args) catch return; var stderr = std.fs.File.stderr().writer(&buffer);
const writer = &stderr.interface;
nosuspend writer.print(level_txt ++ prefix ++ format ++ "\n", args) catch return;
// TODO: Do we want to use flushless stderr in the future?
writer.flush() catch {};
}, },
} }
} }

View File

@ -19,8 +19,9 @@ pub fn current(alloc: Allocator, pid: std.os.linux.pid_t) !?[]const u8 {
defer file.close(); defer file.close();
// Read it all into memory -- we don't expect this file to ever be that large. // Read it all into memory -- we don't expect this file to ever be that large.
var buf_reader = std.io.bufferedReader(file.reader()); var reader_buf: [4096]u8 = undefined;
const contents = try buf_reader.reader().readAllAlloc( var reader = file.reader(&reader_buf);
const contents = try reader.interface.readAlloc(
alloc, alloc,
1 * 1024 * 1024, // 1MB 1 * 1024 * 1024, // 1MB
); );
@ -52,7 +53,11 @@ pub fn create(
); );
const file = try std.fs.cwd().openFile(pid_path, .{ .mode = .write_only }); const file = try std.fs.cwd().openFile(pid_path, .{ .mode = .write_only });
defer file.close(); defer file.close();
try file.writer().print("{}", .{pid});
var file_buf: [64]u8 = undefined;
var writer = file.writer(&file_buf);
try writer.interface.print("{}", .{pid});
try writer.interface.flush();
} }
} }
@ -182,8 +187,9 @@ pub fn controllers(alloc: Allocator, cgroup: []const u8) ![]const u8 {
// Read it all into memory -- we don't expect this file to ever // Read it all into memory -- we don't expect this file to ever
// be that large. // be that large.
var buf_reader = std.io.bufferedReader(file.reader()); var reader_buf: [4096]u8 = undefined;
const contents = try buf_reader.reader().readAllAlloc( var reader = file.reader(&reader_buf);
const contents = try reader.interface.readAlloc(
alloc, alloc,
1 * 1024 * 1024, // 1MB 1 * 1024 * 1024, // 1MB
); );
@ -213,7 +219,10 @@ pub fn configureControllers(
defer file.close(); defer file.close();
// Write // Write
try file.writer().writeAll(v); var writer_buf: [4096]u8 = undefined;
var writer = file.writer(&writer_buf);
try writer.interface.writeAll(v);
try writer.interface.flush();
} }
pub const Limit = union(enum) { pub const Limit = union(enum) {
@ -242,5 +251,8 @@ pub fn configureLimit(cgroup: []const u8, limit: Limit) !void {
defer file.close(); defer file.close();
// Write our limit in bytes // Write our limit in bytes
try file.writer().print("{}", .{size}); var writer_buf: [4096]u8 = undefined;
var writer = file.writer(&writer_buf);
try writer.interface.print("{}", .{size});
try writer.interface.flush();
} }

View File

@ -1,110 +1,121 @@
const std = @import("std"); const std = @import("std");
const testing = std.testing; const testing = std.testing;
const Writer = std.Io.Writer;
/// Writer that escapes characters that shells treat specially to reduce the /// Writer that escapes characters that shells treat specially to reduce the
/// risk of injection attacks or other such weirdness. Specifically excludes /// risk of injection attacks or other such weirdness. Specifically excludes
/// linefeeds so that they can be used to delineate lists of file paths. /// linefeeds so that they can be used to delineate lists of file paths.
/// ///
/// T should be a Zig type that follows the `std.io.Writer` interface. /// T should be a Zig type that follows the `std.Io.Writer` interface.
pub fn ShellEscapeWriter(comptime T: type) type { pub const ShellEscapeWriter = struct {
return struct { writer: Writer,
child_writer: T, child: *Writer,
fn write(self: *ShellEscapeWriter(T), data: []const u8) error{Error}!usize { pub fn init(child: *Writer) ShellEscapeWriter {
var count: usize = 0; return .{
for (data) |byte| { .writer = .{
const buf = switch (byte) { // TODO: Actually use a buffer here
'\\', .buffer = &.{},
'"', .vtable = &.{ .drain = ShellEscapeWriter.drain },
'\'', },
'$', .child = child,
'`', };
'*', }
'?',
' ', fn drain(w: *Writer, data: []const []const u8, splat: usize) Writer.Error!usize {
'|', const self: *ShellEscapeWriter = @fieldParentPtr("writer", w);
'(',
')', // TODO: This is a very naive implementation and does not really make
=> &[_]u8{ '\\', byte }, // full use of the post-Writergate API. However, since we know that
else => &[_]u8{byte}, // this is going into an Allocating writer anyways, we can be a bit
}; // less strict here.
self.child_writer.writeAll(buf) catch return error.Error;
count += 1; var count: usize = 0;
} for (data[0 .. data.len - 1]) |chunk| try self.writeEscaped(chunk, &count);
return count;
for (0..splat) |_| try self.writeEscaped(data[data.len], &count);
return count;
}
fn writeEscaped(
self: *ShellEscapeWriter,
s: []const u8,
count: *usize,
) Writer.Error!void {
for (s) |byte| {
const buf = switch (byte) {
'\\',
'"',
'\'',
'$',
'`',
'*',
'?',
' ',
'|',
'(',
')',
=> &[_]u8{ '\\', byte },
else => &[_]u8{byte},
};
try self.child.writeAll(buf);
count.* += 1;
} }
}
const Writer = std.io.Writer(*ShellEscapeWriter(T), error{Error}, write); };
pub fn init(child_writer: T) ShellEscapeWriter(T) {
return .{ .child_writer = child_writer };
}
pub fn writer(self: *ShellEscapeWriter(T)) Writer {
return .{ .context = self };
}
};
}
test "shell escape 1" { test "shell escape 1" {
var buf: [128]u8 = undefined; var buf: [128]u8 = undefined;
var fmt = std.io.fixedBufferStream(&buf); var writer: std.Io.Writer = .fixed(&buf);
var shell: ShellEscapeWriter(@TypeOf(fmt).Writer) = .{ .child_writer = fmt.writer() }; var shell: ShellEscapeWriter = .{ .child_writer = &writer };
const writer = shell.writer(); try shell.writer.writeAll("abc");
try writer.writeAll("abc"); try testing.expectEqualStrings("abc", writer.buffered());
try testing.expectEqualStrings("abc", fmt.getWritten());
} }
test "shell escape 2" { test "shell escape 2" {
var buf: [128]u8 = undefined; var buf: [128]u8 = undefined;
var fmt = std.io.fixedBufferStream(&buf); var writer: std.Io.Writer = .fixed(&buf);
var shell: ShellEscapeWriter(@TypeOf(fmt).Writer) = .{ .child_writer = fmt.writer() }; var shell: ShellEscapeWriter = .{ .child_writer = &writer };
const writer = shell.writer(); try shell.writer.writeAll("a c");
try writer.writeAll("a c"); try testing.expectEqualStrings("a\\ c", writer.buffered());
try testing.expectEqualStrings("a\\ c", fmt.getWritten());
} }
test "shell escape 3" { test "shell escape 3" {
var buf: [128]u8 = undefined; var buf: [128]u8 = undefined;
var fmt = std.io.fixedBufferStream(&buf); var writer: std.Io.Writer = .fixed(&buf);
var shell: ShellEscapeWriter(@TypeOf(fmt).Writer) = .{ .child_writer = fmt.writer() }; var shell: ShellEscapeWriter = .{ .child_writer = &writer };
const writer = shell.writer(); try shell.writer.writeAll("a?c");
try writer.writeAll("a?c"); try testing.expectEqualStrings("a\\?c", writer.buffered());
try testing.expectEqualStrings("a\\?c", fmt.getWritten());
} }
test "shell escape 4" { test "shell escape 4" {
var buf: [128]u8 = undefined; var buf: [128]u8 = undefined;
var fmt = std.io.fixedBufferStream(&buf); var writer: std.Io.Writer = .fixed(&buf);
var shell: ShellEscapeWriter(@TypeOf(fmt).Writer) = .{ .child_writer = fmt.writer() }; var shell: ShellEscapeWriter = .{ .child_writer = &writer };
const writer = shell.writer(); try shell.writer.writeAll("a\\c");
try writer.writeAll("a\\c"); try testing.expectEqualStrings("a\\\\c", writer.buffered());
try testing.expectEqualStrings("a\\\\c", fmt.getWritten());
} }
test "shell escape 5" { test "shell escape 5" {
var buf: [128]u8 = undefined; var buf: [128]u8 = undefined;
var fmt = std.io.fixedBufferStream(&buf); var writer: std.Io.Writer = .fixed(&buf);
var shell: ShellEscapeWriter(@TypeOf(fmt).Writer) = .{ .child_writer = fmt.writer() }; var shell: ShellEscapeWriter = .{ .child_writer = &writer };
const writer = shell.writer(); try shell.writer.writeAll("a|c");
try writer.writeAll("a|c"); try testing.expectEqualStrings("a\\|c", writer.buffered());
try testing.expectEqualStrings("a\\|c", fmt.getWritten());
} }
test "shell escape 6" { test "shell escape 6" {
var buf: [128]u8 = undefined; var buf: [128]u8 = undefined;
var fmt = std.io.fixedBufferStream(&buf); var writer: std.Io.Writer = .fixed(&buf);
var shell: ShellEscapeWriter(@TypeOf(fmt).Writer) = .{ .child_writer = fmt.writer() }; var shell: ShellEscapeWriter = .{ .child_writer = &writer };
const writer = shell.writer(); try shell.writer.writeAll("a\"c");
try writer.writeAll("a\"c"); try testing.expectEqualStrings("a\\\"c", writer.buffered());
try testing.expectEqualStrings("a\\\"c", fmt.getWritten());
} }
test "shell escape 7" { test "shell escape 7" {
var buf: [128]u8 = undefined; var buf: [128]u8 = undefined;
var fmt = std.io.fixedBufferStream(&buf); var writer: std.Io.Writer = .fixed(&buf);
var shell: ShellEscapeWriter(@TypeOf(fmt).Writer) = .{ .child_writer = fmt.writer() }; var shell: ShellEscapeWriter = .{ .child_writer = &writer };
const writer = shell.writer(); try shell.writer.writeAll("a(1)");
try writer.writeAll("a(1)"); try testing.expectEqualStrings("a\\(1\\)", writer.buffered());
try testing.expectEqualStrings("a\\(1\\)", fmt.getWritten());
} }

View File

@ -216,7 +216,7 @@ const PosixPty = struct {
// Reset our signals // Reset our signals
var sa: posix.Sigaction = .{ var sa: posix.Sigaction = .{
.handler = .{ .handler = posix.SIG.DFL }, .handler = .{ .handler = posix.SIG.DFL },
.mask = posix.empty_sigset, .mask = posix.sigemptyset(),
.flags = 0, .flags = 0,
}; };
posix.sigaction(posix.SIG.ABRT, &sa, null); posix.sigaction(posix.SIG.ABRT, &sa, null);

View File

@ -38,8 +38,8 @@ pub fn loadFromFiles(
paths: configpkg.RepeatablePath, paths: configpkg.RepeatablePath,
target: Target, target: Target,
) ![]const [:0]const u8 { ) ![]const [:0]const u8 {
var list = std.ArrayList([:0]const u8).init(alloc_gpa); var list: std.ArrayList([:0]const u8) = .empty;
defer list.deinit(); defer list.deinit(alloc_gpa);
errdefer for (list.items) |shader| alloc_gpa.free(shader); errdefer for (list.items) |shader| alloc_gpa.free(shader);
for (paths.value.items) |item| { for (paths.value.items) |item| {
@ -56,10 +56,10 @@ pub fn loadFromFiles(
return err; return err;
}; };
log.info("loaded custom shader path={s}", .{path}); log.info("loaded custom shader path={s}", .{path});
try list.append(shader); try list.append(alloc_gpa, shader);
} }
return try list.toOwnedSlice(); return try list.toOwnedSlice(alloc_gpa);
} }
/// Load a single shader from a file and convert it to the target language /// Load a single shader from a file and convert it to the target language
@ -73,34 +73,35 @@ pub fn loadFromFile(
defer arena.deinit(); defer arena.deinit();
const alloc = arena.allocator(); const alloc = arena.allocator();
// Load the shader file
const cwd = std.fs.cwd();
const file = try cwd.openFile(path, .{});
defer file.close();
// Read it all into memory -- we don't expect shaders to be large. // Read it all into memory -- we don't expect shaders to be large.
var buf_reader = std.io.bufferedReader(file.reader()); const src = src: {
const src = try buf_reader.reader().readAllAlloc( // Load the shader file
alloc, const cwd = std.fs.cwd();
4 * 1024 * 1024, // 4MB const file = try cwd.openFile(path, .{});
); defer file.close();
var buf: [4096]u8 = undefined;
var reader = file.reader(&buf);
break :src try reader.interface.readAlloc(
alloc,
4 * 1024 * 1024, // 4MB
);
};
// Convert to full GLSL // Convert to full GLSL
const glsl: [:0]const u8 = glsl: { const glsl: [:0]const u8 = glsl: {
var list = std.ArrayList(u8).init(alloc); var stream: std.Io.Writer.Allocating = .init(alloc);
try glslFromShader(list.writer(), src); try glslFromShader(&stream.writer, src);
try list.append(0); try stream.writer.writeByte(0);
break :glsl list.items[0 .. list.items.len - 1 :0]; break :glsl stream.written()[0 .. stream.written().len - 1 :0];
}; };
// Convert to SPIR-V // Convert to SPIR-V
const spirv: []const u8 = spirv: { const spirv: []const u8 = spirv: {
// SpirV pointer must be aligned to 4 bytes since we expect var stream: std.Io.Writer.Allocating = .init(alloc);
// a slice of words.
var list = std.ArrayListAligned(u8, @alignOf(u32)).init(alloc);
var errlog: SpirvLog = .{ .alloc = alloc }; var errlog: SpirvLog = .{ .alloc = alloc };
defer errlog.deinit(); defer errlog.deinit();
spirvFromGlsl(list.writer(), &errlog, glsl) catch |err| { spirvFromGlsl(&stream.writer, &errlog, glsl) catch |err| {
if (errlog.info.len > 0 or errlog.debug.len > 0) { if (errlog.info.len > 0 or errlog.debug.len > 0) {
log.warn("spirv error path={s} info={s} debug={s}", .{ log.warn("spirv error path={s} info={s} debug={s}", .{
path, path,
@ -111,6 +112,11 @@ pub fn loadFromFile(
return err; return err;
}; };
// SpirV pointer must be aligned to 4 bytes since we expect
// a slice of words.
var list: std.ArrayListAligned(u8, .of(u32)) = .empty;
try list.appendSlice(alloc, stream.written());
break :spirv list.items; break :spirv list.items;
}; };

View File

@ -23,7 +23,7 @@ pub fn destroy(self: *Ascii, alloc: Allocator) void {
alloc.destroy(self); alloc.destroy(self);
} }
pub fn run(self: *Ascii, writer: anytype, rand: std.Random) !void { pub fn run(self: *Ascii, writer: *std.Io.Writer, rand: std.Random) !void {
_ = self; _ = self;
var gen: synthetic.Bytes = .{ var gen: synthetic.Bytes = .{

View File

@ -29,7 +29,7 @@ pub fn destroy(self: *Osc, alloc: Allocator) void {
alloc.destroy(self); alloc.destroy(self);
} }
pub fn run(self: *Osc, writer: anytype, rand: std.Random) !void { pub fn run(self: *Osc, writer: *std.Io.Writer, rand: std.Random) !void {
var gen: synthetic.Osc = .{ var gen: synthetic.Osc = .{
.rand = rand, .rand = rand,
.p_valid = self.opts.@"p-valid", .p_valid = self.opts.@"p-valid",

View File

@ -23,7 +23,7 @@ pub fn destroy(self: *Utf8, alloc: Allocator) void {
alloc.destroy(self); alloc.destroy(self);
} }
pub fn run(self: *Utf8, writer: anytype, rand: std.Random) !void { pub fn run(self: *Utf8, writer: *std.Io.Writer, rand: std.Random) !void {
_ = self; _ = self;
var gen: synthetic.Utf8 = .{ var gen: synthetic.Utf8 = .{

View File

@ -2723,7 +2723,7 @@ pub fn encodeUtf8(
/// 1 | etc.| | 4 /// 1 | etc.| | 4
/// +-----+ : /// +-----+ :
/// +--------+ /// +--------+
pub fn diagram(self: *const PageList, writer: anytype) !void { pub fn diagram(self: *const PageList, writer: *std.Io.Writer) !void {
const active_pin = self.getTopLeft(.active); const active_pin = self.getTopLeft(.active);
var active = false; var active = false;

View File

@ -239,6 +239,11 @@ pub fn deinit(self: *Terminal, alloc: Allocator) void {
self.* = undefined; self.* = undefined;
} }
/// The general allocator we should use for this terminal.
fn gpa(self: *Terminal) Allocator {
return self.screen.alloc;
}
/// Print UTF-8 encoded string to the terminal. /// Print UTF-8 encoded string to the terminal.
pub fn printString(self: *Terminal, str: []const u8) !void { pub fn printString(self: *Terminal, str: []const u8) !void {
const view = try std.unicode.Utf8View.init(str); const view = try std.unicode.Utf8View.init(str);
@ -2531,7 +2536,7 @@ pub fn resize(
/// Set the pwd for the terminal. /// Set the pwd for the terminal.
pub fn setPwd(self: *Terminal, pwd: []const u8) !void { pub fn setPwd(self: *Terminal, pwd: []const u8) !void {
self.pwd.clearRetainingCapacity(); self.pwd.clearRetainingCapacity();
try self.pwd.appendSlice(pwd); try self.pwd.appendSlice(self.gpa(), pwd);
} }
/// Returns the pwd for the terminal, if any. The memory is owned by the /// Returns the pwd for the terminal, if any. The memory is owned by the

View File

@ -276,7 +276,7 @@ pub const Response = struct {
placement_id: u32 = 0, placement_id: u32 = 0,
message: []const u8 = "OK", message: []const u8 = "OK",
pub fn encode(self: Response, writer: anytype) !void { pub fn encode(self: Response, writer: *std.Io.Writer) !void {
// We only encode a result if we have either an id or an image number. // We only encode a result if we have either an id or an image number.
if (self.id == 0 and self.image_number == 0) return; if (self.id == 0 and self.image_number == 0) return;

View File

@ -274,7 +274,7 @@ pub const Terminator = enum {
self: Terminator, self: Terminator,
comptime _: []const u8, comptime _: []const u8,
_: std.fmt.FormatOptions, _: std.fmt.FormatOptions,
writer: anytype, writer: *std.Io.Writer,
) !void { ) !void {
try writer.writeAll(self.string()); try writer.writeAll(self.string());
} }

View File

@ -60,7 +60,7 @@ pub const Style = struct {
self: Color, self: Color,
comptime fmt: []const u8, comptime fmt: []const u8,
options: std.fmt.FormatOptions, options: std.fmt.FormatOptions,
writer: anytype, writer: *std.Io.Writer,
) !void { ) !void {
_ = fmt; _ = fmt;
_ = options; _ = options;
@ -228,7 +228,7 @@ pub const Style = struct {
self: Style, self: Style,
comptime fmt: []const u8, comptime fmt: []const u8,
options: std.fmt.FormatOptions, options: std.fmt.FormatOptions,
writer: anytype, writer: *std.Io.Writer,
) !void { ) !void {
_ = fmt; _ = fmt;
_ = options; _ = options;

View File

@ -43,7 +43,7 @@ pub const Capability = struct {
/// Encode as a terminfo source file. The encoding is always done in a /// Encode as a terminfo source file. The encoding is always done in a
/// human-readable format with whitespace. Fields are always written in the /// human-readable format with whitespace. Fields are always written in the
/// order of the slices on this struct; this will not do any reordering. /// order of the slices on this struct; this will not do any reordering.
pub fn encode(self: Source, writer: anytype) !void { pub fn encode(self: Source, writer: *std.Io.Writer) !void {
// Encode the names in the order specified // Encode the names in the order specified
for (self.names, 0..) |name, i| { for (self.names, 0..) |name, i| {
if (i != 0) try writer.writeAll("|"); if (i != 0) try writer.writeAll("|");

View File

@ -591,6 +591,17 @@ const Subprocess = struct {
flatpak_command: ?FlatpakHostCommand = null, flatpak_command: ?FlatpakHostCommand = null,
linux_cgroup: Command.LinuxCgroup = Command.linux_cgroup_default, linux_cgroup: Command.LinuxCgroup = Command.linux_cgroup_default,
const ArgsFormatter = struct {
args: []const [:0]const u8,
pub fn format(this: @This(), writer: *std.Io.Writer) std.Io.Writer.Error!void {
for (this.args, 0..) |a, i| {
if (i > 0) try writer.writeAll(", ");
try writer.print("`{s}`", .{a});
}
}
};
/// Initialize the subprocess. This will NOT start it, this only sets /// Initialize the subprocess. This will NOT start it, this only sets
/// up the internal state necessary to start it later. /// up the internal state necessary to start it later.
pub fn init(gpa: Allocator, cfg: Config) !Subprocess { pub fn init(gpa: Allocator, cfg: Config) !Subprocess {
@ -897,7 +908,7 @@ const Subprocess = struct {
self.pty = null; self.pty = null;
}; };
log.debug("starting command command={s}", .{self.args}); log.debug("starting command command={f}", .{ArgsFormatter{ .args = self.args }});
// If we can't access the cwd, then don't set any cwd and inherit. // If we can't access the cwd, then don't set any cwd and inherit.
// This is important because our cwd can be set by the shell (OSC 7) // This is important because our cwd can be set by the shell (OSC 7)
@ -1157,7 +1168,7 @@ const Subprocess = struct {
const res = posix.waitpid(pid, std.c.W.NOHANG); const res = posix.waitpid(pid, std.c.W.NOHANG);
log.debug("waitpid result={}", .{res.pid}); log.debug("waitpid result={}", .{res.pid});
if (res.pid != 0) break; if (res.pid != 0) break;
std.time.sleep(10 * std.time.ns_per_ms); std.Thread.sleep(10 * std.time.ns_per_ms);
} }
}, },
} }
@ -1180,7 +1191,7 @@ const Subprocess = struct {
const pgid = c.getpgid(pid); const pgid = c.getpgid(pid);
if (pgid == my_pgid) { if (pgid == my_pgid) {
log.warn("pgid is our own, retrying", .{}); log.warn("pgid is our own, retrying", .{});
std.time.sleep(10 * std.time.ns_per_ms); std.Thread.sleep(10 * std.time.ns_per_ms);
continue; continue;
} }

View File

@ -310,11 +310,11 @@ pub const StreamHandler = struct {
.kitty => |*kitty_cmd| { .kitty => |*kitty_cmd| {
if (self.terminal.kittyGraphics(self.alloc, kitty_cmd)) |resp| { if (self.terminal.kittyGraphics(self.alloc, kitty_cmd)) |resp| {
var buf: [1024]u8 = undefined; var buf: [1024]u8 = undefined;
var buf_stream = std.io.fixedBufferStream(&buf); var writer: std.Io.Writer = .fixed(&buf);
try resp.encode(buf_stream.writer()); try resp.encode(&writer);
const final = buf_stream.getWritten(); const final = writer.buffered();
if (final.len > 2) { if (final.len > 2) {
log.debug("kitty graphics response: {s}", .{std.fmt.fmtSliceHexLower(final)}); log.debug("kitty graphics response: {x}", .{final});
self.messageWriter(try termio.Message.writeReq(self.alloc, final)); self.messageWriter(try termio.Message.writeReq(self.alloc, final));
} }
} }
@ -1141,7 +1141,7 @@ pub const StreamHandler = struct {
// We need to unescape the path. We first try to unescape onto // We need to unescape the path. We first try to unescape onto
// the stack and fall back to heap allocation if we have to. // the stack and fall back to heap allocation if we have to.
var pathBuf: [1024]u8 = undefined; var path_buf: [1024]u8 = undefined;
const path, const heap = path: { const path, const heap = path: {
// Get the raw string of the URI. Its unclear to me if the various // Get the raw string of the URI. Its unclear to me if the various
// tags of this enum guarantee no percent-encoding so we just // tags of this enum guarantee no percent-encoding so we just
@ -1156,15 +1156,16 @@ pub const StreamHandler = struct {
break :path .{ path, false }; break :path .{ path, false };
// First try to stack-allocate // First try to stack-allocate
var fba = std.heap.FixedBufferAllocator.init(&pathBuf); var stack_writer: std.Io.Writer = .fixed(&path_buf);
if (std.fmt.allocPrint(fba.allocator(), "{raw}", .{uri.path})) |v| if (uri.path.formatRaw(&stack_writer)) |_| {
break :path .{ v, false } break :path .{ stack_writer.buffered(), false };
else |_| {} } else |_| {}
// Fall back to heap // Fall back to heap
if (std.fmt.allocPrint(self.alloc, "{raw}", .{uri.path})) |v| var alloc_writer: std.Io.Writer.Allocating = .init(self.alloc);
break :path .{ v, true } if (uri.path.formatRaw(&alloc_writer.writer)) |_| {
else |_| {} break :path .{ alloc_writer.written(), true };
} else |_| {}
// Fall back to using it directly... // Fall back to using it directly...
log.warn("failed to unescape OSC 7 path, using it directly path={s}", .{path}); log.warn("failed to unescape OSC 7 path, using it directly path={s}", .{path});
@ -1471,15 +1472,15 @@ pub const StreamHandler = struct {
self: *StreamHandler, self: *StreamHandler,
request: terminal.kitty.color.OSC, request: terminal.kitty.color.OSC,
) !void { ) !void {
var buf = std.ArrayList(u8).init(self.alloc); var stream: std.Io.Writer.Allocating = .init(self.alloc);
defer buf.deinit(); defer stream.deinit();
const writer = buf.writer(); const writer = &stream.writer;
for (request.list.items) |item| { for (request.list.items) |item| {
switch (item) { switch (item) {
.query => |key| { .query => |key| {
// If the writer buffer is empty, we need to write our prefix // If the writer buffer is empty, we need to write our prefix
if (buf.items.len == 0) try writer.writeAll("\x1b]21"); if (stream.written().len == 0) try writer.writeAll("\x1b]21");
const color: terminal.color.RGB = switch (key) { const color: terminal.color.RGB = switch (key) {
.palette => |palette| self.terminal.color_palette.colors[palette], .palette => |palette| self.terminal.color_palette.colors[palette],
@ -1488,17 +1489,17 @@ pub const StreamHandler = struct {
.background => self.background_color orelse self.default_background_color, .background => self.background_color orelse self.default_background_color,
.cursor => self.cursor_color orelse self.default_cursor_color, .cursor => self.cursor_color orelse self.default_cursor_color,
else => { else => {
log.warn("ignoring unsupported kitty color protocol key: {}", .{key}); log.warn("ignoring unsupported kitty color protocol key: {f}", .{key});
continue; continue;
}, },
}, },
} orelse { } orelse {
try writer.print(";{}=", .{key}); try writer.print(";{f}=", .{key});
continue; continue;
}; };
try writer.print( try writer.print(
";{}=rgb:{x:0>2}/{x:0>2}/{x:0>2}", ";{f}=rgb:{x:0>2}/{x:0>2}/{x:0>2}",
.{ key, color.r, color.g, color.b }, .{ key, color.r, color.g, color.b },
); );
}, },
@ -1525,7 +1526,7 @@ pub const StreamHandler = struct {
}, },
else => { else => {
log.warn( log.warn(
"ignoring unsupported kitty color protocol key: {}", "ignoring unsupported kitty color protocol key: {f}",
.{v.key}, .{v.key},
); );
continue; continue;
@ -1560,7 +1561,7 @@ pub const StreamHandler = struct {
}, },
else => { else => {
log.warn( log.warn(
"ignoring unsupported kitty color protocol key: {}", "ignoring unsupported kitty color protocol key: {f}",
.{key}, .{key},
); );
continue; continue;
@ -1576,12 +1577,12 @@ pub const StreamHandler = struct {
} }
// If we had any writes to our buffer, we queue them now // If we had any writes to our buffer, we queue them now
if (buf.items.len > 0) { if (stream.written().len > 0) {
try writer.writeAll(request.terminator.string()); try writer.writeAll(request.terminator.string());
self.messageWriter(.{ self.messageWriter(.{
.write_alloc = .{ .write_alloc = .{
.alloc = self.alloc, .alloc = self.alloc,
.data = try buf.toOwnedSlice(), .data = try stream.toOwnedSlice(),
}, },
}); });
} }