Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ProgressBar 'shrinks' in Windows cmd #95

Open
Amomum opened this issue Apr 20, 2021 · 7 comments
Open

ProgressBar 'shrinks' in Windows cmd #95

Amomum opened this issue Apr 20, 2021 · 7 comments

Comments

@Amomum
Copy link

Amomum commented Apr 20, 2021

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:

@p-ranav
Copy link
Owner

p-ranav commented May 3, 2021

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

@Amomum
Copy link
Author

Amomum commented May 11, 2021

@p-ranav yes, it still happens with 2.2

@Amomum
Copy link
Author

Amomum commented Jul 14, 2021

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.

@Amomum
Copy link
Author

Amomum commented Jul 15, 2021

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
Copy link
Author

Amomum commented Jul 15, 2021

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

@Amomum
Copy link
Author

Amomum commented Jul 15, 2021

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?

@Amomum
Copy link
Author

Amomum commented Jul 15, 2021

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants