p-ranav / indicators

Activity Indicators for Modern C++
MIT License
3.04k stars 237 forks source link

ProgressBar 'shrinks' in Windows cmd #95

Open Amomum opened 3 years ago

Amomum commented 3 years ago

I took 'Working with Iterables' example and modified it slightly (simple ProgressBar instead of BlockProgressBar, removed cursor manipulation and made vector smaller):

``` cpp #include #include #include #include #include int main() { // Hide cursor // indicators::show_console_cursor(false); // Random list of numbers std::vector numbers; for (size_t i = 0; i < 255; ++i) { numbers.push_back(i); } getchar(); using namespace indicators; ProgressBar bar{ option::BarWidth{80}, option::ForegroundColor{Color::white}, option::FontStyles{ std::vector{FontStyle::bold}}, option::MaxProgress{numbers.size()} }; std::cout << "Iterating over a list of numbers (size = " << numbers.size() << ")\n"; std::vector 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(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); } bar.mark_as_completed(); // Show cursor // indicators::show_console_cursor(true); return 0; } ```

This is how it looks in my windows 7 cmd:

https://imgur.com/ZpXaMvC

Progress-bar itself 'shrinks' and there is no percent-indicator that is present in the example gif:

p-ranav commented 3 years ago

@Amomum Can you confirm if this still happens in the latest release? Thanks.

Amomum commented 3 years ago

@p-ranav yes, it still happens with 2.2

Amomum commented 3 years ago

Okay, after a bit of digging I found out the following that progress bar shrinks on every fourth step. This seems to be related to this snippet: https://github.com/p-ranav/indicators/blob/cdcff01339894728078b81689b0a344b99e86ec2/include/indicators/details/stream_helper.hpp#L139-L155

When progress value is divisible by four, pos will be even and that will cause progress-bar to shrink. I noticed that current_display_width will be equal 2 for fill and lead part. But why? I'm using the simplest progress-bar, it's filled with = and lead is >, their length should be 1.

I checked on my linux machine and there current_display_width is always equal 1. So I presume there is some locale/platform/compiler-specific error in determining character length.

Why and what exactly is happening is a bit beyond me; I can only say for sure that when this call to unicode::display_width will get us to here:

https://github.com/p-ranav/indicators/blob/cdcff01339894728078b81689b0a344b99e86ec2/include/indicators/display_width.hpp#L205

on my Windows machine pwcs will point to some hieroglyphic (and on Linux it points to = as I would expect), so, naturally, it will calculate length 2. To me this smells like buffer overrun but on Windows I can't use ASan or Valgrind to verify this.

Amomum commented 3 years ago

Okay, so on my Windows machine, compiled with MinGW 8.1.0 64-bit this snippet produces a hieroglyphic:

#include <clocale>
#if __has_include(<codecvt>)
#include <codecvt>
#endif
#include <cstdlib>
#include <locale>
#include <string>
#include <wchar.h>
#include <iostream>

int main() {
    auto utf8_decode = [](const std::string &str) -> std::wstring {
      std::wstring_convert<std::codecvt_utf8<wchar_t>> myconv;
      return myconv.from_bytes(str);
    };

    std::string test = "=";
    auto s = utf8_decode(test);

    std::wcout << s << std::endl;

    return 0;
}

This starts to look like bug in standard library implementation ._.

Amomum commented 3 years ago

https://sourceforge.net/p/mingw-w64/bugs/538/ this one looks very similar, I'll try suggested workaround.

Amomum commented 3 years ago

Workaround - codecvt_utf8<wchar_t, 0x10ffff, std::little_endian> instead of codecvt_utf8<wchar_t> kinda works; in my example = is converted correctly. Progress bar also stops shrinking, however, it also now starts with a newline and does not get redrawn inplace.

Looks like some more investigation is required...

<after some investigation>

For some reason this line: https://github.com/p-ranav/indicators/blob/cdcff01339894728078b81689b0a344b99e86ec2/include/indicators/progress_bar.hpp#L342 starts to produce a newline. The most puzzling part is that it's that string with spaces alone produces a newline!

Do you have any ideas why that may be happening?

Amomum commented 3 years ago

Hm, looks like remaining is always one symbol bigger than my available console window space ._. So.. I guess windows console just wraps this line of spaces to the next line?.. Hmmmm..

So, looks like fixing utf8 encoding affected calculation of remaining length and caused this! <after some checking> Yes, when convertion to utf8 was wrong due to bug, length of progress bar postfix was calculated incorrectly ( almost twice as big) so remaining was much lower and spaces were not filling the entire space left in the console!

Sorry for me blabbing here but I think that's it :) Now the real question remains - how to fix this :)