Skip to content

Commit 6159424

Browse files
committed
patch 7.4.851
Problem: Saving and restoring the console buffer does not work properly. Solution: Instead of ReadConsoleOutputA/WriteConsoleOutputA use CreateConsoleScreenBuffer and SetConsoleActiveScreenBuffer. (Ken Takata)
1 parent 5447802 commit 6159424

2 files changed

Lines changed: 79 additions & 126 deletions

File tree

src/os_win32.c

Lines changed: 77 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -2192,8 +2192,7 @@ typedef struct ConsoleBufferStruct
21922192
{
21932193
BOOL IsValid;
21942194
CONSOLE_SCREEN_BUFFER_INFO Info;
2195-
PCHAR_INFO Buffer;
2196-
COORD BufferSize;
2195+
HANDLE handle;
21972196
} ConsoleBuffer;
21982197

21992198
/*
@@ -2210,77 +2209,81 @@ typedef struct ConsoleBufferStruct
22102209
SaveConsoleBuffer(
22112210
ConsoleBuffer *cb)
22122211
{
2213-
DWORD NumCells;
2214-
COORD BufferCoord;
2215-
SMALL_RECT ReadRegion;
2216-
WORD Y, Y_incr;
2217-
22182212
if (cb == NULL)
22192213
return FALSE;
22202214

2221-
if (!GetConsoleScreenBufferInfo(g_hConOut, &cb->Info))
2215+
if (!GetConsoleScreenBufferInfo(cb->handle, &cb->Info))
22222216
{
22232217
cb->IsValid = FALSE;
22242218
return FALSE;
22252219
}
22262220
cb->IsValid = TRUE;
22272221

2228-
/*
2229-
* Allocate a buffer large enough to hold the entire console screen
2230-
* buffer. If this ConsoleBuffer structure has already been initialized
2231-
* with a buffer of the correct size, then just use that one.
2232-
*/
2233-
if (!cb->IsValid || cb->Buffer == NULL ||
2234-
cb->BufferSize.X != cb->Info.dwSize.X ||
2235-
cb->BufferSize.Y != cb->Info.dwSize.Y)
2236-
{
2237-
cb->BufferSize.X = cb->Info.dwSize.X;
2238-
cb->BufferSize.Y = cb->Info.dwSize.Y;
2239-
NumCells = cb->BufferSize.X * cb->BufferSize.Y;
2240-
vim_free(cb->Buffer);
2241-
cb->Buffer = (PCHAR_INFO)alloc(NumCells * sizeof(CHAR_INFO));
2242-
if (cb->Buffer == NULL)
2243-
return FALSE;
2244-
}
2222+
return TRUE;
2223+
}
2224+
2225+
/*
2226+
* CopyOldConsoleBuffer()
2227+
* Description:
2228+
* Copies the old console buffer contents to the current console buffer.
2229+
* This is used when 'restorescreen' is off.
2230+
* Returns:
2231+
* TRUE on success
2232+
*/
2233+
static BOOL
2234+
CopyOldConsoleBuffer(
2235+
ConsoleBuffer *cb,
2236+
HANDLE hConOld)
2237+
{
2238+
COORD BufferCoord;
2239+
COORD BufferSize;
2240+
PCHAR_INFO Buffer;
2241+
DWORD NumCells;
2242+
SMALL_RECT ReadRegion;
22452243

22462244
/*
2247-
* We will now copy the console screen buffer into our buffer.
2248-
* ReadConsoleOutput() seems to be limited as far as how much you
2249-
* can read at a time. Empirically, this number seems to be about
2250-
* 12000 cells (rows * columns). Start at position (0, 0) and copy
2251-
* in chunks until it is all copied. The chunks will all have the
2252-
* same horizontal characteristics, so initialize them now. The
2253-
* height of each chunk will be (12000 / width).
2245+
* Before copying the buffer contents, clear the current buffer, and
2246+
* restore the window information. Doing this now prevents old buffer
2247+
* contents from "flashing" onto the screen.
22542248
*/
2255-
BufferCoord.X = 0;
2249+
ClearConsoleBuffer(cb->Info.wAttributes);
2250+
2251+
/* We only need to copy the window area, not whole buffer. */
2252+
BufferSize.X = cb->Info.srWindow.Right - cb->Info.srWindow.Left + 1;
2253+
BufferSize.Y = cb->Info.srWindow.Bottom - cb->Info.srWindow.Top + 1;
22562254
ReadRegion.Left = 0;
2257-
ReadRegion.Right = cb->Info.dwSize.X - 1;
2258-
Y_incr = 12000 / cb->Info.dwSize.X;
2259-
for (Y = 0; Y < cb->BufferSize.Y; Y += Y_incr)
2255+
ReadRegion.Right = BufferSize.X - 1;
2256+
ReadRegion.Top = 0;
2257+
ReadRegion.Bottom = BufferSize.Y - 1;
2258+
2259+
NumCells = BufferSize.X * BufferSize.Y;
2260+
Buffer = (PCHAR_INFO)alloc(NumCells * sizeof(CHAR_INFO));
2261+
if (Buffer == NULL)
2262+
return FALSE;
2263+
2264+
BufferCoord.X = 0;
2265+
BufferCoord.Y = 0;
2266+
2267+
if (!ReadConsoleOutputW(hConOld, /* output handle */
2268+
Buffer, /* our buffer */
2269+
BufferSize, /* dimensions of our buffer */
2270+
BufferCoord, /* offset in our buffer */
2271+
&ReadRegion)) /* region to save */
22602272
{
2261-
/*
2262-
* Read into position (0, Y) in our buffer.
2263-
*/
2264-
BufferCoord.Y = Y;
2265-
/*
2266-
* Read the region whose top left corner is (0, Y) and whose bottom
2267-
* right corner is (width - 1, Y + Y_incr - 1). This should define
2268-
* a region of size width by Y_incr. Don't worry if this region is
2269-
* too large for the remaining buffer; it will be cropped.
2270-
*/
2271-
ReadRegion.Top = Y;
2272-
ReadRegion.Bottom = Y + Y_incr - 1;
2273-
if (!ReadConsoleOutput(g_hConOut, /* output handle */
2274-
cb->Buffer, /* our buffer */
2275-
cb->BufferSize, /* dimensions of our buffer */
2276-
BufferCoord, /* offset in our buffer */
2277-
&ReadRegion)) /* region to save */
2278-
{
2279-
vim_free(cb->Buffer);
2280-
cb->Buffer = NULL;
2281-
return FALSE;
2282-
}
2273+
vim_free(Buffer);
2274+
return FALSE;
2275+
}
2276+
if (!WriteConsoleOutputW(g_hConOut, /* output handle */
2277+
Buffer, /* our buffer */
2278+
BufferSize, /* dimensions of our buffer */
2279+
BufferCoord, /* offset in our buffer */
2280+
&ReadRegion)) /* region to restore */
2281+
{
2282+
vim_free(Buffer);
2283+
return FALSE;
22832284
}
2285+
vim_free(Buffer);
2286+
SetConsoleWindowInfo(g_hConOut, TRUE, &ReadRegion);
22842287

22852288
return TRUE;
22862289
}
@@ -2299,67 +2302,20 @@ RestoreConsoleBuffer(
22992302
ConsoleBuffer *cb,
23002303
BOOL RestoreScreen)
23012304
{
2302-
COORD BufferCoord;
2303-
SMALL_RECT WriteRegion;
2305+
HANDLE hConOld;
23042306

23052307
if (cb == NULL || !cb->IsValid)
23062308
return FALSE;
23072309

2308-
/*
2309-
* Before restoring the buffer contents, clear the current buffer, and
2310-
* restore the cursor position and window information. Doing this now
2311-
* prevents old buffer contents from "flashing" onto the screen.
2312-
*/
2313-
if (RestoreScreen)
2314-
ClearConsoleBuffer(cb->Info.wAttributes);
2315-
2316-
FitConsoleWindow(cb->Info.dwSize, TRUE);
2317-
if (!SetConsoleScreenBufferSize(g_hConOut, cb->Info.dwSize))
2318-
return FALSE;
2319-
if (!SetConsoleTextAttribute(g_hConOut, cb->Info.wAttributes))
2320-
return FALSE;
2321-
2322-
if (!RestoreScreen)
2323-
{
2324-
/*
2325-
* No need to restore the screen buffer contents, so we're done.
2326-
*/
2327-
return TRUE;
2328-
}
2329-
2330-
if (!SetConsoleCursorPosition(g_hConOut, cb->Info.dwCursorPosition))
2331-
return FALSE;
2332-
if (!SetConsoleWindowInfo(g_hConOut, TRUE, &cb->Info.srWindow))
2333-
return FALSE;
2334-
2335-
/*
2336-
* Restore the screen buffer contents.
2337-
*/
2338-
if (cb->Buffer != NULL)
2339-
{
2340-
BufferCoord.X = 0;
2341-
BufferCoord.Y = 0;
2342-
WriteRegion.Left = 0;
2343-
WriteRegion.Top = 0;
2344-
WriteRegion.Right = cb->Info.dwSize.X - 1;
2345-
WriteRegion.Bottom = cb->Info.dwSize.Y - 1;
2346-
if (!WriteConsoleOutput(g_hConOut, /* output handle */
2347-
cb->Buffer, /* our buffer */
2348-
cb->BufferSize, /* dimensions of our buffer */
2349-
BufferCoord, /* offset in our buffer */
2350-
&WriteRegion)) /* region to restore */
2351-
{
2352-
return FALSE;
2353-
}
2354-
}
2310+
hConOld = g_hConOut;
2311+
g_hConOut = cb->handle;
2312+
if (!RestoreScreen && exiting)
2313+
CopyOldConsoleBuffer(cb, hConOld);
2314+
SetConsoleActiveScreenBuffer(g_hConOut);
23552315

23562316
return TRUE;
23572317
}
23582318

2359-
#define FEAT_RESTORE_ORIG_SCREEN
2360-
#ifdef FEAT_RESTORE_ORIG_SCREEN
2361-
static ConsoleBuffer g_cbOrig = { 0 };
2362-
#endif
23632319
static ConsoleBuffer g_cbNonTermcap = { 0 };
23642320
static ConsoleBuffer g_cbTermcap = { 0 };
23652321

@@ -2498,9 +2454,6 @@ static DWORD g_cmodeout = 0;
24982454
void
24992455
mch_init(void)
25002456
{
2501-
#ifndef FEAT_RESTORE_ORIG_SCREEN
2502-
CONSOLE_SCREEN_BUFFER_INFO csbi;
2503-
#endif
25042457
#ifndef __MINGW32__
25052458
extern int _fmode;
25062459
#endif
@@ -2521,16 +2474,14 @@ mch_init(void)
25212474
else
25222475
create_conin();
25232476
g_hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
2477+
g_cbNonTermcap.handle = g_hConOut;
2478+
g_cbTermcap.handle = CreateConsoleScreenBuffer(
2479+
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
2480+
NULL, CONSOLE_TEXTMODE_BUFFER, NULL);
25242481

2525-
#ifdef FEAT_RESTORE_ORIG_SCREEN
2526-
/* Save the initial console buffer for later restoration */
2527-
SaveConsoleBuffer(&g_cbOrig);
2528-
g_attrCurrent = g_attrDefault = g_cbOrig.Info.wAttributes;
2529-
#else
25302482
/* Get current text attributes */
2531-
GetConsoleScreenBufferInfo(g_hConOut, &csbi);
2532-
g_attrCurrent = g_attrDefault = csbi.wAttributes;
2533-
#endif
2483+
SaveConsoleBuffer(&g_cbNonTermcap);
2484+
g_attrCurrent = g_attrDefault = g_cbNonTermcap.Info.wAttributes;
25342485
if (cterm_normal_fg_color == 0)
25352486
cterm_normal_fg_color = (g_attrCurrent & 0xf) + 1;
25362487
if (cterm_normal_bg_color == 0)
@@ -2630,6 +2581,8 @@ mch_exit(int r)
26302581
SetConsoleMode(g_hConIn, g_cmodein);
26312582
SetConsoleMode(g_hConOut, g_cmodeout);
26322583

2584+
CloseHandle(g_cbTermcap.handle);
2585+
26332586
#ifdef DYNAMIC_GETTEXT
26342587
dyn_libintl_end();
26352588
#endif
@@ -5002,6 +4955,8 @@ termcap_mode_start(void)
50024955
* screen buffer, and resize the buffer to match the current window
50034956
* size. We will use this as the size of our editing environment.
50044957
*/
4958+
g_hConOut = g_cbTermcap.handle;
4959+
SetConsoleActiveScreenBuffer(g_hConOut);
50054960
ClearConsoleBuffer(g_attrCurrent);
50064961
ResizeConBufAndWindow(g_hConOut, Columns, Rows);
50074962
}
@@ -5045,11 +5000,7 @@ termcap_mode_end(void)
50455000
cmodein &= ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
50465001
SetConsoleMode(g_hConIn, cmodein);
50475002

5048-
#ifdef FEAT_RESTORE_ORIG_SCREEN
5049-
cb = exiting ? &g_cbOrig : &g_cbNonTermcap;
5050-
#else
50515003
cb = &g_cbNonTermcap;
5052-
#endif
50535004
RestoreConsoleBuffer(cb, p_rs);
50545005
SetConsoleCursorInfo(g_hConOut, &g_cci);
50555006

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,8 @@ static char *(features[]) =
741741

742742
static int included_patches[] =
743743
{ /* Add new patch number below this line */
744+
/**/
745+
851,
744746
/**/
745747
850,
746748
/**/

0 commit comments

Comments
 (0)