Trap cursor in Window - c++

I wrote my own cameraclass in C++ using DX11.
At the moment I use WM_MOUSEMOVE events to look around in the scene. To prevent the cursur to get out of the window, I call the function SetCursorPos to center the mouse whenever a WM_MOUSEMOVE event occurs.
But if I move my mouse very fast, the cursor gets out of the window.
A solution to that was using the function ClipCursor, but this leads to falter the rotation of the camera when the cursor hits the border of the rect. So ClipCursor solved the original problem, but ended in another.
Do you guys have any solution to that?

For a Windows desktop app, just use ClipCursor giving the rectangle of your window:
RECT rect;
GetClientRect(mWindow, &rect);
POINT ul;
ul.x = rect.left;
ul.y = rect.top;
POINT lr;
lr.x = rect.right;
lr.y = rect.bottom;
MapWindowPoints(mWindow, nullptr, &ul, 1);
MapWindowPoints(mWindow, nullptr, &lr, 1);
rect.left = ul.x;
rect.top = ul.y;
rect.right = lr.x;
rect.bottom = lr.y;
ClipCursor(&rect);
Be sure to have a way to escape this mode so that users can choose to interact with other windows if desired. Typically you call ClipCursor(nullptr); when you go to a 'pause' menu to get out of 'mouse-look' behavior like this.
You can use "raw" input in desktop apps as well, see Taking Advantage of High-Definition Mouse Movement. Keep in mind that raw input works pretty well for relative-movement 'mouse-look' behavior, but (a) it won't work over remote desktop, and (b) you won't get 'pointer-ballistics' which is a non-linear movement rate that makes a mouse more agile so you should generally stick with traditional WM_MOUSE messages when dealing with absolute-movement.
For Universal Windows Platform, you can't use "raw" input as there's no equivalent to WM_INPUT, but you do get high-precision data out of the MouseDevice.MouseMoved event via the MouseDelta property. You don't need to use ClipCursor for relative movement in UWP, just turning off the cursor by setting the CoreWindow.PointerCursor property to nullptr will prevent the system mouse position from being tracked. Again, you should restore the system cursor when you are in a 'pause' menu. See Developing mouse controls (DirectX and C++).
See DirectX Tool Kit Mouse helper class and more importantly the implementation file. For relative mode, it makes use of both ClipCursor and WM_INPUT for the Windows desktop Win32 implementation.

The regular windows message are not the best solution to drive precise controls. They inherit from the OS acceleration system, clipping and depends on other shenanigans as you can see.
The best API to receive mouse inputs is Raw Input. It has the advantage to expose the better dpi and polling rate the hardware can provide and is free of any under the hood manipulation. Once you read the mouse with this, you are free to use SetCapture and clipCursor to prevent unwanted click to an other window.
You can find the documentation here : https://msdn.microsoft.com/en-us/library/windows/desktop/ms645536(v=vs.85).aspx

Related

Setting/Getting my absolute mouse position in windowed mode

I searched, but most posts are just telling me what I already have, so below is basically my code right now:
DIKeyboard->Acquire();
DIMouse->Acquire();
DIMouse->GetDeviceState(sizeof(DIMOUSESTATE), &mouseCurrState);
DIKeyboard->GetDeviceState(sizeof(keyboardState),(LPVOID)&keyboardState);
MousePos.x += mouseCurrState.lX;
MousePos.y += mouseCurrState.lY;
Any post telling me how to get absolute position just says to use those last two lines. But my program is windowed, and the mouse can start anywhere on the screen.
i.e. If my mouse happens to be in the centre of my screen, that becomes position 0,0. I basically just want the top left of my window (not my screen) to be my 0,0 mouse coordinates, but am having a hard time finding anything relevant.
Thanks for any help! :)
Following the discussion in the comments, you'll have to decide which method works best for you. Unfortunately, having never worked with DirectInput, I do not know the ins-and-outs of it.
However, Window Messages work best for RTS-style controls, where a cursor is drawn to screen. This is due to the fact that this respects user settings, such as mouse acceleration and mouse speed, whereas DirectInput only uses the driver settings (so not the control panel settings). The user will expect the mouse to feel the same, especially in windowed mode.
DirectInput works better for FPS-style controls, when there is no cursor drawn, as window messages give you only the cursor coordinates, and not offset values. This means that once you are at the edge of the screen, window messages will no longer allow you to detect the mouse being moved further (actually, I am not 100% sure on this, so if someone could verify, please feel free to comment).
For keyboard, I would definitely suggest window messages, because DirectInput offers no advantages, and WM input is easier to use, and quite powerful (the WM_KEYDOWN messages contains a lot of useful data), and it'll allow you (via TranslateMessage) to get good text input, adjusted to locale, etc.).
Solving your problem with DirectInput:
You could probably use GetCursorPos followed by ScreenToClient to initialise your MousePos structure. I'm guessing you'll need to redo this every time you lose mouse input and reacquire it.
Hybrid solution (for RTS like controls):
It might be possible to use a hybrid solution for the mouse if you desire RTS-like controls. If this is the case, I suggest, though I have not tested this, to use WM for the movement of the mouse, which avoids the need for workaround mentioned above, and only use DirectInput to detect additional mouse buttons.
Now one thing I think you should do in such a hybrid approach is not directly use the button when you detect it via DirectInput, but rather post a custom application message to your own message queue (using PostMessage and WM_APP) with the relevant information. I suggest this because using WM you do not get the real-time state of the mouse & keyboard, but rather the state at the time of the message. Posting a message that the button was pressed allows you to handle the extra buttons in the same state-dependent manner (I don't know how noticeable this 'lag' effect is). It also makes the entire input handling very uniform, as every bit of input with this enters as a window message.

Capture mouse movement in win32/Opengl

At the moment I simply use the WM_MOUSEMOVE message, but it limits the mouse movement to the maximum resolution. So what's the best way of capturing the mouse with Win32 (on a OpenGl window)? I don't want to use freeglut or any extra library.
For games and realtime DirectInput is very suitable, it's moderately hard to use.
That is not core win32 api, the winapi way of getting the input is either GetCursorPos/SetCursorPos driven by your own update loop, so you query and reset with your own frequency.
Or SetCapture and then upon WM_MOUSEMOVE you call SetCursorPos.
The point of setting the cursor pos is to give room for movement so you can get the delta, the amount the cursor moved since the last update and then put it back the cursor into the center of your window.
If you want to be able to capture mouse events after the mouse has existed the window, then you might want to look into the SetCapture function
If your problem is that you want to make a FPS game and you want your character to be able to spin in a continuous motion, then you want to set the mouse position to the center of the window after each mouse move event and handle input based on the difference between the position of the cursor when the mouse move event is fired and the center of the screen. To set the position of the mouse you can use the SetCursorPos function.
Your best bets are to either use DirectInput (which can be a bit of a pain to set up) or RawInput.
There is a fairly comprehensive example available in the Using RawInput page (See example 2).

How do I change the cursor to a line in an OpenGL window?

I want to have a red line instead of mouse pointer in my (written in C++ with OpenGL) application. For example when I move the mouse over an OpenGL window, I would like the cursor to become a red line. How can I do that?
Also, how can I call the mouse related functions (OpenGL) for that line and specify the functionality?
As Nicol Bolas said in his comment, OpenGL knows nothing of the mouse. You'll need to interact with the windowing system one way or another (via direct Win32/X11/etc. API or via a portable windowing library a la Qt, wxWidgets, etc) to monitor mouse position.
If the cursor you're trying to draw is a bitmap, your best bet is likely to handle mouse enter/leave events sent to your window and respond to them by using an API function to change the cursor. This will handle automatically updating the cursor as the mouse moves around and will add minimal overhead to your application (windowing system cursors generally draw in an overlay plane that avoids sending redraw events to your window every time the mouse moves).
If you have a more procedural description of your cursor (that is, you intend to draw it with OpenGL drawing commands), you'll instead want to handle the mouse enter/leave events by using HideCursor()/ShowCursor() commands or the equivalent to turn off the windowing system's native cursor when the mouse is over your window. Then, you'll hook the mouse move callbacks and redraw your scene, adding whatever commands you need to draw the cursor at the position specified in the mouse move event.
The first approach is definitely preferred for performance & latency reasons--but there are some cursor types (think full screen crosshairs) that can't be accomodated that way.

How do I determine if a window is off-screen?

In Windows XP and above, given a window handle (HWND), how can I tell if the window position and size leaves the window irretrievably off screen? For example, if the title bar is available to the cursor, then the window can be dragged back on screen. I need to discover if the window is in fact visible or at least available to the user. I guess I also need to know how to detect and respond to resolution changes and how to deal with multiple monitors. This seems like a fairly big deal. I'm using C++ and the regular SDK, so please limit your answers to that platform rather than invoking C# or similar.
Windows makes it relatively simple to determine the size of a user's working area on the primary monitor (i.e., the area of the screen not obscured by the taskbar). Call the SystemParametersInfo function and specify the SPI_GETWORKAREA flag for the first parameter (uiAction). The pvParam parameter should point to a RECT structure that will receive the coordinates of the working area in virtual screen coordinates.
Once you've got the coordinates that describe the working area, it's a simple matter of comparing those to the current position of your application's window to determine if it lies within those bounds.
The desire to support multiple monitors makes things slightly more complicated. The documentation for SystemParametersInfo suggests that you need to call the GetMonitorInfo function instead to get the working area of a monitor other than the primary. It fills in a structure called MONITORINFOEX that contains the member rcWork that defines the working area of that monitor, again expressed in virtual screen coordinates as a RECT structure.
To do this right, you'll need to enumerate all of the monitors a user has connected to the system and retrieve the working area of each using GetMonitorInfo.
There are a few samples of this to be found around the Internet:
MSDN has some sample code for Positioning Objects on a Multiple Display Setup.
If you're using MFC, here's what looks to be an excellent example of multiple monitor support.
Even if you're not using MFC, that article refers the following link which looks be a real gem as far as explaining how multiple monitor supports works in Windows, even if it's a little bit old school. Like it or not, very little of this has changed in later versions of Windows.
Finally, you mentioned wanting to detect resolution changes. This is much simpler than you probably imagined. As you know if you've done any Windows programming, the primary way that the operating system communicates with your application is by sending messages to your WindowProc function.
In this case, you'll want to watch for the WM_DISPLAYCHANGE message, which is sent to all windows when the display resolution has changed. The wParam contains the new image depth in bits per pixel; the low-order word of the lParam specifies the horizontal resolution and the high-order word of the lParam specifies the vertical resolution of the screen.
You can use MonitorFromRect or MonitorFromPoint to check if window's top left point or bottom right point isn't contained within any display monitor (off screen).
POINT p;
p.x = x;
p.y = y;
HMONITOR hMon = MonitorFromPoint(p, MONITOR_DEFAULTTONULL);
if (hMon == NULL) {
// point is off screen
}
Visibility check is really easy.
RECT rtDesktop, rtView;
GetWindowRect( GetDesktopWindow(), &rtDesktop );
GetWindowRect( m_hWnd, &rtView );
HRGN rgn = CreateRectRgn( rtDesktop.left, rtDesktop.top, rtDesktop.right, rtDesktop.bottom );
BOOL viewIsVisible = RectInRegion( rgn, &rtView );
DeleteObject(rgn);
You don't have to use RectInRegion, I used for shorten code.
Display, resolution change monitoring is also easy if you handle WM_SETTINGCHANGE message.
http://msdn.microsoft.com/en-us/library/ms725497(v=vs.85).aspx
UPDATE
As #Cody Gray noted, I think WM_DISPLAYCHANGE is more appropriate than WM_SETTINGCHANGE. But MFC 9.0 library make use of WM_SETTINGCHANGE.

How do I display dynamic text at the mouse cursor via C++/MFC in a Win32 application

I would like to be able to display some dynamic text at the mouse
cursor location in a win32 app, for instance to give an X,Y coordinate that
would move with the cursor as though attached. I can do this during a
mousemove event using a TextOut() call for the window at the mouse
coordinates and invalidate a rectange around a stored last cursor position
to clear up the previous output. However this can suffer from flickering and
cause problems with other things being drawn in a window such as tracker
boxes. Is there a better way to do this, perhaps using the existing cursor
drawing/invalidating mechanism ?
You can do this via ToolTips - check out CToolTipCtrl.
If you want flicker free tracking ToolTips then you will need to derive your own classes from CToolTipCtrl that use the trackActivate messages.
You may want to consider a small transparent window that you move to follow the mouse. In particular, since Windows 2000, Layered windows seem to be the weapon of choice (confession: no personal experience there).
You can overwrite OnSetCursor to get a dynamic mouse cursor. I just found a German tutorial.
German tutorial
English translated tutorial