17.3.18 Scrolling the Screen Buffer

The current window origin or scroll position is defined as the cell of the screen buffer that is currently displayed at the top left corner of the window. Initially, the window origin is (0,0), i.e., the top left corner of the screen buffer is at the top left corner of the window. However, the origin of the screen buffer with respect to the window can change if the contents of the screen buffer are scrolled in the window. If the active screen buffer is larger than the console window, scroll bars are available for the user to scroll the contents. Scrolling the screen buffer in the window can happen automatically if you are using WriteFile in wrap at EOL mode and text is written past the last row of the window. Or the application can cause scrolling to occur by calling SetConsoleWindowInfo. Scrolling is not reported as an event in the input buffer.

You can call GetConsoleScreenBufferInfo to determine the current window origin (the Left, Top coordinates of the srWindow field of the CONSOLE_SCREEN_BUFFER_INFO structure).

You can cause the contents of the screen buffer to scroll in the window by calling SetConsoleWindowInfo. This can also be used to change the window size. The function takes a SMALL_RECT argument whose coordinates may specify the new topleft and bottomright corners of the window either as absolute screen buffer coordinates or as deltas from the current window coordinates. For example, the following code fragment modifies the absolute coordinates returned by GetConsoleScreenBufferInfo to scroll the view of the screen buffer up by one line:

HANDLE hOutput;

BOOL bResult;

CONSOLE_SCREEN_BUFFER_INFO Info;

SMALL_RECT srWindow;

bResult = GetConsoleScreenBufferInfo(hOutput, &Info);

srWindow = Info.srWindow;

srWindow.Top += 1;

srWindow.Bottom += 1;

bResult = SetConsoleWindowInfo(hOutput, TRUE, &srWindow);

The same scrolling could be done using deltas as follows:

HANDLE hOutput;

BOOL bResult;

SMALL_RECT srWindow;

srWindow.Top = 1;

srWindow.Bottom = 1;

srWindow.Left = 0; // no change

srWindow.Right = 0; // no change

bResult = SetConsoleWindowInfo(hOutput, FALSE, &srWindow);

These examples would fail if the last row of the screen buffer is currently displayed in the last row of the window, since scrolling the screen buffer up would leave the window extending beyond the edge of the screen buffer. To handle this situation, you could call SetConsoleScreenBufferSize to increase the number of rows in the screen buffer. This would make it possible to shift the window origin without exposing the bottom of the window. Alternatively, you could use ScrollConsoleScreenBuffer to scroll the contents of the screen buffer, discarding the top row of the screen buffer and leaving the window origin unchanged. The code fragment below shows a variation of this idea where the top five rows of the buffer contain static data and only the rows below that are scrolled:

HANDLE hOutput;

BOOL bResult;

CONSOLE_SCREEN_BUFFER_INFO Info;

SMALL_RECT srScrollRect, srClipRect;

CHAR_INFO Fill;

// first get the size of the screen buffer

bResult = GetConsoleScreenBufferInfo(hOutput, &Info);

// scroll rectangle is from row 5 to bottom of screen buffer

srScrollRect.Top = 5;

srScrollRect.Left = 0;

srScrollRect.Bottom = Info.dwSize.Y - 1;

srScrollRect.Right = Info.dwSize.X - 1;

// destination for top of scroll rectangle is row(4) */

dwDestinationOrigin.Y = 4;

dwDestinationOrigin.X = 0;

/* copying clipped to the original scroll rectangle */

/* so row 5 is discarded; row 4 is unchanged */

srClipRect = srScrollRect;

/* fill bottom row with green blanks */

Fill.Attributes = BACKGROUND_GREEN | FOREGROUND_RED;

Fill.Char.AsciiChar = ' ';

bResult = ScrollConsoleScreenBuffer(hOutput,

&srScrollRect,

&srClipRect,

dwDestinationOrigin,

&Fill

);

The same code could be used to scroll the entire screen buffer by changing two lines:

// scroll from top of screen buffer instead of row 5

srScrollRect.Top = 0;

// scroll top row to row (-1), outside the screen buffer

dwDestinationOrigin.Y = -1;

Another use of this functions is to implement delete line. The scroll rectangle would encompass all data below the deleted line and the destination origin would be the first character in the deleted line.