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.
|
//! with the terminal.
|
||||||
|
|
||||||
pub usingnamespace @import("termio/message.zig");
|
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 Options = @import("termio/Options.zig");
|
||||||
pub const Termio = @import("termio/Termio.zig");
|
pub const Termio = @import("termio/Termio.zig");
|
||||||
pub const Thread = @import("termio/Thread.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 {
|
fn threadMain_(self: *Thread) !void {
|
||||||
defer log.debug("IO thread exited", .{});
|
defer log.debug("IO thread exited", .{});
|
||||||
|
|
||||||
// Start the async handlers. We start these first so that they're
|
// This is the data sent to xev callbacks. We want a pointer to both
|
||||||
// registered even if anything below fails so we can drain the mailbox.
|
// ourselves and the thread data so we can thread that through (pun intended).
|
||||||
self.wakeup.wait(&self.loop, &self.wakeup_c, Thread, self, wakeupCallback);
|
var cb: CallbackData = .{ .self = self };
|
||||||
self.stop.wait(&self.loop, &self.stop_c, Thread, self, stopCallback);
|
|
||||||
|
|
||||||
// Run our thread start/end callbacks. This allows the implementation
|
// Run our thread start/end callbacks. This allows the implementation
|
||||||
// to hook into the event loop as needed.
|
// to hook into the event loop as needed. The thread data is created
|
||||||
var data = try self.termio.threadEnter(self);
|
// on the stack here so that it has a stable pointer throughout the
|
||||||
defer data.deinit();
|
// lifetime of the thread.
|
||||||
defer self.termio.threadExit(data);
|
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
|
// Run
|
||||||
log.debug("starting IO thread", .{});
|
log.debug("starting IO thread", .{});
|
||||||
@ -236,8 +241,14 @@ fn threadMain_(self: *Thread) !void {
|
|||||||
try self.loop.run(.until_done);
|
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.
|
/// 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 we're draining, we just drain the mailbox and return.
|
||||||
if (self.flags.drain) {
|
if (self.flags.drain) {
|
||||||
while (self.mailbox.pop()) |_| {}
|
while (self.mailbox.pop()) |_| {}
|
||||||
@ -256,21 +267,33 @@ fn drainMailbox(self: *Thread) !void {
|
|||||||
switch (message) {
|
switch (message) {
|
||||||
.change_config => |config| {
|
.change_config => |config| {
|
||||||
defer config.alloc.destroy(config.ptr);
|
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,
|
.inspector => |v| self.flags.has_inspector = v,
|
||||||
.resize => |v| self.handleResize(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),
|
.scroll_viewport => |v| try self.termio.scrollViewport(v),
|
||||||
.jump_to_prompt => |v| try self.termio.jumpToPrompt(v),
|
.jump_to_prompt => |v| try self.termio.jumpToPrompt(v),
|
||||||
.start_synchronized_output => self.startSynchronizedOutput(),
|
.start_synchronized_output => self.startSynchronizedOutput(),
|
||||||
.linefeed_mode => |v| self.flags.linefeed_mode = v,
|
.linefeed_mode => |v| self.flags.linefeed_mode = v,
|
||||||
.child_exited_abnormally => |v| try self.termio.childExitedAbnormally(v.exit_code, v.runtime_ms),
|
.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_small => |v| try self.termio.queueWrite(
|
||||||
.write_stable => |v| try self.termio.queueWrite(v, self.flags.linefeed_mode),
|
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| {
|
.write_alloc => |v| {
|
||||||
defer v.alloc.free(v.data);
|
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(
|
fn wakeupCallback(
|
||||||
self_: ?*Thread,
|
cb_: ?*CallbackData,
|
||||||
_: *xev.Loop,
|
_: *xev.Loop,
|
||||||
_: *xev.Completion,
|
_: *xev.Completion,
|
||||||
r: xev.Async.WaitError!void,
|
r: xev.Async.WaitError!void,
|
||||||
@ -369,23 +392,23 @@ fn wakeupCallback(
|
|||||||
return .rearm;
|
return .rearm;
|
||||||
};
|
};
|
||||||
|
|
||||||
const t = self_.?;
|
const cb = cb_ orelse return .rearm;
|
||||||
|
|
||||||
// When we wake up, we check the mailbox. Mailbox producers should
|
// When we wake up, we check the mailbox. Mailbox producers should
|
||||||
// wake up our thread after publishing.
|
// wake up our thread after publishing.
|
||||||
t.drainMailbox() catch |err|
|
cb.self.drainMailbox(&cb.data) catch |err|
|
||||||
log.err("error draining mailbox err={}", .{err});
|
log.err("error draining mailbox err={}", .{err});
|
||||||
|
|
||||||
return .rearm;
|
return .rearm;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stopCallback(
|
fn stopCallback(
|
||||||
self_: ?*Thread,
|
cb_: ?*CallbackData,
|
||||||
_: *xev.Loop,
|
_: *xev.Loop,
|
||||||
_: *xev.Completion,
|
_: *xev.Completion,
|
||||||
r: xev.Async.WaitError!void,
|
r: xev.Async.WaitError!void,
|
||||||
) xev.CallbackAction {
|
) xev.CallbackAction {
|
||||||
_ = r catch unreachable;
|
_ = r catch unreachable;
|
||||||
self_.?.loop.stop();
|
cb_.?.self.loop.stop();
|
||||||
return .disarm;
|
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