зеркало из https://github.com/microsoft/terminal.git
Use VT for COOKED_READ_DATA (#17445)
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:
Родитель
30447cf7e4
Коммит
ac5b4f5831
|
@ -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,
|
||||
|
|
Загрузка…
Ссылка в новой задаче