Coder Social home page Coder Social logo

adriendelsalle / cpp-terminal Goto Github PK

View Code? Open in Web Editor NEW

This project forked from jupyter-xeus/cpp-terminal

0.0 0.0 0.0 534 KB

Small header only C++ library for writing multiplatform terminal applications

License: Other

Batchfile 0.13% C++ 98.80% Shell 0.15% CMake 0.91%

cpp-terminal's Introduction

Terminal

Terminal is small header only library for writing terminal applications. It works on Linux, macOS and Windows (in the native cmd.exe console). It supports colors, keyboard input and has all the basic features to write any terminal application.

It has a small core (terminal_base.h) that has a few platform specific building blocks, and a platform independent library written on top using the ANSI escape sequences (terminal.h).

This design has the advantage of having only a few lines to maintain on each platform, and the rest is platform independent. We intentionally limit ourselves to a subset of features that all work on all platforms natively. That way, any application written using Terminal will work everywhere out of the box, without emulation. At the same time, because the code of Terminal is short, one can easily debug it if something does not work, and have a full understanding how things work underneath.

Examples

Several examples are provided to show how to use Terminal. Every example works natively on all platforms:

  • kilo.cpp: the kilo text editor ported to C++ and Terminal instead of using Linux specific API.
  • menu.cpp: Shows a menu on the screen
  • keys.cpp: Listens for keys, showing their numbers
  • colors.cpp: Shows how to print text in color to standard output

How to use

The easiest is to just copy the two files terminal.h and terminal_base.h into your project. Consult the examples how to use it. You can just use the terminal_base.h, which is a standalone header file, if you only want the low level platform dependent functionality. Use terminal.h, which depends on terminal_base.h, if you also want the platform independent code to more easily print escape sequences and/or read and translate key codes.

Documentation

We will start from the simplest concept (just printing a text on the screen) and then we will keep adding more features such as colors, cursor movement, keyboard input, etc., and we will be explaining how things work as we go.

Printing

To print text into standard output, one can use std::cout in C++:

std::cout << "Some text" << std::endl;

One does not need Terminal for that.

Colors

To print colors and other styles (such as bold), use the Term::color() function and Term::fg enum for foreground, Term::bg enum for background and Term::style enum for different styles (see the colors.cpp example):

#include <cpp-terminal/terminal.h>
using Term::color;
using Term::fg;
using Term::bg;
using Term::style;
int main() {
    try {
        Term::Terminal term;
        std::string text = "Some text with "
            + color(fg::red) + color(bg::green) + "red on green"
            + color(bg::reset) + color(fg::reset) + " and some "
            + color(style::bold) + "bold text" + color(style::reset) + ".";
        std::cout << text << std::endl;
    } catch(...) {
        throw;
    }
    return 0;
}

One must call Term::fg::reset, Term::bg::reset and Term::style::reset to reset the given color or style.

One must create the Term::Terminal instance. In this case, the Terminal does nothing on Linux and macOS, but on Windows it checks if the program is running withing the Windows console and if so, enables ANSI escape codes in the console, which makes the console show colors properly. One must have a try/catch block in the main program to ensure the Terminal's destructor gets called (even if an unhandled exception occurs), which will put the console into the original mode.

The program might decide to print colors not only if it is in a terminal (which can be checked by term.is_stdout_a_tty()), but also when not run in a terminal, some examples:

  • Running on a CI, e.g. AppVeyor, Travis-CI and Azure Pipelines all show colors properly
  • Using less -r shows colors properly (but less does not)
  • Printing colors in program output in a Jupyter notebook (and then possibly converting such colors from ANSI sequences to html)

An example when the program might not print colors is when the standard output gets redirected to a file (say, compiler error messages using g++ a.cpp > log), and then the file is read directly in some editor.

The color() function always returns a string with the proper ANSI sequence. The program might wrap this in a macro, that will check some program variable if it should print colors and only call color() if colors should be printed.

Cursor movement and its visibility

The next step up is to allow cursor movement and other ANSI sequences. For example, here is how to render a simple menu (see menu.cpp example) and print it on the screen:

void render(int rows, int cols, int pos)
{
    std::string scr;
    scr.reserve(16*1024);

    scr.append(cursor_off());
    scr.append(move_cursor(1, 1));

    for (int i=1; i <= rows; i++) {
        if (i == pos) {
            scr.append(color(fg::red));
            scr.append(color(bg::gray));
            scr.append(color(style::bold));
        } else {
            scr.append(color(fg::blue));
            scr.append(color(bg::green));
        }
        scr.append(std::to_string(i) + ": item");
        scr.append(color(bg::reset));
        scr.append(color(fg::reset));
        scr.append(color(style::reset));
        if (i < rows) scr.append("\n");
    }

    scr.append(move_cursor(rows / 2, cols / 2));

    scr.append(cursor_on());

    std::cout << scr << std::flush;
}

This will accumulate the following operations into a string:

  • Turn off the cursor (so that the terminal does not show the cursor quickly moving around the screen)
  • Move the cursor to the (1,1) position
  • Print the menu in color and highlighting the selected item (specified by pos)
  • Move the cursor to the middle of the screen
  • Turn on the cursor

and print the string. The std::flush ensures that the whole string ends up on the screen.

Saving the original screen and restoring it

It is a good habit to restore the original terminal screen (and cursor position) if we are going move the cursor around and draw (as in the previous section). To do that, call the save_screen() method:

Term::Terminal term;
term.save_screen();

This issues the proper ANSI sequences to the terminal to save the screen. The Terminal's destructor will then automatically issue the corresponding sequences to restore the original screen and the cursor position.

Keyboard input

The final step is to enable keyboard input. To do that, one must set the terminal in a so called "raw" mode:

Terminal term(true);

On Linux and macOS, this disables terminal input buffering, thus every key press is immediately sent to the application (otherwise one has to press ENTER before any input is sent). On Windows, this turns on ANSI keyboard sequences for key presses.

The Terminal's destructor then properly restores the terminal to the original mode on all platforms.

One can then wait and read individual keys and do something based on that, such as (see menu.cpp):

int key = term.read_key();
switch (key) {
    case Key::ARROW_UP: if (pos > 1) pos--; break;
    case Key::ARROW_DOWN: if (pos < rows) pos++; break;
    case 'q':
    case Key::ESC:
          on = false; break;
}

Now we have all the features that are needed to write any terminal application. See kilo.cpp for an example of a simple full screen editor.

Similar Projects

Colors

Libraries to handle color output.

C++:

Drawing

JavaScript:

Prompt

Libraries to handle a prompt in terminals.

C and C++:

Python:

General TUI libraries

C and C++:

Python:

Go:

Rust:

JavaScript:

cpp-terminal's People

Contributors

certik avatar dpoerio avatar guillaumelepape avatar kinetictheory avatar krsch avatar martinrenou avatar mcwertgaming avatar wolfv avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.