Device Attributes

Æsh Readline provides support for querying terminal device attributes using DA1 and DA2 escape sequences. This allows applications to detect terminal capabilities that cannot be determined from terminfo alone.

Overview

Device Attributes (DA) queries are escape sequences that ask the terminal to report its capabilities:

QuerySequenceReturns
DA1 (Primary)ESC [ cDevice class and supported features
DA2 (Secondary)ESC [ > cTerminal type and firmware version

These queries provide authoritative information about what the terminal supports, including:

  • Sixel graphics support
  • ANSI color support
  • Mouse/locator support
  • Rectangular editing operations
  • Terminal type and version

Quick Start

import org.aesh.terminal.DeviceAttributes;
import org.aesh.terminal.tty.TerminalConnection;

TerminalConnection connection = new TerminalConnection();
connection.openNonBlocking();

// Query device attributes (500ms timeout)
DeviceAttributes da = connection.queryDeviceAttributes(500);

if (da != null) {
    System.out.println("Device class: " + da.getDeviceClass());
    System.out.println("Supports Sixel: " + da.supportsSixel());
    System.out.println("Supports ANSI color: " + da.supportsAnsiColor());
    System.out.println("Supports mouse: " + da.supportsMouse());
}

connection.close();

Querying Device Attributes

Primary Device Attributes (DA1)

DA1 returns the device conformance level and supported features:

DeviceAttributes da = connection.queryPrimaryDeviceAttributes(500);

if (da != null && da.hasDA1()) {
    // Device class indicates conformance level
    int deviceClass = da.getDeviceClass();
    // 1 or 2 = VT100
    // 62 = VT220
    // 63 = VT320
    // 64 = VT420

    // Check specific features
    boolean hasSixel = da.supportsSixel();
    boolean hasAnsiColor = da.supportsAnsiColor();
    boolean hasMouse = da.supportsMouse();
    boolean hasRectEdit = da.supportsRectangularEditing();
    boolean has132Cols = da.supports132Columns();

    // Get all features
    Set<DeviceAttributes.Feature> features = da.getFeatures();
}

Secondary Device Attributes (DA2)

DA2 returns terminal identification and version information:

DeviceAttributes da = connection.querySecondaryDeviceAttributes(500);

if (da != null && da.hasDA2()) {
    // Terminal type
    DeviceAttributes.TerminalType type = da.getTerminalType();
    System.out.println("Terminal: " + type.getName());

    // Firmware version
    int version = da.getFirmwareVersion();
    System.out.println("Version: " + version);

    // ROM cartridge (rarely used)
    int rom = da.getRomCartridge();
}

Combined Query

Query both DA1 and DA2 and merge the results:

DeviceAttributes da = connection.queryDeviceAttributes(500);

if (da != null) {
    // Has data from both queries
    if (da.hasDA1()) {
        System.out.println("Class: " + da.getDeviceClass());
        System.out.println("Features: " + da.getFeatures());
    }
    if (da.hasDA2()) {
        System.out.println("Type: " + da.getTerminalType().getName());
        System.out.println("Version: " + da.getFirmwareVersion());
    }
}

DeviceAttributes Class

Feature Enum

The DeviceAttributes.Feature enum represents capabilities reported in DA1:

FeatureCodeDescription
COLUMNS_1321132-column mode support
PRINTER2Printer port
REGIS_GRAPHICS3ReGIS graphics
SIXEL4Sixel graphics
SELECTIVE_ERASE6Selective erase
DRCS7Downloadable character sets
USER_DEFINED_KEYS8User-defined keys
NATIONAL_CHARSETS9National replacement character sets
TECHNICAL_CHARSETS15Technical character set
LOCATOR16DEC locator (mouse)
STATE_INTERROGATION17Terminal state interrogation
USER_WINDOWS18User windows
HORIZONTAL_SCROLLING21Horizontal scrolling
ANSI_COLOR22ANSI color support
RECTANGULAR_EDITING28Rectangular editing operations
ANSI_TEXT_LOCATOR29ANSI text locator (mouse)

Check for features using:

// Convenience methods
da.supportsSixel();          // Feature.SIXEL
da.supportsAnsiColor();      // Feature.ANSI_COLOR
da.supportsMouse();          // Feature.LOCATOR or ANSI_TEXT_LOCATOR
da.supportsRectangularEditing();  // Feature.RECTANGULAR_EDITING
da.supports132Columns();     // Feature.COLUMNS_132

// Generic check
da.hasFeature(DeviceAttributes.Feature.DRCS);

// Get all features
Set<Feature> features = da.getFeatures();

// Get raw parameter codes (includes unknown codes)
Set<Integer> rawParams = da.getRawParameters();

TerminalType Enum

The DeviceAttributes.TerminalType enum represents terminal types from DA2:

TypeCodeDescription
VT1000VT100 terminal
VT2201VT220 terminal
VT2402VT240 terminal
VT33018VT330 terminal
VT34019VT340 terminal
VT32024VT320 terminal
VT38232VT382 terminal
VT42041VT420 terminal
VT51061VT510 terminal
VT52064VT520 terminal
VT52565VT525 terminal
UNKNOWN-1Unknown terminal type
DeviceAttributes.TerminalType type = da.getTerminalType();
System.out.println("Terminal: " + type.getName());
System.out.println("Type code: " + type.getCode());

Merging Device Attributes

Combine DA1 and DA2 results:

DeviceAttributes da1 = connection.queryPrimaryDeviceAttributes(500);
DeviceAttributes da2 = connection.querySecondaryDeviceAttributes(500);

// Merge the results
DeviceAttributes merged = da1.merge(da2);

// Or use the combined query method
DeviceAttributes combined = connection.queryDeviceAttributes(500);

Use Cases

Sixel Graphics Detection

Use DA1 for authoritative Sixel support detection:

DeviceAttributes da = connection.queryPrimaryDeviceAttributes(500);

if (da != null && da.supportsSixel()) {
    // Terminal definitively supports Sixel
    displaySixelImage(imageData);
} else {
    // Fall back to text-based display
    displayAsciiArt(imageData);
}

OSC Query Support Detection

Infer OSC query support from DA1 features:

DeviceAttributes da = connection.queryPrimaryDeviceAttributes(500);

if (da != null && da.likelySupportsOscQueries()) {
    // Terminal likely supports OSC 10/11 color queries
    int[] bgColor = connection.queryBackgroundColor(500);
    if (bgColor != null) {
        boolean isDark = (bgColor[0] + bgColor[1] + bgColor[2]) / 3 < 128;
    }
}

// Or use the convenience method
if (connection.supportsOscQueries(da)) {
    // Safe to use OSC queries
}

Image Protocol Detection

Combine DA1 with heuristic detection for best results:

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

// The ImageProtocolDetector combines:
// 1. Environment variables (for Kitty/iTerm2)
// 2. TERM type string
// 3. DA1 attributes (for authoritative Sixel detection)

Terminal Capabilities Report

Generate a report of terminal capabilities:

DeviceAttributes da = connection.queryDeviceAttributes(500);

if (da != null) {
    StringBuilder report = new StringBuilder();
    report.append("Terminal Capabilities Report\n");
    report.append("============================\n\n");

    if (da.hasDA1()) {
        report.append("Device Class: ").append(da.getDeviceClass()).append("\n");
        report.append("Features:\n");
        for (DeviceAttributes.Feature f : da.getFeatures()) {
            report.append("  - ").append(f.name()).append("\n");
        }
    }

    if (da.hasDA2()) {
        report.append("\nTerminal Type: ").append(da.getTerminalType().getName()).append("\n");
        report.append("Firmware Version: ").append(da.getFirmwareVersion()).append("\n");
    }

    connection.write(report.toString());
}

Synergy with Terminal Environment

The DeviceAttributes class provides synergy methods that work with TerminalEnvironment and Device.TerminalType for comprehensive terminal detection:

Inferring Terminal Type from DA

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

// Heuristic detection (fast, always available)
Device.TerminalType heuristicType = TerminalEnvironment.detectTerminalType();

// Authoritative detection from DA1/DA2
DeviceAttributes da = connection.queryDeviceAttributes(500);
if (da != null) {
    Device.TerminalType authType = da.inferTerminalType();
    System.out.println("Inferred type: " + authType.getIdentifier());
}

Validating Terminal Type

Verify that heuristic detection matches actual capabilities:

Device.TerminalType envType = TerminalEnvironment.detectTerminalType();
DeviceAttributes da = connection.queryDeviceAttributes(500);

if (da != null && !da.matchesTerminalType(envType)) {
    System.out.println("Warning: Terminal capabilities differ from expected for " +
                       envType.getIdentifier());
}

Inferring Color Depth

Get authoritative color depth from DA1:

DeviceAttributes da = connection.queryDeviceAttributes(500);
if (da != null) {
    ColorDepth depth = da.inferColorDepth();
    System.out.println("Color depth: " + depth);
}

Capability Summary

Generate a comprehensive capabilities report:

Device.TerminalType envType = TerminalEnvironment.detectTerminalType();
DeviceAttributes da = connection.queryDeviceAttributes(500);

if (da != null) {
    String summary = da.getCapabilitySummary(envType);
    System.out.println(summary);
}

Output example:

Terminal: kitty
DA1 Class: 64
DA2 Type: VT420

Capabilities:
  Color Depth: COLORS_256
  Sixel Graphics: Yes
  ANSI Color: Yes
  Mouse Support: Yes
  OSC Queries: Likely

Expected Features by Terminal Type

Each Device.TerminalType knows its expected DA1 features:

// Get expected features for a terminal type
Set<DeviceAttributes.Feature> expected = Device.TerminalType.KITTY.getExpectedFeatures();
// Contains: SIXEL, ANSI_COLOR, ANSI_TEXT_LOCATOR

// Convenience methods
boolean expectsSixel = Device.TerminalType.KITTY.expectsSixel();  // true
boolean expectsMouse = Device.TerminalType.XTERM.expectsMouse();  // true

Common DA1 Response Patterns

Different terminals return different DA1 responses:

TerminalResponse ExampleFeatures
xtermESC[?64;1;2;6;9;15;18;21;22cVT420 class, many features
VT220ESC[?62;1;2;6;7;8;9cVT220 class, basic features
xterm+sixelESC[?64;4;22cSixel + ANSI color
BasicESC[?1;0cVT100 class, no features

Error Handling

Device attribute queries may fail or timeout:

DeviceAttributes da = connection.queryPrimaryDeviceAttributes(500);

if (da == null) {
    // Query failed or timed out
    // Possible reasons:
    // - Terminal doesn't support DA queries
    // - Connection not actively reading input
    // - Timeout too short

    // Fall back to heuristic detection
    ImageProtocol protocol = connection.device().getImageProtocol();
}

Performance Considerations

  1. Timeout: Choose an appropriate timeout (100-500ms typically sufficient)
  2. Cache results: Query once at startup, not repeatedly
  3. Non-blocking: Queries require active input reading via openBlocking() or openNonBlocking()
// Good: Query once at startup
public class MyApp {
    private static DeviceAttributes terminalCapabilities;

    public static void main(String[] args) {
        TerminalConnection conn = new TerminalConnection();
        conn.openNonBlocking();

        // Query once
        terminalCapabilities = conn.queryDeviceAttributes(500);

        // Use throughout application
        if (terminalCapabilities != null && terminalCapabilities.supportsSixel()) {
            // Use Sixel graphics
        }
    }
}

Supported Terminals

TerminalDA1DA2Notes
xtermYesYesFull support
VT220+YesYesOriginal DEC terminals
KittyYesLimitedDA1 works, DA2 varies
iTerm2YesYesFull support
GNOME TerminalYesYesFull support
KonsoleYesYesFull support
AlacrittyYesLimitedDA1 works
Windows TerminalYesYesFull support
tmuxPassthroughPassthroughDepends on underlying terminal

Troubleshooting

Query Returns Null

  1. Connection not reading: Call openBlocking() or openNonBlocking() first
  2. Timeout too short: Increase timeout to 500ms or more
  3. Terminal doesn’t support DA: Some terminals don’t respond to DA queries

Wrong Features Detected

  1. Terminal emulation mode: Some terminals emulate VT100 by default
  2. SSH/tmux: Multiplexers may filter or modify responses
  3. Check raw parameters: Use getRawParameters() to see unrecognized codes
// Debug: print raw response codes
Set<Integer> raw = da.getRawParameters();
System.out.println("Raw DA1 parameters: " + raw);