Merge pull request #2108 from ghostty-org/yeet-usingns

Yeet Usingnamespace (Partial)
This commit is contained in:
Mitchell Hashimoto
2024-08-16 15:16:40 -07:00
committed by GitHub
49 changed files with 573 additions and 416 deletions

View File

@ -1042,6 +1042,9 @@ fn addDeps(
step.linkLibC(); step.linkLibC();
step.addIncludePath(b.path("src/stb")); step.addIncludePath(b.path("src/stb"));
step.addCSourceFiles(.{ .files = &.{"src/stb/stb.c"} }); step.addCSourceFiles(.{ .files = &.{"src/stb/stb.c"} });
if (step.rootModuleTarget().os.tag == .linux) {
step.addIncludePath(b.path("src/apprt/gtk"));
}
// C++ files // C++ files
step.linkLibCpp(); step.linkLibCpp();

View File

@ -20,9 +20,9 @@ const builtin = @import("builtin");
const assert = std.debug.assert; const assert = std.debug.assert;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const ArenaAllocator = std.heap.ArenaAllocator; const ArenaAllocator = std.heap.ArenaAllocator;
const global_state = &@import("global.zig").state;
const oni = @import("oniguruma"); const oni = @import("oniguruma");
const unicode = @import("unicode/main.zig"); const unicode = @import("unicode/main.zig");
const main = @import("main.zig");
const renderer = @import("renderer.zig"); const renderer = @import("renderer.zig");
const termio = @import("termio.zig"); const termio = @import("termio.zig");
const objc = @import("objc"); const objc = @import("objc");
@ -448,7 +448,7 @@ pub fn init(
.shell_integration = config.@"shell-integration", .shell_integration = config.@"shell-integration",
.shell_integration_features = config.@"shell-integration-features", .shell_integration_features = config.@"shell-integration-features",
.working_directory = config.@"working-directory", .working_directory = config.@"working-directory",
.resources_dir = main.state.resources_dir, .resources_dir = global_state.resources_dir,
.term = config.term, .term = config.term,
// Get the cgroup if we're on linux and have the decl. I'd love // Get the cgroup if we're on linux and have the decl. I'd love

View File

@ -12,7 +12,8 @@ const std = @import("std");
const builtin = @import("builtin"); const builtin = @import("builtin");
const build_config = @import("build_config.zig"); const build_config = @import("build_config.zig");
pub usingnamespace @import("apprt/structs.zig"); const structs = @import("apprt/structs.zig");
pub const glfw = @import("apprt/glfw.zig"); pub const glfw = @import("apprt/glfw.zig");
pub const gtk = @import("apprt/gtk.zig"); pub const gtk = @import("apprt/gtk.zig");
pub const none = @import("apprt/none.zig"); pub const none = @import("apprt/none.zig");
@ -20,6 +21,18 @@ pub const browser = @import("apprt/browser.zig");
pub const embedded = @import("apprt/embedded.zig"); pub const embedded = @import("apprt/embedded.zig");
pub const surface = @import("apprt/surface.zig"); pub const surface = @import("apprt/surface.zig");
pub const ContentScale = structs.ContentScale;
pub const Clipboard = structs.Clipboard;
pub const ClipboardRequest = structs.ClipboardRequest;
pub const ClipboardRequestType = structs.ClipboardRequestType;
pub const ColorScheme = structs.ColorScheme;
pub const CursorPos = structs.CursorPos;
pub const DesktopNotification = structs.DesktopNotification;
pub const IMEPos = structs.IMEPos;
pub const Selection = structs.Selection;
pub const SplitDirection = structs.SplitDirection;
pub const SurfaceSize = structs.SurfaceSize;
/// The implementation to use for the app runtime. This is comptime chosen /// The implementation to use for the app runtime. This is comptime chosen
/// so that every build has exactly one application runtime implementation. /// so that every build has exactly one application runtime implementation.
/// Note: it is very rare to use Runtime directly; most usage will use /// Note: it is very rare to use Runtime directly; most usage will use

View File

@ -1386,7 +1386,7 @@ pub const Inspector = struct {
// C API // C API
pub const CAPI = struct { pub const CAPI = struct {
const global = &@import("../main.zig").state; const global = &@import("../global.zig").state;
/// This is the same as Surface.KeyEvent but this is the raw C API version. /// This is the same as Surface.KeyEvent but this is the raw C API version.
const KeyEvent = extern struct { const KeyEvent = extern struct {
@ -1427,6 +1427,14 @@ pub const CAPI = struct {
cell_height_px: u32, cell_height_px: u32,
}; };
// Reference the conditional exports based on target platform
// so they're included in the C API.
comptime {
if (builtin.target.isDarwin()) {
_ = Darwin;
}
}
/// Create a new app. /// Create a new app.
export fn ghostty_app_new( export fn ghostty_app_new(
opts: *const apprt.runtime.App.Options, opts: *const apprt.runtime.App.Options,
@ -1830,8 +1838,112 @@ pub const CAPI = struct {
ptr.freeInspector(); ptr.freeInspector();
} }
// Inspector Metal APIs are only available on Apple systems export fn ghostty_inspector_set_size(ptr: *Inspector, w: u32, h: u32) void {
usingnamespace if (builtin.target.isDarwin()) struct { ptr.updateSize(w, h);
}
export fn ghostty_inspector_set_content_scale(ptr: *Inspector, x: f64, y: f64) void {
ptr.updateContentScale(x, y);
}
export fn ghostty_inspector_mouse_button(
ptr: *Inspector,
action: input.MouseButtonState,
button: input.MouseButton,
mods: c_int,
) void {
ptr.mouseButtonCallback(
action,
button,
@bitCast(@as(
input.Mods.Backing,
@truncate(@as(c_uint, @bitCast(mods))),
)),
);
}
export fn ghostty_inspector_mouse_pos(ptr: *Inspector, x: f64, y: f64) void {
ptr.cursorPosCallback(x, y);
}
export fn ghostty_inspector_mouse_scroll(
ptr: *Inspector,
x: f64,
y: f64,
scroll_mods: c_int,
) void {
ptr.scrollCallback(
x,
y,
@bitCast(@as(u8, @truncate(@as(c_uint, @bitCast(scroll_mods))))),
);
}
export fn ghostty_inspector_key(
ptr: *Inspector,
action: input.Action,
key: input.Key,
c_mods: c_int,
) void {
ptr.keyCallback(
action,
key,
@bitCast(@as(
input.Mods.Backing,
@truncate(@as(c_uint, @bitCast(c_mods))),
)),
) catch |err| {
log.err("error processing key event err={}", .{err});
return;
};
}
export fn ghostty_inspector_text(
ptr: *Inspector,
str: [*:0]const u8,
) void {
ptr.textCallback(std.mem.sliceTo(str, 0));
}
export fn ghostty_inspector_set_focus(ptr: *Inspector, focused: bool) void {
ptr.focusCallback(focused);
}
/// Sets the window background blur on macOS to the desired value.
/// I do this in Zig as an extern function because I don't know how to
/// call these functions in Swift.
///
/// This uses an undocumented, non-public API because this is what
/// every terminal appears to use, including Terminal.app.
export fn ghostty_set_window_background_blur(
app: *App,
window: *anyopaque,
) void {
// This is only supported on macOS
if (comptime builtin.target.os.tag != .macos) return;
const config = app.config;
// Do nothing if we don't have background transparency enabled
if (config.@"background-opacity" >= 1.0) return;
// Do nothing if our blur value is zero
if (config.@"background-blur-radius" == 0) return;
const nswindow = objc.Object.fromId(window);
_ = CGSSetWindowBackgroundBlurRadius(
CGSDefaultConnectionForThread(),
nswindow.msgSend(usize, objc.sel("windowNumber"), .{}),
@intCast(config.@"background-blur-radius"),
);
}
/// See ghostty_set_window_background_blur
extern "c" fn CGSSetWindowBackgroundBlurRadius(*anyopaque, usize, c_int) i32;
extern "c" fn CGSDefaultConnectionForThread() *anyopaque;
// Darwin-only C APIs.
const Darwin = struct {
export fn ghostty_surface_set_display_id(ptr: *Surface, display_id: u32) void { export fn ghostty_surface_set_display_id(ptr: *Surface, display_id: u32) void {
const surface = &ptr.core_surface; const surface = &ptr.core_surface;
_ = surface.renderer_thread.mailbox.push( _ = surface.renderer_thread.mailbox.push(
@ -1984,109 +2096,5 @@ pub const CAPI = struct {
ptr.backend = null; ptr.backend = null;
} }
} }
} else struct {}; };
export fn ghostty_inspector_set_size(ptr: *Inspector, w: u32, h: u32) void {
ptr.updateSize(w, h);
}
export fn ghostty_inspector_set_content_scale(ptr: *Inspector, x: f64, y: f64) void {
ptr.updateContentScale(x, y);
}
export fn ghostty_inspector_mouse_button(
ptr: *Inspector,
action: input.MouseButtonState,
button: input.MouseButton,
mods: c_int,
) void {
ptr.mouseButtonCallback(
action,
button,
@bitCast(@as(
input.Mods.Backing,
@truncate(@as(c_uint, @bitCast(mods))),
)),
);
}
export fn ghostty_inspector_mouse_pos(ptr: *Inspector, x: f64, y: f64) void {
ptr.cursorPosCallback(x, y);
}
export fn ghostty_inspector_mouse_scroll(
ptr: *Inspector,
x: f64,
y: f64,
scroll_mods: c_int,
) void {
ptr.scrollCallback(
x,
y,
@bitCast(@as(u8, @truncate(@as(c_uint, @bitCast(scroll_mods))))),
);
}
export fn ghostty_inspector_key(
ptr: *Inspector,
action: input.Action,
key: input.Key,
c_mods: c_int,
) void {
ptr.keyCallback(
action,
key,
@bitCast(@as(
input.Mods.Backing,
@truncate(@as(c_uint, @bitCast(c_mods))),
)),
) catch |err| {
log.err("error processing key event err={}", .{err});
return;
};
}
export fn ghostty_inspector_text(
ptr: *Inspector,
str: [*:0]const u8,
) void {
ptr.textCallback(std.mem.sliceTo(str, 0));
}
export fn ghostty_inspector_set_focus(ptr: *Inspector, focused: bool) void {
ptr.focusCallback(focused);
}
/// Sets the window background blur on macOS to the desired value.
/// I do this in Zig as an extern function because I don't know how to
/// call these functions in Swift.
///
/// This uses an undocumented, non-public API because this is what
/// every terminal appears to use, including Terminal.app.
export fn ghostty_set_window_background_blur(
app: *App,
window: *anyopaque,
) void {
// This is only supported on macOS
if (comptime builtin.target.os.tag != .macos) return;
const config = app.config;
// Do nothing if we don't have background transparency enabled
if (config.@"background-opacity" >= 1.0) return;
// Do nothing if our blur value is zero
if (config.@"background-blur-radius" == 0) return;
const nswindow = objc.Object.fromId(window);
_ = CGSSetWindowBackgroundBlurRadius(
CGSDefaultConnectionForThread(),
nswindow.msgSend(usize, objc.sel("windowNumber"), .{}),
@intCast(config.@"background-blur-radius"),
);
}
/// See ghostty_set_window_background_blur
extern "c" fn CGSSetWindowBackgroundBlurRadius(*anyopaque, usize, c_int) i32;
extern "c" fn CGSDefaultConnectionForThread() *anyopaque;
}; };

View File

@ -28,7 +28,7 @@ const Surface = @import("Surface.zig");
const Window = @import("Window.zig"); const Window = @import("Window.zig");
const ConfigErrorsWindow = @import("ConfigErrorsWindow.zig"); const ConfigErrorsWindow = @import("ConfigErrorsWindow.zig");
const ClipboardConfirmationWindow = @import("ClipboardConfirmationWindow.zig"); const ClipboardConfirmationWindow = @import("ClipboardConfirmationWindow.zig");
const c = @import("c.zig"); const c = @import("c.zig").c;
const inspector = @import("inspector.zig"); const inspector = @import("inspector.zig");
const key = @import("key.zig"); const key = @import("key.zig");
const x11 = @import("x11.zig"); const x11 = @import("x11.zig");

View File

@ -8,7 +8,7 @@ const apprt = @import("../../apprt.zig");
const CoreSurface = @import("../../Surface.zig"); const CoreSurface = @import("../../Surface.zig");
const App = @import("App.zig"); const App = @import("App.zig");
const View = @import("View.zig"); const View = @import("View.zig");
const c = @import("c.zig"); const c = @import("c.zig").c;
const log = std.log.scoped(.gtk); const log = std.log.scoped(.gtk);

View File

@ -8,7 +8,7 @@ const Config = configpkg.Config;
const App = @import("App.zig"); const App = @import("App.zig");
const View = @import("View.zig"); const View = @import("View.zig");
const c = @import("c.zig"); const c = @import("c.zig").c;
const log = std.log.scoped(.gtk); const log = std.log.scoped(.gtk);

View File

@ -4,7 +4,7 @@ const std = @import("std");
const assert = std.debug.assert; const assert = std.debug.assert;
const cimgui = @import("cimgui"); const cimgui = @import("cimgui");
const c = @import("c.zig"); const c = @import("c.zig").c;
const key = @import("key.zig"); const key = @import("key.zig");
const gl = @import("opengl"); const gl = @import("opengl");
const input = @import("../../input.zig"); const input = @import("../../input.zig");

View File

@ -1,7 +1,7 @@
const ResizeOverlay = @This(); const ResizeOverlay = @This();
const std = @import("std"); const std = @import("std");
const c = @import("c.zig"); const c = @import("c.zig").c;
const configpkg = @import("../../config.zig"); const configpkg = @import("../../config.zig");
const Surface = @import("Surface.zig"); const Surface = @import("Surface.zig");

View File

@ -12,7 +12,7 @@ const CoreSurface = @import("../../Surface.zig");
const Surface = @import("Surface.zig"); const Surface = @import("Surface.zig");
const Tab = @import("Tab.zig"); const Tab = @import("Tab.zig");
const c = @import("c.zig"); const c = @import("c.zig").c;
const log = std.log.scoped(.gtk); const log = std.log.scoped(.gtk);

View File

@ -21,7 +21,7 @@ const ClipboardConfirmationWindow = @import("ClipboardConfirmationWindow.zig");
const ResizeOverlay = @import("ResizeOverlay.zig"); const ResizeOverlay = @import("ResizeOverlay.zig");
const inspector = @import("inspector.zig"); const inspector = @import("inspector.zig");
const gtk_key = @import("key.zig"); const gtk_key = @import("key.zig");
const c = @import("c.zig"); const c = @import("c.zig").c;
const x11 = @import("x11.zig"); const x11 = @import("x11.zig");
const log = std.log.scoped(.gtk_surface); const log = std.log.scoped(.gtk_surface);

View File

@ -12,7 +12,7 @@ const CoreSurface = @import("../../Surface.zig");
const Surface = @import("Surface.zig"); const Surface = @import("Surface.zig");
const Window = @import("Window.zig"); const Window = @import("Window.zig");
const c = @import("c.zig"); const c = @import("c.zig").c;
const log = std.log.scoped(.gtk); const log = std.log.scoped(.gtk);

View File

@ -5,7 +5,7 @@
const View = @This(); const View = @This();
const std = @import("std"); const std = @import("std");
const c = @import("c.zig"); const c = @import("c.zig").c;
const log = std.log.scoped(.gtk); const log = std.log.scoped(.gtk);

View File

@ -19,7 +19,7 @@ const App = @import("App.zig");
const Color = configpkg.Config.Color; const Color = configpkg.Config.Color;
const Surface = @import("Surface.zig"); const Surface = @import("Surface.zig");
const Tab = @import("Tab.zig"); const Tab = @import("Tab.zig");
const c = @import("c.zig"); const c = @import("c.zig").c;
const log = std.log.scoped(.gtk); const log = std.log.scoped(.gtk);

View File

@ -1,21 +1,19 @@
const c = @cImport({ /// Imported C API directly from header files
pub const c = @cImport({
@cInclude("gtk/gtk.h"); @cInclude("gtk/gtk.h");
if (@import("build_options").libadwaita) @cInclude("libadwaita-1/adwaita.h"); if (@import("build_options").libadwaita) {
@cInclude("libadwaita-1/adwaita.h");
}
// Add in X11-specific GDK backend which we use for specific things (e.g. // Add in X11-specific GDK backend which we use for specific things
// X11 window class). // (e.g. X11 window class).
@cInclude("gdk/x11/gdkx.h"); @cInclude("gdk/x11/gdkx.h");
// Xkb for X11 state handling // Xkb for X11 state handling
@cInclude("X11/XKBlib.h"); @cInclude("X11/XKBlib.h");
// generated header files // generated header files
@cInclude("ghostty_resources.h"); @cInclude("ghostty_resources.h");
// compatibility
@cInclude("ghostty_gtk_compat.h");
}); });
pub usingnamespace c;
/// Compatibility with gobject < 2.74
pub usingnamespace if (@hasDecl(c, "G_CONNECT_DEFAULT")) struct {} else struct {
pub const G_CONNECT_DEFAULT = 0;
pub const G_APPLICATION_DEFAULT_FLAGS = c.G_APPLICATION_FLAGS_NONE;
};

View File

@ -3,7 +3,7 @@
const std = @import("std"); const std = @import("std");
const assert = std.debug.assert; const assert = std.debug.assert;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const c = @import("c.zig"); const c = @import("c.zig").c;
const App = @import("App.zig"); const App = @import("App.zig");
const internal_os = @import("../../os/main.zig"); const internal_os = @import("../../os/main.zig");

View File

@ -0,0 +1,14 @@
// This file is used to provide compatibility if necessary with
// older versions of GTK and GLib.
#include <gtk/gtk.h>
// Compatibility with gobject < 2.74
#ifndef G_CONNECT_DEFAULT
#define G_CONNECT_DEFAULT 0
#endif
// Compatibility with gobject < 2.74
#ifndef G_APPLICATION_DEFAULT_FLAGS
#define G_APPLICATION_DEFAULT_FLAGS G_APPLICATION_FLAGS_NONE
#endif

View File

@ -6,7 +6,7 @@ const App = @import("App.zig");
const Surface = @import("Surface.zig"); const Surface = @import("Surface.zig");
const TerminalWindow = @import("Window.zig"); const TerminalWindow = @import("Window.zig");
const ImguiWidget = @import("ImguiWidget.zig"); const ImguiWidget = @import("ImguiWidget.zig");
const c = @import("c.zig"); const c = @import("c.zig").c;
const CoreInspector = @import("../../inspector/main.zig").Inspector; const CoreInspector = @import("../../inspector/main.zig").Inspector;
const log = std.log.scoped(.inspector); const log = std.log.scoped(.inspector);

View File

@ -1,6 +1,6 @@
const std = @import("std"); const std = @import("std");
const input = @import("../../input.zig"); const input = @import("../../input.zig");
const c = @import("c.zig"); const c = @import("c.zig").c;
/// Returns a GTK accelerator string from a trigger. /// Returns a GTK accelerator string from a trigger.
pub fn accelFromTrigger(buf: []u8, trigger: input.Binding.Trigger) !?[:0]const u8 { pub fn accelFromTrigger(buf: []u8, trigger: input.Binding.Trigger) !?[:0]const u8 {

View File

@ -1,6 +1,6 @@
/// Utility functions for X11 handling. /// Utility functions for X11 handling.
const std = @import("std"); const std = @import("std");
const c = @import("c.zig"); const c = @import("c.zig").c;
const input = @import("../../input.zig"); const input = @import("../../input.zig");
const log = std.log.scoped(.gtk_x11); const log = std.log.scoped(.gtk_x11);

View File

@ -5,7 +5,7 @@ const Action = @import("action.zig").Action;
const Arena = std.heap.ArenaAllocator; const Arena = std.heap.ArenaAllocator;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const Config = @import("../config/Config.zig"); const Config = @import("../config/Config.zig");
const global_state = &@import("../main.zig").state; const global_state = &@import("../global.zig").state;
pub const Options = struct { pub const Options = struct {
pub fn deinit(self: Options) void { pub fn deinit(self: Options) void {

View File

@ -1,12 +1,15 @@
const builtin = @import("builtin"); const builtin = @import("builtin");
pub usingnamespace @import("config/key.zig"); const formatter = @import("config/formatter.zig");
pub usingnamespace @import("config/formatter.zig");
pub const Config = @import("config/Config.zig"); pub const Config = @import("config/Config.zig");
pub const string = @import("config/string.zig"); pub const string = @import("config/string.zig");
pub const edit = @import("config/edit.zig"); pub const edit = @import("config/edit.zig");
pub const url = @import("config/url.zig"); pub const url = @import("config/url.zig");
pub const FileFormatter = formatter.FileFormatter;
pub const entryFormatter = formatter.entryFormatter;
pub const formatEntry = formatter.formatEntry;
// Field types // Field types
pub const ClipboardAccess = Config.ClipboardAccess; pub const ClipboardAccess = Config.ClipboardAccess;
pub const CopyOnSelect = Config.CopyOnSelect; pub const CopyOnSelect = Config.CopyOnSelect;

View File

@ -1,7 +1,7 @@
const std = @import("std"); const std = @import("std");
const cli = @import("../cli.zig"); const cli = @import("../cli.zig");
const inputpkg = @import("../input.zig"); const inputpkg = @import("../input.zig");
const global = &@import("../main.zig").state; const global = &@import("../global.zig").state;
const Config = @import("Config.zig"); const Config = @import("Config.zig");
const c_get = @import("c_get.zig"); const c_get = @import("c_get.zig");

View File

@ -15,7 +15,7 @@ const builtin = @import("builtin");
const assert = std.debug.assert; const assert = std.debug.assert;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const ArenaAllocator = std.heap.ArenaAllocator; const ArenaAllocator = std.heap.ArenaAllocator;
const global_state = &@import("../main.zig").state; const global_state = &@import("../global.zig").state;
const fontpkg = @import("../font/main.zig"); const fontpkg = @import("../font/main.zig");
const inputpkg = @import("../input.zig"); const inputpkg = @import("../input.zig");
const terminal = @import("../terminal/main.zig"); const terminal = @import("../terminal/main.zig");

View File

@ -300,9 +300,6 @@ pub fn clear(self: *Atlas) void {
/// The wasm-compatible API. This lacks documentation unless the API differs /// The wasm-compatible API. This lacks documentation unless the API differs
/// from the standard Zig API. To learn what a function does, just look one /// from the standard Zig API. To learn what a function does, just look one
/// level deeper to what Zig function is called and read the documentation there. /// level deeper to what Zig function is called and read the documentation there.
///
/// To export this from Zig, use `usingnamespace Wasm` in some top-level
/// space and it will be exported.
pub const Wasm = struct { pub const Wasm = struct {
// If you're copying this file (Atlas.zig) out to a separate project, // If you're copying this file (Atlas.zig) out to a separate project,
// just replace this with the allocator you want to use. // just replace this with the allocator you want to use.

View File

@ -2,6 +2,8 @@ const std = @import("std");
const builtin = @import("builtin"); const builtin = @import("builtin");
const build_config = @import("../build_config.zig"); const build_config = @import("../build_config.zig");
const library = @import("library.zig");
pub const Atlas = @import("Atlas.zig"); pub const Atlas = @import("Atlas.zig");
pub const discovery = @import("discovery.zig"); pub const discovery = @import("discovery.zig");
pub const face = @import("face.zig"); pub const face = @import("face.zig");
@ -23,15 +25,17 @@ pub const Sprite = sprite.Sprite;
pub const SpriteFace = sprite.Face; pub const SpriteFace = sprite.Face;
pub const Descriptor = discovery.Descriptor; pub const Descriptor = discovery.Descriptor;
pub const Discover = discovery.Discover; pub const Discover = discovery.Discover;
pub usingnamespace @import("library.zig"); pub const Library = library.Library;
/// If we're targeting wasm then we export some wasm APIs. // If we're targeting wasm then we export some wasm APIs.
pub usingnamespace if (builtin.target.isWasm()) struct { comptime {
pub usingnamespace Atlas.Wasm; if (builtin.target.isWasm()) {
pub usingnamespace DeferredFace.Wasm; _ = Atlas.Wasm;
pub usingnamespace face.web_canvas.Wasm; _ = DeferredFace.Wasm;
pub usingnamespace shape.web_canvas.Wasm; _ = face.web_canvas.Wasm;
} else struct {}; _ = shape.web_canvas.Wasm;
}
}
/// Build options /// Build options
pub const options: struct { pub const options: struct {

View File

@ -1,11 +1,13 @@
const builtin = @import("builtin"); const builtin = @import("builtin");
const options = @import("main.zig").options; const options = @import("main.zig").options;
const run = @import("shaper/run.zig");
pub const noop = @import("shaper/noop.zig"); pub const noop = @import("shaper/noop.zig");
pub const harfbuzz = @import("shaper/harfbuzz.zig"); pub const harfbuzz = @import("shaper/harfbuzz.zig");
pub const coretext = @import("shaper/coretext.zig"); pub const coretext = @import("shaper/coretext.zig");
pub const web_canvas = @import("shaper/web_canvas.zig"); pub const web_canvas = @import("shaper/web_canvas.zig");
pub usingnamespace @import("shaper/run.zig");
pub const Cache = @import("shaper/Cache.zig"); pub const Cache = @import("shaper/Cache.zig");
pub const TextRun = run.TextRun;
pub const RunIterator = run.RunIterator;
/// Shaper implementation for our compile options. /// Shaper implementation for our compile options.
pub const Shaper = switch (options.backend) { pub const Shaper = switch (options.backend) {

View File

@ -1,7 +1,11 @@
const std = @import("std"); const std = @import("std");
pub usingnamespace @import("sprite/canvas.zig"); const canvas = @import("sprite/canvas.zig");
pub const Face = @import("sprite/Face.zig"); pub const Face = @import("sprite/Face.zig");
pub const Box = canvas.Box;
pub const Canvas = canvas.Canvas;
pub const Color = canvas.Color;
/// Sprites are represented as special codepoints outside of the Unicode /// Sprites are represented as special codepoints outside of the Unicode
/// codepoint range. Unicode maxes out at U+10FFFF (21 bits), and we use the /// codepoint range. Unicode maxes out at U+10FFFF (21 bits), and we use the
/// high 11 bits to hide our special characters. /// high 11 bits to hide our special characters.

147
src/global.zig Normal file
View File

@ -0,0 +1,147 @@
const std = @import("std");
const builtin = @import("builtin");
const build_config = @import("build_config.zig");
const cli = @import("cli.zig");
const internal_os = @import("os/main.zig");
const fontconfig = @import("fontconfig");
const glslang = @import("glslang");
const harfbuzz = @import("harfbuzz");
const oni = @import("oniguruma");
const renderer = @import("renderer.zig");
const xev = @import("xev");
/// Global process state. This is initialized in main() for exe artifacts
/// and by ghostty_init() for lib artifacts. This should ONLY be used by
/// the C API. The Zig API should NOT use any global state and should
/// rely on allocators being passed in as parameters.
pub var state: GlobalState = undefined;
/// This represents the global process state. There should only
/// be one of these at any given moment. This is extracted into a dedicated
/// struct because it is reused by main and the static C lib.
pub const GlobalState = struct {
const GPA = std.heap.GeneralPurposeAllocator(.{});
gpa: ?GPA,
alloc: std.mem.Allocator,
action: ?cli.Action,
logging: Logging,
/// The app resources directory, equivalent to zig-out/share when we build
/// from source. This is null if we can't detect it.
resources_dir: ?[]const u8,
/// Where logging should go
pub const Logging = union(enum) {
disabled: void,
stderr: void,
};
/// Initialize the global state.
pub fn init(self: *GlobalState) !void {
// Initialize ourself to nothing so we don't have any extra state.
// IMPORTANT: this MUST be initialized before any log output because
// the log function uses the global state.
self.* = .{
.gpa = null,
.alloc = undefined,
.action = null,
.logging = .{ .stderr = {} },
.resources_dir = null,
};
errdefer self.deinit();
self.gpa = gpa: {
// Use the libc allocator if it is available because it is WAY
// faster than GPA. We only do this in release modes so that we
// can get easy memory leak detection in debug modes.
if (builtin.link_libc) {
if (switch (builtin.mode) {
.ReleaseSafe, .ReleaseFast => true,
// We also use it if we can detect we're running under
// Valgrind since Valgrind only instruments the C allocator
else => std.valgrind.runningOnValgrind() > 0,
}) break :gpa null;
}
break :gpa GPA{};
};
self.alloc = if (self.gpa) |*value|
value.allocator()
else if (builtin.link_libc)
std.heap.c_allocator
else
unreachable;
// We first try to parse any action that we may be executing.
self.action = try cli.Action.detectCLI(self.alloc);
// If we have an action executing, we disable logging by default
// since we write to stderr we don't want logs messing up our
// output.
if (self.action != null) self.logging = .{ .disabled = {} };
// For lib mode we always disable stderr logging by default.
if (comptime build_config.app_runtime == .none) {
self.logging = .{ .disabled = {} };
}
// I don't love the env var name but I don't have it in my heart
// to parse CLI args 3 times (once for actions, once for config,
// maybe once for logging) so for now this is an easy way to do
// this. Env vars are useful for logging too because they are
// easy to set.
if ((try internal_os.getenv(self.alloc, "GHOSTTY_LOG"))) |v| {
defer v.deinit(self.alloc);
if (v.value.len > 0) {
self.logging = .{ .stderr = {} };
}
}
// Output some debug information right away
std.log.info("ghostty version={s}", .{build_config.version_string});
std.log.info("ghostty build optimize={s}", .{build_config.mode_string});
std.log.info("runtime={}", .{build_config.app_runtime});
std.log.info("font_backend={}", .{build_config.font_backend});
if (comptime build_config.font_backend.hasHarfbuzz()) {
std.log.info("dependency harfbuzz={s}", .{harfbuzz.versionString()});
}
if (comptime build_config.font_backend.hasFontconfig()) {
std.log.info("dependency fontconfig={d}", .{fontconfig.version()});
}
std.log.info("renderer={}", .{renderer.Renderer});
std.log.info("libxev backend={}", .{xev.backend});
// First things first, we fix our file descriptors
internal_os.fixMaxFiles();
// We need to make sure the process locale is set properly. Locale
// affects a lot of behaviors in a shell.
try internal_os.ensureLocale(self.alloc);
// Initialize glslang for shader compilation
try glslang.init();
// Initialize oniguruma for regex
try oni.init(&.{oni.Encoding.utf8});
// Find our resources directory once for the app so every launch
// hereafter can use this cached value.
self.resources_dir = try internal_os.resourcesDir(self.alloc);
errdefer if (self.resources_dir) |dir| self.alloc.free(dir);
}
/// Cleans up the global state. This doesn't _need_ to be called but
/// doing so in dev modes will check for memory leaks.
pub fn deinit(self: *GlobalState) void {
if (self.resources_dir) |dir| self.alloc.free(dir);
if (self.gpa) |*value| {
// We want to ensure that we deinit the GPA because this is
// the point at which it will output if there were safety violations.
_ = value.deinit();
}
}
};

View File

@ -1,15 +1,26 @@
const std = @import("std"); const std = @import("std");
const builtin = @import("builtin"); const builtin = @import("builtin");
pub usingnamespace @import("input/mouse.zig"); const mouse = @import("input/mouse.zig");
pub usingnamespace @import("input/key.zig"); const key = @import("input/key.zig");
pub const function_keys = @import("input/function_keys.zig"); pub const function_keys = @import("input/function_keys.zig");
pub const keycodes = @import("input/keycodes.zig"); pub const keycodes = @import("input/keycodes.zig");
pub const kitty = @import("input/kitty.zig"); pub const kitty = @import("input/kitty.zig");
pub const ctrlOrSuper = key.ctrlOrSuper;
pub const Action = key.Action;
pub const Binding = @import("input/Binding.zig"); pub const Binding = @import("input/Binding.zig");
pub const Link = @import("input/Link.zig"); pub const Link = @import("input/Link.zig");
pub const Key = key.Key;
pub const KeyEncoder = @import("input/KeyEncoder.zig"); pub const KeyEncoder = @import("input/KeyEncoder.zig");
pub const KeyEvent = key.KeyEvent;
pub const InspectorMode = Binding.Action.InspectorMode; pub const InspectorMode = Binding.Action.InspectorMode;
pub const Mods = key.Mods;
pub const MouseButton = mouse.Button;
pub const MouseButtonState = mouse.ButtonState;
pub const MousePressureStage = mouse.PressureStage;
pub const ScrollMods = mouse.ScrollMods;
pub const SplitFocusDirection = Binding.Action.SplitFocusDirection; pub const SplitFocusDirection = Binding.Action.SplitFocusDirection;
pub const SplitResizeDirection = Binding.Action.SplitResizeDirection; pub const SplitResizeDirection = Binding.Action.SplitResizeDirection;

View File

@ -5,7 +5,7 @@ const std = @import("std");
/// This is backed by a c_int so we can use this as-is for our embedding API. /// This is backed by a c_int so we can use this as-is for our embedding API.
/// ///
/// IMPORTANT: Any changes here update include/ghostty.h /// IMPORTANT: Any changes here update include/ghostty.h
pub const MouseButtonState = enum(c_int) { pub const ButtonState = enum(c_int) {
release, release,
press, press,
}; };
@ -20,7 +20,7 @@ pub const MouseButtonState = enum(c_int) {
/// This is backed by a c_int so we can use this as-is for our embedding API. /// This is backed by a c_int so we can use this as-is for our embedding API.
/// ///
/// IMPORTANT: Any changes here update include/ghostty.h /// IMPORTANT: Any changes here update include/ghostty.h
pub const MouseButton = enum(c_int) { pub const Button = enum(c_int) {
const Self = @This(); const Self = @This();
/// The maximum value in this enum. This can be used to create a densely /// The maximum value in this enum. This can be used to create a densely
@ -53,7 +53,7 @@ pub const MouseButton = enum(c_int) {
/// This is used to handle "inertial scrolling" (i.e. flicking). /// This is used to handle "inertial scrolling" (i.e. flicking).
/// ///
/// https://developer.apple.com/documentation/appkit/nseventphase /// https://developer.apple.com/documentation/appkit/nseventphase
pub const MouseMomentum = enum(u3) { pub const Momentum = enum(u3) {
none = 0, none = 0,
began = 1, began = 1,
stationary = 2, stationary = 2,
@ -66,7 +66,7 @@ pub const MouseMomentum = enum(u3) {
/// The pressure stage of a pressure-sensitive input device. /// The pressure stage of a pressure-sensitive input device.
/// ///
/// This currently only supports the stages that macOS supports. /// This currently only supports the stages that macOS supports.
pub const MousePressureStage = enum(u2) { pub const PressureStage = enum(u2) {
/// The input device is unpressed. /// The input device is unpressed.
none = 0, none = 0,
@ -88,7 +88,7 @@ pub const ScrollMods = packed struct(u8) {
/// The momentum phase (if available, supported) of the scroll event. /// The momentum phase (if available, supported) of the scroll event.
/// This is used to handle "inertial scrolling" (i.e. flicking). /// This is used to handle "inertial scrolling" (i.e. flicking).
momentum: MouseMomentum = .none, momentum: Momentum = .none,
_padding: u4 = 0, _padding: u4 = 0,

View File

@ -1,7 +1,7 @@
const build_config = @import("build_config.zig"); const build_config = @import("build_config.zig");
// See build_config.ExeEntrypoint for why we do this. /// See build_config.ExeEntrypoint for why we do this.
pub usingnamespace switch (build_config.exe_entrypoint) { const entrypoint = switch (build_config.exe_entrypoint) {
.ghostty => @import("main_ghostty.zig"), .ghostty => @import("main_ghostty.zig"),
.helpgen => @import("helpgen.zig"), .helpgen => @import("helpgen.zig"),
.mdgen_ghostty_1 => @import("build/mdgen/main_ghostty_1.zig"), .mdgen_ghostty_1 => @import("build/mdgen/main_ghostty_1.zig"),
@ -12,3 +12,10 @@ pub usingnamespace switch (build_config.exe_entrypoint) {
.bench_grapheme_break => @import("bench/grapheme-break.zig"), .bench_grapheme_break => @import("bench/grapheme-break.zig"),
.bench_page_init => @import("bench/page-init.zig"), .bench_page_init => @import("bench/page-init.zig"),
}; };
/// The main entrypoint for the program.
pub const main = entrypoint.main;
test {
_ = entrypoint;
}

View File

@ -12,7 +12,8 @@ const assert = std.debug.assert;
const posix = std.posix; const posix = std.posix;
const builtin = @import("builtin"); const builtin = @import("builtin");
const build_config = @import("build_config.zig"); const build_config = @import("build_config.zig");
const main = @import("main.zig"); const main = @import("main_ghostty.zig");
const state = &@import("global.zig").state;
const apprt = @import("apprt.zig"); const apprt = @import("apprt.zig");
// Some comptime assertions that our C API depends on. // Some comptime assertions that our C API depends on.
@ -23,8 +24,12 @@ comptime {
/// Global options so we can log. This is identical to main. /// Global options so we can log. This is identical to main.
pub const std_options = main.std_options; pub const std_options = main.std_options;
pub usingnamespace @import("config.zig").CAPI; comptime {
pub usingnamespace apprt.runtime.CAPI; // These structs need to be referenced so the `export` functions
// are truly exported by the C API lib.
_ = @import("config.zig").CAPI;
_ = apprt.runtime.CAPI;
}
/// ghostty_info_s /// ghostty_info_s
const Info = extern struct { const Info = extern struct {
@ -51,7 +56,7 @@ export fn ghostty_init() c_int {
var argv: [0][*:0]u8 = .{}; var argv: [0][*:0]u8 = .{};
std.os.argv = &argv; std.os.argv = &argv;
main.state.init() catch |err| { state.init() catch |err| {
std.log.err("failed to initialize ghostty error={}", .{err}); std.log.err("failed to initialize ghostty error={}", .{err});
return 1; return 1;
}; };

View File

@ -21,12 +21,7 @@ const apprt = @import("apprt.zig");
const App = @import("App.zig"); const App = @import("App.zig");
const Ghostty = @import("main_c.zig").Ghostty; const Ghostty = @import("main_c.zig").Ghostty;
const state = &@import("global.zig").state;
/// Global process state. This is initialized in main() for exe artifacts
/// and by ghostty_init() for lib artifacts. This should ONLY be used by
/// the C API. The Zig API should NOT use any global state and should
/// rely on allocators being passed in as parameters.
pub var state: GlobalState = undefined;
/// The return type for main() depends on the build artifact. The lib build /// The return type for main() depends on the build artifact. The lib build
/// also calls "main" in order to run the CLI actions, but it calls it as /// also calls "main" in order to run the CLI actions, but it calls it as
@ -172,136 +167,6 @@ pub const std_options: std.Options = .{
.logFn = logFn, .logFn = logFn,
}; };
/// This represents the global process state. There should only
/// be one of these at any given moment. This is extracted into a dedicated
/// struct because it is reused by main and the static C lib.
pub const GlobalState = struct {
const GPA = std.heap.GeneralPurposeAllocator(.{});
gpa: ?GPA,
alloc: std.mem.Allocator,
action: ?cli.Action,
logging: Logging,
/// The app resources directory, equivalent to zig-out/share when we build
/// from source. This is null if we can't detect it.
resources_dir: ?[]const u8,
/// Where logging should go
pub const Logging = union(enum) {
disabled: void,
stderr: void,
};
/// Initialize the global state.
pub fn init(self: *GlobalState) !void {
// Initialize ourself to nothing so we don't have any extra state.
// IMPORTANT: this MUST be initialized before any log output because
// the log function uses the global state.
self.* = .{
.gpa = null,
.alloc = undefined,
.action = null,
.logging = .{ .stderr = {} },
.resources_dir = null,
};
errdefer self.deinit();
self.gpa = gpa: {
// Use the libc allocator if it is available because it is WAY
// faster than GPA. We only do this in release modes so that we
// can get easy memory leak detection in debug modes.
if (builtin.link_libc) {
if (switch (builtin.mode) {
.ReleaseSafe, .ReleaseFast => true,
// We also use it if we can detect we're running under
// Valgrind since Valgrind only instruments the C allocator
else => std.valgrind.runningOnValgrind() > 0,
}) break :gpa null;
}
break :gpa GPA{};
};
self.alloc = if (self.gpa) |*value|
value.allocator()
else if (builtin.link_libc)
std.heap.c_allocator
else
unreachable;
// We first try to parse any action that we may be executing.
self.action = try cli.Action.detectCLI(self.alloc);
// If we have an action executing, we disable logging by default
// since we write to stderr we don't want logs messing up our
// output.
if (self.action != null) self.logging = .{ .disabled = {} };
// For lib mode we always disable stderr logging by default.
if (comptime build_config.app_runtime == .none) {
self.logging = .{ .disabled = {} };
}
// I don't love the env var name but I don't have it in my heart
// to parse CLI args 3 times (once for actions, once for config,
// maybe once for logging) so for now this is an easy way to do
// this. Env vars are useful for logging too because they are
// easy to set.
if ((try internal_os.getenv(self.alloc, "GHOSTTY_LOG"))) |v| {
defer v.deinit(self.alloc);
if (v.value.len > 0) {
self.logging = .{ .stderr = {} };
}
}
// Output some debug information right away
std.log.info("ghostty version={s}", .{build_config.version_string});
std.log.info("ghostty build optimize={s}", .{build_config.mode_string});
std.log.info("runtime={}", .{build_config.app_runtime});
std.log.info("font_backend={}", .{build_config.font_backend});
if (comptime build_config.font_backend.hasHarfbuzz()) {
std.log.info("dependency harfbuzz={s}", .{harfbuzz.versionString()});
}
if (comptime build_config.font_backend.hasFontconfig()) {
std.log.info("dependency fontconfig={d}", .{fontconfig.version()});
}
std.log.info("renderer={}", .{renderer.Renderer});
std.log.info("libxev backend={}", .{xev.backend});
// First things first, we fix our file descriptors
internal_os.fixMaxFiles();
// We need to make sure the process locale is set properly. Locale
// affects a lot of behaviors in a shell.
try internal_os.ensureLocale(self.alloc);
// Initialize glslang for shader compilation
try glslang.init();
// Initialize oniguruma for regex
try oni.init(&.{oni.Encoding.utf8});
// Find our resources directory once for the app so every launch
// hereafter can use this cached value.
self.resources_dir = try internal_os.resourcesDir(self.alloc);
errdefer if (self.resources_dir) |dir| self.alloc.free(dir);
}
/// Cleans up the global state. This doesn't _need_ to be called but
/// doing so in dev modes will check for memory leaks.
pub fn deinit(self: *GlobalState) void {
if (self.resources_dir) |dir| self.alloc.free(dir);
if (self.gpa) |*value| {
// We want to ensure that we deinit the GPA because this is
// the point at which it will output if there were safety violations.
_ = value.deinit();
}
}
};
test { test {
_ = @import("circ_buf.zig"); _ = @import("circ_buf.zig");
_ = @import("pty.zig"); _ = @import("pty.zig");

View File

@ -3,11 +3,13 @@
const std = @import("std"); const std = @import("std");
const builtin = @import("builtin"); const builtin = @import("builtin");
pub usingnamespace @import("os/wasm.zig"); comptime {
pub usingnamespace @import("font/main.zig"); _ = @import("os/wasm.zig");
pub usingnamespace @import("terminal/main.zig"); _ = @import("font/main.zig");
pub usingnamespace @import("config.zig").Wasm; _ = @import("terminal/main.zig");
pub usingnamespace @import("App.zig").Wasm; _ = @import("config.zig").Wasm;
_ = @import("App.zig").Wasm;
}
pub const std_options = struct { pub const std_options = struct {
// Set our log level. We try to get as much logging as possible but in // Set our log level. We try to get as much logging as possible but in

View File

@ -2,20 +2,42 @@
//! system. These aren't restricted to syscalls or low-level operations, but //! system. These aren't restricted to syscalls or low-level operations, but
//! also OS-specific features and conventions. //! also OS-specific features and conventions.
pub usingnamespace @import("env.zig"); const desktop = @import("desktop.zig");
pub usingnamespace @import("desktop.zig"); const env = @import("env.zig");
pub usingnamespace @import("file.zig"); const file = @import("file.zig");
pub usingnamespace @import("flatpak.zig"); const flatpak = @import("flatpak.zig");
pub usingnamespace @import("homedir.zig"); const homedir = @import("homedir.zig");
pub usingnamespace @import("locale.zig"); const locale = @import("locale.zig");
pub usingnamespace @import("macos_version.zig"); const macos_version = @import("macos_version.zig");
pub usingnamespace @import("mouse.zig"); const mouse = @import("mouse.zig");
pub usingnamespace @import("open.zig"); const openpkg = @import("open.zig");
pub usingnamespace @import("pipe.zig"); const pipepkg = @import("pipe.zig");
pub usingnamespace @import("resourcesdir.zig"); const resourcesdir = @import("resourcesdir.zig");
pub const CFReleaseThread = @import("cf_release_thread.zig");
pub const TempDir = @import("TempDir.zig"); // Namespaces
pub const cgroup = @import("cgroup.zig"); pub const cgroup = @import("cgroup.zig");
pub const passwd = @import("passwd.zig"); pub const passwd = @import("passwd.zig");
pub const xdg = @import("xdg.zig"); pub const xdg = @import("xdg.zig");
pub const windows = @import("windows.zig"); pub const windows = @import("windows.zig");
// Functions and types
pub const CFReleaseThread = @import("cf_release_thread.zig");
pub const TempDir = @import("TempDir.zig");
pub const PATH_SEP = env.PATH_SEP;
pub const appendEnv = env.appendEnv;
pub const appendEnvAlways = env.appendEnvAlways;
pub const getenv = env.getenv;
pub const setenv = env.setenv;
pub const unsetenv = env.unsetenv;
pub const launchedFromDesktop = desktop.launchedFromDesktop;
pub const fixMaxFiles = file.fixMaxFiles;
pub const allocTmpDir = file.allocTmpDir;
pub const freeTmpDir = file.freeTmpDir;
pub const isFlatpak = flatpak.isFlatpak;
pub const home = homedir.home;
pub const ensureLocale = locale.ensureLocale;
pub const macosVersionAtLeast = macos_version.macosVersionAtLeast;
pub const clickInterval = mouse.clickInterval;
pub const open = openpkg.open;
pub const pipe = pipepkg.pipe;
pub const resourcesDir = resourcesdir.resourcesDir;

View File

@ -4,9 +4,9 @@ const wasm = @import("../wasm.zig");
const wasm_target = @import("target.zig"); const wasm_target = @import("target.zig");
// Use the correct implementation // Use the correct implementation
pub usingnamespace if (wasm_target.target) |target| switch (target) { pub const log = if (wasm_target.target) |target| switch (target) {
.browser => Browser, .browser => Browser.log,
} else struct {}; } else @compileError("wasm target required");
/// Browser implementation calls an extern "log" function. /// Browser implementation calls an extern "log" function.
pub const Browser = struct { pub const Browser = struct {

View File

@ -1,7 +1,33 @@
const std = @import("std"); const std = @import("std");
const windows = std.os.windows; const windows = std.os.windows;
pub usingnamespace std.os.windows; // Export any constants or functions we need from the Windows API so
// we can just import one file.
pub const kernel32 = windows.kernel32;
pub const unexpectedError = windows.unexpectedError;
pub const OpenFile = windows.OpenFile;
pub const SetHandleInformation = windows.SetHandleInformation;
pub const DWORD = windows.DWORD;
pub const FILE_ATTRIBUTE_NORMAL = windows.FILE_ATTRIBUTE_NORMAL;
pub const FILE_FLAG_OVERLAPPED = windows.FILE_FLAG_OVERLAPPED;
pub const FILE_SHARE_READ = windows.FILE_SHARE_READ;
pub const GENERIC_READ = windows.GENERIC_READ;
pub const HANDLE = windows.HANDLE;
pub const HANDLE_FLAG_INHERIT = windows.HANDLE_FLAG_INHERIT;
pub const INFINITE = windows.INFINITE;
pub const INVALID_HANDLE_VALUE = windows.INVALID_HANDLE_VALUE;
pub const OPEN_EXISTING = windows.OPEN_EXISTING;
pub const PIPE_ACCESS_OUTBOUND = windows.PIPE_ACCESS_OUTBOUND;
pub const PIPE_TYPE_BYTE = windows.PIPE_TYPE_BYTE;
pub const PROCESS_INFORMATION = windows.PROCESS_INFORMATION;
pub const S_OK = windows.S_OK;
pub const SECURITY_ATTRIBUTES = windows.SECURITY_ATTRIBUTES;
pub const STARTUPINFOW = windows.STARTUPINFOW;
pub const STARTF_USESTDHANDLES = windows.STARTF_USESTDHANDLES;
pub const SYNCHRONIZE = windows.SYNCHRONIZE;
pub const WAIT_FAILED = windows.WAIT_FAILED;
pub const FALSE = windows.FALSE;
pub const TRUE = windows.TRUE;
pub const exp = struct { pub const exp = struct {
pub const HPCON = windows.LPVOID; pub const HPCON = windows.LPVOID;

View File

@ -63,17 +63,15 @@ const PosixPty = struct {
const TIOCSWINSZ = if (builtin.os.tag == .macos) 2148037735 else c.TIOCSWINSZ; const TIOCSWINSZ = if (builtin.os.tag == .macos) 2148037735 else c.TIOCSWINSZ;
const TIOCGWINSZ = if (builtin.os.tag == .macos) 1074295912 else c.TIOCGWINSZ; const TIOCGWINSZ = if (builtin.os.tag == .macos) 1074295912 else c.TIOCGWINSZ;
extern "c" fn setsid() std.c.pid_t; extern "c" fn setsid() std.c.pid_t;
const c = struct { const c = switch (builtin.os.tag) {
usingnamespace switch (builtin.os.tag) { .macos => @cImport({
.macos => @cImport({ @cInclude("sys/ioctl.h"); // ioctl and constants
@cInclude("sys/ioctl.h"); // ioctl and constants @cInclude("util.h"); // openpty()
@cInclude("util.h"); // openpty() }),
}), else => @cImport({
else => @cImport({ @cInclude("sys/ioctl.h"); // ioctl and constants
@cInclude("sys/ioctl.h"); // ioctl and constants @cInclude("pty.h");
@cInclude("pty.h"); }),
}),
};
}; };
/// The file descriptors for the master and slave side of the pty. /// The file descriptors for the master and slave side of the pty.

View File

@ -12,9 +12,9 @@ const builtin = @import("builtin");
const build_config = @import("build_config.zig"); const build_config = @import("build_config.zig");
const WasmTarget = @import("os/wasm/target.zig").Target; const WasmTarget = @import("os/wasm/target.zig").Target;
pub usingnamespace @import("renderer/cursor.zig"); const cursor = @import("renderer/cursor.zig");
pub usingnamespace @import("renderer/message.zig"); const message = @import("renderer/message.zig");
pub usingnamespace @import("renderer/size.zig"); const size = @import("renderer/size.zig");
pub const shadertoy = @import("renderer/shadertoy.zig"); pub const shadertoy = @import("renderer/shadertoy.zig");
pub const Metal = @import("renderer/Metal.zig"); pub const Metal = @import("renderer/Metal.zig");
pub const OpenGL = @import("renderer/OpenGL.zig"); pub const OpenGL = @import("renderer/OpenGL.zig");
@ -22,6 +22,13 @@ pub const WebGL = @import("renderer/WebGL.zig");
pub const Options = @import("renderer/Options.zig"); pub const Options = @import("renderer/Options.zig");
pub const Thread = @import("renderer/Thread.zig"); pub const Thread = @import("renderer/Thread.zig");
pub const State = @import("renderer/State.zig"); pub const State = @import("renderer/State.zig");
pub const CursorStyle = cursor.Style;
pub const Message = message.Message;
pub const CellSize = size.CellSize;
pub const ScreenSize = size.ScreenSize;
pub const GridSize = size.GridSize;
pub const Padding = size.Padding;
pub const cursorStyle = cursor.style;
/// Possible implementations, used for build options. /// Possible implementations, used for build options.
pub const Impl = enum { pub const Impl = enum {

View File

@ -5,15 +5,15 @@ const State = @import("State.zig");
/// Available cursor styles for drawing that renderers must support. /// Available cursor styles for drawing that renderers must support.
/// This is a superset of terminal cursor styles since the renderer supports /// This is a superset of terminal cursor styles since the renderer supports
/// some additional cursor states such as the hollow block. /// some additional cursor states such as the hollow block.
pub const CursorStyle = enum { pub const Style = enum {
block, block,
block_hollow, block_hollow,
bar, bar,
underline, underline,
/// Create a cursor style from the terminal style request. /// Create a cursor style from the terminal style request.
pub fn fromTerminal(style: terminal.CursorStyle) ?CursorStyle { pub fn fromTerminal(term: terminal.CursorStyle) ?Style {
return switch (style) { return switch (term) {
.bar => .bar, .bar => .bar,
.block => .block, .block => .block,
.underline => .underline, .underline => .underline,
@ -23,11 +23,11 @@ pub const CursorStyle = enum {
/// Returns the cursor style to use for the current render state or null /// Returns the cursor style to use for the current render state or null
/// if a cursor should not be rendered at all. /// if a cursor should not be rendered at all.
pub fn cursorStyle( pub fn style(
state: *State, state: *State,
focused: bool, focused: bool,
blink_visible: bool, blink_visible: bool,
) ?CursorStyle { ) ?Style {
// Note the order of conditionals below is important. It represents // Note the order of conditionals below is important. It represents
// a priority system of how we determine what state overrides cursor // a priority system of how we determine what state overrides cursor
// visibility and style. // visibility and style.
@ -57,7 +57,7 @@ pub fn cursorStyle(
} }
// Otherwise, we use whatever style the terminal wants. // Otherwise, we use whatever style the terminal wants.
return CursorStyle.fromTerminal(state.terminal.screen.cursor.cursor_style); return Style.fromTerminal(state.terminal.screen.cursor.cursor_style);
} }
test "cursor: default uses configured style" { test "cursor: default uses configured style" {
@ -75,10 +75,10 @@ test "cursor: default uses configured style" {
.preedit = null, .preedit = null,
}; };
try testing.expect(cursorStyle(&state, true, true) == .bar); try testing.expect(style(&state, true, true) == .bar);
try testing.expect(cursorStyle(&state, false, true) == .block_hollow); try testing.expect(style(&state, false, true) == .block_hollow);
try testing.expect(cursorStyle(&state, false, false) == .block_hollow); try testing.expect(style(&state, false, false) == .block_hollow);
try testing.expect(cursorStyle(&state, true, false) == null); try testing.expect(style(&state, true, false) == null);
} }
test "cursor: blinking disabled" { test "cursor: blinking disabled" {
@ -96,10 +96,10 @@ test "cursor: blinking disabled" {
.preedit = null, .preedit = null,
}; };
try testing.expect(cursorStyle(&state, true, true) == .bar); try testing.expect(style(&state, true, true) == .bar);
try testing.expect(cursorStyle(&state, true, false) == .bar); try testing.expect(style(&state, true, false) == .bar);
try testing.expect(cursorStyle(&state, false, true) == .block_hollow); try testing.expect(style(&state, false, true) == .block_hollow);
try testing.expect(cursorStyle(&state, false, false) == .block_hollow); try testing.expect(style(&state, false, false) == .block_hollow);
} }
test "cursor: explicitly not visible" { test "cursor: explicitly not visible" {
@ -118,10 +118,10 @@ test "cursor: explicitly not visible" {
.preedit = null, .preedit = null,
}; };
try testing.expect(cursorStyle(&state, true, true) == null); try testing.expect(style(&state, true, true) == null);
try testing.expect(cursorStyle(&state, true, false) == null); try testing.expect(style(&state, true, false) == null);
try testing.expect(cursorStyle(&state, false, true) == null); try testing.expect(style(&state, false, true) == null);
try testing.expect(cursorStyle(&state, false, false) == null); try testing.expect(style(&state, false, false) == null);
} }
test "cursor: always block with preedit" { test "cursor: always block with preedit" {
@ -137,18 +137,18 @@ test "cursor: always block with preedit" {
}; };
// In any bool state // In any bool state
try testing.expect(cursorStyle(&state, false, false) == .block); try testing.expect(style(&state, false, false) == .block);
try testing.expect(cursorStyle(&state, true, false) == .block); try testing.expect(style(&state, true, false) == .block);
try testing.expect(cursorStyle(&state, true, true) == .block); try testing.expect(style(&state, true, true) == .block);
try testing.expect(cursorStyle(&state, false, true) == .block); try testing.expect(style(&state, false, true) == .block);
// If we're scrolled though, then we don't show the cursor. // If we're scrolled though, then we don't show the cursor.
for (0..100) |_| try term.index(); for (0..100) |_| try term.index();
try term.scrollViewport(.{ .top = {} }); try term.scrollViewport(.{ .top = {} });
// In any bool state // In any bool state
try testing.expect(cursorStyle(&state, false, false) == null); try testing.expect(style(&state, false, false) == null);
try testing.expect(cursorStyle(&state, true, false) == null); try testing.expect(style(&state, true, false) == null);
try testing.expect(cursorStyle(&state, true, true) == null); try testing.expect(style(&state, true, true) == null);
try testing.expect(cursorStyle(&state, false, true) == null); try testing.expect(style(&state, false, true) == null);
} }

View File

@ -1,9 +1,10 @@
const std = @import("std"); const std = @import("std");
pub usingnamespace @import("codepoint_width.zig"); const codepoint_width = @import("codepoint_width.zig");
pub const base64 = @import("base64.zig"); pub const base64 = @import("base64.zig");
pub const index_of = @import("index_of.zig"); pub const index_of = @import("index_of.zig");
pub const vt = @import("vt.zig"); pub const vt = @import("vt.zig");
pub const codepointWidth = codepoint_width.codepointWidth;
test { test {
@import("std").testing.refAllDecls(@This()); @import("std").testing.refAllDecls(@This());

View File

@ -1,4 +1,10 @@
pub usingnamespace @cImport({ const c = @cImport({
@cInclude("stb_image.h"); @cInclude("stb_image.h");
@cInclude("stb_image_resize.h"); @cInclude("stb_image_resize.h");
}); });
// We'll just add the exports of the functions or types we actually use
// here, no need to export everything from the C lib if we don't use it.
pub const stbi_load_from_memory = c.stbi_load_from_memory;
pub const stbi_image_free = c.stbi_image_free;
pub const stbir_resize_uint8 = c.stbir_resize_uint8;

View File

@ -1,7 +1,11 @@
//! Types and functions related to Kitty protocols. //! Types and functions related to Kitty protocols.
const key = @import("kitty/key.zig");
pub const graphics = @import("kitty/graphics.zig"); pub const graphics = @import("kitty/graphics.zig");
pub usingnamespace @import("kitty/key.zig");
pub const KeyFlags = key.Flags;
pub const KeyFlagStack = key.FlagStack;
pub const KeySetMode = key.SetMode;
test { test {
@import("std").testing.refAllDecls(@This()); @import("std").testing.refAllDecls(@This());

View File

@ -17,12 +17,19 @@
//! though and I think we can go back through and fix this up. //! though and I think we can go back through and fix this up.
const render = @import("graphics_render.zig"); const render = @import("graphics_render.zig");
pub usingnamespace @import("graphics_command.zig"); const command = @import("graphics_command.zig");
pub usingnamespace @import("graphics_exec.zig"); const exec = @import("graphics_exec.zig");
pub usingnamespace @import("graphics_image.zig"); const image = @import("graphics_image.zig");
pub usingnamespace @import("graphics_storage.zig"); const storage = @import("graphics_storage.zig");
pub const unicode = @import("graphics_unicode.zig"); pub const unicode = @import("graphics_unicode.zig");
pub const Command = command.Command;
pub const CommandParser = command.Parser;
pub const Image = image.Image;
pub const ImageStorage = storage.ImageStorage;
pub const RenderPlacement = render.Placement; pub const RenderPlacement = render.Placement;
pub const Response = command.Response;
pub const execute = exec.execute;
test { test {
@import("std").testing.refAllDecls(@This()); @import("std").testing.refAllDecls(@This());

View File

@ -15,7 +15,7 @@ const log = std.log.scoped(.kitty_gfx);
const KV = std.AutoHashMapUnmanaged(u8, u32); const KV = std.AutoHashMapUnmanaged(u8, u32);
/// Command parser parses the Kitty graphics protocol escape sequence. /// Command parser parses the Kitty graphics protocol escape sequence.
pub const CommandParser = struct { pub const Parser = struct {
/// The memory used by the parser is stored in an arena because it is /// The memory used by the parser is stored in an arena because it is
/// all freed at the end of the command. /// all freed at the end of the command.
arena: ArenaAllocator, arena: ArenaAllocator,
@ -54,7 +54,7 @@ pub const CommandParser = struct {
/// Initialize the parser. The allocator given will be used for both /// Initialize the parser. The allocator given will be used for both
/// temporary data and long-lived values such as the final image blob. /// temporary data and long-lived values such as the final image blob.
pub fn init(alloc: Allocator) CommandParser { pub fn init(alloc: Allocator) Parser {
var arena = ArenaAllocator.init(alloc); var arena = ArenaAllocator.init(alloc);
errdefer arena.deinit(); errdefer arena.deinit();
return .{ return .{
@ -63,7 +63,7 @@ pub const CommandParser = struct {
}; };
} }
pub fn deinit(self: *CommandParser) void { pub fn deinit(self: *Parser) void {
// We don't free the hash map because its in the arena // We don't free the hash map because its in the arena
self.arena.deinit(); self.arena.deinit();
self.data.deinit(); self.data.deinit();
@ -74,7 +74,7 @@ pub const CommandParser = struct {
/// The first byte to start parsing should be the byte immediately following /// The first byte to start parsing should be the byte immediately following
/// the "G" in the APC sequence, i.e. "\x1b_G123" the first byte should /// the "G" in the APC sequence, i.e. "\x1b_G123" the first byte should
/// be "1". /// be "1".
pub fn feed(self: *CommandParser, c: u8) !void { pub fn feed(self: *Parser, c: u8) !void {
switch (self.state) { switch (self.state) {
.control_key => switch (c) { .control_key => switch (c) {
// '=' means the key is complete and we're moving to the value. // '=' means the key is complete and we're moving to the value.
@ -119,7 +119,7 @@ pub const CommandParser = struct {
/// ///
/// The allocator given will be used for the long-lived data /// The allocator given will be used for the long-lived data
/// of the final command. /// of the final command.
pub fn complete(self: *CommandParser) !Command { pub fn complete(self: *Parser) !Command {
switch (self.state) { switch (self.state) {
// We can't ever end in the control key state and be valid. // We can't ever end in the control key state and be valid.
// This means the command looked something like "a=1,b" // This means the command looked something like "a=1,b"
@ -175,7 +175,7 @@ pub const CommandParser = struct {
/// Decodes the payload data from base64 and returns it as a slice. /// Decodes the payload data from base64 and returns it as a slice.
/// This function will destroy the contents of self.data, it should /// This function will destroy the contents of self.data, it should
/// only be used once we are done collecting payload bytes. /// only be used once we are done collecting payload bytes.
fn decodeData(self: *CommandParser) ![]const u8 { fn decodeData(self: *Parser) ![]const u8 {
if (self.data.items.len == 0) { if (self.data.items.len == 0) {
return ""; return "";
} }
@ -202,7 +202,7 @@ pub const CommandParser = struct {
return try self.data.toOwnedSlice(); return try self.data.toOwnedSlice();
} }
fn accumulateValue(self: *CommandParser, c: u8, overflow_state: State) !void { fn accumulateValue(self: *Parser, c: u8, overflow_state: State) !void {
const idx = self.kv_temp_len; const idx = self.kv_temp_len;
self.kv_temp_len += 1; self.kv_temp_len += 1;
if (self.kv_temp_len > self.kv_temp.len) { if (self.kv_temp_len > self.kv_temp.len) {
@ -213,7 +213,7 @@ pub const CommandParser = struct {
self.kv_temp[idx] = c; self.kv_temp[idx] = c;
} }
fn finishValue(self: *CommandParser, next_state: State) !void { fn finishValue(self: *Parser, next_state: State) !void {
const alloc = self.arena.allocator(); const alloc = self.arena.allocator();
// We can move states right away, we don't use it. // We can move states right away, we don't use it.
@ -896,7 +896,7 @@ pub const CompositionMode = enum {
test "transmission command" { test "transmission command" {
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; const alloc = testing.allocator;
var p = CommandParser.init(alloc); var p = Parser.init(alloc);
defer p.deinit(); defer p.deinit();
const input = "f=24,s=10,v=20"; const input = "f=24,s=10,v=20";
@ -914,7 +914,7 @@ test "transmission command" {
test "query command" { test "query command" {
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; const alloc = testing.allocator;
var p = CommandParser.init(alloc); var p = Parser.init(alloc);
defer p.deinit(); defer p.deinit();
const input = "i=31,s=1,v=1,a=q,t=d,f=24;QUFBQQ"; const input = "i=31,s=1,v=1,a=q,t=d,f=24;QUFBQQ";
@ -934,7 +934,7 @@ test "query command" {
test "display command" { test "display command" {
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; const alloc = testing.allocator;
var p = CommandParser.init(alloc); var p = Parser.init(alloc);
defer p.deinit(); defer p.deinit();
const input = "a=p,U=1,i=31,c=80,r=120"; const input = "a=p,U=1,i=31,c=80,r=120";
@ -952,7 +952,7 @@ test "display command" {
test "delete command" { test "delete command" {
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; const alloc = testing.allocator;
var p = CommandParser.init(alloc); var p = Parser.init(alloc);
defer p.deinit(); defer p.deinit();
const input = "a=d,d=p,x=3,y=4"; const input = "a=d,d=p,x=3,y=4";
@ -972,7 +972,7 @@ test "delete command" {
test "ignore unknown keys (long)" { test "ignore unknown keys (long)" {
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; const alloc = testing.allocator;
var p = CommandParser.init(alloc); var p = Parser.init(alloc);
defer p.deinit(); defer p.deinit();
const input = "f=24,s=10,v=20,hello=world"; const input = "f=24,s=10,v=20,hello=world";
@ -990,7 +990,7 @@ test "ignore unknown keys (long)" {
test "ignore very long values" { test "ignore very long values" {
const testing = std.testing; const testing = std.testing;
const alloc = testing.allocator; const alloc = testing.allocator;
var p = CommandParser.init(alloc); var p = Parser.init(alloc);
defer p.deinit(); defer p.deinit();
const input = "f=24,s=10,v=2000000000000000000000000000000000000000"; const input = "f=24,s=10,v=2000000000000000000000000000000000000000";

View File

@ -5,23 +5,23 @@ const std = @import("std");
/// Stack for the key flags. This implements the push/pop behavior /// Stack for the key flags. This implements the push/pop behavior
/// of the CSI > u and CSI < u sequences. We implement the stack as /// of the CSI > u and CSI < u sequences. We implement the stack as
/// fixed size to avoid heap allocation. /// fixed size to avoid heap allocation.
pub const KeyFlagStack = struct { pub const FlagStack = struct {
const len = 8; const len = 8;
flags: [len]KeyFlags = .{.{}} ** len, flags: [len]Flags = .{.{}} ** len,
idx: u3 = 0, idx: u3 = 0,
/// Return the current stack value /// Return the current stack value
pub fn current(self: KeyFlagStack) KeyFlags { pub fn current(self: FlagStack) Flags {
return self.flags[self.idx]; return self.flags[self.idx];
} }
/// Perform the "set" operation as described in the spec for /// Perform the "set" operation as described in the spec for
/// the CSI = u sequence. /// the CSI = u sequence.
pub fn set( pub fn set(
self: *KeyFlagStack, self: *FlagStack,
mode: KeySetMode, mode: SetMode,
v: KeyFlags, v: Flags,
) void { ) void {
switch (mode) { switch (mode) {
.set => self.flags[self.idx] = v, .set => self.flags[self.idx] = v,
@ -36,7 +36,7 @@ pub const KeyFlagStack = struct {
/// Push a new set of flags onto the stack. If the stack is full /// Push a new set of flags onto the stack. If the stack is full
/// then the oldest entry is evicted. /// then the oldest entry is evicted.
pub fn push(self: *KeyFlagStack, flags: KeyFlags) void { pub fn push(self: *FlagStack, flags: Flags) void {
// Overflow and wrap around if we're full, which evicts // Overflow and wrap around if we're full, which evicts
// the oldest entry. // the oldest entry.
self.idx +%= 1; self.idx +%= 1;
@ -45,7 +45,7 @@ pub const KeyFlagStack = struct {
/// Pop `n` entries from the stack. This will just wrap around /// Pop `n` entries from the stack. This will just wrap around
/// if `n` is greater than the amount in the stack. /// if `n` is greater than the amount in the stack.
pub fn pop(self: *KeyFlagStack, n: usize) void { pub fn pop(self: *FlagStack, n: usize) void {
// If n is more than our length then we just reset the stack. // If n is more than our length then we just reset the stack.
// This also avoids a DoS vector where a malicious client // This also avoids a DoS vector where a malicious client
// could send a huge number of pop commands to waste cpu. // could send a huge number of pop commands to waste cpu.
@ -64,7 +64,7 @@ pub const KeyFlagStack = struct {
// Make sure we the overflow works as expected // Make sure we the overflow works as expected
test { test {
const testing = std.testing; const testing = std.testing;
var stack: KeyFlagStack = .{}; var stack: FlagStack = .{};
stack.idx = stack.flags.len - 1; stack.idx = stack.flags.len - 1;
stack.idx +%= 1; stack.idx +%= 1;
try testing.expect(stack.idx == 0); try testing.expect(stack.idx == 0);
@ -76,14 +76,14 @@ pub const KeyFlagStack = struct {
}; };
/// The possible flags for the Kitty keyboard protocol. /// The possible flags for the Kitty keyboard protocol.
pub const KeyFlags = packed struct(u5) { pub const Flags = packed struct(u5) {
disambiguate: bool = false, disambiguate: bool = false,
report_events: bool = false, report_events: bool = false,
report_alternates: bool = false, report_alternates: bool = false,
report_all: bool = false, report_all: bool = false,
report_associated: bool = false, report_associated: bool = false,
pub fn int(self: KeyFlags) u5 { pub fn int(self: Flags) u5 {
return @bitCast(self); return @bitCast(self);
} }
@ -93,50 +93,50 @@ pub const KeyFlags = packed struct(u5) {
try testing.expectEqual( try testing.expectEqual(
@as(u5, 0b1), @as(u5, 0b1),
(KeyFlags{ .disambiguate = true }).int(), (Flags{ .disambiguate = true }).int(),
); );
try testing.expectEqual( try testing.expectEqual(
@as(u5, 0b10), @as(u5, 0b10),
(KeyFlags{ .report_events = true }).int(), (Flags{ .report_events = true }).int(),
); );
} }
}; };
/// The possible modes for setting the key flags. /// The possible modes for setting the key flags.
pub const KeySetMode = enum { set, @"or", not }; pub const SetMode = enum { set, @"or", not };
test "KeyFlagStack: push pop" { test "FlagStack: push pop" {
const testing = std.testing; const testing = std.testing;
var stack: KeyFlagStack = .{}; var stack: FlagStack = .{};
stack.push(.{ .disambiguate = true }); stack.push(.{ .disambiguate = true });
try testing.expectEqual( try testing.expectEqual(
KeyFlags{ .disambiguate = true }, Flags{ .disambiguate = true },
stack.current(), stack.current(),
); );
stack.pop(1); stack.pop(1);
try testing.expectEqual(KeyFlags{}, stack.current()); try testing.expectEqual(Flags{}, stack.current());
} }
test "KeyFlagStack: pop big number" { test "FlagStack: pop big number" {
const testing = std.testing; const testing = std.testing;
var stack: KeyFlagStack = .{}; var stack: FlagStack = .{};
stack.pop(100); stack.pop(100);
try testing.expectEqual(KeyFlags{}, stack.current()); try testing.expectEqual(Flags{}, stack.current());
} }
test "KeyFlagStack: set" { test "FlagStack: set" {
const testing = std.testing; const testing = std.testing;
var stack: KeyFlagStack = .{}; var stack: FlagStack = .{};
stack.set(.set, .{ .disambiguate = true }); stack.set(.set, .{ .disambiguate = true });
try testing.expectEqual( try testing.expectEqual(
KeyFlags{ .disambiguate = true }, Flags{ .disambiguate = true },
stack.current(), stack.current(),
); );
stack.set(.@"or", .{ .report_events = true }); stack.set(.@"or", .{ .report_events = true });
try testing.expectEqual( try testing.expectEqual(
KeyFlags{ Flags{
.disambiguate = true, .disambiguate = true,
.report_events = true, .report_events = true,
}, },
@ -145,7 +145,7 @@ test "KeyFlagStack: set" {
stack.set(.not, .{ .report_events = true }); stack.set(.not, .{ .report_events = true });
try testing.expectEqual( try testing.expectEqual(
KeyFlags{ .disambiguate = true }, Flags{ .disambiguate = true },
stack.current(), stack.current(),
); );
} }

View File

@ -1,8 +1,7 @@
const builtin = @import("builtin"); const builtin = @import("builtin");
pub usingnamespace @import("sanitize.zig");
const charsets = @import("charsets.zig"); const charsets = @import("charsets.zig");
const sanitize = @import("sanitize.zig");
const stream = @import("stream.zig"); const stream = @import("stream.zig");
const ansi = @import("ansi.zig"); const ansi = @import("ansi.zig");
const csi = @import("csi.zig"); const csi = @import("csi.zig");
@ -57,6 +56,8 @@ pub const EraseLine = csi.EraseLine;
pub const TabClear = csi.TabClear; pub const TabClear = csi.TabClear;
pub const Attribute = sgr.Attribute; pub const Attribute = sgr.Attribute;
pub const isSafePaste = sanitize.isSafePaste;
test { test {
@import("std").testing.refAllDecls(@This()); @import("std").testing.refAllDecls(@This());

View File

@ -19,7 +19,7 @@
const stream_handler = @import("termio/stream_handler.zig"); const stream_handler = @import("termio/stream_handler.zig");
pub usingnamespace @import("termio/message.zig"); const message = @import("termio/message.zig");
pub const backend = @import("termio/backend.zig"); pub const backend = @import("termio/backend.zig");
pub const mailbox = @import("termio/mailbox.zig"); pub const mailbox = @import("termio/mailbox.zig");
pub const Exec = @import("termio/Exec.zig"); pub const Exec = @import("termio/Exec.zig");
@ -29,6 +29,8 @@ pub const Thread = @import("termio/Thread.zig");
pub const Backend = backend.Backend; pub const Backend = backend.Backend;
pub const DerivedConfig = Termio.DerivedConfig; pub const DerivedConfig = Termio.DerivedConfig;
pub const Mailbox = mailbox.Mailbox; pub const Mailbox = mailbox.Mailbox;
pub const Message = message.Message;
pub const MessageData = message.MessageData;
pub const StreamHandler = stream_handler.StreamHandler; pub const StreamHandler = stream_handler.StreamHandler;
test { test {