Skip to content

getTerminalSize() corrupts cursor position as a side effect #644

@acjervell

Description

@acjervell

Bug Summary

When Terminal.getTerminalSize() is called, it corrupts the cursor position as a side effect, causing subsequent output to appear at incorrect positions on the terminal.

Affected Terminal Types

  • UnixTerminal (and likely other ANSI-based terminals) - ✗ Affected
  • ScrollingSwingTerminal - ✓ Not affected

Root Cause

The getTerminalSize() implementation uses a hack where it moves the cursor to position 5000x5000 to detect terminal boundaries, then reads back the actual cursor position. However, the cursor position is not restored after this operation, leaving the cursor in an incorrect location.

Steps to Reproduce

Terminal terminal = new DefaultTerminalFactory()
    .setPreferTerminalEmulator(false)
    .createTerminal();

// Output some text
terminal.putString("Line 1");
terminal.putCharacter('\n');
terminal.putString("Line 2: ");
terminal.flush();

// Query terminal size (this corrupts cursor position)
TerminalSize size = terminal.getTerminalSize();

// Further output will now appear at incorrect position
terminal.putString("more text");
terminal.flush();

Expected Behavior

getTerminalSize() should be a read-only query operation that doesn't affect terminal state, particularly cursor position. Text output after the call should continue from where it left off.

Actual Behavior

After calling getTerminalSize(), the cursor position is corrupted, causing subsequent output to:

  • Appear at an incorrect column/row position
  • Have unexpected spacing or padding
  • Not align with previously rendered content

The exact manifestation depends on when getTerminalSize() is called and what the application does with cursor positioning.

Workaround

Save and restore cursor position around getTerminalSize() calls:

TerminalPosition savedPosition = terminal.getCursorPosition();
try {
    return terminal.getTerminalSize();
} finally {
    if (savedPosition != null) {
        terminal.setCursorPosition(savedPosition.getColumn(), savedPosition.getRow());
    }
}

We've implemented a transparent wrapper class that applies this fix by intercepting getTerminalSize() calls to preserve cursor position while delegating all other Terminal methods normally.

Environment

  • Lanterna version: 3.1.3
  • OS: macOS (Darwin 25.0.0)
  • Terminal: Unix terminal (via DefaultTerminalFactory)
  • Java: 21

Impact

This affects any application that needs to query terminal size during active rendering, such as:

  • Responsive layouts that adapt to terminal width
  • Word wrapping implementations
  • Table rendering with dynamic column sizing
  • Any code that queries size between text output operations

Suggested Fix

The getTerminalSize() implementation should save and restore cursor position internally, making the cursor movement hack transparent to callers.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions