Mercurial > vim
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 { |