Activity Indicators for Modern C++

Overview

ci status codacy license version

Highlights

  • Thread-safe progress bars and spinners
  • Header-only library. Grab a copy of include/indicators.
  • Single-header version in single_include/indicators.
  • Source for the above GIF can be found here
  • MIT License

Table of Contents

Basic Progress bar

To introduce a progress bar in your application, include indicators/progress_bar.hpp and create a ProgressBar object. Here's the general structure of a progress bar:

{prefix} {start} {fill} {lead} {remaining} {end} {percentage} [{elapsed}<{remaining}] {postfix}
         ^^^^^^^^^^^^^ Bar Width ^^^^^^^^^^^^^^^   

The amount of progress in ProgressBar is maintained as a size_t in range [0, 100]. When progress reaches 100, the progression is complete.

From application-level code, there are two ways in which you can update this progress:

Update progress using bar.tick()

You can update the progress bar using bar.tick() which increments progress by exactly 1%.

#include <indicators/progress_bar.hpp>
#include <thread>
#include <chrono>

int main() {
  using namespace indicators;
  ProgressBar bar{
    option::BarWidth{50},
    option::Start{"["},
    option::Fill{"="},
    option::Lead{">"},
    option::Remainder{" "},
    option::End{"]"},
    option::PostfixText{"Extracting Archive"},
    option::ForegroundColor{Color::green},
    option::FontStyles{std::vector<FontStyle>{FontStyle::bold}}
  };
  
  // Update bar state
  while (true) {
    bar.tick();
    if (bar.is_completed())
      break;
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
  }

  return 0;
}

The above code will print a progress bar that goes from 0 to 100% at the rate of 1% every 100 ms.

Updating progress using bar.set_progress(value)

If you'd rather control progress of the bar in discrete steps, consider using bar.set_progress(value). Example:

#include <chrono>
#include <indicators/cursor_control.hpp>
#include <indicators/progress_bar.hpp>
#include <thread>

int main() {
  using namespace indicators;

  // Hide cursor
  show_console_cursor(false);

  ProgressBar bar{
    option::BarWidth{50},
    option::Start{"["},
    option::Fill{""},
    option::Lead{""},
    option::Remainder{"-"},
    option::End{" ]"},
    option::PostfixText{"Loading dependency 1/4"},
    option::ForegroundColor{Color::cyan},
    option::FontStyles{std::vector<FontStyle>{FontStyle::bold}}
  };

  // Update bar state
  bar.set_progress(10); // 10% done

  // do some work
  std::this_thread::sleep_for(std::chrono::milliseconds(800));

  bar.set_option(option::PostfixText{"Loading dependency 2/4"});  

  bar.set_progress(30); // 30% done

  // do some more work
  std::this_thread::sleep_for(std::chrono::milliseconds(700));

  bar.set_option(option::PostfixText{"Loading dependency 3/4"});  

  bar.set_progress(65); // 65% done

  // do final bit of work
  std::this_thread::sleep_for(std::chrono::milliseconds(900));

  bar.set_option(option::PostfixText{"Loaded dependencies!"});

  bar.set_progress(100); // all done

  // Show cursor
  show_console_cursor(true);

  return 0;
}

Showing Time Elapsed/Remaining

All progress bars and spinners in indicators support showing time elapsed and time remaining. Inspired by python's tqdm module, the format of this meter is [{elapsed}<{remaining}]:

#include <chrono>
#include <indicators/cursor_control.hpp>
#include <indicators/progress_bar.hpp>
#include <thread>

int main() {
  using namespace indicators;

  // Hide cursor
  show_console_cursor(false);

  indicators::ProgressBar bar{
    option::BarWidth{50},
    option::Start{" ["},
    option::Fill{""},
    option::Lead{""},
    option::Remainder{"-"},
    option::End{"]"},
    option::PrefixText{"Training Gaze Network 👀"},
    option::ForegroundColor{Color::yellow},
    option::ShowElapsedTime{true},
    option::ShowRemainingTime{true},
    option::FontStyles{std::vector<FontStyle>{FontStyle::bold}}
  };

  // Update bar state
  while (true) {
    bar.tick();
    if (bar.is_completed())
      break;
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
  }

  // Show cursor
  show_console_cursor(true);

  return 0;
}

Indeterminate Progress Bar

You might have a use-case for a progress bar where the maximum amount of progress is unknown, e.g., you're downloading from a remote server that isn't advertising the total bytes.

Use an indicators::IndeterminateProgressBar for such cases. An IndeterminateProgressBar is similar to a regular progress bar except the total amount to progress towards is unknown. Ticking on this progress bar will happily run forever.

When you know progress is complete, simply call bar.mark_as_completed().

#include <chrono>
#include <indicators/indeterminate_progress_bar.hpp>
#include <indicators/cursor_control.hpp>
#include <indicators/termcolor.hpp>
#include <thread>

int main() {
  indicators::IndeterminateProgressBar bar{
      indicators::option::BarWidth{40},
      indicators::option::Start{"["},
      indicators::option::Fill{"·"},
      indicators::option::Lead{"<==>"},
      indicators::option::End{"]"},
      indicators::option::PostfixText{"Checking for Updates"},
      indicators::option::ForegroundColor{indicators::Color::yellow},
      indicators::option::FontStyles{
          std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}
  };

  indicators::show_console_cursor(false);

  auto job = [&bar]() {
    std::this_thread::sleep_for(std::chrono::milliseconds(10000));
    bar.mark_as_completed();
    std::cout << termcolor::bold << termcolor::green 
        << "System is up to date!\n" << termcolor::reset;
  };
  std::thread job_completion_thread(job);

  // Update bar state
  while (!bar.is_completed()) {
    bar.tick();
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
  }

  job_completion_thread.join();
  
  indicators::show_console_cursor(true);  
  return 0;
}

Block Progress Bar

Are you in need of a smooth block progress bar using unicode block elements? Use BlockProgressBar instead of ProgressBar. Thanks to this blog post for making BlockProgressBar an easy addition to the library.

#include <indicators/block_progress_bar.hpp>
#include <thread>
#include <chrono>

int main() {

  using namespace indicators;

  // Hide cursor
  show_console_cursor(false);

  BlockProgressBar bar{
    option::BarWidth{80},
    option::Start{"["},
    option::End{"]"},
    option::ForegroundColor{Color::white}  ,
    option::FontStyles{std::vector<FontStyle>{FontStyle::bold}}
  };
  
  // Update bar state
  auto progress = 0.0f;
  while (true) {
    bar.set_progress(progress);
    progress += 0.25f;
    if (bar.is_completed())
      break;
    std::this_thread::sleep_for(std::chrono::milliseconds(50));
  }

  // Show cursor
  show_console_cursor(true);

  return 0;
}

MultiProgress

indicators supports management of multiple progress bars with the MultiProgress class template.

template <typename Indicator, size_t count> class MultiProgress is a class template that holds references to multiple progress bars and provides a safe interface to update the state of each bar. MultiProgress works with both ProgressBar and BlockProgressBar classes.

Use this class if you know the number of progress bars to manage at compile time.

Below is an example MultiProgress object that manages three ProgressBar objects.

#include <indicators/multi_progress.hpp>
#include <indicators/progress_bar.hpp>

int main() {
  using namespace indicators;
  // Configure first progress bar
  ProgressBar bar1{
    option::BarWidth{50},
    option::Start{"["},
    option::Fill{""},
    option::Lead{""},
    option::Remainder{" "},
    option::End{" ]"},
    option::ForegroundColor{Color::yellow},
    option::ShowElapsedTime{true},
    option::ShowRemainingTime{true},
    option::PrefixText{"Progress Bar #1 "},
    option::FontStyles{std::vector<FontStyle>{FontStyle::bold}}
  };

  // Configure second progress bar

  ProgressBar bar2{
    option::BarWidth{50},
    option::Start{"["},
    option::Fill{"="},
    option::Lead{">"},
    option::Remainder{" "},
    option::End{" ]"},
    option::ForegroundColor{Color::cyan},
    option::ShowElapsedTime{true},
    option::ShowRemainingTime{true},
    option::PrefixText{"Progress Bar #2 "},
    option::FontStyles{std::vector<FontStyle>{FontStyle::bold}}
  };
  
  // Configure third progress bar
  indicators::ProgressBar bar3{
    option::BarWidth{50},
    option::Start{"["},
    option::Fill{"#"},
    option::Lead{"#"},
    option::Remainder{" "},
    option::End{" ]"},
    option::ForegroundColor{Color::red},
    option::ShowElapsedTime{true},
    option::ShowRemainingTime{true},
    option::PrefixText{"Progress Bar #3 "},
    option::FontStyles{std::vector<FontStyle>{FontStyle::bold}}
  };

  // Construct MultiProgress object
  indicators::MultiProgress<indicators::ProgressBar, 3> bars(bar1, bar2, bar3);

  std::cout << "Multiple Progress Bars:\n";

  auto job1 = [&bars]() {
    while (true) {
      bars.tick<0>();
      if (bars.is_completed<0>())
        break;
      std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
  };

  auto job2 = [&bars]() {
    while (true) {
      bars.tick<1>();
      if (bars.is_completed<1>())
        break;
      std::this_thread::sleep_for(std::chrono::milliseconds(200));
    }
  };

  auto job3 = [&bars]() {
    while (true) {
      bars.tick<2>();
      if (bars.is_completed<2>())
        break;
      std::this_thread::sleep_for(std::chrono::milliseconds(60));
    }
  };

  std::thread first_job(job1);
  std::thread second_job(job2);
  std::thread third_job(job3);

  first_job.join();
  second_job.join();
  third_job.join();

  return 0;
}

DynamicProgress

DynamicProgress is a container class, similar to MultiProgress, for managing multiple progress bars. As the name suggests, with DynamicProgress, you can dynamically add new progress bars.

To add new progress bars, call bars.push_back(new_bar). This call will return the index of the appended bar. You can then refer to this bar with the indexing operator, e.g., bars[4].set_progress(55).

Use this class if you don't know the number of progress bars at compile time.

Below is an example DynamicProgress object that manages six ProgressBar objects. Three of these bars are added dynamically.

#include <indicators/dynamic_progress.hpp>
#include <indicators/progress_bar.hpp>
using namespace indicators;

int main() {

  ProgressBar bar1{option::BarWidth{50}, option::ForegroundColor{Color::red},
                   option::ShowElapsedTime{true}, option::ShowRemainingTime{true},
                   option::PrefixText{"5c90d4a2d1a8: Downloading "}};

  ProgressBar bar2{option::BarWidth{50}, option::ForegroundColor{Color::yellow},
                   option::ShowElapsedTime{true}, option::ShowRemainingTime{true},
                   option::PrefixText{"22337bfd13a9: Downloading "}};

  ProgressBar bar3{option::BarWidth{50}, option::ForegroundColor{Color::green},
                   option::ShowElapsedTime{true}, option::ShowRemainingTime{true},
                   option::PrefixText{"10f26c680a34: Downloading "}};

  ProgressBar bar4{option::BarWidth{50}, option::ForegroundColor{Color::white},
                   option::ShowElapsedTime{true}, option::ShowRemainingTime{true},
                   option::PrefixText{"6364e0d7a283: Downloading "}};

  ProgressBar bar5{option::BarWidth{50}, option::ForegroundColor{Color::blue},
                   option::ShowElapsedTime{true}, option::ShowRemainingTime{true},
                   option::PrefixText{"ff1356ba118b: Downloading "}};

  ProgressBar bar6{option::BarWidth{50}, option::ForegroundColor{Color::cyan},
                   option::ShowElapsedTime{true}, option::ShowRemainingTime{true},
                   option::PrefixText{"5a17453338b4: Downloading "}};

  std::cout << termcolor::bold << termcolor::white << "Pulling image foo:bar/baz\n";

  // Construct with 3 progress bars. We'll add 3 more at a later point
  DynamicProgress<ProgressBar> bars(bar1, bar2, bar3);
  
  // Do not hide bars when completed
  bars.set_option(option::HideBarWhenComplete{false});

  std::thread fourth_job, fifth_job, sixth_job;

  auto job4 = [&bars](size_t i) {
    while (true) {
      bars[i].tick();
      if (bars[i].is_completed()) {
        bars[i].set_option(option::PrefixText{"6364e0d7a283: Pull complete "});
        bars[i].mark_as_completed();
        break;
      }
      std::this_thread::sleep_for(std::chrono::milliseconds(50));
    }
  };

  auto job5 = [&bars](size_t i) {
    while (true) {
      bars[i].tick();
      if (bars[i].is_completed()) {
        bars[i].set_option(option::PrefixText{"ff1356ba118b: Pull complete "});
        bars[i].mark_as_completed();
        break;
      }
      std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
  };

  auto job6 = [&bars](size_t i) {
    while (true) {
      bars[i].tick();
      if (bars[i].is_completed()) {
        bars[i].set_option(option::PrefixText{"5a17453338b4: Pull complete "});
        bars[i].mark_as_completed();
        break;
      }
      std::this_thread::sleep_for(std::chrono::milliseconds(40));
    }
  };

  auto job1 = [&bars, &bar6, &sixth_job, &job6]() {
    while (true) {
      bars[0].tick();
      if (bars[0].is_completed()) {
        bars[0].set_option(option::PrefixText{"5c90d4a2d1a8: Pull complete "});
        // bar1 is completed, adding bar6
        auto i = bars.push_back(bar6);
        sixth_job = std::thread(job6, i);
        sixth_job.join();
        break;
      }
      std::this_thread::sleep_for(std::chrono::milliseconds(140));
    }
  };

  auto job2 = [&bars, &bar5, &fifth_job, &job5]() {
    while (true) {
      bars[1].tick();
      if (bars[1].is_completed()) {
        bars[1].set_option(option::PrefixText{"22337bfd13a9: Pull complete "});
        // bar2 is completed, adding bar5
        auto i = bars.push_back(bar5);
        fifth_job = std::thread(job5, i);
        fifth_job.join();
        break;
      }
      std::this_thread::sleep_for(std::chrono::milliseconds(25));
    }
  };

  auto job3 = [&bars, &bar4, &fourth_job, &job4]() {
    while (true) {
      bars[2].tick();
      if (bars[2].is_completed()) {
        bars[2].set_option(option::PrefixText{"10f26c680a34: Pull complete "});
        // bar3 is completed, adding bar4
        auto i = bars.push_back(bar4);
        fourth_job = std::thread(job4, i);
        fourth_job.join();
        break;
      }
      std::this_thread::sleep_for(std::chrono::milliseconds(50));
    }
  };

  std::thread first_job(job1);
  std::thread second_job(job2);
  std::thread third_job(job3);

  third_job.join();
  second_job.join();
  first_job.join();

  std::cout << termcolor::bold << termcolor::green << "✔ Downloaded image foo/bar:baz" << std::endl;
  std::cout << termcolor::reset;

  return 0;
}

In the above code, notice the option bars.set_option(option::HideBarWhenComplete{true});. Yes, you can hide progress bars as and when they complete by setting this option to true. If you do so, the above example will look like this:

Progress Spinner

To introduce a progress spinner in your application, include indicators/progress_spinner.hpp and create a ProgressSpinner object. Here's the general structure of a progress spinner:

{prefix} {spinner} {percentage} [{elapsed}<{remaining}] {postfix}

ProgressSpinner has a vector of strings: spinner_states. At each update, the spinner will pick the next string from this sequence to print to the console. The spinner state can be updated similarly to ProgressBars: Using either tick() or set_progress(value).

#include <indicators/progress_spinner.hpp>

int main() {
  using namespace indicators;
  indicators::ProgressSpinner spinner{
    option::PostfixText{"Checking credentials"},
    option::ForegroundColor{Color::yellow},
    option::SpinnerStates{std::vector<std::string>{"", "", "", "", "", "", "", ""}},
    option::FontStyles{std::vector<FontStyle>{FontStyle::bold}}
  };
 
  // Update spinner state
  auto job = [&spinner]() {
    while (true) {
      if (spinner.is_completed()) {
        spinner.set_option(option::ForegroundColor{Color::green});
        spinner.set_option(option::PrefixText{""});
        spinner.set_option(option::ShowSpinner{false});
        spinner.set_option(option::ShowPercentage{false});
        spinner.set_option(option::PostfixText{"Authenticated!"});
        spinner.mark_as_completed();	
        break;
      } else
        spinner.tick();
      std::this_thread::sleep_for(std::chrono::milliseconds(40));
    }
  };
  std::thread thread(job);
  thread.join();  

  return 0;
}

Decremental Progress

indicators allows you to easily control the progress direction, i.e., incremental or decremental progress by using option::ProgressType. To program a countdown progress bar, use option::ProgressType::decremental

#include <chrono>
#include <indicators/progress_bar.hpp>
#include <thread>
using namespace indicators;

int main() {

  ProgressBar bar{option::BarWidth{50},
                  option::ProgressType{ProgressType::decremental},
                  option::Start{"["},
                  option::Fill{""},
                  option::Lead{""},
                  option::Remainder{"-"},
                  option::End{"]"},
                  option::PostfixText{"Reverting System Restore"},
                  option::ForegroundColor{Color::yellow},
                  option::FontStyles{std::vector<FontStyle>{FontStyle::bold}}};

  // Update bar state
  while (true) {
    bar.tick();
    if (bar.is_completed())
      break;
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
  }

  std::cout << termcolor::bold << termcolor::white
            << "Task Failed Successfully\n" << termcolor::reset;

  return 0;
}

Working with Iterables

If you'd like to use progress bars to indicate progress while iterating over iterables, e.g., a list of numbers, this can be achieved by using the option::MaxProgress:

#include <chrono>
#include <indicators/block_progress_bar.hpp>
#include <indicators/cursor_control.hpp>
#include <thread>

int main() {

  // Hide cursor
  indicators::show_console_cursor(false);

  // Random list of numbers
  std::vector<size_t> numbers;
  for (size_t i = 0; i < 1259438; ++i) {
      numbers.push_back(i);
  }

  using namespace indicators;
  BlockProgressBar bar{
    option::BarWidth{80},
    option::ForegroundColor{Color::white},
    option::FontStyles{
          std::vector<FontStyle>{FontStyle::bold}},
    option::MaxProgress{numbers.size()}
  };

  std::cout << "Iterating over a list of numbers (size = "
            << numbers.size() << ")\n";

  std::vector<size_t> result;
  for (size_t i = 0; i < numbers.size(); ++i) {

    // Perform some computation
    result.push_back(numbers[i] * numbers[i]);

    // Show iteration as postfix text
    bar.set_option(option::PostfixText{
      std::to_string(i) + "/" + std::to_string(numbers.size())
    });

    // update progress bar
    bar.tick();
  }

  bar.mark_as_completed();

  // Show cursor
  indicators::show_console_cursor(true);

  return 0;
}

Unicode Support

indicators supports multi-byte unicode characters in progress bars.

If the option::BarWidth is set, the library aims to respect this setting. When filling the bar, if the next Fill string has a display width that would exceed the bar width, then the library will fill the remainder of the bar with ' ' space characters instead.

See below an example of some progress bars, each with a bar width of 50, displaying different unicode characters:

#include <chrono>
#include <indicators/progress_bar.hpp>
#include <indicators/indeterminate_progress_bar.hpp>
#include <indicators/cursor_control.hpp>
#include <thread>

int main() {

    indicators::show_console_cursor(false);

    std::this_thread::sleep_for(std::chrono::milliseconds(2000));

    {
        // Plain old ASCII
        indicators::ProgressBar bar{
            indicators::option::BarWidth{50},
            indicators::option::Start{"["},
            indicators::option::Fill{"="},
            indicators::option::Lead{">"},
            indicators::option::Remainder{" "},
            indicators::option::End{" ]"},
            indicators::option::PostfixText{"Plain-old ASCII"},
            indicators::option::ForegroundColor{indicators::Color::green},
            indicators::option::FontStyles{
                std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}
        };

        // Update bar state
        while (true) {
            bar.tick();
            if (bar.is_completed())
            break;
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
    }

    {
        // Unicode
        indicators::ProgressBar bar{
            indicators::option::BarWidth{50},
            indicators::option::Start{"["},
            indicators::option::Fill{"驚くばかり"},
            indicators::option::Lead{">"},
            indicators::option::Remainder{" "},
            indicators::option::End{" ]"},
            indicators::option::PostfixText{"Japanese"},
            indicators::option::ForegroundColor{indicators::Color::yellow},
            indicators::option::FontStyles{
                std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}
        };

        // Update bar state
        while (true) {
            bar.tick();
            if (bar.is_completed())
            break;
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
    }

    {
        // Russian
        indicators::ProgressBar bar{
            indicators::option::BarWidth{50},
            indicators::option::Start{"["},
            indicators::option::Fill{"Потрясающие"},
            indicators::option::Remainder{" "},
            indicators::option::End{" ]"},
            indicators::option::PostfixText{"Russian"},
            indicators::option::ForegroundColor{indicators::Color::red},
            indicators::option::FontStyles{
                std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}
        };

        // Update bar state
        while (true) {
            bar.tick();
            if (bar.is_completed())
            break;
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
    }

    {
        // Greek
        indicators::ProgressBar bar{
            indicators::option::BarWidth{50},
            indicators::option::Start{"["},
            indicators::option::Fill{"Φοβερός"},
            indicators::option::Remainder{" "},
            indicators::option::End{" ]"},
            indicators::option::PostfixText{"Greek"},
            indicators::option::ForegroundColor{indicators::Color::cyan},
            indicators::option::FontStyles{
                std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}
        };

        // Update bar state
        while (true) {
            bar.tick();
            if (bar.is_completed())
            break;
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
    }

    {
        // Chinese
        indicators::ProgressBar bar{
            indicators::option::BarWidth{50},
            indicators::option::Start{"["},
            indicators::option::Fill{"太棒了"},
            indicators::option::Remainder{" "},
            indicators::option::End{" ]"},
            indicators::option::PostfixText{"Chinese"},
            indicators::option::ForegroundColor{indicators::Color::green},
            indicators::option::FontStyles{
                std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}
        };

        // Update bar state
        while (true) {
            bar.tick();
            if (bar.is_completed())
            break;
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }        
    }

    {
        // Emojis
        indicators::ProgressBar bar{
            indicators::option::BarWidth{50},
            indicators::option::Start{"["},
            indicators::option::Fill{"🔥"},
            indicators::option::Lead{"🔥"},
            indicators::option::Remainder{" "},
            indicators::option::End{" ]"},
            indicators::option::PostfixText{"Emojis"},
            indicators::option::ForegroundColor{indicators::Color::white},
            indicators::option::FontStyles{
                std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}
        };

        // Update bar state
        while (true) {
            bar.tick();
            if (bar.is_completed())
            break;
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
    }

    {
        // Indeterminate progress bar
        indicators::IndeterminateProgressBar bar{
            indicators::option::BarWidth{50},
            indicators::option::Start{"["},
            indicators::option::Fill{""},
            indicators::option::Lead{"載入中"},
            indicators::option::End{" ]"},
            indicators::option::PostfixText{"Loading Progress Bar"},
            indicators::option::ForegroundColor{indicators::Color::yellow},
            indicators::option::FontStyles{
                std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}
        };

        auto job = [&bar]() {
            std::this_thread::sleep_for(std::chrono::milliseconds(10000));
            bar.mark_as_completed();
        };
        std::thread job_completion_thread(job);

        // Update bar state
        while (!bar.is_completed()) {
            bar.tick();
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
        }

        job_completion_thread.join();
    }

    indicators::show_console_cursor(true);

  return 0;
}

Building Samples

git clone https://github.com/p-ranav/indicators
cd indicators
mkdir build && cd build
cmake -DINDICATORS_SAMPLES=ON -DINDICATORS_DEMO=ON ..
make

Generating Single Header

python3 utils/amalgamate/amalgamate.py -c single_include.json -s .

Contributing

Contributions are welcome, have a look at the CONTRIBUTING.md document for more information.

License

The project is available under the MIT license.

Issues
  • Make errors

    Make errors

    Just trying to build the source and examples - but using make - get the following error:

    [email protected]:~/Downloads/indicators-master/build$ make [ 3%] Building CXX object demo/CMakeFiles/demo.dir/demo.cpp.o /home/johughes/Downloads/indicators-master/demo/demo.cpp:1:10: fatal error: indicators.hpp: No such file or directory #include "indicators.hpp" ^~~~~~~~~~~~~~~~ compilation terminated. demo/CMakeFiles/demo.dir/build.make:62: recipe for target 'demo/CMakeFiles/demo.dir/demo.cpp.o' failed make[2]: *** [demo/CMakeFiles/demo.dir/demo.cpp.o] Error 1 CMakeFiles/Makefile2:85: recipe for target 'demo/CMakeFiles/demo.dir/all' failed make[1]: *** [demo/CMakeFiles/demo.dir/all] Error 2 Makefile:151: recipe for target 'all' failed make: *** [all] Error 2

    the previous cmake did not report any errors.

    opened by johughes99 11
  • API change - progress bar can be constructed with proper settings.

    API change - progress bar can be constructed with proper settings.

    Hi there!

    I would like to ask whether you would be open to the following changes.

    The rationale behind them is to make it possible, to create ProgressBar object with options given as constructor parameters, as with current approach it would extremaly difficult to do so.

    opened by dawidpilarski 7
  • Postfix string length grows

    Postfix string length grows

    Hello!

    I use prefix and postfix for my interface I use about 30 characters for postfix, but it prints 70 or more like this: -- Info Checksum is correct·········································

    My limit is 84 characters. Why does string length increase with empty characters?

    opened by dd4e 6
  • ProgressBar::tick() prints progress even if nothing has changed

    ProgressBar::tick() prints progress even if nothing has changed

    From my understanding calling tick() method will trigger the print function which will re-print the progress status regardless. Is this correct? Can I open a PR to update only if something has changed?

    https://github.com/p-ranav/indicators/blob/2291c8c39c031dda6007a68f8e725a0be5ba2ca7/include/indicators/progress_bar.hpp#L112-L119

    opened by amallia 6
  • remove bold output and default color to unspecified

    remove bold output and default color to unspecified

    Hi @p-ranav this is probably a bit controversial but I have modified the code so that a "unspecified" color is available (just the default color from the terminal) and made it the default. I also removed the default of making the font bold.

    Let me know if you don't want me to make these two things the default.

    Maybe we can add a setting for bold, and italic?

    opened by wolfv 5
  • "error: 'mutex' in namespace 'std' does not name a type"

    on windows, when I run the code, it will throw this error : "error: 'mutex' in namespace 'std' does not name a type". I google for it , somebody say "#include" cannt work on windows. I wanna know if there some solutions to solve this problem. thanks a lot.

    opened by qzylalala 5
  • Memoryleak on ProgressSpinner

    Memoryleak on ProgressSpinner

    D (03:00:03.997) HEAP: Iteration: 232000 (diff 304 bytes) V (03:00:04.006) Spinner: Created: 1407943886 V (03:00:04.011) Spinner: Destroyed: 1407943886 D (03:00:04.016) HEAP: Iteration: 231700 (diff 300 bytes) V (03:00:04.025) Spinner: Created: -1271674128 V (03:00:04.030) Spinner: Destroyed: -1271674128 D (03:00:04.035) HEAP: Iteration: 231396 (diff 304 bytes) V (03:00:04.044) Spinner: Created: 72749701 V (03:00:04.049) Spinner: Destroyed: 72749701 D (03:00:04.053) HEAP: Iteration: 231096 (diff 300 bytes) V (03:00:04.063) Spinner: Created: -1957109953 V (03:00:04.067) Spinner: Destroyed: -1957109953 D (03:00:04.073) HEAP: Iteration: 230792 (diff 304 bytes) V (03:00:04.082) Spinner: Created: 569508829 V (03:00:04.086) Spinner: Destroyed: 569508829 D (03:00:04.091) HEAP: Iteration: 230492 (diff 300 bytes)

    every instance 304 or 300 bytes leaked

    opened by drony 4
  • Can i re-use a prior defined bar rather than define two separated bars?

    Can i re-use a prior defined bar rather than define two separated bars?

    Is it possible to re-use a prior defined bar? as follows:

    using namespace indicators;
    indicators::ProgressBar bar {
            option::BarWidth{45},
            option::Start{"["},
            option::Fill{"■"},
            option::Lead{"■"},
            option::Remainder{" "},
            option::End{"]"},
            option::ShowPercentage{true},
            option::ShowElapsedTime{true},
            option::ShowRemainingTime{true},
            option::PrefixText{""}
    };
    
    bar.set_progress(0);
    for(int i = 0; i < 100; i++){
        bar.set_progress(i+1);
    }
    
    #re-use the bar again
    bar.set_progress(0);
    for(int j = 0; j < 100; j++){
        bar.set_progress(j+1);
    }
    

    The first works fine, but the second bar will be printed in a single new line for each update. So i am just wondering if there is a solution for this problem?

    Thanks.

    opened by YinLiLin 3
  • How to let indicator don't cover the print message?

    How to let indicator don't cover the print message?

    indicators is a great tool.

    it show the process dynamically, but when I need to print message in my code.

    the indicators bar will cover my print message will bar. and my message is disappeared.

    Is there any method can let me see the message, and indicators bar process meantime?

    opened by nickhuangxinyu 3
  • ProgressBar broken

    ProgressBar broken

    What could possibly be causing the progress bar to behave this way?

    image

    PS: I only updated the output to cerr instead of cout, but it should still work, right? If I use cout it doesn't print anything at all.

    opened by mirand863 3
  • Windows cursor movements

    Windows cursor movements

    The dynamic progress bar uses ANSI escape codes for moving up and erasing lines.

    This doesn't work on Windows (and I could not make it work using SetConsoleMode( ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_VIRTUAL_TERMINAL_INPUT ) either).

    I found the following library for go which seems great: https://github.com/k0kubun/go-ansi

    The following code works fine on Windows:

    namespace cursor
    {
        void move_up(int lines)
        {
            move(0, -lines);
        }
    
        void move_down(int lines)
        {
            move(0, -lines);
        }
    
        void move_left(int cols)
        {
            move(-cols, 0);
        }
    
        void move_up(int cols)
        {
            move(cols, 0);
        }
    
        void move(int x, int y) {
            auto hStdout = GetStdHandle(STD_OUTPUT_HANDLE); 
            if (!hStdout) return;
    
            CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
            GetConsoleScreenBufferInfo(hStdout, &csbiInfo);
    
            COORD cursor;
    
            cursor.X = csbiInfo.dwCursorPosition.X + x;
            cursor.Y = csbiInfo.dwCursorPosition.Y + y;
            SetConsoleCursorPosition(hStdout, cursor);
        }
    
    }
    

    It would be nice to have more abstractions (also around finding the terminal windows size). Do you want to integrate these facilities into this library or should we create another one?

    opened by wolfv 3
  • [bug] Cannot compile the sample codes with MSVC compiler on Windows.

    [bug] Cannot compile the sample codes with MSVC compiler on Windows.

    Building Commands

    git clone https://github.com/p-ranav/indicators
    cd indicators
    mkdir build && cd build
    vcvarsall x64
    cmake -DINDICATORS_SAMPLES=ON -DINDICATORS_DEMO=ON -GNinja ..
    cmake --build .
    

    Platform and Version

    • OS+System: Windows 11
    • CMake Version: 3.21.2
    • Ninja Version: 1.10.2
    • Compiler+Version: MSVC 2019

    Logs

    • Click to expand log
      C:\CPackages\indicators>git clone https://github.com/p-ranav/indicators
      Cloning into 'indicators'...
      remote: Enumerating objects: 1997, done.
      remote: Counting objects: 100% (171/171), done.
      remote: Compressing objects: 100% (41/41), done.
      remote: Total 1997 (delta 83), reused 162 (delta 83), pack-reused 1826
      Receiving objects: 100% (1997/1997), 34.55 MiB | 7.26 MiB/s, done.
      Resolving deltas: 100% (1120/1120), done.
      
      C:\CPackages\indicators>cd indicators
      
      C:\CPackages\indicators\indicators>mkdir build && cd build
      
      C:\CPackages\indicators\indicators\build>vcvarsall x64
      **********************************************************************
      ** Visual Studio 2019 Developer Command Prompt v16.11.11
      ** Copyright (c) 2021 Microsoft Corporation
      **********************************************************************
      [vcvarsall.bat] Environment initialized for: 'x64'
      
      C:\CPackages\indicators\indicators\build>cmake -DINDICATORS_SAMPLES=ON -DINDICATORS_DEMO=ON -GNinja ..
      -- The CXX compiler identification is MSVC 19.29.30141.0
      -- Detecting CXX compiler ABI info
      -- Detecting CXX compiler ABI info - done
      -- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.29.30133/bin/Hostx64/x64/cl.exe - skipped
      -- Detecting CXX compile features
      -- Detecting CXX compile features - done
      -- Looking for C++ include pthread.h
      -- Looking for C++ include pthread.h - not found
      -- Found Threads: TRUE
      -- Configuring done
      -- Generating done
      -- Build files have been written to: C:/CPackages/indicators/indicators/build
      
      C:\CPackages\indicators\indicators\build>cmake --build .
      
    • Log of cmake --build . is too long, so I save it into a .txt file and upload it:

      CompileError.txt

    Screenshots

    image

    opened by hwhsu1231 0
  • [question] An `std::range_error` exception in `intermediate_progress_bar.cpp`.

    [question] An `std::range_error` exception in `intermediate_progress_bar.cpp`.

    LINK: https://github.com/p-ranav/indicators/blob/master/samples/indeterminate_progress_bar.cpp

    Source Code (in case of updating)

    Click to expand the Source Code
    #include <chrono>
    #include <indicators/cursor_control.hpp>
    #include <indicators/indeterminate_progress_bar.hpp>
    #include <thread>
    
    int main() {
      indicators::IndeterminateProgressBar bar{
          indicators::option::BarWidth{40},
          indicators::option::Start{"["},
          indicators::option::Fill{"·"},
          indicators::option::Lead{"<==>"},
          indicators::option::End{"]"},
          indicators::option::PostfixText{"Checking for Updates"},
          indicators::option::ForegroundColor{indicators::Color::yellow},
          indicators::option::FontStyles{
              std::vector<indicators::FontStyle>{indicators::FontStyle::bold}}};
    
      indicators::show_console_cursor(false);
    
      auto job = [&bar]() {
        std::this_thread::sleep_for(std::chrono::milliseconds(10000));
        bar.mark_as_completed();
        std::cout << termcolor::bold << termcolor::green << "System is up to date!\n"
                  << termcolor::reset;
      };
      std::thread job_completion_thread(job);
    
      // Update bar state
      while (!bar.is_completed()) {
        bar.tick();
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
      }
    
      job_completion_thread.join();
    
      indicators::show_console_cursor(true);
      return 0;
    }
    

    Screenshots

    image

    opened by hwhsu1231 0
  • No percentage indication on Windows

    No percentage indication on Windows

    When I try your very first example on Windows, there is no percentage indicator: image

    In PowerShell and Windows console, the progress bar does not even stay on the same line: image

    Is this the normal behavior on windows?

    opened by F-I-D-O 1
  • Compile error

    Compile error

    Hello I have the following error during compilation : D:\www\QT\untitled35\indicators\include\indicators\setting.hpp:184: erreur : C2955: 'indicators::details::option_idx': use of class template requires template argument list

    I'm just trying to compile the demo.cpp with qt creator (MSVC2019 64bits). I added the demo file and next setup the includepath in the .pro file. Any clue ? Regards

    opened by bzctoons 0
  • warning: comparison is always false due to limited range of data type [-Wtype-limits]

    warning: comparison is always false due to limited range of data type [-Wtype-limits]

    On windows compiling the example

    // indicators_issue.cpp
    
    #include <indicators/progress_bar.hpp>
    
    int main()
    {
        int n = 100;
    
        indicators::ProgressBar pbar{
            indicators::option::MaxProgress{n},
        };
    
        for (int i = 0; i < n; ++i) {
            pbar.tick();
        }
    
        return 0;
    }
    

    compiling with gcc.exe (MinGW-W64 x86_64-posix-seh, built by Brecht Sanders) 10.3.0 I get the following annoying warnings:

    In file included from C:/project/build/_deps/indicators_git-src/include/indicators/details/stream_helper.hpp:5,
                     from C:/project/build/_deps/indicators_git-src/include/indicators/progress_bar.hpp:5,
                     from C:\project\examples\indicators_issue.cpp:1:
    C:/project/build/_deps/indicators_git-src/include/indicators/display_width.hpp: In function 'int unicode::details::mk_wcwidth(wchar_t)':
    C:/project/build/_deps/indicators_git-src/include/indicators/display_width.hpp:201:57: warning: comparison is always false due to limited range of data type [-Wtype-limits]
      201 |                (ucs >= 0xffe0 && ucs <= 0xffe6) || (ucs >= 0x20000 && ucs <= 0x2fffd) ||
          |                                                     ~~~~^~~~~~~~~~
    C:/project/build/_deps/indicators_git-src/include/indicators/display_width.hpp:201:75: warning: comparison is always true due to limited range of data type [-Wtype-limits]
      201 |                (ucs >= 0xffe0 && ucs <= 0xffe6) || (ucs >= 0x20000 && ucs <= 0x2fffd) ||
          |                                                                       ~~~~^~~~~~~~~~
    C:/project/build/_deps/indicators_git-src/include/indicators/display_width.hpp:202:21: warning: comparison is always false due to limited range of data type [-Wtype-limits]
      202 |                (ucs >= 0x30000 && ucs <= 0x3fffd)));
          |                 ~~~~^~~~~~~~~~
    C:/project/build/_deps/indicators_git-src/include/indicators/display_width.hpp:202:39: warning: comparison is always true due to limited range of data type [-Wtype-limits]
      202 |                (ucs >= 0x30000 && ucs <= 0x3fffd)));
    

    Can these comparisons be removed?

    opened by JonasHarsch 0
Releases(v2.2)
  • v2.2(May 3, 2021)

    • Fixed the NOMINMAX issue in MinGW build of indicators #76
    • Replaced #pragma once with #ifndef-define pairs to fix the single_include generation and avoid multiple-definition errors
    Source code(tar.gz)
    Source code(zip)
  • v2.1(May 3, 2021)

  • v2.0(Jan 7, 2021)

    • Closed #63 - Use unicode::display_width instead of os.str().size() in helper functions
    • Elevated #includes from being nested in the namespace.
    • Support older compilers with missing header.
    • Closed #67 - typo in terminal_size.hpp
    • Closed #68 - TERMCOLOR_HPP_ defined twice
    • Merged #69 to fix mingw compilation
    • Closed #72 - Using _WIN32 instead of _MSC_VER in macro and defining NOMINMAX correctly
    • Mitigated overflow when calculating ETA
    • Using floating point for remaining time calculation
    • Closed #80
    Source code(tar.gz)
    Source code(zip)
  • v1.9(May 25, 2020)

    • Fixed build issues in g++ caused by missing header #54
    • Auto-detecting terminal width to fill the remainder of the progress bar with empty spaces #56
      • Previously this was hard-coded to 10 spaces
    • Fixed multiple definitions in helper functions, e.g., cursor control #58
    • Implemented option::ProgressType which can be incremental or decremental - Provides an easy interface to implement decremental progress (a regressing bar) #59
    • Updated to latest version of termcolor #60
    • Improved Unicode support in progress bar
    Source code(tar.gz)
    Source code(zip)
  • v1.8(May 16, 2020)

    • IndeterminateProgressBar for modeling bars with unknown totals #43 #51
    • option::MaxProgress to configure the maximum progress for the bar. Default is 100. This enables sweeping over iterables, e.g., a vector of numbers. Example.
    • option::Stream to configure the output stream of progress bars, e.g., using std::stringstream or std::cerr instead of std::cout #22
    • option::FontStyles. The user can specify a vector of font styles, e.g., bold, italic etc. for the progress bar #38
    • New default for progress bar color: Color::unspecified
    • Abstraction for Cursor Movements to work better with Windows #48
    • Show/hide console cursor - Enables hiding the console cursor when progress bar is ticking - Removes the annoying flicker #48
    • Using amalgamate to generate single_include header file.
    Source code(tar.gz)
    Source code(zip)
  • v1.7(Feb 21, 2020)

    • Added DynamicProgress class for managing multiple progress bars dynamically https://github.com/p-ranav/indicators/pull/32
    • Fixed code style - Member names starting with underscore _ https://github.com/p-ranav/indicators/issues/24
    Source code(tar.gz)
    Source code(zip)
  • v1.6(Feb 11, 2020)

    • API Change - Progress bars can be constructed with a Settings object - create ProgressBar object with options given as constructor parameters #21
    • Added pkg-config file to make it easier to consume this package in build systems that support it, such as autotools, Meson, waf, SCons and build2 #27
    • In MultiProgress, elapsed time of each bar is now updated independently, instead of together #18
    • Remove code duplication and refactored for reuse #14 #15 #16 #17
    Source code(tar.gz)
    Source code(zip)
  • v1.5(Dec 18, 2019)

  • v1.4(Dec 18, 2019)

  • v1.3(Dec 17, 2019)

  • v1.2(Dec 17, 2019)

  • v1.1(Dec 16, 2019)

Owner
Pranav
Pranav
A library for interactive command line interfaces in modern C++

cli A cross-platform header only C++14 library for interactive command line interfaces (Cisco style) Features Header only Cross-platform (linux and wi

Daniele Pallastrelli 782 May 9, 2022
Table Maker for Modern C++

Source for the above image can be found here Table of Contents Quick Start Formatting Options Style Inheritance Model Word Wrapping Font Alignment Fon

Pranav 1.2k May 17, 2022
Contour - A modern C++ Terminal Emulator

contour is a modern terminal emulator, for everyday use. It is aiming for power users with a modern feature mindset.

Contour Terminal Emulator 805 May 10, 2022
ssheven - A modern SSH client for Mac OS 7/8/9.

ssheven - A modern SSH client for Mac OS 7/8/9.

null 434 May 11, 2022
Argument Parser for Modern C++

Highlights Single header file Requires C++17 MIT License Quick Start Simply include argparse.hpp and you're good to go. #include <argparse/argparse.hp

Pranav 1.1k May 14, 2022
easy to use, powerful & expressive command line argument parsing for modern C++ / single header / usage & doc generation

clipp - command line interfaces for modern C++ Easy to use, powerful and expressive command line argument handling for C++11/14/17 contained in a sing

André Müller 872 May 11, 2022
A modern frontend for Neovim.

A modern frontend for Neovim.

Rohit Pradhan 1.5k May 10, 2022
Free open-source modern C++17 / C++20 framework to create console, forms (GUI like WinForms) and unit test applications on Microsoft Windows, Apple macOS and Linux.

xtd Modern C++17/20 framework to create console (CLI), forms (GUI like WinForms) and tunit (unit tests like Microsoft Unit Testing Framework) applicat

Gammasoft 292 May 14, 2022
Modern C++ Undo / Redo framework

History Hello Developers! I present to you History, a modern C++ (C++17) Undo / Redo framework. My goal was to create a non-intrusive, compact and int

null 14 Jan 11, 2022
Activity Indicators for Modern C++

Highlights Thread-safe progress bars and spinners Header-only library. Grab a copy of include/indicators. Single-header version in single_include/indi

Pranav 2k May 15, 2022
TulipCell is an Excel add-in providing 100+ technical analysis indicators.

Tulip Cell Introduction Tulip Cell is an Excel add-in that provides the technical analysis functions from the Tulip Indicators library. Building Build

Tulip Charts LLC 26 Jan 8, 2022
Graphs the activity of a chia harvester in a linux terminal.

Chia Harvest Graph Monitor for Chia Harvesting Introduction The chiaharvestgraph tool will graph Chia Harvesting activity in a linux terminal. Use a 2

Bram Stolk 219 May 2, 2022
KDevelop plugin for automatic time tracking and metrics generated from your programming activity.

Wakatime KDevelop Plugin Installation instructions Make sure the project is configured to install to the directory of your choice: In KDevelop, select

snotr 6 Oct 13, 2021
A C-Beginner Project for Winter Code Activity

Whale Market - Winter Code From Jiacai Cui Email: [email protected] 1 Introduction 详细内容见:https://cui-jiacai.gitbook.io/whale-market/ 2 Tutori

Cui Jiacai 62 Apr 23, 2022
A modern day direct port of BOOM 2.02 for modern times. Aiming to tastefully continue the development of BOOM, in the style of TeamTNT.

ReBOOM ReBOOM is a continuation of the BOOM source port, version 2.02. what is it ReBOOM is a source port, directly ported from BOOM 2.02 with additio

Gibbon 10 Apr 2, 2022
C-based/Cached/Core Computer Vision Library, A Modern Computer Vision Library

Build Status Travis CI VM: Linux x64: Raspberry Pi 3: Jetson TX2: Backstory I set to build ccv with a minimalism inspiration. That was back in 2010, o

Liu Liu 6.8k May 6, 2022
Header-only, event based, tiny and easy to use libuv wrapper in modern C++ - now available as also shared/static library!

Do you have a question that doesn't require you to open an issue? Join the gitter channel. If you use uvw and you want to say thanks or support the pr

Michele Caini 1.4k May 11, 2022
A library for interactive command line interfaces in modern C++

cli A cross-platform header only C++14 library for interactive command line interfaces (Cisco style) Features Header only Cross-platform (linux and wi

Daniele Pallastrelli 782 May 9, 2022
Table Maker for Modern C++

Source for the above image can be found here Table of Contents Quick Start Formatting Options Style Inheritance Model Word Wrapping Font Alignment Fon

Pranav 1.2k May 17, 2022
Modern concurrency for C++. Tasks, executors, timers and C++20 coroutines to rule them all

concurrencpp, the C++ concurrency library concurrencpp is a tasking library for C++ allowing developers to write highly concurrent applications easily

David Haim 921 May 11, 2022
A modern, portable, easy to use crypto library.

Sodium is a new, easy-to-use software library for encryption, decryption, signatures, password hashing and more. It is a portable, cross-compilable, i

Frank Denis 10.1k May 15, 2022
Fast CSV parser and writer for Modern C++

Table of Contents CSV Reader Performance Benchmark Reader API CSV Writer Writer API Compiling Tests Generating Single Header Contributing License CSV

Pranav 270 May 15, 2022
A modern C++ library for reading, writing, and analyzing CSV (and similar) files.

Vince's CSV Parser Motivation Documentation Integration C++ Version Single Header CMake Instructions Features & Examples Reading an Arbitrarily Large

Vincent La 550 May 10, 2022
A modern, C++-native, header-only, test framework for unit-tests, TDD and BDD - using C++11, C++14, C++17 and later (or C++03 on the Catch1.x branch)

Catch2 v3 is being developed! You are on the devel branch, where the next major version, v3, of Catch2 is being developed. As it is a significant rewo

Catch Org 15k May 18, 2022
A modern, C++-native, header-only, test framework for unit-tests, TDD and BDD - using C++11, C++14, C++17 and later (or C++03 on the Catch1.x branch)

Catch2 v3 is being developed! You are on the devel branch, where the next major version, v3, of Catch2 is being developed. As it is a significant rewo

Catch Org 14.9k May 14, 2022
Gaming meets modern C++ - a fast and reliable entity component system (ECS) and much more

EnTT is a header-only, tiny and easy to use library for game programming and much more written in modern C++. Among others, it's used in Minecraft by

Michele Caini 6.7k May 9, 2022
A lightweight game engine written in modern C++

Halley Game Engine A lightweight game engine written in C++17. It has been used to ship Wargroove, a turn-based strategy game, on Windows, Mac (experi

Rodrigo Braz Monteiro 3k May 8, 2022
Modern C++14 library for the development of real-time graphical applications

CI Community Support bs::framework is a C++ library that aims to provide a unified foundation for the development of real-time graphical applications,

null 1.7k May 10, 2022
A modern cross-platform low-level graphics library and rendering framework

Diligent Engine A Modern Cross-Platform Low-Level 3D Graphics Library Diligent Engine is a lightweight cross-platform graphics API abstraction library

Diligent Graphics 2.3k May 9, 2022