Class AbstractWindowsTerminal<Console>

  • Type Parameters:
    Console - the Windows console type used by the specific implementation
    All Implemented Interfaces:
    java.io.Closeable, java.io.Flushable, java.lang.AutoCloseable, TerminalExt, Terminal
    Direct Known Subclasses:
    JansiWinSysTerminal, JnaWinSysTerminal, NativeWinSysTerminal

    public abstract class AbstractWindowsTerminal<Console>
    extends AbstractTerminal
    Base implementation for terminals on Windows systems.

    The AbstractWindowsTerminal class provides a foundation for terminal implementations on Windows operating systems. It addresses Windows-specific limitations and peculiarities, particularly related to console handling, character encoding, and ANSI sequence support.

    Due to Windows limitations, particularly the historically limited support for ANSI sequences, this implementation uses Windows-specific APIs to handle terminal operations such as setting character attributes, moving the cursor, erasing content, and other terminal functions. This approach provides better compatibility and performance compared to emulating ANSI sequences on Windows.

    UTF-8 support has also been historically problematic on Windows, with the code page meant to emulate UTF-8 being somewhat broken. To work around these issues, this implementation uses the Windows API WriteConsoleW directly. As a result, the writer() method becomes the primary output mechanism, while the output() stream is bridged to the writer using a WriterOutputStream wrapper.

    Key features provided by this class include:

    • Windows console API integration
    • Color attribute handling
    • Cursor positioning and manipulation
    • Proper UTF-8 and Unicode support
    • Input processing for Windows console events

    This class is designed to be extended by concrete implementations that use specific Windows API access mechanisms (e.g., JNA, JNI, FFM).

    See Also:
    AbstractTerminal
    • Field Detail

      • TYPE_WINDOWS_256_COLOR

        public static final java.lang.String TYPE_WINDOWS_256_COLOR
        See Also:
        Constant Field Values
      • TYPE_WINDOWS_CONEMU

        public static final java.lang.String TYPE_WINDOWS_CONEMU
        See Also:
        Constant Field Values
      • TYPE_WINDOWS_VTP

        public static final java.lang.String TYPE_WINDOWS_VTP
        See Also:
        Constant Field Values
      • ENABLE_VIRTUAL_TERMINAL_PROCESSING

        public static final int ENABLE_VIRTUAL_TERMINAL_PROCESSING
        See Also:
        Constant Field Values
      • ENABLE_PROCESSED_INPUT

        protected static final int ENABLE_PROCESSED_INPUT
        See Also:
        Constant Field Values
      • ENABLE_QUICK_EDIT_MODE

        protected static final int ENABLE_QUICK_EDIT_MODE
        See Also:
        Constant Field Values
      • ENABLE_EXTENDED_FLAGS

        protected static final int ENABLE_EXTENDED_FLAGS
        See Also:
        Constant Field Values
      • slaveInputPipe

        protected final java.io.Writer slaveInputPipe
      • output

        protected final java.io.OutputStream output
      • writer

        protected final java.io.PrintWriter writer
      • nativeHandlers

        protected final java.util.Map<Terminal.Signal,​java.lang.Object> nativeHandlers
      • attributes

        protected final Attributes attributes
      • inConsole

        protected final Console inConsole
      • outConsole

        protected final Console outConsole
      • originalInConsoleMode

        protected final int originalInConsoleMode
      • originalOutConsoleMode

        protected final int originalOutConsoleMode
      • lock

        protected final java.lang.Object lock
      • paused

        protected boolean paused
      • pump

        protected java.lang.Thread pump
      • focusTracking

        protected boolean focusTracking
      • skipNextLf

        protected boolean skipNextLf
      • ANSI_COLORS

        protected static final int[] ANSI_COLORS
        ANSI colors mapping.
    • Constructor Detail

      • AbstractWindowsTerminal

        public AbstractWindowsTerminal​(TerminalProvider provider,
                                       SystemStream systemStream,
                                       java.io.Writer writer,
                                       java.lang.String name,
                                       java.lang.String type,
                                       java.nio.charset.Charset encoding,
                                       boolean nativeSignals,
                                       Terminal.SignalHandler signalHandler,
                                       Console inConsole,
                                       int inConsoleMode,
                                       Console outConsole,
                                       int outConsoleMode)
                                throws java.io.IOException
        Throws:
        java.io.IOException
      • AbstractWindowsTerminal

        public AbstractWindowsTerminal​(TerminalProvider provider,
                                       SystemStream systemStream,
                                       java.io.Writer writer,
                                       java.lang.String name,
                                       java.lang.String type,
                                       java.nio.charset.Charset encoding,
                                       java.nio.charset.Charset stdinEncoding,
                                       java.nio.charset.Charset stdoutEncoding,
                                       java.nio.charset.Charset stderrEncoding,
                                       boolean nativeSignals,
                                       Terminal.SignalHandler signalHandler,
                                       Console inConsole,
                                       int inConsoleMode,
                                       Console outConsole,
                                       int outConsoleMode)
                                throws java.io.IOException
        Throws:
        java.io.IOException
    • Method Detail

      • handle

        public Terminal.SignalHandler handle​(Terminal.Signal signal,
                                             Terminal.SignalHandler handler)
        Description copied from interface: Terminal
        Registers a handler for the given Terminal.Signal.

        This method allows the application to specify custom behavior when a particular signal is raised. The handler's Terminal.SignalHandler.handle(Signal) method will be called whenever the specified signal is raised.

        Note that the JVM does not easily allow catching the Terminal.Signal.QUIT signal (Ctrl+\), which typically causes a thread dump to be displayed. This signal handling is mainly effective when connecting through an SSH socket to a virtual terminal.

        Example usage:

         Terminal terminal = TerminalBuilder.terminal();
        
         // Handle window resize events
         terminal.handle(Signal.WINCH, signal -> {
             Size size = terminal.getSize();
             terminal.writer().println("\nTerminal resized to " +
                                      size.getColumns() + "x" + size.getRows());
             terminal.flush();
         });
        
         // Ignore interrupt signal
         terminal.handle(Signal.INT, SignalHandler.SIG_IGN);
         
        Specified by:
        handle in interface Terminal
        Overrides:
        handle in class AbstractTerminal
        Parameters:
        signal - the signal to register a handler for
        handler - the handler to be called when the signal is raised
        Returns:
        the previous signal handler that was registered for this signal
        See Also:
        Terminal.Signal, Terminal.SignalHandler, Terminal.raise(Signal)
      • reader

        public NonBlockingReader reader()
        Description copied from interface: Terminal
        Retrieve the Reader for this terminal. This is the standard way to read input from this terminal. The reader is non blocking.
        Returns:
        The non blocking reader
      • writer

        public java.io.PrintWriter writer()
        Description copied from interface: Terminal
        Retrieve the Writer for this terminal. This is the standard way to write to this terminal.
        Returns:
        The writer
      • input

        public java.io.InputStream input()
        Description copied from interface: Terminal
        Retrieve the input stream for this terminal. In some rare cases, there may be a need to access the terminal input stream directly. In the usual cases, use the Terminal.reader() instead.
        Returns:
        The input stream
        See Also:
        Terminal.reader()
      • output

        public java.io.OutputStream output()
        Description copied from interface: Terminal
        Retrieve the output stream for this terminal. In some rare cases, there may be a need to access the terminal output stream directly. In the usual cases, use the Terminal.writer() instead.
        Returns:
        The output stream
        See Also:
        Terminal.writer()
      • getAttributes

        public Attributes getAttributes()
        Description copied from interface: Terminal
        Returns the current terminal attributes.

        Terminal attributes control various aspects of terminal behavior, including:

        • Input processing - How input characters are processed (e.g., character mapping, parity checking)
        • Output processing - How output characters are processed (e.g., newline translation)
        • Control settings - Hardware settings like baud rate and character size
        • Local settings - Terminal behavior settings like echo, canonical mode, and signal generation
        • Control characters - Special characters like EOF, interrupt, and erase

        The returned Attributes object is a copy of the terminal's current attributes and can be safely modified without affecting the terminal until it is applied using Terminal.setAttributes(Attributes). This allows for making multiple changes to the attributes before applying them all at once.

        Example usage:

         Terminal terminal = TerminalBuilder.terminal();
        
         // Get current attributes
         Attributes attrs = terminal.getAttributes();
        
         // Modify attributes
         attrs.setLocalFlag(LocalFlag.ECHO, false);      // Disable echo
         attrs.setInputFlag(InputFlag.ICRNL, false);     // Disable CR to NL mapping
         attrs.setControlChar(ControlChar.VMIN, 1);      // Set minimum input to 1 character
         attrs.setControlChar(ControlChar.VTIME, 0);     // Set timeout to 0 deciseconds
        
         // Apply modified attributes
         terminal.setAttributes(attrs);
         
        Returns:
        a copy of the terminal's current attributes
        See Also:
        Terminal.setAttributes(Attributes), Attributes, Terminal.enterRawMode()
      • setAttributes

        public void setAttributes​(Attributes attr)
        Description copied from interface: Terminal
        Sets the terminal attributes to the specified values.

        This method applies the specified attributes to the terminal, changing its behavior according to the settings in the Attributes object. The terminal makes a copy of the provided attributes, so further modifications to the attr object will not affect the terminal until this method is called again.

        Terminal attributes control various aspects of terminal behavior, including input and output processing, control settings, local settings, and special control characters. Changing these attributes allows for fine-grained control over how the terminal processes input and output.

        Common attribute modifications include:

        • Disabling echo for password input
        • Enabling/disabling canonical mode for line-by-line or character-by-character input
        • Disabling signal generation for custom handling of Ctrl+C and other control sequences
        • Changing control characters like the interrupt character or end-of-file character

        For convenience, the Terminal.enterRawMode() method provides a pre-configured set of attributes suitable for full-screen interactive applications.

        Example usage:

         Terminal terminal = TerminalBuilder.terminal();
        
         // Save original attributes for later restoration
         Attributes originalAttrs = terminal.getAttributes();
        
         try {
             // Create and configure new attributes
             Attributes attrs = new Attributes(originalAttrs);
             attrs.setLocalFlag(LocalFlag.ECHO, false);      // Disable echo for password input
             attrs.setLocalFlag(LocalFlag.ICANON, false);    // Disable canonical mode
        
             // Apply the new attributes
             terminal.setAttributes(attrs);
        
             // Use terminal with modified attributes...
         } finally {
             // Restore original attributes
             terminal.setAttributes(originalAttrs);
         }
         
        Parameters:
        attr - the attributes to apply to the terminal
        See Also:
        Terminal.getAttributes(), Attributes, Terminal.enterRawMode()
      • updateConsoleMode

        protected void updateConsoleMode()
      • ctrl

        protected int ctrl​(char key)
      • setSize

        public void setSize​(Size size)
        Description copied from interface: Terminal
        Sets the size of the terminal.

        This method attempts to resize the terminal to the specified dimensions. Note that not all terminals support resizing, and the actual size after this operation may differ from the requested size depending on terminal capabilities and constraints.

        For virtual terminals or terminal emulators, this may update the internal size representation. For physical terminals, this may send appropriate escape sequences to adjust the viewable area.

        Parameters:
        size - the new terminal size (columns and rows)
        See Also:
        Terminal.getSize()
      • doClose

        protected void doClose()
                        throws java.io.IOException
        Overrides:
        doClose in class AbstractTerminal
        Throws:
        java.io.IOException
      • processKeyEvent

        protected void processKeyEvent​(boolean isKeyDown,
                                       short virtualKeyCode,
                                       char ch,
                                       int controlKeyState)
                                throws java.io.IOException
        Throws:
        java.io.IOException
      • getEscapeSequence

        protected java.lang.String getEscapeSequence​(short keyCode,
                                                     int keyState)
      • hasFocusSupport

        public boolean hasFocusSupport()
        Description copied from interface: Terminal
        Returns whether the terminal has support for focus tracking.

        Focus tracking allows the terminal to report when it gains or loses focus. This can be useful for applications that need to change their behavior or appearance based on whether they are currently in focus.

        Not all terminals support focus tracking, so this method should be called before attempting to enable focus tracking with Terminal.trackFocus(boolean).

        When focus tracking is enabled and supported, the terminal will send special escape sequences to the input stream when focus is gained ("\33[I") or lost ("\33[O"). Applications can detect these sequences to respond to focus changes.

        Example usage:

         Terminal terminal = TerminalBuilder.terminal();
        
         if (terminal.hasFocusSupport()) {
             // Enable focus tracking
             terminal.trackFocus(true);
        
             // Now the application can detect focus changes
             // by looking for "\33[I" and "\33[O" in the input stream
         } else {
             System.out.println("Focus tracking not supported by this terminal");
         }
         
        Specified by:
        hasFocusSupport in interface Terminal
        Overrides:
        hasFocusSupport in class AbstractTerminal
        Returns:
        true if the terminal supports focus tracking, false otherwise
        See Also:
        Terminal.trackFocus(boolean)
      • trackFocus

        public boolean trackFocus​(boolean tracking)
        Description copied from interface: Terminal
        Enables or disables focus tracking mode.

        Focus tracking allows applications to detect when the terminal window gains or loses focus. When focus tracking is enabled, the terminal will send special escape sequences to the input stream whenever the focus state changes:

        • When the terminal gains focus: "\33[I" (ESC [ I)
        • When the terminal loses focus: "\33[O" (ESC [ O)

        Applications can monitor the input stream for these sequences to detect focus changes and respond accordingly, such as by changing the cursor appearance, pausing animations, or adjusting the display.

        Not all terminals support focus tracking. Use Terminal.hasFocusSupport() to check whether focus tracking is supported before enabling it.

        Example usage:

         Terminal terminal = TerminalBuilder.terminal();
        
         if (terminal.hasFocusSupport()) {
             // Enable focus tracking
             boolean enabled = terminal.trackFocus(true);
        
             if (enabled) {
                 System.out.println("Focus tracking enabled");
                 // Set up input processing to detect focus change sequences
             }
         }
         
        Specified by:
        trackFocus in interface Terminal
        Overrides:
        trackFocus in class AbstractTerminal
        Parameters:
        tracking - true to enable focus tracking, false to disable it
        Returns:
        true if focus tracking is supported and the operation succeeded, false otherwise
        See Also:
        Terminal.hasFocusSupport()
      • convertAttributeToRgb

        protected int convertAttributeToRgb​(int attribute,
                                            boolean foreground)
        Convert Windows console attribute to RGB color.
        Parameters:
        attribute - the Windows console attribute
        foreground - true for foreground color, false for background color
        Returns:
        the RGB value of the color
      • pause

        public void pause()
        Description copied from interface: Terminal
        Temporarily stops reading the input stream.

        This method pauses the terminal's input processing, which can be useful when transferring control to a subprocess or when the terminal needs to be in a specific state for certain operations. While paused, the terminal will not process input or handle signals that would normally be triggered by special characters in the input stream.

        This method returns immediately without waiting for the terminal to actually pause. To wait until the terminal has fully paused, use Terminal.pause(boolean) with a value of true.

        Example usage:

         Terminal terminal = TerminalBuilder.terminal();
        
         // Pause terminal input processing before running a subprocess
         terminal.pause();
        
         // Run subprocess that takes control of the terminal
         Process process = new ProcessBuilder("vim").inheritIO().start();
         process.waitFor();
        
         // Resume terminal input processing
         terminal.resume();
         
        Specified by:
        pause in interface Terminal
        Overrides:
        pause in class AbstractTerminal
        See Also:
        Terminal.resume(), Terminal.pause(boolean), Terminal.paused(), Terminal.canPauseResume()
      • pause

        public void pause​(boolean wait)
                   throws java.lang.InterruptedException
        Description copied from interface: Terminal
        Stop reading the input stream and optionally wait for the underlying threads to finish.
        Specified by:
        pause in interface Terminal
        Overrides:
        pause in class AbstractTerminal
        Parameters:
        wait - true to wait until the terminal is actually paused
        Throws:
        java.lang.InterruptedException - if the call has been interrupted
      • resume

        public void resume()
        Description copied from interface: Terminal
        Resumes reading the input stream after it has been paused.

        This method restarts the terminal's input processing after it has been temporarily stopped using Terminal.pause() or Terminal.pause(boolean). Once resumed, the terminal will continue to process input and handle signals triggered by special characters in the input stream.

        Calling this method when the terminal is not paused has no effect.

        Example usage:

         Terminal terminal = TerminalBuilder.terminal();
        
         // Pause terminal input processing
         terminal.pause();
        
         // Perform operations while terminal input is paused...
        
         // Resume terminal input processing
         terminal.resume();
         
        Specified by:
        resume in interface Terminal
        Overrides:
        resume in class AbstractTerminal
        See Also:
        Terminal.pause(), Terminal.pause(boolean), Terminal.paused(), Terminal.canPauseResume()
      • paused

        public boolean paused()
        Description copied from interface: Terminal
        Check whether the terminal is currently reading the input stream or not. In order to process signal as quickly as possible, the terminal need to read the input stream and buffer it internally so that it can detect specific characters in the input stream (Ctrl+C, Ctrl+D, etc...) and raise the appropriate signals. However, there are some cases where this processing should be disabled, for example when handing the terminal control to a subprocess.
        Specified by:
        paused in interface Terminal
        Overrides:
        paused in class AbstractTerminal
        Returns:
        whether the terminal is currently reading the input stream or not
        See Also:
        Terminal.pause(), Terminal.resume()
      • pump

        protected void pump()
      • processInputChar

        public void processInputChar​(char c)
                              throws java.io.IOException
        Throws:
        java.io.IOException
      • trackMouse

        public boolean trackMouse​(Terminal.MouseTracking tracking)
        Description copied from interface: Terminal
        Enables or disables mouse tracking with the specified mode.

        This method configures the terminal to report mouse events according to the specified tracking mode. When mouse tracking is enabled, the terminal will send special escape sequences to the input stream whenever mouse events occur. These sequences begin with the InfoCmp.Capability.key_mouse sequence, followed by data that describes the specific mouse event.

        The tracking mode determines which mouse events are reported:

        To process mouse events, applications should:

        1. Enable mouse tracking by calling this method with the desired mode
        2. Monitor the input stream for the InfoCmp.Capability.key_mouse sequence
        3. When this sequence is detected, call Terminal.readMouseEvent() to decode the event
        4. Process the returned MouseEvent as needed

        Example usage:

         Terminal terminal = TerminalBuilder.terminal();
        
         if (terminal.hasMouseSupport()) {
             // Enable tracking of all mouse events
             boolean supported = terminal.trackMouse(MouseTracking.Any);
        
             if (supported) {
                 System.out.println("Mouse tracking enabled");
                 // Set up input processing to detect and handle mouse events
             }
         }
         
        Specified by:
        trackMouse in interface Terminal
        Overrides:
        trackMouse in class AbstractTerminal
        Parameters:
        tracking - the mouse tracking mode to enable, or Terminal.MouseTracking.Off to disable tracking
        Returns:
        true if the requested mouse tracking mode is supported, false otherwise
        See Also:
        Terminal.MouseTracking, Terminal.hasMouseSupport(), Terminal.readMouseEvent()
      • getConsoleMode

        protected abstract int getConsoleMode​(Console console)
      • setConsoleMode

        protected abstract void setConsoleMode​(Console console,
                                               int mode)
      • processConsoleInput

        protected abstract boolean processConsoleInput()
                                                throws java.io.IOException
        Read a single input event from the input buffer and process it.
        Returns:
        true if new input was generated from the event
        Throws:
        java.io.IOException - if anything wrong happens
      • getProvider

        public TerminalProvider getProvider()
        Description copied from interface: TerminalExt
        Returns the terminal provider that created this terminal.

        The terminal provider is responsible for creating and managing terminal instances on a specific platform. This method allows access to the provider that created this terminal, which can be useful for accessing provider-specific functionality or for creating additional terminals with the same provider.

        Returns:
        the TerminalProvider that created this terminal, or null if the terminal was created with no provider
        See Also:
        TerminalProvider
      • getSystemStream

        public SystemStream getSystemStream()
        Description copied from interface: TerminalExt
        Returns the system stream associated with this terminal, if any.

        This method indicates whether the terminal is bound to a standard system stream (standard input, standard output, or standard error). Terminals that are connected to system streams typically represent the actual terminal window or console that the application is running in.

        Returns:
        the underlying system stream, which may be SystemStream.Input, SystemStream.Output, SystemStream.Error, or null if this terminal is not bound to a system stream
        See Also:
        SystemStream