├── .buckconfig ├── .gitignore ├── .travis.yml ├── BUCK ├── BUILD.bazel ├── LICENSE ├── README.md ├── WORKSPACE ├── apps └── main.cpp ├── buckaroo.lock.toml ├── buckaroo.toml ├── include └── rxterm │ ├── components │ ├── component.hpp │ ├── flowlayout.hpp │ ├── maxwidth.hpp │ ├── progress.hpp │ ├── stacklayout.hpp │ └── text.hpp │ ├── image.hpp │ ├── pixel.hpp │ ├── reflow.hpp │ ├── style.hpp │ ├── terminal.hpp │ └── utils.hpp ├── subdir_glob.bzl └── travis ├── before-install-linux.sh └── before-install-osx.sh /.buckconfig: -------------------------------------------------------------------------------- 1 | [project] 2 | ignore = .git 3 | 4 | [parser] 5 | default_build_file_syntax = SKYLARK 6 | 7 | [cxx] 8 | should_remap_host_platform = true 9 | 10 | [cxx#macosx-x86_64] 11 | should_remap_host_platform = true 12 | cxxflags = -std=c++14 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .buckconfig.local 2 | .buckconfig.d 3 | .buckd 4 | buck-out 5 | BUCKAROO_DEPS 6 | buckaroo 7 | buckaroo_macros.bzl 8 | buckaroo-stacktrace.log 9 | bazel-* 10 | 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | sudo: true 3 | dist: trusty 4 | 5 | addons: 6 | apt: 7 | sources: 8 | - ubuntu-toolchain-r-test 9 | packages: 10 | - g++-6 11 | - gcc-6 12 | 13 | os: 14 | - linux 15 | - osx 16 | 17 | env: 18 | - BUCKAROO_VERSION=buckaroo-redux-alpha-15 19 | 20 | cache: 21 | directories: 22 | - $HOME/.buckaroo/cache 23 | - $TRAVIS_BUILD_DIR/bin 24 | 25 | osx_image: xcode9.3 26 | 27 | before_install: 28 | - ./travis/before-install-$TRAVIS_OS_NAME.sh 29 | 30 | script: 31 | - ./bin/buckaroo install 32 | - ./bin/buck build -c ui.superconsole=DISABLED //:rxterm 33 | -------------------------------------------------------------------------------- /BUCK: -------------------------------------------------------------------------------- 1 | load('//:subdir_glob.bzl', 'subdir_glob') 2 | 3 | # Generated by Buckaroo - https://buckaroo.pm 4 | load('//:buckaroo_macros.bzl', 'buckaroo_deps') 5 | 6 | cxx_library( 7 | name = 'rxterm', 8 | header_namespace = 'rxterm', 9 | exported_headers = subdir_glob([ 10 | ('rxterm/include', '**/*.h'), 11 | ('rxterm/include', '**/*.hpp'), 12 | ]), 13 | headers = subdir_glob([ 14 | ('rxterm/detail', '**/*.h'), 15 | ('rxterm/detail', '**/*.hpp'), 16 | ]), 17 | licenses = [ 18 | 'LICENSE', 19 | ], 20 | deps = buckaroo_deps(), 21 | visibility = [ 22 | 'PUBLIC' 23 | ], 24 | ) 25 | 26 | cxx_binary( 27 | name = 'main', 28 | srcs = [ 29 | 'rxterm/apps/main.cpp' 30 | ], 31 | deps = [ 32 | ':rxterm', 33 | ], 34 | ) 35 | -------------------------------------------------------------------------------- /BUILD.bazel: -------------------------------------------------------------------------------- 1 | cc_library( 2 | name = "rxterm", 3 | srcs = [], 4 | strip_include_prefix = "include", 5 | hdrs = glob([ 6 | "include/**/*.hpp", 7 | ]), 8 | visibility = [ 9 | "//visibility:public", 10 | ], 11 | ) 12 | 13 | cc_binary( 14 | name = "main", 15 | srcs = [ 16 | "apps/main.cpp" 17 | ], 18 | deps = [ 19 | ":rxterm", 20 | ], 21 | ) 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 LoopPerfect 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RxTerm ⚡⌨️ 2 | 3 | A C++ library for functional-reactive terminals. RxTerm is a lean alternative to ncurses for many applications. 4 | 5 | ![terminal](https://cdn-images-1.medium.com/max/800/1*G897-DuUI_0q6W9VKcIjZg.gif) 6 | 7 | 8 | ## Build Instructions 9 | 10 | The library builds with [Buckaroo](https://buckaroo.pm) and either [Buck](https://www.buckbuild.com) or [Bazel](https://bazel.build). It requires a C++ 14 compiler. 11 | 12 | ```bash 13 | buckaroo install 14 | 15 | # Buck 16 | buck build :rxterm 17 | 18 | # Bazel 19 | bazel build :rxterm 20 | ``` 21 | 22 | To run the demo: 23 | 24 | ```bash 25 | # Buck 26 | buck run :main 27 | 28 | # Bazel 29 | BAZEL_CXXOPTS="-std=c++14" bazel run :main 30 | ``` 31 | 32 | 33 | ## Usage 34 | 35 | To install this library into your project: 36 | 37 | ```bash= 38 | buckaroo add github.com/loopperfect/rxterm@* 39 | ``` 40 | 41 | Refer to `rxterm/apps/main.cpp` for an example. 42 | 43 | 44 | ## Progress 45 | 46 | * [x] ANSI Output 47 | * [x] Reusable components 48 | * [ ] Managed console input 49 | * [ ] Terminal width detection 50 | * [ ] Windows support 51 | 52 | Contributions are very welcome! 💖 53 | 54 | ## Windows Support 55 | 56 | - VT100 escape sequences are enabled by default in msys2, mingw and cygwin. 57 | - Cmd.exe and Powershell may require explicit activation of terminal mode. The easiest way is to call system(" ") at the start of your application. 58 | 59 | More info: 60 | https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences 61 | https://larsfosdal.blog/2018/08/31/did-you-know-that-you-can-enable-vt100-terminal-emulation-in-powershell-as-well-as-the-cmd-window/ 62 | -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LoopPerfect/rxterm/8679b5379bb94db412b1c4fd0998ac8c066401ce/WORKSPACE -------------------------------------------------------------------------------- /apps/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | using namespace rxterm; 14 | 15 | auto renderToTerm = [](auto const& vt, unsigned const w, Component const& c) { 16 | return vt.flip(c.render(w).toString()); 17 | }; 18 | 19 | int main() { 20 | 21 | using namespace std::chrono_literals; 22 | using namespace std::string_literals; 23 | 24 | VirtualTerminal vt; 25 | 26 | auto superProgressBar = [](auto x, auto y, auto z) -> FlowLayout<> { 27 | return { 28 | Text("3 segment progressbar: "), 29 | FlowLayout<>{ 30 | MaxWidth(20, Progress(x)), 31 | MaxWidth(20, Progress(y)), 32 | MaxWidth(20, Progress(z)) 33 | } 34 | }; 35 | }; 36 | 37 | auto w = VirtualTerminal::width(); 38 | if (!w) w = 80; 39 | for (int i = 0; i < 101; ++i) { 40 | vt = renderToTerm(vt, w, superProgressBar(0.01 * i, 0.02 * i, 0.03 * i)); 41 | std::this_thread::sleep_for(200ms); 42 | } 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /buckaroo.lock.toml: -------------------------------------------------------------------------------- 1 | manifest = "0f1d3b31f7364b81a313af826b735afbeafd8f31d81a2608f8558df85754fb34" 2 | 3 | -------------------------------------------------------------------------------- /buckaroo.toml: -------------------------------------------------------------------------------- 1 | targets = [ "//:rxterm" ] 2 | -------------------------------------------------------------------------------- /include/rxterm/components/component.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RXTERM_COMPONENTS_COMPONENT_HPP 2 | #define RXTERM_COMPONENTS_COMPONENT_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace rxterm { 10 | 11 | struct Renderable { 12 | virtual Image render(unsigned const w)const = 0; 13 | 14 | virtual ~Renderable(){} 15 | }; 16 | 17 | auto isImage(Image const& x){ return x; } 18 | 19 | template 20 | auto getImage(unsigned const w, R const& r) -> decltype(isImage(r.render(w))) { 21 | return r.render(w); 22 | } 23 | 24 | template 25 | auto getImage(unsigned const w, R const& r) -> decltype(isImage(r(w))) { 26 | return r(w); 27 | } 28 | 29 | template 30 | auto getImage(unsigned const w, R const& r) -> decltype(isImage(r)) { 31 | return w; 32 | } 33 | 34 | template 35 | auto getImage(unsigned const w, R const& r) -> decltype(Text{{}, toString(r)}.render(w)) { 36 | return Text{{}, toString(r)}.render(w); 37 | } 38 | 39 | 40 | 41 | struct Component : Renderable { 42 | 43 | template 44 | Component(T const& model) 45 | : ptr{std::make_shared>(model)} 46 | {} 47 | 48 | virtual Image render(unsigned const w)const override{ 49 | return ptr->render(w); 50 | } 51 | 52 | 53 | template 54 | struct Model : Renderable { 55 | T const model; 56 | 57 | Model(T const& data) : model{data} {} 58 | 59 | virtual Image render(unsigned const w)const override{ 60 | return getImage(w, model); 61 | } 62 | }; 63 | 64 | std::shared_ptr ptr; 65 | 66 | }; 67 | 68 | } 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /include/rxterm/components/flowlayout.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RXTERM_COMPONENTS_FLOWLAYOUT_HPP 2 | #define RXTERM_COMPONENTS_FLOWLAYOUT_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace rxterm { 12 | 13 | template 14 | struct FlowLayout { 15 | std::vector children; 16 | Pixel bg; 17 | 18 | FlowLayout(std::vector const& children, Pixel bg = Pixel{}) 19 | : children(children) 20 | , bg{bg} 21 | {} 22 | 23 | template 24 | FlowLayout(Xs const&...xs) 25 | : children{xs...} 26 | , bg{Pixel{}} 27 | {} 28 | 29 | template 30 | FlowLayout(Pixel const& bg, Xs const&...xs) 31 | : children{xs...} 32 | , bg{bg} 33 | {} 34 | 35 | Image render(unsigned const maxWidth) const { 36 | 37 | auto const images = map(children, [maxWidth](auto const& c) { 38 | auto const image = c.render(maxWidth); 39 | return image; 40 | }); 41 | 42 | unsigned width = 0; 43 | unsigned x = 0; 44 | unsigned curHeight = 0; 45 | std::vector xs; 46 | std::vector ys; 47 | unsigned y = 0; 48 | 49 | for (auto const& image : images) { 50 | if (x + image.width > maxWidth) { 51 | x = 0; 52 | xs.push_back(x); 53 | x += image.width; 54 | y += curHeight; 55 | ys.push_back(y); 56 | curHeight = image.height; 57 | width = std::max({width, image.width}); 58 | } else { 59 | xs.push_back(x); 60 | ys.push_back(y); 61 | x += image.width; 62 | width = std::max(width, x); 63 | curHeight = std::max(curHeight, image.height); 64 | } 65 | } 66 | 67 | auto const height = y + curHeight; 68 | auto canvas = Image::create(width, height, bg); 69 | 70 | auto yi = ys.cbegin(); 71 | auto xi = xs.cbegin(); 72 | for(auto const& image : images) { 73 | canvas = drawOnBackground(canvas, *xi, *yi, image); 74 | ++xi; 75 | ++yi; 76 | } 77 | 78 | return canvas; 79 | } 80 | }; 81 | 82 | } 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /include/rxterm/components/maxwidth.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RXTERM_COMPONENTS_MAXWIDTH_HPP 2 | #define RXTERM_COMPONENTS_MAXWIDTH_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace rxterm { 10 | 11 | auto percent(float const& p) { 12 | return [=](auto const& width) -> unsigned { 13 | return p/100 * width; 14 | }; 15 | } 16 | 17 | auto px(unsigned const& width) { 18 | return [=](auto) -> unsigned { 19 | return width; 20 | }; 21 | } 22 | 23 | struct MaxWidth { 24 | std::function maxWidth; 25 | Component const c; 26 | 27 | MaxWidth(std::function maxWidth, Component const& c) 28 | : maxWidth{maxWidth} 29 | , c{c} 30 | {} 31 | 32 | MaxWidth(unsigned const& maxWidth, Component const& c) 33 | : maxWidth{px(maxWidth)} 34 | , c{c} 35 | {} 36 | 37 | Image render(unsigned const width)const { 38 | return c.render(std::min(width, maxWidth(width))); 39 | } 40 | }; 41 | 42 | } 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /include/rxterm/components/progress.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RXTERM_COMPONENTS_PROGRESS_HPP 2 | #define RXTERM_COMPONENTS_PROGRESS_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace rxterm { 10 | 11 | template 12 | T clamp(T const l, T const r, T const x) { 13 | return std::min(r, std::max(l,x)); 14 | } 15 | 16 | struct Progress { 17 | float const progress; 18 | Pixel const bg; 19 | Pixel const fg; 20 | 21 | Progress(float const p, 22 | Pixel const& bg = Pixel{' ', {Color::Cyan}}, 23 | Pixel const& fg = Pixel{' ', {Color::Blue}}) 24 | : progress{p} 25 | , bg{bg} 26 | , fg{fg} 27 | {} 28 | 29 | Image render(unsigned const width)const { 30 | auto const p = clamp(0.0f, 1.0f, progress); 31 | return drawOnBackground( 32 | Image::create(width, 1, bg), 33 | 0, 0, 34 | Image::create(static_cast(width*p), 1, fg)); 35 | } 36 | }; 37 | 38 | } 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /include/rxterm/components/stacklayout.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RXTERM_COMPONENTS_STACKLAYOUT_HPP 2 | #define RXTERM_COMPONENTS_STACKLAYOUT_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace rxterm { 12 | 13 | template 14 | struct StackLayout { 15 | std::vector children; 16 | Pixel bg; 17 | 18 | StackLayout(std::vector const& children, Pixel bg = Pixel{}) 19 | : children(children) 20 | , bg{bg} 21 | {} 22 | 23 | template 24 | StackLayout(Xs const&...xs) 25 | : children{xs...} 26 | , bg{Pixel{}} 27 | {} 28 | 29 | template 30 | StackLayout(Pixel const& bg, Xs const&...xs) 31 | : children{xs...} 32 | , bg{bg} 33 | {} 34 | 35 | Image render(int maxWidth) const { 36 | auto const images = map(children, [maxWidth](auto const& c) { 37 | return c.render(maxWidth); 38 | }); 39 | 40 | auto const width = std::max_element( 41 | images.begin(), images.end(), [](auto const& a, auto const& b) { 42 | return a.width < b.width; 43 | })->width; 44 | 45 | auto const height = std::accumulate( 46 | images.begin(), 47 | images.end(), 48 | 0, [](auto const& a, auto const& b) { 49 | return a + b.height; 50 | }); 51 | 52 | auto canvas = Image::create(width, height, bg); 53 | 54 | auto y = 0; 55 | for(auto const& image : images) { 56 | canvas = drawOnBackground(canvas, 0, y, image); 57 | y += image.height; 58 | } 59 | 60 | return canvas; 61 | } 62 | }; 63 | 64 | 65 | } 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /include/rxterm/components/text.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RXTERM_COMPONENTS_TEXT_HPP 2 | #define RXTERM_COMPONENTS_TEXT_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace rxterm { 12 | 13 | 14 | struct Text { 15 | Style const style; 16 | std::string const content; 17 | 18 | template 19 | Text(Style const& style, T const& content) 20 | : style{style} 21 | , content{toString(content)} 22 | {} 23 | 24 | template 25 | Text(T const& content) 26 | : style{Style{}} 27 | , content{toString(content)} 28 | {} 29 | 30 | Image render(unsigned const maxWidth) const { 31 | auto const lines = split(reflow(maxWidth, content), "\n"); 32 | auto const height = lines.size(); 33 | auto const width = std::max_element( 34 | lines.begin(), 35 | lines.end(), 36 | [](auto const& a, auto const& b){ 37 | return a.size() < b.size(); 38 | })->size(); 39 | 40 | auto image = Image::create(width, height, Pixel{'\0', style}); 41 | unsigned y = 0; 42 | for(auto const& line : lines) { 43 | for(unsigned int x=0; x < line.size(); ++x) { 44 | image(x,y).c = line[x]; 45 | } 46 | ++y; 47 | } 48 | 49 | return image; 50 | } 51 | 52 | }; 53 | 54 | } 55 | #endif 56 | -------------------------------------------------------------------------------- /include/rxterm/image.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RXTERM_IMAGE_HPP 2 | #define RXTERM_IMAGE_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | namespace rxterm { 12 | 13 | struct Image { 14 | 15 | std::vector pixels; 16 | unsigned width; 17 | unsigned height; 18 | 19 | Image( 20 | std::vector const& pixels, 21 | unsigned const width, 22 | unsigned const height) 23 | : pixels{pixels} 24 | , width{width} 25 | , height{height} 26 | {} 27 | 28 | Image() = default; 29 | Image(Image const&) = default; 30 | Image& operator=(Image const&) = default; 31 | 32 | 33 | static Image create(unsigned const& width, unsigned const& height, Pixel const& pixel = Pixel{'\0', Style::None()}) { 34 | return { 35 | std::vector(width*height, pixel), 36 | width, 37 | height 38 | }; 39 | } 40 | 41 | 42 | 43 | Pixel const& operator()(unsigned int x, unsigned int y)const { 44 | return pixels[y*width+x]; 45 | } 46 | 47 | 48 | Pixel& operator()(unsigned int x, unsigned int y) { 49 | return pixels[y*width+x]; 50 | } 51 | 52 | 53 | std::string toString()const { 54 | std::string str = ""; 55 | 56 | auto prev = Style{}; 57 | for (unsigned int y=0; y < height; ++y) { 58 | for (unsigned int x=0; x < width; ++x) { 59 | auto const& pixel = (*this)(x, y); 60 | auto const current = diff(prev, pixel.c ? pixel.style : Style::Default()); 61 | char c = pixel.c ? pixel.c : ' '; 62 | str += current.toString() + c; 63 | prev = pixel.c != '\0' ? pixel.style : Style::Default(); 64 | } 65 | str+="\n"; 66 | } 67 | return str; 68 | } 69 | }; 70 | 71 | struct Sprite { 72 | Image image; 73 | int x = 0; 74 | int y = 0; 75 | }; 76 | 77 | Image drawOnBackground(Image canvas, unsigned const& sx, unsigned const& sy, Image const& fg) { 78 | for (unsigned int y=0; y < fg.height; ++y) { 79 | for (unsigned int x=0; x < fg.width; ++x) { 80 | auto& p = canvas( 81 | clip(sx+x, 0u, canvas.width), 82 | clip(sy+y, 0u, canvas.height) 83 | ); 84 | auto const& q = fg(x, y); 85 | p = Pixel{ 86 | (q.c)? q.c : p.c, 87 | Style{ 88 | q.style.fg, 89 | (q.c)? q.style.bg : p.style.bg, 90 | q.style.font 91 | } 92 | }; 93 | } 94 | } 95 | return canvas; 96 | } 97 | 98 | Image drawOnBackground(Image canvas, Sprite const& s) { 99 | return drawOnBackground(canvas, s.x, s.y, s.image); 100 | } 101 | 102 | Image drawOnBackground(Image const& canvas) { 103 | return canvas; 104 | } 105 | 106 | template 107 | auto drawOnBackground(Image const& canvas, X const& s, Xs const&...xs) 108 | -> decltype( s.image, s.x, s.y , drawOnBackground(canvas, xs...)) { 109 | return drawOnBackground( 110 | drawOnBackground(canvas, s), 111 | xs... 112 | ); 113 | } 114 | 115 | Image drawOnBackground(Image canvas, std::vector const& sprites) { 116 | for (auto const& s: sprites) { 117 | canvas = drawOnBackground(canvas, s); 118 | } 119 | 120 | return canvas; 121 | } 122 | 123 | } 124 | 125 | 126 | #endif 127 | -------------------------------------------------------------------------------- /include/rxterm/pixel.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RXTERM_PIXEL_HPP 2 | #define RXTERM_PIXEL_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace rxterm { 8 | 9 | struct Pixel { 10 | char c ='\0'; 11 | Style style; 12 | 13 | std::string toString() const { 14 | return style.toString() + c; 15 | } 16 | }; 17 | 18 | } 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /include/rxterm/reflow.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RXTERM_REFLOW_HPP 2 | #define RXTERM_REFLOW_HPP 3 | 4 | namespace rxterm { 5 | 6 | std::string reflow(unsigned const& width, std::string const& s) { 7 | 8 | if (width == 0) return ""; 9 | if (s.size() <= width) { return s; } 10 | 11 | std::string content = ""; 12 | size_t b = 0; 13 | while (b < s.size()) { 14 | auto t = s.substr(b, width+1) 15 | .find_last_of("\n\t.,!?:;- "); 16 | 17 | if (t == std::string::npos) { 18 | content += s.substr(b, width); 19 | b += width; 20 | if (b < s.size()) { 21 | content += '\n'; 22 | } 23 | } else { 24 | if (s[t] == '\n') { 25 | content += s.substr(b, t); 26 | b = t+1; 27 | } else if (t >= width) { 28 | content += s.substr(b, width) + '\n'; 29 | b += width; 30 | } else { 31 | content += s.substr(b, t+1) + '\n'; 32 | b += t+1; 33 | } 34 | } 35 | } 36 | 37 | return content; 38 | } 39 | 40 | } 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /include/rxterm/style.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RXTERM_STYLE_HPP 2 | #define RXTERM_STYLE_HPP 3 | 4 | #include 5 | 6 | namespace rxterm { 7 | 8 | using std::string; 9 | 10 | enum class Font { 11 | None = 0, 12 | Default = 1, 13 | Bold = 1 << 1, // 1 14 | Faint = 1 << 2, // 2 15 | Roman = 1 << 3,// not Italic 16 | Italic = 1 << 4, // 3 17 | NotUnderline = 1 << 5, 18 | Underline = 1 << 6, //4 19 | Visible = 1 << 7, 20 | Hidden = 1 << 8, //8 21 | Uncrossed = 1 << 9, 22 | Crossed = 1 << 10, // 9 23 | Inherit = 1 << 11 24 | }; 25 | 26 | enum class FontColor { 27 | None = 0, 28 | Black = 1, 29 | Red = 2, 30 | Green = 3, 31 | Yellow = 4, 32 | Blue = 5, 33 | Magenta = 6, 34 | Cyan = 7, 35 | White = 8, 36 | Default = 9, 37 | Transparent = 10, 38 | Inherit = 11, 39 | 40 | BrightBlack = Black+60, 41 | BrightRed = Red+60, 42 | BrightGreen = Green+60, 43 | BrightYellow = Yellow+60, 44 | BrightBlue = Blue+60, 45 | BrightMagenta = Magenta+60, 46 | BrightCyan = Cyan+60, 47 | BrightWhite = White+60 48 | }; 49 | 50 | 51 | enum class Color { 52 | None = 0, 53 | Black = 1, 54 | Red = 2, 55 | Green = 3, 56 | Yellow = 4, 57 | Blue = 5, 58 | Magenta = 6, 59 | Cyan = 7, 60 | White = 8, 61 | Default = 9, 62 | Transparent = 10, 63 | Inherit = 11, 64 | 65 | BrightBlack = Black+60, 66 | BrightRed = Red+60, 67 | BrightGreen = Green+60, 68 | BrightYellow = Yellow+60, 69 | BrightBlue = Blue+60, 70 | BrightMagenta = Magenta+60, 71 | BrightCyan = Cyan+60, 72 | BrightWhite = White+60 73 | }; 74 | 75 | 76 | 77 | template 78 | constexpr auto composeMod(X x) { 79 | return x; 80 | } 81 | 82 | template 83 | constexpr auto composeMod(X x, Xs...xs) { 84 | auto const r = composeMod(xs...); 85 | auto const delim = (r.empty() || x.empty()) ? "" : ";"; 86 | return x + delim + r; 87 | } 88 | 89 | template 90 | constexpr auto computeMod(X x, Xs...xs) { 91 | auto const r = composeMod(x, xs...); 92 | return (r.empty()) ? "" : "\033[" + r + "m"; 93 | } 94 | 95 | FontColor isStyle(FontColor x) { return x; } 96 | Color isStyle(Color x) { return x; } 97 | Font isStyle(Font x) { return x; } 98 | 99 | FontColor toFontColor(FontColor c) { return c; } 100 | Color toColor(Color c) { return c; } 101 | Font toFont(Font f) { return f; } 102 | 103 | FontColor toFontColor(...) { return FontColor::None; } 104 | Color toColor(...) { return Color::None; } 105 | Font toFont(...) { return Font::None; } 106 | 107 | template 108 | constexpr FontColor getFontColor (Xs...xs) { 109 | return (FontColor)std::max({0, (int)toFontColor(xs)...}); 110 | } 111 | 112 | template 113 | constexpr Color getColor (Xs...xs) { 114 | return (Color)std::max({0, (int)toColor(xs)...}); 115 | } 116 | 117 | constexpr Font getFontStyle(Font f = Font::None) { 118 | return f; 119 | } 120 | 121 | 122 | 123 | template 124 | constexpr Font getFontStyle(X x, Xs...xs) { 125 | return Font( ((int)getFontStyle(toFont(x))) | ((int)getFontStyle(toFont(xs)...)) ); 126 | } 127 | 128 | 129 | bool has(Font x, Font y) { 130 | return (((int)x) & ((int)y)); 131 | } 132 | 133 | struct Style { 134 | Color bg; 135 | FontColor fg; 136 | Font font; 137 | 138 | template 139 | constexpr Style( 140 | Styles...styles) 141 | : bg{getColor(isStyle(styles)...)} 142 | , fg{getFontColor(isStyle(styles)...)} 143 | , font{getFontStyle(isStyle(styles)...)} 144 | {} 145 | 146 | constexpr static Style None(){ return {}; } 147 | constexpr static Style Default(){ return {Font::Default}; } 148 | 149 | string defaultMod() const { 150 | return has(font, Font::Default) ? "0" : ""; 151 | } 152 | 153 | 154 | string boldMod() const { 155 | return has(font, Font::Bold) ? "1" : ""; 156 | } 157 | 158 | string underlineMod() const { 159 | return has(font, Font::Underline) ? "4" : ""; 160 | } 161 | 162 | string faintMod() const { 163 | return has(font, Font::Faint) ? "2" : ""; 164 | } 165 | 166 | string italicMod() const { 167 | return has(font, Font::Italic) ? "3" : ""; 168 | } 169 | 170 | string hiddenMod() const { 171 | return has(font, Font::Hidden) ? "8" : ""; 172 | } 173 | 174 | string crossedMod() const { 175 | return has(font, Font::Crossed) ? "9" : ""; 176 | } 177 | 178 | string bgMod() const { 179 | return ((int)bg != 11) 180 | ? ((!(int)bg) ? "0" : std::to_string(40 + (int)bg -1)) 181 | : ""; 182 | } 183 | 184 | string fgMod() const { 185 | if (!(int)fg) return "0"; 186 | return ((int)fg != 11) 187 | ? ((!(int)bg) ? "0" : std::to_string(30 + (int)fg -1)) 188 | : ""; 189 | } 190 | 191 | std::string toString() const { 192 | return computeMod( 193 | defaultMod(), 194 | boldMod(), 195 | underlineMod(), 196 | faintMod(), 197 | italicMod(), 198 | hiddenMod(), 199 | crossedMod(), 200 | bgMod(), 201 | fgMod() 202 | ); 203 | } 204 | }; 205 | 206 | 207 | Style diff(Style const& a, Style const& b = Style::None() ) { 208 | bool keepBG = (a.bg == b.bg); 209 | bool keepFG = (a.fg == b.fg); 210 | bool keepFont = (a.font == b.font); 211 | 212 | int l = (int)a.font; 213 | int r = (int)b.font; 214 | // bool reset = (l & ~r) ? true : false; 215 | bool reset = (l & ~r); 216 | 217 | return Style { 218 | (keepBG && !reset) ? Color::Inherit : b.bg, 219 | (keepFG && !reset) ? FontColor::Inherit : b.fg, 220 | (keepFont && !reset) ? 221 | Font::Inherit : 222 | (Font)((r&((l&r)^r))|(static_cast(reset))) 223 | }; 224 | } 225 | 226 | } 227 | 228 | #endif 229 | -------------------------------------------------------------------------------- /include/rxterm/terminal.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RXTERM_TERMINAL_HPP 2 | #define RXTERM_TERMINAL_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef _WIN32 9 | #include 10 | #ifndef NOMINMAX 11 | #define NOMINMAX 12 | #endif 13 | #include 14 | #else 15 | #include 16 | #include 17 | #endif 18 | 19 | #include 20 | #include 21 | 22 | namespace rxterm { 23 | 24 | struct VirtualTerminal { 25 | std::string buffer; 26 | static unsigned width() { 27 | unsigned w; 28 | #ifdef _WIN32 29 | CONSOLE_SCREEN_BUFFER_INFO info; 30 | w = (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info)) 31 | ? info.srWindow.Right - info.srWindow.Left + 1 32 | : 0; 33 | #else 34 | winsize ws{}; 35 | ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws); 36 | w = ws.ws_col; 37 | #endif 38 | return w; 39 | } 40 | static unsigned height() { 41 | unsigned h; 42 | #ifdef _WIN32 43 | CONSOLE_SCREEN_BUFFER_INFO info; 44 | h = (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info)) 45 | ? info.srWindow.Bottom - info.srWindow.Top + 1 46 | : 0; 47 | #else 48 | winsize ws{}; 49 | ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws); 50 | h = ws.ws_row; 51 | #endif 52 | return h; 53 | } 54 | 55 | std::string computeTransition(std::string const& next) const { 56 | if(buffer == next) return ""; 57 | unsigned const n = std::count(buffer.begin(), buffer.end(), '\n'); 58 | return clearLines(n) + "\033[0m" + next; 59 | } 60 | 61 | static std::string hide() { return "\033[0;8m"; } 62 | 63 | VirtualTerminal flip(std::string const& next) const { 64 | auto const transition = computeTransition(next); 65 | if(transition.empty()) return *this; 66 | std::cout << transition << hide(); 67 | std::flush(std::cout); 68 | return VirtualTerminal{next}; 69 | } 70 | 71 | #ifdef _WIN32 72 | static std::once_flag initInstanceFlag; 73 | static DWORD initWindowsTerminal() { 74 | #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING 75 | #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 76 | #endif 77 | // Set output mode to handle virtual terminal sequences 78 | HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); 79 | if (hOut == INVALID_HANDLE_VALUE) return GetLastError(); 80 | DWORD dwMode = 0; 81 | if (!GetConsoleMode(hOut, &dwMode)) return GetLastError(); 82 | dwMode |= DWORD(ENABLE_VIRTUAL_TERMINAL_PROCESSING); 83 | if (!SetConsoleMode(hOut, dwMode)) return GetLastError(); 84 | return ERROR_SUCCESS; 85 | } 86 | 87 | VirtualTerminal() { 88 | std::call_once(initInstanceFlag, VirtualTerminal::initWindowsTerminal); 89 | } 90 | VirtualTerminal(const VirtualTerminal&) = default; 91 | VirtualTerminal& operator=(const VirtualTerminal&) = default; 92 | explicit VirtualTerminal(std::string buf) : buffer{std::move(buf)} {}; 93 | #endif 94 | }; 95 | 96 | #ifdef _WIN32 97 | std::once_flag VirtualTerminal::initInstanceFlag; 98 | #endif 99 | } 100 | 101 | #endif 102 | -------------------------------------------------------------------------------- /include/rxterm/utils.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RXTERM_UTILS_HPP 2 | #define RXTERM_UTILS_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace rxterm { 9 | 10 | auto toString(std::string const& x) -> std::string { 11 | return x; 12 | } 13 | 14 | template 15 | auto toString(T const& x) -> decltype(std::to_string(x)) { 16 | return std::to_string(x); 17 | } 18 | 19 | 20 | std::vector split(std::string const& str, const std::string& delimiter = "\n") { 21 | std::vector tokens; 22 | 23 | auto start = 0U; 24 | auto end = str.find(delimiter); 25 | while (end != std::string::npos) { 26 | tokens.push_back(str.substr(start, end - start)); 27 | start = end + delimiter.size(); 28 | end = str.find(delimiter, start); 29 | } 30 | 31 | if(start != str.size()) 32 | tokens.push_back(str.substr(start, str.size()-start)); 33 | 34 | return tokens; 35 | } 36 | 37 | 38 | template 39 | auto map(T const& data, F const& f) { 40 | std::vector result(data.size()); 41 | std::transform( 42 | data.begin(), data.end(), 43 | result.begin(), 44 | f); 45 | return result; 46 | } 47 | 48 | std::string repeat(unsigned n, std::string const& s) { 49 | std::string result = ""; 50 | for(unsigned i=0; i 79 | T clip(const T& n, const T& lower, const T& upper) { 80 | return std::max(lower, std::min(n, upper)); 81 | } 82 | 83 | } 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /subdir_glob.bzl: -------------------------------------------------------------------------------- 1 | """Provides utility macros for working with globs.""" 2 | 3 | def _paths_join(path, *others): 4 | """Joins one or more path components.""" 5 | result = path 6 | 7 | for p in others: 8 | if p.startswith("/"): # absolute 9 | result = p 10 | elif not result or result.endswith("/"): 11 | result += p 12 | else: 13 | result += "/" + p 14 | 15 | return result 16 | 17 | def subdir_glob(glob_specs, exclude = None, prefix = ""): 18 | """Returns a dict of sub-directory relative paths to full paths. 19 | 20 | The subdir_glob() function is useful for defining header maps for C/C++ 21 | libraries which should be relative the given sub-directory. 22 | Given a list of tuples, the form of (relative-sub-directory, glob-pattern), 23 | it returns a dict of sub-directory relative paths to full paths. 24 | 25 | Please refer to native.glob() for explanations and examples of the pattern. 26 | 27 | Args: 28 | glob_specs: The array of tuples in form of 29 | (relative-sub-directory, glob-pattern inside relative-sub-directory). 30 | type: List[Tuple[str, str]] 31 | exclude: A list of patterns to identify files that should be removed 32 | from the set specified by the first argument. Defaults to []. 33 | type: Optional[List[str]] 34 | prefix: If is not None, prepends it to each key in the dictionary. 35 | Defaults to None. 36 | type: Optional[str] 37 | 38 | Returns: 39 | A dict of sub-directory relative paths to full paths. 40 | """ 41 | if exclude == None: 42 | exclude = [] 43 | 44 | results = [] 45 | 46 | for dirpath, glob_pattern in glob_specs: 47 | results.append( 48 | _single_subdir_glob(dirpath, glob_pattern, exclude, prefix), 49 | ) 50 | 51 | return _merge_maps(*results) 52 | 53 | def _merge_maps(*file_maps): 54 | result = {} 55 | for file_map in file_maps: 56 | for key in file_map: 57 | if key in result and result[key] != file_map[key]: 58 | fail( 59 | "Conflicting files in file search paths. " + 60 | "\"%s\" maps to both \"%s\" and \"%s\"." % 61 | (key, result[key], file_map[key]), 62 | ) 63 | 64 | result[key] = file_map[key] 65 | 66 | return result 67 | 68 | def _single_subdir_glob(dirpath, glob_pattern, exclude = None, prefix = None): 69 | if exclude == None: 70 | exclude = [] 71 | results = {} 72 | files = native.glob([_paths_join(dirpath, glob_pattern)], exclude = exclude) 73 | for f in files: 74 | if dirpath: 75 | key = f[len(dirpath) + 1:] 76 | else: 77 | key = f 78 | if prefix: 79 | key = _paths_join(prefix, key) 80 | results[key] = f 81 | 82 | return results 83 | -------------------------------------------------------------------------------- /travis/before-install-linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-6 90 4 | sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-6 90 5 | 6 | c++ --version 7 | g++ --version 8 | gcc --version 9 | 10 | mkdir -p bin 11 | 12 | wget -N https://github.com/njlr/buck-warp/releases/download/v0.2.0/buck-2019.01.10.01-linux -O bin/buck 13 | chmod +x ./bin/buck 14 | ./bin/buck --version 15 | 16 | wget -N https://github.com/LoopPerfect/buckaroo/releases/download/$BUCKAROO_VERSION/buckaroo-linux -O bin/buckaroo 17 | chmod +x ./bin/buckaroo 18 | ./bin/buckaroo version 19 | -------------------------------------------------------------------------------- /travis/before-install-osx.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | c++ --version 4 | g++ --version 5 | gcc --version 6 | 7 | mkdir -p bin 8 | 9 | wget -N https://github.com/njlr/buck-warp/releases/download/v0.2.0/buck-2019.01.10.01-osx -O bin/buck 10 | chmod +x ./bin/buck 11 | ./bin/buck --version 12 | 13 | wget -N https://github.com/LoopPerfect/buckaroo/releases/download/$BUCKAROO_VERSION/buckaroo-macos -O bin/buckaroo 14 | chmod +x ./bin/buckaroo 15 | ./bin/buckaroo version 16 | --------------------------------------------------------------------------------