16
\$\begingroup\$

How can I trap the user's mouse inside my game? It's not a fullscreen game, but I want it so that the user cannot drag the mouse to outside the screen.

Do I have to check the coordinates of the mouse and SDL_WarpCursor to make it say inside the window (manually), or is there an automatic way?

Thank you.

\$\endgroup\$
1
  • \$\begingroup\$ Have you considered hiding the cursor and warping it back to the center? Then, just draw your crosshair in the center of the screen in place of the OS cursor. \$\endgroup\$ Commented Aug 3, 2012 at 14:53

4 Answers 4

26
\$\begingroup\$

While you can implement this using SDL_WarpCursor(), I've run into problems with that method on some platforms. I've had real problems with some platforms not reliably performing the WarpCursor() action, particularly when I've been calling it every frame.

Also, remember that on many platforms, the cursor is handled at a higher frequency than your app. This is particularly true if you're running below 60fps! So even if the platform is successfully warping the mouse on every frame of your game, the cursor could still get away from your window if it's receiving updates in between your rendered frames.

The correct method for trapping the mouse under SDL is:

SDL_WM_GrabInput( SDL_GRAB_ON );

This tells SDL that your intention is to actually grab full control of mouse + keyboard (see the documentation), and to therefore keep the mouse inside the window no matter what. From this point on, the mouse will not move outside of the window, regardless of your frame rate, and you should not call SDL_WarpCursor, except if you really do intend to teleport the cursor somewhere, rather than spamming it continuously.

While in this "grabbing" mode, you will continue to receive mouse motion events as though the mouse was not being constrained inside the window. (So if the cursor is at the right edge of the screen, and the user moves his mouse further right, you'll receive a mouse motion event showing motion to the right, though the cursor position will not change). This is really useful for (for example) turning controls for FPS games, where you only care about relative motion of the cursor, not its absolute position.

Update for SDL2 -- 9 December, 2013

This interface has changed for SDL2. If you're using SDL1.2, the answer above is still correct. However, under SDL2 the SDL_WM_GrabInput(SDL_GrabMode) function is no longer available. SDL2's new way to capture the mouse is:

SDL_SetRelativeMouseMode(SDL_TRUE);

Otherwise, this is identical to the earlier function -- in this mode you will continue to receive SDL_MOUSEMOTION events as though the mouse wasn't being constrained inside the window.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Caveat: I've noticed that on some platforms, I immediately receive a spurious MouseMotion event with a very large motion, immediately after turning on mouse grabbing. To cope with that, I have my code ignore the first mouse motion event received after turning on this mode. \$\endgroup\$ Commented Aug 4, 2012 at 1:19
  • \$\begingroup\$ That worked perfectly, and it's much easier than the manual way. Thank you! \$\endgroup\$ Commented Aug 4, 2012 at 10:53
3
\$\begingroup\$

While Trevor's updated answer does reflect the current state of SDL2, you can also trap the mouse by creating your SDL_Window with the SDL_WINDOW_INPUT_GRABBED set. Be aware that I've run into some strange issues on Linux (presumably due to some X windowing quirk) where you cannot ever exit the window unless SDL_QUIT is called (even if another OS event steals the focus away).

\$\endgroup\$
1
  • \$\begingroup\$ Thanks you, SDL_WINDOW_INPUT_GRABBED work perfectly on my SDL2 code. \$\endgroup\$ Commented May 14, 2014 at 23:04
3
\$\begingroup\$

For SDL2 there seems to also be the function SDL_SetWindowGrab if you want to enable and disable it.

SDL_SetWindowGrab(SDL_Window* window, SDL_bool grabbed)
\$\endgroup\$
2
\$\begingroup\$

You can use the SDL Motion Event to detect when the mouse moves. Then, you can call WarpCursor inside that even after firing an event to pipe the relX and relY values. You can also hide your OS cursor and draw a crosshair or something similar in the center of the screen. This ensures that the mouse seems to always be in the middle for the user and you can still receive values for motion and the like.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .