text: // Copyright (c) Microsoft Corporation.
  // Licensed under the MIT license.
  
  #include "pch.h"
  #include "ControlInteractivity.h"
  #include <argb.h>
  #include <DefaultSettings.h>
  #include <unicode.hpp>
  #include <Utf16Parser.hpp>
  #include <Utils.h>
  #include <LibraryResources.h>
  #include "../../types/inc/GlyphWidth.hpp"
  #include "../../types/inc/Utils.hpp"
  #include "../../buffer/out/search.h"
  
  #include "InteractivityAutomationPeer.h"
  
  #include "ControlInteractivity.g.cpp"
  #include "TermControl.h"
  
  using namespace ::Microsoft::Console::Types;
  using namespace ::Microsoft::Console::VirtualTerminal;
  using namespace ::Microsoft::Terminal::Core;
  using namespace winrt::Windows::Graphics::Display;
  using namespace winrt::Windows::System;
  using namespace winrt::Windows::ApplicationModel::DataTransfer;
  
  static constexpr unsigned int MAX_CLICK_COUNT = 3;
  
  namespace winrt::Microsoft::Terminal::Control::implementation
  {
      static constexpr TerminalInput::MouseButtonState toInternalMouseState(const Control::MouseButtonState& state)
      {
          return TerminalInput::MouseButtonState{
              WI_IsFlagSet(state, MouseButtonState::IsLeftButtonDown),
              WI_IsFlagSet(state, MouseButtonState::IsMiddleButtonDown),
              WI_IsFlagSet(state, MouseButtonState::IsRightButtonDown)
          };
      }
  
      ControlInteractivity::ControlInteractivity(IControlSettings settings,
                                                 Control::IControlAppearance unfocusedAppearance,
                                                 TerminalConnection::ITerminalConnection connection) :
          _touchAnchor{ std::nullopt },
          _lastMouseClickTimestamp{},
          _lastMouseClickPos{},
          _selectionNeedsToBeCopied{ false }
      {
          _core = winrt::make_self<ControlCore>(settings, unfocusedAppearance, connection);
      }
  
      // Method Description:
      // - Updates our internal settings. These settings should be
      //   interactivity-specific. Right now, we primarily update _rowsToScroll
      //   with the current value of SPI_GETWHEELSCROLLLINES.
      // Arguments:
      // - <none>
      // Return Value:
      // - <none>
      void ControlInteractivity::UpdateSettings()
      {
          _updateSystemParameterSettings();
      }
  
      void ControlInteractivity::Initialize()
      {
          _updateSystemParameterSettings();
  
          // import value from WinUser (convert from milli-seconds to micro-seconds)
          _multiClickTimer = GetDoubleClickTime() * 1000;
      }
  
      Control::ControlCore ControlInteractivity::Core()
      {
          return *_core;
      }
  
      // Method Description:
      // - Returns the number of clicks that occurred (double and triple click support).
      // Every call to this function registers a click.
      // Arguments:
      // - clickPos: the (x,y) position of a given cursor (i.e.: mouse cursor).
      //    NOTE: origin (0,0) is top-left.
      // - clickTime: the timestamp that the click occurred
      // Return Value:
      // - if the click is in the same position as the last click and within the timeout, the number of clicks within that time window
      // - otherwise, 1
      unsigned int ControlInteractivity::_numberOfClicks(til::point clickPos,
                                                         Timestamp clickTime)
      {
          // if click occurred at a different location or past the multiClickTimer...
          Timestamp delta;
          THROW_IF_FAILED(UInt64Sub(clickTime, _lastMouseClickTimestamp, &delta));
          if (clickPos != _lastMouseClickPos || delta > _multiClickTimer)
          {
              _multiClickCounter = 1;
          }
          else
          {
              _multiClickCounter++;
          }
  
          _lastMouseClickTimestamp = clickTime;
          _lastMouseClickPos = clickPos;
          return _multiClickCounter;
      }
  
      void ControlInteractivity::GotFocus()
      {
          if (_uiaEngine.get())
          {
              THROW_IF_FAILED(_uiaEngine->Enable());
          }
  
          _updateSystemParameterSettings();
      }
  
      void ControlInteractivity::LostFocus()
      {
          if (_uiaEngine.get())
          {
              THROW_IF_FAILED(_uiaEngine->Disable());
          }
      }
  
      // Method Description
      // - Updates internal params based on system parameters
      void ControlInteractivity::_updateSystemParameterSettings() noexcept
      {
          if (!SystemParametersInfoW(SPI_GETWHEELSCROLLLINES, 0, &_rowsToScroll, 0))
          {
              LOG_LAST_ERROR();
              // If SystemParametersInfoW fails, which it shouldn't, fall back to
              // Windows' default value.
              _rowsToScroll = 3;
          }
      }
  
      // Method Description:
      // - Given a copy-able selection, get the selected text from the buffer and send it to the
      //     Windows Clipboard (CascadiaWin32:main.cpp).
      // - CopyOnSelect does NOT clear the selection
      // Arguments:
      // - singleLine: collapse all of the text to one line
      // - formats: which formats to copy (defined by action's CopyFormatting arg). nullptr
      //             if we should defer which formats are copied to the global setting
      bool ControlInteractivity::CopySelectionToClipboard(bool singleLine,
                                                          const Windows::Foundation::IReference<CopyFormat>& formats)
      {
          if (_core)
          {
              // Return false if there's no selection to copy. If there's no
              // selection, returning false will indicate that the actions that
              // triggered this should _not_ be marked as handled, so ctrl+c
              // without a selection can still send ^C
              if (!_core->HasSelection())
              {
                  return false;
              }
  
              // Mark the current selection as copied
              _selectionNeedsToBeCopied = false;
  
              return _core->CopySelectionToClipboard(singleLine, formats);
          }
  
          return false;
      }
  
      // Method Description:
      // - Initiate a paste operation.
      void ControlInteractivity::RequestPasteTextFromClipboard()
      {
          // attach ControlInteractivity::_sendPastedTextToConnection() as the
          // clipboardDataHandler. This is called when the clipboard data is
          // loaded.
          auto clipboardDataHandler = std::bind(&ControlInteractivity::_sendPastedTextToConnection, this, std::placeholders::_1);
          auto pasteArgs = winrt::make_self<PasteFromClipboardEventArgs>(clipboardDataHandler);
  
          // send paste event up to TermApp
          _PasteFromClipboardHandlers(*this, *pasteArgs);
      }
  
      // Method Description:
      // - Pre-process text pasted (presumably from the clipboard)
      //   before sending it over the terminal's connection.
      void ControlInteractivity::_sendPastedTextToConnection(std::wstring_view wstr)
      {
          _core->PasteText(winrt::hstring{ wstr });
      }
  
      void ControlInteractivity::PointerPressed(Control::MouseButtonState buttonState,
                                                const unsigned int pointerUpdateKind,
                                                const uint64_t timestamp,
                                                const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
                                                const til::point pixelPosition)
      {
          const til::point terminalPosition = _getTerminalPosition(pixelPosition);
  
          const auto altEnabled = modifiers.IsAltPressed();
          const auto shiftEnabled = modifiers.IsShiftPressed();
          const auto ctrlEnabled = modifiers.IsCtrlPressed();
  
          // GH#9396: we prioritize hyper-link over VT mouse events
          auto hyperlink = _core->GetHyperlink(terminalPosition);
          if (WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown) &&
              ctrlEnabled && !hyperlink.empty())
          {
              const auto clickCount = _numberOfClicks(pixelPosition, timestamp);
              // Handle hyper-link only on the first click to prevent multiple activations
              if (clickCount == 1)
              {
                  _hyperlinkHandler(hyperlink);
              }
          }
          else if (_canSendVTMouseInput(modifiers))
          {
              _sendMouseEventHelper(terminalPosition, pointerUpdateKind, modifiers, 0, buttonState);
          }
          else if (WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown))
          {
              const auto clickCount = _numberOfClicks(pixelPosition, timestamp);
              // This formula enables the number of clicks to cycle properly
              // between single-, double-, and triple-click. To increase the
              // number of acceptable click states, simply increment
              // MAX_CLICK_COUNT and add another if-statement
              const auto multiClickMapper = clickCount > MAX_CLICK_COUNT ? ((clickCount + MAX_CLICK_COUNT - 1) % MAX_CLICK_COUNT) + 1 : clickCount;
  
              // Capture the position of the first click when no selection is active
              if (multiClickMapper == 1)
              {
                  _singleClickTouchdownPos = pixelPosition;
  
                  if (!_core->HasSelection())
                  {
                      _lastMouseClickPosNoSelection = pixelPosition;
                  }
              }
              const bool isOnOriginalPosition = _lastMouseClickPosNoSelection == pixelPosition;
  
              _core->LeftClickOnTerminal(terminalPosition,
                                         multiClickMapper,
                                         altEnabled,
                                         shiftEnabled,
                                         isOnOriginalPosition,
                                         _selectionNeedsToBeCopied);
  
              if (_core->HasSelection())
              {
                  // GH#9787: if selection is active we don't want to track the touchdown position
                  // so that dragging the mouse will extend the selection rather than starting the new one
                  _singleClickTouchdownPos = std::nullopt;
              }
          }
          else if (WI_IsFlagSet(buttonState, MouseButtonState::IsRightButtonDown))
          {
              // CopyOnSelect right click always pastes
              if (_core->CopyOnSelect() || !_core->HasSelection())
              {
                  RequestPasteTextFromClipboard();
              }
              else
              {
                  CopySelectionToClipboard(shiftEnabled, nullptr);
              }
          }
      }
  
      void ControlInteractivity::TouchPressed(const til::point contactPoint)
      {
          _touchAnchor = contactPoint;
      }
  
      void ControlInteractivity::PointerMoved(Control::MouseButtonState buttonState,
                                              const unsigned int pointerUpdateKind,
                                              const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
                                              const bool focused,
                                              const til::point pixelPosition,
                                              const bool pointerPressedInBounds)
      {
          const til::point terminalPosition = _getTerminalPosition(pixelPosition);
  
          // Short-circuit isReadOnly check to avoid warning dialog
          if (focused && !_core->IsInReadOnlyMode() && _canSendVTMouseInput(modifiers))
          {
              _sendMouseEventHelper(terminalPosition, pointerUpdateKind, modifiers, 0, buttonState);
          }
          // GH#4603 - don't modify the selection if the pointer press didn't
          // actually start _in_ the control bounds. Case in point - someone drags
          // a file into the bounds of the control. That shouldn't send the
          // selection into space.
          else if (focused && pointerPressedInBounds && WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown))
          {
              if (_singleClickTouchdownPos)
              {
                  // Figure out if the user's moved a quarter of a cell's smaller axis away from the clickdown point
                  auto& touchdownPoint{ *_singleClickTouchdownPos };
                  float dx = ::base::saturated_cast<float>(pixelPosition.x() - touchdownPoint.x());
                  float dy = ::base::saturated_cast<float>(pixelPosition.y() - touchdownPoint.y());
                  auto distance{ std::sqrtf(std::powf(dx, 2) +
                                            std::powf(dy, 2)) };
  
                  const auto fontSizeInDips{ _core->FontSizeInDips() };
                  if (distance >= (std::min(fontSizeInDips.width(), fontSizeInDips.height()) / 4.f))
                  {
                      // GH#9955.c: Make sure to use the terminal location of the
                      // _touchdown_ point here. We want to start the selection
                      // from where the user initially clicked, not where they are
                      // now.
                      _core->SetSelectionAnchor(_getTerminalPosition(touchdownPoint));
  
                      // stop tracking the touchdown point
                      _singleClickTouchdownPos = std::nullopt;
                  }
              }
  
              SetEndSelectionPoint(pixelPosition);
          }
  
          _core->SetHoveredCell(terminalPosition);
      }
  
      void ControlInteractivity::TouchMoved(const til::point newTouchPoint,
                                            const bool focused)
      {
          if (focused &&
              _touchAnchor)
          {
              const auto anchor = _touchAnchor.value();
  
              // Our actualFont's size is in pixels, convert to DIPs, which the
              // rest of the Points here are in.
              const auto fontSizeInDips{ _core->FontSizeInDips() };
  
              // Get the difference between the point we've dragged to and the start of the touch.
              const float dy = ::base::saturated_cast<float>(newTouchPoint.y() - anchor.y());
  
              // Start viewport scroll after we've moved more than a half row of text
              if (std::abs(dy) > (fontSizeInDips.height<float>() / 2.0f))
              {
                  // Multiply by -1, because moving the touch point down will
                  // create a positive delta, but we want the viewport to move up,
                  // so we'll need a negative scroll amount (and the inverse for
                  // panning down)
                  const float numRows = -1.0f * (dy / fontSizeInDips.height<float>());
  
                  const double currentOffset = ::base::ClampedNumeric<double>(_core->ScrollOffset());
                  const double newValue = numRows + currentOffset;
  
                  // Update the Core's viewport position, and raise a
                  // ScrollPositionChanged event to update the scrollbar
                  UpdateScrollbar(newValue);
  
                  // Use this point as our new scroll anchor.
                  _touchAnchor = newTouchPoint;
              }
          }
      }
  
      void ControlInteractivity::PointerReleased(Control::MouseButtonState buttonState,
                                                 const unsigned int pointerUpdateKind,
                                                 const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
                                                 const til::point pixelPosition)
      {
          const til::point terminalPosition = _getTerminalPosition(pixelPosition);
          // Short-circuit isReadOnly check to avoid warning dialog
          if (!_core->IsInReadOnlyMode() && _canSendVTMouseInput(modifiers))
          {
              _sendMouseEventHelper(terminalPosition, pointerUpdateKind, modifiers, 0, buttonState);
              return;
          }
  
          // Only a left click release when copy on select is active should perform a copy.
          // Right clicks and middle clicks should not need to do anything when released.
          const bool isLeftMouseRelease = pointerUpdateKind == WM_LBUTTONUP;
  
          if (_core->CopyOnSelect() &&
              isLeftMouseRelease &&
              _selectionNeedsToBeCopied)
          {
              CopySelectionToClipboard(false, nullptr);
          }
  
          _singleClickTouchdownPos = std::nullopt;
      }
  
      void ControlInteractivity::TouchReleased()
      {
          _touchAnchor = std::nullopt;
      }
  
      // Method Description:
      // - Actually handle a scrolling event, whether from a mouse wheel or a
      //   touchpad scroll. Depending upon what modifier keys are pressed,
      //   different actions will take place.
      //   * Attempts to first dispatch the mouse scroll as a VT event
      //   * If Ctrl+Shift are pressed, then attempts to change our opacity
      //   * If just Ctrl is pressed, we'll attempt to "zoom" by changing our font size
      //   * Otherwise, just scrolls the content of the viewport
      // Arguments:
      // - point: the location of the mouse during this event
      // - modifiers: The modifiers pressed during this event, in the form of a VirtualKeyModifiers
      // - delta: the mouse wheel delta that triggered this event.
      bool ControlInteractivity::MouseWheel(const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
                                            const int32_t delta,
                                            const til::point pixelPosition,
                                            const Control::MouseButtonState buttonState)
      {
          const til::point terminalPosition = _getTerminalPosition(pixelPosition);
  
          // Short-circuit isReadOnly check to avoid warning dialog
          if (!_core->IsInReadOnlyMode() && _canSendVTMouseInput(modifiers))
          {
              // Most mouse event handlers call
              //      _trySendMouseEvent(point);
              // here with a PointerPoint. However, as of #979, we don't have a
              // PointerPoint to work with. So, we're just going to do a
              // mousewheel event manually
              return _sendMouseEventHelper(terminalPosition,
                                           WM_MOUSEWHEEL,
                                           modifiers,
                                           ::base::saturated_cast<short>(delta),
                                           buttonState);
          }
  
          const auto ctrlPressed = modifiers.IsCtrlPressed();
          const auto shiftPressed = modifiers.IsShiftPressed();
  
          if (ctrlPressed && shiftPressed)
          {
              _mouseTransparencyHandler(delta);
          }
          else if (ctrlPressed)
          {
              _mouseZoomHandler(delta);
          }
          else
          {
              _mouseScrollHandler(delta, pixelPosition, WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown));
          }
          return false;
      }
  
      // Method Description:
      // - Adjust the opacity of the acrylic background in response to a mouse
      //   scrolling event.
      // Arguments:
      // - mouseDelta: the mouse wheel delta that triggered this event.
      void ControlInteractivity::_mouseTransparencyHandler(const double mouseDelta)
      {
          // Transparency is on a scale of [0.0,1.0], so only increment by .01.
          const auto effectiveDelta = mouseDelta < 0 ? -.01 : .01;
          _core->AdjustOpacity(effectiveDelta);
      }
  
      // Method Description:
      // - Adjust the font size of the terminal in response to a mouse scrolling
      //   event.
      // Arguments:
      // - mouseDelta: the mouse wheel delta that triggered this event.
      void ControlInteractivity::_mouseZoomHandler(const double mouseDelta)
      {
          const auto fontDelta = mouseDelta < 0 ? -1 : 1;
          _core->AdjustFontSize(fontDelta);
      }
  
      // Method Description:
      // - Scroll the visible viewport in response to a mouse wheel event.
      // Arguments:
      // - mouseDelta: the mouse wheel delta that triggered this event.
      // - pixelPosition: the location of the mouse during this event
      // - isLeftButtonPressed: true iff the left mouse button was pressed during this event.
      void ControlInteractivity::_mouseScrollHandler(const double mouseDelta,
                                                     const til::point pixelPosition,
                                                     const bool isLeftButtonPressed)
      {
          // GH#9955.b: Start scrolling from our internal scrollbar position. This
          // lets us accumulate fractional numbers of rows to scroll with each
          // event. Especially for precision trackpads, we might be getting scroll
          // deltas smaller than a single row, but we still want lots of those to
          // accumulate.
          //
          // At the start, let's compare what we _think_ the scrollbar is, with
          // what it should be. It's possible the core scrolled out from
          // underneath us. We wouldn't know - we don't want the overhead of
          // another ScrollPositionChanged handler. If the scrollbar should be
          // somewhere other than where it is currently, then start from that row.
          const int currentInternalRow = ::base::saturated_cast<int>(::std::round(_internalScrollbarPosition));
          const int currentCoreRow = _core->ScrollOffset();
          const double currentOffset = currentInternalRow == currentCoreRow ?
                                           _internalScrollbarPosition :
                                           currentCoreRow;
  
          // negative = down, positive = up
          // However, for us, the signs are flipped.
          // With one of the precision mice, one click is always a multiple of 120 (WHEEL_DELTA),
          // but the "smooth scrolling" mode results in non-int values
          const double rowDelta = mouseDelta / (-1.0 * WHEEL_DELTA);
  
          // WHEEL_PAGESCROLL is a Win32 constant that represents the "scroll one page
          // at a time" setting. If we ignore it, we will scroll a truly absurd number
          // of rows.
          const double rowsToScroll{ _rowsToScroll == WHEEL_PAGESCROLL ? ::base::saturated_cast<double>(_core->ViewHeight()) : _rowsToScroll };
          double newValue = (rowsToScroll * rowDelta) + (currentOffset);
  
          // Update the Core's viewport position, and raise a
          // ScrollPositionChanged event to update the scrollbar
          UpdateScrollbar(newValue);
  
          if (isLeftButtonPressed)
          {
              // If user is mouse selecting and scrolls, they then point at new
              // character. Make sure selection reflects that immediately.
              SetEndSelectionPoint(pixelPosition);
          }
      }
  
      // Method Description:
      // - Update the scroll position in such a way that should update the
      //   scrollbar. For example, when scrolling the buffer with the mouse or
      //   touch input. This will both update the Core's Terminal's buffer
      //   location, then also raise our own ScrollPositionChanged event.
      //   UserScrollViewport _won't_ raise the core's ScrollPositionChanged
      //   event, because it's assumed that's already being called from a context
      //   that knows about the change to the scrollbar. So we need to raise the
      //   event on our own.
      // - The hosting control should make sure to listen to our own
      //   ScrollPositionChanged event and use that as an opportunity to update
      //   the location of the scrollbar.
      // Arguments:
      // - newValue: The new top of the viewport
      // Return Value:
      // - <none>
      void ControlInteractivity::UpdateScrollbar(const double newValue)
      {
          // Set this as the new value of our internal scrollbar representation.
          // We're doing this so we can accumulate fractional amounts of a row to
          // scroll each time the mouse scrolls.
          _internalScrollbarPosition = std::clamp<double>(newValue, 0.0, _core->BufferHeight());
  
          // If the new scrollbar position, rounded to an int, is at a different
          // row, then actually update the scroll position in the core, and raise
          // a ScrollPositionChanged to inform the control.
          int viewTop = ::base::saturated_cast<int>(::std::round(_internalScrollbarPosition));
          if (viewTop != _core->ScrollOffset())
          {
              _core->UserScrollViewport(viewTop);
  
              // _core->ScrollOffset() is now set to newValue
              _ScrollPositionChangedHandlers(*this,
                                             winrt::make<ScrollPositionChangedArgs>(_core->ScrollOffset(),
                                                                                    _core->ViewHeight(),
                                                                                    _core->BufferHeight()));
          }
      }
  
      void ControlInteractivity::_hyperlinkHandler(const std::wstring_view uri)
      {
          _OpenHyperlinkHandlers(*this, winrt::make<OpenHyperlinkEventArgs>(winrt::hstring{ uri }));
      }
  
      bool ControlInteractivity::_canSendVTMouseInput(const ::Microsoft::Terminal::Core::ControlKeyStates modifiers)
      {
          // If the user is holding down Shift, suppress mouse events
          // TODO GH#4875: disable/customize this functionality
          if (modifiers.IsShiftPressed())
          {
              return false;
          }
          return _core->IsVtMouseModeEnabled();
      }
  
      // Method Description:
      // - Sets selection's end position to match supplied cursor position, e.g. while mouse dragging.
      // Arguments:
      // - cursorPosition: in pixels, relative to the origin of the control
      void ControlInteractivity::SetEndSelectionPoint(const til::point pixelPosition)
      {
          _core->SetEndSelectionPoint(_getTerminalPosition(pixelPosition));
          _selectionNeedsToBeCopied = true;
      }
  
      // Method Description:
      // - Gets the corresponding viewport terminal position for the point in
      //   pixels, by normalizing with the font size.
      // Arguments:
      // - pixelPosition: the (x,y) position of a given point (i.e.: mouse cursor).
      //    NOTE: origin (0,0) is top-left.
      // Return Value:
      // - the corresponding viewport terminal position for the given Point parameter
      til::point ControlInteractivity::_getTerminalPosition(const til::point& pixelPosition)
      {
          // Get the size of the font, which is in pixels
          const til::size fontSize{ _core->GetFont().GetSize() };
          // Convert the location in pixels to characters within the current viewport.
          return til::point{ pixelPosition / fontSize };
      }
  
      bool ControlInteractivity::_sendMouseEventHelper(const til::point terminalPosition,
                                                       const unsigned int pointerUpdateKind,
                                                       const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
                                                       const SHORT wheelDelta,
                                                       Control::MouseButtonState buttonState)
      {
          const auto adjustment = _core->ScrollOffset() > 0 ? _core->BufferHeight() - _core->ScrollOffset() - _core->ViewHeight() : 0;
          // If the click happened outside the active region, just don't send any mouse event
          if (const auto adjustedY = terminalPosition.y() - adjustment; adjustedY >= 0)
          {
              return _core->SendMouseEvent({ terminalPosition.x(), adjustedY }, pointerUpdateKind, modifiers, wheelDelta, toInternalMouseState(buttonState));
          }
          return false;
      }
  
      // Method Description:
      // - Creates an automation peer for the Terminal Control, enabling
      //   accessibility on our control.
      // - Our implementation implements the ITextProvider pattern, and the
      //   IControlAccessibilityInfo, to connect to the UiaEngine, which must be
      //   attached to the core's renderer.
      // - The TermControlAutomationPeer will connect this to the UI tree.
      // Arguments:
      // - None
      // Return Value:
      // - The automation peer for our control
      Control::InteractivityAutomationPeer ControlInteractivity::OnCreateAutomationPeer()
      try
      {
          const auto autoPeer = winrt::make_self<implementation::InteractivityAutomationPeer>(this);
  
          _uiaEngine = std::make_unique<::Microsoft::Console::Render::UiaEngine>(autoPeer.get());
          _core->AttachUiaEngine(_uiaEngine.get());
          return *autoPeer;
      }
      catch (...)
      {
          LOG_CAUGHT_EXCEPTION();
          return nullptr;
      }
  
      ::Microsoft::Console::Types::IUiaData* ControlInteractivity::GetUiaData() const
      {
          return _core->GetUiaData();
      }
  
      // Method Description:
      // - Used by the TermControl to know if it should translate drag-dropped
      //   paths into WSL-friendly paths.
      // Arguments:
      // - <none>
      // Return Value:
      // - true if the connection we were created with was a WSL profile.
      bool ControlInteractivity::ManglePathsForWsl()
      {
          return _core->Settings().ProfileSource() == L"Windows.Terminal.Wsl";
      }
  }
  