Getting real-time input from the console or terminal can take a fair amount of platform-specific code, quite a bit more than writing out colored output.
Let's execute the following steps to get real-time input from the terminal:
terminal
.Terminal
object.RealTimeConsoleInput
object, passing it a pointer to your Terminal
object and passing flags for the types of input you need.input
object's methods.The code is as follows:
import terminal; void main() { auto terminal = Terminal(ConsoleOutputType.linear); auto input = RealTimeConsoleInput(&terminal, ConsoleInputFlags.raw); terminal.writeln("Press any key to exit"); auto ch = input.getch(); terminal.writeln("Bye!"); }
Now, run the program. It will immediately exit as soon as you press any key without waiting for a full line.
By default, text input from the user is line buffered by the operating system. This means your program won't be able to process any of the data the user types until she or he either presses the Enter key or signals the end of the file (Ctrl + D on most Posix setups and Ctrl + Z on Windows). To get real-time input, this line buffering must be disabled.
RealTimeConsoleInput
is a struct that disables this buffering in its constructor and resets the settings to normal in its destructor. Like Terminal
, the default constructor and postblit functions of RealTimeConsoleInput
are annotated with @disable
to force correct use of the shared resource.
The terminal.d
module also has optional integration with another file in my Github repository, eventloop.d
. To try it, compile both files together and add –version=with_eventloop
to the compile command. The terminal.d
module includes a commented-out demo function to show the usage. The eventloop.d
module implements a generic event loop through which any kind of message can be passed from any number of sources, identified by type. The goal of the event loop is to allow arbitrary combinations of event-driven libraries, such as a terminal and a GUI window, in a single application.
The implementation of eventloop.d
passes messages in two parts: a type identifier and a pointer to the data. The type identifier is created by hashing the type's mangled name, which is acquired with typeof(data).mangleof
. A type's mangled name is guaranteed to be unique across an application because a non-unique name will cause a compile-time name clash error. Alternatively, a handle to typeid
could have been used. This pair of numbers is sent through an operating system pipe back to the application itself, and it can be handled in a generic event loop as another file descriptor, watched by functions such as select
or poll
.
An interesting consequence of this implementation is that pointers to the garbage-collected data cannot be reliably sent as a message. The reason is that the garbage collector cannot see the kernel's pipe buffer. Consider the following: you send a message, then allocate some memory before receiving the message. The allocation triggers a garbage collection run.
As the garbage collector cannot see the pipe buffer, it may not find any reference to the message data and thus believe it is safe to free the data. Then, when the message is read, the pointer is invalid.
To fix this bug, I opted for manual memory management in the event loop's send
and receive
functions. When you send a message, it uses malloc to allocate a copy of the message. When the message is received, it frees the memory.
The eventloop.d
module only contains a Linux epoll
and self-pipe-based implementation at this time.
3.14.134.17