How Ghostty Handles Terminal Window Resizing and Grid Reflow

Ghostty processes terminal window resizing through a coordinated pipeline that atomically updates PTY dimensions, reallocates internal grid buffers, and triggers renderer reflow to prevent visual flicker.

Ghostty, the cross-platform terminal emulator written in Zig, implements a sophisticated resize handling mechanism that bridges UI events with the underlying pseudo-terminal (PTY). When the operating system reports a window size change, the terminal grid reflow occurs atomically across the Termio layer, ensuring the rendered output always matches the new dimensions.

The Resize Event Pipeline

Ghostty's resize handling follows a strict five-stage pipeline that separates UI events from computational updates and rendering.

Capture and Coalescing in the Termio Thread

When the GTK or macOS frontend detects a size change, it posts a resize message to the termio thread. According to the Ghostty source code in src/termio/Thread.zig, the thread loop coalesces rapid successive events to avoid redundant calculations:

while (true) {
    const msg = cb.io.wait();
    switch (msg) {
        .resize => |size| {
            // Stores only the latest size during the debounce interval
            thread.coalesce_data.resize = size;
        },
        .timer => {
            if (thread.coalesce_data.resize) |sz| {
                thread.coalesce_data.resize = null;
                cb.io.resize(&cb.data, sz) catch |err| log.warn("resize error {}", .{err});
            }
        },
        // … other messages …
    }
}

This debouncing mechanism collapses multiple rapid resize events into a single Termio.resize call.

State Update and PTY Synchronization

Once coalesced, the resize function in src/termio/Termio.zig (lines 62-101) performs three critical operations:

  • Stores the new renderer.Size containing pixel and cell dimensions
  • Calculates the grid size using the grid() method
  • Invokes backend.resize to synchronize the underlying PTY file descriptors

The implementation ensures the operating system's view of the terminal size stays locked to Ghostty's internal state.

Grid Reallocation Under Mutex Lock

Inside the critical section guarded by renderer_state.mutex, Ghostty calls terminal.resize as implemented in src/terminal/c/terminal.zig (lines 467-484). This function:

  • Reallocates the logical grid buffers to match new cell counts
  • Updates pixel dimensions for font rendering calculations
  • Clears any pending "synchronized output" mode (SM?2026) to force immediate visual updates
  • Handles edge cases including zero-size windows and dimension overflow

The mutex ensures grid reflow happens atomically without race conditions between the terminal state and the rendering thread.

Renderer Notification and Redraw

After updating the grid, the termio thread enqueues a resize message and wakes the renderer. As found in src/termio/Termio.zig (lines 99-102):

renderer_mailbox.push(.{ .resize = size }, .{ .forever = {} });
renderer_wakeup.notify();

The renderer thread consumes this message, recomputes cell layout based on the new grid dimensions, and redraws the entire screen surface.

Size Reporting and Synchronization

In-Band Size Reports

When configured, Ghostty emits in-band size reports using CSI sequences. The sizeReportLocked function in src/termio/Termio.zig (lines 12-20) formats and sends OSC 1337-style reports (CSI = 3 ; … c) to applications requesting window dimension notifications.

Forced Resize Fallback

In src/termio/stream_handler.zig (line 723), Ghostty implements a consistency check: if the terminal reports dimensions differing from the UI window size, the system forces the UI to adopt the terminal's size. This prevents desynchronization between the PTY state and the visual window bounds.

Practical Code Examples

Manual Resize Invocation

For testing or custom UI integrations, developers can trigger the resize pipeline directly:

const newSize = renderer.Size{
    .cols = 120,
    .rows = 35,
    .cell = .{ .width = 8, .height = 16 },
};
try termio.resize(td, newSize);

This call executes the full sequence: PTY resize, grid reallocation, and renderer notification.

Understanding the Coalescing Behavior

The debounce logic in src/termio/Thread.zig ensures that during animated window resizes, only the final dimensions trigger expensive grid reallocation operations. This optimization prevents memory thrashing and reduces CPU usage during continuous resize events.

Summary

  • Event Coalescing: Rapid resize events collapse into single updates via src/termio/Thread.zig to optimize performance
  • Atomic Updates: Grid reallocation occurs under renderer_state.mutex in src/terminal/c/terminal.zig to prevent race conditions
  • PTY Synchronization: backend.resize in src/termio/Termio.zig ensures the operating system's pseudo-terminal matches the UI dimensions
  • Renderer Integration: The mailbox push pattern decouples terminal state updates from GPU rendering operations
  • Consistency Guarantees: Fallback logic in src/termio/stream_handler.zig resynchronizes the UI if terminal reports diverge from window bounds

Frequently Asked Questions

How does Ghostty prevent flicker during window resizing?

Ghostty guards grid reallocation with a mutex (renderer_state.mutex) and clears synchronized output mode before updating dimensions. This ensures the terminal buffer atomically switches to the new size before the renderer redraws, eliminating intermediate frames that could cause visual tearing.

What happens if I resize the window rapidly?

The termio thread coalesces multiple resize events into a single operation. In src/termio/Thread.zig, intermediate sizes are stored temporarily in coalesce_data and only the final dimensions processed after a brief debounce interval, preventing redundant grid reallocations and PTY updates.

Does Ghostty support in-band size reporting to applications?

Yes. When enabled, Ghostty emits CSI sequences via sizeReportLocked in src/termio/Termio.zig, sending OSC 1337-style notifications that allow terminal applications to react programmatically to dimension changes without relying on SIGWINCH signals alone.

How does Ghostty handle size mismatches between the UI and PTY?

If the terminal reports dimensions different from the window size, the stream handler in src/termio/stream_handler.zig (line 723) forces the UI to adopt the terminal's size. This ensures consistency between the logical grid and the visual window, preventing display corruption or input coordinate misalignment.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →