Input System

Microsoft::Xna::Framework::Input — Keyboard, Mouse, GamePad, TouchPanel, TextInputEXT, Accelerometer

ⓘ

Implementation status: Input is one of CNA's broadest implemented namespaces. The Keys enum carries the full XNA key vocabulary (160 enumerators); GamePad provides rumble, trigger rumble, LED control, hot-plug handling and per-device capability probing; and TouchPanel detects all 10 XNA gesture types. This snapshot has 47 input test sources with 523 statically discoverable GoogleTest-family definitions (alpha.1: 43 and 490), and Microsoft.Xna.Framework.Input is the one XNA namespace whose public API is pinned by compile-time signature-freeze tests (PublicApiInputSignatureFreezeTests.cpp takes the address of, or static_asserts, every public Input member). One known contract bug is documented under TouchPanel.

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. The selected CNA platform (SDL3 by default) receives the underlying OS events and translates them into the state snapshots that CNA exposes.

💡

C++ spelling of properties. C# properties such as MouseState.X or GamePadState.IsConnected are accessor functions in CNA: getXProperty(), getIsConnectedProperty(), and setXProperty(value) where XNA has a setter. Nested value types chain the same way (pad.getThumbSticksProperty().getLeftProperty()). Static methods such as Keyboard::GetState() and GamePad::SetVibration(...) are plain functions. The tables and examples below use the real names.

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 (Keyboard::GetState(), or Keyboard::GetState(PlayerIndex)). 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.

MemberDescription
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 std::vector<Keys> of all Keys values that are currently down.

The Keys enum (160 enumerators) 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.getElapsedGameTimeProperty().getTotalSecondsProperty();

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 = 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. On the SDL3 platform’s x11, windows and cocoa video drivers CNA reads the global cursor position minus the window position, as XNA and FNA do, so the value is current and is not clamped to the window edge; other drivers report the window-relative state. In a browser, Mouse::SetPosition cannot move the real cursor, so CNA emulates the warp: it anchors the requested position and applies later physical movement to it, which keeps re-centring mouse-look working.

MemberTypeDescription
Mouse::GetState() MouseState Returns the current mouse state snapshot.
state.getXProperty() int Cursor X position in logical game coordinates: the window-client position mapped through the window's renderer transform (letterboxing, virtual resolution). It equals window pixels only when no transform applies.
state.getYProperty() int Cursor Y position in logical game coordinates, mapped the same way as X.
state.getScrollWheelValueProperty() int Accumulated scroll wheel delta since game start.
state.getLeftButtonProperty() ButtonState Pressed or Released.
state.getRightButtonProperty() ButtonState Pressed or Released.
state.getMiddleButtonProperty() ButtonState Pressed or Released.
state.getXButton1Property() ButtonState First extra side button.
state.getXButton2Property() ButtonState Second extra side button.
Mouse::SetPosition(int x, int y) void Warps the cursor to the given logical (game) coordinates: the point is converted through the window's renderer transform to window coordinates before the warp, the exact inverse of what GetState() reports, so under a virtual resolution or letterbox it is not a window pixel. It does nothing in relative mouse mode.

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.getLeftButtonProperty() == ButtonState::Released &&
    previousMouse.getLeftButtonProperty() == ButtonState::Pressed)
{
    // Left click at (mouse.getXProperty(), mouse.getYProperty())
    OnLeftClick(mouse.getXProperty(), mouse.getYProperty());
}

// Track scroll
int scrollDelta = mouse.getScrollWheelValueProperty() - previousMouse.getScrollWheelValueProperty();
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 getIsConnectedProperty() before reading state to avoid acting on a zeroed-out snapshot for an absent controller.

Behind the XNA surface, on the default SDL3 platform, sits a substantial SDL3 bridge. It is not a thin passthrough: it handles hot-plug (controllers connected and disconnected mid-session), per-device capability probing (so GamePadCapabilities reports what the specific pad actually has rather than a fixed assumption), and the full haptics surface — standard rumble, trigger rumble on pads that support it, and LED control.

ⓘ

No pad? Keyboard GamePad emulation (CNAEXT, off by default). GamePad::setKeyboardEmulationEnabledEXT(true) makes PlayerIndex::One a connected pad driven by the keyboard: W/A/S/D left stick, arrow keys right stick, T/F/G/H D-pad, K/L/J/I for A/B/X/Y, Q/E shoulders, Z/C triggers, Left/Right Shift stick clicks, Enter/Escape for Start/Back. Buttons are combined with a real pad’s, a non-zero keyboard axis wins over the physical one, dead zones still apply, and players two to four are unchanged. The keys stay visible to Keyboard::GetState. BigButton, vibration, sensors and controller identity are not emulated. The switch is process-wide; getKeyboardEmulationEnabledEXT() reads it.

MemberTypeDescription
GamePad::GetState(PlayerIndex) GamePadState Returns the state for the specified player slot. An overload takes a GamePadDeadZone (below); GamePad::GetCapabilities(PlayerIndex) returns the per-device GamePadCapabilities.
state.getIsConnectedProperty() bool Whether a controller is plugged in for this slot.
state.getThumbSticksProperty().getLeftProperty() Vector2 Left stick, X and Y in −1..1.
state.getThumbSticksProperty().getRightProperty() Vector2 Right stick, X and Y in −1..1.
state.getTriggersProperty().getLeftProperty() float Left trigger, 0..1.
state.getTriggersProperty().getRightProperty() float Right trigger, 0..1.
state.getButtonsProperty() GamePadButtons A, B, X, Y, Start, Back, LeftShoulder, RightShoulder, LeftStick, RightStick, BigButton — each read with getAProperty(), getBProperty() and so on, and each is ButtonState::Pressed or Released.
state.getDPadProperty() GamePadDPad Up, Down, Left, Right — read with getUpProperty() and so on; 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():

ModeBehaviour
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.getIsConnectedProperty()) {
    float speed = 200.0f * (float)gameTime.getElapsedGameTimeProperty().getTotalSecondsProperty();

    // Left stick movement
    Vector2 move = pad.getThumbSticksProperty().getLeftProperty();
    move.Y = -move.Y;  // XNA Y axis: up is positive on stick, down on screen
    playerPosition = playerPosition + move * speed;

    // Right trigger as an accelerator
    speed *= 1.0f + pad.getTriggersProperty().getRightProperty();

    // A button just pressed
    if (pad.getButtonsProperty().getAProperty() == ButtonState::Pressed &&
        previousPad.getButtonsProperty().getAProperty() == ButtonState::Released)
    {
        Jump();
    }

    // Vibrate on damage
    if (playerHit) {
        GamePad::SetVibration(PlayerIndex::One, 0.8f, 0.4f);
    }
}

previousPad = pad;

Beyond XNA: GamePad EXT methods

XNA 4.0 was designed around the Xbox 360 controller, so its GamePad API has no vocabulary for anything a modern pad does. CNA adds 21 CNAEXT-tagged static functions to GamePad to cover the gap: the keyboard-emulation switch above, device GUID, light bar colour, trigger vibration, gyroscope and accelerometer readings, battery/power info, touchpad finger positions, and the Steam input handle. The extra buttons modern pads expose — back paddles, Misc1, and the touchpad click — are available too, alongside six Keyboard scancode methods, MouseCursor, and TextInputEXT.

These are outside the XNA surface by construction. Compile with the CNA_STRICT_XNA_API definition on your target and the compiler flags every use as deprecated, which is what you want if the goal is a codebase that could also build against real XNA. (It is a compile definition, not a CMake option; CNA's own automated check of it covers the Devices namespace only.)

TouchPanel

The TouchPanel class exposes multi-touch and gesture recognition. All 10 XNA gesture types are genuinely detected — not stubbed, not partially wired — by a 474-line state machine that tracks contacts over time and computes flick velocity with exponential smoothing. Three points to know: GetState() is event-driven where FNA polls the platform every frame (a deliberate deviation from FNA; see Touch and gestures), while MaximumTouchCount reports 4 and GetState() caps at 8 touches, both matching FNA.

⚠

Known contract quirk: deliberate FNA parity, not XNA's contract. TouchCollection reports IsReadOnly == true, but its mutator methods and its indexer setter mutate the collection instead of throwing, as FNA's do (XNA 4.0's throw NotSupportedException); CNA's tests pin this as intended (TouchCollectionTest.IsReadOnlyIsAdvisoryAndMutationStillSucceedsLikeFna). Code that trusts IsReadOnly as a guarantee will be misled. Do not rely on the flag to protect a collection you hand out.

MemberTypeDescription
TouchPanel::GetState() TouchCollection Returns all active touch points this frame; the collection is iterable with a range-based for.
location.getIdProperty() int Unique identifier for this touch contact.
location.getStateProperty() TouchLocationState Pressed, Moved, or Released.
location.getPositionProperty() Vector2 Screen-space position of this touch point.
TouchPanel::getIsGestureAvailableProperty() bool Returns true when at least one gesture is queued.
TouchPanel::ReadGesture() GestureSample Dequeues and returns the next gesture sample.

All 10 GestureType values are detected: Tap, DoubleTap, Hold, HorizontalDrag, VerticalDrag, 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::setEnabledGesturesProperty(
    GestureType::Tap | GestureType::FreeDrag | GestureType::Flick);

// In Game::Update(GameTime& gameTime)
while (TouchPanel::getIsGestureAvailableProperty()) {
    GestureSample gesture = TouchPanel::ReadGesture();

    switch (gesture.getGestureTypeProperty()) {
        case GestureType::Tap:
            OnTap(gesture.getPositionProperty());
            break;
        case GestureType::Flick:
            // getDeltaProperty() holds the flick velocity
            playerVelocity = playerVelocity + gesture.getDeltaProperty() * 0.01f;
            break;
        case GestureType::FreeDrag:
            cameraOffset = cameraOffset - gesture.getDeltaProperty();
            break;
        default:
            break;
    }
}

New in this snapshot: mouse emulation and touch suppression

Two CNAEXT switches were added to TouchPanel. setMouseTouchEmulationEnabledEXT(true) makes the left mouse button synthesise a touch, so a touch UI can be tried on a desktop; it is off by default, matching XNA and FNA. getInputSuppressedEXT()/setInputSuppressedEXT() withhold touch input while a Guide message box or keyboard prompt is on screen; CNA raises it for you while such an overlay is pending. Mouse events that SDL synthesised from a touch do not feed the emulation back, so a real pinch stays two real fingers, and an emulated touch is clamped to the display. CNA also gained a MIME-typed CNA::Input::Clipboard API (including the X11 primary selection).

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. Draft IME text arrives separately through TextEditing (UTF-8), and TextEditingCandidatesEXT reports candidate lists.

MemberTypeDescription
TextInputEXT::TextInput Event Fires each time the user produces a printable character. The argument is one UTF-16 code unit (char16_t); a code point above U+FFFF, such as an emoji, arrives as two calls (high surrogate, then low surrogate), as in FNA.
#include <Microsoft/Xna/Framework/Input/TextInputEXT.hpp>

using namespace Microsoft::Xna::Framework::Input;

// In Game::Initialize() or wherever you set up input
// inputBuffer is a std::u16string; TextInput passes one UTF-16 code unit per call
TextInputEXT::TextInput += [this](char16_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. Text input is a mode, not a no-op on desktop: CNA's SDL3 platform turns SDL's text input on only from StartTextInput() (Sdl3TextInput::Start calls SDL_StartTextInput), and SDL3 sends text-input events only while that mode is on, so a game that never calls it receives no TextInput events on Windows, X11 or Wayland alike. Call StartTextInput() before you expect TextInput events.

Accelerometer (Sensors)

The Accelerometer class provides access to the device accelerometer. It is backed by the selected platform's real sensor service (the SDL3 sensor API on the default platform) on Android, iOS and desktop wherever a sensor exists, with correct m/s²-to-g conversion and substantial concurrency hardening around the sensor callbacks. Its sibling Gyroscope works the same way. See Sensors for the full Microsoft::Devices::Sensors surface, including which sensors are Android-only. Unlike the input classes above, the sensors are instances: construct one, Start() it and read it.

MemberTypeDescription
Accelerometer::getIsSupportedProperty() bool (static) true if the selected platform reports an accelerometer on an Android, iOS or desktop target, or, with keyboard emulation on, on desktop and in the browser.
accel.Start() / accel.Stop() void Begin and end data acquisition on an Accelerometer instance.
accel.getCurrentValueProperty() AccelerometerReading The latest reading.
reading.getAccelerationProperty() Vector3 Acceleration in G-force units along X, Y, Z device axes.
accel.getStateProperty() SensorState An enum (NotSupported, Ready, Initializing, NoData, NoPermissions, Disabled); getIsDataValidProperty() says whether live data has arrived.

Keyboard sensor and orientation emulation (CNAEXT, off by default). Accelerometer::setKeyboardEmulationEnabledEXT(true) replaces the primary accelerometer with a software one driven by the arrow keys (tilt of one g, no keys reads (0, 0, -1)), and getIsSupportedProperty() becomes true on desktop and in the browser without hardware; stop every running accelerometer before switching. Separately, getWindowProperty().setKeyboardOrientationEmulationEnabledEXT(true) lets Up, Left and Right request Portrait, LandscapeLeft and LandscapeRight within SupportedOrientations, through the normal device-reset path. Both are for trying phone-style games on a desktop; see Sensors.

Implementation status

Class Namespace Status Notes
Keyboard Input Done All 160 Keys enumerators. The scancode bridge maps 122 platform scancodes to keys and 119 keys back; IME, ChatPad, browser/media keys and OemBackslash have no physical position (see the input model). Public API pinned by compile-time signature-freeze tests.
Mouse Input Done All five buttons, scroll wheel, SetPosition.
GamePad Input Done SDL3 bridge: rumble, trigger rumble, LED, hot-plug, per-device capability probing. All three dead zone modes. 21 CNAEXT extension functions, including opt-in keyboard emulation of player one’s pad.
TouchPanel Input.Touch Done All 10 XNA gesture types detected by a 474-line state machine. Opt-in mouse-to-touch emulation and touch suppression (CNAEXT). See the TouchCollection::IsReadOnly FNA-parity note above.
TextInputEXT Input Done CNA extension (CNAEXT). UTF-16 char16_t code-unit events; IME composition and candidate events on desktop.
Accelerometer Devices.Sensors Done Instance-based (Start/getCurrentValueProperty); real platform sensor service on Android, iOS and desktop where a sensor exists; correct m/s²-to-g conversion. Built in every configuration: CNA_DEVICES gates only the CNA-specific device extensions (CNA::Devices), not the Microsoft::Devices sensors.