changeset 7078:383d6f39669b v7.4.851

commit https://github.com/vim/vim/commit/615942452eb74eee7d8386fd3d76a1534181fa06 Author: Bram Moolenaar <Bram@vim.org> Date: Tue Sep 1 20:23:37 2015 +0200 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)
author Christian Brabandt <cb@256bit.org>
date Tue, 01 Sep 2015 20:30:04 +0200
parents 2f26e5950851
children 1d7d1607be8a
files src/os_win32.c src/version.c
diffstat 2 files changed, 80 insertions(+), 127 deletions(-) [+]
line wrap: on
line diff
--- a/src/os_win32.c
+++ b/src/os_win32.c
@@ -2192,8 +2192,7 @@ typedef struct ConsoleBufferStruct
 {
     BOOL			IsValid;
     CONSOLE_SCREEN_BUFFER_INFO	Info;
-    PCHAR_INFO			Buffer;
-    COORD			BufferSize;
+    HANDLE			handle;
 } ConsoleBuffer;
 
 /*
@@ -2210,77 +2209,81 @@ typedef struct ConsoleBufferStruct
 SaveConsoleBuffer(
     ConsoleBuffer *cb)
 {
-    DWORD NumCells;
-    COORD BufferCoord;
-    SMALL_RECT ReadRegion;
-    WORD Y, Y_incr;
-
     if (cb == NULL)
 	return FALSE;
 
-    if (!GetConsoleScreenBufferInfo(g_hConOut, &cb->Info))
+    if (!GetConsoleScreenBufferInfo(cb->handle, &cb->Info))
     {
 	cb->IsValid = FALSE;
 	return FALSE;
     }
     cb->IsValid = TRUE;
 
-    /*
-     * Allocate a buffer large enough to hold the entire console screen
-     * buffer.  If this ConsoleBuffer structure has already been initialized
-     * with a buffer of the correct size, then just use that one.
-     */
-    if (!cb->IsValid || cb->Buffer == NULL ||
-	    cb->BufferSize.X != cb->Info.dwSize.X ||
-	    cb->BufferSize.Y != cb->Info.dwSize.Y)
-    {
-	cb->BufferSize.X = cb->Info.dwSize.X;
-	cb->BufferSize.Y = cb->Info.dwSize.Y;
-	NumCells = cb->BufferSize.X * cb->BufferSize.Y;
-	vim_free(cb->Buffer);
-	cb->Buffer = (PCHAR_INFO)alloc(NumCells * sizeof(CHAR_INFO));
-	if (cb->Buffer == NULL)
-	    return FALSE;
-    }
+    return TRUE;
+}
+
+/*
+ * CopyOldConsoleBuffer()
+ * Description:
+ *  Copies the old console buffer contents to the current console buffer.
+ *  This is used when 'restorescreen' is off.
+ * Returns:
+ *  TRUE on success
+ */
+    static BOOL
+CopyOldConsoleBuffer(
+    ConsoleBuffer   *cb,
+    HANDLE	    hConOld)
+{
+    COORD		    BufferCoord;
+    COORD		    BufferSize;
+    PCHAR_INFO		    Buffer;
+    DWORD		    NumCells;
+    SMALL_RECT		    ReadRegion;
 
     /*
-     * We will now copy the console screen buffer into our buffer.
-     * ReadConsoleOutput() seems to be limited as far as how much you
-     * can read at a time.  Empirically, this number seems to be about
-     * 12000 cells (rows * columns).  Start at position (0, 0) and copy
-     * in chunks until it is all copied.  The chunks will all have the
-     * same horizontal characteristics, so initialize them now.  The
-     * height of each chunk will be (12000 / width).
+     * Before copying the buffer contents, clear the current buffer, and
+     * restore the window information.  Doing this now prevents old buffer
+     * contents from "flashing" onto the screen.
      */
-    BufferCoord.X = 0;
+    ClearConsoleBuffer(cb->Info.wAttributes);
+
+    /* We only need to copy the window area, not whole buffer. */
+    BufferSize.X = cb->Info.srWindow.Right - cb->Info.srWindow.Left + 1;
+    BufferSize.Y = cb->Info.srWindow.Bottom - cb->Info.srWindow.Top + 1;
     ReadRegion.Left = 0;
-    ReadRegion.Right = cb->Info.dwSize.X - 1;
-    Y_incr = 12000 / cb->Info.dwSize.X;
-    for (Y = 0; Y < cb->BufferSize.Y; Y += Y_incr)
+    ReadRegion.Right = BufferSize.X - 1;
+    ReadRegion.Top = 0;
+    ReadRegion.Bottom = BufferSize.Y - 1;
+
+    NumCells = BufferSize.X * BufferSize.Y;
+    Buffer = (PCHAR_INFO)alloc(NumCells * sizeof(CHAR_INFO));
+    if (Buffer == NULL)
+	return FALSE;
+
+    BufferCoord.X = 0;
+    BufferCoord.Y = 0;
+
+    if (!ReadConsoleOutputW(hConOld,	    /* output handle */
+		Buffer,			    /* our buffer */
+		BufferSize,		    /* dimensions of our buffer */
+		BufferCoord,		    /* offset in our buffer */
+		&ReadRegion))		    /* region to save */
     {
-	/*
-	 * Read into position (0, Y) in our buffer.
-	 */
-	BufferCoord.Y = Y;
-	/*
-	 * Read the region whose top left corner is (0, Y) and whose bottom
-	 * right corner is (width - 1, Y + Y_incr - 1).  This should define
-	 * a region of size width by Y_incr.  Don't worry if this region is
-	 * too large for the remaining buffer; it will be cropped.
-	 */
-	ReadRegion.Top = Y;
-	ReadRegion.Bottom = Y + Y_incr - 1;
-	if (!ReadConsoleOutput(g_hConOut,	/* output handle */
-		cb->Buffer,			/* our buffer */
-		cb->BufferSize,			/* dimensions of our buffer */
-		BufferCoord,			/* offset in our buffer */
-		&ReadRegion))			/* region to save */
-	{
-	    vim_free(cb->Buffer);
-	    cb->Buffer = NULL;
-	    return FALSE;
-	}
+	vim_free(Buffer);
+	return FALSE;
     }
+    if (!WriteConsoleOutputW(g_hConOut,     /* output handle */
+		Buffer,			    /* our buffer */
+		BufferSize,		    /* dimensions of our buffer */
+		BufferCoord,		    /* offset in our buffer */
+		&ReadRegion))		    /* region to restore */
+    {
+	vim_free(Buffer);
+	return FALSE;
+    }
+    vim_free(Buffer);
+    SetConsoleWindowInfo(g_hConOut, TRUE, &ReadRegion);
 
     return TRUE;
 }
@@ -2299,67 +2302,20 @@ RestoreConsoleBuffer(
     ConsoleBuffer   *cb,
     BOOL	    RestoreScreen)
 {
-    COORD BufferCoord;
-    SMALL_RECT WriteRegion;
+    HANDLE hConOld;
 
     if (cb == NULL || !cb->IsValid)
 	return FALSE;
 
-    /*
-     * Before restoring the buffer contents, clear the current buffer, and
-     * restore the cursor position and window information.  Doing this now
-     * prevents old buffer contents from "flashing" onto the screen.
-     */
-    if (RestoreScreen)
-	ClearConsoleBuffer(cb->Info.wAttributes);
-
-    FitConsoleWindow(cb->Info.dwSize, TRUE);
-    if (!SetConsoleScreenBufferSize(g_hConOut, cb->Info.dwSize))
-	return FALSE;
-    if (!SetConsoleTextAttribute(g_hConOut, cb->Info.wAttributes))
-	return FALSE;
-
-    if (!RestoreScreen)
-    {
-	/*
-	 * No need to restore the screen buffer contents, so we're done.
-	 */
-	return TRUE;
-    }
-
-    if (!SetConsoleCursorPosition(g_hConOut, cb->Info.dwCursorPosition))
-	return FALSE;
-    if (!SetConsoleWindowInfo(g_hConOut, TRUE, &cb->Info.srWindow))
-	return FALSE;
-
-    /*
-     * Restore the screen buffer contents.
-     */
-    if (cb->Buffer != NULL)
-    {
-	BufferCoord.X = 0;
-	BufferCoord.Y = 0;
-	WriteRegion.Left = 0;
-	WriteRegion.Top = 0;
-	WriteRegion.Right = cb->Info.dwSize.X - 1;
-	WriteRegion.Bottom = cb->Info.dwSize.Y - 1;
-	if (!WriteConsoleOutput(g_hConOut,	/* output handle */
-		cb->Buffer,			/* our buffer */
-		cb->BufferSize,			/* dimensions of our buffer */
-		BufferCoord,			/* offset in our buffer */
-		&WriteRegion))			/* region to restore */
-	{
-	    return FALSE;
-	}
-    }
+    hConOld = g_hConOut;
+    g_hConOut = cb->handle;
+    if (!RestoreScreen && exiting)
+	CopyOldConsoleBuffer(cb, hConOld);
+    SetConsoleActiveScreenBuffer(g_hConOut);
 
     return TRUE;
 }
 
-#define FEAT_RESTORE_ORIG_SCREEN
-#ifdef FEAT_RESTORE_ORIG_SCREEN
-static ConsoleBuffer g_cbOrig = { 0 };
-#endif
 static ConsoleBuffer g_cbNonTermcap = { 0 };
 static ConsoleBuffer g_cbTermcap = { 0 };
 
@@ -2498,9 +2454,6 @@ static DWORD g_cmodeout = 0;
     void
 mch_init(void)
 {
-#ifndef FEAT_RESTORE_ORIG_SCREEN
-    CONSOLE_SCREEN_BUFFER_INFO csbi;
-#endif
 #ifndef __MINGW32__
     extern int _fmode;
 #endif
@@ -2521,16 +2474,14 @@ mch_init(void)
     else
 	create_conin();
     g_hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
-
-#ifdef FEAT_RESTORE_ORIG_SCREEN
-    /* Save the initial console buffer for later restoration */
-    SaveConsoleBuffer(&g_cbOrig);
-    g_attrCurrent = g_attrDefault = g_cbOrig.Info.wAttributes;
-#else
+    g_cbNonTermcap.handle = g_hConOut;
+    g_cbTermcap.handle = CreateConsoleScreenBuffer(
+	    GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
+	    NULL, CONSOLE_TEXTMODE_BUFFER, NULL);
+
     /* Get current text attributes */
-    GetConsoleScreenBufferInfo(g_hConOut, &csbi);
-    g_attrCurrent = g_attrDefault = csbi.wAttributes;
-#endif
+    SaveConsoleBuffer(&g_cbNonTermcap);
+    g_attrCurrent = g_attrDefault = g_cbNonTermcap.Info.wAttributes;
     if (cterm_normal_fg_color == 0)
 	cterm_normal_fg_color = (g_attrCurrent & 0xf) + 1;
     if (cterm_normal_bg_color == 0)
@@ -2630,6 +2581,8 @@ mch_exit(int r)
     SetConsoleMode(g_hConIn,  g_cmodein);
     SetConsoleMode(g_hConOut, g_cmodeout);
 
+    CloseHandle(g_cbTermcap.handle);
+
 #ifdef DYNAMIC_GETTEXT
     dyn_libintl_end();
 #endif
@@ -5002,6 +4955,8 @@ termcap_mode_start(void)
 	 * screen buffer, and resize the buffer to match the current window
 	 * size.  We will use this as the size of our editing environment.
 	 */
+	g_hConOut = g_cbTermcap.handle;
+	SetConsoleActiveScreenBuffer(g_hConOut);
 	ClearConsoleBuffer(g_attrCurrent);
 	ResizeConBufAndWindow(g_hConOut, Columns, Rows);
     }
@@ -5045,11 +5000,7 @@ termcap_mode_end(void)
     cmodein &= ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
     SetConsoleMode(g_hConIn, cmodein);
 
-#ifdef FEAT_RESTORE_ORIG_SCREEN
-    cb = exiting ? &g_cbOrig : &g_cbNonTermcap;
-#else
     cb = &g_cbNonTermcap;
-#endif
     RestoreConsoleBuffer(cb, p_rs);
     SetConsoleCursorInfo(g_hConOut, &g_cci);
 
--- a/src/version.c
+++ b/src/version.c
@@ -742,6 +742,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    851,
+/**/
     850,
 /**/
     849,