terminal/src/cascadia/TerminalCore/ControlKeyStates.hpp
Mike Griese a12a6285f5
Manually pass mouse wheel messages to TermControls (#5131)
## Summary of the Pull Request

As we've learned in #979, not all touchpads are created equal. Some of them have bad drivers that makes scrolling inactive windows not work. For whatever reason, these devices think the Terminal is all one giant inactive window, so we don't get the mouse wheel events through the XAML stack. We do however get the event as a `WM_MOUSEWHEEL` on those devices (a message we don't get on devices with normally functioning trackpads).

This PR attempts to take that `WM_MOUSEWHEEL` and manually dispatch it to the `TermControl`, so we can at least scroll the terminal content.

Unfortunately, this solution is not very general purpose. This only works to scroll controls that manually implement our own `IMouseWheelListener` interface. As we add more controls, we'll need to continue manually implementing this interface, until the underlying XAML Islands bug is fixed. **I don't love this**. I'd rather have a better solution, but it seems that we can't synthesize a more general-purpose `PointerWheeled` event that could get routed through the XAML tree as normal. 

## References

* #2606 and microsoft/microsoft-ui-xaml#2101 - these bugs are also tracking a similar "inactive windows" / "scaled mouse events" issue in XAML

## PR Checklist
* [x] Closes #979
* [x] I work here
* [ ] Tests added/passed
* [n/a] Requires documentation to be updated

## Detailed Description of the Pull Request / Additional comments

I've also added a `til::point` conversion _to_ `winrt::Windows::Foundation::Point`, and some scaling operators for `point`

## Validation Steps Performed

* It works on my HP Spectre 2017 with a synaptics trackpad
  - I also made sure to test that `tmux` works in panes on this laptop
* It works on my slaptop, and DOESN'T follow this hack codepath on this machine.
2020-04-01 16:58:16 +00:00

134 lines
4.7 KiB
C++

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
namespace Microsoft::Terminal::Core
{
class ControlKeyStates;
}
constexpr Microsoft::Terminal::Core::ControlKeyStates operator|(Microsoft::Terminal::Core::ControlKeyStates lhs, Microsoft::Terminal::Core::ControlKeyStates rhs) noexcept;
// This class is functionally equivalent to PowerShell's System.Management.Automation.Host.ControlKeyStates enum:
// https://docs.microsoft.com/en-us/dotnet/api/system.management.automation.host.controlkeystates
// It's flagging values are compatible to those used by the NT console subsystem (<um/wincon.h>),
// as these are being used throughout older parts of this project.
class Microsoft::Terminal::Core::ControlKeyStates
{
struct StaticValue
{
DWORD v;
};
public:
static constexpr StaticValue RightAltPressed{ RIGHT_ALT_PRESSED };
static constexpr StaticValue LeftAltPressed{ LEFT_ALT_PRESSED };
static constexpr StaticValue RightCtrlPressed{ RIGHT_CTRL_PRESSED };
static constexpr StaticValue LeftCtrlPressed{ LEFT_CTRL_PRESSED };
static constexpr StaticValue ShiftPressed{ SHIFT_PRESSED };
static constexpr StaticValue NumlockOn{ NUMLOCK_ON };
static constexpr StaticValue ScrolllockOn{ SCROLLLOCK_ON };
static constexpr StaticValue CapslockOn{ CAPSLOCK_ON };
static constexpr StaticValue EnhancedKey{ ENHANCED_KEY };
constexpr ControlKeyStates() noexcept :
_value(0) {}
constexpr ControlKeyStates(StaticValue value) noexcept :
_value(value.v) {}
explicit constexpr ControlKeyStates(DWORD value) noexcept :
_value(value) {}
ControlKeyStates& operator|=(ControlKeyStates rhs) noexcept
{
_value |= rhs.Value();
return *this;
}
#ifdef WINRT_Windows_System_H
ControlKeyStates(const winrt::Windows::System::VirtualKeyModifiers& modifiers) noexcept :
_value{ 0 }
{
// static_cast to a uint32_t because we can't use the WI_IsFlagSet
// macro directly with a VirtualKeyModifiers
const auto m = static_cast<uint32_t>(modifiers);
_value |= WI_IsFlagSet(m, static_cast<uint32_t>(winrt::Windows::System::VirtualKeyModifiers::Shift)) ?
SHIFT_PRESSED :
0;
// Since we can't differentiate between the left & right versions of Ctrl & Alt in a VirtualKeyModifiers
_value |= WI_IsFlagSet(m, static_cast<uint32_t>(winrt::Windows::System::VirtualKeyModifiers::Menu)) ?
LEFT_ALT_PRESSED :
0;
_value |= WI_IsFlagSet(m, static_cast<uint32_t>(winrt::Windows::System::VirtualKeyModifiers::Control)) ?
LEFT_CTRL_PRESSED :
0;
}
#endif
constexpr DWORD Value() const noexcept
{
return _value;
}
constexpr bool IsShiftPressed() const noexcept
{
return IsAnyFlagSet(ShiftPressed);
}
constexpr bool IsAltPressed() const noexcept
{
return IsAnyFlagSet(RightAltPressed | LeftAltPressed);
}
constexpr bool IsCtrlPressed() const noexcept
{
return IsAnyFlagSet(RightCtrlPressed | LeftCtrlPressed);
}
constexpr bool IsAltGrPressed() const noexcept
{
return AreAllFlagsSet(RightAltPressed | LeftCtrlPressed);
}
constexpr bool IsModifierPressed() const noexcept
{
return IsAnyFlagSet(RightAltPressed | LeftAltPressed | RightCtrlPressed | LeftCtrlPressed | ShiftPressed);
}
private:
constexpr bool AreAllFlagsSet(ControlKeyStates mask) const noexcept
{
return (Value() & mask.Value()) == mask.Value();
}
constexpr bool IsAnyFlagSet(ControlKeyStates mask) const noexcept
{
return (Value() & mask.Value()) != 0;
}
DWORD _value;
};
constexpr Microsoft::Terminal::Core::ControlKeyStates operator|(Microsoft::Terminal::Core::ControlKeyStates lhs, Microsoft::Terminal::Core::ControlKeyStates rhs) noexcept
{
return Microsoft::Terminal::Core::ControlKeyStates{ lhs.Value() | rhs.Value() };
}
constexpr Microsoft::Terminal::Core::ControlKeyStates operator&(Microsoft::Terminal::Core::ControlKeyStates lhs, Microsoft::Terminal::Core::ControlKeyStates rhs) noexcept
{
return Microsoft::Terminal::Core::ControlKeyStates{ lhs.Value() & rhs.Value() };
}
constexpr bool operator==(Microsoft::Terminal::Core::ControlKeyStates lhs, Microsoft::Terminal::Core::ControlKeyStates rhs) noexcept
{
return lhs.Value() == rhs.Value();
}
constexpr bool operator!=(Microsoft::Terminal::Core::ControlKeyStates lhs, Microsoft::Terminal::Core::ControlKeyStates rhs) noexcept
{
return lhs.Value() != rhs.Value();
}