Mouse Capture and Global Mouse State

Learn how to track mouse movements and button states across your entire application, even when the mouse leaves your window.
This lesson is part of the course:

Game Dev with SDL2

Learn C++ and SDL development by creating hands on, practical projects inspired by classic retro games

Free, Unlimited Access
Abstract art representing computer programming
Ryan McCombe
Ryan McCombe
Posted

In our earlier lessons where we covered mouse events and mouse tracking, we were primarily concerned with what the mouse is doing whilst it is hovering over our windows. That is, when one of our windows has mouse focus.

However, in certain scenarios, we may need to understand what’s going on with the mouse even when we don’t have mouse focus. This lesson covers the techniques we can use to accomplish this, and some of the scenarios where we may need to.

Global Mouse State

We previously introduced the SDL_GetMouseState() function, which allows us to determine where the mouse is within our window.

The similar SDL_GetGlobalMouseState() function lets us find the cursor position, regardless of where it is on the user’s desktop. It has a familiar API - it accepts two pointers to integers, and will update those integers with the horizontal and vertical position of the cursor respectively:

int x, y;
SDL_GetGlobalMouseState(&x, &y);

Whilst SDL_GetMouseState() reports the position relative to the top-left of our window, SDL_GetGlobalMouseState() reports the position relative to the top-left corner of the primary display.

Note that this means that either of the coordinates can be negative. This can happen in multi-monitor setups, where the user has a display above and/or to the left of their primary display.

Similar to SDL_GetMouseState(), the SDL_GetGlobalMouseState() allows us to pass a nullptr to either argument. We’d use this if we only care about the mouse’s horizontal or vertical position:

// We only care about the horizontal position
int x;
SDL_GetGlobalMouseState(&x, nullptr);
// We only care about the vertical position
int y;
SDL_GetGlobalMouseState(nullptr, &y);

Button State

In addition to reporting the position of the mouse, the SDL_GetGlobalMouseState() function also lets us understand what buttons are currently pressed. It does this by return an unsigned integer, which acts as a bit-mask. We can use the & operator to examine individual bits to determine if specific buttons are pressed.

SDL includes the SDL_BUTTON_LMASK, SDL_BUTTON_MMASK, and SDL_BUTTON_RMASK to help us isolate the bits associated with the left, middle and right mouse buttons respectively:

void LogMouseGlobalState() {
  int x, y;
  Uint32 Buttons{SDL_GetGlobalMouseState(&x, &y)};  

  std::cout << "\nMouse Position: "
    << x << ", " << y;
  if (Buttons & SDL_BUTTON_LMASK) {
    std::cout << " - Left Button is pressed";
  }
  if (Buttons & SDL_BUTTON_MMASK) {
    std::cout << " - Middle Button is pressed";
  }
  if (Buttons & SDL_BUTTON_RMASK) {
    std::cout << " - Right Button is pressed";
  }
}

Mouse Capture

Previously, we covered how SDL will only report mouse events like movement and button presses if the event happens when one of our windows has mouse focus. That is, when the user’s cursor is hovering over one of our windows:

#include <SDL.h>
#include <iostream>
#include "Window.h"

void HandleWindowEvent(SDL_WindowEvent& E) {
  if (E.event == SDL_WINDOWEVENT_ENTER) {
    std::cout << "Mouse Entered Window - "
      "Tracking Resumed\n";
  } else if (E.event == SDL_WINDOWEVENT_LEAVE) {
    std::cout << "Mouse Left Window - "
      "Tracking Stopped\n";
  }
}

void HandleMouseMotion(SDL_MouseMotionEvent& E) {
  std::cout << "Mouse moved to "
    << E.x << ", " << E.y << '\n';
}

int main(int argc, char** argv) {
  SDL_Init(SDL_INIT_VIDEO);
  Window GameWindow;
  SDL_Event E;

  while (true) {
    while (SDL_PollEvent(&E)) {
      if (E.type == SDL_QUIT) {
        SDL_Quit();
        return 0;
      }
      if (E.type == SDL_WINDOWEVENT) {
        HandleWindowEvent(E.window);
      } else if (E.type == SDL_MOUSEMOTION) {
        HandleMouseMotion(E.motion);
      }
    }

    GameWindow.Update();
  }
}
Mouse moved to 2, 120
Mouse moved to 0, 120
Mouse Left Window - Tracking Stopped
Mouse Entered Window - Tracking Resumed
Mouse moved to 1, 108
Mouse moved to 2, 107

In some situations, we’d prefer mouse events be reported even when none of our windows have mouse focus. SDL refers to this as mouse capture, and we can enable it using the SDL_CaptureMouse() function.

We pass SDL_TRUE to enable capturing, or SDL_FALSE to disable it:

// Enable mouse capture
SDL_CaptureMouse(SDL_TRUE);

// Disable mouse capture
SDL_CaptureMouse(SDL_FALSE);

Enabling mouse capture for prolonged period of times is not recommended, as it can interfere with other windows the user might be interacting with. Instead, we typically enable mouse capture briefly for certain actions, and then disable it when those actions complete.

The most common use case for mouse capture is to support drag-and-drop interactions. For example, if we want to allow the user to drag something from one of our windows to another, there’s typically going to be a point in that action where neither of our windows have mouse focus.

Even if the user is dragging and dropping within the same window, their mouse may arc outside our window temporarily.

Without mouse capture, we may not be able to track the cursor when our windows do not have mouse focus. So instead, we’d typically enable mouse capture throughout that action to remove that restriction.

Mouse Grab vs Mouse Capture

Previously, we introduced the ability to have a window grab the mouse. This is often confused with mouse capture, but their effects are different.

  • Mouse Grabbing using SDL_SetWindowMouseGrab() prevents the cursor from leaving the window
  • Mouse Capture using SDL_CaptureMouse() does not constrain the cursor - rather, it asks SDL to continue to report mouse events even when the cursor is outside the window

Getting Capture State

Enabling mouse capture requires that one of our windows has input focus (ie, keyboard focus). Additionally, if we lose input focus, then mouse capture is automatically disabled.

The SDL_WindowFlags bit set includes an SDL_WINDOW_MOUSE_CAPTURE flag. It’s unusual that we would enable this when creating our window, for the reasons we covered above about not enabling it until necessary.

However, this flag is still helpful, as we can retrieve the flags at any time and examine the SDL_WINDOW_MOUSE_CAPTURE bit to understand if we’re currently capturing the mouse.

#include <SDL.h>
#include <iostream>

int main(int argc, char** argv) {
  SDL_Init(SDL_INIT_VIDEO);
  SDL_Window* Window{SDL_CreateWindow(
    "Example Window",
    SDL_WINDOWPOS_UNDEFINED,
    SDL_WINDOWPOS_UNDEFINED,
    400, 400, 0
  )};

  // Capture mouse
  SDL_CaptureMouse(SDL_TRUE);
  if (SDL_GetWindowFlags(Window)
      & SDL_WINDOW_MOUSE_CAPTURE) {
    std::cout << "Mouse is captured\n";
  }

  // Minimized windows lose input focus,
  // so we will lose mouse capture too
  SDL_MinimizeWindow(Window);
  if (!(SDL_GetWindowFlags(Window)
      & SDL_WINDOW_MOUSE_CAPTURE)) {
    std::cout << "Mouse is no longer captured";
  }

}
Mouse is captured
Mouse is no longer captured

Error Handling

SDL_CaptureMouse() returns 0 if it was successful, or a negative error code otherwise. We can use this to react to errors in the normal way, and call SDL_GetError() if we need a description of what went wrong.

The most common reasons enabling mouse capture will fail is that we didn’t have input focus when we tried to enable it, or it isn’t supported on the platform at all.

if (SDL_CaptureMouse(SDL_TRUE) < 0) {
  std::cout << "Error: " << SDL_GetError(); 
}
Error: No window has focus

Mouse Auto Capture

The drag-and-drop use case for enabling mouse capture is so common that, from version 2.0.22 of SDL, mouse capture is automatically enabled when the user holds down one of their mouse buttons.

The following program shows an example of this in action:

#include <SDL.h>
#include <iostream>

int main(int argc, char** argv) {
  SDL_Init(SDL_INIT_VIDEO);
  SDL_Window* Window{SDL_CreateWindow(
    "Example Window",
    SDL_WINDOWPOS_UNDEFINED,
    SDL_WINDOWPOS_UNDEFINED,
    400, 400, 0
  )};

  SDL_Event E;
  while (true) {
    while (SDL_PollEvent(&E)) {
      if (E.type == SDL_QUIT) {
        SDL_Quit();
        SDL_DestroyWindow(Window);
        return 0;
      }

      if (E.type == SDL_MOUSEBUTTONDOWN) {
        std::cout << "Mouse button down\n";
      } else if (E.type == SDL_MOUSEBUTTONUP) {
        std::cout << "Mouse button up\n";
      }
    }

    std::cout << "Capturing Mouse: ";
    if (SDL_GetWindowFlags(Window) & SDL_WINDOW_MOUSE_CAPTURE) {
      std::cout << "true\n";
    } else {
      std::cout << "false\n";
    }
  }
}
Capturing Mouse: false
Capturing Mouse: false
Mouse button down
Capturing Mouse: true
Capturing Mouse: true
Mouse button up
Capturing Mouse: false

This behavior is controlled by the SDL_HINT_MOUSE_AUTO_CAPTURE hint. By default, it is set to "1", representing we want auto-capture to be enabled.

We can disable auto-capture by setting it to "0" using SDL_SetHint():

#include <SDL.h>
#include <iostream>

int main(int argc, char** argv) {
  SDL_Init(SDL_INIT_VIDEO);
  SDL_Window* Window{SDL_CreateWindow(
    "Some Window",
    SDL_WINDOWPOS_UNDEFINED,
    SDL_WINDOWPOS_UNDEFINED,
    400, 400, 0
  )};

  SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "0");

  SDL_Event E;
  while (true) {
std::cout << "Capturing Mouse: "; if (SDL_GetWindowFlags(Window) & SDL_WINDOW_MOUSE_CAPTURE) { std::cout << "true\n"; } else { std::cout << "false\n"; } } }
Capturing Mouse: false
Capturing Mouse: false
Mouse button down
Capturing Mouse: false
Capturing Mouse: false
Mouse button up
Capturing Mouse: false

Summary

SDL's mouse capture and global mouse state features let us track mouse movement and button states beyond our window boundaries. This lets us implement advanced mouse interactions like drag-and-drop. Key topics covered:

  • Using SDL_GetGlobalMouseState() to track mouse position and button states across the entire desktop
  • Understanding the difference between window-relative and global mouse coordinates
  • Implementing mouse capture to maintain mouse event tracking outside window boundaries
  • Managing automatic mouse capture during drag operations using SDL hints
  • Handling common error cases and platform-specific considerations
  • Distinguishing between mouse capture and mouse grab functionality

Was this lesson useful?

Next Lesson

Relative Mouse Mode

Learn how to restrict cursor movement to a window whilst capturing mouse motion continuously.
Abstract art representing computer programming
Ryan McCombe
Ryan McCombe
Posted
Lesson Contents

Mouse Capture and Global Mouse State

Learn how to track mouse movements and button states across your entire application, even when the mouse leaves your window.

sdl2-promo.jpg
This lesson is part of the course:

Game Dev with SDL2

Learn C++ and SDL development by creating hands on, practical projects inspired by classic retro games

Free, Unlimited Access
Mouse Input
sdl2-promo.jpg
This lesson is part of the course:

Game Dev with SDL2

Learn C++ and SDL development by creating hands on, practical projects inspired by classic retro games

Free, unlimited access

This course includes:

  • 75 Lessons
  • 100+ Code Samples
  • 91% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Next Lesson

Relative Mouse Mode

Learn how to restrict cursor movement to a window whilst capturing mouse motion continuously.
Abstract art representing computer programming
Contact|Privacy Policy|Terms of Use
Copyright © 2025 - All Rights Reserved