By rewriting `COOKED_READ_DATA` to use VT for its output we make it
possible to pass this VT output 1:1 straight to the hosting terminal
if we're running under ConPTY. This is also possible with the current
console APIs it uses, but it's somewhat janky. In particular the
usage of `ReadConsoleOutput` to backup/restore the popup contents
could be considered bad faith "rules for thee, not for me",
given that we're telling people to move away from those APIs.

The new implementation contains a bare bones "pager" to fit even
very long prompt contents into the VT viewport.
I fully expect this initial PR to not be entirely bug free, because
writing a proper pager with line wrapping is a little bit complex.
This PR takes some significant shortcuts by leveraging the fact
that the prompt line is always left-to-right and always a series
of fully filled lines followed by one potentially semi-full line.
This allows us to skip using a front/back-buffer for diffing the
contents between two redisplay calls.

Part of #14000

## Validation Steps Performed
* ASCII input
* Chinese input (中文維基百科) 
* Surrogate pair input (🙂) 
* In cmd.exe
  * Create 2 files: "a😊b.txt" and "a😟b.txt"
  * Press tab: Autocomplete to "a😊b.txt" 
  * Navigate the cursor right past the "a"
  * Press tab twice: Autocomplete to "a😟b.txt" 
* Execute `printf("    "); gets(buffer);` in C (or equivalent)
  * Press Tab, A, Ctrl+V, Tab, A 
  * The prompt is "        A^V     A" 
  * Cursor navigation works 
  * Backspacing/Deleting random parts of it works 
  * It never deletes the initial 4 spaces 
* Backspace deletes preceding glyphs 
* Ctrl+Backspace deletes preceding words 
* Escape clears input 
* Home navigates to start 
* Ctrl+Home deletes text between cursor and start 
* End navigates to end 
* Ctrl+End deletes text between cursor and end 
* Left navigates over previous code points 
* Ctrl+Left navigates to previous word-starts 
* Right and F1 navigate over next code points 
  * Pressing right at the end of input copies characters
    from the previous command 
* Ctrl+Right navigates to next word-ends 
* Insert toggles overwrite mode 
* Delete deletes next code point 
* Up and F5 cycle through history 
  * Doesn't crash with no history 
  * Stops at first entry 
* Down cycles through history 
  * Doesn't crash with no history 
  * Stops at last entry 
* PageUp retrieves the oldest command 
* PageDown retrieves the newest command 
* F2 starts "copy to char" prompt 
  * Escape dismisses prompt 
  * Typing a character copies text from the previous command up
    until that character into the current buffer (acts identical
    to F3, but with automatic character search) 
* F3 copies the previous command into the current buffer,
  starting at the current cursor position,
  for as many characters as possible 
  * Doesn't erase trailing text if the current buffer
    is longer than the previous command 
  * Puts the cursor at the end of the copied text 
* F4 starts "copy from char" prompt 
  * Escape dismisses prompt 
  * Erases text between the current cursor position and the
    first instance of a given char (but not including it) 
* F6 inserts Ctrl+Z 
* F7 without modifiers starts "command list" prompt 
  * Escape dismisses prompt 
  * Entries wider than the window width are truncated 
  * Height expands up to 20 rows with longer histories 
  * F9 starts "command number" prompt 
  * Left/Right replace the buffer with the given command 
    * And put cursor at the end of the buffer 
  * Up/Down navigate selection through history 
    * Stops at start/end with <10 entries 
    * Stops at start/end with >20 entries 
    * Scrolls through the entries if there are too many 
  * Shift+Up/Down moves history items around 
  * Home navigates to first entry 
  * End navigates to last entry 
  * PageUp navigates by $height items at a time or to first 
  * PageDown navigates by $height items at a time or to last 
* Alt+F7 clears command history 
* F8 cycles through commands that start with the same text as
  the current buffer up until the current cursor position 
  * Doesn't crash with no history 
* F9 starts "command number" prompt 
  * Escape dismisses prompt 
  * Ignores non-ASCII-decimal characters 
  * Allows entering between 1 and 5 digits 
  * Pressing Enter fetches the given command from the history 
* Alt+F10 clears doskey aliases 
* In cmd.exe, with an empty prompt in an empty directory:
  Pressing tab produces an audible bing and prints no text 
* When Narrator is enabled, in cmd.exe:
  * Typing individual characters announces only
    exactly each character that is being typed 
  * Backspacing at the end of a prompt announces
    only exactly each deleted character 
This commit is contained in:
Leonard Hecker 2024-07-11 22:11:44 +02:00 коммит произвёл GitHub
Родитель 30447cf7e4
Коммит ac5b4f5831
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
15 изменённых файлов: 882 добавлений и 774 удалений

Просмотреть файл

@ -4,12 +4,8 @@
All of the following ✅ marks must be fulfilled during manual testing:
* ASCII input
* Chinese input (中文維基百科) ❔
* Resizing the window properly wraps/unwraps wide glyphs ❌
Broken due to `TextBuffer::Reflow` bugs
* Surrogate pair input (🙂) ❔
* Resizing the window properly wraps/unwraps surrogate pairs ❌
Broken due to `TextBuffer::Reflow` bugs
* Chinese input (中文維基百科) ✅
* Surrogate pair input (🙂) ✅
* In cmd.exe
* Create 2 file: "a😊b.txt" and "a😟b.txt"
* Press tab: Autocomplete to "a😊b.txt" ✅
@ -62,21 +58,20 @@ All of the following ✅ marks must be fulfilled during manual testing:
* F6 inserts Ctrl+Z ✅
* F7 without modifiers starts "command list" prompt ✅
* Escape dismisses prompt ✅
* Minimum size of 40x10 characters ✅
* Width expands to fit the widest history command ✅
* Entries wider than the window width are truncated ✅
* Height expands up to 20 rows with longer histories ✅
* F9 starts "command number" prompt ✅
* Left/Right paste replace the buffer with the given command ✅
* Left/Right replace the buffer with the given command ✅
* And put cursor at the end of the buffer ✅
* Up/Down navigate selection through history ✅
* Stops at start/end with <10 entries
* Stops at start/end with >20 entries ✅
* Wide text rendering during pagination with >20 entries
* Scrolls through the entries if there are too many
* Shift+Up/Down moves history items around ✅
* Home navigates to first entry ✅
* End navigates to last entry ✅
* PageUp navigates by 20 items at a time or to first ✅
* PageDown navigates by 20 items at a time or to last ✅
* PageUp navigates by $height items at a time or to first ✅
* PageDown navigates by $height items at a time or to last ✅
* Alt+F7 clears command history ✅
* F8 cycles through commands that start with the same text as
the current buffer up until the current cursor position ✅

Просмотреть файл

@ -20,7 +20,6 @@ Cursor::Cursor(const ULONG ulSize, TextBuffer& parentBuffer) noexcept :
_fBlinkingAllowed(true),
_fDelay(false),
_fIsConversionArea(false),
_fIsPopupShown(false),
_fDelayedEolWrap(false),
_fDeferCursorRedraw(false),
_fHaveDeferredCursorRedraw(false),
@ -66,11 +65,6 @@ bool Cursor::IsConversionArea() const noexcept
return _fIsConversionArea;
}
bool Cursor::IsPopupShown() const noexcept
{
return _fIsPopupShown;
}
bool Cursor::GetDelay() const noexcept
{
return _fDelay;
@ -126,13 +120,6 @@ void Cursor::SetIsConversionArea(const bool fIsConversionArea) noexcept
_RedrawCursorAlways();
}
void Cursor::SetIsPopupShown(const bool fIsPopupShown) noexcept
{
// Functionally the same as "Hide cursor"
_fIsPopupShown = fIsPopupShown;
_RedrawCursorAlways();
}
void Cursor::SetDelay(const bool fDelay) noexcept
{
_fDelay = fDelay;

Просмотреть файл

@ -44,7 +44,6 @@ public:
bool IsBlinkingAllowed() const noexcept;
bool IsDouble() const noexcept;
bool IsConversionArea() const noexcept;
bool IsPopupShown() const noexcept;
bool GetDelay() const noexcept;
ULONG GetSize() const noexcept;
til::point GetPosition() const noexcept;
@ -61,7 +60,6 @@ public:
void SetBlinkingAllowed(const bool fIsOn) noexcept;
void SetIsDouble(const bool fIsDouble) noexcept;
void SetIsConversionArea(const bool fIsConversionArea) noexcept;
void SetIsPopupShown(const bool fIsPopupShown) noexcept;
void SetDelay(const bool fDelay) noexcept;
void SetSize(const ULONG ulSize) noexcept;
void SetStyle(const ULONG ulSize, const CursorType type) noexcept;
@ -99,7 +97,6 @@ private:
bool _fBlinkingAllowed; //Whether or not the cursor is allowed to blink at all. only set through VT (^[[?12h/l)
bool _fDelay; // don't blink scursor on next timer message
bool _fIsConversionArea; // is attached to a conversion area so it doesn't actually need to display the cursor.
bool _fIsPopupShown; // if a popup is being shown, turn off, stop blinking.
bool _fDelayedEolWrap; // don't wrap at EOL till the next char comes in.
til::point _coordDelayedAt; // coordinate the EOL wrap was delayed at.

Просмотреть файл

@ -48,7 +48,7 @@ til::point Terminal::GetCursorPosition() const noexcept
bool Terminal::IsCursorVisible() const noexcept
{
const auto& cursor = _activeBuffer().GetCursor();
return cursor.IsVisible() && !cursor.IsPopupShown();
return cursor.IsVisible();
}
bool Terminal::IsCursorOn() const noexcept

Просмотреть файл

@ -518,3 +518,66 @@ bool VtIo::IsResizeQuirkEnabled() const
}
return S_OK;
}
// Formats the given console attributes to their closest VT equivalent.
// `out` must refer to at least `formatAttributesMaxLen` characters of valid memory.
// Returns a pointer past the end.
static constexpr size_t formatAttributesMaxLen = 16;
static char* formatAttributes(char* out, const TextAttribute& attributes) noexcept
{
static uint8_t sgr[] = { 30, 31, 32, 33, 34, 35, 36, 37, 90, 91, 92, 93, 94, 95, 96, 97 };
// Applications expect that SetConsoleTextAttribute() completely replaces whatever attributes are currently set,
// including any potential VT-exclusive attributes. Since we don't know what those are, we must always emit a SGR 0.
// Copying 4 bytes instead of the correct 3 means we need just 1 DWORD mov. Neat.
//
// 3 bytes.
memcpy(out, "\x1b[0", 4);
out += 3;
// 2 bytes.
if (attributes.IsReverseVideo())
{
memcpy(out, ";7", 2);
out += 2;
}
// 3 bytes (";97").
if (attributes.GetForeground().IsLegacy())
{
const uint8_t index = sgr[attributes.GetForeground().GetIndex()];
out = fmt::format_to(out, FMT_COMPILE(";{}"), index);
}
// 4 bytes (";107").
if (attributes.GetBackground().IsLegacy())
{
const uint8_t index = sgr[attributes.GetBackground().GetIndex()] + 10;
out = fmt::format_to(out, FMT_COMPILE(";{}"), index);
}
// 1 byte.
*out++ = 'm';
return out;
}
void VtIo::FormatAttributes(std::string& target, const TextAttribute& attributes)
{
char buf[formatAttributesMaxLen];
const size_t len = formatAttributes(&buf[0], attributes) - &buf[0];
target.append(buf, len);
}
void VtIo::FormatAttributes(std::wstring& target, const TextAttribute& attributes)
{
char buf[formatAttributesMaxLen];
const size_t len = formatAttributes(&buf[0], attributes) - &buf[0];
wchar_t bufW[formatAttributesMaxLen];
for (size_t i = 0; i < len; i++)
{
bufW[i] = buf[i];
}
target.append(bufW, len);
}

Просмотреть файл

@ -20,6 +20,9 @@ namespace Microsoft::Console::VirtualTerminal
class VtIo
{
public:
static void FormatAttributes(std::string& target, const TextAttribute& attributes);
static void FormatAttributes(std::wstring& target, const TextAttribute& attributes);
VtIo();
[[nodiscard]] HRESULT Initialize(const ConsoleArguments* const pArgs);

Просмотреть файл

@ -255,6 +255,13 @@ void WriteCharsLegacy(SCREEN_INFORMATION& screenInfo, const std::wstring_view& t
}
}
// This is the main entrypoint for conhost to write VT to the buffer.
// This wrapper around StateMachine exists so that we can add the necessary ConPTY transformations.
void WriteCharsVT(SCREEN_INFORMATION& screenInfo, const std::wstring_view& str)
{
screenInfo.GetStateMachine().ProcessString(str);
}
// Routine Description:
// - Takes the given text and inserts it into the given screen buffer.
// Note:

Просмотреть файл

@ -20,6 +20,7 @@ Revision History:
#include "writeData.hpp"
void WriteCharsLegacy(SCREEN_INFORMATION& screenInfo, const std::wstring_view& str, til::CoordType* psScrollY);
void WriteCharsVT(SCREEN_INFORMATION& screenInfo, const std::wstring_view& str);
// NOTE: console lock must be held when calling this routine
// String has been translated to unicode at this point.

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -39,7 +39,7 @@ public:
til::point_span GetBoundaries() const noexcept;
private:
static constexpr uint8_t CommandNumberMaxInputLength = 5;
static constexpr size_t CommandNumberMaxInputLength = 5;
static constexpr size_t npos = static_cast<size_t>(-1);
enum class State : uint8_t
@ -49,40 +49,9 @@ private:
DoneWithCarriageReturn,
};
// A helper struct to ensure we keep track of _dirtyBeg while the
// underlying _buffer is being modified by COOKED_READ_DATA.
struct BufferState
{
const std::wstring& Get() const noexcept;
std::wstring Extract() noexcept
{
return std::move(_buffer);
}
void Replace(size_t offset, size_t remove, const wchar_t* input, size_t count);
void Replace(const std::wstring_view& str);
size_t GetCursorPosition() const noexcept;
void SetCursorPosition(size_t pos) noexcept;
bool IsClean() const noexcept;
void MarkEverythingDirty() noexcept;
void MarkAsClean() noexcept;
std::wstring_view GetUnmodifiedTextBeforeCursor() const noexcept;
std::wstring_view GetUnmodifiedTextAfterCursor() const noexcept;
std::wstring_view GetModifiedTextBeforeCursor() const noexcept;
std::wstring_view GetModifiedTextAfterCursor() const noexcept;
private:
std::wstring_view _slice(size_t from, size_t to) const noexcept;
std::wstring _buffer;
size_t _dirtyBeg = npos;
size_t _cursor = 0;
};
enum class PopupKind
{
// The F2 popup:
// Copies text from the previous command between the current cursor position and the first instance
// of a given char (but not including it) into the current prompt line at the current cursor position.
// Basically, F3 and this prompt have identical behavior, but the prompt searches for a terminating character.
@ -95,11 +64,14 @@ private:
// Then this command, given the char "o" will turn it into
// echo hell efgh
CopyToChar,
// The F4 popup:
// Erases text between the current cursor position and the first instance of a given char (but not including it).
// It's unknown to me why this is was historically called "copy from char" as it conhost never copied anything.
CopyFromChar,
// The F9 popup:
// Let's you choose to replace the current prompt with one from the command history by index.
CommandNumber,
// The F7 popup:
// Let's you choose to replace the current prompt with one from the command history via a
// visual select dialog. Among all the popups this one is the most widely used one by far.
CommandList,
@ -109,15 +81,6 @@ private:
{
PopupKind kind;
// The inner rectangle of the popup, excluding the border that we draw.
// In absolute TextBuffer coordinates.
til::rect contentRect;
// The area we've backed up and need to restore when we dismiss the popup.
// It'll practically always be 1 larger than contentRect in all 4 directions.
Microsoft::Console::Types::Viewport backupRect;
// The backed up buffer contents. Uses CHAR_INFO for convenience.
std::vector<CHAR_INFO> backup;
// Using a std::variant would be preferable in modern C++ but is practically equally annoying to use.
union
{
@ -126,23 +89,37 @@ private:
{
// Keep 1 char space for the trailing \0 char.
std::array<wchar_t, CommandNumberMaxInputLength + 1> buffer;
uint8_t bufferSize;
size_t bufferSize;
} commandNumber;
// Used by PopupKind::CommandList
struct
{
// The previous height of the popup.
til::CoordType height;
// Command history index of the first row we draw in the popup.
// A value of -1 means it hasn't been initialized yet.
CommandHistory::Index top;
// Command history index of the currently selected row.
CommandHistory::Index selected;
// Tracks the part of the popup that has previously been drawn and needs to be redrawn in the next paint.
// This becomes relevant when the length of the history changes while the popup is open (= when deleting entries).
til::CoordType dirtyHeight;
} commandList;
};
};
struct LayoutResult
{
size_t offset;
til::CoordType column = 0;
};
struct Line
{
std::wstring text;
size_t dirtyBegOffset = 0;
til::CoordType dirtyBegColumn = 0;
til::CoordType columns = 0;
};
static size_t _wordPrev(const std::wstring_view& chars, size_t position);
static size_t _wordNext(const std::wstring_view& chars, size_t position);
@ -151,17 +128,15 @@ private:
void _handleVkey(uint16_t vkey, DWORD modifiers);
void _handlePostCharInputLoop(bool isUnicode, size_t& numBytes, ULONG& controlKeyState);
void _transitionState(State state) noexcept;
void _flushBuffer();
void _erase(ptrdiff_t distance) const;
ptrdiff_t _measureChars(const std::wstring_view& text, ptrdiff_t cursorOffset) const;
ptrdiff_t _writeChars(const std::wstring_view& text) const;
ptrdiff_t _writeCharsImpl(const std::wstring_view& text, bool measureOnly, ptrdiff_t cursorOffset) const;
ptrdiff_t _measureCharsUnprocessed(const std::wstring_view& text, ptrdiff_t cursorOffset) const;
ptrdiff_t _writeCharsUnprocessed(const std::wstring_view& text) const;
til::point _offsetPosition(til::point pos, ptrdiff_t distance) const;
void _offsetCursorPosition(ptrdiff_t distance) const;
void _offsetCursorPositionAlways(ptrdiff_t distance) const;
til::CoordType _getColumnAtRelativeCursorPosition(ptrdiff_t distance) const;
til::point _getViewportCursorPosition() const noexcept;
void _replace(size_t offset, size_t remove, const wchar_t* input, size_t count);
void _replace(const std::wstring_view& str);
std::wstring_view _slice(size_t from, size_t to) const noexcept;
void _setCursorPosition(size_t position) noexcept;
void _redisplay();
LayoutResult _layoutLine(std::wstring& output, const std::wstring_view& input, size_t inputOffset, til::CoordType columnBegin, til::CoordType columnLimit) const;
static void _appendCUP(std::wstring& output, til::point pos);
void _appendPopupAttr(std::wstring& output) const;
void _popupPush(PopupKind kind);
void _popupsDone();
@ -170,8 +145,8 @@ private:
void _popupHandleCommandNumberInput(Popup& popup, wchar_t wch, uint16_t vkey, DWORD modifiers);
void _popupHandleCommandListInput(Popup& popup, wchar_t wch, uint16_t vkey, DWORD modifiers);
void _popupHandleInput(wchar_t wch, uint16_t vkey, DWORD keyState);
void _popupDrawPrompt(const Popup& popup, UINT id) const;
void _popupDrawCommandList(Popup& popup) const;
void _popupDrawPrompt(std::vector<Line>& lines, const til::CoordType width, UINT id, const std::wstring_view& prefix, const std::wstring_view& suffix) const;
void _popupDrawCommandList(std::vector<Line>& lines, til::size size, Popup& popup) const;
SCREEN_INFORMATION& _screenInfo;
std::span<char> _userBuffer;
@ -182,15 +157,23 @@ private:
ULONG _controlKeyState = 0;
std::unique_ptr<ConsoleHandleData> _tempHandle;
BufferState _buffer;
// _distanceCursor is the distance between the start of the prompt and the
// current cursor location in columns (including wide glyph padding columns).
ptrdiff_t _distanceCursor = 0;
// _distanceEnd is the distance between the start of the prompt and its last
// glyph at the end in columns (including wide glyph padding columns).
ptrdiff_t _distanceEnd = 0;
bool _insertMode = false;
std::wstring _buffer;
size_t _bufferDirtyBeg = npos;
size_t _bufferCursor = 0;
State _state = State::Accumulating;
bool _insertMode = false;
bool _dirty = false;
bool _redrawPending = false;
til::point _originInViewport;
// This value is in the pager coordinate space. (0,0) is the first character of the
// first line, independent on where the prompt actually appears on the screen.
til::point _pagerPromptEnd;
// The scroll position of the pager.
til::CoordType _pagerContentTop = 0;
// Contains the viewport height for which it previously was drawn for.
til::CoordType _pagerHeight = 0;
std::vector<Popup> _popups;
bool _popupOpened = false;
};

Просмотреть файл

@ -134,7 +134,7 @@ bool RenderData::IsCursorVisible() const noexcept
{
const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
const auto& cursor = gci.GetActiveOutputBuffer().GetTextBuffer().GetCursor();
return cursor.IsVisible() && !cursor.IsPopupShown();
return cursor.IsVisible();
}
// Method Description:

Просмотреть файл

@ -1041,6 +1041,11 @@ void SCREEN_INFORMATION::_InternalSetViewportSize(const til::size* const pcoordS
const auto DeltaY = pcoordSize->height - _viewport.Height();
const auto coordScreenBufferSize = GetBufferSize().Dimensions();
if (DeltaX == 0 && DeltaY == 0)
{
return;
}
// do adjustments on a copy that's easily manipulated.
auto srNewViewport = _viewport.ToInclusive();
@ -1124,15 +1129,11 @@ void SCREEN_INFORMATION::_InternalSetViewportSize(const til::size* const pcoordS
// If the new bottom is supposed to be before the final line of the buffer
// Check to ensure that we don't hide the prompt by collapsing the window.
// The final valid end position will be the coordinates of
// the last character displayed (including any characters
// in the input line)
til::point coordValidEnd;
Selection::Instance().GetValidAreaBoundaries(nullptr, &coordValidEnd);
const auto coordValidEnd = _textBuffer->GetCursor().GetPosition();
// If the bottom of the window when adjusted would be
// above the final line of valid text...
if (srNewViewport.bottom + DeltaY < coordValidEnd.y)
if (sBottomProposed < coordValidEnd.y)
{
// Adjust the top of the window instead of the bottom
// (so the lines slide upward)
@ -1208,11 +1209,16 @@ void SCREEN_INFORMATION::_InternalSetViewportSize(const til::size* const pcoordS
// till the start of the next frame. If any other text gets output before
// that frame starts, there's a very real chance that it'll cause errors as
// the engine tries to invalidate those regions.
const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
if (gci.IsInVtIoMode() && ServiceLocator::LocateGlobals().pRender)
{
ServiceLocator::LocateGlobals().pRender->TriggerScroll();
}
if (gci.HasPendingCookedRead())
{
gci.CookedReadData().RedrawAfterResize();
MakeCurrentCursorVisible();
}
}
// Routine Description:
@ -1465,13 +1471,6 @@ NT_CATCH_RETURN()
{
gci.CookedReadData().EraseBeforeResize();
}
const auto cookedReadRestore = wil::scope_exit([]() {
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
if (gci.HasPendingCookedRead())
{
gci.CookedReadData().RedrawAfterResize();
}
});
const auto fWrapText = gci.GetWrapText();
// GH#3493: Don't reflow the alt buffer.
@ -2454,20 +2453,24 @@ void SCREEN_INFORMATION::UpdateBottom()
_virtualBottom = _viewport.BottomInclusive();
}
// Method Description:
// - Returns the "virtual" Viewport - the viewport with its bottom at
// `_virtualBottom`. For VT operations, this is essentially the mutable
// section of the buffer.
// Arguments:
// - <none>
// Return Value:
// - the virtual terminal viewport
// Returns the section of the text buffer that would be visible on the screen
// if the user didn't scroll away vertically. It's essentially the same as
// GetVtPageArea() but includes the horizontal scroll offset and window width.
Viewport SCREEN_INFORMATION::GetVirtualViewport() const noexcept
{
const auto newTop = _virtualBottom - _viewport.Height() + 1;
return Viewport::FromDimensions({ _viewport.Left(), newTop }, _viewport.Dimensions());
}
// Returns the section of the text buffer that's addressable by VT sequences.
Viewport SCREEN_INFORMATION::GetVtPageArea() const noexcept
{
const auto viewportHeight = _viewport.Height();
const auto bufferWidth = _textBuffer->GetSize().Width();
const auto top = std::max(0, _virtualBottom - viewportHeight + 1);
return Viewport::FromExclusive({ 0, top, bufferWidth, top + viewportHeight });
}
// Method Description:
// - Returns true if the character at the cursor's current position is wide.
// Arguments:

Просмотреть файл

@ -115,6 +115,7 @@ public:
const Microsoft::Console::Types::Viewport& GetViewport() const noexcept;
void SetViewport(const Microsoft::Console::Types::Viewport& newViewport, const bool updateBottom);
Microsoft::Console::Types::Viewport GetVirtualViewport() const noexcept;
Microsoft::Console::Types::Viewport GetVtPageArea() const noexcept;
void ProcessResizeWindow(const til::rect* const prcClientNew, const til::rect* const prcClientOld);
void SetViewportSize(const til::size* const pcoordSize);

Просмотреть файл

@ -59,9 +59,8 @@ WORD ConvertStringToDec(_In_ PCWSTR pwchToConvert, _Out_opt_ PCWSTR* const ppwch
// - Retrieves string resources from our resource files.
// Arguments:
// - id - Resource id from resource.h to the string we need to load.
// Return Value:
// - The string resource
std::wstring _LoadString(const UINT id)
// - out - Receives the translated string.
void _LoadString(const UINT id, std::wstring& out)
{
const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
WCHAR ItemString[70];
@ -78,7 +77,7 @@ std::wstring _LoadString(const UINT id)
ItemLength = LoadStringW(ServiceLocator::LocateGlobals().hInstance, id, ItemString, ARRAYSIZE(ItemString));
}
return std::wstring(ItemString, ItemLength);
out.append(ItemString, ItemLength);
}
// Routine Description:

Просмотреть файл

@ -27,7 +27,7 @@ til::CoordType CalcWindowSizeY(const til::inclusive_rect& rect) noexcept;
til::CoordType CalcCursorYOffsetInPixels(const til::CoordType sFontSizeY, const ULONG ulSize) noexcept;
WORD ConvertStringToDec(_In_ PCWSTR pwchToConvert, _Out_opt_ PCWSTR* const ppwchEnd) noexcept;
std::wstring _LoadString(const UINT id);
void _LoadString(const UINT id, std::wstring& out);
static UINT s_LoadStringEx(_In_ HINSTANCE hModule,
_In_ UINT wID,
_Out_writes_(cchBufferMax) LPWSTR lpBuffer,