You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: readme.md
+55-19Lines changed: 55 additions & 19 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,7 +1,7 @@
1
1
# Oof (omnipotent output friend)
2
-
It's common for C++ programs to write output to the console. But consoles are far more capable than what they are usually used for. The magic lies in the so-called [Virtual Terminal sequences](https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences) (sometimes also confusingly called ["escape codes"](https://en.wikipedia.org/wiki/ANSI_escape_code)): These cryptic character sequences allow complete control over position, color and other properties of written characters. Your omnipotent output friend (*oof*) wraps this in a single C++ header.
2
+
It's common for C++ programs to write output to the console. But consoles are far more capable than what they are usually used for. The magic lies in the so-called [Virtual Terminal sequences](https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences) (sometimes also confusingly called ["escape codes"](https://en.wikipedia.org/wiki/ANSI_escape_code)): These cryptic character sequences allow complete control over position, color and other properties of written characters. Oof is a single C++ header that wraps these in a convenient way
3
3
4
-
On top of that, *oof* provides two special interfaces that can apply two different optimizations to the resulting stream of VT sequences, so that real-time outputs like those below are possible. Note that everything in these videos are letters in a console window:
4
+
On top of that, *oof* provides two special interfaces that heavily optimizes the resulting stream of VT sequences, so that real-time outputs like those below are possible. Note that everything in these videos are letters in a console window:
To use the library, include `oof.h`. As with most header-only libraries, that include must be preceeded with `#define OOF_IMPL` in **one** cpp file. That way, the function implementations are only compiled once.
16
16
17
17
The simple interface consists of the functions below.
@@ -59,7 +59,9 @@ auto move_up (int amount) -> ...
59
59
auto move_down (int amount) -> ...
60
60
```
61
61
62
-
Those functions return a magic type that can `operator<<` into `std::cout` and `std::wcout`. They also implicitly convert into `std::string` and `std::wstring` so you can build up your own strings with them. Example:
62
+
Index colors are simply colors refered to by an index. The colors behind the indices can be set with `set_index_color()`.
63
+
64
+
All these functions return a magic type that can `operator<<` into `std::cout` and `std::wcout`. Example:
They also implicitly convert into `std::string` and `std::wstring` so you can build up your own strings with them.
74
+
71
75
The type `oof::color` is just a `struct color { uint8_t red{}, green{}, blue{}; }`. You're encouraged to `std::bit_cast`, `reinterpret_cast` or `memcpy` your favorite 3-byte RGB color type into this.
72
76
73
77
## Performance and screen interfaces
74
78
Each printing command (regardless of wether it's `printf`, `std::cout` or something OS-specific) is pretty expensive. If performance is a priority, then consider building up your string first, and printing it in one go.
75
79
76
-
For real-time output, there is even more potential: If a program keeps track of the current state of the screen, it can avoid overriding cells that haven't changed. Even more: Changing the console cursor state (even without printing anything) is expensive. By avoiding unnecessary state changes, the performance can be optimized even more. Both of these optimizations are implemented in the `screen` and `pixel_screen` classes.
80
+
If you want real-time output, ie continuously changing what's on the screen, there's even more potential: If a program keeps track of the current state of the screen, it can avoid writing cells that haven't changed. And: Changing the console cursor state (even without printing anything) is expensive. By avoiding unnecessary state changes, the performance can be optimized even more. Both of these optimizations are implemented in the `screen` and `pixel_screen` classes.
81
+
82
+
`oof::screen` lets you define a rectangle of your console window, and set the state of every single cell. Its `get_string()` and `write_string(string_type&)` methods then output an optimized string to achieve the desired state. This assumes that the user didn't interfere - so don't. The difference between `get_string()` and `write_string(string_type&)` is that the passed string will be used to avoid allocating a new string. So it'll avoid memory waste. Almost always, the cost of building up the string is tiny vs the cost of printing, so don't worry about this too much.
The API in general is pretty low level compared to [other](https://github.com/ArthurSonzogni/FTXUI) [libraries](https://github.com/ggerganov/imtui), focused on high performance and modularity. You're encouraged to use it to build your own components. A good example for this is [the horizontal bars demo](demos/bars_demo.cpp):
77
100
78
-
`oof::screen` lets you define a rectangle of your console window, and set the state of every single cell. Its `get_string()` and `write_string(string_type&)` methods then output an *optimal* string to achieve the desired state. This assumes that the user didn't interfere - so don't.
`oof::pixel_screen` does the same for a niche case. Consoles always write text, ie letters. With most fonts, a single letter or cell is much taller than wide. By using a very special character that exactly fills the upper half of a cell, the visible area gets effectively transformed into (almost) square pixels. Exactly that's done by the `pixel_screen` class. There you only set colors and give up control of the letters themselves. Note that that type often has `halfline` type parameters. That's due to the fact that a "pixel" is now just half a line high.
103
+
Consoles always write text, ie letters. With most fonts, a single letter or cell is much taller than wide. By using a very special character that exactly fills the upper half of a cell, the visible area gets effectively transformed into (almost) square pixels. Exactly that's done by the `pixel_screen` class. There you only set colors and give up control of the letters themselves. Note that that type often has `halfline` type parameters. That's due to the fact that a "pixel" is now just half a line high.
104
+
105
+
### `oof::pixel_screen`
106
+
Example for `oof::pixel_screen` usage:
107
+
```c++
108
+
oof::pixel_screen screen(10, 10);
109
+
const auto t0 = std::chrono::high_resolution_clock::now();
110
+
while(true){
111
+
const auto t1 = std::chrono::high_resolution_clock::now();
The source code from the demo videos at the beginning is in this repo under /demos. That code uses a not-included and yet unreleased helper library (`s9w::`) for colors and math. But those aren't crucial if you just want to have a look.
81
125
82
126
## Notes
83
127
Consoles display text. Text is displayed via fonts. If you use letters that aren't included in your console font, that will result in visual artifacts - duh. This especially important for the `pixel_display` type, as it uses the mildly special [Block element](https://en.wikipedia.org/wiki/Block_Elements) '▀'. Some fonts may not have them included. Others do, but have them poorly aligned or sized - breaking up the even pixel grid.
84
128
85
-
This is a short overview of common monospce fonts and how well they are suited for "pixel" displays. Note that many are great in some sizes, ugly in others.
129
+
This is a short overview of common monospce fonts and how well they are suited for "pixel" displays. Note that many are great in some sizes but ugly in others.
86
130
87
131
|| Font name |
88
132
|---|---|
@@ -152,14 +196,6 @@ auto enable_vt_mode() -> void
152
196
}
153
197
```
154
198
155
-
Also note that while the VT sequences are universal, not all consoles programs and operating systems may support them. I only have access to a windows machine so I can't make any claims on other operating systems.
156
-
157
-
Be warned that the [new Windows Terminal](https://github.com/microsoft/terminal) has some problems with irregular frame pacing. It will report high FPS but "feel" much choppier than good old `cmd.exe`.
158
-
159
-
## Similar projects
160
-
Tere's [FXTUI](https://github.com/ArthurSonzogni/FTXUI) and [imtui](https://github.com/ggerganov/imtui) which write complete text-based user interfaces.
161
-
162
-
## TODO
163
-
- components to write higher-level compoennts
164
-
- helper functions
165
-
- demo videos, demo code, s9w lib
199
+
- If you use `pixel_screen` or `screen<std::wstring>` in combination with `std::wcout`, you might not see the output. That's because unicode output might need some magic to enable. Either google that, or use the recommended `fast_print` above as it's faster and doesn't suffer from these problems.
200
+
- While the VT sequences are universal, not all consoles programs and operating systems may support them. I only have access to a windows machine so I can't make any claims on other operating systems.
201
+
- The [new Windows Terminal](https://github.com/microsoft/terminal) has some problems with irregular frame pacing. It will report high FPS but "feel" much choppier than good old `cmd.exe`.
0 commit comments