Class StreamBasedTerminal

  • All Implemented Interfaces:
    InputProvider, Terminal, java.io.Closeable, java.lang.AutoCloseable
    Direct Known Subclasses:
    ANSITerminal

    public abstract class StreamBasedTerminal
    extends AbstractTerminal
    An abstract terminal implementing functionality for terminals using OutputStream/InputStream. You can extend from this class if your terminal implementation is using standard input and standard output but not ANSI escape codes (in which case you should extend ANSITerminal). This class also contains some automatic UTF-8 to VT100 character conversion when the terminal is not set to read UTF-8.
    • Field Detail

      • UTF8_REFERENCE

        private static final java.nio.charset.Charset UTF8_REFERENCE
      • terminalInput

        private final java.io.InputStream terminalInput
      • terminalOutput

        private final java.io.OutputStream terminalOutput
      • terminalCharset

        private final java.nio.charset.Charset terminalCharset
      • keyQueue

        private final java.util.Queue<KeyStroke> keyQueue
      • readLock

        private final java.util.concurrent.locks.Lock readLock
      • lastReportedCursorPosition

        private volatile TerminalPosition lastReportedCursorPosition
    • Constructor Detail

      • StreamBasedTerminal

        public StreamBasedTerminal​(java.io.InputStream terminalInput,
                                   java.io.OutputStream terminalOutput,
                                   java.nio.charset.Charset terminalCharset)
    • Method Detail

      • putCharacter

        public void putCharacter​(char c)
                          throws java.io.IOException
        Prints one character to the terminal at the current cursor location. Please note that the cursor will then move one column to the right, so multiple calls to putCharacter will print out a text string without the need to reposition the text cursor. If you reach the end of the line while putting characters using this method, you can expect the text cursor to move to the beginning of the next line.

        You can output CJK (Chinese, Japanese, Korean) characters (as well as other regional scripts) but remember that the terminal that the user is using might not have the required font to render it. Also worth noticing is that CJK (and some others) characters tend to take up 2 columns per character, simply because they are a square in their construction as opposed to the somewhat rectangular shape we fit latin characters in. As it's very difficult to create a monospace font for CJK with a 2:1 height-width proportion, it seems like the implementers back in the days simply gave up and made each character take 2 column. It causes issues for the random terminal programmer because you can't really trust 1 character = 1 column, but I suppose it's "しょうがない". If you try to print non-printable control characters, the terminal is likely to ignore them (all Terminal implementations bundled with Lanterna will). The StreamBasedTerminal class will attempt to translate some unicode characters to VT100 if the encoding attached to this Terminal isn't UTF-8.

        Parameters:
        c - Character to place on the terminal
        Throws:
        java.io.IOException - If there was an underlying I/O error
      • putString

        public void putString​(java.lang.String string)
                       throws java.io.IOException
        Prints a string to the terminal at the current cursor location. Please note that the cursor will then move one column to the right, so multiple calls to putString will print out a text string without the need to reposition the text cursor. If you reach the end of the line while putting characters using this method, you can expect the text cursor to move to the beginning of the next line.

        You can output CJK (Chinese, Japanese, Korean) characters (as well as other regional scripts) but remember that the terminal that the user is using might not have the required font to render it. Also worth noticing is that CJK (and some others) characters tend to take up 2 columns per character, simply because they are a square in their construction as opposed to the somewhat rectangular shape we fit latin characters in. As it's very difficult to create a monospace font for CJK with a 2:1 height-width proportion, it seems like the implementers back in the days simply gave up and made each character take 2 column. It causes issues for the random terminal programmer because you can't really trust 1 character = 1 column, but I suppose it's "しょうがない".

        If you try to print non-printable control characters, the terminal is likely to ignore them (all Terminal implementations bundled with Lanterna will).

        You can use this method to place emoji characters on the terminal, since they take up more than one char with Java's built-in UTF16 encoding. The StreamBasedTerminal class will attempt to translate some unicode characters to VT100 if the encoding attached to this Terminal isn't UTF-8.

        Parameters:
        string - String to place on the terminal
        Throws:
        java.io.IOException - If there was an underlying I/O error
      • writeToTerminal

        protected void writeToTerminal​(byte... bytes)
                                throws java.io.IOException
        This method will write a list of bytes directly to the output stream of the terminal.
        Parameters:
        bytes - Bytes to write to the terminal (synchronized)
        Throws:
        java.io.IOException - If there was an underlying I/O error
      • enquireTerminal

        public byte[] enquireTerminal​(int timeout,
                                      java.util.concurrent.TimeUnit timeoutTimeUnit)
                               throws java.io.IOException
        Description copied from interface: Terminal
        Retrieves optional information from the terminal by printing the ENQ (\u005) character. Terminals and terminal emulators may or may not respond to this command, sometimes it's configurable.
        Parameters:
        timeout - How long to wait for the talk-back message, if there's nothing immediately available on the input stream, you should probably set this to a somewhat small value to prevent unnecessary blockage on the input stream but large enough to accommodate a round-trip to the user's terminal (~300 ms if you are connection across the globe).
        timeoutTimeUnit - What unit to use when interpreting the timeout parameter
        Returns:
        Answer-back message from the terminal or empty if there was nothing
        Throws:
        java.io.IOException - If there was an I/O error while trying to read the enquiry reply
      • bell

        public void bell()
                  throws java.io.IOException
        Description copied from interface: Terminal
        Prints 0x7 to the terminal, which will make the terminal (emulator) ring a bell (or more likely beep). Not all terminals implements this. Wikipedia has more details.
        Throws:
        java.io.IOException - If there was an underlying I/O error
      • getInputDecoder

        public InputDecoder getInputDecoder()
        Returns the InputDecoder attached to this StreamBasedTerminal. Can be used to add additional character patterns to recognize and tune the way input is turned in KeyStroke:s.
        Returns:
        InputDecoder attached to this StreamBasedTerminal
      • resetMemorizedCursorPosition

        void resetMemorizedCursorPosition()
        Used by the cursor reporting methods to reset any previous position memorized, so we're guaranteed to return the next reported position
      • waitForCursorPositionReport

        TerminalPosition waitForCursorPositionReport()
                                              throws java.io.IOException
        Waits for up to 5 seconds for a terminal cursor position report to appear in the input stream. If the timeout expires, it will return null. You should have sent the cursor position query already before calling this method.
        Returns:
        Current position of the cursor, or null if the terminal didn't report it in time.
        Throws:
        java.io.IOException - If there was an I/O error
      • pollInput

        public KeyStroke pollInput()
                            throws java.io.IOException
        Description copied from interface: InputProvider
        Returns the next Key off the input queue or null if there is no more input events available. Note, this method call is not blocking, it returns null immediately if there is nothing on the input stream.
        Returns:
        Key object which represents a keystroke coming in through the input stream
        Throws:
        java.io.IOException - Propagated error if the underlying stream gave errors
      • readInput

        public KeyStroke readInput()
                            throws java.io.IOException
        Description copied from interface: InputProvider
        Returns the next Key off the input queue or blocks until one is available. NOTE: In previous versions of Lanterna, this method was not blocking. From lanterna 3, it is blocking and you can call pollInput() for the non-blocking version.
        Returns:
        Key object which represents a keystroke coming in through the input stream
        Throws:
        java.io.IOException - Propagated error if the underlying stream gave errors
      • readInput

        private KeyStroke readInput​(boolean blocking,
                                    boolean useKeyQueue)
                             throws java.io.IOException
        Throws:
        java.io.IOException
      • flush

        public void flush()
                   throws java.io.IOException
        Description copied from interface: Terminal
        Calls flush() on the underlying OutputStream object, or whatever other implementation this terminal is built around. Some implementing classes of this interface (like SwingTerminal) doesn't do anything as it doesn't really apply to them.
        Throws:
        java.io.IOException - If there was an underlying I/O error
      • close

        public void close()
                   throws java.io.IOException
        Description copied from interface: Terminal
        Closes the terminal, if applicable. If the implementation doesn't support closing the terminal, this will do nothing. The Swing/AWT emulator implementations will translate this into a dispose() call on the UI resources, the telnet implementation will hang out the connection.
        Throws:
        java.io.IOException - If there was an underlying I/O error
      • getCharset

        protected java.nio.charset.Charset getCharset()
      • translateCharacter

        protected byte[] translateCharacter​(char input)
      • convertToVT100

        private byte[] convertToVT100​(char code)
      • convertToCharset

        private byte[] convertToCharset​(char input)