Files
StellarX-kaifa/include/StellarX/InputEvents.h
T

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{};
};
}