0

I was trying out the example code from this answer.

#include <iostream>
#include <thread>
#include <chrono>

void drawProgressBar(int, double);

int main() {

    drawProgressBar(30, .25);
    std::this_thread::sleep_for(std::chrono::seconds(1));
    drawProgressBar(30, .50);
    std::this_thread::sleep_for(std::chrono::seconds(1));
    drawProgressBar(30, .75);
    std::this_thread::sleep_for(std::chrono::seconds(1));
    drawProgressBar(30, 1);

    return 0;
}

void drawProgressBar(int len, double percent) {
    std::cout << "\x1B[2K"; // Erase the entire current line.
    std::cout << "\x1B[0E"; // Move to the beginning of the current line.
    std::string progress;
    for (int i = 0; i < len; ++i) {
        if (i < static_cast<int>(len * percent)) {
            progress += "=";
        } else {
            progress += " ";
        }
    }
    std::cout << "[" << progress << "] " << (static_cast<int>(100 * percent)) << "%" << std::flush;
}

The expected behavior was a progress bar like so:

[=======                       ] 25%

which would update three times on the same line, ending up as:

[==============================] 100%

after 3 seconds.

While each progress bar gets erased as expected, the next progress bar is drawn one line down, not on the same line as I was expecting it to.

The documentation linked in the answer (Wikipedia) says that CSI n E (ESC[nE) where n is an integer:

Moves cursor to beginning of the line n (default 1) lines down.

So I would expect CSI 0 E (ESC[0E) to move the cursor to the beginning of the current line (the line 0 lines down).

Why doesn't it? Also, how can I achieve the intended behavior?


I'm using Terminal.app on OS X to run this program.

Community
  • 1
  • 1
Michael Dorst
  • 8,210
  • 11
  • 44
  • 71

1 Answers1

1

Hmm, try:

std::cout << "\r";

instead of:

std::cout << "\x1B[2K"; // Erase the entire current line.
std::cout << "\x1B[0E"; // Move to the beginning of the current line.

This is a carriage return, which should reposition the cursor at the beginning of the line.

(By the way, kudos for commenting your code. Love it when people do that on here :) )

Blue Ice
  • 7,888
  • 6
  • 32
  • 52
  • That worked! Thank you. However I would still like to know why `"\x1B[0E"` didn't work. – Michael Dorst Aug 18 '14 at 03:54
  • @anthropomorphic My best guess is that `1`- the default number of rows to move downwards- is the minimum number possible. After all, there is an ANSI escape code to do what you are doing, but in a simpler way- `\x[F`. – Blue Ice Aug 18 '14 at 04:00
  • `[` isn't a valid hex character. Did you mean `\x1B[F`? – Michael Dorst Aug 18 '14 at 04:05
  • I tried `\1B[F` and the result was the curser relocating to the beginning of the very top line of the terminal. – Michael Dorst Aug 18 '14 at 04:06
  • Hmm. http://stackoverflow.com/questions/11474391/is-there-go-up-line-character-opposite-of-n says it should have it go to the beginning of the line. Maybe try `\033[0E`? Just playing with the escape sequences, I don't really know if it will work. – Blue Ice Aug 18 '14 at 04:09
  • `033 == 0x1B`. `\033[0E` does is equivalent to the original `\x1B[0E` – Michael Dorst Aug 18 '14 at 04:14
  • Hmm yep, I don't know. I guess I'll stick with my "minimum number theory" :) – Blue Ice Aug 18 '14 at 04:16
  • The `0` parameter, unless specifically documented as having an effect, is treated as though missing. The default for `CSI E` is "1". The `HPA` (`CSI` *n* `G`) sequence moves within the current row on the screen. – Thomas Dickey Mar 20 '15 at 09:34