p-ranav / indicators

Activity Indicators for Modern C++

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ProgressBar 'shrinks' in Windows cmd

Amomum opened this issue · comments

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

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

#include <stdio.h>

int main() {

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

  // Random list of numbers
  std::vector<size_t> 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>{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();
    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:

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

@p-ranav yes, it still happens with 2.2

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:

std::ostream &write(float progress) {
auto pos = static_cast<size_t>(progress * bar_width / 100.0);
for (size_t i = 0, current_display_width = 0; i < bar_width;) {
std::string next;
if (i < pos) {
next = fill;
current_display_width = unicode::display_width(fill);
} else if (i == pos) {
next = lead;
current_display_width = unicode::display_width(lead);
} else {
next = remainder;
current_display_width = unicode::display_width(remainder);
}
i += current_display_width;

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:

static inline int mk_wcswidth(const wchar_t *pwcs, size_t n) {

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.

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 ._.

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

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:

os << std::string(remaining, ' ') << "\r";

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?

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 :)