Skip to content

SpinningVinyl/Terminality

Repository files navigation

Terminality

This is a very simple Java library for working with the Linux terminal. It allows client applications to do something like this, for example:

./screencast.gif

(Flickering is an artifact of the screen capturing process.)

Features

Terminality is designed to allow basic terminal manipulation: moving the cursor around, setting the color of text and background, and non-blocking reading of keyboard input.

Why did you write it?

I wanted to create terminal applications that could do more than simply print text at the current cursor position and read keyboard input line by line. Unfortunately, this is not possible using the Java Class Library. There is Lanterna which is an absolutely fantastic piece of software, but it comes with a lot of features that I don’t want or need (such as the built-in Swing terminal emulator, an UI library similar to ncurses, Windows support etc.). Hence this project.

Compatibility

The library has been extensively tested on Linux (with GNU C Library) using a variety of terminal emulators (xterm, urxvt, kitty, Konsole and Gnome Terminal). It is supposed to be compatible with macOS, but I have not tested it. It should work in WSL2, but I have not tested it for compatibility with the Windows Terminal. I have not tested Terminality with alternative libc implementations, e.g. musl.

How does it work?

Terminality uses JNA to call native libc functions on Posix-compatible systems. These functions are called to switch the terminal into the so-called “raw mode” (see Low-level Terminal Interface). After the terminal is set up as required, the library uses ANSI Escape sequences to control the output.

To initialise the terminal:

UnixTerminal t = new UnixTerminal();
t.begin(); // enter the raw mode

To exit the raw mode and restore the original attributes of the terminal:

t.end();

Even if your program terminates unexpectedly, Terminality will try to leave the terminal in a usable state by using a shutdown hook.

Keyboard input

To read the keyboard, use the readKey() method. The method returns an object of the KeyStroke class. It contains the type of the key (see KeyType.java), the character (if ks.type == KeyType.CHARACTER) and the status of the modifier keys (ks.ctrl, ks.alt and ks.shift). The shift field is useful only when the user presses special keys (e.g. [Ctrl]+[Shift]+[F5]), otherwise it would always be false. The library tries to do its best to guess when the [Ctrl] key is used, but due to the nature of the Unix terminal it’s not always possible (for example, there is literally no way to tell whether the user has pressed [Ctrl]+[h] or [Backspace]; this also applies to several other key combinations).

The library supports three modes for reading keyboard input: blocking, non-blocking and asynchronous.

Blocking keyboard I/O is useful when your program simply reacts to user input and does nothing in the background. To use it:

KeyStroke ks = t.readKey(true);

Conversely, calling readKey(false) or without any parameters reads the keyboard in the non-blocking manner. In this case, the method returns null if the user has not pressed any keys.

To use asynchronous keyboard input, instantiate the terminal object with the asyncIO parameter set to true. Asynchronous input is always non-blocking.

Controlling the cursor

Use the setCursorVisibility() method to control the visibility of the cursor:

t.setCursorVisibility(false); // make the cursor invisible

To move the cursor around, use the setCursorPosition() method. Row and column indices are zero-based:

t.setCursorPosition(9, 4); // move the cursor to 10-th row and 5-th column

Setting text attributes (color, etc.)

Use the setTextRendition() and resetTextRendition() methods to change text attributes such as color, background color etc. The most common text and background colors are provided as constants in the TextRendition class. See TextRendition.java for more information.

Printing text

To print text, use the put() method.

t.put('a'); // outputs single character -- 'a'
t.put("Hello, world!"); // outputs the string "Hello, world!"

There are also convenience methods that allow the client to specify text position and text attributes. For example,

t.put("text", TextRendition.FG_WHITE_INTENSE);

is the same as calling

t.setTextRendition(TextRendition.FG_WHITE_INTENSE);
t.put("text");
t.resetTextRendition();

and

t.put(row, column, "text", TextRendition.FG_WHITE_INTENSE, TextRendition.BG_PURPLE);

is the same as calling

t.setCursorPosition(row, column);
t.setTextRendition(TextRendition.FG_WHITE_INTENSE, TextRendition.BG_PURPLE);
t.put("text");
t.resetTextRendition();

Flushing the buffer

All output operations in Terminality are buffered, the client has to call flush() to make any changes visible:

t.flush(); // flush the output buffer

Chaining the method calls

Since version 0.5, Terminality supports the Builder pattern, which means that you can chain calls to some methods, e.g.:

t.begin().clear().setCursorVisibility(false).setCursorPosition(5, 5).put("Hello!").flush();

Terminal size

Use the getTerminalSize() method to get the size of the terminal window:

Terminal.WindowSize ws = t.getTerminalSize();
int cols = ws.columns;
int rows = ws.rows;

When you create a new UnixTerminal object, by default the constructor tries to register a handler for the SIGWINCH signal (the OS sends this signal to an app when the size of the terminal window changes). If the terminal is handling SIGWINCH itself, you can query whether the size of the terminal has changed using the sizeChanged() method. Subsequent calls to this method will return false until the terminal window is resized again.

You can disable this functionality by calling the constructor with the handleSigwinch parameter set to false (for example, if you want to implement your own handler using the unsupported sun.misc.Signal API). Alternatively, you can simply call getTerminalSize() on every iteration of your main application loop.

Example

See BouncyBall.java in src/main/java/net/prsv/terminality/example.

License

The project is licensed under the terms of Apache License, version 2.0. See LICENSE for details.

About

A simple Java library for working with the Linux terminal

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Languages