{"id":13424467,"url":"https://github.com/s9w/oof","last_synced_at":"2026-01-24T23:05:06.890Z","repository":{"id":37560987,"uuid":"428338958","full_name":"s9w/oof","owner":"s9w","description":"Convenient, high-performance RGB color and position control for console output","archived":false,"fork":false,"pushed_at":"2024-09-21T16:35:42.000Z","size":150,"stargazers_count":830,"open_issues_count":3,"forks_count":37,"subscribers_count":10,"default_branch":"master","last_synced_at":"2024-10-26T23:55:16.817Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/s9w.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-11-15T16:25:54.000Z","updated_at":"2024-10-16T20:59:28.000Z","dependencies_parsed_at":"2024-05-01T22:14:23.797Z","dependency_job_id":"eb388d40-299d-4eba-9291-6f1fe4fed2d7","html_url":"https://github.com/s9w/oof","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s9w%2Foof","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s9w%2Foof/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s9w%2Foof/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/s9w%2Foof/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/s9w","download_url":"https://codeload.github.com/s9w/oof/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243775849,"owners_count":20346275,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T00:00:54.767Z","updated_at":"2026-01-24T23:05:01.810Z","avatar_url":"https://github.com/s9w.png","language":"C++","readme":"# Oof (omnipotent output friend)\nIt'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++20 header that wraps these in a convenient way.\n\n```c++\nfor (int i = 0; i \u003c 10; ++i){\n   std::cout \u003c\u003c oof::fg_color(oof::color{255 - i * 25});\n   std::cout \u003c\u003c oof::position(i, 2 * i) \u003c\u003c std::to_string(i);\n}\n```\n![123_example](https://user-images.githubusercontent.com/6044318/142816762-f1167a81-3d11-4b4a-85fc-d4edcdc06bf6.png)\n\n\n```c++\nconstexpr double values[]{0.54, 0.88, 0.42, 0.21, 0.33, 0.68, 0.91};\nfor(int i=0; i\u003cstd::size(values); ++i){\n   std::cout \u003c\u003c oof::underline(true) \u003c\u003c std::format(\"value {}\", i) \u003c\u003c oof::reset_formatting() \u003c\u003c \": \";\n   const oof::color color{ 255, static_cast\u003cint\u003e(255 - values[i] * 255), 0 };\n   std::cout \u003c\u003c oof::fg_color(color) \u003c\u003c std::format(\"{:.2f}\\n\", values[i]) \u003c\u003c oof::reset_formatting();\n}\n```\n![values_example](https://user-images.githubusercontent.com/6044318/142819817-d1fc16fc-01e7-4a49-a908-33ed4b7a7c37.png)\n\nOn top of that, *oof* provides two special interfaces that heavily optimize the resulting stream of VT sequences, so that real-time outputs like those below are possible. The following videos are showcases - everything in them are letters in a console window:\n\nhttps://user-images.githubusercontent.com/6044318/142469815-ce680909-9151-4322-85aa-01dc9ba29c1d.mp4\n\nhttps://user-images.githubusercontent.com/6044318/142469820-f7af6525-d1ce-4d09-95c9-458297add315.mp4\n\nhttps://user-images.githubusercontent.com/6044318/142469849-e359955d-fa3a-47d9-9a81-74905ee2f8fd.mp4\n\nhttps://user-images.githubusercontent.com/6044318/142469871-39f34712-f05e-4f8a-818a-b023081c1eee.mp4\n\n## Usage\nTo 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.\n\nThe simple interface consists of the functions below.\n\n```c++\n// Sets the foreground RGB color\nauto fg_color(const color\u0026 col) -\u003e fg_rgb_color_sequence;\n\n// Sets the background RGB color\nauto bg_color(const color\u0026 col) -\u003e bg_rgb_color_sequence;\n\n// Sets the foreground indexed color. Index must be in [1, 255]\nauto fg_color(int index) -\u003e fg_index_color_sequence;\n\n// Sets the background indexed color. Index must be in [1, 255]\nauto bg_color(int index) -\u003e bg_index_color_sequence;\n\n// Sets the indexed color. Index must be in [1, 255]\nauto set_index_color(int index, const color\u0026 col) -\u003e set_index_color_sequence;\n\n// Sets the underline state\nauto underline(bool new_value = true) -\u003e underline_sequence;\n\n// Sets the bold state. Warning: Bold is not supported by all console, see readme\nauto bold(bool new_value = true) -\u003e bold_sequence;\n\n// Sets cursor visibility state. Recommended to turn off before doing real-time displays\nauto cursor_visibility(bool new_value) -\u003e cursor_visibility_sequence;\n\n// Resets foreground- and background color, underline and bold state\nauto reset_formatting() -\u003e reset_sequence;\n\n// Clears the screen\nauto clear_screen() -\u003e clear_screen_sequence;\n\n// Sets the cursor position. Zero-based ie 0, 0 is first line, first column\nauto position(int line, int column) -\u003e position_sequence;\nauto vposition(int line) -\u003e vposition_sequence;\nauto hposition(int column) -\u003e hposition_sequence;\n\n// Moves the cursor a certain amount\nauto move_left (int amount) -\u003e move_left_sequence;\nauto move_right(int amount) -\u003e move_right_sequence;\nauto move_up   (int amount) -\u003e move_up_sequence;\nauto move_down (int amount) -\u003e move_down_sequence;\n```\n\nIndex colors are simply colors referred to by an index. The colors behind the indices can be set with `set_index_color()`.\n\nAll these functions return a magic type that can `operator\u003c\u003c` into `std::cout` and `std::wcout`. Example:\n```c++\nstd::cout \u003c\u003c oof::fg_color({ 255, 100, 100 }) \u003c\u003c \"This is red\\n\";\nstd::cout \u003c\u003c \"Still the same - state was changed!\\n\";\nstd::cout \u003c\u003c oof::reset_formatting() \u003c\u003c oof::hposition(10) \u003c\u003c \"All back to normal\\n\";\n```\n\n![example](https://user-images.githubusercontent.com/6044318/142437248-a999738c-2191-4ccc-be78-132685e2169c.png)\n\nThey also implicitly convert into `std::string` and `std::wstring` so you can build up your own strings with them.\n\nThe type [`oof::color`](https://github.com/s9w/oof/blob/master/oof.h#L12-L26) is mostly just a `struct color { uint8_t red{}, green{}, blue{}; }`. It does have convenience constructors for integer component parameters that get automatically `static_cast`ed into `uint8_t`. And it can be constructed with a single value, which will result in a grayscale color. You're encouraged to `std::bit_cast`, `reinterpret_cast` or `memcpy` your favorite 3-byte RGB color type into this.\n\n## Performance and screen interfaces\nEach 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.\n\nIf you want real-time output, ie continuously changing what's on the screen, there's even more potential: By keeping track of the current screen state, *oof* avoids writing to cells that haven't changed. And: Changing the console cursor state (even without printing anything) is expensive. Avoiding unnecessary state changes is key. Both of these optimizations are implemented in the `screen` and `pixel_screen` classes.\n\nWith [`oof::screen`](https://github.com/s9w/oof/blob/master/oof.h#L147-L175) you define a rectangle in your console window and set the state of every single cell. Its `get_string()` and `write_string(string_type\u0026)` 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\u0026)` is that the passed string will be reused to avoid allocating a new string. Almost always, the time to build up the string is tiny vs the time it takes to print, so don't worry about this too much.\n\nExample for `oof::screen` usage:\n```c++\noof::screen scr(10, 3, 0, 0, ' ');\nfor(uint64_t i=0; ; ++i){\n   int j = 0;\n   for (auto\u0026 cell : scr) {\n      cell.m_letter = 'A' + (j + i) % 26;\n      cell.m_format.m_bg_color.red = j * 8;\n      ++j;\n   }\n   std::cout \u003c\u003c scr.get_string();\n}\n```\n![screen_example](https://user-images.githubusercontent.com/6044318/142577018-cc25f98e-0572-4179-ac65-ffa79964d25c.gif)\n\nThe 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):\n\n![bars_demo](https://user-images.githubusercontent.com/6044318/142583233-c026da81-815e-4486-9588-b02ecd9c6ac8.gif)\n\nConsoles 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.\n\n### `oof::pixel_screen`\nExample for [`oof::pixel_screen`](https://github.com/s9w/oof/blob/master/oof.h#L191-L220) usage:\n```c++\noof::pixel_screen screen(10, 10);\nconst auto t0 = std::chrono::high_resolution_clock::now();\nwhile(true){\n   const auto t1 = std::chrono::high_resolution_clock::now();\n   const double seconds = std::chrono::duration\u003cdouble\u003e(t1-t0).count();\n\n   for (oof::color\u0026 pixel : screen) {\n      pixel.red   = 127.5 + 127.5 * std::sin(1.0 * seconds);\n      pixel.green = 127.5 + 127.5 * std::sin(2.0 * seconds);\n      pixel.blue  = 127.5 + 127.5 * std::sin(3.0 * seconds);\n   }\n   fast_print(screen.get_string());\n}\n```\n![pixel_screen_example](https://user-images.githubusercontent.com/6044318/142581841-66a235d1-d1e8-4f02-b7e7-2c9889a321e6.gif)\n\nThe source code from the demo videos at the beginning is in this repo under [demos/](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.\n\n## Notes\nConsoles 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.\n\nThis is a short overview of common monospace fonts and how well they are suited for \"pixel\" displays. Note that many are great in some sizes but ugly in others.\n\n| | Font name |\n|---|---|\n| Great fonts | Fira Mono, Anka/Coder, Cascadia Code, Courier New, Hack, Lucida Console, Source Code pro, Pragmata Pro, Consolas |\n| Mediocre | Inconsolata, Iosevka |\n| Broken or awful | Input mono, Menlo, Office Code pro |\n\n### Bold\nThe `bold` sequence is special. Some consoles ignore it entirely, some implement is as actual bold and others implement it as \"bright\" - slightly altering the colors of affected letters. Check yours before you use it. `cmd.exe` interprets it as bright. The Windows Terminal however correctly implements it as bold since v1.11, if you set `\"intenseTextStyle\": \"bold\"`. See [this article](https://devblogs.microsoft.com/commandline/windows-terminal-preview-1-11-release/#intense-text-style).\n\n### Errors\nYou can provide an error function that gets called when something goes wrong. This mostly catches obvious usage errors (negative sizes, out of bounds indices etc), so feel free to ignore this. If you want, this is how:\n```c++\nauto my_error_function(const std::string\u0026 msg) -\u003e void\n{\n   std::cerr \u003c\u003c std::format(\"ERROR! msg: {}\\n\", msg);\n   std::terminate();\n}\n\noof::error_callback = my_error_function;\n```\n\n## Performance \u0026 OS-Specific details\nUnder windows, `printf` and `std::cout` are very slow. They're completely fine to use for static outputs but they're unsuitable real-time displays. It's much faster to use Windows own [WriteConsole()](https://docs.microsoft.com/en-us/windows/console/writeconsole) directly. A ready-to use wrapper that works for `std::string` and `std::wstring` would be:\n```c++\ntemplate\u003ctypename char_type\u003e\nauto fast_print(const std::basic_string\u003cchar_type\u003e\u0026 sss) -\u003e void\n{\n   HANDLE const output_handle = GetStdHandle(STD_OUTPUT_HANDLE);\n   const auto char_count = static_cast\u003cDWORD\u003e(sss.length());\n   if constexpr (std::is_same_v\u003cchar_type, char\u003e)\n      WriteConsoleA(output_handle, sss.c_str(), char_count, nullptr, nullptr);\n   else\n      WriteConsoleW(output_handle, sss.c_str(), char_count, nullptr, nullptr);\n}\n```\n\nIf *oof* doesn't produce the output expected, it may be that the VT mode is not enabled. To enable it, this function can be used on windows:\n```c++\nauto enable_vt_mode() -\u003e void\n{\n   HANDLE const handle = GetStdHandle(STD_OUTPUT_HANDLE);\n   if (handle == INVALID_HANDLE_VALUE)\n   {\n      std::terminate(); // error handling\n   }\n\n   DWORD dwMode = 0;\n   if (!GetConsoleMode(handle, \u0026dwMode))\n   {\n      std::terminate(); // error handling\n   }\n\n   if (dwMode \u0026 ENABLE_VIRTUAL_TERMINAL_PROCESSING)\n   {\n      // VT mode is already enabled\n      return;\n   }\n\n   dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;\n   if (!SetConsoleMode(handle, dwMode))\n   {\n      std::terminate(); // error handling\n   }\n}\n```\n\n- If you use `pixel_screen` or `screen\u003cstd::wstring\u003e` 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.\n- 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.\n- 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`.\n","funding_links":[],"categories":["CLI","Console"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fs9w%2Foof","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fs9w%2Foof","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fs9w%2Foof/lists"}