Search code examples
c++escapingansi-escape

Unclear ANSI CUB/CUF behaviour


While studying the ANSI control sequences I came across unclear behaviour.

I tried to make "matrix" effect in terminal, which changes one random digit per loop iteration in a row of n 0/1 digits:

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

const int n = 5; // row length
const int pause = 50; // milliseconds

int main() {
    using namespace std;
    using this_thread::sleep_for;
    using chrono::milliseconds;

    for (int i = 0; i < n; i++) {
      cout << rand() % 2;
    }

    int offset;
    while (true) {
      offset = rand() % n + 1; // random digit in row 1..n

      cout << "\x1b[" << offset << "D";
      cout.flush();
      sleep_for(milliseconds(pause));

      cout << rand() % 2;
      cout.flush();
      // cout << "\x1b[" << 1 << "D";
      // cout.flush();
      sleep_for(milliseconds(pause));

      cout << "\x1b[" << offset - 1 << "C";
      cout.flush();
      sleep_for(milliseconds(pause));
    }
}

As far as I understand CSI offset D moves cursor back offset times if it's possible in current row, then I cout random digit, that moves cursor 1 time forward, then I use CSI offset - 1 C to move it forward offset - 1 times, that should return cursor to n + 1 row position, so n digits go before cursor, but sometimes it goes further than n + 1, so, after several iterations it ends up at the end of the row. I would like to know why it happens.

However, when I use cout << "\x1b[" << 1 << "D"; and then cout << "\x1b[" << offset << "C"; instead of cout << "\x1b[" << offset - 1 << "C"; it works fine.

I'm also aware of using CSI n G, which also works fine, but I want to understand what's going on with CSI n C/D . Maybe it's my maths fault, but not using cout << "\x1b[" << 1 << "D"; seems to be equivalent of using cout << "\x1b[" << offset - 1 << "C"; so it's really confusing to me.


Solution

  • It seems that \x1b[0C moves cursor to the right, at least on my terminal, (dunno if that is specified/required behavior).

    $ printf "a\x1b[0Cb\n"
    a b
    $ printf "a\x1b[1Cb\n"
    a b
    

    Check if offset - 1 is not zero.

    if (offset - 1) {
      cout << "\x1b[" << offset - 1 << "C";
    }