mirror of
https://github.com/microsoft/terminal.git
synced 2025-12-10 00:48:23 -06:00
Merge remote-tracking branch 'origin/main' into feature/llm
This commit is contained in:
commit
10f8933d15
@ -13,6 +13,7 @@
|
||||
#include "srvinit.h"
|
||||
|
||||
#include "../interactivity/inc/ServiceLocator.hpp"
|
||||
#include "../interactivity/win32/CustomWindowMessages.h"
|
||||
#include "../types/inc/convert.hpp"
|
||||
|
||||
using Microsoft::Console::Interactivity::ServiceLocator;
|
||||
@ -179,6 +180,32 @@ void CONSOLE_INFORMATION::SetBracketedPasteMode(const bool enabled) noexcept
|
||||
_bracketedPasteMode = enabled;
|
||||
}
|
||||
|
||||
void CONSOLE_INFORMATION::CopyTextToClipboard(const std::wstring_view text)
|
||||
{
|
||||
const auto window = ServiceLocator::LocateConsoleWindow();
|
||||
if (window)
|
||||
{
|
||||
// The clipboard can only be updated from the main GUI thread, so we
|
||||
// need to post a message to trigger the actual copy operation. But if
|
||||
// the pending clipboard content is already set, a message would have
|
||||
// already been posted, so there's no need to post another one.
|
||||
const auto clipboardMessageSent = _pendingClipboardText.has_value();
|
||||
_pendingClipboardText = text;
|
||||
if (!clipboardMessageSent)
|
||||
{
|
||||
PostMessageW(window->GetWindowHandle(), CM_UPDATE_CLIPBOARD, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::wstring> CONSOLE_INFORMATION::UsePendingClipboardText()
|
||||
{
|
||||
// Once the pending text has been used, we clear the variable to let the
|
||||
// CopyTextToClipboard method know that the last CM_UPDATE_CLIPBOARD message
|
||||
// has been processed, and future updates will require another message.
|
||||
return std::exchange(_pendingClipboardText, {});
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - Return the active screen buffer of the console.
|
||||
// Arguments:
|
||||
|
||||
@ -280,9 +280,9 @@ unsigned int ConhostInternalGetSet::GetInputCodePage() const
|
||||
// - content - the text to be copied.
|
||||
// Return Value:
|
||||
// - <none>
|
||||
void ConhostInternalGetSet::CopyToClipboard(const wil::zwstring_view /*content*/)
|
||||
void ConhostInternalGetSet::CopyToClipboard(const wil::zwstring_view content)
|
||||
{
|
||||
// TODO
|
||||
ServiceLocator::LocateGlobals().getConsoleInformation().CopyTextToClipboard(content);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
||||
@ -126,6 +126,8 @@ public:
|
||||
|
||||
bool GetBracketedPasteMode() const noexcept;
|
||||
void SetBracketedPasteMode(const bool enabled) noexcept;
|
||||
void CopyTextToClipboard(const std::wstring_view text);
|
||||
std::optional<std::wstring> UsePendingClipboardText();
|
||||
|
||||
void SetTitle(const std::wstring_view newTitle);
|
||||
void SetTitlePrefix(const std::wstring_view newTitlePrefix);
|
||||
@ -160,6 +162,7 @@ private:
|
||||
SCREEN_INFORMATION* pCurrentScreenBuffer = nullptr;
|
||||
COOKED_READ_DATA* _cookedReadData = nullptr; // non-ownership pointer
|
||||
bool _bracketedPasteMode = false;
|
||||
std::optional<std::wstring> _pendingClipboardText;
|
||||
|
||||
Microsoft::Console::VirtualTerminal::VtIo _vtIo;
|
||||
Microsoft::Console::CursorBlinker _blinker;
|
||||
|
||||
@ -24,6 +24,22 @@ using namespace Microsoft::Console::Types;
|
||||
|
||||
#pragma region Public Methods
|
||||
|
||||
void Clipboard::CopyText(const std::wstring& text)
|
||||
{
|
||||
const auto clipboard = _openClipboard(ServiceLocator::LocateConsoleWindow()->GetWindowHandle());
|
||||
if (!clipboard)
|
||||
{
|
||||
LOG_LAST_ERROR();
|
||||
return;
|
||||
}
|
||||
|
||||
EmptyClipboard();
|
||||
// As per: https://learn.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats
|
||||
// CF_UNICODETEXT: [...] A null character signals the end of the data.
|
||||
// --> We add +1 to the length. This works because .c_str() is null-terminated.
|
||||
_copyToClipboard(CF_UNICODETEXT, text.c_str(), (text.size() + 1) * sizeof(wchar_t));
|
||||
}
|
||||
|
||||
// Arguments:
|
||||
// - fAlsoCopyFormatting - Place colored HTML & RTF text onto the clipboard as well as the usual plain text.
|
||||
// Return Value:
|
||||
|
||||
@ -29,4 +29,6 @@
|
||||
#define CM_SET_KEYBOARD_LAYOUT (WM_USER+19)
|
||||
#endif
|
||||
|
||||
#define CM_UPDATE_CLIPBOARD (WM_USER+20)
|
||||
|
||||
// clang-format on
|
||||
|
||||
@ -29,6 +29,7 @@ namespace Microsoft::Console::Interactivity::Win32
|
||||
public:
|
||||
static Clipboard& Instance();
|
||||
|
||||
void CopyText(const std::wstring& text);
|
||||
void Copy(_In_ const bool fAlsoCopyFormatting = false);
|
||||
void Paste();
|
||||
void PasteDrop(HDROP drop);
|
||||
|
||||
@ -773,6 +773,15 @@ static constexpr TsfDataProvider s_tsfDataProvider;
|
||||
}
|
||||
#endif // DBG
|
||||
|
||||
case CM_UPDATE_CLIPBOARD:
|
||||
{
|
||||
if (const auto clipboardText = gci.UsePendingClipboardText())
|
||||
{
|
||||
Clipboard::Instance().CopyText(clipboardText.value());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_CONSOLE_CARET:
|
||||
case EVENT_CONSOLE_UPDATE_REGION:
|
||||
case EVENT_CONSOLE_UPDATE_SIMPLE:
|
||||
|
||||
@ -237,6 +237,7 @@ void SixelParser::_executeNextLine()
|
||||
_imageCursor.y += _sixelHeight;
|
||||
_availablePixelHeight -= _sixelHeight;
|
||||
_resizeImageBuffer(_sixelHeight);
|
||||
_fillImageBackgroundWhenScrolled();
|
||||
}
|
||||
|
||||
void SixelParser::_executeMoveToHome()
|
||||
@ -341,6 +342,12 @@ void SixelParser::_initRasterAttributes(const VTInt macroParameter, const Dispat
|
||||
|
||||
// By default, the filled area will cover the maximum extent allowed.
|
||||
_backgroundSize = { til::CoordTypeMax, til::CoordTypeMax };
|
||||
|
||||
// If the image ends up extending beyond the bottom of the page, we may need
|
||||
// to perform additional background filling as the screen is scrolled, which
|
||||
// requires us to track the area filled so far. This will be initialized, if
|
||||
// necessary, in the _fillImageBackground method below.
|
||||
_filledBackgroundHeight = std::nullopt;
|
||||
}
|
||||
|
||||
void SixelParser::_updateRasterAttributes(const VTParameters& rasterAttributes)
|
||||
@ -373,6 +380,15 @@ void SixelParser::_updateRasterAttributes(const VTParameters& rasterAttributes)
|
||||
// back to the dimensions from an earlier raster attributes command.
|
||||
_backgroundSize.width = width > 0 ? width : _backgroundSize.width;
|
||||
_backgroundSize.height = height > 0 ? height : _backgroundSize.height;
|
||||
|
||||
// If the aspect ratio has changed, the image height may increase, and that
|
||||
// could potentially trigger a scroll requiring the background to be filled.
|
||||
_fillImageBackgroundWhenScrolled();
|
||||
|
||||
// And while not documented, we know from testing on a VT330 that the raster
|
||||
// attributes command should also trigger a carriage return. This applies
|
||||
// regardless of whether the requested aspect ratio is valid or not.
|
||||
_executeCarriageReturn();
|
||||
}
|
||||
|
||||
void SixelParser::_scrollTextBuffer(Page& page, const int scrollAmount)
|
||||
@ -656,15 +672,26 @@ void SixelParser::_fillImageBackground()
|
||||
{
|
||||
_backgroundFillRequired = false;
|
||||
|
||||
const auto backgroundHeight = std::min(_backgroundSize.height, _availablePixelHeight);
|
||||
const auto backgroundWidth = std::min(_backgroundSize.width, _availablePixelWidth);
|
||||
_resizeImageBuffer(backgroundHeight);
|
||||
|
||||
// When a background fill is requested, we prefill the buffer with the 0
|
||||
// color index, up to the boundaries set by the raster attributes (or if
|
||||
// none were given, up to the page boundaries). The actual image output
|
||||
// isn't limited by the background dimensions though.
|
||||
const auto backgroundHeight = std::min(_backgroundSize.height, _availablePixelHeight);
|
||||
_resizeImageBuffer(backgroundHeight);
|
||||
_fillImageBackground(backgroundHeight);
|
||||
// When the image extends beyond the page boundaries, and the screen is
|
||||
// scrolled, we also need to fill the newly exposed lines, so we keep a
|
||||
// record of the area filled so far. Initially this is considered to be
|
||||
// the available height, even if it wasn't all filled to start with.
|
||||
_filledBackgroundHeight = _imageCursor.y + _availablePixelHeight;
|
||||
_fillImageBackgroundWhenScrolled();
|
||||
}
|
||||
}
|
||||
|
||||
void SixelParser::_fillImageBackground(const int backgroundHeight)
|
||||
{
|
||||
static constexpr auto backgroundPixel = IndexedPixel{};
|
||||
const auto backgroundWidth = std::min(_backgroundSize.width, _availablePixelWidth);
|
||||
const auto backgroundOffset = _imageCursor.y * _imageMaxWidth;
|
||||
auto dst = std::next(_imageBuffer.begin(), backgroundOffset);
|
||||
for (auto i = 0; i < backgroundHeight; i++)
|
||||
@ -672,9 +699,34 @@ void SixelParser::_fillImageBackground()
|
||||
std::fill_n(dst, backgroundWidth, backgroundPixel);
|
||||
std::advance(dst, _imageMaxWidth);
|
||||
}
|
||||
|
||||
_imageWidth = std::max(_imageWidth, backgroundWidth);
|
||||
}
|
||||
|
||||
void SixelParser::_fillImageBackgroundWhenScrolled()
|
||||
{
|
||||
// If _filledBackgroundHeight is set, that means a background fill has been
|
||||
// requested, and we need to extend that area whenever the image is about to
|
||||
// overrun it. The newly filled area is a multiple of the cell height (this
|
||||
// is to match the behavior of the original hardware terminals).
|
||||
const auto imageHeight = _imageCursor.y + _sixelHeight;
|
||||
if (_filledBackgroundHeight && imageHeight > _filledBackgroundHeight) [[unlikely]]
|
||||
{
|
||||
_filledBackgroundHeight = (imageHeight + _cellSize.height - 1) / _cellSize.height * _cellSize.height;
|
||||
const auto additionalFillHeight = *_filledBackgroundHeight - _imageCursor.y;
|
||||
_resizeImageBuffer(additionalFillHeight);
|
||||
_fillImageBackground(additionalFillHeight);
|
||||
}
|
||||
}
|
||||
|
||||
void SixelParser::_decreaseFilledBackgroundHeight(const int decreasedHeight) noexcept
|
||||
{
|
||||
// Sometimes the top of the image buffer may be clipped (e.g. when the image
|
||||
// scrolls off the top of a margin area). When that occurs, our record of
|
||||
// the filled height will need to be decreased to account for the new start.
|
||||
if (_filledBackgroundHeight) [[unlikely]]
|
||||
{
|
||||
_filledBackgroundHeight = *_filledBackgroundHeight - decreasedHeight;
|
||||
}
|
||||
}
|
||||
|
||||
void SixelParser::_writeToImageBuffer(int sixelValue, int repeatCount)
|
||||
@ -717,11 +769,13 @@ void SixelParser::_eraseImageBufferRows(const int rowCount, const til::CoordType
|
||||
const auto bufferOffsetEnd = bufferOffset + pixelCount * _imageMaxWidth;
|
||||
if (static_cast<size_t>(bufferOffsetEnd) >= _imageBuffer.size()) [[unlikely]]
|
||||
{
|
||||
_decreaseFilledBackgroundHeight(_imageCursor.y);
|
||||
_imageBuffer.clear();
|
||||
_imageCursor.y = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_decreaseFilledBackgroundHeight(pixelCount);
|
||||
_imageBuffer.erase(_imageBuffer.begin() + bufferOffset, _imageBuffer.begin() + bufferOffsetEnd);
|
||||
_imageCursor.y -= pixelCount;
|
||||
}
|
||||
|
||||
@ -89,6 +89,7 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
til::CoordType _pendingTextScrollCount = 0;
|
||||
til::size _backgroundSize;
|
||||
bool _backgroundFillRequired = false;
|
||||
std::optional<til::CoordType> _filledBackgroundHeight;
|
||||
|
||||
void _initColorMap(const VTParameter backgroundColor);
|
||||
void _defineColor(const VTParameters& colorParameters);
|
||||
@ -109,6 +110,9 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
void _initImageBuffer();
|
||||
void _resizeImageBuffer(const til::CoordType requiredHeight);
|
||||
void _fillImageBackground();
|
||||
void _fillImageBackground(const int backgroundHeight);
|
||||
void _fillImageBackgroundWhenScrolled();
|
||||
void _decreaseFilledBackgroundHeight(const int decreasedHeight) noexcept;
|
||||
void _writeToImageBuffer(const int sixelValue, const int repeatCount);
|
||||
void _eraseImageBufferRows(const int rowCount, const til::CoordType startRow = 0) noexcept;
|
||||
void _maybeFlushImageBuffer(const bool endOfSequence = false);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user