Repository: microsoft/terminal
File: src/renderer/atlas/AtlasEngine.cpp (C++)

Code before:
        // the dirty region to be relative to the buffer origin
        const til::CoordType offsetX = _api.viewportOffset.x;
        const til::CoordType offsetY = _api.viewportOffset.y;
        const til::point bufferOrigin{ -offsetX, -offsetY };
        const auto dr = _api.dirtyRect.to_origin(bufferOrigin);

        _api.searchHighlights = til::point_span_subspan_within_rect(info.searchHighlights, dr);

        // do the same for the focused search highlight
        if (info.searchHighlightFocused)
        {
            const auto focusedStartY = info.searchHighlightFocused->start.y;
            const auto focusedEndY = info.searchHighlightFocused->end.y;
            const auto isFocusedInside = (focusedStartY >= dr.top && focusedStartY < dr.bottom) || (focusedEndY >= dr.top && focusedEndY < dr.bottom) || (focusedStartY < dr.top && focusedEndY >= dr.bottom);
            if (isFocusedInside)
            {
                _api.searchHighlightFocused = { info.searchHighlightFocused, 1 };
            }
        }

        _api.selectionSpans = til::point_span_subspan_within_rect(info.selectionSpans, dr);

        u32 newSelectionColor{ static_cast<COLORREF>(info.selectionBackground) | 0xff000000 };
        if (_api.s->misc->selectionColor != newSelectionColor)
        {
            auto misc = _api.s.write()->misc.write();
            misc->selectionColor = newSelectionColor;
            // Select a black or white foreground based on the perceptual lightness of the background.
            misc->selectionForeground = ColorFix::GetLuminosity(newSelectionColor) < 0.5f ? 0xffffffff : 0xff000000;
        }
    }

    return S_OK;
}

[[nodiscard]] HRESULT AtlasEngine::ResetLineTransform() noexcept
{
    return S_OK;
}

[[nodiscard]] HRESULT AtlasEngine::PrepareLineTransform(const LineRendition lineRendition, const til::CoordType targetRow, const til::CoordType viewportLeft) noexcept
{
    const auto y = gsl::narrow_cast<u16>(clamp<til::CoordType>(targetRow, 0, _p.s->viewportCellCount.y - 1));
    _p.rows[y]->lineRendition = lineRendition;
    _api.lineRendition = lineRendition;
    return S_OK;
}

[[nodiscard]] HRESULT AtlasEngine::PaintBackground() noexcept
{
    return S_OK;

Review comment (suggestion):
Copying from `_api.s` to `_p.s` happens in `StartPaint()`. Similar to `AtlasEngine::PaintCursor` you need to update both structs simultaneously to keep their generation count in sync.

You can test that this by setting a breakpoint into `AtlasEngine::_handleSettingsUpdate`. Then and emit an OSC 17. If done correctly, `AtlasEngine::_handleSettingsUpdate` should not get called.

Code after:
        // the dirty region to be relative to the buffer origin
        const til::CoordType offsetX = _api.viewportOffset.x;
        const til::CoordType offsetY = _api.viewportOffset.y;
        const til::point bufferOrigin{ -offsetX, -offsetY };
        const auto dr = _api.dirtyRect.to_origin(bufferOrigin);

        _api.searchHighlights = til::point_span_subspan_within_rect(info.searchHighlights, dr);

        // do the same for the focused search highlight
        if (info.searchHighlightFocused)
        {
            const auto focusedStartY = info.searchHighlightFocused->start.y;
            const auto focusedEndY = info.searchHighlightFocused->end.y;
            const auto isFocusedInside = (focusedStartY >= dr.top && focusedStartY < dr.bottom) || (focusedEndY >= dr.top && focusedEndY < dr.bottom) || (focusedStartY < dr.top && focusedEndY >= dr.bottom);
            if (isFocusedInside)
            {
                _api.searchHighlightFocused = { info.searchHighlightFocused, 1 };
            }
        }

        _api.selectionSpans = til::point_span_subspan_within_rect(info.selectionSpans, dr);

        const u32 newSelectionColor{ static_cast<COLORREF>(info.selectionBackground) | 0xff000000 };
        if (_api.s->misc->selectionColor != newSelectionColor)
        {
            auto misc = _api.s.write()->misc.write();
            misc->selectionColor = newSelectionColor;
            // Select a black or white foreground based on the perceptual lightness of the background.
            misc->selectionForeground = ColorFix::GetLuminosity(newSelectionColor) < 0.5f ? 0xffffffff : 0xff000000;

            // We copied the selection colors into _p during StartPaint, which happened just before PrepareRenderInfo
            // This keeps their generations in sync.
            auto pm = _p.s.write()->misc.write();
            pm->selectionColor = misc->selectionColor;
            pm->selectionForeground = misc->selectionForeground;
        }
    }

    return S_OK;
}

[[nodiscard]] HRESULT AtlasEngine::ResetLineTransform() noexcept
{
    return S_OK;
}

[[nodiscard]] HRESULT AtlasEngine::PrepareLineTransform(const LineRendition lineRendition, const til::CoordType targetRow, const til::CoordType viewportLeft) noexcept
{
    const auto y = gsl::narrow_cast<u16>(clamp<til::CoordType>(targetRow, 0, _p.s->viewportCellCount.y - 1));
    _p.rows[y]->lineRendition = lineRendition;
    _api.lineRendition = lineRendition;