Tutorial 11: Handling Mouse Input

CNA Tutorial Series  ·  Beginner

Mouse::GetState()

Mouse input follows the same snapshot-polling pattern as keyboard input. Call Mouse::GetState() once at the start of Update():

#include "Microsoft/Xna/Framework/Input/Mouse.hpp"

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

void MyGame::Update(GameTime& gameTime) {
    MouseState ms = Mouse::GetState();
    // Use 'ms' to read position, buttons, and scroll wheel
}

MouseState is a lightweight value type. Keeping the previous frame's state alongside the current one lets you detect click events (press) and release events.

MouseState Struct

MouseState exposes the following properties:

Property / MethodTypeDescription
getX()intcsMouse X position in client (window) coordinates
getY()intcsMouse Y position in client coordinates
getPosition()PointPosition as a Point{X, Y}
getLeftButton()ButtonStateLeft mouse button
getRightButton()ButtonStateRight mouse button
getMiddleButton()ButtonStateMiddle mouse button (scroll wheel click)
getXButton1()ButtonStateExtra mouse button 1 (side button)
getXButton2()ButtonStateExtra mouse button 2 (side button)
getScrollWheelValue()intcsCumulative scroll wheel ticks (can be negative)

Mouse Position (X and Y)

Mouse position is returned in window client coordinates: (0,0) is the top-left corner of the game window, with X increasing right and Y increasing down.

void MyGame::Update(GameTime& gameTime) {
    MouseState ms = Mouse::GetState();

    int mouseX = ms.getX();
    int mouseY = ms.getY();

    // As a Vector2 for math:
    Vector2 mousePos(static_cast<float>(mouseX), static_cast<float>(mouseY));

    // As a Point (integer):
    Point mousePoint = ms.getPosition();

    std::cout << "Mouse: " << mouseX << ", " << mouseY << "\n";
}

The position is in screen pixels, which is what you need when comparing against sprite rectangles. If your game uses a virtual resolution with a scaling matrix, you will need to transform the mouse position through the inverse of that matrix.

Button State

ButtonState is an enum with two values:

ButtonState::Pressed   // button is currently held down
ButtonState::Released  // button is not held

Hold detection (is the button currently down?)

MouseState ms = Mouse::GetState();

if (ms.getLeftButton() == ButtonState::Pressed) {
    // Left button is held — drag or continuous fire
    std::cout << "Left button held at " << ms.getX() << ", " << ms.getY() << "\n";
}

Click detection (single press event)

As with keyboard input, single-click events require comparing the current and previous state:

// Class members:
MouseState prevMs_;

// In Update():
MouseState ms = Mouse::GetState();

bool leftClicked  = (ms.getLeftButton()  == ButtonState::Pressed) &&
                    (prevMs_.getLeftButton()  == ButtonState::Released);
bool rightClicked = (ms.getRightButton() == ButtonState::Pressed) &&
                    (prevMs_.getRightButton() == ButtonState::Released);

if (leftClicked) {
    // Fire once on click
    spawnProjectile(ms.getX(), ms.getY());
}
if (rightClicked) {
    // Right-click action
    openContextMenu(ms.getX(), ms.getY());
}

prevMs_ = ms;  // save for next frame

Hit testing: did the user click a rectangle?

Rectangle buttonRect(200, 250, 200, 60);  // x, y, w, h

bool clickedButton = leftClicked && buttonRect.Contains(ms.getPosition());
if (clickedButton) {
    std::cout << "Button clicked!\n";
    startGame();
}

Rectangle::Contains(Point) returns true if the point is inside the rectangle (inclusive). ms.getPosition() returns the mouse position as a Point.

ScrollWheelValue

getScrollWheelValue() returns a cumulative counter of scroll wheel ticks. It increases when you scroll up and decreases when you scroll down. To detect scroll events, compare the current and previous value:

// Class member:
int prevScrollValue_ = 0;

// In Update():
MouseState ms = Mouse::GetState();
int scrollDelta = ms.getScrollWheelValue() - prevScrollValue_;

if (scrollDelta > 0) {
    // Scrolled up — zoom in
    cameraZoom_ = std::min(cameraZoom_ + 0.1f, 3.0f);
} else if (scrollDelta < 0) {
    // Scrolled down — zoom out
    cameraZoom_ = std::max(cameraZoom_ - 0.1f, 0.5f);
}

prevScrollValue_ = ms.getScrollWheelValue();

One notch of the scroll wheel typically corresponds to 120 units (SDL convention). Divide by 120 to get "notches scrolled":

int notches = scrollDelta / 120;  // positive = up, negative = down

Mouse::SetPosition()

Mouse::SetPosition(x, y) moves the OS cursor to a specific position within the game window. This is useful for FPS-style games where you want to lock the cursor to the screen centre:

// Lock the cursor to the window centre every frame
auto& vp = getGraphicsDeviceProperty().getViewport();
int centreX = vp.getWidth()  / 2;
int centreY = vp.getHeight() / 2;

Mouse::SetPosition(centreX, centreY);

// Read the delta from this centre position
MouseState ms = Mouse::GetState();
// After SetPosition, GetState will return the new position immediately
// so store the delta before calling SetPosition

Typical FPS mouse-look pattern:

void MyGame::Update(GameTime& gameTime) {
    auto& vp = getGraphicsDeviceProperty().getViewport();
    int cx = vp.getWidth() / 2, cy = vp.getHeight() / 2;

    MouseState ms = Mouse::GetState();

    // Compute delta from centre
    float dx = static_cast<float>(ms.getX() - cx);
    float dy = static_cast<float>(ms.getY() - cy);

    cameraYaw_   += dx * mouseSensitivity_;
    cameraPitch_ += dy * mouseSensitivity_;

    // Clamp pitch to prevent flipping
    cameraPitch_ = std::clamp(cameraPitch_, -MathHelper::PiOver2 + 0.01f,
                                             MathHelper::PiOver2 - 0.01f);

    // Reset cursor to centre
    Mouse::SetPosition(cx, cy);
}

Cursor Visibility

Hide or show the OS cursor:

// Hide the cursor (e.g., when showing a custom crosshair)
Mouse::setIsVisible(false);

// Show the cursor again (e.g., in menus)
Mouse::setIsVisible(true);

// Query current visibility
bool visible = Mouse::getIsVisible();

Common pattern: hide the cursor when entering gameplay and show it when the game is paused or in a menu.

Complete drag demo

class DragDemo final : public Game {
public:
    DragDemo() : graphics_(this) {}

protected:
    void LoadContent() override {
        spriteBatch_ = std::make_unique<SpriteBatch>(getGraphicsDeviceProperty());
        pixel_ = std::make_unique<Texture2D>(getGraphicsDeviceProperty(), 1, 1);
        Color w = Color::White;
        pixel_->SetData(&w, 1);
    }

    void Update(GameTime& gameTime) override {
        MouseState ms = Mouse::GetState();
        Point mousePos = ms.getPosition();

        bool lbDown = ms.getLeftButton() == ButtonState::Pressed;
        bool lbClick = lbDown && prevMs_.getLeftButton() == ButtonState::Released;

        Rectangle boxRect(static_cast<int>(boxX_), static_cast<int>(boxY_), 80, 80);

        // Start dragging on click inside the box
        if (lbClick && boxRect.Contains(mousePos)) {
            dragging_ = true;
            dragOffsetX_ = static_cast<float>(mousePos.X) - boxX_;
            dragOffsetY_ = static_cast<float>(mousePos.Y) - boxY_;
        }
        // Move box while dragging
        if (dragging_ && lbDown) {
            boxX_ = static_cast<float>(mousePos.X) - dragOffsetX_;
            boxY_ = static_cast<float>(mousePos.Y) - dragOffsetY_;
        }
        // Stop dragging on release
        if (!lbDown) dragging_ = false;

        if (Keyboard::GetState().IsKeyDown(Keys::Escape)) Exit();
        prevMs_ = ms;
    }

    void Draw(const GameTime&) override {
        auto& device = getGraphicsDeviceProperty();
        device.Clear(Color::CornflowerBlue);
        spriteBatch_->Begin();
        Color boxColor = dragging_ ? Color::Yellow : Color::Red;
        spriteBatch_->Draw(*pixel_,
            Rectangle(static_cast<int>(boxX_), static_cast<int>(boxY_), 80, 80),
            boxColor);
        spriteBatch_->End();
        device.Present();
    }

private:
    GraphicsDeviceManager graphics_;
    std::unique_ptr<SpriteBatch> spriteBatch_;
    std::unique_ptr<Texture2D> pixel_;
    MouseState prevMs_;
    float boxX_ = 360.0f, boxY_ = 260.0f;
    float dragOffsetX_ = 0, dragOffsetY_ = 0;
    bool dragging_ = false;
};

Click the red box to drag it around. It turns yellow while dragging. In Tutorial 12 you will combine input, delta time, and texture drawing into a proper moving sprite demo.