// Copyright (c) Microsoft Corporation. // Licensed under the MIT license. #include "pch.h" #include "Terminal.hpp" #include "ColorFix.hpp" #include using namespace Microsoft::Terminal::Core; using namespace Microsoft::Console::Types; using namespace Microsoft::Console::Render; static constexpr size_t DefaultBgIndex{ 16 }; static constexpr size_t DefaultFgIndex{ 17 }; Viewport Terminal::GetViewport() noexcept { return _GetVisibleViewport(); } COORD Terminal::GetTextBufferEndPosition() const noexcept { // We use the end line of mutableViewport as the end // of the text buffer, it always moves with the written // text COORD endPosition{ _GetMutableViewport().Width() - 1, gsl::narrow(ViewEndIndex()) }; return endPosition; } const TextBuffer& Terminal::GetTextBuffer() noexcept { return *_buffer; } const FontInfo& Terminal::GetFontInfo() noexcept { return _fontInfo; } void Terminal::SetFontInfo(const FontInfo& fontInfo) { _fontInfo = fontInfo; } const TextAttribute Terminal::GetDefaultBrushColors() noexcept { return TextAttribute{}; } std::pair Terminal::GetAttributeColors(const TextAttribute& attr) const noexcept { std::pair colors; _blinkingState.RecordBlinkingUsage(attr); const auto fgTextColor = attr.GetForeground(); const auto bgTextColor = attr.GetBackground(); // We want to nudge the foreground color to make it more perceivable only for the // default color pairs within the color table if (_adjustIndistinguishableColors && !(attr.IsFaint() || (attr.IsBlinking() && _blinkingState.IsBlinkingFaint())) && (fgTextColor.IsDefault() || fgTextColor.IsLegacy()) && (bgTextColor.IsDefault() || bgTextColor.IsLegacy())) { const auto bgIndex = bgTextColor.IsDefault() ? DefaultBgIndex : bgTextColor.GetIndex(); auto fgIndex = fgTextColor.IsDefault() ? DefaultFgIndex : fgTextColor.GetIndex(); if (fgTextColor.IsIndex16() && (fgIndex < 8) && attr.IsBold() && _intenseIsBright) { // There is a special case for bold here - we need to get the bright version of the foreground color fgIndex += 8; } if (attr.IsReverseVideo() ^ _screenReversed) { colors.first = _adjustedForegroundColors[fgIndex][bgIndex]; colors.second = fgTextColor.GetColor(_colorTable, _defaultFg); } else { colors.first = _adjustedForegroundColors[bgIndex][fgIndex]; colors.second = bgTextColor.GetColor(_colorTable, _defaultBg); } } else { colors = attr.CalculateRgbColors(_colorTable, _defaultFg, _defaultBg, _screenReversed, _blinkingState.IsBlinkingFaint(), _intenseIsBright); } colors.first |= 0xff000000; // We only care about alpha for the default BG (which enables acrylic) // If the bg isn't the default bg color, or reverse video is enabled, make it fully opaque. if (!attr.BackgroundIsDefault() || (attr.IsReverseVideo() ^ _screenReversed)) { colors.second |= 0xff000000; } return colors; } COORD Terminal::GetCursorPosition() const noexcept { const auto& cursor = _buffer->GetCursor(); return cursor.GetPosition(); } bool Terminal::IsCursorVisible() const noexcept { const auto& cursor = _buffer->GetCursor(); return cursor.IsVisible() && !cursor.IsPopupShown(); } bool Terminal::IsCursorOn() const noexcept { const auto& cursor = _buffer->GetCursor(); return cursor.IsOn(); } ULONG Terminal::GetCursorPixelWidth() const noexcept { return 1; } ULONG Terminal::GetCursorHeight() const noexcept { return _buffer->GetCursor().GetSize(); } CursorType Terminal::GetCursorStyle() const noexcept { return _buffer->GetCursor().GetType(); } COLORREF Terminal::GetCursorColor() const noexcept { return _buffer->GetCursor().GetColor(); } bool Terminal::IsCursorDoubleWidth() const { const auto position = _buffer->GetCursor().GetPosition(); TextBufferTextIterator it(TextBufferCellIterator(*_buffer, position)); return IsGlyphFullWidth(*it); } const std::vector Terminal::GetOverlays() const noexcept { return {}; } const bool Terminal::IsGridLineDrawingAllowed() noexcept { return true; } const std::wstring Microsoft::Terminal::Core::Terminal::GetHyperlinkUri(uint16_t id) const noexcept { return _buffer->GetHyperlinkUriFromId(id); } const std::wstring Microsoft::Terminal::Core::Terminal::GetHyperlinkCustomId(uint16_t id) const noexcept { return _buffer->GetCustomIdFromId(id); } // Method Description: // - Gets the regex pattern ids of a location // Arguments: // - The location // Return value: // - The pattern IDs of the location const std::vector Terminal::GetPatternId(const COORD location) const noexcept { // Look through our interval tree for this location const auto intervals = _patternIntervalTree.findOverlapping(COORD{ location.X + 1, location.Y }, location); if (intervals.size() == 0) { return {}; } else { std::vector result{}; for (const auto& interval : intervals) { result.emplace_back(interval.value); } return result; } return {}; } std::vector Terminal::GetSelectionRects() noexcept try { std::vector result; for (const auto& lineRect : _GetSelectionRects()) { result.emplace_back(Viewport::FromInclusive(lineRect)); } return result; } catch (...) { LOG_CAUGHT_EXCEPTION(); return {}; } void Terminal::SelectNewRegion(const COORD coordStart, const COORD coordEnd) { #pragma warning(push) #pragma warning(disable : 26496) // cpp core checks wants these const, but they're decremented below. COORD realCoordStart = coordStart; COORD realCoordEnd = coordEnd; #pragma warning(pop) bool notifyScrollChange = false; if (coordStart.Y < _VisibleStartIndex()) { // recalculate the scrollOffset _scrollOffset = ViewStartIndex() - coordStart.Y; notifyScrollChange = true; } else if (coordEnd.Y > _VisibleEndIndex()) { // recalculate the scrollOffset, note that if the found text is // beneath the current visible viewport, it may be within the // current mutableViewport and the scrollOffset will be smaller // than 0 _scrollOffset = std::max(0, ViewStartIndex() - coordStart.Y); notifyScrollChange = true; } if (notifyScrollChange) { _buffer->GetRenderTarget().TriggerScroll(); _NotifyScrollEvent(); } realCoordStart.Y -= gsl::narrow(_VisibleStartIndex()); realCoordEnd.Y -= gsl::narrow(_VisibleStartIndex()); SetSelectionAnchor(realCoordStart); SetSelectionEnd(realCoordEnd, SelectionExpansion::Char); } const std::wstring_view Terminal::GetConsoleTitle() const noexcept try { if (_title.has_value()) { return _title.value(); } return _startingTitle; } catch (...) { LOG_CAUGHT_EXCEPTION(); return {}; } // Method Description: // - Lock the terminal for reading the contents of the buffer. Ensures that the // contents of the terminal won't be changed in the middle of a paint // operation. // Callers should make sure to also call Terminal::UnlockConsole once // they're done with any querying they need to do. void Terminal::LockConsole() noexcept { _readWriteLock.lock(); #ifndef NDEBUG _lastLocker = GetCurrentThreadId(); #endif } // Method Description: // - Unlocks the terminal after a call to Terminal::LockConsole. void Terminal::UnlockConsole() noexcept { _readWriteLock.unlock(); } // Method Description: // - Returns whether the screen is inverted; // Return Value: // - false. bool Terminal::IsScreenReversed() const noexcept { return _screenReversed; } const bool Terminal::IsUiaDataInitialized() const noexcept { // GH#11135: Windows Terminal needs to create and return an automation peer // when a screen reader requests it. However, the terminal might not be fully // initialized yet. So we use this to check if any crucial components of // UiaData are not yet initialized. return !!_buffer; } // Method Description: // - Creates the adjusted color array, which contains the possible foreground colors, // adjusted for perceivability // - The adjusted color array is 2-d, and effectively maps a background and foreground // color pair to the adjusted foreground for that color pair void Terminal::_MakeAdjustedColorArray() { // The color table has 16 colors, but the adjusted color table needs to be 18 // to include the default background and default foreground colors std::array colorTableWithDefaults; std::copy_n(std::begin(_colorTable), 16, std::begin(colorTableWithDefaults)); colorTableWithDefaults[DefaultBgIndex] = _defaultBg; colorTableWithDefaults[DefaultFgIndex] = _defaultFg; for (auto fgIndex = 0; fgIndex < 18; ++fgIndex) { const auto fg = til::at(colorTableWithDefaults, fgIndex); for (auto bgIndex = 0; bgIndex < 18; ++bgIndex) { if (fgIndex == bgIndex) { _adjustedForegroundColors[bgIndex][fgIndex] = fg; } else { const auto bg = til::at(colorTableWithDefaults, bgIndex); _adjustedForegroundColors[bgIndex][fgIndex] = ColorFix::GetPerceivableColor(fg, bg); } } } }