Something went wrong. Try again.
A GPU terminal emulator for Wayland and Windows on libghostty-vt, drawing with Vulkan, running a program on a pty or a serial port.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142// SPDX-FileCopyrightText: © 2026 Jeffrey C. Ollie <jeff@ocjtech.us>// SPDX-License-Identifier: MIT
//! What is on the other side of the terminal: a program on a pseudo-terminal,//! a serial port, or a message saying why there is neither.//!//! Everything above this — the session, its IO thread, the window — sees a//! byte stream with something to wait on and a size it can be told about,//! and does not care which. A tagged union rather than a vtable, because the//! set is closed and known at compile time.//!//! What is waited on differs. On Linux it is one file descriptor, polled for//! being readable, or writable. On Windows, where nothing waits for a handle//! to become writable, writes are made whole, and reads are waited for on//! the handles `waitHandles` gives — or, for a backend with nothing to wait//! on, looked for every `pollInterval` milliseconds. The pseudo-terminal//! is a pty on Linux and a pseudoconsole on Windows, under one name.
const std = @import("std");const builtin = @import("builtin");
pub const Handle = @import("../os/handle.zig").Handle;
pub const Pty = switch (builtin.os.tag) { .windows => @import("ConPty.zig"), else => @import("Pty.zig"),};pub const Serial = @import("Serial.zig");pub const Message = @import("Message.zig");
/// The most handles `waitHandles` gives.pub const max_wait_handles = 2;
pub const Backend = union(enum) { pty: Pty, serial: Serial, message: Message,
/// What an event loop polls for readability, on Linux. pub fn handle(b: *const Backend) Handle { return switch (b.*) { inline else => |*v| v.handle(), }; }
/// What the IO thread waits on to be signaled before reading, on /// Windows; none, for a backend that is polled instead. pub fn waitHandles(b: *const Backend, buf: *[max_wait_handles]Handle) []const Handle { return switch (b.*) { inline else => |*v| v.waitHandles(buf), }; }
/// How often, in milliseconds, to look for something to read from a /// backend with nothing to wait on; null for one with handles. pub fn pollInterval(b: *const Backend) ?u32 { return switch (b.*) { .serial => Serial.poll_interval_ms, else => null, }; }
/// Reads what has arrived. Zero means the other side is gone for good: /// the program exited, or the port's device went away. pub fn read(b: *Backend, buf: []u8) ReadError!usize { return switch (b.*) { inline else => |*v| v.read(buf), }; }
pub fn write(b: *Backend, bytes: []const u8) WriteError!usize { return switch (b.*) { inline else => |*v| v.write(bytes), }; }
/// Tells the other side the grid changed size. A serial port has no /// way to be told, so this does nothing there. pub fn resize(b: *Backend, size: Size) void { switch (b.*) { inline else => |*v| v.resize(size), } }
/// A line for the window title before the program sets one. pub fn describe(b: *const Backend, w: *std.Io.Writer) std.Io.Writer.Error!void { return switch (b.*) { inline else => |*v| v.describe(w), }; }
pub fn deinit(b: *Backend) void { switch (b.*) { inline else => |*v| v.deinit(), } b.* = undefined; }};
/// How a program ended.pub const ExitStatus = union(enum) { /// It exited, with this status. exited: u32, /// A signal killed it. signaled: u32, /// Neither, as far as can be told. other,
pub fn successful(s: ExitStatus) bool { return s == .exited and s.exited == 0; }};
/// The grid's size, in cells and in pixels.pub const Size = struct { cols: u16, rows: u16, width_px: u16 = 0, height_px: u16 = 0,};
pub const ReadError = error{ /// Nothing has arrived since the last read. WouldBlock, Unexpected,};
pub const WriteError = error{ /// The other side is not taking bytes yet; try again when the handle is /// writable. WouldBlock, /// The other side is gone. Closed, Unexpected,};
test { _ = Message; _ = Pty; _ = Serial;}