diff --git a/src/cascadia/TerminalControl/HwndTerminal.cpp b/src/cascadia/TerminalControl/HwndTerminal.cpp index 5086515e975..4b8c345baac 100644 --- a/src/cascadia/TerminalControl/HwndTerminal.cpp +++ b/src/cascadia/TerminalControl/HwndTerminal.cpp @@ -882,7 +882,7 @@ void _stdcall TerminalSetTheme(void* terminal, TerminalTheme theme, LPCWSTR font renderSettings.SetColorTableEntry(TextColor::DEFAULT_FOREGROUND, theme.DefaultForeground); renderSettings.SetColorTableEntry(TextColor::DEFAULT_BACKGROUND, theme.DefaultBackground); - publicTerminal->_renderEngine->SetSelectionBackground(theme.DefaultSelectionBackground, theme.SelectionBackgroundAlpha); + publicTerminal->_renderEngine->SetSelectionBackground(theme.DefaultSelectionBackground); // Set the font colors for (size_t tableIndex = 0; tableIndex < 16; tableIndex++) diff --git a/src/cascadia/TerminalControl/HwndTerminal.hpp b/src/cascadia/TerminalControl/HwndTerminal.hpp index e561f1793fc..9a19b6090db 100644 --- a/src/cascadia/TerminalControl/HwndTerminal.hpp +++ b/src/cascadia/TerminalControl/HwndTerminal.hpp @@ -35,7 +35,6 @@ typedef struct _TerminalTheme COLORREF DefaultBackground; COLORREF DefaultForeground; COLORREF DefaultSelectionBackground; - float SelectionBackgroundAlpha; uint32_t CursorStyle; // This will be converted to DispatchTypes::CursorStyle (size_t), but C# cannot marshal an enum type and have it fit in a size_t. COLORREF ColorTable[16]; } TerminalTheme, *LPTerminalTheme; diff --git a/src/cascadia/WpfTerminalControl/TerminalTheme.cs b/src/cascadia/WpfTerminalControl/TerminalTheme.cs index 89e2ada53a4..699d1665609 100644 --- a/src/cascadia/WpfTerminalControl/TerminalTheme.cs +++ b/src/cascadia/WpfTerminalControl/TerminalTheme.cs @@ -70,11 +70,6 @@ public struct TerminalTheme /// public uint DefaultSelectionBackground; - /// - /// The opacity alpha for the selection color of the terminal, must be between 1.0 and 0.0. - /// - public float SelectionBackgroundAlpha; - /// /// The style of cursor to use in the terminal. /// diff --git a/src/cascadia/WpfTerminalTestNetCore/MainWindow.xaml.cs b/src/cascadia/WpfTerminalTestNetCore/MainWindow.xaml.cs index 1389e542913..e67b1280319 100644 --- a/src/cascadia/WpfTerminalTestNetCore/MainWindow.xaml.cs +++ b/src/cascadia/WpfTerminalTestNetCore/MainWindow.xaml.cs @@ -97,7 +97,6 @@ private void Terminal_Loaded(object sender, RoutedEventArgs e) DefaultBackground = 0x0c0c0c, DefaultForeground = 0xcccccc, DefaultSelectionBackground = 0xcccccc, - SelectionBackgroundAlpha = 0.5f, CursorStyle = CursorStyle.BlinkingBar, // This is Campbell. ColorTable = new uint[] { 0x0C0C0C, 0x1F0FC5, 0x0EA113, 0x009CC1, 0xDA3700, 0x981788, 0xDD963A, 0xCCCCCC, 0x767676, 0x5648E7, 0x0CC616, 0xA5F1F9, 0xFF783B, 0x9E00B4, 0xD6D661, 0xF2F2F2 }, diff --git a/src/renderer/atlas/AtlasEngine.api.cpp b/src/renderer/atlas/AtlasEngine.api.cpp index 1850cf0cab1..f5ddc5a000d 100644 --- a/src/renderer/atlas/AtlasEngine.api.cpp +++ b/src/renderer/atlas/AtlasEngine.api.cpp @@ -7,6 +7,7 @@ #include "Backend.h" #include "../../buffer/out/textBuffer.hpp" #include "../base/FontCache.h" +#include "../../types/inc/ColorFix.hpp" // #### NOTE #### // If you see any code in here that contains "_r." you might be seeing a race condition. @@ -424,12 +425,15 @@ void AtlasEngine::SetRetroTerminalEffect(bool enable) noexcept } } -void AtlasEngine::SetSelectionBackground(const COLORREF color, const float alpha) noexcept +void AtlasEngine::SetSelectionBackground(const COLORREF color) noexcept { - const u32 selectionColor = (color & 0xffffff) | gsl::narrow_cast(lrintf(alpha * 255.0f)) << 24; + const u32 selectionColor = (color & 0xffffff) | 0xff000000; if (_api.s->misc->selectionColor != selectionColor) { - _api.s.write()->misc.write()->selectionColor = selectionColor; + auto misc = _api.s.write()->misc.write(); + misc->selectionColor = selectionColor; + // Selection Foreground is based on the default foreground; it is also updated in UpdateDrawingBrushes + misc->selectionForeground = 0xff000000 | ColorFix::GetPerceivableColor(misc->foregroundColor, color, 0.5f * 0.5f); } } diff --git a/src/renderer/atlas/AtlasEngine.cpp b/src/renderer/atlas/AtlasEngine.cpp index 0f80bceb787..6aa8bff265a 100644 --- a/src/renderer/atlas/AtlasEngine.cpp +++ b/src/renderer/atlas/AtlasEngine.cpp @@ -11,6 +11,8 @@ #include "DWriteTextAnalysis.h" #include "../../interactivity/win32/CustomWindowMessages.h" +#include "../types/inc/ColorFix.hpp" + // #### NOTE #### // This file should only contain methods that are only accessed by the caller of Present() (the "Renderer" class). // Basically this file poses the "synchronization" point between the concurrently running @@ -311,6 +313,8 @@ CATCH_RETURN() _api.searchHighlightFocused = { info.searchHighlightFocused, 1 }; } } + + _api.selectionSpans = til::point_span_subspan_within_rect(info.selectionSpans, dr); } return S_OK; @@ -411,9 +415,10 @@ try const auto end = isFinalRow ? std::min(hiEnd.x + 1, x2) : x2; _fillColorBitmap(row, x1, end, fgColor, bgColor); - // Return early if we couldn't paint the whole region. We will resume - // from here in the next call. - if (!isFinalRow || end == x2) + // Return early if we couldn't paint the whole region (either this was not the last row, or + // it was the last row but the highlight ends outside of our x range.) + // We will resume from here in the next call. + if (!isFinalRow || hiEnd.x /*inclusive*/ >= x2 /*exclusive*/) { return S_OK; } @@ -497,6 +502,7 @@ try // Apply the highlighting colors to the highlighted cells RETURN_IF_FAILED(_drawHighlighted(_api.searchHighlights, y, x, columnEnd, highlightFg, highlightBg)); RETURN_IF_FAILED(_drawHighlighted(_api.searchHighlightFocused, y, x, columnEnd, highlightFocusFg, highlightFocusBg)); + RETURN_IF_FAILED(_drawHighlighted(_api.selectionSpans, y, x, columnEnd, _api.s->misc->selectionForeground, _api.s->misc->selectionColor)); _api.lastPaintBufferLineCoord = { x, y }; return S_OK; @@ -563,28 +569,9 @@ try CATCH_RETURN() [[nodiscard]] HRESULT AtlasEngine::PaintSelection(const til::rect& rect) noexcept -try { - // Unfortunately there's no step after Renderer::_PaintBufferOutput that - // would inform us that it's done with the last AtlasEngine::PaintBufferLine. - // As such we got to call _flushBufferLine() here just to be sure. - _flushBufferLine(); - - const auto y = gsl::narrow_cast(clamp(rect.top, 0, _p.s->viewportCellCount.y - 1)); - const auto from = gsl::narrow_cast(clamp(rect.left, 0, _p.s->viewportCellCount.x - 1)); - const auto to = gsl::narrow_cast(clamp(rect.right, from, _p.s->viewportCellCount.x)); - - auto& row = *_p.rows[y]; - row.selectionFrom = from; - row.selectionTo = to; - - _p.dirtyRectInPx.left = std::min(_p.dirtyRectInPx.left, from * _p.s->font->cellSize.x); - _p.dirtyRectInPx.top = std::min(_p.dirtyRectInPx.top, y * _p.s->font->cellSize.y); - _p.dirtyRectInPx.right = std::max(_p.dirtyRectInPx.right, to * _p.s->font->cellSize.x); - _p.dirtyRectInPx.bottom = std::max(_p.dirtyRectInPx.bottom, _p.dirtyRectInPx.top + _p.s->font->cellSize.y); return S_OK; } -CATCH_RETURN() [[nodiscard]] HRESULT AtlasEngine::PaintCursor(const CursorOptions& options) noexcept try @@ -661,10 +648,21 @@ try _api.currentForeground = gsl::narrow_cast(fg); _api.attributes = attributes; } - else if (textAttributes.BackgroundIsDefault() && bg != _api.s->misc->backgroundColor) + else { - _api.s.write()->misc.write()->backgroundColor = bg; - _p.s.write()->misc.write()->backgroundColor = bg; + if (textAttributes.BackgroundIsDefault() && bg != _api.s->misc->backgroundColor) + { + _api.s.write()->misc.write()->backgroundColor = bg; + _p.s.write()->misc.write()->backgroundColor = bg; + } + + if (textAttributes.GetForeground().IsDefault() && fg != _api.s->misc->foregroundColor) + { + auto misc = _api.s.write()->misc.write(); + misc->foregroundColor = fg; + // Selection Foreground is based on the default foreground; it is also updated in SetSelectionColor + misc->selectionForeground = 0xff000000 | ColorFix::GetPerceivableColor(fg, misc->selectionColor, 0.5f * 0.5f); + } } return S_OK; diff --git a/src/renderer/atlas/AtlasEngine.h b/src/renderer/atlas/AtlasEngine.h index 84ac6f601c6..b20bd04f4c2 100644 --- a/src/renderer/atlas/AtlasEngine.h +++ b/src/renderer/atlas/AtlasEngine.h @@ -71,7 +71,7 @@ namespace Microsoft::Console::Render::Atlas void SetPixelShaderPath(std::wstring_view value) noexcept; void SetPixelShaderImagePath(std::wstring_view value) noexcept; void SetRetroTerminalEffect(bool enable) noexcept; - void SetSelectionBackground(COLORREF color, float alpha = 0.5f) noexcept; + void SetSelectionBackground(COLORREF color) noexcept; void SetSoftwareRendering(bool enable) noexcept; void SetDisablePartialInvalidation(bool enable) noexcept; void SetGraphicsAPI(GraphicsAPI graphicsAPI) noexcept; @@ -171,6 +171,7 @@ namespace Microsoft::Console::Render::Atlas // These tracks the highlighted regions on the screen that are yet to be painted. std::span searchHighlights; std::span searchHighlightFocused; + std::span selectionSpans; // dirtyRect is a computed value based on invalidatedRows. til::rect dirtyRect; diff --git a/src/renderer/atlas/BackendD2D.cpp b/src/renderer/atlas/BackendD2D.cpp index 8a7861ecd9b..c05dd1f21e3 100644 --- a/src/renderer/atlas/BackendD2D.cpp +++ b/src/renderer/atlas/BackendD2D.cpp @@ -51,7 +51,6 @@ void BackendD2D::Render(RenderingPayload& p) _drawCursorPart1(p); _drawText(p); _drawCursorPart2(p); - _drawSelection(p); #if ATLAS_DEBUG_SHOW_DIRTY _debugShowDirty(p); #endif @@ -938,26 +937,6 @@ void BackendD2D::_drawCursor(const RenderingPayload& p, ID2D1RenderTarget* rende } } -void BackendD2D::_drawSelection(const RenderingPayload& p) -{ - u16 y = 0; - for (const auto& row : p.rows) - { - if (row->selectionTo > row->selectionFrom) - { - const D2D1_RECT_F rect{ - static_cast(p.s->font->cellSize.x * row->selectionFrom), - static_cast(p.s->font->cellSize.y * y), - static_cast(p.s->font->cellSize.x * row->selectionTo), - static_cast(p.s->font->cellSize.y * (y + 1)), - }; - _fillRectangle(rect, p.s->misc->selectionColor); - } - - y++; - } -} - #if ATLAS_DEBUG_SHOW_DIRTY void BackendD2D::_debugShowDirty(const RenderingPayload& p) { diff --git a/src/renderer/atlas/BackendD3D.cpp b/src/renderer/atlas/BackendD3D.cpp index 2e02e342863..6d832ae0d75 100644 --- a/src/renderer/atlas/BackendD3D.cpp +++ b/src/renderer/atlas/BackendD3D.cpp @@ -229,7 +229,6 @@ void BackendD3D::Render(RenderingPayload& p) _drawBackground(p); _drawCursorBackground(p); _drawText(p); - _drawSelection(p); _debugShowDirty(p); _flushQuads(p); @@ -2248,45 +2247,6 @@ size_t BackendD3D::_drawCursorForegroundSlowPath(const CursorRect& c, size_t off return addedInstances; } -void BackendD3D::_drawSelection(const RenderingPayload& p) -{ - u16 y = 0; - u16 lastFrom = 0; - u16 lastTo = 0; - - for (const auto& row : p.rows) - { - if (row->selectionTo > row->selectionFrom) - { - // If the current selection line matches the previous one, we can just extend the previous quad downwards. - // The way this is implemented isn't very smart, but we also don't have very many rows to iterate through. - if (row->selectionFrom == lastFrom && row->selectionTo == lastTo) - { - _getLastQuad().size.y += p.s->font->cellSize.y; - } - else - { - _appendQuad() = { - .shadingType = static_cast(ShadingType::Selection), - .position = { - static_cast(p.s->font->cellSize.x * row->selectionFrom), - static_cast(p.s->font->cellSize.y * y), - }, - .size = { - static_cast(p.s->font->cellSize.x * (row->selectionTo - row->selectionFrom)), - static_cast(p.s->font->cellSize.y), - }, - .color = p.s->misc->selectionColor, - }; - lastFrom = row->selectionFrom; - lastTo = row->selectionTo; - } - } - - y++; - } -} - void BackendD3D::_debugShowDirty(const RenderingPayload& p) { #if ATLAS_DEBUG_SHOW_DIRTY @@ -2302,7 +2262,7 @@ void BackendD3D::_debugShowDirty(const RenderingPayload& p) { const auto& rect = _presentRects[(_presentRectsPos + i) % std::size(_presentRects)]; _appendQuad() = { - .shadingType = static_cast(ShadingType::Selection), + .shadingType = static_cast(ShadingType::FilledRect), .position = { static_cast(rect.left), static_cast(rect.top), diff --git a/src/renderer/atlas/BackendD3D.h b/src/renderer/atlas/BackendD3D.h index cb1bfcccea4..962d691b01d 100644 --- a/src/renderer/atlas/BackendD3D.h +++ b/src/renderer/atlas/BackendD3D.h @@ -76,7 +76,7 @@ namespace Microsoft::Console::Render::Atlas SolidLine, Cursor, - Selection, + FilledRect, TextDrawingFirst = TextGrayscale, TextDrawingLast = SolidLine, diff --git a/src/renderer/atlas/common.h b/src/renderer/atlas/common.h index 56cc1fce139..3f70ae6748e 100644 --- a/src/renderer/atlas/common.h +++ b/src/renderer/atlas/common.h @@ -393,7 +393,9 @@ namespace Microsoft::Console::Render::Atlas struct MiscellaneousSettings { u32 backgroundColor = 0; - u32 selectionColor = 0x7fffffff; + u32 foregroundColor = 0; + u32 selectionColor = 0xffffffff; + u32 selectionForeground = 0xff000000; std::wstring customPixelShaderPath; std::wstring customPixelShaderImagePath; bool useRetroTerminalEffect = false; @@ -475,8 +477,6 @@ namespace Microsoft::Console::Render::Atlas bitmap.active = false; gridLineRanges.clear(); lineRendition = LineRendition::SingleWidth; - selectionFrom = 0; - selectionTo = 0; dirtyTop = y * cellHeight; dirtyBottom = dirtyTop + cellHeight; } @@ -496,8 +496,6 @@ namespace Microsoft::Console::Render::Atlas Bitmap bitmap; std::vector gridLineRanges; LineRendition lineRendition = LineRendition::SingleWidth; - u16 selectionFrom = 0; - u16 selectionTo = 0; til::CoordType dirtyTop = 0; til::CoordType dirtyBottom = 0; }; diff --git a/src/renderer/atlas/shader_common.hlsl b/src/renderer/atlas/shader_common.hlsl index 51eb524b86b..18268407f26 100644 --- a/src/renderer/atlas/shader_common.hlsl +++ b/src/renderer/atlas/shader_common.hlsl @@ -16,7 +16,7 @@ #define SHADING_TYPE_CURLY_LINE 7 #define SHADING_TYPE_SOLID_LINE 8 #define SHADING_TYPE_CURSOR 9 -#define SHADING_TYPE_SELECTION 10 +#define SHADING_TYPE_FILLED_RECT 10 struct VSData {