How to bring std::cout output back to the top after a new line

Viewed 106

I have the following menu, which is supposed to update based on whether the user has typed the keys F1 or F2:

int main()
{
    bool f1 = false;
    bool f2 = false;
 
    while (true)    
    {
        std::cout << "[F1]:  " << (f1 ? "ON" : "OFF") << std::endl;
        std::cout << "[F2]:  " << (f2 ? "ON" : "OFF") << std::endl;
        std::cout << "[INS] to quit" << std::endl;

        if (GetAsyncKeyState(VK_INSERT) & 0x1)
            break;

        if (GetAsyncKeyState(VK_F1) & 0x1)
            f1 = !f1;
        
        if (GetAsyncKeyState(VK_F2) & 0x1)
            f2 = !f2;
        
        Sleep(100);
        cleanWindow();
    }

    return 0;
}

Now, I was using system("cls") before and it was working "fine", but I've been told that I should rather use the Win32 API for cleaning out the console, and so I created cleanWindow() as described by this MSVC article.

DWORD cleanWindow()
{
    HANDLE hStdOut;

    hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

    // Fetch existing console mode so we correctly add a flag and not turn off others
    DWORD mode = 0;
    if (!GetConsoleMode(hStdOut, &mode))
    {
        return ::GetLastError();
    }

    // Hold original mode to restore on exit to be cooperative with other command-line apps.
    const DWORD originalMode = mode;
    mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;

    // Try to set the mode.
    if (!SetConsoleMode(hStdOut, mode))
    {
        return ::GetLastError();
    }

    // Write the sequence for clearing the display.
    // \x1b[2J is the code for clearing the screen and set cursor to home
    DWORD written = 0;
    PCWSTR sequence = L"\x1b[2J";
    if (!WriteConsoleW(hStdOut, sequence, (DWORD)wcslen(sequence), &written, NULL))
    {
        // If we fail, try to restore the mode on the way out.
        SetConsoleMode(hStdOut, originalMode);
        return ::GetLastError();
    }

    // To also clear the scroll back, emit L"\x1b[3J" as well.
    // 2J only clears the visible window and 3J only clears the scroll back.

    // Restore the mode on the way out to be nice to other command-line applications.
    SetConsoleMode(hStdOut, originalMode);
}

Now, the problem is that the "menu" is at the end of command prompt rather than at the start, as it was with system("cls"):

output

My question is, how do I do fix this? How do I bring the output back to the top of the shell?

EDIT:

I also edited the cleanWindow() function to write the sequences: \033[2J and L"\033[H" with WriteConsoleW(), which works, but I still get the "blinking" effect just like with system("cls"), which is something I was trying to avoid.

1 Answers

You can use SetConsoleCursorPosition to set the cursor location back to the top left after doing the clear.

There are also ANSI escape codes (similar to the one you use to clear the screen) that would allow you to reposition the cursor.

"\033[r;cH"

replacing r with the row and c the column to move to. They are 1-based, and default to the top left, so you can use "\033[1;1H" or just "\033[H"

Related