feat(input): 完成阶段 1 输入采集与规范化

This commit is contained in:
Codex
2026-10-07 20:42:50 +08:00
parent 18caf91633
commit c57ddaf74f
12 changed files with 1803 additions and 6 deletions
+124
View File
@@ -0,0 +1,124 @@
/*******************************************************************************
* StellarX native keyboard, text, IME, and activation event model.
* Stage 1 only captures, normalizes, queues, and observes these events.
* Focus, routing, and control behavior are intentionally out of scope.
******************************************************************************/
#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;
};
using NativeInputPayload = std::variant<
KeyEvent,
TextInputEvent,
CompositionEvent,
WindowActivationEvent>;
struct NativeInputEvent
{
std::uint64_t sequence = 0;
DWORD messageTime = 0;
HWND hwnd = nullptr;
NativeInputPayload payload{};
};
}