How to Capture Mouse Cursor in QWindow for First-Person Camera Control (Raw Mouse Input / Relative Mouse Mode)?
-
Hi everyone,
I'm developing a graphics application (a game) with a first-person perspective using Qt (QWindow ) and I'm facing a challenge with mouse-based camera control.
My Goal:
I need the mouse cursor to remain "captured" within my QWindow, and for me to receive only relative changes in mouse position (deltas), not absolute cursor coordinates. This is crucial for smooth and continuous camera rotation, regardless of the physical screen boundaries or the cursor moving outside the QWindow. Additionally, the system cursor should be hidden, as I plan to draw my own custom cursor or not display one at all.The Problem:
Standard QMouseEvents (for example, from QWindow::mouseMoveEvent) stop being sent when the mouse cursor reaches the edge of the screen or moves outside my QWindow. This results in the camera "hitting a wall" and interrupting rotation.What I've Explored/Tried:
I understand that this functionality (often referred to as "raw mouse input" or "relative mouse mode") is possible on all major platforms (Windows, Linux, macOS) and is commonly used in games.
I've looked into the QSinglePointEvent::exclusivePointGrabber property, but:
I can't find a working example of how to use it.
Based on its description, it seems to be more about "grabbing" pointer events (to ensure they're delivered only to one QWindow), rather than "locking" the cursor in the center of the window and providing relative deltas. My primary need is for the cursor not to escape and hit the screen edge.My Question:
Is there a built-in, cross-platform way in Qt to achieve this "relative mouse mode" (mouse grabbing / relative mouse mode) for a QWindow, such that:The mouse cursor remains contained within the QWindow?
I receive only xrel and yrel (delta) mouse movements?
The system cursor is hidden?
I'm looking for something that works like SDL_SetRelativeMouseMode in SDL
Any help and code examples would be greatly appreciated!
Thank you. -
I've done this with only Qt APIs by calling QCursor::setPos everytime I get a new cursor position. The delta is the diff between the new position and the forced pos.
A blank cursor can be set with
QGuiApplication::setOverrideCursor(QCursor(Qt::BlankCursor));
-
I've done this with only Qt APIs by calling QCursor::setPos everytime I get a new cursor position. The delta is the diff between the new position and the forced pos.
A blank cursor can be set with
QGuiApplication::setOverrideCursor(QCursor(Qt::BlankCursor));
@GrecKo "Oh, I did that, but it's a bad solution and mostly a crutch, as I know that all display servers on all platforms offer mouse capture functionality at the windowing system level, and that's exactly what should be used.
The problem with setPos is that it's an attempt to modify a system component – a direct security violation – and new window managers like Wayland directly forbid such a function. I've written about this here and here, but I haven't found a relatively simple solution to this problem. Currently, I'm using the outdated xWayland for Wayland to redirect setPos through the X11 API where such functionality is allowed.
However, using xWayland for games means huge FPS losses for no good reason, which is especially noticeable with Vulkan, and that's extremely frustrating.
I need a way to directly subscribe to the cursor capture functionality, so that for a specific window, the display server starts sending deltas instead of absolute coordinates. These deltas should always be received, regardless of where the cursor physically lands."