Resizing the console window
The most complicated part of the problem is probably to resize the console screen buffer and scroll window to the desired dimensions. This is because the functions SetConsoleWindowInfo and SetConsoleScreenBufferSize both have strong restrictions on how they can be used. You
- cannot set the dimensions of the scroll window to be larger than any of the dimensions of the scroll buffer, and
- cannot you cannot set the dimensions of the scroll buffer to be smaller than any of the dimensions of the scroll window.
This means that you must first shrink the dimensions of the scroll window that are too large, then set the scroll buffer to the desired size, and then expand the dimensions of the scroll window that are too large. See the following questions for further information:
There does exist a function called SetConsoleScreenBufferInfoEx which appears to be able to set both the size of the scroll window and the size of the screen buffer in a single function call, so it should theoretically be able to simplify the complicated situation mentioned above. However, this function is not well documented and does not work as expected with me, so I am not using it in my program below.
In my program below, I have created my own function ResizeConsoleWindow which according to my tests, appears to resize the console window to the desired size reliably.
Solving the flickering problem
My guess is that your flickering problem is caused by scrolling, which happens automatically when you write past the last line that is visible on the screen.
To prevent this from happening, I recommend that you do not use the stream objects provided by the standard C++ library, but rather use low-level console output functions, which gives you full control of the area to which you write the output. That way, you can prevent scrolling from occurring.
For representing the content of the console, you can create a 2D array of CHAR_INFO elements. This array can be initialized with the function ReadConsoleOutput and you can write the content of this array to the console using the function WriteConsoleOutput.
I have rewritten your code as described above:
#define WIN32_LEAN_AND_MEAN #include <Windows.h> #include <iostream> #include <stdexcept> #include <cassert> #define WIDTH 50 #define HEIGHT 30 CHAR_INFO board[HEIGHT][WIDTH]; static void UpdateBoard(); static void DrawBoard( HANDLE hConsole ); static void InitializeBoard( HANDLE hConsole ); static void ResizeConsoleWindow( HANDLE hConsole, int width, int height ); static void DisableBlinkingCursor( HANDLE hConsole ); int main() try { // get handle to console HANDLE hConsole = GetStdHandle( STD_OUTPUT_HANDLE ); // resize console window and buffer to desired dimensions ResizeConsoleWindow( hConsole, WIDTH, HEIGHT ); // disable blinking of the cursor DisableBlinkingCursor( hConsole ); // initialize the 2D board array InitializeBoard( hConsole ); // update and draw board in infinite loop for (;;) { UpdateBoard(); DrawBoard( hConsole ); } } catch ( const std::runtime_error &e ) { std::cerr << "Runtime error: " << e.what() << '\n'; } static void UpdateBoard() { // draw the lines with the top wall for ( int i = 0; i < WIDTH; i++ ) { board[0][i].Char.AsciiChar = '#'; } // draw the left and right walls, clearing the space in between for ( int i = 1; i < HEIGHT - 1; i++ ) { board[i][0].Char.AsciiChar = '#'; for ( int j = 1; j < WIDTH - 1; j++ ) { board[i][j].Char.AsciiChar = ' '; } board[i][WIDTH-1].Char.AsciiChar = '#'; } // draw the bottom wall for ( int i = 0; i < WIDTH; i++ ) { board[HEIGHT-1][i].Char.AsciiChar = '#'; } } static void DrawBoard( HANDLE hConsole ) { SMALL_RECT rect = { 0, 0, WIDTH - 1, HEIGHT - 1 }; if ( ! WriteConsoleOutputA( hConsole, &board[0][0], COORD{ WIDTH, HEIGHT }, COORD{ 0, 0 }, &rect ) ) { throw std::runtime_error( "WriteConsoleOutput error!" ); } } static void InitializeBoard( HANDLE hConsole ) { SMALL_RECT rect = { 0, 0, WIDTH - 1, HEIGHT - 1 }; if ( ! ReadConsoleOutputA( hConsole, &board[0][0], COORD{ WIDTH, HEIGHT }, COORD{ 0, 0 }, &rect ) ) { throw std::runtime_error( "ReadConsoleOutput error!" ); } } static void ResizeConsoleWindow( HANDLE hConsole, int width, int height ) { CONSOLE_SCREEN_BUFFER_INFO sbi; // set console cursor position to 0, 0 if ( ! SetConsoleCursorPosition( hConsole, COORD{ 0, 0 } ) ) { throw std::runtime_error( "Error setting console cursor position!" ); } // get information about console screen buffer size and console window size if ( ! GetConsoleScreenBufferInfo( hConsole, &sbi ) ) { throw std::runtime_error( "Error getting console information!" ); } // the following calculations are based on the assumption that the top-left // corner of the scroll window is 0,0, so make sure that this assumption is // correct assert( sbi.srWindow.Top == 0 && sbi.srWindow.Left == 0 ); // shrink scroll window, if necessary bool bMustShrinkX = sbi.srWindow.Right > WIDTH - 1; bool bMustShrinkY = sbi.srWindow.Bottom > HEIGHT - 1; if ( bMustShrinkX || bMustShrinkY ) { if ( bMustShrinkX ) { sbi.srWindow.Right = WIDTH - 1; } if ( bMustShrinkY ) { sbi.srWindow.Bottom = HEIGHT - 1; } if ( ! SetConsoleWindowInfo( hConsole, TRUE, &sbi.srWindow ) ) { throw std::runtime_error( "SetConsoleWindowInfo error!" ); } } // set scroll buffer size sbi.dwSize.X = WIDTH; sbi.dwSize.Y = HEIGHT; if ( ! SetConsoleScreenBufferSize( hConsole, sbi.dwSize ) ) { throw std::runtime_error( "Error setting buffer size!" ); } // expand scroll window, if necessary bool bMustExpandX = sbi.srWindow.Right < WIDTH - 1; bool bMustExpandY = sbi.srWindow.Bottom < HEIGHT - 1; if ( bMustExpandX || bMustExpandY ) { if ( bMustExpandX ) { sbi.srWindow.Right = WIDTH - 1; } if ( bMustExpandY ) { sbi.srWindow.Bottom = HEIGHT - 1; } if ( ! SetConsoleWindowInfo( hConsole, TRUE, &sbi.srWindow ) ) { throw std::runtime_error( "SetConsoleWindowInfo error!" ); } } } static void DisableBlinkingCursor( HANDLE hConsole ) { CONSOLE_CURSOR_INFO cci; // hide cursor of console if ( ! GetConsoleCursorInfo( hConsole, &cci ) ) { throw std::runtime_error( "GetConsoleCursorInfo error!" ); } cci.bVisible = FALSE; if ( ! SetConsoleCursorInfo( hConsole, &cci ) ) { throw std::runtime_error( "SetConsoleCursorInfo error!" ); } }
This program prints the following output in an infinite loop, without any flickering:
################################################## # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ##################################################
cout. Secondly create a 'buffer' (which is invisble), do all your drawing to that buffer, then when you are finished swap the buffer you've just created with the buffer that is currently being displayed. That way the whole modifed screen gets displayed at once without any flickering. You can find some documentation herencurses(if you're ok with using msys2)CreateConsoleScreenBuffer), perform all rendering to it, and then make it the active screen buffer (SetConsoleActiveScreenBuffer).cout, and use SetConsoleCursorPosition. Writing more than one character at once to the console will probably be better for performance.