jupyter-xeus / cpp-terminal Goto Github PK
View Code? Open in Web Editor NEWC++ library for writing multiplatform terminal applications
Home Page: https://jupyter-xeus.github.io/cpp-terminal/
License: Other
C++ library for writing multiplatform terminal applications
Home Page: https://jupyter-xeus.github.io/cpp-terminal/
License: Other
Currently cpp-terminal
only supports 4bit colors. We should extend it to also support 24 bit colors:
https://en.wikipedia.org/wiki/ANSI_escape_code#24-bit
We have to add a new function color_24bit
(similar to the current function color
which is only 4 bit) and use the ESC[ 38;2;⟨r⟩;⟨g⟩;⟨b⟩ m
sequence to set it.
Then we need to extend the Window
class, currently the color is only represented by the 3bit color vector<fg>
. Instead we have to change it to vector<Color>
, where the Color
struct is something like this:
struct Color {
enum { bit24, bit3, bit4 } tag;
union {
struct {
char R, G, B;
};
fg color_fg;
fgB color_fgB;
};
};
and then the set_fg
method would become:
void set_fg(size_t x, size_t y, fg c) {
m_fg[(y-1)*w+(x-1)].tag = bit3;
m_fg[(y-1)*w+(x-1)].color_fg = c;
}
void set_fg_24bit(size_t x, size_t y, char R, char G, char B) {
m_fg[(y-1)*w+(x-1)].tag = bit24;
m_fg[(y-1)*w+(x-1)].R = R;
m_fg[(y-1)*w+(x-1)].G = G;
m_fg[(y-1)*w+(x-1)].B = B;
}
Also the render
method would need to be updated.
As we have discussed in #104 we have to fix issues with the alt keys. The current macro tends to cast values greater than 127 into a char type which results into a runtime error because a char can only hold values from 0 to 127.
The doctest.h
file in the root of the repository claims to be gerated by cmake - but that's a lie. I would sugest to let it really be generated by cmake, or at least get updated every now and then. We are using 2.3.4, newest version is 2.4.5. I would remove the file and setup doctest as cmake sub-project.
Another solution would be moving to gtest (the unit testing framework, I usually use). It's basially the same but if I got it right, it can even test colors and stuff like that. I'll definietely test it a bit in terms of benefits for cpp-terminal and I understand, if you don't want to move yet. Do you know that testing framework at all?
Hey,
i encountered some problems with cpp-terminal while working inside of visual studio code. The window class causes wrong colors on wrong parts on the screen and visual studio crashes sometimes while debugging. I will look into the cause (and will try to fix that) later on.
The crash happens randomly, i'm not sure yet on how to replicate it.
I have thought of some things, I would like to to do with cpp-terminal to make it better for it's purpose - providing a way to write cross-platform terminal applications. I thought of doing those steps in the particular order:
cpp-terminal/base.hpp
which provides the raw functions to handle console input (basically the color functions and such)cpp-terminal/window.hpp
which provides a manages "window", basically the window classcpp-terminal/input.hpp
which provides the input functionscpp-terminal/prompt.hpp
which provides the prompt and things like (#72)cpp-terminal/tools.hpp
wich provides all function which are just meant for being used inside of the library/bot apply-clang-format
as issue comment)render()
algorithm (#71)get_term_size_slow()
(#115)I will make this Issue pinned on the repository and update it when needed to keep track on the things we want to do. Also for other peoples and possible contributors. Help is always appreciated!
I was thinking about making the readme a bit prettier by adding CI checks, version (if added) and/or thing like code quality badges to the readme. Maybe some smaller visual changes too. I'll make a sample of what I think of soon.
Even after the fix of the alt and ctrl keys ALT+N remains broken. I'm not sure yet what's exactly the problem, but it's inside of the utf8_to_utf32()
functions because it's throwing a runtime_error when you press it. The console output is:
[mcwertgaming@archlinux examples]$ /home/mcwertgaming/git/cpp-terminal/build/examples/prompt
Interactive prompt.
* Use Ctrl-D to exit.
* Use Enter to submit.
* Features:
- Editing (Keys: Left, Right, Home, End, Backspace)
- History (Keys: Up, Down)
- Multi-line editing (use Alt-Enter to add a new line)
> Runtime error: Expected more bytes in UTF8 encoded string 1,1 ]
[mcwertgaming@archlinux examples]$
This would wait for y/n
answer.
@wolfv knows the exact requirements.
This is meant to add the usuall dotfiles. I'm thinking of a .gitingore and a .clang-format, but can be extended with .clang-tidy (static analysis), .gitpod (an online version of visual studio code, runs a free IDE on the web with everything included inside the file), GitHub codespaces (GitHub version of gitpod, runs visual studio code remote containers for free on the web). I don't know if all are actually usefull, but I wanted to note that they exist. There are even more, but I would prefer to keep it to the minimum required files.
Hello,
i want to ask, if I can split the header files of this library into header and source files. Yes, I am aware of the desciption advertising this library as a "header only library", but I see many disadvantages here.
First of all we face many bad practices:
And I want to note, that this library is quiet small and consist of two headers. I would like to move the source parts into a source file and keep the class and function definitions inside of the header file (we can even move both header together, something you were / are already planing to do, if I saw that right). As most people (hopefully) use cpp-terminal as cmake dependency, there wouldn't be anything different for them at all, if they just copy the files they would have to copy two or 3/4 files and add the source file/s to their compile target - I wouldn't call that much more complicated.
Critizism is appreceated, it's an Idea - I hope this is not against the goal of this library!
Best regards
Damon
color
, does it make sense to also provide a function to remove color from strings?#include <regex>
...
inline std::string remove_color(std::string const &colored_string) {
std::regex color_regex("\033[" "[[:digit:]]+[m]");
return std::regex_replace(colored_string, color_regex, "");
}
inline
to most of the functions in terminal.hh
to avoid possible multiple definition (e.g. lib1->terminal.h, lib2-> terminal.h, app1-> {lib1, lib2}).terminal_base.h
, I had to add #define _WINSOCKAPI_
just before #include <windows.h>
to avoid multiple definition collisions with winsock2.h
that I use in my code. I don't really understand this, but this work-around seems to do the right thing.terminal.h
, I needed to make a few changes to avoid warnings:
- write("\0338"); // restore current cursor position
+ write("\033" "8"); // restore current cursor position
- write("\0337"); // save current cursor position
+ write("\033" "7"); // save current cursor position
int
to size_t
to avoid build warnings under MSVC (with option /W4
). There were also some character types that needed to be more specific (char
-> char32_t
, etc.). I'll create a patch file or PR for these so you can review them. Example:- uint32_t codepoint, state = UTF8_ACCEPT;
+ uint32_t codepoint;
+ uint8_t state = UTF8_ACCEPT;
Remove the get_term_size_slow() function, because it's already improved and will probably not needed anyway.
... or use CMake?
That would make it easy to have terminal on conda-forge, and we could use it right away in mamba.
I just tested it and it looks very impressive and also very much like what we need so I am happy to help out here.
Basically moving cpp-terminal's functions between the header files so the provided set of functions makes actually sense. This will also include cutting the BaseTerminal and Terminal classes so they actually make sense.
That way the CI will fail if any compiler issues a warning, so that we can catch any regressions.
Term::Terminal
should have an option to call std::cout << Term::cursor_off();
in it's constructor and should re-enable the cursor in the destructor.
Also a small fix for terminals like the linux TTY:
Terminal::savescreen() should also do std::cout << Term::clear_screen_buffer() << std::flush;
after saving the screen to avoid left over characters on terminals that do no support the save and restore ANSI codes. Also the restore function should do the same and move the cusrsor to 1,1 before restoring the screen to avoid lefover characters and black space (otherwise the prompt would be on the line where the cursor was).
The prompt example uses 100% of one of my CPU core, and the fan starts as soon as I execute the binary.
Maybe read_key
should sleep a bit between reads? Sleeping 10ms might be good enough for easing the CPU while still having a good user experience when typing?
int read_key() const
{
int key;
using namespace std::chrono_literals;
std::this_thread::sleep_for(10ms);
while ((key = read_key0()) == 0) { std::this_thread::sleep_for(10ms); }
return key;
}
And enforce it on the CI.
So that one does not need to move them to a cpp file.
Currently the render method always prints the whole Window from scratch.
Instead, we should add an optional argument Window &old_window
which would represent the current state of the screen and render
would do an efficient diff against it and only update things that changed (characters and colors).
This should be done after #69 is fixed, as that issue will extend how colors are represented in Window
.
And specify any caveats if any for each.
Adding versioning to cpp-terminal would allow developers depending on cpp-terminal to depend on a specific version of cpp-terminal and don't have to fear their applications to be broken by breaking changes for example.
This would include using the VERSION variable in cmake and providing a dedicated header cmake will generate and the creation of git tags (like on every pull-request merge).
At investigating a better solution to the sscanf problem I notices that the code is really complicated (or at least to me). I would be happy to "de-complicate" the code by changing workarounds around some things to shorter and more simple solutions (for example you created two while loops inside get_cursor_position() with creating the i var above and are using it inside both while loops and outside. I would like to make both while loops into for loops and move all parts which need the i car inside that loop. The contents of the same functions are really complicated too, like you created inline functions like write()
, but it's contents was just std::cout << s << std::flush;
I thought you would just write something elsewhere but not into stdout. So why not just doing that immediately where its needed? It would help others to understand your code faster). I have to add that I'm new to ANSI and this library is the first I'm contributing to and i respect if you don't like some things in upstream.
Changes i would like to make:
#ifdef
makes it really hard to tell what functions are providedbase_terminal
class outside into the Term
namespace (if it isn't already, my IDE is a bit confused with all the system dependent code)get_term_size()
static, as only windows needs members, but maybe you could just make a workaround (that would enable the usage without needing the object of it's class)I just wanted to ask, if these changes are welcomed inside this project. As I will use this library for some small games I write to practice programming, I'm willing to improve it with my own ideas.
Greetings!
Damon Leven
I thought about maybe creating a github action which just checks, if the commit is formated by clang-format to prevent pushing of unformated code into the master master branch. The same could be done with clang-tidy as soon as it's set up as well.
As discussed in #119 it would be a good feature to make it possible to use Dear ImGUI inside of cpp-terminal.
I have created the release tag V1.0.0
. Do you agree on doing it like that? or would you prefer to do it different? And do you want to make a new release on every merged pull-request? Or rather like once a week or something like that?
Thanks to Dominic Poerio we now have a working multi-line REPL in LFortran:
https://gitlab.com/lfortran/lfortran/-/blob/44bad104711796fa0b4743460ea832c0ee04682a/src/bin/lfortran.cpp#L226
https://gitlab.com/lfortran/lfortran/-/blob/44bad104711796fa0b4743460ea832c0ee04682a/src/bin/tpl/cpp-terminal/prompt0.h
We should port it to cpp-terminal
so that other projects can also use it.
I think it would be good to provide warnings for problematic keys. That would include:
Is there maybe any way to check for throw errors on compile time based on function input? Or something like that?
The way to test it is to add the input into a file, and then redirect stdin
from this file. An example would be a string like
"Hello Wold!" and check that we get the correct string. The <LEFT ARROW>
and <DELETE>
are meant to be the corresponding escape sequences for those keys.
Due to this define:
https://github.com/bminor/glibc/blob/0b262ca4c64cd9042576ddb9969607c0ea1187d7/bits/termios.h#L288
user code can break. To fix it, we should but
#undef B0
right after including termios.h
. The same for all the other constants.
Reported by @KineticTheory.
cpp-terminal is currently not checking that much for legal inputs on some functions. For example: Term::Window_24bit scr(0, 0, 10, 10);
returns a segmentation fault, because the used vectors of the window class can't have negative places. we should simply throw a std::runtime_error
here. That would include:
Hello there, I would like to use cpp-terminal in Mamba. Specifically we'd like to use it for a yes/no prompt with Ctrl+C support. What's the recommended version to use right now? Would you recommend using latest master, or v0.1, or would you consider publishing a more recent stable release?
I would like to move all files used for testing into a tests/
folder to make the repository more cleaned up.
Move terminal_base.h
into terminal.h
. That way there is just one header and it's easier to distribute.
Until now, cpp-terminal is documented in examples and small snippets in the readme only. A wiki would give new users better a better understanding on what cpp-terminal is and how you can use it the best.
Help is really apreceated!
CodeQL is a security scanning tool by github, free with github actions. It's scanning code for security riscs and some bad practices.
https://securitylab.github.com/tools/codeql
Probably not a measurement for "This code is secure", but a good thing to avoid misstakes that are already documented.
I thought, if we move the write function to Term::write() (disqussed in #81), shouldn't we just move all function there including the console_is_a_tty outside of the class into the namespace? I think that would look better. You can use using namespace Term
to just use the namesapce and skip the Term::
part.
We have to unify the color type as follows:
diff --git a/cpp-terminal/terminal.cpp b/cpp-terminal/terminal.cpp
index 69a303f..ff076b8 100644
--- a/cpp-terminal/terminal.cpp
+++ b/cpp-terminal/terminal.cpp
@@ -624,6 +624,13 @@ void Term::Window_24bit::clear() {
}
bool Term::Window_24bit::rgb_equal(rgb& rgb_one, rgb rgb_two) {
+ if (rgb_two.color_type == rgb::type_basic) {
+ std::cout << rgb_two.color_type_union.basic << std::endl;
+ } else {
+ std::cout << rgb_two.color_type_union.rgb.r << std::endl;
+ std::cout << rgb_two.color_type_union.rgb.g << std::endl;
+ std::cout << rgb_two.color_type_union.rgb.b << std::endl;
+ }
return rgb_one.r == rgb_two.r && rgb_one.b == rgb_two.b &&
rgb_one.g == rgb_two.g;
}
diff --git a/cpp-terminal/terminal.h b/cpp-terminal/terminal.h
index a7b384c..c0e4999 100644
--- a/cpp-terminal/terminal.h
+++ b/cpp-terminal/terminal.h
@@ -196,6 +196,33 @@ class Window_24bit {
std::vector<char32_t> chars; // the characters in row first order
struct rgb {
unsigned int r, g, b;
+ enum ColorType {
+ type_rgb, type_basic
+ } color_type;
+ union ColorTypeUnion {
+ struct {
+ unsigned int r, g, b;
+ } rgb;
+ enum BasicColors {
+ black = 30,
+ red = 31,
+ green = 32,
+ yellow = 33,
+ blue = 34,
+ magenta = 35,
+ cyan = 36,
+ white = 37,
+ reset = 39,
+ gray = 90,
+ bright_red = 91,
+ bright_green = 92,
+ bright_yellow = 93,
+ bright_blue = 94,
+ bright_magenta = 95,
+ bright_cyan = 96,
+ bright_white = 97
+ } basic;
+ } color_type_union;
};
std::vector<rgb> m_fg;
std::vector<rgb> m_bg;
This provides an example how to declare it and how to access it.
A good rule seems to be:
Improvements, redesigns, etc. would go into terminal.h
. It seems it's possible to keep it under, say, 2000 lines of code and have all this functionality in. So terminal.h
would act as a multiplatform ncurses replacement.
Everything else (such as widgets) would go into other header files (or a separate library).
This scope of terminal.h
should be documented in the README.
I think it would be a great thing to have functions to determine things like color support (4bit / 24bit) and maybe even some more information on what the current terminal supports. That would also make it possible to include fall back functions in window24 for example (we could fall back to 4bit colors, if 24bit is not supported). As Ncurses and such libraries are already implementing that feature I think this won't that of a big deal. I have looked it up quickly, but there are no ANSI code for that, also it would require a system dependent solution (on linux probably even terminal dependent). Windows has some functions inside of windows.h
to do that, linux has the TERM
variable. The problem with the term variable though is that Terminals like xterm and the visual studio integrated terminal set it to xterm-256color
wich means that it supports 256 colors (8bit colors) but doesn't tells if it supports true color (24bit) for example. The actuall problem here is that Kitty (my primary terminal) sets it to "xterm-kitty" which means that if we just check that variable to be equal to "xterm-256color" we wouldn't cover all terminals.
But I think this would be a great thing, if we are done with #73.
Term::Terminal term
in the main program.Currently the single line prompt is in the terminal.h
header file. We should split it into its own header file and possible a namespace.
We need to merge the Term::Window
and Term::WIndow_24bit
classes some day to avoid having the same code twice (and fix things twice too). The first idea of:
struct Color {
enum { bit24, bit3, bit4 } tag;
union {
struct {
char R, G, B;
};
fg color_fg;
fgB color_fgB;
};
};
Didn't worked, because of the vectors and forbidden anonymous structures and unions. A different approach is required here.
There is a problem in windows.
in the menu example.
when you resize the height the menu doubles up because it not clearing or over writing the full console.
I think in windows you need to write to the consoles buffer directly.
We have brainstormed a few ideas, but I think we haven't figured out which organization (or a new one) should we moved this under.
/cc @wolfv, @martinRenou
Hello,
I'm using your library for a small project. It works well for what I want to do. However, I have an error because I'm using an other library for reading, writing and modifying json (https://github.com/nlohmann/json). This lib declares two variables UTF8_ACCEPT and UTF8_REJECT that are also declared in your library. Since the json library variables are declared as static constexpr and yours using #define, compilation fail. I manage to fix this issue by declaring the two variables in your lib as static constexpr as follow, at lines 448 and 449 of terminal.h
:
static constexpr std::uint8_t UTF8_ACCEPT = 0;
static constexpr std::uint8_t UTF8_REJECT = 0xf;
Since the json library is quite common lib in projects, this minor change can be done to avoid clash between the libs.
Regards.
Guillaume Le Pape
As my project broke (again) because the master master branch of cpp-terminal has functions which are not declared as static and included into two source files that get compiled together, I would like to add another source file to the stand-alone test to avoid such mistakes in the future.
We already dicussed in #68 that we could move is_stdin_a_tty() and is_stdout_a_tty() outside of the class. As it is already a static member, no object is required. It's just a question of preference. What do you think? Rather leave everything as it is, or rather move both functions out of the class into the namespace?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.