Etermal
Embeddable Terminal and shell for OpenGL
Getting started

Building and linking

First, build etermal.

Installed with CMake

Add the following to your CMakeLists.txt:

find_package(etermal 2.0.2 REQUIRED)
find_package(Freetype 2.1 REQUIRED)
target_link_libraries(yourTarget etermal)
target_link_libraries(yourTarget Freetype::Freetype)
Note
You only need to link against freetype if your program uses the default font as opposed to a bitmap font.

Manual package install

Simply make sure that the include dir is in your include path and the library binary is in your linker path - and that you link to the binary.

Running

Note
All the things explained below could also be inferred from reading the docs for the pertinent classes.
Please also read the docs for terminal and shell, this is all just a brief intoduction.

Basics

To set everything up, you literally just have to do:

#include <etermal/etermal.h>
int main() {
// ... Set up the context etc etc ...
// Create font
std::shared_ptr<Font> font = std::make_shared<Font>("some/font/path.ttf");
// Create terminal and shell
etm::Terminal terminal(font);
etm::Shell shell;
// Link the two
terminal.setShell(shell);
shell.setTerminal(terminal);
terminal.dispText("Is anyone there?");
terminal.flush();
while (rendering) {
// ... Render stuff ...
// ... Make terminal context current ...
// Render terminal to framebuffer
terminal.render();
// ... Do other stuff ...
}
return 0;
}
Note
Of course, adding input calls and such are a bit more, but this is the basic level for getting the terminal to render on screen.
Warning
If the terminal is created this way, its OpenGL resources will be tied to the current context. This is not always desired behavior, especially if there isn't even a context active!

To fix that, you can instead do late initialization:

#include <etermal/etermal.h>
...
// Create normal font (doesn't immediately allocate OpenGL resources,
// only when caching - which happens during render time)
std::shared_ptr<Font> font = std::make_shared<Font>("some/font/path.ttf");
// Create terminal early
etm::Terminal terminal(
font,
true // Tell terminal to postpone initialization of OpenGL resources
);
// ... Set up and make current the desired context ...
terminal.init();

You can also call init whenever you want to regenerate the terminal's resources in a different context.

Of course, since Terminal is a little heavy, it might be better for you to dynamically allocate it with shared pointers... That's up to you, however.

Streaming

What's cool about terminal is that it extends std::streambuf, so you can create an std::ostream with it.

#include <etermal/etermal.h>
#include <ostream>
...
std::shared_ptr<Font> font = std::make_shared<Font>("some/font/path.ttf");
etm::Terminal terminal(font);
std::ostream stream(&terminal);
// Write to terminal through stream
stream << "What's the answer? " << std::flush;
// Since it's a stream, will flush on newline
int a = 42;
stream << a << "!\n";
// NB: Terminal DOES NOT automatically flush otherwise.
terminal.dispText("\nWait, who's Rem?\n");
terminal.flush();

Beautifying terminal text

See the page on escape sequences.

Adding custom commands to Shell

Note
My Shell implementation really isn't the best - it's terminal that really shines. Remember, you can always give your own custom implementation of the shell via EShell.

Really quite straightforward.

#include <etermal/etermal.h>
...
etm::Shell shell;
// Add a clear command
// The argument filter
etm::ArgFilter clearArgs;
// Set the usage (printed when invalid arguments are given;
// in this case, if any are)
clearArgs.setUsage("Usage: [clear|wipe] [no args]\nClears the screen");
// We don't want any flagless arguments.
// A flagless argument would be "main.cpp" in "[command] main.cpp",
// whereas a flag'd argument would be the same in "[command] -source main.cpp"
clearArgs.setMaxArrayArgs(0);
shell.addCommand(
"clear" // The initial alias
clearArgs, // The argument filter
[](etm::Shell &shell, etm::ETerminal &terminal, etm::Args &args)->void{ // std::function to execute
terminal.clear();
}
);
// Alias the last added command
shell.alias("wipe");
// Add a command that argues with you
// The argument filter
// Take a few
argArgs.setMaxArrayArgs(100);
argArgs.setUsage("Usage: arg [arg]\nClears the screen");
shell.addCommand(
"arg" // The initial alias
argArgs, // The argument filter
[](etm::Shell &shell, etm::ETerminal &terminal, etm::Args &args)->void{ // std::function to execute
for (std::string &str : args.getArrayArgs()) {
terminal.dispText("\"" + str + "\" is a stupid argument!\n");
terminal.flush();
}
}
);
etm::ETerminal
Base interface for an Etermal frontend.
Definition: ETerminal.h:16
etm::ArgFilter::setMaxArrayArgs
void setMaxArrayArgs(unsigned int count)
Sets the maximum number of flagless parameters that can be passed.
etm::ArgFilter::setUsage
void setUsage(const std::string &usage)
Sets the string that is printed when the filter fails to parse arguments.
etm::Shell::addCommand
comid_t addCommand(const std::string &name, const ArgFilter &filter, const callback_t &callback)
Add a new command to the Shell.
etm::Shell::alias
void alias(comid_t id, const std::string &name)
Associate another name with the given command.
etm::ArgFilter
Used to process - or filter, if you will - command arguments.
Definition: ArgFilter.h:18
etm::ETerminal::clear
virtual void clear()=0
Clears the screen.
etm::Terminal
Build-in implementation of the ETerminal frontend.
Definition: Terminal.h:38
etm::Args::getArrayArgs
std::vector< std::string > & getArrayArgs()
Gets the flagless parameters.
etm::Shell
Built in implementation of EShell.
Definition: Shell.h:27
etm::Shell::setTerminal
void setTerminal(ETerminal &terminal)
Set the program frontend.
etm::Args
Data class containing information about arguments passed to a command.
Definition: Args.h:19