comparison src/os_win32.c @ 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 f77d1f32c357
children 1a34f5272977
comparison
equal deleted inserted replaced
7077:2f26e5950851 7078:383d6f39669b
2190 2190
2191 typedef struct ConsoleBufferStruct 2191 typedef struct ConsoleBufferStruct
2192 { 2192 {
2193 BOOL IsValid; 2193 BOOL IsValid;
2194 CONSOLE_SCREEN_BUFFER_INFO Info; 2194 CONSOLE_SCREEN_BUFFER_INFO Info;
2195 PCHAR_INFO Buffer; 2195 HANDLE handle;
2196 COORD BufferSize;
2197 } ConsoleBuffer; 2196 } ConsoleBuffer;
2198 2197
2199 /* 2198 /*
2200 * SaveConsoleBuffer() 2199 * SaveConsoleBuffer()
2201 * Description: 2200 * Description:
2208 */ 2207 */
2209 static BOOL 2208 static BOOL
2210 SaveConsoleBuffer( 2209 SaveConsoleBuffer(
2211 ConsoleBuffer *cb) 2210 ConsoleBuffer *cb)
2212 { 2211 {
2213 DWORD NumCells;
2214 COORD BufferCoord;
2215 SMALL_RECT ReadRegion;
2216 WORD Y, Y_incr;
2217
2218 if (cb == NULL) 2212 if (cb == NULL)
2219 return FALSE; 2213 return FALSE;
2220 2214
2221 if (!GetConsoleScreenBufferInfo(g_hConOut, &cb->Info)) 2215 if (!GetConsoleScreenBufferInfo(cb->handle, &cb->Info))
2222 { 2216 {
2223 cb->IsValid = FALSE; 2217 cb->IsValid = FALSE;
2224 return FALSE; 2218 return FALSE;
2225 } 2219 }
2226 cb->IsValid = TRUE; 2220 cb->IsValid = TRUE;
2227 2221
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;
2243
2228 /* 2244 /*
2229 * Allocate a buffer large enough to hold the entire console screen 2245 * Before copying the buffer contents, clear the current buffer, and
2230 * buffer. If this ConsoleBuffer structure has already been initialized 2246 * restore the window information. Doing this now prevents old buffer
2231 * with a buffer of the correct size, then just use that one. 2247 * contents from "flashing" onto the screen.
2232 */ 2248 */
2233 if (!cb->IsValid || cb->Buffer == NULL || 2249 ClearConsoleBuffer(cb->Info.wAttributes);
2234 cb->BufferSize.X != cb->Info.dwSize.X || 2250
2235 cb->BufferSize.Y != cb->Info.dwSize.Y) 2251 /* We only need to copy the window area, not whole buffer. */
2236 { 2252 BufferSize.X = cb->Info.srWindow.Right - cb->Info.srWindow.Left + 1;
2237 cb->BufferSize.X = cb->Info.dwSize.X; 2253 BufferSize.Y = cb->Info.srWindow.Bottom - cb->Info.srWindow.Top + 1;
2238 cb->BufferSize.Y = cb->Info.dwSize.Y; 2254 ReadRegion.Left = 0;
2239 NumCells = cb->BufferSize.X * cb->BufferSize.Y; 2255 ReadRegion.Right = BufferSize.X - 1;
2240 vim_free(cb->Buffer); 2256 ReadRegion.Top = 0;
2241 cb->Buffer = (PCHAR_INFO)alloc(NumCells * sizeof(CHAR_INFO)); 2257 ReadRegion.Bottom = BufferSize.Y - 1;
2242 if (cb->Buffer == NULL) 2258
2243 return FALSE; 2259 NumCells = BufferSize.X * BufferSize.Y;
2244 } 2260 Buffer = (PCHAR_INFO)alloc(NumCells * sizeof(CHAR_INFO));
2245 2261 if (Buffer == NULL)
2246 /* 2262 return FALSE;
2247 * We will now copy the console screen buffer into our buffer. 2263
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).
2254 */
2255 BufferCoord.X = 0; 2264 BufferCoord.X = 0;
2256 ReadRegion.Left = 0; 2265 BufferCoord.Y = 0;
2257 ReadRegion.Right = cb->Info.dwSize.X - 1; 2266
2258 Y_incr = 12000 / cb->Info.dwSize.X; 2267 if (!ReadConsoleOutputW(hConOld, /* output handle */
2259 for (Y = 0; Y < cb->BufferSize.Y; Y += Y_incr) 2268 Buffer, /* our buffer */
2260 { 2269 BufferSize, /* dimensions of our buffer */
2261 /* 2270 BufferCoord, /* offset in our buffer */
2262 * Read into position (0, Y) in our buffer. 2271 &ReadRegion)) /* region to save */
2263 */ 2272 {
2264 BufferCoord.Y = Y; 2273 vim_free(Buffer);
2265 /* 2274 return FALSE;
2266 * Read the region whose top left corner is (0, Y) and whose bottom 2275 }
2267 * right corner is (width - 1, Y + Y_incr - 1). This should define 2276 if (!WriteConsoleOutputW(g_hConOut, /* output handle */
2268 * a region of size width by Y_incr. Don't worry if this region is 2277 Buffer, /* our buffer */
2269 * too large for the remaining buffer; it will be cropped. 2278 BufferSize, /* dimensions of our buffer */
2270 */ 2279 BufferCoord, /* offset in our buffer */
2271 ReadRegion.Top = Y; 2280 &ReadRegion)) /* region to restore */
2272 ReadRegion.Bottom = Y + Y_incr - 1; 2281 {
2273 if (!ReadConsoleOutput(g_hConOut, /* output handle */ 2282 vim_free(Buffer);
2274 cb->Buffer, /* our buffer */ 2283 return FALSE;
2275 cb->BufferSize, /* dimensions of our buffer */ 2284 }
2276 BufferCoord, /* offset in our buffer */ 2285 vim_free(Buffer);
2277 &ReadRegion)) /* region to save */ 2286 SetConsoleWindowInfo(g_hConOut, TRUE, &ReadRegion);
2278 {
2279 vim_free(cb->Buffer);
2280 cb->Buffer = NULL;
2281 return FALSE;
2282 }
2283 }
2284 2287
2285 return TRUE; 2288 return TRUE;
2286 } 2289 }
2287 2290
2288 /* 2291 /*
2297 static BOOL 2300 static BOOL
2298 RestoreConsoleBuffer( 2301 RestoreConsoleBuffer(
2299 ConsoleBuffer *cb, 2302 ConsoleBuffer *cb,
2300 BOOL RestoreScreen) 2303 BOOL RestoreScreen)
2301 { 2304 {
2302 COORD BufferCoord; 2305 HANDLE hConOld;
2303 SMALL_RECT WriteRegion;
2304 2306
2305 if (cb == NULL || !cb->IsValid) 2307 if (cb == NULL || !cb->IsValid)
2306 return FALSE; 2308 return FALSE;
2307 2309
2308 /* 2310 hConOld = g_hConOut;
2309 * Before restoring the buffer contents, clear the current buffer, and 2311 g_hConOut = cb->handle;
2310 * restore the cursor position and window information. Doing this now 2312 if (!RestoreScreen && exiting)
2311 * prevents old buffer contents from "flashing" onto the screen. 2313 CopyOldConsoleBuffer(cb, hConOld);
2312 */ 2314 SetConsoleActiveScreenBuffer(g_hConOut);
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 }
2355 2315
2356 return TRUE; 2316 return TRUE;
2357 } 2317 }
2358 2318
2359 #define FEAT_RESTORE_ORIG_SCREEN
2360 #ifdef FEAT_RESTORE_ORIG_SCREEN
2361 static ConsoleBuffer g_cbOrig = { 0 };
2362 #endif
2363 static ConsoleBuffer g_cbNonTermcap = { 0 }; 2319 static ConsoleBuffer g_cbNonTermcap = { 0 };
2364 static ConsoleBuffer g_cbTermcap = { 0 }; 2320 static ConsoleBuffer g_cbTermcap = { 0 };
2365 2321
2366 #ifdef FEAT_TITLE 2322 #ifdef FEAT_TITLE
2367 #ifdef __BORLANDC__ 2323 #ifdef __BORLANDC__
2496 * non-GUI version of mch_init(). 2452 * non-GUI version of mch_init().
2497 */ 2453 */
2498 void 2454 void
2499 mch_init(void) 2455 mch_init(void)
2500 { 2456 {
2501 #ifndef FEAT_RESTORE_ORIG_SCREEN
2502 CONSOLE_SCREEN_BUFFER_INFO csbi;
2503 #endif
2504 #ifndef __MINGW32__ 2457 #ifndef __MINGW32__
2505 extern int _fmode; 2458 extern int _fmode;
2506 #endif 2459 #endif
2507 2460
2508 /* Silently handle invalid parameters to CRT functions */ 2461 /* Silently handle invalid parameters to CRT functions */
2519 if (read_cmd_fd == 0) 2472 if (read_cmd_fd == 0)
2520 g_hConIn = GetStdHandle(STD_INPUT_HANDLE); 2473 g_hConIn = GetStdHandle(STD_INPUT_HANDLE);
2521 else 2474 else
2522 create_conin(); 2475 create_conin();
2523 g_hConOut = GetStdHandle(STD_OUTPUT_HANDLE); 2476 g_hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
2524 2477 g_cbNonTermcap.handle = g_hConOut;
2525 #ifdef FEAT_RESTORE_ORIG_SCREEN 2478 g_cbTermcap.handle = CreateConsoleScreenBuffer(
2526 /* Save the initial console buffer for later restoration */ 2479 GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
2527 SaveConsoleBuffer(&g_cbOrig); 2480 NULL, CONSOLE_TEXTMODE_BUFFER, NULL);
2528 g_attrCurrent = g_attrDefault = g_cbOrig.Info.wAttributes; 2481
2529 #else
2530 /* Get current text attributes */ 2482 /* Get current text attributes */
2531 GetConsoleScreenBufferInfo(g_hConOut, &csbi); 2483 SaveConsoleBuffer(&g_cbNonTermcap);
2532 g_attrCurrent = g_attrDefault = csbi.wAttributes; 2484 g_attrCurrent = g_attrDefault = g_cbNonTermcap.Info.wAttributes;
2533 #endif
2534 if (cterm_normal_fg_color == 0) 2485 if (cterm_normal_fg_color == 0)
2535 cterm_normal_fg_color = (g_attrCurrent & 0xf) + 1; 2486 cterm_normal_fg_color = (g_attrCurrent & 0xf) + 1;
2536 if (cterm_normal_bg_color == 0) 2487 if (cterm_normal_bg_color == 0)
2537 cterm_normal_bg_color = ((g_attrCurrent >> 4) & 0xf) + 1; 2488 cterm_normal_bg_color = ((g_attrCurrent >> 4) & 0xf) + 1;
2538 2489
2627 } 2578 }
2628 2579
2629 SetConsoleCursorInfo(g_hConOut, &g_cci); 2580 SetConsoleCursorInfo(g_hConOut, &g_cci);
2630 SetConsoleMode(g_hConIn, g_cmodein); 2581 SetConsoleMode(g_hConIn, g_cmodein);
2631 SetConsoleMode(g_hConOut, g_cmodeout); 2582 SetConsoleMode(g_hConOut, g_cmodeout);
2583
2584 CloseHandle(g_cbTermcap.handle);
2632 2585
2633 #ifdef DYNAMIC_GETTEXT 2586 #ifdef DYNAMIC_GETTEXT
2634 dyn_libintl_end(); 2587 dyn_libintl_end();
2635 #endif 2588 #endif
2636 2589
5000 /* 4953 /*
5001 * This is our first time entering termcap mode. Clear the console 4954 * This is our first time entering termcap mode. Clear the console
5002 * screen buffer, and resize the buffer to match the current window 4955 * screen buffer, and resize the buffer to match the current window
5003 * size. We will use this as the size of our editing environment. 4956 * size. We will use this as the size of our editing environment.
5004 */ 4957 */
4958 g_hConOut = g_cbTermcap.handle;
4959 SetConsoleActiveScreenBuffer(g_hConOut);
5005 ClearConsoleBuffer(g_attrCurrent); 4960 ClearConsoleBuffer(g_attrCurrent);
5006 ResizeConBufAndWindow(g_hConOut, Columns, Rows); 4961 ResizeConBufAndWindow(g_hConOut, Columns, Rows);
5007 } 4962 }
5008 4963
5009 #ifdef FEAT_TITLE 4964 #ifdef FEAT_TITLE
5043 4998
5044 GetConsoleMode(g_hConIn, &cmodein); 4999 GetConsoleMode(g_hConIn, &cmodein);
5045 cmodein &= ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT); 5000 cmodein &= ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
5046 SetConsoleMode(g_hConIn, cmodein); 5001 SetConsoleMode(g_hConIn, cmodein);
5047 5002
5048 #ifdef FEAT_RESTORE_ORIG_SCREEN
5049 cb = exiting ? &g_cbOrig : &g_cbNonTermcap;
5050 #else
5051 cb = &g_cbNonTermcap; 5003 cb = &g_cbNonTermcap;
5052 #endif
5053 RestoreConsoleBuffer(cb, p_rs); 5004 RestoreConsoleBuffer(cb, p_rs);
5054 SetConsoleCursorInfo(g_hConOut, &g_cci); 5005 SetConsoleCursorInfo(g_hConOut, &g_cci);
5055 5006
5056 if (p_rs || exiting) 5007 if (p_rs || exiting)
5057 { 5008 {