Files
terminal-microsoft/src/cascadia/TerminalControl/ControlInteractivity.h
Dustin L. Howett d1a1f9836e Restore off-the-top behavior for VT mouse mode (#17779)
PR #10642 and #11290 introduced an adjustment for the cursor position
used to generate VT mouse mode events.

One of the decisions made in those PRs was to only send coordinates
where Y was >= 0, so if you were off the top of the screen you wouldn't
get any events. However, terminal emulators are expected to send
_clamped_ events when the mouse is off the screen. This decision broke
clamping Y to 0 when the mouse was above the screen.

The other decision was to only adjust the Y coordinate if the core's
`ScrollOffset` was greater than 0. It turns out that `ScrollOffset` _is
0_ when you are scrolled all the way back in teh buffer. With this
check, we would clamp coordinates properly _until the top line of the
scrollback was visible_, at which point we would send those coordinates
over directly. This resulted in the same weird behavior as observed in
#10190.

I've fixed both of those things. Core is expected to receive negative
coordinates and clamp them to the viewport. ScrollOffset should never be
below 0, as it refers to the top visible buffer line.

In addition to that, #17744 uncovered that we were allowing
autoscrolling to happen even when VT mouse events were being generated.
I added a way for `ControlInteractivity` to halt further event
processing. It's crude.

Refs #10190
Closes #17744
2024-08-23 16:43:39 +00:00

175 lines
7.6 KiB
C++

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
//
// Module Name:
// - ControlInteractivity.h
//
// Abstract:
// - This is a wrapper for the `ControlCore`. It holds the logic for things like
// double-click, right click copy/paste, selection, etc. This is intended to
// be a UI framework-independent abstraction. The methods this layer exposes
// can be called the same from both the WinUI `TermControl` and the WPF
// control.
//
// Author:
// - Mike Griese (zadjii-msft) 01-Apr-2021
#pragma once
#include "ControlInteractivity.g.h"
#include "EventArgs.h"
#include "../buffer/out/search.h"
#include "ControlCore.h"
#include "../../renderer/uia/UiaRenderer.hpp"
namespace ControlUnitTests
{
class ControlCoreTests;
class ControlInteractivityTests;
};
namespace winrt::Microsoft::Terminal::Control::implementation
{
struct ControlInteractivity : ControlInteractivityT<ControlInteractivity>
{
public:
ControlInteractivity(IControlSettings settings,
Control::IControlAppearance unfocusedAppearance,
TerminalConnection::ITerminalConnection connection);
void GotFocus();
void LostFocus();
void UpdateSettings();
void Initialize();
Control::ControlCore Core();
void Close();
void Detach();
Control::InteractivityAutomationPeer OnCreateAutomationPeer();
::Microsoft::Console::Render::IRenderData* GetRenderData() const;
#pragma region Input Methods
void PointerPressed(Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind,
const uint64_t timestamp,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const Core::Point pixelPosition);
void TouchPressed(const Core::Point contactPoint);
bool PointerMoved(Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const bool focused,
const Core::Point pixelPosition,
const bool pointerPressedInBounds);
void TouchMoved(const Core::Point newTouchPoint,
const bool focused);
void PointerReleased(Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const Core::Point pixelPosition);
void TouchReleased();
bool MouseWheel(const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const int32_t delta,
const Core::Point pixelPosition,
const Control::MouseButtonState state);
void UpdateScrollbar(const float newValue);
#pragma endregion
bool CopySelectionToClipboard(bool singleLine,
const Windows::Foundation::IReference<CopyFormat>& formats);
void RequestPasteTextFromClipboard();
void SetEndSelectionPoint(const Core::Point pixelPosition);
bool ManglePathsForWsl();
uint64_t Id();
void AttachToNewControl(const Microsoft::Terminal::Control::IKeyBindings& keyBindings);
til::typed_event<IInspectable, Control::OpenHyperlinkEventArgs> OpenHyperlink;
til::typed_event<IInspectable, Control::PasteFromClipboardEventArgs> PasteFromClipboard;
til::typed_event<IInspectable, Control::ScrollPositionChangedArgs> ScrollPositionChanged;
til::typed_event<IInspectable, Control::ContextMenuRequestedEventArgs> ContextMenuRequested;
til::typed_event<IInspectable, IInspectable> Attached;
til::typed_event<IInspectable, IInspectable> Closed;
private:
// NOTE: _uiaEngine must be ordered before _core.
//
// ControlCore::AttachUiaEngine receives a IRenderEngine as a raw pointer, which we own.
// We must ensure that we first destroy the ControlCore before the UiaEngine instance
// in order to safely resolve this unsafe pointer dependency. Otherwise a deallocated
// IRenderEngine is accessed when ControlCore calls Renderer::TriggerTeardown.
// (C++ class members are destroyed in reverse order.)
std::unique_ptr<::Microsoft::Console::Render::UiaEngine> _uiaEngine;
winrt::com_ptr<ControlCore> _core{ nullptr };
UINT _rowsToScroll = 3;
float _internalScrollbarPosition = 0;
// If this is set, then we assume we are in the middle of panning the
// viewport via touch input.
std::optional<Core::Point> _touchAnchor;
using Timestamp = uint64_t;
// imported from WinUser
// Used for PointerPoint.Timestamp Property (https://docs.microsoft.com/en-us/uwp/api/windows.ui.input.pointerpoint.timestamp#Windows_UI_Input_PointerPoint_Timestamp)
Timestamp _multiClickTimer;
unsigned int _multiClickCounter;
Timestamp _lastMouseClickTimestamp;
std::optional<Core::Point> _lastMouseClickPos;
std::optional<Core::Point> _singleClickTouchdownPos;
std::optional<Core::Point> _lastMouseClickPosNoSelection;
// This field tracks whether the selection has changed meaningfully
// since it was last copied. It's generally used to prevent copyOnSelect
// from firing when the pointer _just happens_ to be released over the
// terminal.
bool _selectionNeedsToBeCopied;
std::optional<til::point> _lastHoveredCell{ std::nullopt };
// Track the last hyperlink ID we hovered over
uint16_t _lastHoveredId{ 0 };
std::optional<interval_tree::IntervalTree<til::point, size_t>::interval> _lastHoveredInterval{ std::nullopt };
uint64_t _id;
static std::atomic<uint64_t> _nextId;
unsigned int _numberOfClicks(Core::Point clickPos, Timestamp clickTime);
void _updateSystemParameterSettings() noexcept;
void _mouseTransparencyHandler(const int32_t mouseDelta) const;
void _mouseZoomHandler(const int32_t mouseDelta) const;
void _mouseScrollHandler(const int32_t mouseDelta,
const Core::Point terminalPosition,
const bool isLeftButtonPressed);
void _hyperlinkHandler(const std::wstring_view uri);
bool _canSendVTMouseInput(const ::Microsoft::Terminal::Core::ControlKeyStates modifiers);
bool _shouldSendAlternateScroll(const ::Microsoft::Terminal::Core::ControlKeyStates modifiers, const int32_t delta);
til::point _getTerminalPosition(const til::point pixelPosition);
bool _sendMouseEventHelper(const til::point terminalPosition,
const unsigned int pointerUpdateKind,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const SHORT wheelDelta,
Control::MouseButtonState buttonState);
friend class ControlUnitTests::ControlCoreTests;
friend class ControlUnitTests::ControlInteractivityTests;
};
}
namespace winrt::Microsoft::Terminal::Control::factory_implementation
{
BASIC_FACTORY(ControlInteractivity);
}