The OpenD Programming Language

arsd.terminal

Module for interacting with the user's terminal, including color output, cursor manipulation, and full-featured real-time mouse and keyboard input. Also includes high-level convenience methods, like Terminal.getline, which gives the user a line editor with history, completion, etc. See the examples.

Members

Classes

FileLineGetter
class FileLineGetter

This is a line getter that customizes the tab completion to fill in file names separated by spaces, like a command line thing.

HangupException
class HangupException

Thrown by LineGetter if the terminal closes while it is processing input.

LineGetter
class LineGetter

A user-interactive line editor class, used by Terminal.getline. It is similar to GNU readline, offering comparable features like tab completion, history, and graceful degradation to adapt to the user's terminal.

TerminalEmulatorWindow
class TerminalEmulatorWindow

Represents the window that the library pops up for you.

UserInterruptionException
class UserInterruptionException

Thrown by LineGetter if the user pressed ctrl+c while it is processing events.

Enums

Color
enum Color

Defines the list of standard colors understood by Terminal. See also: Bright

ConsoleInputFlags
enum ConsoleInputFlags

When capturing input, what events are you interested in?

ConsoleOutputType
enum ConsoleOutputType

Defines how terminal output should be handled.

ForceOption
enum ForceOption

Some methods will try not to send unnecessary commands to the screen. You can override their judgement using a ForceOption parameter, if present

ModifierState
enum ModifierState
TerminalCursor
enum TerminalCursor

Functions

main
void main()

View the source of this!

removeTerminalGraphicsSequences
inout(char)[] removeTerminalGraphicsSequences(inout(char)[] s)

Removes terminal color, bold, etc. sequences from a string, making it plain text suitable for output to a normal .txt file.

Manifest constants

Bright
enum Bright;

A modifier for Color

IntegratedEmulator
enum IntegratedEmulator;
IntegratedEmulator
enum IntegratedEmulator;

Indicates the TerminalDirectToEmulator features are present. You can check this with static if.

Mixin templates

LineGetterConstructors
mixintemplate LineGetterConstructors()

Adds default constructors that just forward to the superclass

Static variables

continuedFromSuspend
bool continuedFromSuspend;

SIGCONT was just received, the terminal state may have changed. Added Feb 18, 2021.

hangedUp
bool hangedUp;

similar to interrupted.

integratedTerminalEmulatorConfiguration
IntegratedTerminalEmulatorConfiguration integratedTerminalEmulatorConfiguration;

You can set this in a static module constructor. (shared static this() {})

interrupted
bool interrupted;

you might periodically check this in a long operation and abort if it is set. Remember it is volatile. It is also sent through the input event loop via RealTimeConsoleInput

sigIntExtension
void delegate() nothrow @(nogc) sigIntExtension;

A function the sigint handler will call (if overridden - which is the case when RealTimeConsoleInput is active on Posix or if you compile with TerminalDirectToEmulator version on any platform at this time) in addition to the library's default handling, which is to set a flag for the event loop to inform you.

Structs

CharacterEvent
struct CharacterEvent
EndOfFileEvent
struct EndOfFileEvent

Sent upon receiving end-of-file from stdin.

HangupEvent
struct HangupEvent

If the user hangs up (for example, closes the terminal emulator without exiting the app), this is sent. If you receive it, you should generally cleanly exit.

InputEvent
struct InputEvent

RealTimeConsoleInput.nextEvent returns one of these. Check the type, then use the get method to get the more detailed information about the event.

IntegratedTerminalEmulatorConfiguration
struct IntegratedTerminalEmulatorConfiguration

Allows customization of the integrated emulator window. You may change the default colors, font, and other aspects of GUI integration.

KeyboardEvent
struct KeyboardEvent

The new style of keyboard event

LinkEvent
struct LinkEvent

Indicates a hyperlink was clicked in my custom terminal emulator or with version TerminalDirectToEmulator.

MouseEvent
struct MouseEvent

.

NonCharacterKeyEvent
struct NonCharacterKeyEvent
PasteEvent
struct PasteEvent

.

RGB
struct RGB

Represents a 24-bit color.

RealTimeConsoleInput
struct RealTimeConsoleInput

Encapsulates the stream of input events received from the terminal input.

ScrollbackBuffer
struct ScrollbackBuffer

The ScrollbackBuffer is a writable in-memory terminal that can be drawn to a real Terminal and maintain some internal position state by handling events. It is your responsibility to draw it (using the drawInto method) and dispatch events to its handleEvent method (if you want to, you can also just call the methods yourself).

SizeChangedEvent
struct SizeChangedEvent

When you get this, check terminal.width and terminal.height to see the new size and react accordingly.

Terminal
struct Terminal

Encapsulates the I/O capabilities of a terminal.

UserInterruptionEvent
struct UserInterruptionEvent

the user hitting ctrl+c will send this You should drop what you're doing and perhaps exit when this happens.

Detailed Description

The main interface for this module is the Terminal struct, which encapsulates the output functions and line-buffered input of the terminal, and RealTimeConsoleInput, which gives real time input.

Creating an instance of these structs will perform console initialization. When the struct goes out of scope, any changes in console settings will be automatically reverted and pending output is flushed. Do not create a global Terminal, as this will skip the destructor. Also do not create an instance inside a class or array, as again the destructor will be nondeterministic. You should create the object as a local inside main (or wherever else will encapsulate its whole usage lifetime), then pass borrowed pointers to it if needed somewhere else. This ensures the construction and destruction is run in a timely manner.

Output is NOT flushed on \n! Output is buffered until:
  • Terminal's destructor is run
  • You request input from the terminal object
  • You call terminal.flush()

If you want to see output immediately, always call terminal.flush() after writing.

Note: on Posix, it traps SIGINT and translates it into an input event. You should keep your event loop moving and keep an eye open for this to exit cleanly; simply break your event loop upon receiving a UserInterruptionEvent. (Without the signal handler, ctrl+c can leave your terminal in a bizarre state.)

As a user, if you have to forcibly kill your program and the event doesn't work, there's still ctrl+\

On old Mac Terminal btw, a lot of hacks are needed and mouse support doesn't work on older versions. Most functions work now with newer Mac OS versions though.

Future Roadmap

  • The CharacterEvent and NonCharacterKeyEvent types will be removed. Instead, use KeyboardEvent on new programs.
  • The ScrollbackBuffer will be expanded to be easier to use to partition your screen. It might even handle input events of some sort. Its API may change.
  • getline I want to be really easy to use both for code and end users. It will need multi-line support eventually.
  • I might add an expandable event loop and base level widget classes. This may be Linux-specific in places and may overlap with similar functionality in simpledisplay.d. If I can pull it off without a third module, I want them to be compatible with each other too so the two modules can be combined easily. (Currently, they are both compatible with my eventloop.d and can be easily combined through it, but that is a third module.)
  • More advanced terminal features as functions, where available, like cursor changing and full-color functions.
  • More documentation.

WHAT I WON'T DO:

  • support everything under the sun. If it isn't default-installed on an OS I or significant number of other people might actually use, and isn't written by me, I don't really care about it. This means the only supported terminals are:
    • xterm (and decently xterm compatible emulators like Konsole)
    • Windows console
    • rxvt (to a lesser extent)
    • Linux console
    • My terminal emulator family of applications https://github.com/adamdruppe/terminal-emulator

    Anything else is cool if it does work, but I don't want to go out of my way for it.

  • Use other libraries, unless strictly optional. terminal.d is a stand-alone module by default and always will be.
  • Do a full TUI widget set. I might do some basics and lay a little groundwork, but a full TUI is outside the scope of this module (unless I can do it really small.)

Examples

Get Line

This example will demonstrate the high-level Terminal.getline interface.

The user will be able to type a line and navigate around it with cursor keys and even the mouse on some systems, as well as perform editing as they expect (e.g. the backspace and delete keys work normally) until they press enter. Then, the final line will be returned to your program, which the example will simply print back to the user.

import arsd.terminal;

void main() {
	auto terminal = Terminal(ConsoleOutputType.linear);
	string line = terminal.getline();
	terminal.writeln("You wrote: ", line);

	// new on October 11, 2021: you can change the echo char
	// for password masking now. Also pass `0` there to get unix-style
	// total silence.
	string pwd = terminal.getline("Password: ", '*');
	terminal.writeln("Your password is: ", pwd);
}

Color

This example demonstrates color output, using Terminal.color and the output functions like Terminal.writeln.

import arsd.terminal;

void main() {
	auto terminal = Terminal(ConsoleOutputType.linear);
	terminal.color(Color.green, Color.black);
	terminal.writeln("Hello world, in green on black!");
	terminal.color(Color.DEFAULT, Color.DEFAULT);
	terminal.writeln("And back to normal.");
}

Single Key

This shows how to get one single character press using the RealTimeConsoleInput structure.

import arsd.terminal;

void main() {
	auto terminal = Terminal(ConsoleOutputType.linear);
	auto input = RealTimeConsoleInput(&terminal, ConsoleInputFlags.raw);

	terminal.writeln("Press any key to continue...");
	auto ch = input.getch();
	terminal.writeln("You pressed ", ch);
}

Full screen

This shows how to use the cellular (full screen) mode and pass terminal to functions.

import arsd.terminal;

// passing terminals must be done by ref or by pointer
void helper(Terminal* terminal) {
	terminal.moveTo(0, 1);
	terminal.getline("Press enter to exit...");
}

void main() {
	// ask for cellular mode, it will go full screen
	auto terminal = Terminal(ConsoleOutputType.cellular);

	// it is automatically cleared upon entry
	terminal.write("Hello upper left corner");

	// pass it by pointer to other functions
	helper(&terminal);

	// since at the end of main, Terminal's destructor
	// resets the terminal to how it was before for the
	// user
}

Meta

History

On December 29, 2020 the structs and their destructors got more protection against in-GC finalization errors and duplicate executions.

This should not affect your code.