Input System
Implementation status: Keyboard, Mouse, GamePad, and TouchPanel are fully implemented — TouchPanel's gesture pipeline (Tap, FreeDrag, Flick, Pinch...PinchComplete) is a byte-faithful FNA port, wired end-to-end and tested with a deterministic clock (~98% behavior). Accelerometer is declared and structurally wired but still requires hardware validation. The underlying OS event layer is provided by SDL3.
Overview
All input classes live in the Microsoft::Xna::Framework::Input namespace and follow a polling model that matches XNA 4.0 exactly: you call GetState() once per frame, store the result, and compare it with the previous frame's state to detect transitions. SDL3 handles the underlying OS events and translates them into the state snapshots that CNA exposes.
The typical per-frame pattern for any device is:
// Declare in your Game class
KeyboardState previousKeyboard;
// In Update()
KeyboardState currentKeyboard = Keyboard::GetState();
if (currentKeyboard.IsKeyDown(Keys::Space) && previousKeyboard.IsKeyUp(Keys::Space)) {
// Space was just pressed this frame
}
previousKeyboard = currentKeyboard; // save for next frame
The same pattern applies to MouseState and GamePadState. Because the state objects are plain value types, storing and copying them is cheap.
Keyboard
The Keyboard class provides a snapshot of every key on the physical keyboard. There is no distinction between left and right modifier keys at the KeyboardState level — use Keys::LeftShift / Keys::RightShift etc. when you need that granularity.
| Member | Description |
|---|---|
Keyboard::GetState() |
Returns a KeyboardState snapshot for the current frame. Call once per frame. |
state.IsKeyDown(Keys k) |
Returns true if the specified key is held down. |
state.IsKeyUp(Keys k) |
Returns true if the specified key is not pressed. |
state.GetPressedKeys() |
Returns a collection of all Keys values that are currently down. |
The Keys enum covers the full keyboard: alphanumeric keys, function keys (F1–F24), numpad keys (NumPad0–NumPad9, Multiply, Add, etc.), navigation keys (Home, End, PageUp, PageDown), and modifier keys (LeftShift, RightShift, LeftControl, RightControl, LeftAlt, RightAlt).
Detecting a key press (held last frame, pressed this frame) versus a key release:
// Press: key was up last frame and is down this frame
bool justPressed = current.IsKeyDown(k) && previous.IsKeyUp(k);
// Release: key was down last frame and is up this frame
bool justReleased = current.IsKeyUp(k) && previous.IsKeyDown(k);
Example: WASD movement
#include <Microsoft/Xna/Framework/Input/Keyboard.hpp>
using namespace Microsoft::Xna::Framework::Input;
// In Game::Update(GameTime gameTime)
KeyboardState kb = Keyboard::GetState();
Vector2 velocity = Vector2::Zero;
float speed = 200.0f * (float)gameTime.ElapsedGameTime.TotalSeconds();
if (kb.IsKeyDown(Keys::W) || kb.IsKeyDown(Keys::Up)) velocity.Y -= speed;
if (kb.IsKeyDown(Keys::S) || kb.IsKeyDown(Keys::Down)) velocity.Y += speed;
if (kb.IsKeyDown(Keys::A) || kb.IsKeyDown(Keys::Left)) velocity.X -= speed;
if (kb.IsKeyDown(Keys::D) || kb.IsKeyDown(Keys::Right)) velocity.X += speed;
playerPosition += velocity;
Mouse
The Mouse class returns a MouseState containing cursor coordinates in screen pixels, scroll wheel accumulation, and the state of all five buttons. Coordinates are relative to the top-left corner of the game window.
| Member | Type | Description |
|---|---|---|
Mouse::GetState() |
MouseState |
Returns the current mouse state snapshot. |
state.X |
int |
Cursor X position in window pixels. |
state.Y |
int |
Cursor Y position in window pixels. |
state.ScrollWheelValue |
int |
Accumulated scroll wheel delta since game start. |
state.LeftButton |
ButtonState |
Pressed or Released. |
state.RightButton |
ButtonState |
Pressed or Released. |
state.MiddleButton |
ButtonState |
Pressed or Released. |
state.XButton1 |
ButtonState |
First extra side button. |
state.XButton2 |
ButtonState |
Second extra side button. |
Mouse::SetPosition(int x, int y) |
void |
Warps the cursor to the given window coordinates. |
Example: mouse click detection
#include <Microsoft/Xna/Framework/Input/Mouse.hpp>
using namespace Microsoft::Xna::Framework::Input;
MouseState previousMouse;
// In Game::Update(GameTime gameTime)
MouseState mouse = Mouse::GetState();
// Single click: button was released this frame
if (mouse.LeftButton == ButtonState::Released &&
previousMouse.LeftButton == ButtonState::Pressed)
{
// Left click at (mouse.X, mouse.Y)
OnLeftClick(mouse.X, mouse.Y);
}
// Track scroll
int scrollDelta = mouse.ScrollWheelValue - previousMouse.ScrollWheelValue;
if (scrollDelta != 0) {
camera.Zoom(scrollDelta * 0.1f);
}
previousMouse = mouse;
GamePad
The GamePad class supports up to four simultaneously connected controllers via the PlayerIndex enum (One through Four). GamePadState exposes analog sticks, triggers, buttons, and the D-pad. Always check IsConnected before reading state to avoid acting on a zeroed-out snapshot for an absent controller.
| Member | Type | Description |
|---|---|---|
GamePad::GetState(PlayerIndex) |
GamePadState |
Returns the state for the specified player slot. |
state.IsConnected |
bool |
Whether a controller is plugged in for this slot. |
state.ThumbSticks.Left |
Vector2 |
Left stick, X and Y in −1..1. |
state.ThumbSticks.Right |
Vector2 |
Right stick, X and Y in −1..1. |
state.Triggers.Left |
float |
Left trigger, 0..1. |
state.Triggers.Right |
float |
Right trigger, 0..1. |
state.Buttons |
GamePadButtons |
A, B, X, Y, Start, Back, LeftShoulder, RightShoulder, LeftStick, RightStick — each is ButtonState::Pressed or Released. |
state.DPad |
GamePadDPad |
Up, Down, Left, Right — each is ButtonState::Pressed or Released. |
GamePad::SetVibration(PlayerIndex, float left, float right) |
bool |
Sets rumble motor strength, 0.0–1.0 per motor. Returns false if the controller does not support vibration. |
Dead zone handling
Analog sticks rarely rest at exactly (0, 0) due to hardware tolerances. CNA applies a dead zone before returning stick values. The dead zone mode is passed as an optional second argument to GetState():
| Mode | Behaviour |
|---|---|
GamePadDeadZone::IndependentAxes |
Default. X and Y are clamped and rescaled independently. |
GamePadDeadZone::Circular |
The dead zone is a circle; values inside it are zeroed, the remainder is rescaled radially. |
GamePadDeadZone::None |
Raw values; no dead zone applied. Use when you implement your own dead zone logic. |
Example: analog stick movement
#include <Microsoft/Xna/Framework/Input/GamePad.hpp>
using namespace Microsoft::Xna::Framework::Input;
GamePadState previousPad;
// In Game::Update(GameTime gameTime)
GamePadState pad = GamePad::GetState(PlayerIndex::One);
if (pad.IsConnected) {
float speed = 200.0f * (float)gameTime.ElapsedGameTime.TotalSeconds();
// Left stick movement
Vector2 move = pad.ThumbSticks.Left;
move.Y = -move.Y; // XNA Y axis: up is positive on stick, down on screen
playerPosition += move * speed;
// Right trigger as an accelerator
speed *= 1.0f + pad.Triggers.Right;
// A button just pressed
if (pad.Buttons.A == ButtonState::Pressed &&
previousPad.Buttons.A == ButtonState::Released)
{
Jump();
}
// Vibrate on damage
if (playerHit) {
GamePad::SetVibration(PlayerIndex::One, 0.8f, 0.4f);
}
}
previousPad = pad;
TouchPanel
The TouchPanel class exposes multi-touch and gesture recognition. The SDL3 touch backend and gesture pipeline (Tap through PinchComplete) are a byte-faithful FNA port, fully wired end-to-end and tested with a deterministic clock (~98% behavior). Documented deviations only: GetState() is event-driven rather than poll-based, MaximumTouchCount reports 4, and GetState() caps at 8 touches — all intentionally matching FNA.
| Member | Type | Description |
|---|---|---|
TouchPanel::GetState() |
TouchCollection |
Returns all active touch points this frame. |
location.Id |
int |
Unique identifier for this touch contact. |
location.State |
TouchLocationState |
Pressed, Moved, or Released. |
location.Position |
Vector2 |
Screen-space position of this touch point. |
TouchPanel::IsGestureAvailable |
bool |
Returns true when at least one gesture is queued. |
TouchPanel::ReadGesture() |
GestureSample |
Dequeues and returns the next gesture sample. |
Supported GestureType values: Tap, DoubleTap, Hold, FreeDrag, Flick, Pinch, PinchComplete, DragComplete.
Example: gesture reading
#include <Microsoft/Xna/Framework/Input/Touch/TouchPanel.hpp>
using namespace Microsoft::Xna::Framework::Input::Touch;
// Configure which gestures you want before your game loop
TouchPanel::EnabledGestures = GestureType::Tap | GestureType::FreeDrag | GestureType::Flick;
// In Game::Update(GameTime gameTime)
while (TouchPanel::IsGestureAvailable) {
GestureSample gesture = TouchPanel::ReadGesture();
switch (gesture.GestureType) {
case GestureType::Tap:
OnTap(gesture.Position);
break;
case GestureType::Flick:
// gesture.Delta holds the flick velocity
playerVelocity += gesture.Delta * 0.01f;
break;
case GestureType::FreeDrag:
cameraOffset -= gesture.Delta;
break;
default:
break;
}
}
Physical hardware validation still pending: The TouchPanel SDL3 backend and gesture pipeline are implemented, wired end-to-end, and tested with a deterministic clock — but gesture recognition has not yet been separately confirmed on a physical touchscreen device. Feedback from real-hardware testing is welcome.
TextInputEXT
TextInputEXT is a CNA extension (not in the original XNA 4.0 API) that captures text entry events from the OS input method, including IME composition on platforms that support it. It is the correct way to implement chat boxes, in-game consoles, or any other free-text input — keyboard polling alone will not handle key repeat, modifier combinations, or international characters correctly.
| Member | Type | Description |
|---|---|---|
TextInputEXT::TextInput |
Event | Fires each time the user produces a printable character. The event provides a char32_t Unicode code point. |
#include <Microsoft/Xna/Framework/Input/TextInputEXT.hpp>
using namespace Microsoft::Xna::Framework::Input;
// In Game::Initialize() or wherever you set up input
TextInputEXT::TextInput += [this](char32_t c) {
if (c == U'\b') { // backspace
if (!inputBuffer.empty()) inputBuffer.pop_back();
} else if (c >= U' ') { // printable
inputBuffer += c;
}
};
// To stop receiving events (e.g. when the text field is dismissed)
// TextInputEXT::StopTextInput();
Call TextInputEXT::StartTextInput() to activate text-entry mode (which may show a soft keyboard on mobile) and StopTextInput() when done. On desktop platforms these calls have no visible effect but are still good practice for cross-platform portability.
Accelerometer (Sensors)
The Accelerometer class provides access to the device accelerometer on platforms that expose one (primarily Android and iOS). The API is declared and compiles on all platforms; the Android backend integration needs runtime validation before it can be considered production-ready.
| Member | Type | Description |
|---|---|---|
Accelerometer::GetState() |
AccelerometerState |
Returns the current accelerometer reading. |
state.Acceleration |
Vector3 |
Acceleration in G-force units along X, Y, Z device axes. |
state.IsActive |
bool |
true if the sensor is available and returning data. |
Android runtime validation needed: The Accelerometer API compiles on all target platforms. The Android backend needs validation on a physical device before this class should be relied upon in shipping code.
Implementation status
| Class | Namespace | Status | Notes |
|---|---|---|---|
Keyboard |
Input |
Done | Full Keys enum; SDL3 scancode mapping complete. |
Mouse |
Input |
Done | All five buttons, scroll wheel, SetPosition. |
GamePad |
Input |
Done | Up to four players; vibration; all three dead zone modes. |
TouchPanel |
Input.Touch |
Partial | API declared; SDL3 backend wired; hardware validation pending. |
TextInputEXT |
Input |
Done | Unicode char32_t events; IME-safe on desktop. |
Accelerometer |
Input |
Partial | API declared; compiles everywhere; Android runtime validation needed. |