Terminal

Æsh Readline provides terminal abstraction through the Connection and Terminal interfaces.

TerminalConnection

The standard terminal connection:

import org.aesh.tty.terminal.TerminalConnection;

TerminalConnection connection = new TerminalConnection();
connection.openBlocking();  // Blocking mode
// OR
connection.openNonBlocking();  // Non-blocking mode

Connection Interface

Key Methods

Connection connection = new TerminalConnection();

// Terminal information
Device device = connection.device();
Size size = connection.size();
Charset inputEncoding = connection.inputEncoding();
Charset outputEncoding = connection.outputEncoding();
boolean supportsAnsi = connection.supportsAnsi();

// Write output
connection.write("Hello, World!\n");

// Handlers
Consumer<int[]> stdinHandler = connection.getStdinHandler();
connection.setStdinHandler(handler -> { /* process input */ });

Consumer<Size> sizeHandler = connection.getSizeHandler();
connection.setSizeHandler(size -> { /* handle resize */ });

Consumer<Signal> signalHandler = connection.getSignalHandler();
connection.setSignalHandler(signal -> { /* handle signals */ });

// Close
connection.close();
connection.close(exitCode);

Raw Mode

Enter raw mode for character-by-character input:

Attributes previousAttributes = connection.enterRawMode();
// ... do work ...
connection.setAttributes(previousAttributes);

Terminal Attributes

Control terminal behavior:

Attributes attrs = connection.getAttributes();

// Local flags
attrs.setLocalFlag(Attributes.LocalFlag.ICANON, false);  // Disable canonical mode
attrs.setLocalFlag(Attributes.LocalFlag.ECHO, false);     // Disable echo
attrs.setLocalFlag(Attributes.LocalFlag.IEXTEN, false);   // Disable implementation-defined processing

// Input flags
attrs.setInputFlag(Attributes.InputFlag.IXON, false);      // Disable XON/XOFF flow control

// Control characters
attrs.setControlChar(Attributes.ControlChar.VMIN, 1);      // Minimum read
attrs.setControlChar(Attributes.ControlChar.VTIME, 0);    // Timeout deciseconds

connection.setAttributes(attrs);

Terminal Size

Size size = connection.size();
int rows = size.getHeight();
int columns = size.getWidth();

Handle resize events:

connection.setSizeHandler(newSize -> {
    System.out.println("Terminal resized to: " + newSize.getWidth() + "x" + newSize.getHeight());
});

TerminalBuilder

Build custom terminal configurations:

import org.aesh.readline.terminal.TerminalBuilder;

Terminal terminal = TerminalBuilder.builder()
        .name("myterminal")
        .nativeSignals(true)
        .streams(System.in, System.out)
        .build();

Signal Handling

Handle terminal signals:

connection.setSignalHandler(signal -> {
    switch (signal) {
        case INT:   // Ctrl+C
            System.out.println("Interrupt received");
            break;
        case QUIT:  // Ctrl+\
            System.out.println("Quit received");
            break;
        case TSTP:  // Ctrl+Z
            System.out.println("Suspend received");
            break;
        case CONT:  // Continue after suspend
            System.out.println("Continue received");
            break;
        case WINCH: // Window change
            System.out.println("Window changed");
            break;
    }
});

Cursor Position

Get current cursor position:

Point position = connection.getCursorPosition();
int row = position.getRow();
int col = position.getColumn();

Device Attributes

Query the terminal for its capabilities using DA1/DA2 escape sequences:

// Query primary device attributes (DA1)
DeviceAttributes da = connection.queryPrimaryDeviceAttributes(500);

if (da != null) {
    // Check device class (conformance level)
    int deviceClass = da.getDeviceClass();  // 62=VT220, 64=VT420, etc.

    // Check feature support
    if (da.supportsSixel()) {
        System.out.println("Terminal supports Sixel graphics");
    }
    if (da.supportsAnsiColor()) {
        System.out.println("Terminal supports ANSI colors");
    }
    if (da.supportsMouse()) {
        System.out.println("Terminal supports mouse/locator");
    }
}

// Query both DA1 and DA2
DeviceAttributes full = connection.queryDeviceAttributes(500);
if (full != null && full.hasDA2()) {
    System.out.println("Terminal type: " + full.getTerminalType().getName());
    System.out.println("Firmware version: " + full.getFirmwareVersion());
}

DeviceAttributes Class

The DeviceAttributes class provides:

  • Device class: Conformance level (1=VT100, 62=VT220, 64=VT420)
  • Features: Set of supported capabilities (Sixel, ANSI color, mouse, etc.)
  • Terminal type: From DA2 (VT100, VT220, VT420, xterm)
  • Firmware version: Version number from DA2
// Check if OSC queries are likely supported based on DA1 features
if (da.likelySupportsOscQueries()) {
    int[] bgColor = connection.queryBackgroundColor(500);
}

Terminal Environment Detection

The TerminalEnvironment class provides centralized detection of terminal type and capabilities:

import org.aesh.terminal.Device;
import org.aesh.terminal.utils.TerminalEnvironment;

TerminalEnvironment env = TerminalEnvironment.getInstance();

// Detect terminal type
Device.TerminalType type = env.getTerminalType();
System.out.println("Terminal: " + type.getIdentifier());

// Check for specific terminals
if (env.isKitty()) {
    System.out.println("Running in Kitty");
}
if (env.isJetBrains()) {
    System.out.println("Running in JetBrains IDE");
}

// Check multiplexer status
if (env.isInMultiplexer()) {
    System.out.println("Running in tmux or screen");
}

// Check OSC support
if (env.supportsOscQueries()) {
    System.out.println("OSC queries supported");
}

// Get color depth
ColorDepth depth = env.getDefaultColorDepth();

The Device interface methods also use TerminalEnvironment internally:

Device device = connection.device();

// These use TerminalEnvironment
Device.TerminalType type = device.detectTerminalType();
boolean oscSupported = device.supportsOscQueries();
boolean isMultiplexer = device.isMultiplexer();

See Terminal Environment for complete documentation.

Image Protocol Detection

Detect the terminal’s inline image support:

// Heuristic detection (fast, no terminal query)
Device device = connection.device();
ImageProtocol protocol = device.getImageProtocol();

// Query-based detection (accurate, uses DA1)
ImageProtocol protocol = connection.queryImageProtocol(500);

switch (protocol) {
    case KITTY:
        // Kitty, Ghostty, Konsole
        break;
    case ITERM2:
        // iTerm2, WezTerm, VS Code, Mintty
        break;
    case SIXEL:
        // xterm, mlterm, foot, contour
        break;
    case NONE:
        // No inline image support
        break;
}

The ImageProtocolDetector class combines multiple detection methods:

  1. Environment variables (KITTY_WINDOW_ID, ITERM_SESSION_ID, etc.)
  2. Terminal type string (from TERM environment variable)
  3. DA1 device attributes (authoritative Sixel detection)

OSC Queries

Query the terminal using OSC (Operating System Command) sequences:

// Query background color
int[] bgColor = connection.queryBackgroundColor(500);
if (bgColor != null) {
    int r = bgColor[0], g = bgColor[1], b = bgColor[2];
    boolean isDark = (r + g + b) / 3 < 128;
}

// Query foreground color
int[] fgColor = connection.queryForegroundColor(500);

// Generic OSC query with custom parser
String result = connection.queryOsc(oscCode, "?", 500, input -> {
    // Parse the response
    return parseResponse(input);
});

OSC Support Detection

// Check if OSC queries are supported
if (connection.supportsOscQueries()) {
    // Safe to use OSC queries
}

// More accurate check using DA1 attributes
DeviceAttributes da = connection.queryPrimaryDeviceAttributes(500);
if (connection.supportsOscQueries(da)) {
    // DA1 indicates modern terminal features
}