Skip to content

macOS: Dragging window between displays with different backing scale factors transiently shouldn't change pty dimensions #12925

Description

@bo2themax

Discussed in #12923

Originally posted by sortalongo June 4, 2026

Issue Description

When dragging a Ghostty window between displays with different backing scale factors on Mac, the pty dimensions transiently change to an incorrect value before self-correcting. The transient is long enough for TUI applications to redraw — and in Claude Code's case, irreversibly destroying the scrollback buffer.

This has been confirmed in two ways:

  • a program that monitors window size shows pty dims transiently changing
  • setting both displays to the same backing scale factor (both 2×) and repeating the drag produces no scrollback corruption, and no pty dim change.

Expected Behavior

Dragging a Ghostty window between displays leaves pty dimensions (rows × columns) unchanged when the window is not resized by the user.

Actual Behavior

The pty dimensions transiently change to an incorrect value before self-correcting. The transient is long enough for TUI applications to redraw into the wrong size — in Claude Code's case irreversibly destroying the scrollback buffer.

Observed via a tight TIOCGWINSZ polling loop while dragging between a 1× external display and a 2× Retina display:
size changed: 38 x 90 ← baseline (correct, on external)
size changed: 19 x 45 ← transient on move to Retina (halved)
size changed: 38 x 90 ← self-correction
size changed: 76 x 180 ← transient on move back to external (doubled)
size changed: 38 x 90 ← self-correction

Moving to the Retina display: dimensions halve transiently
Moving back to the external display: dimensions double transiently
Both directions self-correct, but not before the TUI has redrawn into the wrong size

The exact halving and doubling is consistent with the new display's backing scale factor being applied before the window geometry has fully settled. Setting both displays to the same backing scale factor (both 2×) eliminates the transient and produces no scrollback corruption.

Reproduction Steps

  1. Set up two displays with different backing scale factors (e.g. built-in Retina at 2× + external monitor at 1×) — the default for most MacBook + external monitor setups.
  2. Open Ghostty and run a TUI app with scrollback, e.g. claude (Claude Code).
  3. Build up several screenfuls of history.
  4. Drag the Ghostty window to the other display without resizing it.

To observe the transient dimension change directly, compile and run this before dragging:

#include <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h>

int main() {
    struct winsize ws, prev = {0};
    while (1) {
        ioctl(0, TIOCGWINSZ, &ws);
        if (ws.ws_col != prev.ws_col || ws.ws_row != prev.ws_row) {
            printf("size changed: %d x %d\n", ws.ws_row, ws.ws_col);
            fflush(stdout);
            prev = ws;
        }
    }
}

Confirmed non-reproduction: Setting both displays to the same backing scale factor (2×) and repeating the drag produces no transient and no scrollback corruption.

Ghostty Logs

No response

Ghostty Version

Ghostty 1.3.1

Version
  - version: 1.3.1
  - channel: stable
Build Config
  - Zig version   : 0.15.2
  - build mode    : .ReleaseFast
  - app runtime   : .none
  - font engine   : .coretext
  - renderer      : renderer.generic.Renderer(renderer.Metal)
  - libxev        : kqueue

OS Version Information

macOS Sequoia 15.x, M5 MacBook Pro

(Linux only) Display Server

None

(Linux only) Desktop Environment/Window Manager

No response

Minimal Ghostty Configuration

# empty config

Additional Relevant Configuration

No response

I acknowledge that:

  • I have reviewed the FAQ and confirm that my issue is NOT among them.
  • I have searched the Ghostty repository (both open and closed Discussions and Issues) and confirm this is not a duplicate of an existing issue or discussion.
  • I have checked the "Preview" tab on all text fields to ensure that everything looks right, and have wrapped all configuration and code in code blocks with a group of three backticks (```) on separate lines.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions