137 lines
3.5 KiB
C++
137 lines
3.5 KiB
C++
/*******************************************************************************
|
|
* StellarX native keyboard, text, IME, and activation event model.
|
|
* Stage 1 introduced capture, normalization, queuing, and observation.
|
|
* After the Stage 3 atomic cutover, normalized KeyEvent routing is the
|
|
* production business path; EasyX EX_KEY remains a drain-only observation.
|
|
******************************************************************************/
|
|
#pragma once
|
|
|
|
#include <windows.h>
|
|
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include <variant>
|
|
|
|
namespace StellarX
|
|
{
|
|
enum class KeyAction : std::uint8_t
|
|
{
|
|
Down,
|
|
Up
|
|
};
|
|
|
|
// Snapshot modifiers in WndProc; delayed consumers must not reread global state.
|
|
struct KeyModifiers
|
|
{
|
|
bool leftControl = false;
|
|
bool rightControl = false;
|
|
bool leftShift = false;
|
|
bool rightShift = false;
|
|
bool leftAlt = false;
|
|
bool rightAlt = false;
|
|
|
|
bool control() const noexcept { return leftControl || rightControl; }
|
|
bool shift() const noexcept { return leftShift || rightShift; }
|
|
bool alt() const noexcept { return leftAlt || rightAlt; }
|
|
};
|
|
|
|
struct KeyEvent
|
|
{
|
|
KeyAction action = KeyAction::Down;
|
|
|
|
UINT virtualKey = 0; // Raw WndProc VK; VK_PROCESSKEY is never rewritten.
|
|
UINT physicalVirtualKey = 0; // Left/right-normalized modifier VK.
|
|
UINT scanCode = 0;
|
|
|
|
std::uint16_t repeatCount = 0;
|
|
bool extended = false;
|
|
bool wasDown = false;
|
|
bool altContext = false; // Raw lParam bit 29; modifiers.alt() is the semantic snapshot.
|
|
bool systemKey = false;
|
|
|
|
// Snapshot of the standard IME lifecycle axis at capture time.
|
|
// It never overrides the per-key ownership expressed by VK_PROCESSKEY.
|
|
bool imeComposingAtCapture = false;
|
|
|
|
KeyModifiers modifiers{};
|
|
|
|
bool isAutoRepeat() const noexcept
|
|
{
|
|
return action == KeyAction::Down && wasDown;
|
|
}
|
|
|
|
bool isImeOwnedAtCapture() const noexcept
|
|
{
|
|
// Per-key ownership and IME lifecycle are intentionally independent axes.
|
|
// A normal key captured during a standard composition remains a normal key;
|
|
// routing may suppress it because of the lifecycle snapshot, but must not
|
|
// rewrite its ownership.
|
|
return virtualKey == VK_PROCESSKEY;
|
|
}
|
|
};
|
|
|
|
enum class TextSource : std::uint8_t
|
|
{
|
|
ImeResult,
|
|
UnicodeChar,
|
|
UnicodeScalar,
|
|
AnsiCharFallback,
|
|
Clipboard
|
|
};
|
|
|
|
struct TextInputEvent
|
|
{
|
|
std::wstring text;
|
|
TextSource source = TextSource::UnicodeChar;
|
|
std::uint64_t compositionId = 0; // Zero for non-IME input.
|
|
};
|
|
|
|
enum class CompositionStage : std::uint8_t
|
|
{
|
|
Start,
|
|
Update,
|
|
Commit,
|
|
Cancel,
|
|
End
|
|
};
|
|
|
|
struct CompositionEvent
|
|
{
|
|
CompositionStage stage = CompositionStage::Start;
|
|
std::uint64_t compositionId = 0;
|
|
std::wstring compositionText;
|
|
std::uint32_t caretOffset = 0;
|
|
};
|
|
|
|
struct WindowActivationEvent
|
|
{
|
|
bool keyboardFocusActive = false;
|
|
HWND otherWindow = nullptr;
|
|
};
|
|
|
|
// Atomic producer snapshot copied into every native input event. Generation
|
|
// changes whenever keyboard focus active state changes, so a delayed event
|
|
// cannot cross a deactivate/reactivate boundary merely because the window is
|
|
// active again when the host thread eventually drains it.
|
|
struct NativeInputActivationSnapshot
|
|
{
|
|
bool keyboardFocusActive = false;
|
|
std::uint64_t generation = 0;
|
|
};
|
|
|
|
using NativeInputPayload = std::variant<
|
|
KeyEvent,
|
|
TextInputEvent,
|
|
CompositionEvent,
|
|
WindowActivationEvent>;
|
|
|
|
struct NativeInputEvent
|
|
{
|
|
std::uint64_t sequence = 0;
|
|
DWORD messageTime = 0;
|
|
HWND hwnd = nullptr;
|
|
NativeInputActivationSnapshot activationAtCapture{};
|
|
NativeInputPayload payload{};
|
|
};
|
|
}
|