mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 00:06:09 +03:00
hook up the real subprocess reader/writer to the UI!
This commit is contained in:
@ -261,6 +261,7 @@ pub fn updateCells(self: *Grid, term: Terminal) !void {
|
|||||||
// we have plus a full width. This is very likely too much but its
|
// we have plus a full width. This is very likely too much but its
|
||||||
// the probably close enough while guaranteeing no more allocations.
|
// the probably close enough while guaranteeing no more allocations.
|
||||||
self.cells.clearRetainingCapacity();
|
self.cells.clearRetainingCapacity();
|
||||||
|
if (term.screen.items.len == 0) return;
|
||||||
try self.cells.ensureTotalCapacity(
|
try self.cells.ensureTotalCapacity(
|
||||||
self.alloc,
|
self.alloc,
|
||||||
term.screen.items.len * term.cols,
|
term.screen.items.len * term.cols,
|
||||||
|
119
src/Window.zig
119
src/Window.zig
@ -16,9 +16,14 @@ const libuv = @import("libuv/main.zig");
|
|||||||
const Pty = @import("Pty.zig");
|
const Pty = @import("Pty.zig");
|
||||||
const Command = @import("Command.zig");
|
const Command = @import("Command.zig");
|
||||||
const Terminal = @import("terminal/Terminal.zig");
|
const Terminal = @import("terminal/Terminal.zig");
|
||||||
|
const SegmentedPool = @import("segmented_pool.zig").SegmentedPool;
|
||||||
|
|
||||||
const log = std.log.scoped(.window);
|
const log = std.log.scoped(.window);
|
||||||
|
|
||||||
|
// The preallocation size for the write request pool. This should be big
|
||||||
|
// enough to satisfy most write requests. It must be a power of 2.
|
||||||
|
const WRITE_REQ_PREALLOC = std.math.pow(usize, 2, 5);
|
||||||
|
|
||||||
/// Allocator
|
/// Allocator
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
|
|
||||||
@ -43,8 +48,16 @@ terminal: Terminal,
|
|||||||
/// Timer that blinks the cursor.
|
/// Timer that blinks the cursor.
|
||||||
cursor_timer: libuv.Timer,
|
cursor_timer: libuv.Timer,
|
||||||
|
|
||||||
/// The reader stream for the pty.
|
/// The reader/writer stream for the pty.
|
||||||
pty_reader: libuv.Tty,
|
pty_stream: libuv.Tty,
|
||||||
|
|
||||||
|
/// This is the pool of available (unused) write requests. If you grab
|
||||||
|
/// one from the pool, you must put it back when you're done!
|
||||||
|
write_req_pool: SegmentedPool(libuv.WriteReq.T, WRITE_REQ_PREALLOC) = .{},
|
||||||
|
|
||||||
|
/// The pool of available buffers for writing to the pty.
|
||||||
|
/// TODO: [1]u8 is probably not right.
|
||||||
|
buf_pool: SegmentedPool([1]u8, WRITE_REQ_PREALLOC) = .{},
|
||||||
|
|
||||||
/// Set this to true whenver an event occurs that we may want to wake up
|
/// Set this to true whenver an event occurs that we may want to wake up
|
||||||
/// the event loop. Only set this from the main thread.
|
/// the event loop. Only set this from the main thread.
|
||||||
@ -142,14 +155,14 @@ pub fn create(alloc: Allocator, loop: libuv.Loop) !*Window {
|
|||||||
log.debug("started subcommand path={s} pid={}", .{ path, cmd.pid });
|
log.debug("started subcommand path={s} pid={}", .{ path, cmd.pid });
|
||||||
|
|
||||||
// Read data
|
// Read data
|
||||||
var reader = try libuv.Tty.init(alloc, loop, pty.master);
|
var stream = try libuv.Tty.init(alloc, loop, pty.master);
|
||||||
errdefer reader.deinit(alloc);
|
errdefer stream.deinit(alloc);
|
||||||
try reader.readStart(ttyReadAlloc, ttyRead);
|
stream.setData(self);
|
||||||
|
try stream.readStart(ttyReadAlloc, ttyRead);
|
||||||
|
|
||||||
// Create our terminal
|
// Create our terminal
|
||||||
var term = Terminal.init(grid.size.columns, grid.size.rows);
|
var term = Terminal.init(grid.size.columns, grid.size.rows);
|
||||||
errdefer term.deinit(alloc);
|
errdefer term.deinit(alloc);
|
||||||
try term.append(alloc, "> ");
|
|
||||||
|
|
||||||
// Setup a timer for blinking the cursor
|
// Setup a timer for blinking the cursor
|
||||||
var timer = try libuv.Timer.init(alloc, loop);
|
var timer = try libuv.Timer.init(alloc, loop);
|
||||||
@ -166,7 +179,7 @@ pub fn create(alloc: Allocator, loop: libuv.Loop) !*Window {
|
|||||||
.command = cmd,
|
.command = cmd,
|
||||||
.terminal = term,
|
.terminal = term,
|
||||||
.cursor_timer = timer,
|
.cursor_timer = timer,
|
||||||
.pty_reader = reader,
|
.pty_stream = stream,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Setup our callbacks and user data
|
// Setup our callbacks and user data
|
||||||
@ -180,21 +193,6 @@ pub fn create(alloc: Allocator, loop: libuv.Loop) !*Window {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn destroy(self: *Window) void {
|
pub fn destroy(self: *Window) void {
|
||||||
self.cursor_timer.close((struct {
|
|
||||||
fn callback(t: *libuv.Timer) void {
|
|
||||||
const alloc = t.loop().getData(Allocator).?.*;
|
|
||||||
t.deinit(alloc);
|
|
||||||
}
|
|
||||||
}).callback);
|
|
||||||
|
|
||||||
self.pty_reader.readStop();
|
|
||||||
self.pty_reader.close((struct {
|
|
||||||
fn callback(t: *libuv.Tty) void {
|
|
||||||
const alloc = t.loop().getData(Allocator).?.*;
|
|
||||||
t.deinit(alloc);
|
|
||||||
}
|
|
||||||
}).callback);
|
|
||||||
|
|
||||||
// Deinitialize the pty. This closes the pty handles. This should
|
// Deinitialize the pty. This closes the pty handles. This should
|
||||||
// cause a close in the our subprocess so just wait for that.
|
// cause a close in the our subprocess so just wait for that.
|
||||||
self.pty.deinit();
|
self.pty.deinit();
|
||||||
@ -204,7 +202,28 @@ pub fn destroy(self: *Window) void {
|
|||||||
self.terminal.deinit(self.alloc);
|
self.terminal.deinit(self.alloc);
|
||||||
self.grid.deinit();
|
self.grid.deinit();
|
||||||
self.window.destroy();
|
self.window.destroy();
|
||||||
self.alloc.destroy(self);
|
|
||||||
|
self.cursor_timer.close((struct {
|
||||||
|
fn callback(t: *libuv.Timer) void {
|
||||||
|
const alloc = t.loop().getData(Allocator).?.*;
|
||||||
|
t.deinit(alloc);
|
||||||
|
}
|
||||||
|
}).callback);
|
||||||
|
|
||||||
|
// We have to dealloc our window in the close callback because
|
||||||
|
// we can't free some of the memory associated with the window
|
||||||
|
// until the stream is closed.
|
||||||
|
self.pty_stream.readStop();
|
||||||
|
self.pty_stream.close((struct {
|
||||||
|
fn callback(t: *libuv.Tty) void {
|
||||||
|
const win = t.getData(Window).?;
|
||||||
|
const alloc = win.alloc;
|
||||||
|
t.deinit(alloc);
|
||||||
|
win.write_req_pool.deinit(alloc);
|
||||||
|
win.buf_pool.deinit(alloc);
|
||||||
|
win.alloc.destroy(win);
|
||||||
|
}
|
||||||
|
}).callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn shouldClose(self: Window) bool {
|
pub fn shouldClose(self: Window) bool {
|
||||||
@ -262,18 +281,15 @@ fn sizeCallback(window: glfw.Window, width: i32, height: i32) void {
|
|||||||
fn charCallback(window: glfw.Window, codepoint: u21) void {
|
fn charCallback(window: glfw.Window, codepoint: u21) void {
|
||||||
const win = window.getUserPointer(Window) orelse return;
|
const win = window.getUserPointer(Window) orelse return;
|
||||||
|
|
||||||
// Append this character to the terminal
|
// Write the character to the pty
|
||||||
win.terminal.appendChar(win.alloc, @intCast(u8, codepoint)) catch unreachable;
|
const req = win.write_req_pool.get() catch unreachable;
|
||||||
|
const buf = win.buf_pool.get() catch unreachable;
|
||||||
// Whenever a character is typed, we ensure the cursor is visible
|
buf[0] = @intCast(u8, codepoint);
|
||||||
// and we restart the cursor timer.
|
win.pty_stream.write(
|
||||||
win.grid.cursor_visible = true;
|
.{ .req = req },
|
||||||
if (win.cursor_timer.isActive() catch false) {
|
&[1][]u8{buf[0..1]},
|
||||||
_ = win.cursor_timer.again() catch null;
|
ttyWrite,
|
||||||
}
|
) catch unreachable;
|
||||||
|
|
||||||
// Update the cells for drawing
|
|
||||||
win.grid.updateCells(win.terminal) catch unreachable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn keyCallback(
|
fn keyCallback(
|
||||||
@ -321,9 +337,34 @@ fn ttyReadAlloc(t: *libuv.Tty, size: usize) ?[]u8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn ttyRead(t: *libuv.Tty, n: isize, buf: []const u8) void {
|
fn ttyRead(t: *libuv.Tty, n: isize, buf: []const u8) void {
|
||||||
const alloc = t.loop().getData(Allocator).?.*;
|
const win = t.getData(Window).?;
|
||||||
defer alloc.free(buf);
|
defer win.alloc.free(buf);
|
||||||
|
|
||||||
// TODO: actually handle this
|
// log.info("DATA: {s}", .{buf[0..@intCast(usize, n)]});
|
||||||
log.info("DATA: {s}", .{buf[0..@intCast(usize, n)]});
|
|
||||||
|
win.terminal.append(win.alloc, buf[0..@intCast(usize, n)]) catch |err|
|
||||||
|
log.err("error writing terminal data: {}", .{err});
|
||||||
|
win.grid.updateCells(win.terminal) catch unreachable;
|
||||||
|
|
||||||
|
// Whenever a character is typed, we ensure the cursor is visible
|
||||||
|
// and we restart the cursor timer.
|
||||||
|
win.grid.cursor_visible = true;
|
||||||
|
if (win.cursor_timer.isActive() catch false) {
|
||||||
|
_ = win.cursor_timer.again() catch null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the cells for drawing
|
||||||
|
win.grid.updateCells(win.terminal) catch unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ttyWrite(req: *libuv.WriteReq, status: i32) void {
|
||||||
|
const tty = req.handle(libuv.Tty).?;
|
||||||
|
const win = tty.getData(Window).?;
|
||||||
|
win.write_req_pool.put();
|
||||||
|
win.buf_pool.put();
|
||||||
|
|
||||||
|
libuv.convertError(status) catch |err|
|
||||||
|
log.err("write error: {}", .{err});
|
||||||
|
|
||||||
|
//log.info("WROTE: {d}", .{status});
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,12 @@ pub const Timer = @import("Timer.zig");
|
|||||||
pub const Tty = @import("Tty.zig");
|
pub const Tty = @import("Tty.zig");
|
||||||
pub const Sem = @import("Sem.zig");
|
pub const Sem = @import("Sem.zig");
|
||||||
pub const Thread = @import("Thread.zig");
|
pub const Thread = @import("Thread.zig");
|
||||||
pub const Error = @import("error.zig").Error;
|
|
||||||
pub const WriteReq = stream.WriteReq;
|
pub const WriteReq = stream.WriteReq;
|
||||||
|
|
||||||
pub const Embed = @import("Embed.zig");
|
pub const Embed = @import("Embed.zig");
|
||||||
|
|
||||||
|
pub usingnamespace @import("error.zig");
|
||||||
|
|
||||||
test {
|
test {
|
||||||
_ = @import("tests.zig");
|
_ = @import("tests.zig");
|
||||||
_ = stream;
|
_ = stream;
|
||||||
@ -23,7 +24,6 @@ test {
|
|||||||
_ = Tty;
|
_ = Tty;
|
||||||
_ = Sem;
|
_ = Sem;
|
||||||
_ = Thread;
|
_ = Thread;
|
||||||
_ = Error;
|
|
||||||
|
|
||||||
_ = Embed;
|
_ = Embed;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user