Repository: microsoft/terminal
File: src/cascadia/TerminalControl/ControlCore.cpp (C++)

Code before:
        ::Search search(*GetUiaData(), text.c_str(), direction, sensitivity);
        auto lock = _terminal->LockForWriting();
        const auto foundMatch{ search.FindNext() };
        if (foundMatch)
        {
            _terminal->SetBlockSelection(false);
            search.Select();

            // this is used for search,
            // DO NOT call _updateSelectionUI() here.
            // We don't want to show the markers so manually tell it to clear it.
            _renderer->TriggerSelection();
            _UpdateSelectionMarkersHandlers(*this, winrt::make<implementation::UpdateSelectionMarkersEventArgs>(true));
        }

        // Raise a FoundMatch event, which the control will use to notify
        // narrator if there was any results in the buffer
        auto foundResults = winrt::make_self<implementation::FoundResultsArgs>(foundMatch);
        _FoundMatchHandlers(*this, *foundResults);
    }

    void ControlCore::Close()
    {
        if (!_IsClosing())
        {
            _closing = true;

            // Ensure Close() doesn't hang, waiting for MidiAudio to finish playing an hour long song.
            _midiAudio.BeginSkip();

            // Stop accepting new output and state changes before we disconnect everything.
            _connection.TerminalOutput(_connectionOutputEventToken);
            _connectionStateChangedRevoker.revoke();
            _connection.Close();
        }
    }

    void ControlCore::_rendererWarning(const HRESULT hr)
    {
        _RendererWarningHandlers(*this, winrt::make<RendererWarningArgs>(hr));
    }

    void ControlCore::_renderEngineSwapChainChanged(const HANDLE handle)
    {
        _SwapChainChangedHandlers(*this, winrt::box_value<uint64_t>(reinterpret_cast<uint64_t>(handle)));
    }

    void ControlCore::_rendererBackgroundColorChanged()
    {
        _BackgroundColorChangedHandlers(*this, nullptr);
    }

Review comment (bug):
This is what required larger changes to `MidiAudio`:
On shutdown we have to tell the VT output thread to skip all DECPS/MIDI sequences. `_getMidiAudio` uses no mutex to allow thread-safe access of `_midiAudio` from multiple threads. But luckily we can either rely on how it's only called when the console lock is held, or we simply add another mutex there.

But both options are bad IMO: We had a long history of deadlock in Windows Terminal due to the main thread acquiring the console lock. And technically we don't need one, since all we want to do is call `BeginSkip` which is already inherently thread-safe. I simply dropped the lazy creation of `MidiAudio` which now allows me to blindly call `BeginSkip` witout if conditions and checks. I think this makes the code simpler and more robust. But it requires us to pass the `HWND` on all calls now for the internal lazy creation of DirectSound types.

Another downside is that I had to sprinkle the DirectSound headers into a bunch of .cpp files so that the destructor compiles without "undefined type" errors. I tried declaring a custom `MidiAudio::~MidiAudio` destructor, but that didn't make the compiler treat the destructor as an "extern" symbol either. I don't get why `std::unique_ptr` allows such type erasure where the compiler doesn't need to know the destructor definition, but `Microsoft::Wrl` and `wil::com_ptr` can't do that...

Code after:
        if (foundMatch)
        {
            _terminal->SetBlockSelection(false);
            search.Select();

            // this is used for search,
            // DO NOT call _updateSelectionUI() here.
            // We don't want to show the markers so manually tell it to clear it.
            _renderer->TriggerSelection();
            _UpdateSelectionMarkersHandlers(*this, winrt::make<implementation::UpdateSelectionMarkersEventArgs>(true));
        }

        // Raise a FoundMatch event, which the control will use to notify
        // narrator if there was any results in the buffer
        auto foundResults = winrt::make_self<implementation::FoundResultsArgs>(foundMatch);
        _FoundMatchHandlers(*this, *foundResults);
    }

    void ControlCore::Close()
    {
        if (!_IsClosing())
        {
            _closing = true;

            // Ensure Close() doesn't hang, waiting for MidiAudio to finish playing an hour long song.
            _midiAudio.BeginSkip();

            // Stop accepting new output and state changes before we disconnect everything.
            _connection.TerminalOutput(_connectionOutputEventToken);
            _connectionStateChangedRevoker.revoke();
            _connection.Close();
        }
    }

    void ControlCore::_rendererWarning(const HRESULT hr)
    {
        _RendererWarningHandlers(*this, winrt::make<RendererWarningArgs>(hr));
    }

    void ControlCore::_renderEngineSwapChainChanged(const HANDLE handle)
    {
        _SwapChainChangedHandlers(*this, winrt::box_value<uint64_t>(reinterpret_cast<uint64_t>(handle)));
    }

    void ControlCore::_rendererBackgroundColorChanged()
    {
        _BackgroundColorChangedHandlers(*this, nullptr);
    }

    void ControlCore::_rendererTabColorChanged()
    {