2019-05-03 00:29:04 +02:00
|
|
|
// Copyright (c) Microsoft Corporation.
|
|
|
|
// Licensed under the MIT license.
|
|
|
|
|
|
|
|
#include "precomp.h"
|
|
|
|
#include "XtermEngine.hpp"
|
|
|
|
#include "../../types/inc/convert.hpp"
|
|
|
|
#pragma hdrstop
|
|
|
|
using namespace Microsoft::Console;
|
|
|
|
using namespace Microsoft::Console::Render;
|
|
|
|
using namespace Microsoft::Console::Types;
|
|
|
|
|
|
|
|
XtermEngine::XtermEngine(_In_ wil::unique_hfile hPipe,
|
2019-09-21 00:43:11 +02:00
|
|
|
wil::shared_event shutdownEvent,
|
2019-05-03 00:29:04 +02:00
|
|
|
const IDefaultColorProvider& colorProvider,
|
|
|
|
const Viewport initialViewport,
|
|
|
|
_In_reads_(cColorTable) const COLORREF* const ColorTable,
|
|
|
|
const WORD cColorTable,
|
|
|
|
const bool fUseAsciiOnly) :
|
2019-09-21 00:43:11 +02:00
|
|
|
VtEngine(std::move(hPipe), shutdownEvent, colorProvider, initialViewport),
|
2019-05-03 00:29:04 +02:00
|
|
|
_ColorTable(ColorTable),
|
|
|
|
_cColorTable(cColorTable),
|
|
|
|
_fUseAsciiOnly(fUseAsciiOnly),
|
|
|
|
_previousLineWrapped(false),
|
|
|
|
_usingUnderLine(false),
|
2019-10-03 01:11:27 +02:00
|
|
|
_needToDisableCursor(false),
|
|
|
|
_lastCursorIsVisible(false),
|
|
|
|
_nextCursorIsVisible(true)
|
2019-05-03 00:29:04 +02:00
|
|
|
{
|
|
|
|
// Set out initial cursor position to -1, -1. This will force our initial
|
|
|
|
// paint to manually move the cursor to 0, 0, not just ignore it.
|
|
|
|
_lastText = VtEngine::INVALID_COORDS;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Prepares internal structures for a painting operation. Turns the cursor
|
|
|
|
// off, so we don't see it flashing all over the client's screen as we
|
|
|
|
// paint the new contents.
|
|
|
|
// Arguments:
|
|
|
|
// - <none>
|
|
|
|
// Return Value:
|
|
|
|
// - S_OK if we started to paint. S_FALSE if we didn't need to paint. HRESULT
|
|
|
|
// error code if painting didn't start successfully, or we failed to write
|
|
|
|
// the pipe.
|
2019-06-11 22:27:09 +02:00
|
|
|
[[nodiscard]] HRESULT XtermEngine::StartPaint() noexcept
|
2019-05-03 00:29:04 +02:00
|
|
|
{
|
|
|
|
RETURN_IF_FAILED(VtEngine::StartPaint());
|
|
|
|
|
|
|
|
_trace.TraceLastText(_lastText);
|
|
|
|
|
2019-10-03 01:11:27 +02:00
|
|
|
// Prep us to think that the cursor is not visible this frame. If it _is_
|
|
|
|
// visible, then PaintCursor will be called, and we'll set this to true
|
|
|
|
// during the frame.
|
|
|
|
_nextCursorIsVisible = false;
|
|
|
|
|
2019-05-03 00:29:04 +02:00
|
|
|
if (_firstPaint)
|
|
|
|
{
|
|
|
|
// MSFT:17815688
|
|
|
|
// If the caller requested to inherit the cursor, we shouldn't
|
|
|
|
// clear the screen on the first paint. Otherwise, we'll clear
|
|
|
|
// the screen on the first paint, just to make sure that the
|
|
|
|
// terminal's state is consistent with what we'll be rendering.
|
|
|
|
RETURN_IF_FAILED(_ClearScreen());
|
|
|
|
_clearedAllThisFrame = true;
|
|
|
|
_firstPaint = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const auto dirtyRect = GetDirtyRectInChars();
|
|
|
|
const auto dirtyView = Viewport::FromInclusive(dirtyRect);
|
|
|
|
if (!_resized && dirtyView == _lastViewport)
|
|
|
|
{
|
|
|
|
// TODO: MSFT:21096414 - This is never actually hit. We set
|
|
|
|
// _resized=true on every frame (see VtEngine::UpdateViewport).
|
|
|
|
// Unfortunately, not always setting _resized is not a good enough
|
|
|
|
// solution, see that work item for a description why.
|
|
|
|
RETURN_IF_FAILED(_ClearScreen());
|
|
|
|
_clearedAllThisFrame = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!_quickReturn)
|
|
|
|
{
|
2019-10-03 01:11:27 +02:00
|
|
|
if (_WillWriteSingleChar())
|
2019-05-03 00:29:04 +02:00
|
|
|
{
|
|
|
|
// Don't re-enable the cursor.
|
|
|
|
_quickReturn = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Routine Description:
|
|
|
|
// - EndPaint helper to perform the final rendering steps. Turn the cursor back
|
|
|
|
// on.
|
|
|
|
// Arguments:
|
|
|
|
// - <none>
|
|
|
|
// Return Value:
|
|
|
|
// - S_OK if we succeeded, else an appropriate HRESULT for failing to allocate or write.
|
2019-06-11 22:27:09 +02:00
|
|
|
[[nodiscard]] HRESULT XtermEngine::EndPaint() noexcept
|
2019-05-03 00:29:04 +02:00
|
|
|
{
|
|
|
|
// If during the frame we determined that the cursor needed to be disabled,
|
|
|
|
// then insert a cursor off at the start of the buffer, and re-enable
|
|
|
|
// the cursor here.
|
|
|
|
if (_needToDisableCursor)
|
|
|
|
{
|
2019-10-03 01:11:27 +02:00
|
|
|
// If the cursor was previously visible, let's hide it for this frame,
|
|
|
|
// by prepending a cursor off.
|
|
|
|
if (_lastCursorIsVisible)
|
|
|
|
{
|
|
|
|
_buffer.insert(0, "\x1b[25l");
|
|
|
|
_lastCursorIsVisible = false;
|
|
|
|
}
|
|
|
|
// If the cursor was NOT previously visible, then that's fine! we don't
|
|
|
|
// need to worry, it's already off.
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the cursor is moving from off -> on (including cases where we just
|
|
|
|
// disabled if for this frame), show the cursor at the end of the frame
|
|
|
|
if (_nextCursorIsVisible && !_lastCursorIsVisible)
|
|
|
|
{
|
2019-05-03 00:29:04 +02:00
|
|
|
RETURN_IF_FAILED(_ShowCursor());
|
|
|
|
}
|
2019-10-03 01:11:27 +02:00
|
|
|
// Otherwise, if the cursor previously was visible, and it should be hidden
|
|
|
|
// (on -> off), hide it at the end of the frame.
|
|
|
|
else if (!_nextCursorIsVisible && _lastCursorIsVisible)
|
|
|
|
{
|
|
|
|
RETURN_IF_FAILED(_HideCursor());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update our tracker of what we thought the last cursor state of the
|
|
|
|
// terminal was.
|
|
|
|
_lastCursorIsVisible = _nextCursorIsVisible;
|
2019-05-03 00:29:04 +02:00
|
|
|
|
|
|
|
RETURN_IF_FAILED(VtEngine::EndPaint());
|
|
|
|
|
|
|
|
_needToDisableCursor = false;
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Routine Description:
|
|
|
|
// - Write a VT sequence to either start or stop underlining text.
|
|
|
|
// Arguments:
|
|
|
|
// - legacyColorAttribute: A console attributes bit field containing information
|
|
|
|
// about the underlining state of the text.
|
|
|
|
// Return Value:
|
|
|
|
// - S_OK if we succeeded, else an appropriate HRESULT for failing to allocate or write.
|
2019-06-11 22:27:09 +02:00
|
|
|
[[nodiscard]] HRESULT XtermEngine::_UpdateUnderline(const WORD legacyColorAttribute) noexcept
|
2019-05-03 00:29:04 +02:00
|
|
|
{
|
|
|
|
bool textUnderlined = WI_IsFlagSet(legacyColorAttribute, COMMON_LVB_UNDERSCORE);
|
|
|
|
if (textUnderlined != _usingUnderLine)
|
|
|
|
{
|
|
|
|
if (textUnderlined)
|
|
|
|
{
|
|
|
|
RETURN_IF_FAILED(_BeginUnderline());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
RETURN_IF_FAILED(_EndUnderline());
|
|
|
|
}
|
|
|
|
_usingUnderLine = textUnderlined;
|
|
|
|
}
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Routine Description:
|
|
|
|
// - Write a VT sequence to change the current colors of text. Only writes
|
|
|
|
// 16-color attributes.
|
|
|
|
// Arguments:
|
|
|
|
// - colorForeground: The RGB Color to use to paint the foreground text.
|
|
|
|
// - colorBackground: The RGB Color to use to paint the background of the text.
|
|
|
|
// - legacyColorAttribute: A console attributes bit field specifying the brush
|
|
|
|
// colors we should use.
|
Add support for passing through extended text attributes, like… (#2917)
## Summary of the Pull Request
Adds support for Italics, Blinking, Invisible, CrossedOut text, THROUGH CONPTY. This does **NOT** add support for those styles to conhost or the terminal.
We will store these "Extended Text Attributes" in a `TextAttribute`. When we go to render a line, we'll see if the state has changed from our previous state, and if so, we'll appropriately toggle that state with VT. Boldness has been moved from a `bool` to a single bit in these flags.
Technically, now that these are stored in the buffer, we only need to make changes to the renderers to be able to support them. That's not being done as a part of this PR however.
## References
See also #2915 and #2916, which are some follow-up tasks from this fix. I thought them too risky for 20H1.
## PR Checklist
* [x] Closes #2554
* [x] I work here
* [x] Tests added/passed
* [n/a] Requires documentation to be updated
<hr>
* store text with extended attributes too
* Plumb attributes through all the renderers
* parse extended attrs, though we're not renderering them right
* Render these states correctly
* Add a very extensive test
* Cleanup for PR
* a block of PR feedback
* add 512 test cases
* Fix the build
* Fix @carlos-zamora's suggestions
* @miniksa's PR feedback
2019-10-04 22:53:54 +02:00
|
|
|
// - extendedAttrs - extended text attributes (italic, underline, etc.) to use.
|
2019-05-03 00:29:04 +02:00
|
|
|
// - isSettingDefaultBrushes: indicates if we should change the background color of
|
|
|
|
// the window. Unused for VT
|
|
|
|
// Return Value:
|
|
|
|
// - S_OK if we succeeded, else an appropriate HRESULT for failing to allocate or write.
|
2019-06-11 22:27:09 +02:00
|
|
|
[[nodiscard]] HRESULT XtermEngine::UpdateDrawingBrushes(const COLORREF colorForeground,
|
|
|
|
const COLORREF colorBackground,
|
|
|
|
const WORD legacyColorAttribute,
|
Add support for passing through extended text attributes, like… (#2917)
## Summary of the Pull Request
Adds support for Italics, Blinking, Invisible, CrossedOut text, THROUGH CONPTY. This does **NOT** add support for those styles to conhost or the terminal.
We will store these "Extended Text Attributes" in a `TextAttribute`. When we go to render a line, we'll see if the state has changed from our previous state, and if so, we'll appropriately toggle that state with VT. Boldness has been moved from a `bool` to a single bit in these flags.
Technically, now that these are stored in the buffer, we only need to make changes to the renderers to be able to support them. That's not being done as a part of this PR however.
## References
See also #2915 and #2916, which are some follow-up tasks from this fix. I thought them too risky for 20H1.
## PR Checklist
* [x] Closes #2554
* [x] I work here
* [x] Tests added/passed
* [n/a] Requires documentation to be updated
<hr>
* store text with extended attributes too
* Plumb attributes through all the renderers
* parse extended attrs, though we're not renderering them right
* Render these states correctly
* Add a very extensive test
* Cleanup for PR
* a block of PR feedback
* add 512 test cases
* Fix the build
* Fix @carlos-zamora's suggestions
* @miniksa's PR feedback
2019-10-04 22:53:54 +02:00
|
|
|
const ExtendedAttributes extendedAttrs,
|
2019-06-11 22:27:09 +02:00
|
|
|
const bool /*isSettingDefaultBrushes*/) noexcept
|
2019-05-03 00:29:04 +02:00
|
|
|
{
|
|
|
|
//When we update the brushes, check the wAttrs to see if the LVB_UNDERSCORE
|
|
|
|
// flag is there. If the state of that flag is different then our
|
|
|
|
// current state, change the underlining state.
|
|
|
|
// We have to do this here, instead of in PaintBufferGridLines, because
|
|
|
|
// we'll have already painted the text by the time PaintBufferGridLines
|
|
|
|
// is called.
|
Add support for passing through extended text attributes, like… (#2917)
## Summary of the Pull Request
Adds support for Italics, Blinking, Invisible, CrossedOut text, THROUGH CONPTY. This does **NOT** add support for those styles to conhost or the terminal.
We will store these "Extended Text Attributes" in a `TextAttribute`. When we go to render a line, we'll see if the state has changed from our previous state, and if so, we'll appropriately toggle that state with VT. Boldness has been moved from a `bool` to a single bit in these flags.
Technically, now that these are stored in the buffer, we only need to make changes to the renderers to be able to support them. That's not being done as a part of this PR however.
## References
See also #2915 and #2916, which are some follow-up tasks from this fix. I thought them too risky for 20H1.
## PR Checklist
* [x] Closes #2554
* [x] I work here
* [x] Tests added/passed
* [n/a] Requires documentation to be updated
<hr>
* store text with extended attributes too
* Plumb attributes through all the renderers
* parse extended attrs, though we're not renderering them right
* Render these states correctly
* Add a very extensive test
* Cleanup for PR
* a block of PR feedback
* add 512 test cases
* Fix the build
* Fix @carlos-zamora's suggestions
* @miniksa's PR feedback
2019-10-04 22:53:54 +02:00
|
|
|
// TODO:GH#2915 Treat underline separately from LVB_UNDERSCORE
|
2019-05-03 00:29:04 +02:00
|
|
|
RETURN_IF_FAILED(_UpdateUnderline(legacyColorAttribute));
|
|
|
|
// The base xterm mode only knows about 16 colors
|
Add support for passing through extended text attributes, like… (#2917)
## Summary of the Pull Request
Adds support for Italics, Blinking, Invisible, CrossedOut text, THROUGH CONPTY. This does **NOT** add support for those styles to conhost or the terminal.
We will store these "Extended Text Attributes" in a `TextAttribute`. When we go to render a line, we'll see if the state has changed from our previous state, and if so, we'll appropriately toggle that state with VT. Boldness has been moved from a `bool` to a single bit in these flags.
Technically, now that these are stored in the buffer, we only need to make changes to the renderers to be able to support them. That's not being done as a part of this PR however.
## References
See also #2915 and #2916, which are some follow-up tasks from this fix. I thought them too risky for 20H1.
## PR Checklist
* [x] Closes #2554
* [x] I work here
* [x] Tests added/passed
* [n/a] Requires documentation to be updated
<hr>
* store text with extended attributes too
* Plumb attributes through all the renderers
* parse extended attrs, though we're not renderering them right
* Render these states correctly
* Add a very extensive test
* Cleanup for PR
* a block of PR feedback
* add 512 test cases
* Fix the build
* Fix @carlos-zamora's suggestions
* @miniksa's PR feedback
2019-10-04 22:53:54 +02:00
|
|
|
return VtEngine::_16ColorUpdateDrawingBrushes(colorForeground,
|
|
|
|
colorBackground,
|
|
|
|
WI_IsFlagSet(extendedAttrs, ExtendedAttributes::Bold),
|
|
|
|
_ColorTable,
|
|
|
|
_cColorTable);
|
2019-05-03 00:29:04 +02:00
|
|
|
}
|
|
|
|
|
2019-10-03 01:11:27 +02:00
|
|
|
// Routine Description:
|
|
|
|
// - Draws the cursor on the screen
|
|
|
|
// Arguments:
|
|
|
|
// - options - Options that affect the presentation of the cursor
|
|
|
|
// Return Value:
|
|
|
|
// - S_OK or suitable HRESULT error from writing pipe.
|
|
|
|
[[nodiscard]] HRESULT XtermEngine::PaintCursor(const IRenderEngine::CursorOptions& options) noexcept
|
|
|
|
{
|
|
|
|
// PaintCursor is only called when the cursor is in fact visible in a single
|
|
|
|
// frame. When this is called, mark _nextCursorIsVisible as true. At the end
|
|
|
|
// of the frame, we'll decide to either turn the cursor on or not, based
|
|
|
|
// upon the previous state.
|
|
|
|
|
|
|
|
// When this method is not called during a frame, it's because the cursor
|
|
|
|
// was not visible. In that case, at the end of the frame,
|
|
|
|
// _nextCursorIsVisible will still be false (from when we set it during
|
|
|
|
// StartPaint)
|
|
|
|
_nextCursorIsVisible = true;
|
|
|
|
return VtEngine::PaintCursor(options);
|
|
|
|
}
|
|
|
|
|
2019-05-03 00:29:04 +02:00
|
|
|
// Routine Description:
|
|
|
|
// - Write a VT sequence to move the cursor to the specified coordinates. We
|
|
|
|
// also store the last place we left the cursor for future optimizations.
|
|
|
|
// If the cursor only needs to go to the origin, only write the home sequence.
|
|
|
|
// If the new cursor is only down one line from the current, only write a newline
|
|
|
|
// If the new cursor is only down one line and at the start of the line, write
|
|
|
|
// a carriage return.
|
|
|
|
// Otherwise just write the whole sequence for moving it.
|
|
|
|
// Arguments:
|
|
|
|
// - coord: location to move the cursor to.
|
|
|
|
// Return Value:
|
|
|
|
// - S_OK if we succeeded, else an appropriate HRESULT for failing to allocate or write.
|
2019-06-11 22:27:09 +02:00
|
|
|
[[nodiscard]] HRESULT XtermEngine::_MoveCursor(COORD const coord) noexcept
|
2019-05-03 00:29:04 +02:00
|
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
|
|
|
|
if (coord.X != _lastText.X || coord.Y != _lastText.Y)
|
|
|
|
{
|
|
|
|
if (coord.X == 0 && coord.Y == 0)
|
|
|
|
{
|
|
|
|
_needToDisableCursor = true;
|
|
|
|
hr = _CursorHome();
|
|
|
|
}
|
2019-06-11 22:27:09 +02:00
|
|
|
else if (coord.X == 0 && coord.Y == (_lastText.Y + 1))
|
2019-05-03 00:29:04 +02:00
|
|
|
{
|
|
|
|
// Down one line, at the start of the line.
|
|
|
|
|
|
|
|
// If the previous line wrapped, then the cursor is already at this
|
|
|
|
// position, we just don't know it yet. Don't emit anything.
|
|
|
|
if (_previousLineWrapped)
|
|
|
|
{
|
|
|
|
hr = S_OK;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::string seq = "\r\n";
|
|
|
|
hr = _Write(seq);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (coord.X == 0 && coord.Y == _lastText.Y)
|
|
|
|
{
|
|
|
|
// Start of this line
|
|
|
|
std::string seq = "\r";
|
|
|
|
hr = _Write(seq);
|
|
|
|
}
|
2019-06-11 22:27:09 +02:00
|
|
|
else if (coord.X == _lastText.X && coord.Y == (_lastText.Y + 1))
|
2019-05-03 00:29:04 +02:00
|
|
|
{
|
|
|
|
// Down one line, same X position
|
|
|
|
std::string seq = "\n";
|
|
|
|
hr = _Write(seq);
|
|
|
|
}
|
2019-06-11 22:27:09 +02:00
|
|
|
else if (coord.X == (_lastText.X - 1) && coord.Y == (_lastText.Y))
|
2019-05-03 00:29:04 +02:00
|
|
|
{
|
|
|
|
// Back one char, same Y position
|
|
|
|
std::string seq = "\b";
|
|
|
|
hr = _Write(seq);
|
|
|
|
}
|
|
|
|
else if (coord.Y == _lastText.Y && coord.X > _lastText.X)
|
|
|
|
{
|
|
|
|
// Same line, forward some distance
|
|
|
|
short distance = coord.X - _lastText.X;
|
|
|
|
hr = _CursorForward(distance);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_needToDisableCursor = true;
|
|
|
|
hr = _CursorPosition(coord);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
{
|
|
|
|
_lastText = coord;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (_lastText.Y != _lastViewport.ToOrigin().BottomInclusive())
|
|
|
|
{
|
|
|
|
_newBottomLine = false;
|
|
|
|
}
|
|
|
|
_deferredCursorPos = INVALID_COORDS;
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Routine Description:
|
|
|
|
// - Scrolls the existing data on the in-memory frame by the scroll region
|
|
|
|
// deltas we have collectively received through the Invalidate methods
|
|
|
|
// since the last time this was called.
|
|
|
|
// Move the cursor to the origin, and insert or delete rows as appropriate.
|
|
|
|
// The inserted rows will be blank, but marked invalid by InvalidateScroll,
|
|
|
|
// so they will later be written by PaintBufferLine.
|
|
|
|
// Arguments:
|
|
|
|
// - <none>
|
|
|
|
// Return Value:
|
|
|
|
// - S_OK if we succeeded, else an appropriate HRESULT for failing to allocate or write.
|
2019-06-11 22:27:09 +02:00
|
|
|
[[nodiscard]] HRESULT XtermEngine::ScrollFrame() noexcept
|
2019-05-03 00:29:04 +02:00
|
|
|
{
|
|
|
|
if (_scrollDelta.X != 0)
|
|
|
|
{
|
|
|
|
// No easy way to shift left-right. Everything needs repainting.
|
|
|
|
return InvalidateAll();
|
|
|
|
}
|
|
|
|
if (_scrollDelta.Y == 0)
|
|
|
|
{
|
|
|
|
// There's nothing to do here. Do nothing.
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
const short dy = _scrollDelta.Y;
|
|
|
|
const short absDy = static_cast<short>(abs(dy));
|
|
|
|
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
if (dy < 0)
|
|
|
|
{
|
|
|
|
// Instead of deleting the first line (causing everything to move up)
|
|
|
|
// move to the bottom of the buffer, and newline.
|
|
|
|
// That will cause everything to move up, by moving the viewport down.
|
|
|
|
// This will let remote conhosts scroll up to see history like normal.
|
|
|
|
const short bottom = _lastViewport.ToOrigin().BottomInclusive();
|
2019-06-11 22:27:09 +02:00
|
|
|
hr = _MoveCursor({ 0, bottom });
|
2019-05-03 00:29:04 +02:00
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
{
|
|
|
|
std::string seq = std::string(absDy, '\n');
|
|
|
|
hr = _Write(seq);
|
|
|
|
// Mark that the bottom line is new, so we won't spend time with an
|
|
|
|
// ECH on it.
|
|
|
|
_newBottomLine = true;
|
|
|
|
}
|
|
|
|
// We don't need to _MoveCursor the cursor again, because it's still
|
|
|
|
// at the bottom of the viewport.
|
|
|
|
}
|
|
|
|
else if (dy > 0)
|
|
|
|
{
|
|
|
|
// Move to the top of the buffer, and insert some lines of text, to
|
|
|
|
// cause the viewport contents to shift down.
|
2019-06-11 22:27:09 +02:00
|
|
|
hr = _MoveCursor({ 0, 0 });
|
2019-05-03 00:29:04 +02:00
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
{
|
|
|
|
hr = _InsertLine(absDy);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Routine Description:
|
|
|
|
// - Notifies us that the console is attempting to scroll the existing screen
|
|
|
|
// area. Add the top or bottom rows to the invalid region, and update the
|
|
|
|
// total scroll delta accumulated this frame.
|
|
|
|
// Arguments:
|
|
|
|
// - pcoordDelta - Pointer to character dimension (COORD) of the distance the
|
|
|
|
// console would like us to move while scrolling.
|
|
|
|
// Return Value:
|
|
|
|
// - S_OK if we succeeded, else an appropriate HRESULT for safemath failure
|
2019-06-11 22:27:09 +02:00
|
|
|
[[nodiscard]] HRESULT XtermEngine::InvalidateScroll(const COORD* const pcoordDelta) noexcept
|
2019-05-03 00:29:04 +02:00
|
|
|
{
|
|
|
|
const short dx = pcoordDelta->X;
|
|
|
|
const short dy = pcoordDelta->Y;
|
|
|
|
|
|
|
|
if (dx != 0 || dy != 0)
|
|
|
|
{
|
|
|
|
// Scroll the current offset
|
|
|
|
RETURN_IF_FAILED(_InvalidOffset(pcoordDelta));
|
|
|
|
|
|
|
|
// Add the top/bottom of the window to the invalid area
|
|
|
|
SMALL_RECT invalid = _lastViewport.ToOrigin().ToExclusive();
|
|
|
|
|
|
|
|
if (dy > 0)
|
|
|
|
{
|
|
|
|
invalid.Bottom = dy;
|
|
|
|
}
|
|
|
|
else if (dy < 0)
|
|
|
|
{
|
|
|
|
invalid.Top = invalid.Bottom + dy;
|
|
|
|
}
|
|
|
|
LOG_IF_FAILED(_InvalidCombine(Viewport::FromExclusive(invalid)));
|
|
|
|
|
|
|
|
COORD invalidScrollNew;
|
|
|
|
RETURN_IF_FAILED(ShortAdd(_scrollDelta.X, dx, &invalidScrollNew.X));
|
|
|
|
RETURN_IF_FAILED(ShortAdd(_scrollDelta.Y, dy, &invalidScrollNew.Y));
|
|
|
|
|
|
|
|
// Store if safemath succeeded
|
|
|
|
_scrollDelta = invalidScrollNew;
|
|
|
|
}
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Routine Description:
|
|
|
|
// - Draws one line of the buffer to the screen. Writes the characters to the
|
|
|
|
// pipe, encoded in UTF-8 or ASCII only, depending on the VtIoMode.
|
|
|
|
// (See descriptions of both implementations for details.)
|
|
|
|
// Arguments:
|
|
|
|
// - clusters - text and column counts for each piece of text.
|
|
|
|
// - coord - character coordinate target to render within viewport
|
|
|
|
// - trimLeft - This specifies whether to trim one character width off the left
|
|
|
|
// side of the output. Used for drawing the right-half only of a
|
|
|
|
// double-wide character.
|
|
|
|
// Return Value:
|
|
|
|
// - S_OK or suitable HRESULT error from writing pipe.
|
2019-06-11 22:27:09 +02:00
|
|
|
[[nodiscard]] HRESULT XtermEngine::PaintBufferLine(std::basic_string_view<Cluster> const clusters,
|
|
|
|
const COORD coord,
|
|
|
|
const bool /*trimLeft*/) noexcept
|
2019-05-03 00:29:04 +02:00
|
|
|
{
|
|
|
|
return _fUseAsciiOnly ?
|
2019-06-11 22:27:09 +02:00
|
|
|
VtEngine::_PaintAsciiBufferLine(clusters, coord) :
|
|
|
|
VtEngine::_PaintUtf8BufferLine(clusters, coord);
|
2019-05-03 00:29:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Wrapper for ITerminalOutputConnection. Write either an ascii-only, or a
|
|
|
|
// proper utf-8 string, depending on our mode.
|
|
|
|
// Arguments:
|
|
|
|
// - wstr - wstring of text to be written
|
|
|
|
// Return Value:
|
|
|
|
// - S_OK or suitable HRESULT error from either conversion or writing pipe.
|
2019-06-11 22:27:09 +02:00
|
|
|
[[nodiscard]] HRESULT XtermEngine::WriteTerminalW(const std::wstring& wstr) noexcept
|
2019-05-03 00:29:04 +02:00
|
|
|
{
|
|
|
|
return _fUseAsciiOnly ?
|
2019-06-11 22:27:09 +02:00
|
|
|
VtEngine::_WriteTerminalAscii(wstr) :
|
|
|
|
VtEngine::_WriteTerminalUtf8(wstr);
|
2019-05-03 00:29:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Method Description:
|
|
|
|
// - Updates the window's title string. Emits the VT sequence to SetWindowTitle.
|
|
|
|
// Arguments:
|
|
|
|
// - newTitle: the new string to use for the title of the window
|
|
|
|
// Return Value:
|
|
|
|
// - S_OK
|
2019-06-11 22:27:09 +02:00
|
|
|
[[nodiscard]] HRESULT XtermEngine::_DoUpdateTitle(const std::wstring& newTitle) noexcept
|
2019-05-03 00:29:04 +02:00
|
|
|
{
|
2019-05-21 08:15:44 +02:00
|
|
|
// inbox telnet uses xterm-ascii as its mode. If we're in ascii mode, don't
|
2019-05-03 00:29:04 +02:00
|
|
|
// do anything, to maintain compatibility.
|
|
|
|
if (_fUseAsciiOnly)
|
|
|
|
{
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
const auto converted = ConvertToA(CP_UTF8, newTitle);
|
|
|
|
return VtEngine::_ChangeTitle(converted);
|
|
|
|
}
|
|
|
|
CATCH_RETURN();
|
|
|
|
}
|