mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 00:06:09 +03:00
termio: wip but it builds
This commit is contained in:
@ -3,7 +3,7 @@
|
||||
//! with the terminal.
|
||||
|
||||
pub usingnamespace @import("termio/message.zig");
|
||||
pub const Exec = @import("termio/Exec.zig");
|
||||
pub const reader = @import("termio/reader.zig");
|
||||
pub const Options = @import("termio/Options.zig");
|
||||
pub const Termio = @import("termio/Termio.zig");
|
||||
pub const Thread = @import("termio/Thread.zig");
|
||||
|
2965
src/termio/Old.zig
Normal file
2965
src/termio/Old.zig
Normal file
File diff suppressed because it is too large
Load Diff
2987
src/termio/Termio.zig
Normal file
2987
src/termio/Termio.zig
Normal file
File diff suppressed because it is too large
Load Diff
@ -219,16 +219,21 @@ pub fn threadMain(self: *Thread) void {
|
||||
fn threadMain_(self: *Thread) !void {
|
||||
defer log.debug("IO thread exited", .{});
|
||||
|
||||
// Start the async handlers. We start these first so that they're
|
||||
// registered even if anything below fails so we can drain the mailbox.
|
||||
self.wakeup.wait(&self.loop, &self.wakeup_c, Thread, self, wakeupCallback);
|
||||
self.stop.wait(&self.loop, &self.stop_c, Thread, self, stopCallback);
|
||||
// This is the data sent to xev callbacks. We want a pointer to both
|
||||
// ourselves and the thread data so we can thread that through (pun intended).
|
||||
var cb: CallbackData = .{ .self = self };
|
||||
|
||||
// Run our thread start/end callbacks. This allows the implementation
|
||||
// to hook into the event loop as needed.
|
||||
var data = try self.termio.threadEnter(self);
|
||||
defer data.deinit();
|
||||
defer self.termio.threadExit(data);
|
||||
// to hook into the event loop as needed. The thread data is created
|
||||
// on the stack here so that it has a stable pointer throughout the
|
||||
// lifetime of the thread.
|
||||
try self.termio.threadEnter(self, &cb.data);
|
||||
defer cb.data.deinit();
|
||||
defer self.termio.threadExit(&cb.data);
|
||||
|
||||
// Start the async handlers.
|
||||
self.wakeup.wait(&self.loop, &self.wakeup_c, CallbackData, &cb, wakeupCallback);
|
||||
self.stop.wait(&self.loop, &self.stop_c, CallbackData, &cb, stopCallback);
|
||||
|
||||
// Run
|
||||
log.debug("starting IO thread", .{});
|
||||
@ -236,8 +241,14 @@ fn threadMain_(self: *Thread) !void {
|
||||
try self.loop.run(.until_done);
|
||||
}
|
||||
|
||||
/// This is the data passed to xev callbacks on the thread.
|
||||
const CallbackData = struct {
|
||||
self: *Thread,
|
||||
data: termio.Termio.ThreadData = undefined,
|
||||
};
|
||||
|
||||
/// Drain the mailbox, handling all the messages in our terminal implementation.
|
||||
fn drainMailbox(self: *Thread) !void {
|
||||
fn drainMailbox(self: *Thread, data: *termio.Termio.ThreadData) !void {
|
||||
// If we're draining, we just drain the mailbox and return.
|
||||
if (self.flags.drain) {
|
||||
while (self.mailbox.pop()) |_| {}
|
||||
@ -256,21 +267,33 @@ fn drainMailbox(self: *Thread) !void {
|
||||
switch (message) {
|
||||
.change_config => |config| {
|
||||
defer config.alloc.destroy(config.ptr);
|
||||
try self.termio.changeConfig(config.ptr);
|
||||
try self.termio.changeConfig(data, config.ptr);
|
||||
},
|
||||
.inspector => |v| self.flags.has_inspector = v,
|
||||
.resize => |v| self.handleResize(v),
|
||||
.clear_screen => |v| try self.termio.clearScreen(v.history),
|
||||
.clear_screen => |v| try self.termio.clearScreen(data, v.history),
|
||||
.scroll_viewport => |v| try self.termio.scrollViewport(v),
|
||||
.jump_to_prompt => |v| try self.termio.jumpToPrompt(v),
|
||||
.start_synchronized_output => self.startSynchronizedOutput(),
|
||||
.linefeed_mode => |v| self.flags.linefeed_mode = v,
|
||||
.child_exited_abnormally => |v| try self.termio.childExitedAbnormally(v.exit_code, v.runtime_ms),
|
||||
.write_small => |v| try self.termio.queueWrite(v.data[0..v.len], self.flags.linefeed_mode),
|
||||
.write_stable => |v| try self.termio.queueWrite(v, self.flags.linefeed_mode),
|
||||
.write_small => |v| try self.termio.queueWrite(
|
||||
data,
|
||||
v.data[0..v.len],
|
||||
self.flags.linefeed_mode,
|
||||
),
|
||||
.write_stable => |v| try self.termio.queueWrite(
|
||||
data,
|
||||
v,
|
||||
self.flags.linefeed_mode,
|
||||
),
|
||||
.write_alloc => |v| {
|
||||
defer v.alloc.free(v.data);
|
||||
try self.termio.queueWrite(v.data, self.flags.linefeed_mode);
|
||||
try self.termio.queueWrite(
|
||||
data,
|
||||
v.data,
|
||||
self.flags.linefeed_mode,
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -359,7 +382,7 @@ fn coalesceCallback(
|
||||
}
|
||||
|
||||
fn wakeupCallback(
|
||||
self_: ?*Thread,
|
||||
cb_: ?*CallbackData,
|
||||
_: *xev.Loop,
|
||||
_: *xev.Completion,
|
||||
r: xev.Async.WaitError!void,
|
||||
@ -369,23 +392,23 @@ fn wakeupCallback(
|
||||
return .rearm;
|
||||
};
|
||||
|
||||
const t = self_.?;
|
||||
const cb = cb_ orelse return .rearm;
|
||||
|
||||
// When we wake up, we check the mailbox. Mailbox producers should
|
||||
// wake up our thread after publishing.
|
||||
t.drainMailbox() catch |err|
|
||||
cb.self.drainMailbox(&cb.data) catch |err|
|
||||
log.err("error draining mailbox err={}", .{err});
|
||||
|
||||
return .rearm;
|
||||
}
|
||||
|
||||
fn stopCallback(
|
||||
self_: ?*Thread,
|
||||
cb_: ?*CallbackData,
|
||||
_: *xev.Loop,
|
||||
_: *xev.Completion,
|
||||
r: xev.Async.WaitError!void,
|
||||
) xev.CallbackAction {
|
||||
_ = r catch unreachable;
|
||||
self_.?.loop.stop();
|
||||
cb_.?.self.loop.stop();
|
||||
return .disarm;
|
||||
}
|
||||
|
48
src/termio/reader.zig
Normal file
48
src/termio/reader.zig
Normal file
@ -0,0 +1,48 @@
|
||||
const std = @import("std");
|
||||
const configpkg = @import("../config.zig");
|
||||
const Command = @import("../Command.zig");
|
||||
|
||||
/// The kinds of readers.
|
||||
pub const Kind = std.meta.Tag(Config);
|
||||
|
||||
/// Configuration for the various reader types.
|
||||
pub const Config = union(enum) {
|
||||
/// Manual means that the termio caller will handle reading input
|
||||
/// and passing it to the termio implementation. Note that even if you
|
||||
/// select a different reader, you can always still manually provide input;
|
||||
/// this config just makes it so that it is ONLY manual input.
|
||||
manual: void,
|
||||
|
||||
/// Exec uses posix exec to run a command with a pty.
|
||||
exec: Exec,
|
||||
|
||||
pub const Exec = struct {
|
||||
command: ?[]const u8 = null,
|
||||
shell_integration: configpkg.Config.ShellIntegration = .detect,
|
||||
shell_integration_features: configpkg.Config.ShellIntegrationFeatures = .{},
|
||||
working_directory: ?[]const u8 = null,
|
||||
linux_cgroup: Command.LinuxCgroup = Command.linux_cgroup_default,
|
||||
};
|
||||
};
|
||||
|
||||
/// Termio thread data. See termio.ThreadData for docs.
|
||||
pub const ThreadData = union(Kind) {
|
||||
manual: void,
|
||||
|
||||
exec: struct {
|
||||
/// Process start time and boolean of whether its already exited.
|
||||
start: std.time.Instant,
|
||||
exited: bool = false,
|
||||
|
||||
/// The number of milliseconds below which we consider a process
|
||||
/// exit to be abnormal. This is used to show an error message
|
||||
/// when the process exits too quickly.
|
||||
abnormal_runtime_threshold_ms: u32,
|
||||
|
||||
/// If true, do not immediately send a child exited message to the
|
||||
/// surface to close the surface when the command exits. If this is
|
||||
/// false we'll show a process exited message and wait for user input
|
||||
/// to close the surface.
|
||||
wait_after_command: bool,
|
||||
},
|
||||
};
|
Reference in New Issue
Block a user