eb2be374fd
I added `enum class` to one thing and decided that that was quite enough before disabling the `enum class` warning. Looks like 16.8 made more map/vector operations noexcept, so we have to re-annotate to remain compliant.
553 lines
16 KiB
C++
553 lines
16 KiB
C++
/*++
|
|
Copyright (c) Microsoft Corporation
|
|
|
|
Module Name:
|
|
- IInputEvent.hpp
|
|
|
|
Abstract:
|
|
- Internal representation of public INPUT_RECORD struct.
|
|
|
|
Author:
|
|
- Austin Diviness (AustDi) 18-Aug-2017
|
|
--*/
|
|
|
|
#pragma once
|
|
|
|
#include <wil/common.h>
|
|
#include <wil/resource.h>
|
|
|
|
#ifndef ALTNUMPAD_BIT
|
|
// from winconp.h
|
|
#define ALTNUMPAD_BIT 0x04000000 // AltNumpad OEM char (copied from ntuser/inc/kbd.h)
|
|
#endif
|
|
|
|
#include <wtypes.h>
|
|
|
|
#include <unordered_set>
|
|
#include <memory>
|
|
#include <deque>
|
|
#include <ostream>
|
|
|
|
enum class InputEventType
|
|
{
|
|
KeyEvent,
|
|
MouseEvent,
|
|
WindowBufferSizeEvent,
|
|
MenuEvent,
|
|
FocusEvent
|
|
};
|
|
|
|
class IInputEvent
|
|
{
|
|
public:
|
|
static std::unique_ptr<IInputEvent> Create(const INPUT_RECORD& record);
|
|
static std::deque<std::unique_ptr<IInputEvent>> Create(gsl::span<const INPUT_RECORD> records);
|
|
static std::deque<std::unique_ptr<IInputEvent>> Create(const std::deque<INPUT_RECORD>& records);
|
|
|
|
static std::vector<INPUT_RECORD> ToInputRecords(const std::deque<std::unique_ptr<IInputEvent>>& events);
|
|
|
|
virtual ~IInputEvent() = 0;
|
|
IInputEvent() = default;
|
|
IInputEvent(const IInputEvent&) = default;
|
|
IInputEvent(IInputEvent&&) = default;
|
|
IInputEvent& operator=(const IInputEvent&) & = default;
|
|
IInputEvent& operator=(IInputEvent&&) & = default;
|
|
|
|
virtual INPUT_RECORD ToInputRecord() const noexcept = 0;
|
|
|
|
virtual InputEventType EventType() const noexcept = 0;
|
|
|
|
#ifdef UNIT_TESTING
|
|
friend std::wostream& operator<<(std::wostream& stream, const IInputEvent* const pEvent);
|
|
#endif
|
|
};
|
|
|
|
inline IInputEvent::~IInputEvent()
|
|
{
|
|
}
|
|
|
|
#ifdef UNIT_TESTING
|
|
std::wostream& operator<<(std::wostream& stream, const IInputEvent* pEvent);
|
|
#endif
|
|
|
|
#define ALT_PRESSED (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED)
|
|
#define CTRL_PRESSED (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)
|
|
#define MOD_PRESSED (SHIFT_PRESSED | ALT_PRESSED | CTRL_PRESSED)
|
|
|
|
// Note taken from VkKeyScan docs (https://msdn.microsoft.com/en-us/library/windows/desktop/ms646329(v=vs.85).aspx):
|
|
// For keyboard layouts that use the right-hand ALT key as a shift key
|
|
// (for example, the French keyboard layout), the shift state is
|
|
// represented by the value 6, because the right-hand ALT key is
|
|
// converted internally into CTRL+ALT.
|
|
struct VkKeyScanModState
|
|
{
|
|
static const byte None = 0;
|
|
static const byte ShiftPressed = 1;
|
|
static const byte CtrlPressed = 2;
|
|
static const byte ShiftAndCtrlPressed = ShiftPressed | CtrlPressed;
|
|
static const byte AltPressed = 4;
|
|
static const byte ShiftAndAltPressed = ShiftPressed | AltPressed;
|
|
static const byte CtrlAndAltPressed = CtrlPressed | AltPressed;
|
|
static const byte ModPressed = ShiftPressed | CtrlPressed | AltPressed;
|
|
};
|
|
|
|
enum class ModifierKeyState
|
|
{
|
|
RightAlt,
|
|
LeftAlt,
|
|
RightCtrl,
|
|
LeftCtrl,
|
|
Shift,
|
|
NumLock,
|
|
ScrollLock,
|
|
CapsLock,
|
|
EnhancedKey,
|
|
NlsDbcsChar,
|
|
NlsAlphanumeric,
|
|
NlsKatakana,
|
|
NlsHiragana,
|
|
NlsRoman,
|
|
NlsImeConversion,
|
|
AltNumpad,
|
|
NlsImeDisable,
|
|
ENUM_COUNT // must be the last element in the enum class
|
|
};
|
|
|
|
std::unordered_set<ModifierKeyState> FromVkKeyScan(const short vkKeyScanFlags);
|
|
std::unordered_set<ModifierKeyState> FromConsoleControlKeyFlags(const DWORD flags);
|
|
DWORD ToConsoleControlKeyFlag(const ModifierKeyState modifierKey) noexcept;
|
|
|
|
class KeyEvent : public IInputEvent
|
|
{
|
|
public:
|
|
enum class Modifiers : DWORD
|
|
{
|
|
None = 0,
|
|
RightAlt = RIGHT_ALT_PRESSED,
|
|
LeftAlt = LEFT_ALT_PRESSED,
|
|
RightCtrl = RIGHT_CTRL_PRESSED,
|
|
LeftCtrl = LEFT_CTRL_PRESSED,
|
|
Shift = SHIFT_PRESSED,
|
|
NumLock = NUMLOCK_ON,
|
|
ScrollLock = SCROLLLOCK_ON,
|
|
CapsLock = CAPSLOCK_ON,
|
|
EnhancedKey = ENHANCED_KEY,
|
|
DbcsChar = NLS_DBCSCHAR,
|
|
Alphanumeric = NLS_ALPHANUMERIC,
|
|
Katakana = NLS_KATAKANA,
|
|
Hiragana = NLS_HIRAGANA,
|
|
Roman = NLS_ROMAN,
|
|
ImeConvert = NLS_IME_CONVERSION,
|
|
AltNumpad = ALTNUMPAD_BIT,
|
|
ImeDisable = NLS_IME_DISABLE
|
|
};
|
|
|
|
constexpr KeyEvent(const KEY_EVENT_RECORD& record) :
|
|
_keyDown{ !!record.bKeyDown },
|
|
_repeatCount{ record.wRepeatCount },
|
|
_virtualKeyCode{ record.wVirtualKeyCode },
|
|
_virtualScanCode{ record.wVirtualScanCode },
|
|
_charData{ record.uChar.UnicodeChar },
|
|
_activeModifierKeys{ record.dwControlKeyState }
|
|
{
|
|
}
|
|
|
|
constexpr KeyEvent(const bool keyDown,
|
|
const WORD repeatCount,
|
|
const WORD virtualKeyCode,
|
|
const WORD virtualScanCode,
|
|
const wchar_t charData,
|
|
const DWORD activeModifierKeys) :
|
|
_keyDown{ keyDown },
|
|
_repeatCount{ repeatCount },
|
|
_virtualKeyCode{ virtualKeyCode },
|
|
_virtualScanCode{ virtualScanCode },
|
|
_charData{ charData },
|
|
_activeModifierKeys{ activeModifierKeys }
|
|
{
|
|
}
|
|
|
|
constexpr KeyEvent() noexcept :
|
|
_keyDown{ 0 },
|
|
_repeatCount{ 0 },
|
|
_virtualKeyCode{ 0 },
|
|
_virtualScanCode{ 0 },
|
|
_charData{ 0 },
|
|
_activeModifierKeys{ 0 }
|
|
{
|
|
}
|
|
|
|
static std::pair<KeyEvent, KeyEvent> MakePair(
|
|
const WORD repeatCount,
|
|
const WORD virtualKeyCode,
|
|
const WORD virtualScanCode,
|
|
const wchar_t charData,
|
|
const DWORD activeModifierKeys)
|
|
{
|
|
return std::make_pair<KeyEvent, KeyEvent>(
|
|
{ true,
|
|
repeatCount,
|
|
virtualKeyCode,
|
|
virtualScanCode,
|
|
charData,
|
|
activeModifierKeys },
|
|
{ false,
|
|
repeatCount,
|
|
virtualKeyCode,
|
|
virtualScanCode,
|
|
charData,
|
|
activeModifierKeys });
|
|
}
|
|
|
|
~KeyEvent();
|
|
KeyEvent(const KeyEvent&) = default;
|
|
KeyEvent(KeyEvent&&) = default;
|
|
// For these two operators, there seems to be a bug in the compiler:
|
|
// See https://stackoverflow.com/a/60206505/1481137
|
|
// > C.128 applies only to virtual member functions, but operator= is not
|
|
// > virtual in your base class and neither does it have the same signature as
|
|
// > in the derived class, so there is no reason for it to apply.
|
|
#pragma warning(push)
|
|
#pragma warning(disable : 26456)
|
|
KeyEvent& operator=(const KeyEvent&) & = default;
|
|
KeyEvent& operator=(KeyEvent&&) & = default;
|
|
#pragma warning(pop)
|
|
|
|
INPUT_RECORD ToInputRecord() const noexcept override;
|
|
InputEventType EventType() const noexcept override;
|
|
|
|
constexpr bool IsShiftPressed() const noexcept
|
|
{
|
|
return WI_IsFlagSet(GetActiveModifierKeys(), SHIFT_PRESSED);
|
|
}
|
|
|
|
constexpr bool IsAltPressed() const noexcept
|
|
{
|
|
return WI_IsAnyFlagSet(GetActiveModifierKeys(), ALT_PRESSED);
|
|
}
|
|
|
|
constexpr bool IsCtrlPressed() const noexcept
|
|
{
|
|
return WI_IsAnyFlagSet(GetActiveModifierKeys(), CTRL_PRESSED);
|
|
}
|
|
|
|
constexpr bool IsAltGrPressed() const noexcept
|
|
{
|
|
return WI_IsFlagSet(GetActiveModifierKeys(), LEFT_CTRL_PRESSED) &&
|
|
WI_IsFlagSet(GetActiveModifierKeys(), RIGHT_ALT_PRESSED);
|
|
}
|
|
|
|
constexpr bool IsModifierPressed() const noexcept
|
|
{
|
|
return WI_IsAnyFlagSet(GetActiveModifierKeys(), MOD_PRESSED);
|
|
}
|
|
|
|
constexpr bool IsCursorKey() const noexcept
|
|
{
|
|
// true iff vk in [End, Home, Left, Up, Right, Down]
|
|
return (_virtualKeyCode >= VK_END) && (_virtualKeyCode <= VK_DOWN);
|
|
}
|
|
|
|
constexpr bool IsAltNumpadSet() const noexcept
|
|
{
|
|
return WI_IsFlagSet(GetActiveModifierKeys(), ALTNUMPAD_BIT);
|
|
}
|
|
|
|
constexpr bool IsKeyDown() const noexcept
|
|
{
|
|
return _keyDown;
|
|
}
|
|
|
|
constexpr bool IsPauseKey() const noexcept
|
|
{
|
|
return (_virtualKeyCode == VK_PAUSE);
|
|
}
|
|
|
|
constexpr WORD GetRepeatCount() const noexcept
|
|
{
|
|
return _repeatCount;
|
|
}
|
|
|
|
constexpr WORD GetVirtualKeyCode() const noexcept
|
|
{
|
|
return _virtualKeyCode;
|
|
}
|
|
|
|
constexpr WORD GetVirtualScanCode() const noexcept
|
|
{
|
|
return _virtualScanCode;
|
|
}
|
|
|
|
constexpr wchar_t GetCharData() const noexcept
|
|
{
|
|
return _charData;
|
|
}
|
|
|
|
constexpr DWORD GetActiveModifierKeys() const noexcept
|
|
{
|
|
return static_cast<DWORD>(_activeModifierKeys);
|
|
}
|
|
|
|
void SetKeyDown(const bool keyDown) noexcept;
|
|
void SetRepeatCount(const WORD repeatCount) noexcept;
|
|
void SetVirtualKeyCode(const WORD virtualKeyCode) noexcept;
|
|
void SetVirtualScanCode(const WORD virtualScanCode) noexcept;
|
|
void SetCharData(const wchar_t character) noexcept;
|
|
|
|
void SetActiveModifierKeys(const DWORD activeModifierKeys) noexcept;
|
|
void DeactivateModifierKey(const ModifierKeyState modifierKey) noexcept;
|
|
void ActivateModifierKey(const ModifierKeyState modifierKey) noexcept;
|
|
bool DoActiveModifierKeysMatch(const std::unordered_set<ModifierKeyState>& consoleModifiers) const noexcept;
|
|
bool IsCommandLineEditingKey() const noexcept;
|
|
bool IsPopupKey() const noexcept;
|
|
|
|
// Function Description:
|
|
// - Returns true if the given VKey represents a modifier key - shift, alt,
|
|
// control or the Win key.
|
|
// Arguments:
|
|
// - vkey: the VKEY to check
|
|
// Return Value:
|
|
// - true iff the key is a modifier key.
|
|
constexpr static bool IsModifierKey(const WORD vkey)
|
|
{
|
|
return (vkey == VK_CONTROL) ||
|
|
(vkey == VK_LCONTROL) ||
|
|
(vkey == VK_RCONTROL) ||
|
|
(vkey == VK_MENU) ||
|
|
(vkey == VK_LMENU) ||
|
|
(vkey == VK_RMENU) ||
|
|
(vkey == VK_SHIFT) ||
|
|
(vkey == VK_LSHIFT) ||
|
|
(vkey == VK_RSHIFT) ||
|
|
// There is no VK_WIN
|
|
(vkey == VK_LWIN) ||
|
|
(vkey == VK_RWIN);
|
|
};
|
|
|
|
private:
|
|
bool _keyDown;
|
|
WORD _repeatCount;
|
|
WORD _virtualKeyCode;
|
|
WORD _virtualScanCode;
|
|
wchar_t _charData;
|
|
Modifiers _activeModifierKeys;
|
|
|
|
friend constexpr bool operator==(const KeyEvent& a, const KeyEvent& b) noexcept;
|
|
#ifdef UNIT_TESTING
|
|
friend std::wostream& operator<<(std::wostream& stream, const KeyEvent* const pKeyEvent);
|
|
#endif
|
|
};
|
|
|
|
constexpr bool operator==(const KeyEvent& a, const KeyEvent& b) noexcept
|
|
{
|
|
return (a._keyDown == b._keyDown &&
|
|
a._repeatCount == b._repeatCount &&
|
|
a._virtualKeyCode == b._virtualKeyCode &&
|
|
a._virtualScanCode == b._virtualScanCode &&
|
|
a._charData == b._charData &&
|
|
a._activeModifierKeys == b._activeModifierKeys);
|
|
}
|
|
|
|
#ifdef UNIT_TESTING
|
|
std::wostream& operator<<(std::wostream& stream, const KeyEvent* const pKeyEvent);
|
|
#endif
|
|
|
|
class MouseEvent : public IInputEvent
|
|
{
|
|
public:
|
|
constexpr MouseEvent(const MOUSE_EVENT_RECORD& record) :
|
|
_position{ record.dwMousePosition },
|
|
_buttonState{ record.dwButtonState },
|
|
_activeModifierKeys{ record.dwControlKeyState },
|
|
_eventFlags{ record.dwEventFlags }
|
|
{
|
|
}
|
|
|
|
constexpr MouseEvent(const COORD position,
|
|
const DWORD buttonState,
|
|
const DWORD activeModifierKeys,
|
|
const DWORD eventFlags) :
|
|
_position{ position },
|
|
_buttonState{ buttonState },
|
|
_activeModifierKeys{ activeModifierKeys },
|
|
_eventFlags{ eventFlags }
|
|
{
|
|
}
|
|
|
|
~MouseEvent();
|
|
MouseEvent(const MouseEvent&) = default;
|
|
MouseEvent(MouseEvent&&) = default;
|
|
MouseEvent& operator=(const MouseEvent&) & = default;
|
|
MouseEvent& operator=(MouseEvent&&) & = default;
|
|
|
|
INPUT_RECORD ToInputRecord() const noexcept override;
|
|
InputEventType EventType() const noexcept override;
|
|
|
|
constexpr bool IsMouseMoveEvent() const noexcept
|
|
{
|
|
return _eventFlags == MOUSE_MOVED;
|
|
}
|
|
|
|
constexpr COORD GetPosition() const noexcept
|
|
{
|
|
return _position;
|
|
}
|
|
|
|
constexpr DWORD GetButtonState() const noexcept
|
|
{
|
|
return _buttonState;
|
|
}
|
|
|
|
constexpr DWORD GetActiveModifierKeys() const noexcept
|
|
{
|
|
return _activeModifierKeys;
|
|
}
|
|
|
|
constexpr DWORD GetEventFlags() const noexcept
|
|
{
|
|
return _eventFlags;
|
|
}
|
|
|
|
void SetPosition(const COORD position) noexcept;
|
|
void SetButtonState(const DWORD buttonState) noexcept;
|
|
void SetActiveModifierKeys(const DWORD activeModifierKeys) noexcept;
|
|
void SetEventFlags(const DWORD eventFlags) noexcept;
|
|
|
|
private:
|
|
COORD _position;
|
|
DWORD _buttonState;
|
|
DWORD _activeModifierKeys;
|
|
DWORD _eventFlags;
|
|
|
|
#ifdef UNIT_TESTING
|
|
friend std::wostream& operator<<(std::wostream& stream, const MouseEvent* const pMouseEvent);
|
|
#endif
|
|
};
|
|
|
|
#ifdef UNIT_TESTING
|
|
std::wostream& operator<<(std::wostream& stream, const MouseEvent* const pMouseEvent);
|
|
#endif
|
|
|
|
class WindowBufferSizeEvent : public IInputEvent
|
|
{
|
|
public:
|
|
constexpr WindowBufferSizeEvent(const WINDOW_BUFFER_SIZE_RECORD& record) :
|
|
_size{ record.dwSize }
|
|
{
|
|
}
|
|
|
|
constexpr WindowBufferSizeEvent(const COORD size) :
|
|
_size{ size }
|
|
{
|
|
}
|
|
|
|
~WindowBufferSizeEvent();
|
|
WindowBufferSizeEvent(const WindowBufferSizeEvent&) = default;
|
|
WindowBufferSizeEvent(WindowBufferSizeEvent&&) = default;
|
|
WindowBufferSizeEvent& operator=(const WindowBufferSizeEvent&) & = default;
|
|
WindowBufferSizeEvent& operator=(WindowBufferSizeEvent&&) & = default;
|
|
|
|
INPUT_RECORD ToInputRecord() const noexcept override;
|
|
InputEventType EventType() const noexcept override;
|
|
|
|
constexpr COORD GetSize() const noexcept
|
|
{
|
|
return _size;
|
|
}
|
|
|
|
void SetSize(const COORD size) noexcept;
|
|
|
|
private:
|
|
COORD _size;
|
|
|
|
#ifdef UNIT_TESTING
|
|
friend std::wostream& operator<<(std::wostream& stream, const WindowBufferSizeEvent* const pEvent);
|
|
#endif
|
|
};
|
|
|
|
#ifdef UNIT_TESTING
|
|
std::wostream& operator<<(std::wostream& stream, const WindowBufferSizeEvent* const pEvent);
|
|
#endif
|
|
|
|
class MenuEvent : public IInputEvent
|
|
{
|
|
public:
|
|
constexpr MenuEvent(const MENU_EVENT_RECORD& record) :
|
|
_commandId{ record.dwCommandId }
|
|
{
|
|
}
|
|
|
|
constexpr MenuEvent(const UINT commandId) :
|
|
_commandId{ commandId }
|
|
{
|
|
}
|
|
|
|
~MenuEvent();
|
|
MenuEvent(const MenuEvent&) = default;
|
|
MenuEvent(MenuEvent&&) = default;
|
|
MenuEvent& operator=(const MenuEvent&) & = default;
|
|
MenuEvent& operator=(MenuEvent&&) & = default;
|
|
|
|
INPUT_RECORD ToInputRecord() const noexcept override;
|
|
InputEventType EventType() const noexcept override;
|
|
|
|
constexpr UINT GetCommandId() const noexcept
|
|
{
|
|
return _commandId;
|
|
}
|
|
|
|
void SetCommandId(const UINT commandId) noexcept;
|
|
|
|
private:
|
|
UINT _commandId;
|
|
|
|
#ifdef UNIT_TESTING
|
|
friend std::wostream& operator<<(std::wostream& stream, const MenuEvent* const pMenuEvent);
|
|
#endif
|
|
};
|
|
|
|
#ifdef UNIT_TESTING
|
|
std::wostream& operator<<(std::wostream& stream, const MenuEvent* const pMenuEvent);
|
|
#endif
|
|
|
|
class FocusEvent : public IInputEvent
|
|
{
|
|
public:
|
|
constexpr FocusEvent(const FOCUS_EVENT_RECORD& record) :
|
|
_focus{ !!record.bSetFocus }
|
|
{
|
|
}
|
|
|
|
constexpr FocusEvent(const bool focus) :
|
|
_focus{ focus }
|
|
{
|
|
}
|
|
|
|
~FocusEvent();
|
|
FocusEvent(const FocusEvent&) = default;
|
|
FocusEvent(FocusEvent&&) = default;
|
|
FocusEvent& operator=(const FocusEvent&) & = default;
|
|
FocusEvent& operator=(FocusEvent&&) & = default;
|
|
|
|
INPUT_RECORD ToInputRecord() const noexcept override;
|
|
InputEventType EventType() const noexcept override;
|
|
|
|
constexpr bool GetFocus() const noexcept
|
|
{
|
|
return _focus;
|
|
}
|
|
|
|
void SetFocus(const bool focus) noexcept;
|
|
|
|
private:
|
|
bool _focus;
|
|
|
|
#ifdef UNIT_TESTING
|
|
friend std::wostream& operator<<(std::wostream& stream, const FocusEvent* const pFocusEvent);
|
|
#endif
|
|
};
|
|
|
|
#ifdef UNIT_TESTING
|
|
std::wostream& operator<<(std::wostream& stream, const FocusEvent* const pFocusEvent);
|
|
#endif
|