|
1 | | -# Terminal |
| 1 | +<p align="center"> |
| 2 | + <img src="https://raw.githubusercontent.com//jupyter-xeus/cpp-terminal/readme/logo.svg" alt="CPP-Terminal logo"/> |
| 3 | +</p> |
2 | 4 |
|
3 | | -`Terminal` is small header only library for writing terminal applications. It |
4 | | -works on Linux, macOS and Windows (in the native `cmd.exe` console). It |
| 5 | +`CPP-Terminal` is a small and simple library for writing platform independent terminal applications. It works on Windows, MacOS and Linux and offers a simple programming interface. It |
5 | 6 | supports colors, keyboard input and has all the basic features to write any |
6 | 7 | terminal application. |
7 | 8 |
|
8 | | -It has a small core ([terminal_base.h](cpp-terminal/terminal_base.h)) that has a |
9 | | -few platform specific building blocks, and a platform independent library |
10 | | -written on top using the ANSI escape sequences |
11 | | -([terminal.h](cpp-terminal/terminal.h)). |
| 9 | +Until 2021, CPP-Terminal used to be a single header library. Now, CPP-Terminal consists out of multiple small and usage oriented headers: |
| 10 | +- `cpp-terminal/base.hpp`: everything for basic Terminal control |
| 11 | +- `cpp-terminal/input.hpp`: functions for gathering input |
| 12 | +- `cpp-terminal/prompt.hpp`: some variations of different prompts |
| 13 | +- `cpp-terminal/window.hpp`: a fully managed terminal window for terminal user interfaces (TUI) |
| 14 | +- `cpp-terminal/version.hpp`: macros with cpp-terminal's version number |
12 | 15 |
|
13 | | -This design has the advantage of having only a few lines to maintain on each |
14 | | -platform, and the rest is platform independent. We intentionally limit |
15 | | -ourselves to a subset of features that all work on all platforms natively. That |
16 | | -way, any application written using `Terminal` will work everywhere out of the |
17 | | -box, without emulation. At the same time, because the code of `Terminal` is |
18 | | -short, one can easily debug it if something does not work, and have a full |
19 | | -understanding how things work underneath. |
| 16 | +The library uses private header for platform dependent code: |
| 17 | +- `cpp-terminal/private/conversion.hpp`: Various conversions |
| 18 | +- `cpp-terminal/private/platform.hpp`: platform dependant code |
20 | 19 |
|
21 | | -## Examples |
22 | | - |
23 | | -Several examples are provided to show how to use `Terminal`. Every example |
24 | | -works natively on all platforms: |
25 | | - |
26 | | -* [kilo.cpp](examples/kilo.cpp): the [kilo](https://github.com/snaptoken/kilo-src) text editor |
27 | | - ported to C++ and `Terminal` instead of using Linux specific API. |
28 | | -* [menu.cpp](examples/menu.cpp): Shows a menu on the screen |
29 | | -* [keys.cpp](examples/keys.cpp): Listens for keys, showing their numbers |
30 | | -* [colors.cpp](examples/colors.cpp): Shows how to print text in color to standard output |
31 | | - |
32 | | -## How to use |
33 | | - |
34 | | -The easiest is to just copy the two files `terminal.h` and `terminal_base.h` |
35 | | -into your project. Consult the examples how to use it. You can just use the |
36 | | -`terminal_base.h`, which is a standalone header file, if you only want the low |
37 | | -level platform dependent functionality. Use `terminal.h`, which depends on |
38 | | -`terminal_base.h`, if you also want the platform independent code to more |
39 | | -easily print escape sequences and/or read and translate key codes. |
40 | | - |
41 | | -## Documentation |
42 | | - |
43 | | -We will start from the simplest concept (just printing a text on the screen) |
44 | | -and then we will keep adding more features such as colors, cursor movement, |
45 | | -keyboard input, etc., and we will be explaining how things work as we go. |
46 | | - |
47 | | -### Printing |
48 | | - |
49 | | -To print text into standard output, one can use `std::cout` in C++: |
50 | | -```c++ |
51 | | -std::cout << "Some text" << std::endl; |
52 | | -``` |
53 | | -One does not need `Terminal` for that. |
54 | | - |
55 | | -### Colors |
56 | | - |
57 | | -To print colors and other styles (such as bold), use the `Term::color()` |
58 | | -function and `Term::fg` enum for foreground, `Term::bg` enum for background and |
59 | | -`Term::style` enum for different styles (see the `colors.cpp` example): |
60 | | -```c++ |
61 | | -#include <cpp-terminal/terminal.h> |
62 | | -using Term::color; |
63 | | -using Term::fg; |
64 | | -using Term::bg; |
65 | | -using Term::style; |
66 | | -int main() { |
67 | | - try { |
68 | | - Term::Terminal term; |
69 | | - std::string text = "Some text with " |
70 | | - + color(fg::red) + color(bg::green) + "red on green" |
71 | | - + color(bg::reset) + color(fg::reset) + " and some " |
72 | | - + color(style::bold) + "bold text" + color(style::reset) + "."; |
73 | | - std::cout << text << std::endl; |
74 | | - } catch(...) { |
75 | | - throw; |
76 | | - } |
77 | | - return 0; |
78 | | -} |
79 | | -``` |
80 | | -One must call `Term::fg::reset`, `Term::bg::reset` and `Term::style::reset` to |
81 | | -reset the given color or style. |
82 | | - |
83 | | -One must create the `Term::Terminal` instance. In this case, the `Terminal` |
84 | | -does nothing on Linux and macOS, but on Windows it checks if the program is |
85 | | -running withing the Windows console and if so, enables ANSI escape codes in the |
86 | | -console, which makes the console show colors properly. One must have a |
87 | | -`try/catch` block in the main program to ensure the `Terminal`'s destructor |
88 | | -gets called (even if an unhandled exception occurs), which will put the console |
89 | | -into the original mode. |
90 | | - |
91 | | -The program might decide to print colors not only if it is in a terminal (which |
92 | | -can be checked by `term.is_stdout_a_tty()`), but also when not run in a |
93 | | -terminal, some examples: |
94 | | - |
95 | | -* Running on a CI, e.g. AppVeyor, Travis-CI and Azure Pipelines all show colors |
96 | | - properly |
97 | | -* Using `less -r` shows colors properly (but `less` does not) |
98 | | -* Printing colors in program output in a Jupyter notebook (and then possibly |
99 | | - converting such colors from ANSI sequences to html) |
100 | | - |
101 | | -An example when the program might not print colors is when the standard output |
102 | | -gets redirected to a file (say, compiler error messages using `g++ a.cpp > |
103 | | -log`), and then the file is read directly in some editor. |
104 | | - |
105 | | -The `color()` function always returns a string with the proper ANSI sequence. |
106 | | -The program might wrap this in a macro, that will check some program variable |
107 | | -if it should print colors and only call `color()` if colors should be printed. |
108 | | - |
109 | | -### Cursor movement and its visibility |
110 | | - |
111 | | -The next step up is to allow cursor movement and other ANSI sequences. For |
112 | | -example, here is how to render a simple menu (see `menu.cpp` example) and print |
113 | | -it on the screen: |
114 | | -```c++ |
115 | | -void render(int rows, int cols, int pos) |
116 | | -{ |
117 | | - std::string scr; |
118 | | - scr.reserve(16*1024); |
119 | | - |
120 | | - scr.append(cursor_off()); |
121 | | - scr.append(move_cursor(1, 1)); |
122 | | - |
123 | | - for (int i=1; i <= rows; i++) { |
124 | | - if (i == pos) { |
125 | | - scr.append(color(fg::red)); |
126 | | - scr.append(color(bg::gray)); |
127 | | - scr.append(color(style::bold)); |
128 | | - } else { |
129 | | - scr.append(color(fg::blue)); |
130 | | - scr.append(color(bg::green)); |
131 | | - } |
132 | | - scr.append(std::to_string(i) + ": item"); |
133 | | - scr.append(color(bg::reset)); |
134 | | - scr.append(color(fg::reset)); |
135 | | - scr.append(color(style::reset)); |
136 | | - if (i < rows) scr.append("\n"); |
137 | | - } |
138 | | - |
139 | | - scr.append(move_cursor(rows / 2, cols / 2)); |
140 | | - |
141 | | - scr.append(cursor_on()); |
142 | | - |
143 | | - std::cout << scr << std::flush; |
144 | | -} |
145 | | -``` |
146 | | -This will accumulate the following operations into a string: |
| 20 | +CPP-Terminal tries to be a small and simple replacement for ncurses. This approach keeps the code small and maintainable, but also easy to extend it's functionality. We limit ourselves to a subset of features that work on all supported platforms without needing to worry about style differences or other changes. Any application written with `CPP-Terminal` will work everywhere out of the box natively, without emulation or extra work. The small codebase makes CPP-Terminal easy to debug and extend, as well as understanding what happens behind the scenes in the library's core. |
147 | 21 |
|
148 | | -* Turn off the cursor (so that the terminal does not show the cursor |
149 | | - quickly moving around the screen) |
150 | | -* Move the cursor to the `(1,1)` position |
151 | | -* Print the menu in color and highlighting the selected item (specified by |
152 | | - `pos`) |
153 | | -* Move the cursor to the middle of the screen |
154 | | -* Turn on the cursor |
155 | 22 |
|
156 | | -and print the string. The `std::flush` ensures that the whole string ends up on |
157 | | -the screen. |
158 | | -
|
159 | | -### Saving the original screen and restoring it |
160 | | -
|
161 | | -It is a good habit to restore the original terminal screen (and cursor |
162 | | -position) if we are going move the cursor around and draw (as in the previous |
163 | | -section). To do that, call the `save_screen()` method: |
164 | | -```c++ |
165 | | -Term::Terminal term; |
166 | | -term.save_screen(); |
167 | | -``` |
168 | | -This issues the proper ANSI sequences to the terminal to save the screen. The |
169 | | -`Terminal`'s destructor will then automatically issue the corresponding |
170 | | -sequences to restore the original screen and the cursor position. |
171 | | - |
172 | | -### Keyboard input |
173 | | - |
174 | | -The final step is to enable keyboard input. To do that, one must set the |
175 | | -terminal in a so called "raw" mode: |
176 | | -```c++ |
177 | | -Terminal term(true); |
178 | | -``` |
179 | | -On Linux and macOS, this disables terminal input buffering, thus every key |
180 | | -press is immediately sent to the application (otherwise one has to press ENTER |
181 | | -before any input is sent). On Windows, this turns on ANSI keyboard sequences |
182 | | -for key presses. |
183 | | -
|
184 | | -The `Terminal`'s destructor then properly restores the terminal to the original |
185 | | -mode on all platforms. |
186 | | -
|
187 | | -One can then wait and read individual keys and do something based on |
188 | | -that, such as (see `menu.cpp`): |
189 | | -```c++ |
190 | | -int key = term.read_key(); |
191 | | -switch (key) { |
192 | | - case Key::ARROW_UP: if (pos > 1) pos--; break; |
193 | | - case Key::ARROW_DOWN: if (pos < rows) pos++; break; |
194 | | - case 'q': |
195 | | - case Key::ESC: |
196 | | - on = false; break; |
197 | | -} |
198 | | -``` |
199 | | - |
200 | | -Now we have all the features that are needed to write any terminal application. |
201 | | -See `kilo.cpp` for an example of a simple full screen editor. |
202 | | - |
203 | | -## Similar Projects |
204 | | - |
205 | | -### Colors |
206 | | - |
207 | | -Libraries to handle color output. |
208 | | - |
209 | | -C++: |
210 | | - |
211 | | -* [rang](https://github.com/agauniyal/rang) |
212 | | - |
213 | | -### Drawing |
214 | | - |
215 | | -JavaScript: |
216 | | - |
217 | | -* [node-drawille](https://github.com/madbence/node-drawille) |
218 | | - |
219 | | -### Prompt |
220 | | - |
221 | | -Libraries to handle a prompt in terminals. |
222 | | - |
223 | | -C and C++: |
224 | | - |
225 | | -* [readline](https://tiswww.case.edu/php/chet/readline/rltop.html) |
226 | | -* [libedit](http://thrysoee.dk/editline/) |
227 | | -* [linenoise](https://github.com/antirez/linenoise) |
228 | | -* [replxx](https://github.com/AmokHuginnsson/replxx) |
229 | | - |
230 | | -Python: |
231 | | - |
232 | | -* [python-prompt-toolkit](https://github.com/prompt-toolkit/python-prompt-toolkit) |
| 23 | +## Examples |
| 24 | +We have created serval examples to show possible use cases of CPP-Terminal and to get you started more quickly. Every example works natively on all platforms in the exact same way: |
| 25 | +- [base.cpp](examples/base.cpp): basic color, style and unicode demo |
| 26 | +- [kilo.cpp](examples/kilo.cpp): the [kilo](https://github.com/snaptoken/kilo-src) text editor |
| 27 | + ported to C++ and `CPP-Terminal` instead of using Linux specific API |
| 28 | +- [menu.cpp](examples/menu.cpp): An interactive menu using only the contents of `cpp-terminal/base.hpp` |
| 29 | +- [menu_window.cpp](examples/menu_window.cpp): An interactive menu using the fully managed windowing system from `cpp-terminal/window.hpp` |
| 30 | +- [keys.cpp](examples/keys.cpp): Interactively shows the keys pressed |
233 | 31 |
|
234 | | -### General TUI libraries |
| 32 | +## Supported platforms |
235 | 33 |
|
236 | | -C and C++: |
| 34 | +| Platform | Supported versions | Coverage by unit test | |
| 35 | +| -------- | ------------------ | --------------------- | |
| 36 | +| Windows | 10 and higher* | MSVC | |
| 37 | +| MacOS | All supported | Apple-clang | |
| 38 | +| Linux | All supported | GCC | |
237 | 39 |
|
238 | | -* [curses](https://en.wikipedia.org/wiki/Curses_%28programming_library%29) and [ncurses](https://www.gnu.org/software/ncurses/ncurses.html) |
239 | | -* [Newt](https://en.wikipedia.org/wiki/Newt_(programming_library)) |
240 | | -* [termbox](https://github.com/nsf/termbox) |
241 | | -* [FTXUI](https://github.com/ArthurSonzogni/FTXUI) |
242 | | -* [ImTui](https://github.com/ggerganov/imtui) |
| 40 | +* Windows versions prior 10 have no proper terminal supports. They are lacking ANSI support which is required by CPP-Terminal. |
243 | 41 |
|
244 | | -Python: |
| 42 | +## How to use |
245 | 43 |
|
246 | | -* [urwid](http://urwid.org/) |
247 | | -* [python-prompt-toolkit](https://github.com/prompt-toolkit/python-prompt-toolkit) |
248 | | -* [npyscreen](http://www.npcole.com/npyscreen/) |
249 | | -* [curtsies](https://github.com/bpython/curtsies) |
| 44 | +Adding CPP-Terminal to your project is really easy. We have collected various ways with easy how-to's [in our documentation](https://github.com/jupyter-xeus/cpp-terminal/wiki/Adding-CPP-Terminal-to-your-ptoject). |
250 | 45 |
|
251 | | -Go: |
| 46 | +## Documentation |
252 | 47 |
|
253 | | -* [gocui](https://github.com/jroimartin/gocui) |
254 | | -* [clui](https://github.com/VladimirMarkelov/clui) |
255 | | -* [tview](https://github.com/rivo/tview) |
256 | | -* [termbox-go](https://github.com/nsf/termbox-go) |
257 | | -* [termui](https://github.com/gizak/termui) |
258 | | -* [tcell](https://github.com/gdamore/tcell) |
| 48 | +CPP-Terminal's documentation can be found [here](https://github.com/jupyter-xeus/cpp-terminal/wiki). |
259 | 49 |
|
260 | | -Rust: |
| 50 | +## Contributing |
261 | 51 |
|
262 | | -* [tui-rs](https://github.com/fdehau/tui-rs) |
| 52 | +Contributing to CPP-Terminal is highly appreciated and can be done in more ways than code. Extending it's functionality, reporting or fixing bugs and extending the documentations are just a few of them. |
263 | 53 |
|
264 | | -JavaScript: |
| 54 | +## License |
265 | 55 |
|
266 | | -* [blessed](https://github.com/chjj/blessed) and [blessed-contrib](https://github.com/yaronn/blessed-contrib) |
| 56 | +CPP-Terminal is licensed under the terms of [the MIT License](https://github.com/jupyter-xeus/cpp-terminal/blob/master/LICENSE) by Ondřej Čertík. |
0 commit comments