Skip to main content

Terminal Attributes and Modes

JLine provides fine-grained control over terminal attributes and modes, allowing you to customize how the terminal behaves. Understanding these attributes is essential for creating sophisticated terminal applications.

Terminal Attributes​

Terminal attributes control various aspects of terminal behavior, such as input processing, output processing, and control characters.

TerminalAttributesExample.java
import org.jline.terminal.Attributes;
import org.jline.terminal.Attributes.InputFlag;
import org.jline.terminal.Attributes.OutputFlag;
import org.jline.terminal.Attributes.LocalFlag;
import org.jline.terminal.Attributes.ControlChar;
import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;

import java.io.IOException;

public class TerminalAttributesExample {
public static void main(String[] args) throws IOException {
Terminal terminal = TerminalBuilder.builder().build();

try {
// Get current terminal attributes
Attributes attributes = terminal.getAttributes();

// Display current attributes
terminal.writer().println("Current terminal attributes:");
terminal.writer().println(" ECHO: " + attributes.getInputFlag(InputFlag.ECHO));
terminal.writer().println(" ICANON: " + attributes.getInputFlag(InputFlag.ICANON));
terminal.writer().println(" ICRNL: " + attributes.getInputFlag(InputFlag.ICRNL));
terminal.writer().println(" INLCR: " + attributes.getInputFlag(InputFlag.INLCR));
terminal.writer().println(" ISIG: " + attributes.getInputFlag(InputFlag.ISIG));
terminal.writer().println(" OPOST: " + attributes.getOutputFlag(OutputFlag.OPOST));
terminal.writer().println(" ONLCR: " + attributes.getOutputFlag(OutputFlag.ONLCR));
terminal.writer().println(" OCRNL: " + attributes.getOutputFlag(OutputFlag.OCRNL));
terminal.writer().println(" IEXTEN: " + attributes.getLocalFlag(LocalFlag.IEXTEN));
terminal.writer().println(" VEOF: " + attributes.getControlChar(ControlChar.VEOF));
terminal.writer().println(" VERASE: " + attributes.getControlChar(ControlChar.VERASE));
terminal.writer().println(" VINTR: " + attributes.getControlChar(ControlChar.VINTR));
terminal.writer().println(" VSUSP: " + attributes.getControlChar(ControlChar.VSUSP));
terminal.writer().flush();
} finally {
terminal.close();
}
}
}

Input Flags​

Input flags control how input is processed:

InputFlagsExample.java
import org.jline.terminal.Attributes;
import org.jline.terminal.Attributes.InputFlag;
import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;

import java.io.IOException;

public class InputFlagsExample {
public static void main(String[] args) throws IOException {
Terminal terminal = TerminalBuilder.builder().build();

try {
// Get current attributes
Attributes attributes = terminal.getAttributes();

// Display current input flags
terminal.writer().println("Current input flags:");

// Common input flags
for (InputFlag flag : new InputFlag[] {
InputFlag.IGNBRK, // Ignore break condition
InputFlag.BRKINT, // Map break to interrupt
InputFlag.IGNPAR, // Ignore parity errors
InputFlag.PARMRK, // Mark parity errors
InputFlag.INPCK, // Enable parity check
InputFlag.ISTRIP, // Strip 8th bit
InputFlag.INLCR, // Map NL to CR
InputFlag.IGNCR, // Ignore CR
InputFlag.ICRNL, // Map CR to NL
InputFlag.IXON, // Enable XON/XOFF flow control
InputFlag.IXOFF, // Enable sending XON/XOFF
InputFlag.IXANY, // Any character restarts output
InputFlag.IMAXBEL, // Ring bell on input queue full
InputFlag.IUCLC, // Map uppercase to lowercase
InputFlag.ECHO, // Echo input characters
InputFlag.ECHOE, // Echo erase character as BS-SP-BS
InputFlag.ECHOK, // Echo NL after kill character
InputFlag.ECHONL, // Echo NL
InputFlag.NOFLSH, // Disable flush after interrupt
InputFlag.TOSTOP, // Stop background jobs that write to terminal
InputFlag.IEXTEN, // Enable extended functions
InputFlag.ECHOCTL, // Echo control characters in hat notation
InputFlag.ECHOKE, // BS-SP-BS entire line on line kill
InputFlag.PENDIN, // Retype pending input at next read
InputFlag.ICANON, // Enable canonical mode
InputFlag.ISIG, // Enable signals
}) {
try {
boolean value = attributes.getInputFlag(flag);
terminal.writer().println(" " + flag + ": " + value);
} catch (Exception e) {
terminal.writer().println(" " + flag + ": unsupported");
}
}

terminal.writer().flush();
} finally {
terminal.close();
}
}
}

Output Flags​

Output flags control how output is processed:

OutputFlagsExample.java
import org.jline.terminal.Attributes;
import org.jline.terminal.Attributes.OutputFlag;
import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;

import java.io.IOException;

public class OutputFlagsExample {
public static void main(String[] args) throws IOException {
Terminal terminal = TerminalBuilder.builder().build();

try {
// Get current attributes
Attributes attributes = terminal.getAttributes();

// Display current output flags
terminal.writer().println("Current output flags:");

// Common output flags
for (OutputFlag flag : new OutputFlag[] {
OutputFlag.OPOST, // Post-process output
OutputFlag.ONLCR, // Map NL to CR-NL
OutputFlag.OCRNL, // Map CR to NL
OutputFlag.ONOCR, // Don't output CR at column 0
OutputFlag.ONLRET, // NL performs CR function
OutputFlag.OFILL, // Use fill characters for delay
OutputFlag.OFDEL, // Fill is DEL
OutputFlag.NLDLY, // NL delay
OutputFlag.CRDLY, // CR delay
OutputFlag.TABDLY, // Tab delay
OutputFlag.BSDLY, // Backspace delay
OutputFlag.VTDLY, // Vertical tab delay
OutputFlag.FFDLY, // Form feed delay
}) {
try {
int value = attributes.getOutputFlag(flag);
terminal.writer().println(" " + flag + ": " + value);
} catch (Exception e) {
terminal.writer().println(" " + flag + ": unsupported");
}
}

terminal.writer().flush();
} finally {
terminal.close();
}
}
}

Control Characters​

Control characters define special characters that have specific functions:

ControlCharsExample.java
import org.jline.terminal.Attributes;
import org.jline.terminal.Attributes.ControlChar;
import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;

import java.io.IOException;

public class ControlCharsExample {
public static void main(String[] args) throws IOException {
Terminal terminal = TerminalBuilder.builder().build();

try {
// Get current attributes
Attributes attributes = terminal.getAttributes();

// Display current control characters
terminal.writer().println("Current control characters:");

// Common control characters
for (ControlChar cc : new ControlChar[] {
ControlChar.VEOF, // EOF character (usually Ctrl-D)
ControlChar.VEOL, // EOL character
ControlChar.VERASE, // Erase character (usually Backspace)
ControlChar.VINTR, // Interrupt character (usually Ctrl-C)
ControlChar.VKILL, // Kill character (usually Ctrl-U)
ControlChar.VMIN, // Minimum number of characters for non-canonical read
ControlChar.VQUIT, // Quit character (usually Ctrl-\\)
ControlChar.VSTART, // Start character (usually Ctrl-Q)
ControlChar.VSTOP, // Stop character (usually Ctrl-S)
ControlChar.VSUSP, // Suspend character (usually Ctrl-Z)
ControlChar.VTIME, // Timeout in deciseconds for non-canonical read
}) {
try {
int value = attributes.getControlChar(cc);
terminal.writer().println(" " + cc + ": " + value +
(value > 0 ? " ('" + (char)value + "')" : ""));
} catch (Exception e) {
terminal.writer().println(" " + cc + ": unsupported");
}
}

terminal.writer().flush();
} finally {
terminal.close();
}
}
}

Terminal Modes​

JLine supports different terminal modes that affect how input and output are processed.

Canonical Mode vs. Raw Mode​

The most important distinction is between canonical mode and raw mode:

TerminalModesExample.java
import org.jline.terminal.Attributes;
import org.jline.terminal.Attributes.InputFlag;
import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;

import java.io.IOException;

public class TerminalModesExample {
public static void main(String[] args) throws IOException {
Terminal terminal = TerminalBuilder.builder().build();

try {
// Save original attributes
Attributes originalAttributes = terminal.getAttributes();

// Display current mode
boolean canonicalMode = originalAttributes.getInputFlag(InputFlag.ICANON);
terminal.writer().println("Terminal is currently in " +
(canonicalMode ? "canonical" : "raw") + " mode");
terminal.writer().flush();

// Switch to raw mode
Attributes rawAttributes = new Attributes(originalAttributes);
rawAttributes.setInputFlag(InputFlag.ICANON, false); // Disable canonical mode
rawAttributes.setInputFlag(InputFlag.ECHO, false); // Disable echo
rawAttributes.setInputFlag(InputFlag.ISIG, false); // Disable signals
rawAttributes.setInputFlag(InputFlag.IEXTEN, false); // Disable extended functions

// Set control characters for non-canonical mode
rawAttributes.setControlChar(Attributes.ControlChar.VMIN, 1); // Read at least 1 character
rawAttributes.setControlChar(Attributes.ControlChar.VTIME, 0); // No timeout

// Apply raw mode attributes
terminal.setAttributes(rawAttributes);

terminal.writer().println("Switched to raw mode. Press any key to continue...");
terminal.writer().flush();

// Read a single character
int c = terminal.reader().read();

// Restore original attributes
terminal.setAttributes(originalAttributes);

terminal.writer().println("\nSwitched back to canonical mode");
terminal.writer().println("You pressed: " + (char)c + " (ASCII: " + c + ")");
terminal.writer().flush();

terminal.writer().println("\nPress Enter to exit...");
terminal.writer().flush();
terminal.reader().readLine();
} finally {
terminal.close();
}
}
}

Mode Comparison​

FeatureCanonical ModeRaw Mode
Input ProcessingLine-buffered (until Enter)Character-by-character
Special CharactersProcessed (Ctrl+C, Ctrl+Z, etc.)Not processed (unless configured)
EchoingEnabled by defaultDisabled by default
BackspaceProcessedNot processed
Line EditingEnabledDisabled
Use CaseCommand-line interfacesInteractive applications, games

Customizing Terminal Behavior​

You can customize terminal behavior by modifying attributes:

CustomTerminalBehaviorExample.java
import org.jline.terminal.Attributes;
import org.jline.terminal.Attributes.InputFlag;
import org.jline.terminal.Attributes.ControlChar;
import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;

import java.io.IOException;

public class CustomTerminalBehaviorExample {
public static void main(String[] args) throws IOException {
Terminal terminal = TerminalBuilder.builder().build();

try {
// Save original attributes
Attributes originalAttributes = terminal.getAttributes();

// Create custom attributes
Attributes customAttributes = new Attributes(originalAttributes);

// Customize input behavior
customAttributes.setInputFlag(InputFlag.ECHO, true); // Enable echo
customAttributes.setInputFlag(InputFlag.ICANON, true); // Enable canonical mode
customAttributes.setInputFlag(InputFlag.ICRNL, true); // Map CR to NL

// Customize control characters
customAttributes.setControlChar(ControlChar.VINTR, 3); // Set Ctrl+C as interrupt
customAttributes.setControlChar(ControlChar.VEOF, 4); // Set Ctrl+D as EOF
customAttributes.setControlChar(ControlChar.VSUSP, 26); // Set Ctrl+Z as suspend

// Apply custom attributes
terminal.setAttributes(customAttributes);

terminal.writer().println("Terminal configured with custom attributes");
terminal.writer().println("Type some text and press Enter (Ctrl+D to exit):");
terminal.writer().flush();

// Read lines until EOF
String line;
while ((line = terminal.reader().readLine()) != null) {
terminal.writer().println("You typed: " + line);
terminal.writer().println("Type another line (Ctrl+D to exit):");
terminal.writer().flush();
}

// Restore original attributes
terminal.setAttributes(originalAttributes);
} finally {
terminal.close();
}
}
}

Saving and Restoring Attributes​

It's important to save and restore terminal attributes:

SaveRestoreAttributesExample.java
import org.jline.terminal.Attributes;
import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;

import java.io.IOException;

public class SaveRestoreAttributesExample {
public static void main(String[] args) throws IOException {
Terminal terminal = TerminalBuilder.builder().build();

try {
// Save original attributes
Attributes originalAttributes = terminal.getAttributes();

terminal.writer().println("Original terminal attributes saved");
terminal.writer().println("Temporarily changing terminal attributes...");
terminal.writer().flush();

// Change attributes (enter raw mode)
terminal.enterRawMode();

terminal.writer().println("Terminal is now in raw mode");
terminal.writer().println("Press any key to restore original attributes...");
terminal.writer().flush();

// Wait for a keypress
terminal.reader().read();

// Restore original attributes
terminal.setAttributes(originalAttributes);

terminal.writer().println("\nOriginal terminal attributes restored");
terminal.writer().flush();
} finally {
terminal.close();
}
}
}

Best Practices​

When working with terminal attributes and modes, consider these best practices:

  1. Always Save Original Attributes: Save the original terminal attributes before making changes.

  2. Always Restore Attributes: Restore the original attributes before exiting, even in error cases.

  3. Use try-finally Blocks: Ensure attributes are restored even if exceptions occur.

  4. Document Mode Changes: Clearly document when your application changes terminal modes.

  5. Provide User Feedback: Inform users when the terminal mode changes.

  6. Check Terminal Capabilities: Not all terminals support all attributes and modes.

  7. Test on Different Platforms: Terminal behavior can vary across platforms.

  8. Use Helper Methods: Use helper methods like enterRawMode() when available.

  9. Consider User Experience: Choose modes appropriate for your application's user experience.

  10. Handle Signals Appropriately: Be careful when disabling signal processing (ISIG flag).