Compare commits

...

5 Commits

Author SHA1 Message Date
Dustin L. Howett
2f25f0dc0f HAX to make it crash less 2025-12-03 23:29:07 -06:00
Dustin L. Howett
09ad716882 Squash of all other Win7WPF work 2025-12-03 23:24:52 -06:00
Dustin L. Howett
e32a96d947 WpfTest: Add a more complicated test string 2025-12-03 23:20:16 -06:00
Dustin L. Howett
6e5f5a973b Move AutoScroll down into Interactivity 2025-12-03 23:19:42 -06:00
Dustin L. Howett
46de6846bc Push PointerId handling down to Interactivity to prep for autoscroll 2025-12-03 23:18:24 -06:00
24 changed files with 1263 additions and 332 deletions

View File

@@ -26,7 +26,7 @@
</PropertyGroup>
<PropertyGroup Condition="!Exists('CascadiaPackage_TemporaryKey.pfx')">
<AppxPackageSigningEnabled>false</AppxPackageSigningEnabled>
<AppxBundle>Never</AppxBundle>
<!--<AppxBundle>Never</AppxBundle>-->
</PropertyGroup>
<PropertyGroup Condition="Exists('CascadiaPackage_TemporaryKey.pfx')">
<AppxPackageSigningEnabled>true</AppxPackageSigningEnabled>

View File

@@ -78,6 +78,7 @@
<Project>{18D09A24-8240-42D6-8CB6-236EEE820263}</Project>
</ProjectReference>
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\WinRTUtils\WinRTUtils.vcxproj">
<Private>true</Private>
<Project>{CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}</Project>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>

View File

@@ -29,6 +29,7 @@
#include "winrt/Windows.Data.Json.h"
#include <Windows.h>
#include <wrl.h>
#include <winhttp.h>
#include <wil/resource.h>

View File

@@ -160,6 +160,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlCore::_setupDispatcherAndCallbacks()
{
///* TODO(DH) */ return;
// Get our dispatcher. If we're hosted in-proc with XAML, this will get
// us the same dispatcher as TermControl::Dispatcher(). If we're out of
// proc, this'll return null. We'll need to instead make a new
@@ -408,12 +409,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_terminal->Create(viewportSize, Utils::ClampToShortMax(_settings.HistorySize(), 0), *_renderer);
_terminal->UpdateSettings(_settings);
// Tell the render engine to notify us when the swap chain changes.
// We do this after we initially set the swapchain so as to avoid
// unnecessary callbacks (and locking problems)
_renderEngine->SetCallback([this](HANDLE handle) {
_renderEngineSwapChainChanged(handle);
});
if (_hookup == HookupMode::ForComposition)
{
// Tell the render engine to notify us when the swap chain changes.
// We do this after we initially set the swapchain so as to avoid
// unnecessary callbacks (and locking problems)
_renderEngine->SetCallback([this](HANDLE handle) {
_renderEngineSwapChainChanged(handle);
});
}
_renderEngine->SetRetroTerminalEffect(_settings.RetroTerminalEffect());
_renderEngine->SetPixelShaderPath(_settings.PixelShaderPath());
@@ -434,6 +438,23 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return true;
}
bool ControlCore::InitializeWithHwnd(const float actualWidth,
const float actualHeight,
const float compositionScale,
const uint64_t hwnd)
{
_owningHwnd = hwnd;
_hookup = HookupMode::ForHwnd;
auto i = Initialize(actualWidth, actualHeight, compositionScale);
if (i)
{
auto lock = _terminal->LockForWriting();
(void)_renderEngine->SetHwnd(reinterpret_cast<HWND>(hwnd));
}
return i;
}
// Method Description:
// - Tell the renderer to start painting.
// - !! IMPORTANT !! Make sure that we've attached our swap chain to an
@@ -1939,7 +1960,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
RendererWarning.raise(*this, winrt::make<RendererWarningArgs>(hr, winrt::hstring{ parameter }));
}
safe_void_coroutine ControlCore::_renderEngineSwapChainChanged(const HANDLE sourceHandle)
/* TODO(DH) */ void ControlCore::_renderEngineSwapChainChanged(const HANDLE sourceHandle)
{
// `sourceHandle` is a weak ref to a HANDLE that's ultimately owned by the
// render engine's own unique_handle. We'll add another ref to it here.
@@ -1957,7 +1978,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// Concurrent read of _dispatcher is safe, because Detach() calls TriggerTeardown()
// which blocks until this call returns. _dispatcher will only be changed afterwards.
co_await wil::resume_foreground(_dispatcher);
// TODO(DH) co_await wil::resume_foreground(_dispatcher);
if (auto core{ weakThis.get() })
{
@@ -2922,4 +2943,20 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
_terminal->PreviewText(input);
}
winrt::Windows::Foundation::Size ControlCore::RenderedSize()
{
return { _panelWidth, _panelHeight };
}
void ControlCore::ResizeToDimensions(uint32_t width, uint32_t height, winrt::Windows::Foundation::Size& newSizeInPixels)
{
if (!_renderEngine)
{
throw winrt::hresult_error(E_INVALIDARG);
}
auto pixelSize = _renderEngine->GetViewportInPixels(Viewport::FromDimensions({ 0, 0 }, til::size{ static_cast<til::CoordType>(width), static_cast<til::CoordType>(height) }));
SizeOrScaleChanged(static_cast<float>(pixelSize.Width()), static_cast<float>(pixelSize.Height()), _compositionScale);
newSizeInPixels = RenderedSize();
}
}

View File

@@ -78,6 +78,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation
struct ControlCore : ControlCoreT<ControlCore>
{
private:
enum class HookupMode
{
ForHwnd = 0x0,
ForComposition = 0x1,
};
public:
ControlCore(Control::IControlSettings settings,
Control::IControlAppearance unfocusedAppearance,
@@ -87,6 +93,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool Initialize(const float actualWidth,
const float actualHeight,
const float compositionScale);
bool InitializeWithHwnd(const float actualWidth,
const float actualHeight,
const float compositionScale,
const uint64_t hwnd);
void EnablePainting();
void Detach();
@@ -263,6 +273,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool ShouldShowSelectCommand();
bool ShouldShowSelectOutput();
winrt::Windows::Foundation::Size RenderedSize();
void ResizeToDimensions(uint32_t width, uint32_t height, winrt::Windows::Foundation::Size& newSizeInPixels);
void PreviewInput(std::wstring_view input);
RUNTIME_SETTING(float, Opacity, _settings.Opacity());
@@ -341,7 +354,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
#pragma region RendererCallbacks
void _rendererWarning(const HRESULT hr, wil::zwstring_view parameter);
safe_void_coroutine _renderEngineSwapChainChanged(const HANDLE handle);
/* TODO(DH) */ void _renderEngineSwapChainChanged(const HANDLE handle);
void _rendererBackgroundColorChanged();
void _rendererTabColorChanged();
#pragma endregion
@@ -375,7 +388,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
//
// Though, the unit tests don't actually run in TAEF's main
// thread, so we don't care when we're running in tests.
assert(_inUnitTests || _dispatcher.HasThreadAccess());
assert(_hookup == HookupMode::ForHwnd || _inUnitTests || _dispatcher.HasThreadAccess());
}
#endif
return _closing;
@@ -420,6 +433,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
std::atomic<bool> _initializedTerminal{ false };
bool _isReadOnly{ false };
bool _closing{ false };
HookupMode _hookup{ HookupMode::ForComposition };
struct StashedColorScheme
{

View File

@@ -92,6 +92,9 @@ namespace Microsoft.Terminal.Control
Boolean Initialize(Single actualWidth,
Single actualHeight,
Single compositionScale);
Boolean InitializeWithHwnd(Single actualWidth,
Single actualHeight,
Single compositionScale, UInt64 hwnd);
void UpdateSettings(IControlSettings settings, IControlAppearance appearance);
void ApplyAppearance(Boolean focused);
@@ -182,6 +185,9 @@ namespace Microsoft.Terminal.Control
Boolean ShouldShowSelectCommand();
Boolean ShouldShowSelectOutput();
Windows.Foundation.Size RenderedSize { get; };
void ResizeToDimensions(UInt32 width, UInt32 height, out Windows.Foundation.Size newSizeInPixels);
void OpenCWD();
void ClearQuickFix();

View File

@@ -54,6 +54,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
self->Attached.raise(*self, nullptr);
}
});
_createInteractivityTimers();
}
uint64_t ControlInteractivity::Id()
@@ -80,12 +82,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
LOG_IF_FAILED(_uiaEngine->Disable());
_core->DetachUiaEngine(_uiaEngine.get());
}
_destroyInteractivityTimers();
_core->Detach();
}
void ControlInteractivity::AttachToNewControl()
{
_core->AttachToNewControl();
_createInteractivityTimers();
}
// Method Description:
@@ -117,12 +121,30 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlInteractivity::Close()
{
Closed.raise(*this, nullptr);
_destroyInteractivityTimers();
if (_core)
{
_core->Close();
}
}
void ControlInteractivity::_createInteractivityTimers()
{
_autoScrollTimer = _core->Dispatcher().CreateTimer();
static constexpr auto AutoScrollUpdateInterval = std::chrono::microseconds(static_cast<int>(1.0 / 30.0 * 1000000));
_autoScrollTimer.Interval(AutoScrollUpdateInterval);
_autoScrollTimer.Tick({ get_weak(), &ControlInteractivity::_updateAutoScroll });
}
void ControlInteractivity::_destroyInteractivityTimers()
{
if (_autoScrollTimer)
{
_autoScrollTimer.Stop();
_autoScrollTimer = nullptr;
}
}
// Method Description:
// - Returns the number of clicks that occurred (double and triple click support).
// Every call to this function registers a click.
@@ -155,6 +177,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlInteractivity::GotFocus()
{
_focused = true;
if (_uiaEngine.get())
{
THROW_IF_FAILED(_uiaEngine->Enable());
@@ -167,6 +191,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void ControlInteractivity::LostFocus()
{
_focused = false;
if (_uiaEngine.get())
{
THROW_IF_FAILED(_uiaEngine->Disable());
@@ -234,7 +260,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
PasteFromClipboard.raise(*this, std::move(args));
}
void ControlInteractivity::PointerPressed(Control::MouseButtonState buttonState,
void ControlInteractivity::PointerPressed(const uint32_t /*pointerId*/,
Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind,
const uint64_t timestamp,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
@@ -247,6 +274,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const auto shiftEnabled = modifiers.IsShiftPressed();
const auto ctrlEnabled = modifiers.IsCtrlPressed();
// Mark that this pointer event actually started within our bounds.
// We'll need this later, for PointerMoved events.
_pointerPressedInBounds = true;
// GH#9396: we prioritize hyper-link over VT mouse events
auto hyperlink = _core->GetHyperlink(terminalPosition.to_core_point());
if (WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown) &&
@@ -327,24 +358,23 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
}
void ControlInteractivity::TouchPressed(const winrt::Windows::Foundation::Point contactPoint)
void ControlInteractivity::TouchPressed(const Core::Point contactPoint)
{
_touchAnchor = contactPoint;
}
bool ControlInteractivity::PointerMoved(Control::MouseButtonState buttonState,
bool ControlInteractivity::PointerMoved(const uint32_t pointerId,
Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const bool focused,
const Core::Point pixelPosition,
const bool pointerPressedInBounds)
const Core::Point pixelPosition)
{
const auto terminalPosition = _getTerminalPosition(til::point{ pixelPosition }, false);
// Returning true from this function indicates that the caller should do no further processing of this movement.
bool handledCompletely = false;
// Short-circuit isReadOnly check to avoid warning dialog
if (focused && !_core->IsInReadOnlyMode() && _canSendVTMouseInput(modifiers))
if (_focused && !_core->IsInReadOnlyMode() && _canSendVTMouseInput(modifiers))
{
_sendMouseEventHelper(terminalPosition, pointerUpdateKind, modifiers, 0, buttonState);
handledCompletely = true;
@@ -353,7 +383,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// 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))
else if (_focused && _pointerPressedInBounds && WI_IsFlagSet(buttonState, MouseButtonState::IsLeftButtonDown))
{
if (_singleClickTouchdownPos)
{
@@ -393,16 +423,49 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
SetEndSelectionPoint(pixelPosition);
// GH#9109 - Only start an auto-scroll when the drag actually
// started within our bounds. Otherwise, someone could start a drag
// outside the terminal control, drag into the padding, and trick us
// into starting to scroll.
{
// We want to find the distance relative to the bounds of the
// SwapChainPanel, not the entire control. If they drag out of
// the bounds of the text, into the padding, we still what that
// to auto-scroll
const auto height = _core->ViewHeight() * _core->FontSize().Height;
const auto cursorBelowBottomDist = pixelPosition.Y - height;
const auto cursorAboveTopDist = -1 * pixelPosition.Y;
constexpr auto MinAutoScrollDist = 2.0; // Arbitrary value
auto newAutoScrollVelocity = 0.0;
if (cursorBelowBottomDist > MinAutoScrollDist)
{
newAutoScrollVelocity = _getAutoScrollSpeed(cursorBelowBottomDist);
}
else if (cursorAboveTopDist > MinAutoScrollDist)
{
newAutoScrollVelocity = -1.0 * _getAutoScrollSpeed(cursorAboveTopDist);
}
if (newAutoScrollVelocity != 0)
{
_tryStartAutoScroll(pointerId, pixelPosition, newAutoScrollVelocity);
}
else
{
_tryStopAutoScroll(pointerId);
}
}
}
_core->SetHoveredCell(terminalPosition.to_core_point());
return handledCompletely;
}
void ControlInteractivity::TouchMoved(const winrt::Windows::Foundation::Point newTouchPoint,
const bool focused)
void ControlInteractivity::TouchMoved(const Core::Point newTouchPoint)
{
if (focused &&
if (_focused &&
_touchAnchor)
{
const auto anchor = _touchAnchor.value();
@@ -436,11 +499,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
}
void ControlInteractivity::PointerReleased(Control::MouseButtonState buttonState,
void ControlInteractivity::PointerReleased(const uint32_t pointerId,
Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const Core::Point pixelPosition)
{
_pointerPressedInBounds = false;
const auto terminalPosition = _getTerminalPosition(til::point{ pixelPosition }, false);
// Short-circuit isReadOnly check to avoid warning dialog
if (!_core->IsInReadOnlyMode() && _canSendVTMouseInput(modifiers))
@@ -464,6 +530,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
}
_singleClickTouchdownPos = std::nullopt;
_tryStopAutoScroll(pointerId);
}
void ControlInteractivity::TouchReleased()
@@ -757,4 +824,101 @@ namespace winrt::Microsoft::Terminal::Control::implementation
{
return _core->GetRenderData();
}
// Method Description:
// - Calculates speed of single axis of auto scrolling. It has to allow for both
// fast and precise selection.
// Arguments:
// - cursorDistanceFromBorder: distance from viewport border to cursor, in pixels. Must be non-negative.
// Return Value:
// - positive speed in characters / sec
double ControlInteractivity::_getAutoScrollSpeed(double cursorDistanceFromBorder) const
{
// The numbers below just feel well, feel free to change.
// TODO: Maybe account for space beyond border that user has available
return std::pow(cursorDistanceFromBorder, 2.0) / 25.0 + 2.0;
}
// Method Description:
// - Starts new pointer related auto scroll behavior, or continues existing one.
// Does nothing when there is already auto scroll associated with another pointer.
// Arguments:
// - pointerId, point: info about pointer that causes auto scroll. Pointer's position
// is later used to update selection.
// - scrollVelocity: target velocity of scrolling in characters / sec
void ControlInteractivity::_tryStartAutoScroll(const uint32_t pointerId, const Core::Point& point, const double scrollVelocity)
{
// Allow only one pointer at the time
if (!_autoScrollingPointerId ||
_autoScrollingPointerId == pointerId)
{
_autoScrollingPointerId = pointerId;
_autoScrollingPointerPoint = point;
_autoScrollVelocity = scrollVelocity;
// If this is first time the auto scroll update is about to be called,
// kick-start it by initializing its time delta as if it started now
if (!_lastAutoScrollUpdateTime)
{
_lastAutoScrollUpdateTime = std::chrono::high_resolution_clock::now();
}
// Apparently this check is not necessary but greatly improves performance
if (!_autoScrollTimer.IsRunning())
{
_autoScrollTimer.Start();
}
}
}
// Method Description:
// - Stops auto scroll if it's active and is associated with supplied pointer id.
// Arguments:
// - pointerId: id of pointer for which to stop auto scroll
void ControlInteractivity::_tryStopAutoScroll(const uint32_t pointerId)
{
if (_autoScrollingPointerId &&
pointerId == _autoScrollingPointerId)
{
_autoScrollingPointerId = std::nullopt;
_autoScrollingPointerPoint = std::nullopt;
_autoScrollVelocity = 0;
_lastAutoScrollUpdateTime = std::nullopt;
// Apparently this check is not necessary but greatly improves performance
if (_autoScrollTimer.IsRunning())
{
_autoScrollTimer.Stop();
}
}
}
// Method Description:
// - Called continuously to gradually scroll viewport when user is mouse
// selecting outside it (to 'follow' the cursor).
// Arguments:
// - none
void ControlInteractivity::_updateAutoScroll(const Windows::Foundation::IInspectable& /* sender */,
const Windows::Foundation::IInspectable& /* e */)
{
if (_autoScrollVelocity != 0)
{
const auto timeNow = std::chrono::high_resolution_clock::now();
if (_lastAutoScrollUpdateTime)
{
static constexpr auto microSecPerSec = 1000000.0;
const auto deltaTime = std::chrono::duration_cast<std::chrono::microseconds>(timeNow - *_lastAutoScrollUpdateTime).count() / microSecPerSec;
UpdateScrollbar(static_cast<float>(_core->ScrollOffset()) + static_cast<float>(_autoScrollVelocity * deltaTime) /* TODO(DH) */);
if (_autoScrollingPointerPoint)
{
SetEndSelectionPoint(*_autoScrollingPointerPoint);
}
}
_lastAutoScrollUpdateTime = timeNow;
}
}
}

View File

@@ -51,23 +51,23 @@ namespace winrt::Microsoft::Terminal::Control::implementation
::Microsoft::Console::Render::IRenderData* GetRenderData() const;
#pragma region Input Methods
void PointerPressed(Control::MouseButtonState buttonState,
void PointerPressed(const uint32_t pointerId,
Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind,
const uint64_t timestamp,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const Core::Point pixelPosition);
void TouchPressed(const winrt::Windows::Foundation::Point contactPoint);
void TouchPressed(const Core::Point contactPoint);
bool PointerMoved(Control::MouseButtonState buttonState,
bool PointerMoved(const uint32_t pointerId,
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 winrt::Windows::Foundation::Point newTouchPoint,
const bool focused);
const Core::Point pixelPosition);
void TouchMoved(const Core::Point newTouchPoint);
void PointerReleased(Control::MouseButtonState buttonState,
void PointerReleased(const uint32_t pointerId,
Control::MouseButtonState buttonState,
const unsigned int pointerUpdateKind,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const Core::Point pixelPosition);
@@ -115,7 +115,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// If this is set, then we assume we are in the middle of panning the
// viewport via touch input.
std::optional<winrt::Windows::Foundation::Point> _touchAnchor;
std::optional<Core::Point> _touchAnchor;
using Timestamp = uint64_t;
@@ -142,6 +142,25 @@ namespace winrt::Microsoft::Terminal::Control::implementation
uint64_t _id;
static std::atomic<uint64_t> _nextId;
bool _focused{ false };
// Auto scroll occurs when user, while selecting, drags cursor outside
// viewport. View is then scrolled to 'follow' the cursor.
double _autoScrollVelocity;
std::optional<uint32_t> _autoScrollingPointerId;
std::optional<Core::Point> _autoScrollingPointerPoint;
Windows::System::DispatcherQueueTimer _autoScrollTimer{ nullptr };
std::optional<std::chrono::high_resolution_clock::time_point> _lastAutoScrollUpdateTime;
bool _pointerPressedInBounds{ false };
void _tryStartAutoScroll(const uint32_t id, const Core::Point& point, const double scrollVelocity);
void _tryStopAutoScroll(const uint32_t pointerId);
void _updateAutoScroll(const Windows::Foundation::IInspectable& sender, const Windows::Foundation::IInspectable& e);
double _getAutoScrollSpeed(double cursorDistanceFromBorder) const;
void _createInteractivityTimers();
void _destroyInteractivityTimers();
unsigned int _numberOfClicks(Core::Point clickPos, Timestamp clickTime);
void _updateSystemParameterSettings() noexcept;

View File

@@ -36,24 +36,24 @@ namespace Microsoft.Terminal.Control
void RequestPasteTextFromClipboard();
void SetEndSelectionPoint(Microsoft.Terminal.Core.Point point);
void PointerPressed(MouseButtonState buttonState,
void PointerPressed(UInt32 pointerId,
MouseButtonState buttonState,
UInt32 pointerUpdateKind,
UInt64 timestamp,
Microsoft.Terminal.Core.ControlKeyStates modifiers,
Microsoft.Terminal.Core.Point pixelPosition);
void TouchPressed(Windows.Foundation.Point contactPoint);
void TouchPressed(Microsoft.Terminal.Core.Point contactPoint);
Boolean PointerMoved(MouseButtonState buttonState,
Boolean PointerMoved(UInt32 pointerId,
MouseButtonState buttonState,
UInt32 pointerUpdateKind,
Microsoft.Terminal.Core.ControlKeyStates modifiers,
Boolean focused,
Microsoft.Terminal.Core.Point pixelPosition,
Boolean pointerPressedInBounds);
Microsoft.Terminal.Core.Point pixelPosition);
void TouchMoved(Windows.Foundation.Point newTouchPoint,
Boolean focused);
void TouchMoved(Microsoft.Terminal.Core.Point newTouchPoint);
void PointerReleased(MouseButtonState buttonState,
void PointerReleased(UInt32 pointerId,
MouseButtonState buttonState,
UInt32 pointerUpdateKind,
Microsoft.Terminal.Core.ControlKeyStates modifiers,
Microsoft.Terminal.Core.Point pixelPosition);

View File

@@ -0,0 +1,629 @@
#include "pch.h"
#include "FlatC.h"
#include "winrt/Microsoft.Terminal.Control.h"
#include "winrt/Microsoft.Terminal.Core.h"
#include "../TerminalCore/ControlKeyStates.hpp"
#include "../types/inc/colorTable.hpp"
#include "../inc/DefaultSettings.h"
#include "../inc/cppwinrt_utils.h"
#include "ControlCore.h"
#include "ControlInteractivity.h"
#include <windowsx.h>
#pragma warning(disable : 4100)
#define HARDCODED_PROPERTY(type, name, ...) \
type name() const \
{ \
return type{ __VA_ARGS__ }; \
} \
void name(const type&) \
{ \
}
using namespace winrt::Microsoft::Terminal::Control;
using namespace winrt::Microsoft::Terminal::Core;
using CKS = ::Microsoft::Terminal::Core::ControlKeyStates;
static CKS getControlKeyState() noexcept
{
struct KeyModifier
{
int vkey;
CKS flags;
};
constexpr std::array<KeyModifier, 5> modifiers{ {
{ VK_RMENU, CKS::RightAltPressed },
{ VK_LMENU, CKS::LeftAltPressed },
{ VK_RCONTROL, CKS::RightCtrlPressed },
{ VK_LCONTROL, CKS::LeftCtrlPressed },
{ VK_SHIFT, CKS::ShiftPressed },
} };
CKS flags;
for (const auto& mod : modifiers)
{
const auto state = GetKeyState(mod.vkey);
const auto isDown = state < 0;
if (isDown)
{
flags |= mod.flags;
}
}
return flags;
}
static MouseButtonState MouseButtonStateFromWParam(WPARAM wParam)
{
MouseButtonState state{};
WI_UpdateFlag(state, MouseButtonState::IsLeftButtonDown, WI_IsFlagSet(wParam, MK_LBUTTON));
WI_UpdateFlag(state, MouseButtonState::IsMiddleButtonDown, WI_IsFlagSet(wParam, MK_MBUTTON));
WI_UpdateFlag(state, MouseButtonState::IsRightButtonDown, WI_IsFlagSet(wParam, MK_RBUTTON));
return state;
}
static Point PointFromLParam(LPARAM lParam)
{
return { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
}
struct CsBridgeConnection : public winrt::implements<CsBridgeConnection, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection>
{
void Initialize(IInspectable x) {}
void Start() {}
void WriteInput(winrt::array_view<const char16_t> d)
{
if (_pfnWriteCallback)
{
_pfnWriteCallback(reinterpret_cast<const wchar_t*>(d.data()));
}
}
void Resize(uint32_t r, uint32_t c) {}
void Close() {}
winrt::Microsoft::Terminal::TerminalConnection::ConnectionState State() const noexcept { return winrt::Microsoft::Terminal::TerminalConnection::ConnectionState::Connected; }
WINRT_CALLBACK(TerminalOutput, winrt::Microsoft::Terminal::TerminalConnection::TerminalOutputHandler);
TYPED_EVENT(StateChanged, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection, winrt::Windows::Foundation::IInspectable);
public:
HARDCODED_PROPERTY(winrt::guid, SessionId, winrt::guid{});
public:
PWRITECB _pfnWriteCallback{ nullptr };
void OriginateOutputFromConnection(const wchar_t* data)
{
_TerminalOutputHandlers(winrt::to_hstring(data));
}
};
struct CsBridgeTerminalSettings : winrt::implements<CsBridgeTerminalSettings, IControlSettings, ICoreSettings, IControlAppearance, ICoreAppearance>
{
using IFontAxesMap = winrt::Windows::Foundation::Collections::IMap<winrt::hstring, float>;
using IFontFeatureMap = winrt::Windows::Foundation::Collections::IMap<winrt::hstring, float>;
CsBridgeTerminalSettings()
{
const auto campbellSpan = Microsoft::Console::Utils::CampbellColorTable();
std::transform(campbellSpan.begin(), campbellSpan.end(), std::begin(_theme.ColorTable), [](auto&& color) {
return color;
});
}
~CsBridgeTerminalSettings() = default;
winrt::Microsoft::Terminal::Core::Color GetColorTableEntry(int32_t index) noexcept
{
return til::color{ til::at(_theme.ColorTable, index) };
}
til::color DefaultForeground() const
{
return _theme.DefaultForeground;
}
til::color DefaultBackground() const
{
return _theme.DefaultBackground;
}
til::color SelectionBackground() const
{
return til::color{ _theme.DefaultSelectionBackground };
}
winrt::hstring FontFace() const
{
return _fontFace;
}
float FontSize() const
{
return _fontSize;
}
winrt::Microsoft::Terminal::Core::CursorStyle CursorShape() const
{
return static_cast<winrt::Microsoft::Terminal::Core::CursorStyle>(_theme.CursorStyle);
}
void DefaultForeground(const til::color&) {}
void DefaultBackground(const til::color&) {}
void SelectionBackground(const til::color&) {}
void FontFace(const winrt::hstring&) {}
void FontSize(const float&) {}
void CursorShape(const winrt::Microsoft::Terminal::Core::CursorStyle&) {}
HARDCODED_PROPERTY(int32_t, HistorySize, DEFAULT_HISTORY_SIZE);
HARDCODED_PROPERTY(int32_t, InitialRows, 30);
HARDCODED_PROPERTY(int32_t, InitialCols, 80);
HARDCODED_PROPERTY(bool, SnapOnInput, true);
HARDCODED_PROPERTY(bool, AltGrAliasing, true);
HARDCODED_PROPERTY(til::color, CursorColor, DEFAULT_CURSOR_COLOR);
HARDCODED_PROPERTY(uint32_t, CursorHeight, DEFAULT_CURSOR_HEIGHT);
HARDCODED_PROPERTY(winrt::hstring, WordDelimiters, DEFAULT_WORD_DELIMITERS);
HARDCODED_PROPERTY(bool, CopyOnSelect, false);
HARDCODED_PROPERTY(bool, InputServiceWarning, true);
HARDCODED_PROPERTY(bool, FocusFollowMouse, false);
HARDCODED_PROPERTY(bool, TrimBlockSelection, false);
HARDCODED_PROPERTY(bool, DetectURLs, true);
HARDCODED_PROPERTY(winrt::Windows::Foundation::IReference<winrt::Microsoft::Terminal::Core::Color>, TabColor, nullptr);
HARDCODED_PROPERTY(winrt::Windows::Foundation::IReference<winrt::Microsoft::Terminal::Core::Color>, StartingTabColor, nullptr);
HARDCODED_PROPERTY(winrt::hstring, ProfileName);
HARDCODED_PROPERTY(bool, UseAcrylic, false);
HARDCODED_PROPERTY(float, Opacity, 1.0);
HARDCODED_PROPERTY(winrt::hstring, Padding, DEFAULT_PADDING);
HARDCODED_PROPERTY(winrt::Windows::UI::Text::FontWeight, FontWeight, winrt::Windows::UI::Text::FontWeight{ 400 });
HARDCODED_PROPERTY(IFontAxesMap, FontAxes);
HARDCODED_PROPERTY(IFontFeatureMap, FontFeatures);
HARDCODED_PROPERTY(winrt::hstring, BackgroundImage);
HARDCODED_PROPERTY(float, BackgroundImageOpacity, 1.0);
HARDCODED_PROPERTY(winrt::Windows::UI::Xaml::Media::Stretch, BackgroundImageStretchMode, winrt::Windows::UI::Xaml::Media::Stretch::UniformToFill);
HARDCODED_PROPERTY(winrt::Windows::UI::Xaml::HorizontalAlignment, BackgroundImageHorizontalAlignment, winrt::Windows::UI::Xaml::HorizontalAlignment::Center);
HARDCODED_PROPERTY(winrt::Windows::UI::Xaml::VerticalAlignment, BackgroundImageVerticalAlignment, winrt::Windows::UI::Xaml::VerticalAlignment::Center);
HARDCODED_PROPERTY(winrt::Microsoft::Terminal::Control::IKeyBindings, KeyBindings, nullptr);
HARDCODED_PROPERTY(winrt::hstring, Commandline);
HARDCODED_PROPERTY(winrt::hstring, StartingDirectory);
HARDCODED_PROPERTY(winrt::hstring, StartingTitle);
HARDCODED_PROPERTY(bool, SuppressApplicationTitle);
HARDCODED_PROPERTY(winrt::hstring, EnvironmentVariables);
HARDCODED_PROPERTY(winrt::Microsoft::Terminal::Control::ScrollbarState, ScrollState, winrt::Microsoft::Terminal::Control::ScrollbarState::Visible);
HARDCODED_PROPERTY(winrt::Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode, winrt::Microsoft::Terminal::Control::TextAntialiasingMode::Grayscale);
HARDCODED_PROPERTY(bool, RetroTerminalEffect, false);
HARDCODED_PROPERTY(bool, ForceFullRepaintRendering, false);
HARDCODED_PROPERTY(bool, SoftwareRendering, false);
HARDCODED_PROPERTY(bool, ForceVTInput, false);
HARDCODED_PROPERTY(winrt::hstring, PixelShaderPath);
HARDCODED_PROPERTY(winrt::hstring, PixelShaderImagePath);
HARDCODED_PROPERTY(bool, IntenseIsBright);
HARDCODED_PROPERTY(bool, IntenseIsBold);
HARDCODED_PROPERTY(bool, ShowMarks);
HARDCODED_PROPERTY(bool, UseBackgroundImageForWindow);
HARDCODED_PROPERTY(bool, AutoMarkPrompts);
HARDCODED_PROPERTY(bool, VtPassthrough);
HARDCODED_PROPERTY(bool, UseAtlasEngine, false);
HARDCODED_PROPERTY(AdjustTextMode, AdjustIndistinguishableColors, AdjustTextMode::Never);
HARDCODED_PROPERTY(bool, RightClickContextMenu, false);
HARDCODED_PROPERTY(winrt::hstring, CellWidth, L"");
HARDCODED_PROPERTY(winrt::hstring, CellHeight, L"");
HARDCODED_PROPERTY(bool, RepositionCursorWithMouse, false);
HARDCODED_PROPERTY(bool, EnableUnfocusedAcrylic, false);
HARDCODED_PROPERTY(bool, RainbowSuggestions, false);
HARDCODED_PROPERTY(bool, AllowVtClipboardWrite, true);
HARDCODED_PROPERTY(bool, AllowVtChecksumReport, false);
HARDCODED_PROPERTY(winrt::hstring, AnswerbackMessage, L"");
HARDCODED_PROPERTY(winrt::Microsoft::Terminal::Control::PathTranslationStyle, PathTranslationStyle, winrt::Microsoft::Terminal::Control::PathTranslationStyle::None);
HARDCODED_PROPERTY(winrt::Microsoft::Terminal::Control::DefaultInputScope, DefaultInputScope, winrt::Microsoft::Terminal::Control::DefaultInputScope::Default);
HARDCODED_PROPERTY(winrt::Microsoft::Terminal::Control::TextMeasurement, TextMeasurement, winrt::Microsoft::Terminal::Control::TextMeasurement::Graphemes);
HARDCODED_PROPERTY(bool, DisablePartialInvalidation, false);
HARDCODED_PROPERTY(winrt::Microsoft::Terminal::Control::GraphicsAPI, GraphicsAPI, winrt::Microsoft::Terminal::Control::GraphicsAPI::Automatic);
HARDCODED_PROPERTY(winrt::Microsoft::Terminal::Control::CopyFormat, CopyFormatting, winrt::Microsoft::Terminal::Control::CopyFormat::All);
HARDCODED_PROPERTY(bool, EnableColorGlyphs, true);
HARDCODED_PROPERTY(bool, EnableBuiltinGlyphs, true);
HARDCODED_PROPERTY(winrt::guid, SessionId, winrt::guid{});
public:
void SetTheme(TerminalTheme theme, LPCWSTR fontFamily, til::CoordType fontSize, int newDpi)
{
_theme = std::move(theme);
_fontFace = fontFamily;
_fontSize = static_cast<float>(fontSize);
}
private:
TerminalTheme _theme;
winrt::hstring _fontFace{ L"Cascadia Mono" };
float _fontSize = 12.0f;
};
struct HwndTerminal
{
static constexpr LPCWSTR term_window_class = L"HwndTerminalClass";
static LRESULT CALLBACK HwndTerminalWndProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam) noexcept
try
{
#pragma warning(suppress : 26490) // Win32 APIs can only store void*, have to use reinterpret_cast
HwndTerminal* terminal = reinterpret_cast<HwndTerminal*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
if (terminal)
{
return terminal->WindowProc(hwnd, uMsg, wParam, lParam);
}
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
return 0;
}
static bool RegisterTermClass(HINSTANCE hInstance) noexcept
{
WNDCLASSW wc;
if (GetClassInfoW(hInstance, term_window_class, &wc))
{
return true;
}
wc.style = 0;
wc.lpfnWndProc = HwndTerminal::HwndTerminalWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = nullptr;
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
wc.hbrBackground = nullptr;
wc.lpszMenuName = nullptr;
wc.lpszClassName = term_window_class;
return RegisterClassW(&wc) != 0;
}
LRESULT WindowProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam) noexcept
{
switch (uMsg)
{
case WM_LBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONDOWN:
SetCapture(_hwnd.get());
_interactivity->PointerPressed(
0, // Mouse
MouseButtonStateFromWParam(wParam),
uMsg,
std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count(),
getControlKeyState(),
PointFromLParam(lParam));
return 0;
case WM_MOUSEMOVE:
_interactivity->PointerMoved(
0, // Mouse
MouseButtonStateFromWParam(wParam),
WM_MOUSEMOVE,
getControlKeyState(),
PointFromLParam(lParam));
return 0;
case WM_LBUTTONUP:
case WM_MBUTTONUP:
case WM_RBUTTONUP:
_interactivity->PointerReleased(
0, // Mouse
MouseButtonStateFromWParam(wParam),
uMsg,
getControlKeyState(),
PointFromLParam(lParam));
ReleaseCapture();
return 0;
case WM_POINTERDOWN:
if (!IS_POINTER_INCONTACT_WPARAM(wParam))
{
break;
}
SetCapture(_hwnd.get());
_interactivity->TouchPressed(PointFromLParam(lParam));
return 0;
case WM_POINTERUPDATE:
if (!IS_POINTER_INCONTACT_WPARAM(wParam))
{
break;
}
_interactivity->TouchMoved(PointFromLParam(lParam));
return 0;
case WM_POINTERUP:
if (!IS_POINTER_INCONTACT_WPARAM(wParam))
{
break;
}
_interactivity->TouchReleased();
ReleaseCapture();
return 0;
case WM_MOUSEWHEEL:
if (_interactivity->MouseWheel(getControlKeyState(), GET_WHEEL_DELTA_WPARAM(wParam), PointFromLParam(lParam), MouseButtonStateFromWParam(wParam)))
{
return 0;
}
break;
case WM_SETFOCUS:
_interactivity->GotFocus();
_focused = true;
_core->ApplyAppearance(_focused);
break;
case WM_KILLFOCUS:
_interactivity->LostFocus();
_focused = true;
_core->ApplyAppearance(_focused);
break;
}
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
wil::unique_hwnd _hwnd;
HwndTerminal(HWND parentHwnd)
{
HINSTANCE hInstance = wil::GetModuleInstanceHandle();
if (RegisterTermClass(hInstance))
{
_hwnd.reset(CreateWindowExW(
0,
term_window_class,
nullptr,
WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE,
0,
0,
0,
0,
parentHwnd,
nullptr,
hInstance,
nullptr));
#pragma warning(suppress : 26490) // Win32 APIs can only store void*, so we have to use reinterpret_cast
SetWindowLongPtr(_hwnd.get(), GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
}
_settingsBridge = winrt::make_self<CsBridgeTerminalSettings>();
_connection = winrt::make_self<CsBridgeConnection>();
_interactivity = winrt::make_self<implementation::ControlInteractivity>(*_settingsBridge, nullptr, *_connection);
_core.copy_from(winrt::get_self<implementation::ControlCore>(_interactivity->Core()));
_core->ScrollPositionChanged({ this, &HwndTerminal::_scrollPositionChanged });
_interactivity->ScrollPositionChanged({ this, &HwndTerminal::_scrollPositionChanged });
}
/*( PUBLIC API )*/
HRESULT SendOutput(LPCWSTR data)
{
_connection->OriginateOutputFromConnection(data);
return S_OK;
}
HRESULT RegisterScrollCallback(PSCROLLCB callback)
{
_scrollCallback = callback;
return S_OK;
}
HRESULT TriggerResize(_In_ til::CoordType width, _In_ til::CoordType height, _Out_ til::size* dimensions)
{
if (!_initialized)
return S_FALSE;
SetWindowPos(_hwnd.get(), nullptr, 0, 0, width, height, 0);
// **NOTE** The sizes we get here are unscaled ...
auto dpi = GetDpiForWindow(_hwnd.get());
float w = static_cast<float>(width * USER_DEFAULT_SCREEN_DPI) / dpi;
float h = static_cast<float>(height * USER_DEFAULT_SCREEN_DPI) / dpi;
// ... but ControlCore expects scaled sizes.
_core->SizeChanged(w, h);
// TODO(DH): ControlCore has no API that returns the new size in cells
//wil::assign_to_opt_param(dimensions, /*thing*/);
return S_OK;
}
HRESULT TriggerResizeWithDimension(_In_ til::size dimensions, _Out_ til::size* dimensionsInPixels)
{
if (!_initialized)
return S_FALSE;
winrt::Windows::Foundation::Size outSizeInPixels;
_core->ResizeToDimensions(dimensions.width, dimensions.height, outSizeInPixels);
wil::assign_to_opt_param(dimensionsInPixels, til::size{ til::math::rounding, outSizeInPixels });
return S_OK;
}
HRESULT CalculateResize(_In_ til::CoordType width, _In_ til::CoordType height, _Out_ til::size* dimensions)
{
// TODO(DH): It seems weird to have to do this manually.
auto fontSizeInPx = _core->FontSize();
wil::assign_to_opt_param(dimensions, til::size{
static_cast<til::CoordType>(width / fontSizeInPx.Width),
static_cast<til::CoordType>(height / fontSizeInPx.Height),
});
return S_OK;
}
HRESULT DpiChanged(int newDpi)
{
_core->ScaleChanged((float)newDpi / 96.0f);
return S_OK;
}
HRESULT UserScroll(int viewTop)
{
_interactivity->UpdateScrollbar(static_cast<float>(viewTop) /* TODO(DH) */);
return S_OK;
}
HRESULT GetSelection(const wchar_t** out)
{
auto strings = _core->SelectedText(true);
auto concatenated = std::accumulate(std::begin(strings), std::end(strings), std::wstring{}, [](auto&& l, auto&& r) {
return l + r;
});
auto returnText = wil::make_cotaskmem_string_nothrow(concatenated.c_str());
*out = returnText.release();
return S_OK;
}
HRESULT IsSelectionActive(bool* out)
{
*out = _core->HasSelection();
return S_OK;
}
HRESULT SetTheme(TerminalTheme theme, LPCWSTR fontFamily, til::CoordType fontSize, int newDpi)
{
_settingsBridge->SetTheme(theme, fontFamily, fontSize, newDpi);
_core->UpdateSettings(*_settingsBridge, nullptr);
_interactivity->UpdateSettings();
_core->ScaleChanged((static_cast<float>(newDpi) / USER_DEFAULT_SCREEN_DPI));
_core->ApplyAppearance(_focused);
return S_OK;
}
HRESULT RegisterWriteCallback(PWRITECB callback)
{
_connection->_pfnWriteCallback = callback;
return S_OK;
}
HRESULT SendKeyEvent(WORD vkey, WORD scanCode, WORD flags, bool keyDown)
{
_core->TrySendKeyEvent(vkey, scanCode, getControlKeyState(), keyDown);
return S_OK;
}
HRESULT SendCharEvent(wchar_t ch, WORD flags, WORD scanCode)
{
_core->SendCharEvent(ch, scanCode, getControlKeyState());
return S_OK;
}
HRESULT SetCursorVisible(const bool visible)
{
_core->CursorOn(visible);
return S_OK;
}
void Initialize()
{
RECT windowRect;
GetWindowRect(_hwnd.get(), &windowRect);
auto dpi = GetDpiForWindow(_hwnd.get());
// BODGY: the +/-1 is because ControlCore will ignore an Initialize with zero size (oops)
// because in the old days, TermControl would accidentally try to resize the Swap Chain to 0x0 (oops)
// and therefore resize the connection to 0x0 (oops)
_core->InitializeWithHwnd(
gsl::narrow_cast<float>(windowRect.right - windowRect.left + 1),
gsl::narrow_cast<float>(windowRect.bottom - windowRect.top + 1),
(static_cast<float>(dpi) / USER_DEFAULT_SCREEN_DPI),
reinterpret_cast<uint64_t>(_hwnd.get()));
_interactivity->Initialize();
_core->ApplyAppearance(_focused);
int blinkTime = GetCaretBlinkTime();
auto animationsEnabled = TRUE;
SystemParametersInfoW(SPI_GETCLIENTAREAANIMATION, 0, &animationsEnabled, 0);
_core->CursorBlinkTime(std::chrono::milliseconds(blinkTime == INFINITE ? 0 : blinkTime));
_core->VtBlinkEnabled(animationsEnabled);
_core->EnablePainting();
_initialized = true;
}
private:
winrt::com_ptr<CsBridgeConnection> _connection;
winrt::com_ptr<CsBridgeTerminalSettings> _settingsBridge;
winrt::com_ptr<implementation::ControlInteractivity> _interactivity{ nullptr };
winrt::com_ptr<implementation::ControlCore> _core{ nullptr };
bool _initialized{ false };
bool _focused{ false };
PSCROLLCB _scrollCallback{};
void _scrollPositionChanged(const winrt::Windows::Foundation::IInspectable& i, const ScrollPositionChangedArgs& update)
{
if (_scrollCallback)
{
_scrollCallback(update.ViewTop(), update.ViewHeight(), update.BufferSize());
}
}
};
__declspec(dllexport) HRESULT _stdcall CreateTerminal(HWND parentHwnd, _Out_ void** hwnd, _Out_ PTERM* terminal)
{
auto inner = new HwndTerminal{ parentHwnd };
*terminal = inner;
*hwnd = inner->_hwnd.get();
inner->Initialize();
return S_OK;
}
__declspec(dllexport) void _stdcall DestroyTerminal(PTERM terminal)
{
delete (HwndTerminal*)terminal;
}
// Generate all of the C->C++ bridge functions.
#define API_NAME(name) Terminal##name
#define GENERATOR_0(name) \
__declspec(dllexport) HRESULT _stdcall API_NAME(name)(PTERM terminal) \
try \
{ \
return ((HwndTerminal*)(terminal))->name(); \
} \
CATCH_RETURN()
#define GENERATOR_1(name, t1, a1) \
__declspec(dllexport) HRESULT _stdcall API_NAME(name)(PTERM terminal, t1 a1) \
try \
{ \
return ((HwndTerminal*)(terminal))->name(a1); \
} \
CATCH_RETURN()
#define GENERATOR_2(name, t1, a1, t2, a2) \
__declspec(dllexport) HRESULT _stdcall API_NAME(name)(PTERM terminal, t1 a1, t2 a2) \
try \
{ \
return ((HwndTerminal*)(terminal))->name(a1, a2); \
} \
CATCH_RETURN()
#define GENERATOR_3(name, t1, a1, t2, a2, t3, a3) \
__declspec(dllexport) HRESULT _stdcall API_NAME(name)(PTERM terminal, t1 a1, t2 a2, t3 a3) \
try \
{ \
return ((HwndTerminal*)(terminal))->name(a1, a2, a3); \
} \
CATCH_RETURN()
#define GENERATOR_4(name, t1, a1, t2, a2, t3, a3, t4, a4) \
__declspec(dllexport) HRESULT _stdcall API_NAME(name)(PTERM terminal, t1 a1, t2 a2, t3 a3, t4 a4) \
try \
{ \
return ((HwndTerminal*)(terminal))->name(a1, a2, a3, a4); \
} \
CATCH_RETURN()
#define GENERATOR_N(name, t1, a1, t2, a2, t3, a3, t4, a4, MACRO, ...) MACRO
#define GENERATOR(...) \
GENERATOR_N(__VA_ARGS__, GENERATOR_4, GENERATOR_4, GENERATOR_3, GENERATOR_3, GENERATOR_2, GENERATOR_2, GENERATOR_1, GENERATOR_1, GENERATOR_0) \
(__VA_ARGS__)
TERMINAL_API_TABLE(GENERATOR)
#undef GENERATOR_0
#undef GENERATOR_1
#undef GENERATOR_2
#undef GENERATOR_3
#undef GENERATOR_4
#undef GENERATOR_N
#undef GENERATOR
#undef API_NAME

View File

@@ -0,0 +1,64 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
// Keep in sync with TerminalTheme.cs
typedef struct _TerminalTheme
{
COLORREF DefaultBackground;
COLORREF DefaultForeground;
COLORREF DefaultSelectionBackground;
uint32_t CursorStyle; // This will be converted to DispatchTypes::CursorStyle (size_t), but C# cannot marshal an enum type and have it fit in a size_t.
COLORREF ColorTable[16];
} TerminalTheme;
using PTERM = void*;
using PSCROLLCB = void(_stdcall*)(int, int, int);
using PWRITECB = void(_stdcall*)(const wchar_t*);
#define TERMINAL_API_TABLE(XX) \
XX(SendOutput, LPCWSTR, data) \
XX(RegisterScrollCallback, PSCROLLCB, callback) \
XX(TriggerResize, _In_ til::CoordType, width, _In_ til::CoordType, height, _Out_ til::size*, dimensions) \
XX(TriggerResizeWithDimension, _In_ til::size, dimensions, _Out_ til::size*, dimensionsInPixels) \
XX(CalculateResize, _In_ til::CoordType, width, _In_ til::CoordType, height, _Out_ til::size*, dimensions) \
XX(DpiChanged, int, newDpi) \
XX(UserScroll, int, viewTop) \
XX(GetSelection, const wchar_t**, out) \
XX(IsSelectionActive, bool*, out) \
XX(SetTheme, TerminalTheme, theme, LPCWSTR, fontFamily, til::CoordType, fontSize, int, newDpi) \
XX(RegisterWriteCallback, PWRITECB, callback) \
XX(SendKeyEvent, WORD, vkey, WORD, scanCode, WORD, flags, bool, keyDown) \
XX(SendCharEvent, wchar_t, ch, WORD, flags, WORD, scanCode) \
XX(SetCursorVisible, const bool, visible)
extern "C" {
#define API_NAME(name) Terminal##name
#define GENERATOR_0(name) \
__declspec(dllexport) HRESULT _stdcall API_NAME(name)(PTERM);
#define GENERATOR_1(name, t1, a1) \
__declspec(dllexport) HRESULT _stdcall API_NAME(name)(PTERM, t1);
#define GENERATOR_2(name, t1, a1, t2, a2) \
__declspec(dllexport) HRESULT _stdcall API_NAME(name)(PTERM, t1, t2);
#define GENERATOR_3(name, t1, a1, t2, a2, t3, a3) \
__declspec(dllexport) HRESULT _stdcall API_NAME(name)(PTERM, t1, t2, t3);
#define GENERATOR_4(name, t1, a1, t2, a2, t3, a3, t4, a4) \
__declspec(dllexport) HRESULT _stdcall API_NAME(name)(PTERM, t1, t2, t3, t4);
#define GENERATOR_N(name, t1, a1, t2, a2, t3, a3, t4, a4, MACRO, ...) MACRO
#define GENERATOR(...) \
GENERATOR_N(__VA_ARGS__, GENERATOR_4, GENERATOR_4, GENERATOR_3, GENERATOR_3, GENERATOR_2, GENERATOR_2, GENERATOR_1, GENERATOR_1, GENERATOR_0) \
(__VA_ARGS__)
TERMINAL_API_TABLE(GENERATOR)
#undef GENERATOR
#undef GENERATOR_0
#undef GENERATOR_1
#undef GENERATOR_2
#undef GENERATOR_3
#undef GENERATOR_4
#undef GENERATOR_N
#undef API_NAME
__declspec(dllexport) HRESULT _stdcall CreateTerminal(HWND parentHwnd, _Out_ void** hwnd, _Out_ PTERM* terminal);
__declspec(dllexport) void _stdcall DestroyTerminal(PTERM terminal);
};

View File

@@ -272,9 +272,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
TermControl::TermControl(Control::ControlInteractivity content) :
_interactivity{ content },
_isInternalScrollBarUpdate{ false },
_autoScrollVelocity{ 0 },
_autoScrollingPointerPoint{ std::nullopt },
_lastAutoScrollUpdateTime{ std::nullopt },
_searchBox{ nullptr }
{
InitializeComponent();
@@ -394,10 +391,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_revokers.coreScrollPositionChanged = _core.ScrollPositionChanged(winrt::auto_revoke, { get_weak(), &TermControl::_ScrollPositionChanged });
_revokers.WarningBell = _core.WarningBell(winrt::auto_revoke, { get_weak(), &TermControl::_coreWarningBell });
static constexpr auto AutoScrollUpdateInterval = std::chrono::microseconds(static_cast<int>(1.0 / 30.0 * 1000000));
_autoScrollTimer.Interval(AutoScrollUpdateInterval);
_autoScrollTimer.Tick({ get_weak(), &TermControl::_UpdateAutoScroll });
_ApplyUISettings();
_originalPrimaryElements = winrt::single_threaded_observable_vector<Controls::ICommandBarElement>();
@@ -1942,21 +1935,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation
Focus(FocusState::Pointer);
}
// Mark that this pointer event actually started within our bounds.
// We'll need this later, for PointerMoved events.
_pointerPressedInBounds = true;
if (type == Windows::Devices::Input::PointerDeviceType::Touch)
{
// NB: I don't think this is correct because the touch should be in the center of the rect.
// I suspect the point.Position() would be correct.
const auto contactRect = point.Properties().ContactRect();
_interactivity.TouchPressed({ contactRect.X, contactRect.Y });
til::point newTouchPoint{ til::math::rounding, contactRect.X, contactRect.Y };
_interactivity.TouchPressed(newTouchPoint.to_core_point());
}
else
{
const auto cursorPosition = point.Position();
_interactivity.PointerPressed(TermControl::GetPressedMouseButtons(point),
_interactivity.PointerPressed(point.PointerId(),
TermControl::GetPressedMouseButtons(point),
TermControl::GetPointerUpdateKind(point),
point.Timestamp(),
ControlKeyStates{ args.KeyModifiers() },
@@ -1995,51 +1986,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation
if (type == Windows::Devices::Input::PointerDeviceType::Mouse ||
type == Windows::Devices::Input::PointerDeviceType::Pen)
{
auto suppressFurtherHandling = _interactivity.PointerMoved(TermControl::GetPressedMouseButtons(point),
auto suppressFurtherHandling = _interactivity.PointerMoved(point.PointerId(),
TermControl::GetPressedMouseButtons(point),
TermControl::GetPointerUpdateKind(point),
ControlKeyStates(args.KeyModifiers()),
_focused,
pixelPosition,
_pointerPressedInBounds);
// GH#9109 - Only start an auto-scroll when the drag actually
// started within our bounds. Otherwise, someone could start a drag
// outside the terminal control, drag into the padding, and trick us
// into starting to scroll.
if (!suppressFurtherHandling && _focused && _pointerPressedInBounds && point.Properties().IsLeftButtonPressed())
{
// We want to find the distance relative to the bounds of the
// SwapChainPanel, not the entire control. If they drag out of
// the bounds of the text, into the padding, we still what that
// to auto-scroll
const auto cursorBelowBottomDist = cursorPosition.Y - SwapChainPanel().Margin().Top - SwapChainPanel().ActualHeight();
const auto cursorAboveTopDist = -1 * cursorPosition.Y + SwapChainPanel().Margin().Top;
constexpr auto MinAutoScrollDist = 2.0; // Arbitrary value
auto newAutoScrollVelocity = 0.0;
if (cursorBelowBottomDist > MinAutoScrollDist)
{
newAutoScrollVelocity = _GetAutoScrollSpeed(cursorBelowBottomDist);
}
else if (cursorAboveTopDist > MinAutoScrollDist)
{
newAutoScrollVelocity = -1.0 * _GetAutoScrollSpeed(cursorAboveTopDist);
}
if (newAutoScrollVelocity != 0)
{
_TryStartAutoScroll(point, newAutoScrollVelocity);
}
else
{
_TryStopAutoScroll(ptr.PointerId());
}
}
pixelPosition);
/* TODO(DH) */ UNREFERENCED_PARAMETER(suppressFurtherHandling);
}
else if (type == Windows::Devices::Input::PointerDeviceType::Touch)
{
const auto contactRect = point.Properties().ContactRect();
_interactivity.TouchMoved({ contactRect.X, contactRect.Y }, _focused);
til::point newTouchPoint{ til::math::rounding, contactRect.X, contactRect.Y };
_interactivity.TouchMoved(newTouchPoint.to_core_point());
}
args.Handled(true);
@@ -2059,8 +2018,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return;
}
_pointerPressedInBounds = false;
const auto ptr = args.Pointer();
const auto point = args.GetCurrentPoint(*this);
const auto cursorPosition = point.Position();
@@ -2072,7 +2029,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
if (type == Windows::Devices::Input::PointerDeviceType::Mouse ||
type == Windows::Devices::Input::PointerDeviceType::Pen)
{
_interactivity.PointerReleased(TermControl::GetPressedMouseButtons(point),
_interactivity.PointerReleased(point.PointerId(),
TermControl::GetPressedMouseButtons(point),
TermControl::GetPointerUpdateKind(point),
ControlKeyStates(args.KeyModifiers()),
pixelPosition);
@@ -2082,8 +2040,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_interactivity.TouchReleased();
}
_TryStopAutoScroll(ptr.PointerId());
args.Handled(true);
}
@@ -2238,86 +2194,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return false;
}
// Method Description:
// - Starts new pointer related auto scroll behavior, or continues existing one.
// Does nothing when there is already auto scroll associated with another pointer.
// Arguments:
// - pointerPoint: info about pointer that causes auto scroll. Pointer's position
// is later used to update selection.
// - scrollVelocity: target velocity of scrolling in characters / sec
void TermControl::_TryStartAutoScroll(const Windows::UI::Input::PointerPoint& pointerPoint, const double scrollVelocity)
{
// Allow only one pointer at the time
if (!_autoScrollingPointerPoint ||
_autoScrollingPointerPoint->PointerId() == pointerPoint.PointerId())
{
_autoScrollingPointerPoint = pointerPoint;
_autoScrollVelocity = scrollVelocity;
// If this is first time the auto scroll update is about to be called,
// kick-start it by initializing its time delta as if it started now
if (!_lastAutoScrollUpdateTime)
{
_lastAutoScrollUpdateTime = std::chrono::high_resolution_clock::now();
}
// Apparently this check is not necessary but greatly improves performance
if (!_autoScrollTimer.IsEnabled())
{
_autoScrollTimer.Start();
}
}
}
// Method Description:
// - Stops auto scroll if it's active and is associated with supplied pointer id.
// Arguments:
// - pointerId: id of pointer for which to stop auto scroll
void TermControl::_TryStopAutoScroll(const uint32_t pointerId)
{
if (_autoScrollingPointerPoint &&
pointerId == _autoScrollingPointerPoint->PointerId())
{
_autoScrollingPointerPoint = std::nullopt;
_autoScrollVelocity = 0;
_lastAutoScrollUpdateTime = std::nullopt;
// Apparently this check is not necessary but greatly improves performance
if (_autoScrollTimer.IsEnabled())
{
_autoScrollTimer.Stop();
}
}
}
// Method Description:
// - Called continuously to gradually scroll viewport when user is mouse
// selecting outside it (to 'follow' the cursor).
// Arguments:
// - none
void TermControl::_UpdateAutoScroll(const Windows::Foundation::IInspectable& /* sender */,
const Windows::Foundation::IInspectable& /* e */)
{
if (_autoScrollVelocity != 0)
{
const auto timeNow = std::chrono::high_resolution_clock::now();
if (_lastAutoScrollUpdateTime)
{
static constexpr auto microSecPerSec = 1000000.0;
const auto deltaTime = std::chrono::duration_cast<std::chrono::microseconds>(timeNow - *_lastAutoScrollUpdateTime).count() / microSecPerSec;
ScrollBar().Value(ScrollBar().Value() + _autoScrollVelocity * deltaTime);
if (_autoScrollingPointerPoint)
{
_SetEndSelectionPointAtCursor(_autoScrollingPointerPoint->Position());
}
}
_lastAutoScrollUpdateTime = timeNow;
}
}
// Method Description:
// - Event handler for the GotFocus event. This is used to...
// - enable accessibility notifications for this TermControl
@@ -2618,7 +2494,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// On Win10 we don't destroy window threads due to bugs in DesktopWindowXamlSource.
// In turn, we leak TermControl instances. This results in constant HWND messages
// while the thread is supposed to be idle. Stop these timers avoids this.
_autoScrollTimer.Stop();
_bellLightTimer.Stop();
// This is absolutely crucial, as the TSF code tries to hold a strong reference to _tsfDataProvider,
@@ -2766,7 +2641,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// UWP XAML scrollbars aren't guaranteed to be the same size as the
// ComCtl scrollbars, but it's certainly close enough.
const auto scrollbarSize = GetSystemMetricsForDpi(SM_CXVSCROLL, dpi);
auto scrollbarSize = GetSystemMetrics(SM_CXVSCROLL);
scrollbarSize = gsl::narrow_cast<decltype(scrollbarSize)>(scrollbarSize * (dpi / 96));
float width = cols * static_cast<float>(actualFontSize.width);
@@ -3016,20 +2892,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
};
}
// Method Description:
// - Calculates speed of single axis of auto scrolling. It has to allow for both
// fast and precise selection.
// Arguments:
// - cursorDistanceFromBorder: distance from viewport border to cursor, in pixels. Must be non-negative.
// Return Value:
// - positive speed in characters / sec
double TermControl::_GetAutoScrollSpeed(double cursorDistanceFromBorder) const
{
// The numbers below just feel well, feel free to change.
// TODO: Maybe account for space beyond border that user has available
return std::pow(cursorDistanceFromBorder, 2.0) / 25.0 + 2.0;
}
// Method Description:
// - Async handler for the "Drop" event. If a file was dropped onto our
// root, we'll try to get the path of the file dropped onto us, and write

View File

@@ -301,14 +301,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool _isInternalScrollBarUpdate;
// Auto scroll occurs when user, while selecting, drags cursor outside
// viewport. View is then scrolled to 'follow' the cursor.
double _autoScrollVelocity;
std::optional<Windows::UI::Input::PointerPoint> _autoScrollingPointerPoint;
SafeDispatcherTimer _autoScrollTimer;
std::optional<std::chrono::high_resolution_clock::time_point> _lastAutoScrollUpdateTime;
bool _pointerPressedInBounds{ false };
winrt::Windows::UI::Composition::ScalarKeyFrameAnimation _bellLightAnimation{ nullptr };
winrt::Windows::UI::Composition::ScalarKeyFrameAnimation _bellDarkAnimation{ nullptr };
SafeDispatcherTimer _bellLightTimer;
@@ -386,8 +378,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void _BellLightOff(const Windows::Foundation::IInspectable& sender, const Windows::Foundation::IInspectable& e);
void _SetEndSelectionPointAtCursor(const Windows::Foundation::Point& cursorPosition);
void _SwapChainSizeChanged(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::SizeChangedEventArgs& e);
void _SwapChainScaleChanged(const Windows::UI::Xaml::Controls::SwapChainPanel& sender, const Windows::Foundation::IInspectable& args);
@@ -396,10 +386,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool _CapturePointer(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e);
bool _ReleasePointerCapture(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e);
void _TryStartAutoScroll(const Windows::UI::Input::PointerPoint& pointerPoint, const double scrollVelocity);
void _TryStopAutoScroll(const uint32_t pointerId);
void _UpdateAutoScroll(const Windows::Foundation::IInspectable& sender, const Windows::Foundation::IInspectable& e);
void _KeyHandler(const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e, const bool keyDown);
bool _KeyHandler(WORD vkey, WORD scanCode, ::Microsoft::Terminal::Core::ControlKeyStates modifiers, bool keyDown);
static ::Microsoft::Terminal::Core::ControlKeyStates _GetPressedModifierKeys() noexcept;
@@ -410,8 +396,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
winrt::Windows::Foundation::Point _toControlOrigin(const til::point terminalPosition);
Core::Point _toTerminalOrigin(winrt::Windows::Foundation::Point cursorPosition);
double _GetAutoScrollSpeed(double cursorDistanceFromBorder) const;
void _Search(const winrt::hstring& text, const bool goForward, const bool caseSensitive, const bool regularExpression);
void _SearchChanged(const winrt::hstring& text, const bool goForward, const bool caseSensitive, const bool regularExpression);
void _CloseSearchBoxControl(const winrt::Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args);

View File

@@ -30,6 +30,7 @@
<!-- ========================= Headers ======================== -->
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="FlatC.h" />
<ClInclude Include="ControlCore.h">
<DependentUpon>ControlCore.idl</DependentUpon>
</ClInclude>
@@ -61,14 +62,13 @@
<DependentUpon>InteractivityAutomationPeer.idl</DependentUpon>
</ClInclude>
<ClInclude Include="XamlUiaTextRange.h" />
<ClInclude Include="HwndTerminal.hpp" />
<ClInclude Include="HwndTerminalAutomationPeer.hpp" />
</ItemGroup>
<!-- ========================= Cpp Files ======================== -->
<ItemGroup>
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="FlatC.cpp" />
<ClCompile Include="ControlCore.cpp">
<DependentUpon>ControlCore.idl</DependentUpon>
</ClCompile>
@@ -102,8 +102,6 @@
<DependentUpon>InteractivityAutomationPeer.idl</DependentUpon>
</ClCompile>
<ClCompile Include="XamlUiaTextRange.cpp" />
<ClCompile Include="HwndTerminal.cpp" />
<ClCompile Include="HwndTerminalAutomationPeer.cpp" />
</ItemGroup>
<!-- ========================= idl Files ======================== -->
<ItemGroup>
@@ -160,6 +158,7 @@
<CopyLocalSatelliteAssemblies>false</CopyLocalSatelliteAssemblies>
</ProjectReference>
<ProjectReference Include="$(OpenConsoleDir)src\cascadia\WinRTUtils\WinRTUtils.vcxproj">
<Private>true</Private>
<Project>{CA5CAD1A-039A-4929-BA2A-8BEB2E4106FE}</Project>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>

View File

@@ -7,17 +7,16 @@ EXPORTS
AvoidBuggyTSFConsoleFlags
CreateTerminal
DestroyTerminal
TerminalCalculateResize
TerminalDpiChanged
TerminalGetSelection
TerminalIsSelectionActive
TerminalRegisterScrollCallback
TerminalRegisterWriteCallback
TerminalSendCharEvent
TerminalSendKeyEvent
TerminalSendOutput
TerminalSetFocused
TerminalSetTheme
TerminalRegisterScrollCallback
TerminalTriggerResize
TerminalTriggerResizeWithDimension
TerminalCalculateResize
TerminalDpiChanged
TerminalUserScroll
TerminalGetSelection
TerminalIsSelectionActive
TerminalSetTheme
TerminalRegisterWriteCallback
TerminalSendKeyEvent
TerminalSendCharEvent

View File

@@ -115,4 +115,12 @@
<!-- This -must- go after cppwinrt.build.post.props because that includes many VS-provided props including appcontainer.common.props, which stomps on what cppwinrt.targets did. -->
<Import Project="$(OpenConsoleDir)src\common.nugetversions.targets" />
<!--LATE LATE LATE-->
<ItemDefinitionGroup>
<Link>
<AdditionalDependencies>delayimp.lib;uiautomationcore.lib;oleaut32.lib;onecoreuap.lib;user32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<DelayLoadDLLs>uiautomationcore.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
</Link>
</ItemDefinitionGroup>
</Project>

View File

@@ -312,10 +312,13 @@ namespace ControlUnitTests
const til::size fontSize{ 9, 21 };
interactivity->GotFocus();
Log::Comment(L"Click on the terminal");
const til::point terminalPosition0{ 0, 0 };
const auto cursorPosition0 = terminalPosition0 * fontSize;
interactivity->PointerPressed(leftMouseDown,
interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -328,29 +331,28 @@ namespace ControlUnitTests
// move not quite a whole cell, but enough to start a selection
const til::point terminalPosition1{ 0, 0 };
const til::point cursorPosition1{ 6, 0 };
interactivity->PointerMoved(leftMouseDown,
interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition1.to_core_point(),
true);
cursorPosition1.to_core_point());
Log::Comment(L"Verify that there's one selection");
VERIFY_IS_TRUE(core->HasSelection());
Log::Comment(L"Drag the mouse down a whole row");
const til::point terminalPosition2{ 1, 1 };
const auto cursorPosition2 = terminalPosition2 * fontSize;
interactivity->PointerMoved(leftMouseDown,
interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition2.to_core_point(),
true);
cursorPosition2.to_core_point());
Log::Comment(L"Verify that there's now two selections (one on each row)");
VERIFY_IS_TRUE(core->HasSelection());
Log::Comment(L"Release the mouse");
interactivity->PointerReleased(noMouseDown,
interactivity->PointerReleased(0,
noMouseDown,
WM_LBUTTONUP, //pointerUpdateKind
modifiers,
cursorPosition2.to_core_point());
@@ -360,7 +362,8 @@ namespace ControlUnitTests
Log::Comment(L"click outside the current selection");
const til::point terminalPosition3{ 2, 2 };
const auto cursorPosition3 = terminalPosition3 * fontSize;
interactivity->PointerPressed(leftMouseDown,
interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -371,12 +374,11 @@ namespace ControlUnitTests
Log::Comment(L"Drag the mouse");
const til::point terminalPosition4{ 3, 2 };
const auto cursorPosition4 = terminalPosition4 * fontSize;
interactivity->PointerMoved(leftMouseDown,
interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition4.to_core_point(),
true);
cursorPosition4.to_core_point());
Log::Comment(L"Verify that there's now one selection");
VERIFY_IS_TRUE(core->HasSelection());
}
@@ -407,10 +409,13 @@ namespace ControlUnitTests
const til::size fontSize{ 9, 21 };
interactivity->GotFocus();
Log::Comment(L"Click on the terminal");
const til::point terminalPosition0{ 5, 5 };
const auto cursorPosition0{ terminalPosition0 * fontSize };
interactivity->PointerPressed(leftMouseDown,
interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -425,12 +430,11 @@ namespace ControlUnitTests
Log::Comment(L"Drag the mouse just a little");
// move not quite a whole cell, but enough to start a selection
const auto cursorPosition1{ cursorPosition0 + til::point{ 6, 0 } };
interactivity->PointerMoved(leftMouseDown,
interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition1.to_core_point(),
true);
cursorPosition1.to_core_point());
Log::Comment(L"Verify that there's one selection");
VERIFY_IS_TRUE(core->HasSelection());
@@ -558,9 +562,12 @@ namespace ControlUnitTests
const til::size fontSize{ 9, 21 };
interactivity->GotFocus();
Log::Comment(L"Click on the terminal");
const til::point cursorPosition0{ 6, 0 };
interactivity->PointerPressed(leftMouseDown,
interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -574,12 +581,11 @@ namespace ControlUnitTests
Log::Comment(L"Drag the mouse a lot. This simulates dragging the mouse real fast.");
const til::point cursorPosition1{ 6 + fontSize.width * 2, 0 };
interactivity->PointerMoved(leftMouseDown,
interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition1.to_core_point(),
true);
cursorPosition1.to_core_point());
Log::Comment(L"Verify that there's one selection");
VERIFY_IS_TRUE(core->HasSelection());
@@ -601,10 +607,13 @@ namespace ControlUnitTests
const auto leftMouseDown{ Control::MouseButtonState::IsLeftButtonDown };
const Control::MouseButtonState noMouseDown{};
interactivity->GotFocus();
const til::size fontSize{ 9, 21 };
Log::Comment(L"Click on the terminal");
const til::point cursorPosition0{ 6, 0 };
interactivity->PointerPressed(leftMouseDown,
interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -618,12 +627,11 @@ namespace ControlUnitTests
Log::Comment(L"Drag the mouse a lot. This simulates dragging the mouse real fast.");
const til::point cursorPosition1{ 6 + fontSize.width * 2, 0 };
interactivity->PointerMoved(leftMouseDown,
interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition1.to_core_point(),
true);
cursorPosition1.to_core_point());
Log::Comment(L"Verify that there's one selection");
VERIFY_IS_TRUE(core->HasSelection());
@@ -633,7 +641,8 @@ namespace ControlUnitTests
til::point expectedEnd{ 3, 0 }; // add 1 to x-coordinate because end is exclusive
VERIFY_ARE_EQUAL(expectedEnd, core->_terminal->GetSelectionEnd());
interactivity->PointerReleased(noMouseDown,
interactivity->PointerReleased(0,
noMouseDown,
WM_LBUTTONUP,
modifiers,
cursorPosition1.to_core_point());
@@ -643,12 +652,11 @@ namespace ControlUnitTests
Log::Comment(L"Simulate dragging the mouse into the control, without first clicking into the control");
const til::point cursorPosition2{ fontSize.width * 10, 0 };
interactivity->PointerMoved(leftMouseDown,
interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition2.to_core_point(),
false);
cursorPosition2.to_core_point());
Log::Comment(L"The selection should be unchanged.");
VERIFY_ARE_EQUAL(expectedAnchor, core->_terminal->GetSelectionAnchor());
@@ -675,6 +683,8 @@ namespace ControlUnitTests
auto expectedViewHeight = 20;
auto expectedBufferHeight = 20;
interactivity->GotFocus();
auto scrollChangedHandler = [&](auto&&, const Control::ScrollPositionChangedArgs& args) mutable {
VERIFY_ARE_EQUAL(expectedTop, args.ViewTop());
VERIFY_ARE_EQUAL(expectedViewHeight, args.ViewHeight());
@@ -721,7 +731,8 @@ namespace ControlUnitTests
Log::Comment(L"Click on the terminal");
const til::point terminalPosition0{ 4, 4 };
const auto cursorPosition0 = terminalPosition0 * fontSize;
interactivity->PointerPressed(leftMouseDown,
interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -734,12 +745,11 @@ namespace ControlUnitTests
// move the mouse as if to make a selection
const til::point terminalPosition1{ 10, 4 };
const auto cursorPosition1 = terminalPosition1 * fontSize;
interactivity->PointerMoved(leftMouseDown,
interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition1.to_core_point(),
true);
cursorPosition1.to_core_point());
Log::Comment(L"Verify that there's still no selection");
VERIFY_IS_FALSE(core->HasSelection());
}
@@ -769,10 +779,13 @@ namespace ControlUnitTests
const til::size fontSize{ 9, 21 };
interactivity->GotFocus();
Log::Comment(L"Click on the terminal");
const til::point terminalPosition0{ 5, 5 };
const auto cursorPosition0{ terminalPosition0 * fontSize };
interactivity->PointerPressed(leftMouseDown,
interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -787,12 +800,11 @@ namespace ControlUnitTests
Log::Comment(L"Drag the mouse just a little");
// move not quite a whole cell, but enough to start a selection
const auto cursorPosition1{ cursorPosition0 + til::point{ 6, 0 } };
interactivity->PointerMoved(leftMouseDown,
interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition1.to_core_point(),
true);
cursorPosition1.to_core_point());
Log::Comment(L"Verify that there's one selection");
VERIFY_IS_TRUE(core->HasSelection());
@@ -849,12 +861,11 @@ namespace ControlUnitTests
// character in the buffer (if, albeit in a new location).
//
// This helps test GH #14462, a regression from #10749.
interactivity->PointerMoved(leftMouseDown,
interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition0.to_core_point(),
true);
cursorPosition0.to_core_point());
VERIFY_IS_TRUE(core->HasSelection());
{
const auto anchor{ core->_terminal->GetSelectionAnchor() };
@@ -875,12 +886,11 @@ namespace ControlUnitTests
expectedAnchor.y -= 1;
expectedEnd.y -= 1;
VERIFY_ARE_EQUAL(scrollbackLength - 3, core->_terminal->GetScrollOffset());
interactivity->PointerMoved(leftMouseDown,
interactivity->PointerMoved(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
modifiers,
true, // focused,
cursorPosition1.to_core_point(),
true);
cursorPosition1.to_core_point());
VERIFY_IS_TRUE(core->HasSelection());
{
const auto anchor{ core->_terminal->GetSelectionAnchor() };
@@ -928,7 +938,8 @@ namespace ControlUnitTests
const til::size fontSize{ 9, 21 };
const til::point terminalPosition0{ 5, 5 };
const auto cursorPosition0{ terminalPosition0 * fontSize };
interactivity->PointerPressed(leftMouseDown,
interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -977,7 +988,8 @@ namespace ControlUnitTests
const til::size fontSize{ 9, 21 };
const til::point terminalPosition0{ 5, 5 };
const auto cursorPosition0{ terminalPosition0 * fontSize };
interactivity->PointerPressed(leftMouseDown,
interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -993,7 +1005,8 @@ namespace ControlUnitTests
// The viewport is only 30 wide, so clamping 35 to the buffer size gets
// us 29, which converted is (32 + 29 + 1) = 62 = '>'
expectedOutput.push_back(L"\x1b[M >&");
interactivity->PointerPressed(leftMouseDown,
interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -1008,7 +1021,8 @@ namespace ControlUnitTests
// will be clamped to the top line.
expectedOutput.push_back(L"\x1b[M &!"); // 5, 1
interactivity->PointerPressed(leftMouseDown,
interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -1024,7 +1038,8 @@ namespace ControlUnitTests
VERIFY_ARE_EQUAL(0, core->ScrollOffset());
Log::Comment(L" --- Click on a spot that's still outside the buffer ---");
expectedOutput.push_back(L"\x1b[M >&");
interactivity->PointerPressed(leftMouseDown,
interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,
@@ -1038,7 +1053,8 @@ namespace ControlUnitTests
Log::Comment(L" --- Click on a spot that's NOW INSIDE the buffer ---");
// (32 + 35 + 1) = 68 = 'D'
expectedOutput.push_back(L"\x1b[M D&");
interactivity->PointerPressed(leftMouseDown,
interactivity->PointerPressed(0,
leftMouseDown,
WM_LBUTTONDOWN, //pointerUpdateKind
0, // timestamp
modifiers,

View File

@@ -85,18 +85,30 @@ try
#endif
return loader;
}
CATCH_FAIL_FAST()
catch (...)
{
LOG_CAUGHT_EXCEPTION();
return winrt::hstring{};
}
winrt::hstring GetLibraryResourceString(const std::wstring_view key)
try
{
return GetLibraryResourceLoader().GetLocalizedString(key);
}
CATCH_FAIL_FAST()
catch (...)
{
LOG_CAUGHT_EXCEPTION();
return winrt::hstring{};
}
bool HasLibraryResourceWithName(const std::wstring_view key)
try
{
return GetLibraryResourceLoader().HasResourceWithName(key);
}
CATCH_FAIL_FAST()
catch (...)
{
LOG_CAUGHT_EXCEPTION();
return false;
}

View File

@@ -3,8 +3,8 @@
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Castle.Core" publicKeyToken="407dd0808d44fbdc" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
<assemblyIdentity name="Castle.Core" publicKeyToken="407dd0808d44fbdc" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />

View File

@@ -177,7 +177,10 @@ namespace Microsoft.Terminal.Wpf
[DllImport("Microsoft.Terminal.Control.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, PreserveSig = false)]
public static extern void CreateTerminal(IntPtr parent, out IntPtr hwnd, out IntPtr terminal);
[DllImport("Microsoft.Terminal.Control.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, PreserveSig = true)]
[DllImport("Microsoft.Terminal.Control.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
public static extern void DestroyTerminal(IntPtr terminal);
[DllImport("Microsoft.Terminal.Control.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, PreserveSig = false)]
public static extern void TerminalSendOutput(IntPtr terminal, string lpdata);
[DllImport("Microsoft.Terminal.Control.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, PreserveSig = false)]
@@ -189,44 +192,44 @@ namespace Microsoft.Terminal.Wpf
[DllImport("Microsoft.Terminal.Control.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, PreserveSig = false)]
public static extern void TerminalCalculateResize(IntPtr terminal, int width, int height, out TilSize dimensions);
[DllImport("Microsoft.Terminal.Control.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, PreserveSig = true)]
[DllImport("Microsoft.Terminal.Control.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, PreserveSig = false)]
public static extern void TerminalDpiChanged(IntPtr terminal, int newDpi);
[DllImport("Microsoft.Terminal.Control.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, PreserveSig = true)]
[DllImport("Microsoft.Terminal.Control.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, PreserveSig = false)]
public static extern void TerminalRegisterScrollCallback(IntPtr terminal, [MarshalAs(UnmanagedType.FunctionPtr)] ScrollCallback callback);
[DllImport("Microsoft.Terminal.Control.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, PreserveSig = true)]
[DllImport("Microsoft.Terminal.Control.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, PreserveSig = false)]
public static extern void TerminalRegisterWriteCallback(IntPtr terminal, [MarshalAs(UnmanagedType.FunctionPtr)] WriteCallback callback);
[DllImport("Microsoft.Terminal.Control.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, PreserveSig = true)]
[DllImport("Microsoft.Terminal.Control.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, PreserveSig = false)]
public static extern void TerminalUserScroll(IntPtr terminal, int viewTop);
[DllImport("Microsoft.Terminal.Control.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, PreserveSig = true)]
[DllImport("Microsoft.Terminal.Control.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, PreserveSig = false)]
[return: MarshalAs(UnmanagedType.LPWStr)]
public static extern string TerminalGetSelection(IntPtr terminal);
[DllImport("Microsoft.Terminal.Control.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, PreserveSig = true)]
[DllImport("Microsoft.Terminal.Control.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, PreserveSig = false)]
[return: MarshalAs(UnmanagedType.I1)]
public static extern bool TerminalIsSelectionActive(IntPtr terminal);
[DllImport("Microsoft.Terminal.Control.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
public static extern void DestroyTerminal(IntPtr terminal);
[DllImport("Microsoft.Terminal.Control.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, PreserveSig = true)]
[DllImport("Microsoft.Terminal.Control.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, PreserveSig = false)]
public static extern void TerminalSendKeyEvent(IntPtr terminal, ushort vkey, ushort scanCode, ushort flags, bool keyDown);
[DllImport("Microsoft.Terminal.Control.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, PreserveSig = true)]
[DllImport("Microsoft.Terminal.Control.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, PreserveSig = false)]
public static extern void TerminalSendCharEvent(IntPtr terminal, char ch, ushort scanCode, ushort flags);
[DllImport("Microsoft.Terminal.Control.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, PreserveSig = true)]
[DllImport("Microsoft.Terminal.Control.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, PreserveSig = false)]
public static extern void TerminalSetTheme(IntPtr terminal, [MarshalAs(UnmanagedType.Struct)] TerminalTheme theme, string fontFamily, short fontSize, int newDpi);
[DllImport("Microsoft.Terminal.Control.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, PreserveSig = true)]
public static extern void TerminalSetFocused(IntPtr terminal, bool focused);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetFocus(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetFocus();
[DllImport("user32.dll", SetLastError = true)]
public static extern short GetKeyState(int keyCode);
[StructLayout(LayoutKind.Sequential)]
public struct WINDOWPOS
{

View File

@@ -37,7 +37,6 @@ namespace Microsoft.Terminal.Wpf
NativeMethods.AvoidBuggyTSFConsoleFlags();
this.MessageHook += this.TerminalContainer_MessageHook;
this.GotFocus += this.TerminalContainer_GotFocus;
this.Focusable = true;
}
@@ -324,24 +323,12 @@ namespace Microsoft.Terminal.Wpf
character = (char)vKey;
}
private void TerminalContainer_GotFocus(object sender, RoutedEventArgs e)
{
e.Handled = true;
NativeMethods.SetFocus(this.hwnd);
}
private IntPtr TerminalContainer_MessageHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (hwnd == this.hwnd)
{
switch ((NativeMethods.WindowMessage)msg)
{
case NativeMethods.WindowMessage.WM_SETFOCUS:
NativeMethods.TerminalSetFocused(this.terminal, true);
break;
case NativeMethods.WindowMessage.WM_KILLFOCUS:
NativeMethods.TerminalSetFocused(this.terminal, false);
break;
case NativeMethods.WindowMessage.WM_MOUSEACTIVATE:
this.Focus();
NativeMethods.SetFocus(this.hwnd);
@@ -349,6 +336,7 @@ namespace Microsoft.Terminal.Wpf
case NativeMethods.WindowMessage.WM_SYSKEYDOWN: // fallthrough
case NativeMethods.WindowMessage.WM_KEYDOWN:
{
// WM_KEYDOWN lParam layout documentation: https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-keydown
UnpackKeyMessage(wParam, lParam, out ushort vkey, out ushort scanCode, out ushort flags);
NativeMethods.TerminalSendKeyEvent(this.terminal, vkey, scanCode, flags, true);
break;
@@ -403,10 +391,10 @@ namespace Microsoft.Terminal.Wpf
this.Connection?.Resize((uint)dimensions.Y, (uint)dimensions.X);
break;
case NativeMethods.WindowMessage.WM_MOUSEWHEEL:
var delta = (short)(((long)wParam) >> 16);
this.UserScrolled?.Invoke(this, delta);
break;
//case NativeMethods.WindowMessage.WM_MOUSEWHEEL:
//var delta = (short)(((long)wParam) >> 16);
//this.UserScrolled?.Invoke(this, delta);
//break;
}
}

View File

@@ -11,6 +11,117 @@ namespace WpfTerminalTestNetCore
{
public class EchoConnection : Microsoft.Terminal.Wpf.ITerminalConnection
{
byte[] __foo_txt = {
0x1b, 0x5b, 0x48, 0x1b, 0x5b, 0x32, 0x4a, 0x1b, 0x5b, 0x33, 0x4a, 0x0d,
0x0a, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x31, 0x38, 0x6d,
0x54, 0x1b, 0x5b, 0x30, 0x6d, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b,
0x31, 0x35, 0x34, 0x6d, 0x68, 0x1b, 0x5b, 0x30, 0x6d, 0x1b, 0x5b, 0x33,
0x38, 0x3b, 0x35, 0x3b, 0x31, 0x35, 0x34, 0x6d, 0x61, 0x1b, 0x5b, 0x30,
0x6d, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x35, 0x34, 0x6d,
0x6e, 0x1b, 0x5b, 0x30, 0x6d, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b,
0x31, 0x34, 0x38, 0x6d, 0x6b, 0x1b, 0x5b, 0x30, 0x6d, 0x1b, 0x5b, 0x33,
0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x34, 0x6d, 0x73, 0x1b, 0x5b, 0x30,
0x6d, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x34, 0x6d,
0x20, 0x1b, 0x5b, 0x30, 0x6d, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b,
0x31, 0x38, 0x34, 0x6d, 0x66, 0x1b, 0x5b, 0x30, 0x6d, 0x1b, 0x5b, 0x33,
0x38, 0x3b, 0x35, 0x3b, 0x31, 0x37, 0x38, 0x6d, 0x6f, 0x1b, 0x5b, 0x30,
0x6d, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x31, 0x34, 0x6d,
0x72, 0x1b, 0x5b, 0x30, 0x6d, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b,
0x32, 0x31, 0x34, 0x6d, 0x20, 0x1b, 0x5b, 0x30, 0x6d, 0x1b, 0x5b, 0x33,
0x38, 0x3b, 0x35, 0x3b, 0x32, 0x31, 0x34, 0x6d, 0x76, 0x1b, 0x5b, 0x30,
0x6d, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x30, 0x38, 0x6d,
0x69, 0x1b, 0x5b, 0x30, 0x6d, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b,
0x32, 0x30, 0x38, 0x6d, 0x73, 0x1b, 0x5b, 0x30, 0x6d, 0x1b, 0x5b, 0x33,
0x38, 0x3b, 0x35, 0x3b, 0x32, 0x30, 0x38, 0x6d, 0x69, 0x1b, 0x5b, 0x30,
0x6d, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x30, 0x33, 0x6d,
0x74, 0x1b, 0x5b, 0x30, 0x6d, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b,
0x32, 0x30, 0x33, 0x6d, 0x69, 0x1b, 0x5b, 0x30, 0x6d, 0x1b, 0x5b, 0x33,
0x38, 0x3b, 0x35, 0x3b, 0x32, 0x30, 0x33, 0x6d, 0x6e, 0x1b, 0x5b, 0x30,
0x6d, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x30, 0x33, 0x6d,
0x67, 0x1b, 0x5b, 0x30, 0x6d, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b,
0x31, 0x39, 0x38, 0x6d, 0x20, 0x1b, 0x5b, 0x30, 0x6d, 0x1b, 0x5b, 0x33,
0x38, 0x3b, 0x35, 0x3b, 0x31, 0x39, 0x38, 0x6d, 0x6d, 0x1b, 0x5b, 0x30,
0x6d, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x39, 0x38, 0x6d,
0x79, 0x1b, 0x5b, 0x30, 0x6d, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b,
0x31, 0x39, 0x39, 0x6d, 0x20, 0x1b, 0x5b, 0x30, 0x6d, 0x1b, 0x5b, 0x33,
0x38, 0x3b, 0x35, 0x3b, 0x31, 0x39, 0x39, 0x6d, 0x77, 0x1b, 0x5b, 0x30,
0x6d, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x39, 0x39, 0x6d,
0x65, 0x1b, 0x5b, 0x30, 0x6d, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b,
0x31, 0x36, 0x33, 0x6d, 0x62, 0x1b, 0x5b, 0x30, 0x6d, 0x1b, 0x5b, 0x33,
0x38, 0x3b, 0x35, 0x3b, 0x31, 0x36, 0x34, 0x6d, 0x73, 0x1b, 0x5b, 0x30,
0x6d, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x36, 0x34, 0x6d,
0x69, 0x1b, 0x5b, 0x30, 0x6d, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b,
0x31, 0x36, 0x34, 0x6d, 0x74, 0x1b, 0x5b, 0x30, 0x6d, 0x1b, 0x5b, 0x33,
0x38, 0x3b, 0x35, 0x3b, 0x31, 0x32, 0x38, 0x6d, 0x65, 0x1b, 0x5b, 0x30,
0x6d, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x32, 0x39, 0x6d,
0x21, 0x1b, 0x5b, 0x30, 0x6d, 0x0d, 0x0a, 0x0d, 0x0a, 0x54, 0x68, 0x69,
0x73, 0x20, 0x69, 0x73, 0x20, 0x6d, 0x79, 0x20, 0x63, 0x6f, 0x6f, 0x6c,
0x20, 0x77, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x20, 0x61, 0x62, 0x6f,
0x75, 0x74, 0x20, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x73,
0x0d, 0x0a, 0x48, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20,
0x6c, 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x63, 0x6f, 0x6f, 0x6c,
0x20, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x73, 0x0d, 0x0a,
0x2a, 0x20, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x20, 0x54, 0x65,
0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x20, 0x28, 0x68, 0x74, 0x74, 0x70,
0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
0x6f, 0x6d, 0x2f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74,
0x2f, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x29, 0x0d, 0x0a,
0x0d, 0x0a, 0x1b, 0x5b, 0x35, 0x6d, 0x5b, 0x55, 0x4e, 0x44, 0x45, 0x52,
0x20, 0x43, 0x4f, 0x4e, 0x53, 0x54, 0x52, 0x55, 0x43, 0x54, 0x49, 0x4f,
0x4e, 0x5d, 0x1b, 0x5b, 0x6d, 0x0d, 0x0a, 0x0d, 0x0a, 0x59, 0x6f, 0x75,
0x20, 0x61, 0x72, 0x65, 0x20, 0x76, 0x69, 0x73, 0x69, 0x74, 0x6f, 0x72,
0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x1b, 0x5b, 0x35, 0x33,
0x3b, 0x34, 0x3b, 0x39, 0x32, 0x6d, 0x30, 0x30, 0x30, 0x30, 0x32, 0x1b,
0x5b, 0x6d, 0x20, 0x74, 0x6f, 0x20, 0x6d, 0x79, 0x20, 0x77, 0x65, 0x62,
0x73, 0x69, 0x74, 0x65, 0x2e, 0x0d, 0x0a, 0x0d, 0x0a, 0x1b, 0x5b, 0x33,
0x38, 0x3b, 0x35, 0x3b, 0x32, 0x34, 0x34, 0x6d, 0x1b, 0x5b, 0x35, 0x33,
0x6d, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x70, 0x61,
0x72, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x43, 0x6f,
0x6f, 0x6c, 0x20, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x73,
0x20, 0x77, 0x65, 0x62, 0x72, 0x69, 0x6e, 0x67, 0x21, 0x20, 0x1b, 0x5b,
0x35, 0x35, 0x6d, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x56, 0x69, 0x73, 0x69,
0x74, 0x20, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x73, 0x69, 0x74, 0x65,
0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, 0x65, 0x62,
0x20, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x2e, 0x2e, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x0d, 0x0a, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x1b, 0x5b, 0x39, 0x34, 0x6d, 0x1b, 0x5d, 0x38, 0x3b,
0x69, 0x64, 0x3d, 0x70, 0x72, 0x65, 0x76, 0x3b, 0x68, 0x74, 0x74, 0x70,
0x73, 0x3a, 0x2f, 0x2f, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c,
0x2e, 0x73, 0x69, 0x74, 0x65, 0x2f, 0x3f, 0x70, 0x72, 0x65, 0x76, 0x69,
0x6f, 0x75, 0x73, 0x3d, 0x31, 0x1b, 0x5c, 0x50, 0x72, 0x65, 0x76, 0x69,
0x6f, 0x75, 0x73, 0x1b, 0x5d, 0x38, 0x3b, 0x3b, 0x1b, 0x5c, 0x1b, 0x5b,
0x6d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x39, 0x34, 0x6d, 0x1b,
0x5d, 0x38, 0x3b, 0x69, 0x64, 0x3d, 0x72, 0x61, 0x6e, 0x64, 0x3b, 0x68,
0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x74, 0x65, 0x72, 0x6d, 0x69,
0x6e, 0x61, 0x6c, 0x2e, 0x73, 0x69, 0x74, 0x65, 0x2f, 0x3f, 0x72, 0x61,
0x6e, 0x64, 0x6f, 0x6d, 0x3d, 0x31, 0x1b, 0x5c, 0x52, 0x61, 0x6e, 0x64,
0x6f, 0x6d, 0x1b, 0x5d, 0x38, 0x3b, 0x3b, 0x1b, 0x5c, 0x1b, 0x5b, 0x6d,
0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x39, 0x34, 0x6d, 0x1b, 0x5d,
0x38, 0x3b, 0x69, 0x64, 0x3d, 0x6e, 0x65, 0x78, 0x74, 0x3b, 0x68, 0x74,
0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e,
0x61, 0x6c, 0x2e, 0x73, 0x69, 0x74, 0x65, 0x2f, 0x3f, 0x6e, 0x65, 0x78,
0x74, 0x3d, 0x31, 0x1b, 0x5c, 0x4e, 0x65, 0x78, 0x74, 0x1b, 0x5d, 0x38,
0x3b, 0x3b, 0x1b, 0x5c, 0x1b, 0x5b, 0x6d, 0x20, 0x0d, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x39, 0x34, 0x6d, 0x1b, 0x5d,
0x38, 0x3b, 0x69, 0x64, 0x3d, 0x70, 0x72, 0x65, 0x76, 0x3b, 0x68, 0x74,
0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e,
0x61, 0x6c, 0x2e, 0x73, 0x69, 0x74, 0x65, 0x2f, 0x3f, 0x70, 0x72, 0x65,
0x76, 0x69, 0x6f, 0x75, 0x73, 0x3d, 0x31, 0x1b, 0x5c, 0x20, 0x20, 0x53,
0x69, 0x74, 0x65, 0x20, 0x20, 0x1b, 0x5d, 0x38, 0x3b, 0x3b, 0x1b, 0x5c,
0x1b, 0x5b, 0x6d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x39, 0x34, 0x6d,
0x1b, 0x5d, 0x38, 0x3b, 0x69, 0x64, 0x3d, 0x6e, 0x65, 0x78, 0x74, 0x3b,
0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x74, 0x65, 0x72, 0x6d,
0x69, 0x6e, 0x61, 0x6c, 0x2e, 0x73, 0x69, 0x74, 0x65, 0x2f, 0x3f, 0x6e,
0x65, 0x78, 0x74, 0x3d, 0x31, 0x1b, 0x5c, 0x53, 0x69, 0x74, 0x65, 0x1b,
0x5d, 0x38, 0x3b, 0x3b, 0x1b, 0x5c, 0x1b, 0x5b, 0x6d, 0x20, 0x0d, 0x0a,
0x1b, 0x5b, 0x34, 0x3b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x34, 0x34,
0x6d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b,
0x6d, 0x0d, 0x0a
};
public event EventHandler<TerminalOutputEventArgs> TerminalOutput;
public void Resize(uint rows, uint columns)
@@ -18,10 +129,15 @@ namespace WpfTerminalTestNetCore
return;
}
private void _preamble()
{
_print(System.Text.Encoding.UTF8.GetString(__foo_txt));
_print("^A: prt esc ^B: sgrmouse ^C: win32im ^D: again!\r\n");
return;
}
public void Start()
{
TerminalOutput.Invoke(this, new TerminalOutputEventArgs("ECHO CONNECTION\r\n^A: toggle printable ESC\r\n^B: toggle SGR mouse mode\r\n^C: toggle win32 input mode\r\n\r\n"));
return;
_preamble();
}
private bool _escapeMode;
@@ -62,18 +178,27 @@ namespace WpfTerminalTestNetCore
TerminalOutput.Invoke(this, new TerminalOutputEventArgs($"Printable ESC mode: {_escapeMode}\r\n"));
}
}
else if (data[0] == '\x04')
{
_preamble();
}
else
{
// Echo back to the terminal, but make backspace/newline work properly.
var str = data.Replace("\r", "\r\n").Replace("\x7f", "\x08 \x08");
if (_escapeMode)
{
str = str.Replace("\x1b", "\u241b");
}
TerminalOutput.Invoke(this, new TerminalOutputEventArgs(str));
_print(str);
}
}
private void _print(string str)
{
if (_escapeMode)
{
str = str.Replace("\x1b", "\u241b");
}
TerminalOutput.Invoke(this, new TerminalOutputEventArgs(str));
}
public void Close()
{
return;

View File

@@ -137,7 +137,7 @@
This diagnostic is broken in VS 17.7 which our CI currently uses. It's fixed in 17.8.
-->
<DisableSpecificWarnings>4201;4312;4467;5105;26434;26445;26456;26478;26494;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<PreprocessorDefinitions>_WINDOWS;EXTERNAL_BUILD;_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING;_WINDOWS;EXTERNAL_BUILD;_SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>

View File

@@ -3,8 +3,8 @@
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Castle.Core" publicKeyToken="407dd0808d44fbdc" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
<assemblyIdentity name="Castle.Core" publicKeyToken="407dd0808d44fbdc" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />