1010
1111#include < DefaultSettings.h>
1212#include < unicode.hpp>
13+ #include < utils.hpp>
1314#include < WinUser.h>
1415#include < LibraryResources.h>
1516
1617#include " EventArgs.h"
17- #include " ../../types/inc/GlyphWidth.hpp"
1818#include " ../../buffer/out/search.h"
1919#include " ../../renderer/atlas/AtlasEngine.h"
2020#include " ../../renderer/dx/DxRenderer.hpp"
@@ -443,6 +443,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
443443 // - <none>
444444 void ControlCore::_sendInputToConnection (std::wstring_view wstr)
445445 {
446+ if (wstr.empty ())
447+ {
448+ return ;
449+ }
450+
451+ // The connection may call functions like WriteFile() which may block indefinitely.
452+ // It's important we don't hold any mutexes across such calls.
453+ _terminal->_assertUnlocked ();
454+
446455 if (_isReadOnly)
447456 {
448457 _raiseReadOnlyWarning ();
@@ -492,8 +501,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
492501 _handleControlC ();
493502 }
494503
495- const auto lock = _terminal->LockForWriting ();
496- return _terminal->SendCharEvent (ch, scanCode, modifiers);
504+ TerminalInput::OutputType out;
505+ {
506+ const auto lock = _terminal->LockForReading ();
507+ out = _terminal->SendCharEvent (ch, scanCode, modifiers);
508+ }
509+ if (out)
510+ {
511+ _sendInputToConnection (*out);
512+ return true ;
513+ }
514+ return false ;
497515 }
498516
499517 void ControlCore::_handleControlC ()
@@ -602,46 +620,56 @@ namespace winrt::Microsoft::Terminal::Control::implementation
602620 const ControlKeyStates modifiers,
603621 const bool keyDown)
604622 {
605- const auto lock = _terminal->LockForWriting ();
623+ if (!vkey)
624+ {
625+ return true ;
626+ }
606627
607- // Update the selection, if it's present
608- // GH#8522, GH#3758 - Only modify the selection on key _down_. If we
609- // modify on key up, then there's chance that we'll immediately dismiss
610- // a selection created by an action bound to a keydown.
611- if (_shouldTryUpdateSelection (vkey) && keyDown)
628+ TerminalInput::OutputType out;
612629 {
613- // try to update the selection
614- if (const auto updateSlnParams{ _terminal->ConvertKeyEventToUpdateSelectionParams (modifiers, vkey) })
615- {
616- _terminal->UpdateSelection (updateSlnParams->first , updateSlnParams->second , modifiers);
617- _updateSelectionUI ();
618- return true ;
619- }
630+ const auto lock = _terminal->LockForWriting ();
620631
621- // GH#8791 - don't dismiss selection if Windows key was also pressed as a key-combination.
622- if (!modifiers.IsWinPressed ())
632+ // Update the selection, if it's present
633+ // GH#8522, GH#3758 - Only modify the selection on key _down_. If we
634+ // modify on key up, then there's chance that we'll immediately dismiss
635+ // a selection created by an action bound to a keydown.
636+ if (_shouldTryUpdateSelection (vkey) && keyDown)
623637 {
624- _terminal->ClearSelection ();
625- _updateSelectionUI ();
626- }
638+ // try to update the selection
639+ if (const auto updateSlnParams{ _terminal->ConvertKeyEventToUpdateSelectionParams (modifiers, vkey) })
640+ {
641+ _terminal->UpdateSelection (updateSlnParams->first , updateSlnParams->second , modifiers);
642+ _updateSelectionUI ();
643+ return true ;
644+ }
627645
628- // When there is a selection active, escape should clear it and NOT flow through
629- // to the terminal. With any other keypress, it should clear the selection AND
630- // flow through to the terminal.
631- if (vkey == VK_ESCAPE)
632- {
633- return true ;
646+ // GH#8791 - don't dismiss selection if Windows key was also pressed as a key-combination.
647+ if (!modifiers.IsWinPressed ())
648+ {
649+ _terminal->ClearSelection ();
650+ _updateSelectionUI ();
651+ }
652+
653+ // When there is a selection active, escape should clear it and NOT flow through
654+ // to the terminal. With any other keypress, it should clear the selection AND
655+ // flow through to the terminal.
656+ if (vkey == VK_ESCAPE)
657+ {
658+ return true ;
659+ }
634660 }
635- }
636661
637- // If the terminal translated the key, mark the event as handled.
638- // This will prevent the system from trying to get the character out
639- // of it and sending us a CharacterReceived event.
640- return vkey ? _terminal->SendKeyEvent (vkey,
641- scanCode,
642- modifiers,
643- keyDown) :
644- true ;
662+ // If the terminal translated the key, mark the event as handled.
663+ // This will prevent the system from trying to get the character out
664+ // of it and sending us a CharacterReceived event.
665+ out = _terminal->SendKeyEvent (vkey, scanCode, modifiers, keyDown);
666+ }
667+ if (out)
668+ {
669+ _sendInputToConnection (*out);
670+ return true ;
671+ }
672+ return false ;
645673 }
646674
647675 bool ControlCore::SendMouseEvent (const til::point viewportPos,
@@ -650,8 +678,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
650678 const short wheelDelta,
651679 const TerminalInput::MouseButtonState state)
652680 {
653- const auto lock = _terminal->LockForWriting ();
654- return _terminal->SendMouseEvent (viewportPos, uiButton, states, wheelDelta, state);
681+ TerminalInput::OutputType out;
682+ {
683+ const auto lock = _terminal->LockForReading ();
684+ out = _terminal->SendMouseEvent (viewportPos, uiButton, states, wheelDelta, state);
685+ }
686+ if (out)
687+ {
688+ _sendInputToConnection (*out);
689+ return true ;
690+ }
691+ return false ;
655692 }
656693
657694 void ControlCore::UserScrollViewport (const int viewTop)
@@ -1324,8 +1361,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation
13241361 // before sending it over the terminal's connection.
13251362 void ControlCore::PasteText (const winrt::hstring& hstr)
13261363 {
1364+ using namespace ::Microsoft::Console::Utils;
1365+
1366+ auto filtered = FilterStringForPaste (hstr, CarriageReturnNewline | ControlCodes);
1367+ if (BracketedPasteEnabled ())
1368+ {
1369+ filtered.insert (0 , L" \x1b [200~" );
1370+ filtered.append (L" \x1b [201~" );
1371+ }
1372+
1373+ // It's important to not hold the terminal lock while calling this function as sending the data may take a long time.
1374+ _sendInputToConnection (filtered);
1375+
13271376 const auto lock = _terminal->LockForWriting ();
1328- _terminal->WritePastedText (hstr);
13291377 _terminal->ClearSelection ();
13301378 _updateSelectionUI ();
13311379 _terminal->TrySnapOnInput ();
@@ -1894,17 +1942,27 @@ namespace winrt::Microsoft::Terminal::Control::implementation
18941942 const auto endPoint = goRight ? clampedClick : cursorPos;
18951943
18961944 const auto delta = _terminal->GetTextBuffer ().GetCellDistance (startPoint, endPoint);
1897-
18981945 const WORD key = goRight ? VK_RIGHT : VK_LEFT;
1946+
1947+ std::wstring buffer;
1948+ const auto append = [&](TerminalInput::OutputType&& out) {
1949+ if (out)
1950+ {
1951+ buffer.append (std::move (*out));
1952+ }
1953+ };
1954+
18991955 // Send an up and a down once per cell. This won't
19001956 // accurately handle wide characters, or continuation
19011957 // prompts, or cases where a single escape character in the
19021958 // command (e.g. ^[) takes up two cells.
19031959 for (size_t i = 0u ; i < delta; i++)
19041960 {
1905- _terminal->SendKeyEvent (key, 0 , {}, true );
1906- _terminal->SendKeyEvent (key, 0 , {}, false );
1961+ append ( _terminal->SendKeyEvent (key, 0 , {}, true ) );
1962+ append ( _terminal->SendKeyEvent (key, 0 , {}, false ) );
19071963 }
1964+
1965+ _sendInputToConnection (buffer);
19081966 }
19091967 }
19101968 }
@@ -1915,7 +1973,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
19151973 // - Updates the renderer's representation of the selection as well as the selection marker overlay in TermControl
19161974 void ControlCore::_updateSelectionUI ()
19171975 {
1918- const auto lock = _terminal->LockForWriting ();
19191976 _renderer->TriggerSelection ();
19201977 // only show the markers if we're doing a keyboard selection or in mark mode
19211978 const bool showMarkers{ _terminal->SelectionMode () >= ::Microsoft::Terminal::Core::Terminal::SelectionInteractionMode::Keyboard };
@@ -2247,14 +2304,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
22472304
22482305 void ControlCore::_focusChanged (bool focused)
22492306 {
2250- // GH#13461 - temporarily turn off read-only mode, send the focus event,
2251- // then turn it back on. Even in focus mode, focus events are fine to
2252- // send. We don't want to pop a warning every time the control is
2253- // focused.
2254- const auto previous = std::exchange (_isReadOnly, false );
2255- const auto restore = wil::scope_exit ([&]() { _isReadOnly = previous; });
2256- const auto lock = _terminal->LockForWriting ();
2257- _terminal->FocusChanged (focused);
2307+ TerminalInput::OutputType out;
2308+ {
2309+ const auto lock = _terminal->LockForReading ();
2310+ out = _terminal->FocusChanged (focused);
2311+ }
2312+ if (out && !out->empty ())
2313+ {
2314+ // _sendInputToConnection() asserts that we aren't in focus mode,
2315+ // but window focus events are always fine to send.
2316+ _connection.WriteInput (*out);
2317+ }
22582318 }
22592319
22602320 bool ControlCore::_isBackgroundTransparent ()
0 commit comments