microsoft / DirectXTK

The DirectX Tool Kit (aka DirectXTK) is a collection of helper classes for writing DirectX 11.x code in C++

Home Page:https://walbourn.github.io/directxtk/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

After Mouse::SetMode(Mouse::MODE_RELATIVE) mouse position is always zero

vocasle opened this issue · comments

Hi there.
After Mouse::SetMode(Mouse::MODE_RELATIVE) method call when I call Mouse::Get().GetState().x or Mouse::Get().GetState().y the values of x and y do not get updated. If I call Mouse::SetMode(Mouse::MODE_ABSOLUTE) the values of x and y update immediately.

I am using 2021.11.8.1 version of DirectXTK (directxtk_desktop_win10) downloaded from NuGet.

You can find a working demo in this repo.

I think the problem is calling SetMode in the initialization. You generally need to support a mechanism for getting into and escaping from relative mode since it makes use of raw input. Because I use an event to signal changes here, it's probably not working in this "I always want relative only" model.

For example, you could Mouse::SetMode(Mouse::MODE_RELATIVE) in the OnActivated method, and you could add MODE_ABSOLUTE to OnDeactivated. It might require some work in Suspended/Resume as well.

If you remember how DOOM handled this, you'd be in RELATIVE mode during the game, but whenever you were in the menu you'd be in ABSOLUTE mode. Since RELATIVE mode locks the input to the game window, not having a way out of it makes it really hard to adjust the window, use multi-monitor, etc. unless you ALT+TAB away.

I'll have to try out this scenario. Most of my tests switch to RELATIVE while a mouse button is held down.

I tried this out for Win32 desktop and it more or less works, but a few tweaks made it better when click activating rather than ALT+TAB.

050b1aa

By having Mouse::ProcessMessage now also take WM_ACTIVATE it works every time doing the ClipCursor reset. Otherwise, you sometimes don't get the cursor constrained if you have it in relative mode.

In your code, try changing the initialization order a bit:

    m_gamePad = std::make_unique<GamePad>();
    m_keyboard = std::make_unique<Keyboard>();
    m_mouse = std::make_unique<Mouse>();

    m_deviceResources->SetWindow(window, width, height);
    m_mouse->SetWindow(window);
    m_mouse->SetMode(Mouse::MODE_RELATIVE);

    // Initialize camera
    m_camera = std::make_unique<Camera>();

Also, minor bug but you need to also add to WM_SYSKEYDOWN:

case WM_SYSKEYDOWN:
    Keyboard::ProcessMessage(message, wParam, lParam);
    if (wParam == VK_RETURN && (lParam & 0x60000000) == 0x20000000)
    {
...

Just a quick question: Are you using remote desktop to test this or a local machine?

Thanks a lot for the explanation, Chuck.
I am testing on a local machine.

I have tried to rearrange code as you suggested in Initialize, I am passing now a message to Keyboard when WM_SYSKEYDOWN occurs and I have added change of mode to OnActivated and OnDiactivated, I have toggled Alt+Tab few times but each time OnActivated gets called after mode is set to MODE_RELATIVE the x and y coordinates have values set to zero.

I am going to build DirectXKT from source and recheck. Also I will try with left mouse click.

I do not know if this is important or not: I am using a touchpad. I have made a small test with Bluetooth-enabled mouse and same thing occurs with mouse - x and y do not get updated after mode is set to MODE_RELATIVE.

Hmm... It may be an issue with Raw Input (WM_INPUT ) not being supported for that device.

How are you determining the x/y are zero? Under the debugger single-step is not going to give you anything but 0 for relative movement.

I was printing the values of Mouse::State::x and Mouse::State::y each second in Camera::Update. I have just printed them each frame in Game::Update and voila, the values of x and y actually change.
I will recheck how I calculate Euler angles in Camera::Update.

Finally I have found what is happening.
In Camera::Update coordinates in MODE_RELATIVE are always zero. I obtain Mouse::State via Mouse::Get().GetState().
But in Game::Update coordinates in MODE_RELATIVE do change each tick.

I have tried to pass Mouse::State from Game::Update to Camera::Update and now my camera movement do work.

I think that it might be a bug, because Mouse is a singleton and in my understanding Mouse::Get().GetState() should return the same object the call to unique_ptr_to_mouse->GetState() returns.

Ah, ok. That makes sense.

Yes, the implementation assumes for relative movement you'd call GetState once per frame, and them pass Mouse::State to other methods.

I've updated the wiki and tutorial to make this explicit:

https://github.com/microsoft/DirectXTK/wiki/Mouse
https://github.com/microsoft/DirectXTK/wiki/Mouse-and-keyboard-input

In any case, my next release of DirectX Tool Kit will have better support for 'always relative' scenarios. Thanks for the feedback!