Adjust the font size according to the number of columns

Additionally, if it is impossible to fit the entire screen buffer on the
current monitor, detect this condition uising the
GetLargestConsoleWindowSize API and make the console window as large as
possible.  This is suboptimal for full-screen console programs (e.g. Far
Manager), but because the scrolling-mode scraper scrapes the screen buffer
rather than the visble window, the smaller-than-desired window frequently
has no noticeable effect.

Revert the use of MARK to freeze the new Windows 10 console.  Use
SELECT_ALL again.

Fixes https://github.com/rprichard/winpty/issues/61
Fixes https://github.com/rprichard/winpty/issues/79
Breaks https://github.com/rprichard/winpty/issues/53 again
This commit is contained in:
Ryan Prichard 2016-06-06 22:45:38 -07:00
parent b67841911f
commit cda0b02660
5 changed files with 411 additions and 167 deletions

View File

@ -42,6 +42,7 @@
#include "../shared/WindowsVersion.h"
#include "../shared/WinptyAssert.h"
#include "ConsoleFont.h"
#include "ConsoleInput.h"
#include "NamedPipe.h"
#include "Scraper.h"
@ -59,39 +60,48 @@ static BOOL WINAPI consoleCtrlHandler(DWORD dwCtrlType)
return FALSE;
}
// In versions of the Windows console before Windows 10, the SelectAll and
// Mark commands both run quickly, but Mark changes the cursor position read
// by GetConsoleScreenBufferInfo. Therefore, use SelectAll to be less
// intrusive.
// We can detect the new Windows 10 console by observing the effect of the
// Mark command. In older consoles, Mark temporarily moves the cursor to the
// top-left of the console window. In the new console, the cursor isn't
// initially moved.
//
// Starting with the new Windows 10 console, the Mark command no longer moves
// the cursor, and SelectAll uses a lot of CPU time. Therefore, use Mark.
//
// The Windows 10 legacy-mode console behaves the same way as previous console
// versions, so detect which syscommand to use by testing whether Mark changes
// the cursor position.
static void initConsoleFreezeMethod(
// We might like to use Mark to freeze the console, but we can't, because when
// the Mark command ends, the console moves the cursor back to its starting
// point, even if the console application has moved it in the meantime.
static void detectNewWindows10Console(
Win32Console &console, Win32ConsoleBuffer &buffer)
{
const ConsoleScreenBufferInfo info = buffer.bufferInfo();
ConsoleScreenBufferInfo info = buffer.bufferInfo();
// Make sure the buffer and window aren't 1x1. (Is that even possible?)
buffer.resizeBuffer(Coord(
std::max<int>(2, info.dwSize.X),
std::max<int>(2, info.dwSize.Y)));
buffer.moveWindow(SmallRect(0, 0, 2, 2));
const Coord initialPosition(1, 1);
buffer.setCursorPosition(initialPosition);
// Make sure the window isn't 1x1.
if (info.srWindow.Left == info.srWindow.Right &&
info.srWindow.Top == info.srWindow.Bottom) {
// Make sure the buffer isn't 1x1.
if (info.dwSize.X == 1 && info.dwSize.Y == 1) {
setSmallFont(buffer.conout(), 80, false);
buffer.resizeBuffer(Coord(80, 25));
info = buffer.bufferInfo();
}
buffer.moveWindow(SmallRect(0, 0, 2, 1));
info = buffer.bufferInfo();
ASSERT(info.srWindow.Right > info.srWindow.Left &&
"Could not expand console window from 1x1");
}
// Test whether MARK moves the cursor.
const Coord initialPosition(info.srWindow.Right, info.srWindow.Bottom);
buffer.setCursorPosition(initialPosition);
ASSERT(!console.frozen());
console.setFreezeUsesMark(true);
console.setFrozen(true);
const bool useMark = (buffer.cursorPosition() == initialPosition);
const bool isNewW10 = (buffer.cursorPosition() == initialPosition);
console.setFrozen(false);
trace("Using %s syscommand to freeze console",
useMark ? "MARK" : "SELECT_ALL");
console.setFreezeUsesMark(useMark);
buffer.setCursorPosition(Coord(0, 0));
trace("Attempting to detect new Windows 10 console using MARK: %s",
isNewW10 ? "detected" : "not detected");
console.setFreezeUsesMark(false);
console.setNewW10(isNewW10);
}
static inline WriteBuffer newPacket() {
@ -141,7 +151,7 @@ Agent::Agent(LPCWSTR controlPipeName,
m_errorBuffer = Win32ConsoleBuffer::createErrorBuffer();
}
initConsoleFreezeMethod(m_console, *primaryBuffer);
detectNewWindows10Console(m_console, *primaryBuffer);
m_controlPipe = &connectToControlPipe(controlPipeName);
m_coninPipe = &createDataServerPipe(false, L"conin");

View File

@ -27,11 +27,14 @@
#include <algorithm>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
#include "../shared/DebugClient.h"
#include "../shared/OsModule.h"
#include "../shared/StringUtil.h"
#include "../shared/WindowsVersion.h"
#include "../shared/WinptyAssert.h"
#include "../shared/winpty_snprintf.h"
@ -40,121 +43,212 @@ namespace {
#define COUNT_OF(x) (sizeof(x) / sizeof((x)[0]))
// See https://en.wikipedia.org/wiki/List_of_CJK_fonts
const wchar_t kMSGothic[] = { 0xff2d, 0xff33, 0x0020, 0x30b4, 0x30b7, 0x30c3, 0x30af, 0 }; // Japanese
const wchar_t kNSimSun[] = { 0x65b0, 0x5b8b, 0x4f53, 0 }; // Simplified Chinese
const wchar_t kMingLight[] = { 0x7d30, 0x660e, 0x9ad4, 0 }; // Traditional Chinese
const wchar_t kGulimChe[] = { 0xad74, 0xb9bc, 0xccb4, 0 }; // Korean
const wchar_t kLucidaConsole[] = L"Lucida Console";
const wchar_t kMSGothic[] = { 0xff2d, 0xff33, 0x0020, 0x30b4, 0x30b7, 0x30c3, 0x30af, 0 }; // 932, Japanese
const wchar_t kNSimSun[] = { 0x65b0, 0x5b8b, 0x4f53, 0 }; // 936, Chinese Simplified
const wchar_t kGulimChe[] = { 0xad74, 0xb9bc, 0xccb4, 0 }; // 949, Korean
const wchar_t kMingLight[] = { 0x7d30, 0x660e, 0x9ad4, 0 }; // 950, Chinese Traditional
struct Font {
int codePage;
const wchar_t *faceName;
int pxSize;
struct FontSize {
int size;
int width;
};
const Font kFonts[] = {
// MS Gothic double-width handling seems to be broken with console versions
// prior to Windows 10 (including Windows 10's legacy mode), and it's
// especially broken in Windows 8 and 8.1. AFAICT, MS Gothic at size 9
// avoids problems in Windows 7 and minimizes problems in 8/8.1.
//
// Test by running: misc/Utf16Echo A2 A3 2014 3044 30FC 4000
//
// The first three codepoints are always rendered as half-width with the
// Windows Japanese fonts. (Of these, the first two must be half-width,
// but U+2014 could be either.) The last three are rendered as full-width,
// and they are East_Asian_Width=Wide.
//
// Windows 7 fails by modeling all codepoints as full-width with font
// sizes 14 and above.
//
// Windows 8 gets U+00A2, U+00A3, U+2014, U+30FC, and U+4000 wrong, but
// using a point size not listed in the console properties dialog
// (e.g. "9") is less wrong:
//
// | code point |
// font | 00A2 00A3 2014 3044 30FC 4000 | cell size
// ------------+---------------------------------+----------
// 8 | F F F F H H | 4x8
// 9 | F F F F F F | 5x9
// 16 | F F F F H H | 8x16
// raster 6x13 | H H H F F H(*) | 6x13
//
// (*) The Raster Font renders U+4000 as a white box (i.e. an unsupported
// character).
//
{ 932, kMSGothic, 9 },
struct Font {
const wchar_t *face;
int size;
};
// kNSimSun: I verified that `misc/Utf16Echo A2 A3 2014 3044 30FC 4000`
// did something sensible with Windows 8. It *did* with the listed font
// sizes, but not with unlisted sizes. Listed sizes:
// - 6 ==> 3x7px
// - 8 ==> 4x9px
// - 10 ==> 5x11px
// - 12 ==> 6x14px
// - 14 ==> 7x16px
// - 16 ==> 8x18px
// ...
// - 36 ==> 18x41px
// - 72 ==> 36x82px
// U+2014 is modeled and rendered as full-width.
{ 936, kNSimSun, 8 },
// Ideographs in East Asian languages take two columns rather than one.
// In the console screen buffer, a "full-width" character will occupy two
// cells of the buffer, the first with attribute 0x100 and the second with
// attribute 0x200.
//
// Windows does not correctly identify code points as double-width in all
// configurations. It depends heavily on the code page, the font facename,
// and (somehow) even the font size. In the 437 code page (MS-DOS), for
// example, no codepoints are interpreted as double-width. When the console
// is in an East Asian code page (932, 936, 949, or 950), then sometimes
// selecting a "Western" facename like "Lucida Console" or "Consolas" doesn't
// register, or if the font *can* be chosen, then the console doesn't handle
// double-width correctly. I tested the double-width handling by writing
// several code points with WriteConsole and checking whether one or two cells
// were filled.
//
// In the Japanese code page (932), Microsoft's default font is MS Gothic.
// MS Gothic double-width handling seems to be broken with console versions
// prior to Windows 10 (including Windows 10's legacy mode), and it's
// especially broken in Windows 8 and 8.1.
//
// Test by running: misc/Utf16Echo A2 A3 2014 3044 30FC 4000
//
// The first three codepoints are always rendered as half-width with the
// Windows Japanese fonts. (Of these, the first two must be half-width,
// but U+2014 could be either.) The last three are rendered as full-width,
// and they are East_Asian_Width=Wide.
//
// Windows 7 fails by modeling all codepoints as full-width with font
// sizes 22 and above.
//
// Windows 8 gets U+00A2, U+00A3, U+2014, U+30FC, and U+4000 wrong, but
// using a point size not listed in the console properties dialog
// (e.g. "9") is less wrong:
//
// | code point |
// font | 00A2 00A3 2014 3044 30FC 4000 | cell size
// ------------+---------------------------------+----------
// 8 | F F F F H H | 4x8
// 9 | F F F F F F | 5x9
// 16 | F F F F H H | 8x16
// raster 6x13 | H H H F F H(*) | 6x13
//
// (*) The Raster Font renders U+4000 as a white box (i.e. an unsupported
// character).
//
// kMingLight: I verified that `misc/Utf16Echo A2 A3 2014 3044 30FC 4000`
// did something sensible with Windows 8. It *did* with the listed font
// sizes, but not with unlisted sizes. Listed sizes:
// - 6 => 3x7px
// - 8 => 4x10px
// - 10 => 5x12px
// - 12 => 6x14px
// - 14 => 7x17px
// - 16 => 8x19px
// ...
// - 36 => 18x43px
// - 72 => 36x86px
// U+2014 is modeled and rendered as full-width.
{ 950, kMingLight, 8 },
// See:
// - misc/Font-Report-June2016 directory for per-size details
// - misc/font-notes.txt
// - misc/Utf16Echo.cc, misc/FontSurvey.cc, misc/SetFont.cc, misc/GetFont.cc
// kGulimChe: I verified that `misc/Utf16Echo A2 A3 2014 3044 30FC 4000`
// did something sensible with Windows 8. It *did* with the listed font
// sizes, but not with unlisted sizes. Listed sizes:
// - 6 ==> 3x7px
// - 8 ==> 4x9px
// - 10 ==> 5x11px
// - 12 ==> 6x14px
// - 14 ==> 7x16px
// - 16 ==> 8x18px
// ...
// - 36 ==> 18x41px
// - 72 ==> 36x83px
{ 949, kGulimChe, 8 },
const FontSize kLucidaFontSizes[] = {
{ 2, 1 },
{ 4, 2 },
{ 5, 3 },
{ 6, 4 },
{ 8, 5 },
{ 10, 6 },
{ 12, 7 },
{ 14, 8 },
{ 16, 10 },
{ 18, 11 },
{ 20, 12 },
{ 36, 22 },
{ 48, 29 },
{ 60, 36 },
{ 72, 43 },
};
// Listed sizes:
// - 5 ==> 2x5px
// - 6 ==> 3x6px
// - 7 ==> 3x6px
// - 8 ==> 4x8px
// - 10 ==> 5x10px
// - 12 ==> 6x12px
// - 14 ==> 7x14px
// - 16 ==> 8x16px
// ...
// - 36 ==> 17x36px
// - 72 ==> 34x72px
{ 0, L"Consolas", 8 },
// Japanese. Used on Vista and Windows 7.
const FontSize k932GothicVista[] = {
{ 2, 1 },
{ 4, 2 },
{ 6, 3 },
{ 8, 4 },
{ 10, 5 },
{ 12, 6 },
{ 13, 7 },
{ 15, 8 },
{ 17, 9 },
{ 19, 10 },
{ 21, 11 },
// All larger fonts are more broken w.r.t. full-size East Asian characters.
};
// Listed sizes:
// - 5 ==> 3x5px
// - 6 ==> 4x6px
// - 7 ==> 4x7px
// - 8 ==> 5x8px
// - 10 ==> 6x10px
// - 12 ==> 7x12px
// - 14 ==> 8x14px
// - 16 ==> 10x16px
// ...
// - 36 ==> 22x36px
// - 72 ==> 43x72px
{ 0, L"Lucida Console", 6 },
// Japanese. Used on Windows 8, 8.1, and the legacy 10 console.
const FontSize k932GothicWin8[] = {
// All of these characters are broken w.r.t. full-size East Asian
// characters, but they're equally broken.
{ 3, 2 },
{ 5, 3 },
{ 7, 4 },
{ 9, 5 },
{ 11, 6 },
{ 13, 7 },
{ 15, 8 },
{ 17, 9 },
{ 20, 10 },
{ 22, 11 },
{ 24, 12 },
// include extra-large fonts for small terminals
{ 36, 18 },
{ 48, 24 },
{ 60, 30 },
{ 72, 36 },
};
// Japanese. Used on the new Windows 10 console.
const FontSize k932GothicWin10[] = {
{ 2, 1 },
{ 4, 2 },
{ 6, 3 },
{ 8, 4 },
{ 10, 5 },
{ 12, 6 },
{ 14, 7 },
{ 16, 8 },
{ 18, 9 },
{ 20, 10 },
{ 22, 11 },
{ 24, 12 },
// include extra-large fonts for small terminals
{ 36, 18 },
{ 48, 24 },
{ 60, 30 },
{ 72, 36 },
};
// Chinese Simplified.
const FontSize k936SimSun[] = {
{ 2, 1 },
{ 4, 2 },
{ 6, 3 },
{ 8, 4 },
{ 10, 5 },
{ 12, 6 },
{ 14, 7 },
{ 16, 8 },
{ 18, 9 },
{ 20, 10 },
{ 22, 11 },
{ 24, 12 },
// include extra-large fonts for small terminals
{ 36, 18 },
{ 48, 24 },
{ 60, 30 },
{ 72, 36 },
};
// Korean.
const FontSize k949GulimChe[] = {
{ 2, 1 },
{ 4, 2 },
{ 6, 3 },
{ 8, 4 },
{ 10, 5 },
{ 12, 6 },
{ 14, 7 },
{ 16, 8 },
{ 18, 9 },
{ 20, 10 },
{ 22, 11 },
{ 24, 12 },
// include extra-large fonts for small terminals
{ 36, 18 },
{ 48, 24 },
{ 60, 30 },
{ 72, 36 },
};
// Chinese Traditional.
const FontSize k950MingLight[] = {
{ 2, 1 },
{ 4, 2 },
{ 6, 3 },
{ 8, 4 },
{ 10, 5 },
{ 12, 6 },
{ 14, 7 },
{ 16, 8 },
{ 18, 9 },
{ 20, 10 },
{ 22, 11 },
{ 24, 12 },
// include extra-large fonts for small terminals
{ 36, 18 },
{ 48, 24 },
{ 60, 30 },
{ 72, 36 },
};
// Some of these types and functions are missing from the MinGW headers.
@ -422,15 +516,109 @@ static bool setFontVista(
return true;
}
static void setSmallFontVista(VistaFontAPI &api, HANDLE conout) {
int codePage = GetConsoleOutputCP();
for (size_t i = 0; i < COUNT_OF(kFonts); ++i) {
if (kFonts[i].codePage == 0 || kFonts[i].codePage == codePage) {
if (setFontVista(api, conout,
kFonts[i].faceName, kFonts[i].pxSize)) {
trace("setSmallFontVista: success");
return;
static Font selectSmallFont(int codePage, int columns, bool isNewW10) {
// Iterate over a set of font sizes according to the code page, and select
// one.
const wchar_t *face = nullptr;
const FontSize *table = nullptr;
size_t tableSize = 0;
switch (codePage) {
case 932: // Japanese
face = kMSGothic;
if (isNewW10) {
table = k932GothicWin10;
tableSize = COUNT_OF(k932GothicWin10);
} else if (isAtLeastWindows8()) {
table = k932GothicWin8;
tableSize = COUNT_OF(k932GothicWin8);
} else {
table = k932GothicVista;
tableSize = COUNT_OF(k932GothicVista);
}
break;
case 936: // Chinese Simplified
face = kNSimSun;
table = k936SimSun;
tableSize = COUNT_OF(k936SimSun);
break;
case 949: // Korean
face = kGulimChe;
table = k949GulimChe;
tableSize = COUNT_OF(k949GulimChe);
break;
case 950: // Chinese Traditional
face = kMingLight;
table = k950MingLight;
tableSize = COUNT_OF(k950MingLight);
break;
default:
face = kLucidaConsole;
table = kLucidaFontSizes;
tableSize = COUNT_OF(kLucidaFontSizes);
break;
}
size_t bestIndex = static_cast<size_t>(-1);
std::tuple<int, int> bestScore = std::make_tuple(-1, -1);
// We might want to pick the smallest possible font, because we don't know
// how large the monitor is (and the monitor size can change). We might
// want to pick a larger font to accommodate console programs that resize
// the console on their own, like DOS edit.com, which tends to resize the
// console to 80 columns.
for (size_t i = 0; i < tableSize; ++i) {
const int width = table[i].width * columns;
// In general, we'd like to pick a font size where cutting the number
// of columns in half doesn't immediately violate the minimum width
// constraint. (e.g. To run DOS edit.com, a user might resize their
// terminal to ~100 columns so it's big enough to show the 80 columns
// post-resize.) To achieve this, give priority to fonts that allow
// this halving. We don't want to encourage *very* large fonts,
// though, so disable the effect as the number of columns scales from
// 80 to 40.
const int halfColumns = std::min(columns, std::max(40, columns / 2));
const int halfWidth = table[i].width * halfColumns;
std::tuple<int, int> thisScore = std::make_tuple(-1, -1);
if (width >= 160 && halfWidth >= 160) {
// Both sizes are good. Prefer the smaller fonts.
thisScore = std::make_tuple(2, -width);
} else if (width >= 160) {
// Prefer the smaller fonts.
thisScore = std::make_tuple(1, -width);
} else {
// Otherwise, prefer the largest font in our table.
thisScore = std::make_tuple(0, width);
}
if (thisScore > bestScore) {
bestIndex = i;
bestScore = thisScore;
}
}
ASSERT(bestIndex != static_cast<size_t>(-1));
return Font { face, table[bestIndex].size };
}
static void setSmallFontVista(VistaFontAPI &api, HANDLE conout,
int columns, bool isNewW10) {
int codePage = GetConsoleOutputCP();
const auto font = selectSmallFont(codePage, columns, isNewW10);
if (setFontVista(api, conout, font.face, font.size)) {
trace("setSmallFontVista: success");
return;
}
if (codePage == 932 || codePage == 936 ||
codePage == 949 || codePage == 950) {
trace("setSmallFontVista: falling back to default codepage font instead");
const auto fontFB = selectSmallFont(0, columns, isNewW10);
if (setFontVista(api, conout, fontFB.face, fontFB.size)) {
trace("setSmallFontVista: fallback was successful");
return;
}
}
trace("setSmallFontVista: failure");
@ -486,15 +674,17 @@ static void setSmallFontXP(UndocumentedXPFontAPI &api, HANDLE conout) {
// maximize the possible size of the console in rows*cols, try to configure
// the console with a small font. Unfortunately, we cannot make the font *too*
// small, because there is also a minimum window size in pixels.
void setSmallFont(HANDLE conout) {
trace("setSmallFont: attempting to set a small font (CP=%u OutputCP=%u)",
void setSmallFont(HANDLE conout, int columns, bool isNewW10) {
trace("setSmallFont: attempting to set a small font for %d columns "
"(CP=%u OutputCP=%u)",
columns,
static_cast<unsigned>(GetConsoleCP()),
static_cast<unsigned>(GetConsoleOutputCP()));
VistaFontAPI vista;
if (vista.valid()) {
dumpVistaFont(vista, conout, "previous font: ");
dumpFontTable(conout, "previous font table: ");
setSmallFontVista(vista, conout);
setSmallFontVista(vista, conout, columns, isNewW10);
dumpVistaFont(vista, conout, "new font: ");
dumpFontTable(conout, "new font table: ");
return;

View File

@ -23,6 +23,6 @@
#include <windows.h>
void setSmallFont(HANDLE conout);
void setSmallFont(HANDLE conout, int columns, bool isNewW10);
#endif // CONSOLEFONT_H

View File

@ -59,10 +59,26 @@ Scraper::Scraper(
m_bufferData.resize(BUFFER_LINE_COUNT);
setSmallFont(buffer.conout());
// Setup the initial screen buffer and window size.
//
// Use SetConsoleWindowInfo to shrink the console window as much as
// possible -- to a 1x1 cell at the top-left. This call always succeeds.
// Prior to the new Windows 10 console, it also actually resizes the GUI
// window to 1x1 cell. Nevertheless, even though the GUI window can
// therefore be narrower than its minimum, calling
// SetConsoleScreenBufferSize with a 1x1 size still fails.
//
// While the small font intends to support large buffers, a user could
// still hit a limit imposed by their monitor width, so cap the new window
// size to GetLargestConsoleWindowSize().
setSmallFont(buffer.conout(), initialSize.X, m_console.isNewW10());
buffer.moveWindow(SmallRect(0, 0, 1, 1));
buffer.resizeBuffer(Coord(initialSize.X, BUFFER_LINE_COUNT));
buffer.moveWindow(SmallRect(0, 0, initialSize.X, initialSize.Y));
const auto largest = GetLargestConsoleWindowSize(buffer.conout());
buffer.moveWindow(SmallRect(
0, 0,
std::min(initialSize.X, largest.X),
std::min(initialSize.Y, largest.Y)));
buffer.setCursorPosition(Coord(0, 0));
// For the sake of the color translation heuristic, set the console color
@ -159,11 +175,18 @@ void Scraper::clearBufferLines(
}
}
static bool cursorInWindow(const ConsoleScreenBufferInfo &info)
{
return info.dwCursorPosition.Y >= info.srWindow.Top &&
info.dwCursorPosition.Y <= info.srWindow.Bottom;
}
void Scraper::resizeImpl(const ConsoleScreenBufferInfo &origInfo)
{
ASSERT(m_console.frozen());
const int cols = m_ptySize.X;
const int rows = m_ptySize.Y;
Coord finalBufferSize;
{
//
@ -188,7 +211,7 @@ void Scraper::resizeImpl(const ConsoleScreenBufferInfo &origInfo)
}
}
const Coord finalBufferSize(
finalBufferSize = Coord(
cols,
// If there was previously no scrollback (e.g. a full-screen app
// in direct mode) and we're reducing the window height, then
@ -196,44 +219,62 @@ void Scraper::resizeImpl(const ConsoleScreenBufferInfo &origInfo)
(origWindowRect.height() == origBufferSize.Y)
? rows
: std::max<int>(rows, origBufferSize.Y));
const bool cursorWasInWindow =
origInfo.cursorPosition().Y >= origWindowRect.Top &&
origInfo.cursorPosition().Y <= origWindowRect.Bottom;
// Step 1: move the window.
const int tmpWindowWidth = std::min(origBufferSize.X, finalBufferSize.X);
const int tmpWindowHeight = std::min<int>(origBufferSize.Y, rows);
// Reset the console font size. We need to do this before shrinking
// the window, because we might need to make the font bigger to permit
// a smaller window width. Making the font smaller could expand the
// screen buffer, which would hang the conhost process in the
// Windows 10 (10240 build) if the console selection is in progress, so
// unfreeze it first.
m_console.setFrozen(false);
setSmallFont(m_consoleBuffer->conout(), cols, m_console.isNewW10());
}
// We try to make the font small enough so that the entire screen buffer
// fits on the monitor, but it can't be guaranteed.
const auto largest =
GetLargestConsoleWindowSize(m_consoleBuffer->conout());
const short visibleCols = std::min<short>(cols, largest.X);
const short visibleRows = std::min<short>(rows, largest.Y);
{
// Make the window small enough. We want the console frozen during
// this step so we don't accidentally move the window above the cursor.
m_console.setFrozen(true);
const auto info = m_consoleBuffer->bufferInfo();
const auto &bufferSize = info.dwSize;
const int tmpWindowWidth = std::min(bufferSize.X, visibleCols);
const int tmpWindowHeight = std::min(bufferSize.Y, visibleRows);
SmallRect tmpWindowRect(
0,
std::min<int>(origBufferSize.Y - tmpWindowHeight,
origWindowRect.Top),
std::min<int>(bufferSize.Y - tmpWindowHeight,
info.windowRect().Top),
tmpWindowWidth,
tmpWindowHeight);
if (cursorWasInWindow) {
if (cursorInWindow(info)) {
tmpWindowRect = tmpWindowRect.ensureLineIncluded(
origInfo.cursorPosition().Y);
info.cursorPosition().Y);
}
m_consoleBuffer->moveWindow(tmpWindowRect);
}
// Step 2: resize the buffer.
{
// Resize the buffer to the final desired size.
m_console.setFrozen(false);
m_consoleBuffer->resizeBuffer(finalBufferSize);
}
// Step 3: expand the window to its full size.
{
// Expand the window to its full size.
m_console.setFrozen(true);
const ConsoleScreenBufferInfo info = m_consoleBuffer->bufferInfo();
const bool cursorWasInWindow =
info.cursorPosition().Y >= info.windowRect().Top &&
info.cursorPosition().Y <= info.windowRect().Bottom;
SmallRect finalWindowRect(
0,
std::min<int>(info.bufferSize().Y - rows,
std::min<int>(info.bufferSize().Y - visibleRows,
info.windowRect().Top),
cols,
rows);
visibleCols,
visibleRows);
//
// Once a line in the screen buffer is "dirty", it should stay visible
@ -248,12 +289,12 @@ void Scraper::resizeImpl(const ConsoleScreenBufferInfo &origInfo)
// unfrozen, so that the *top* of the window is now below the
// dirtiest tracked line.
finalWindowRect = SmallRect(
0, m_dirtyLineCount - rows,
cols, rows);
0, m_dirtyLineCount - visibleRows,
visibleCols, visibleRows);
}
// Highest priority constraint: ensure that the cursor remains visible.
if (cursorWasInWindow) {
if (cursorInWindow(info)) {
finalWindowRect = finalWindowRect.ensureLineIncluded(
info.cursorPosition().Y);
}

View File

@ -51,6 +51,8 @@ public:
std::wstring title();
void setTitle(const std::wstring &title);
void setFreezeUsesMark(bool useMark) { m_freezeUsesMark = useMark; }
void setNewW10(bool isNewW10) { m_isNewW10 = isNewW10; }
bool isNewW10() { return m_isNewW10; }
void setFrozen(bool frozen=true);
bool frozen() { return m_frozen; }
@ -58,6 +60,7 @@ private:
HWND m_hwnd = nullptr;
bool m_frozen = false;
bool m_freezeUsesMark = false;
bool m_isNewW10 = false;
std::vector<wchar_t> m_titleWorkBuf;
};