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

Problem in changed mouse relative/absolute mode

KittoChanKC opened this issue · comments

I have some problem in changed mouse relative/absolute mode

In absolute Mode:
I used SetMode(MODE_RELATIVE);

when the mouse not moved, it will still is absolute Mode and still showing mouse cursor.

and i just move the mouse a little bit, it correctly changed to relative and hide the mouse,
how to solve this problem without moving the mouse?

Make sure you are passing all relevant Win32 messages to Mouse::ProcessMesage including WM_MOUSEHOVER in your WndProc.

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_ACTIVATE:
    case WM_ACTIVATEAPP:
    case WM_INPUT:
    case WM_MOUSEMOVE:
    case WM_LBUTTONDOWN:
    case WM_LBUTTONUP:
    case WM_RBUTTONDOWN:
    case WM_RBUTTONUP:
    case WM_MBUTTONDOWN:
    case WM_MBUTTONUP:
    case WM_MOUSEWHEEL:
    case WM_XBUTTONDOWN:
    case WM_XBUTTONUP:
    case WM_MOUSEHOVER:
        Mouse::ProcessMessage(message, wParam, lParam);
        break;
    }

    return DefWindowProc(hWnd, message, wParam, lParam);
}

yes, I just copied this from document and make sure the mouse is hovering in application.

Did you also call Mouse::SetWindow?

I came here with the same problem and think I've found the why, just not entirely sure what the best solution is.

In Mouse::SetMode() you set the event of the new mode (mAbsoluteMode or mRelativeMode), then send a message to trigger Mouse::ProcessMessage():

DirectXTK/Src/Mouse.cpp

Lines 1056 to 1066 in b116b58

SetEvent((mode == MODE_ABSOLUTE) ? mAbsoluteMode.get() : mRelativeMode.get());
assert(mWindow != nullptr);
// Send a WM_HOVER as a way to 'kick' the message processing even if the mouse is still.
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(tme);
tme.dwFlags = TME_HOVER;
tme.hwndTrack = mWindow;
tme.dwHoverTime = 1;
if (!TrackMouseEvent(&tme))

Then in Mouse::ProcessMessage():

DirectXTK/Src/Mouse.cpp

Lines 1196 to 1197 in b116b58

HANDLE events[3] = { pImpl->mScrollWheelValue.get(), pImpl->mAbsoluteMode.get(), pImpl->mRelativeMode.get() };
switch (WaitForMultipleObjectsEx(static_cast<DWORD>(std::size(events)), events, FALSE, 0, FALSE))

For me at least, the problem was occuring because Mouse::ResetScrollWheelValue() had also been called at some point, meaning the mScrollWheelValue event was also set, which meant that WaitForMultipleObjectsEx returned WAIT_OBJECT_0 and handled that, and the message triggered by Mouse::SetMode got swallowed. The actual mode change wouldn't occur until the next message handled, which was the WM_MOUSEMOVE.

In the code I was working with, Mouse::ResetScrollWheelValue() was being called every frame (as it seems to be in the example at https://github.com/walbourn/directxtk-tutorials/blob/c596a4785f96fd8335a06ba08d7bc2b3a04036b5/DX11/KeyboardMouseTest/Game.cpp#L90?) Which would mean the mScrollWheelValue is going to be set every frame.

In this instance, I "fixed" it by only calling Mouse::ResetScrollWheelValue() when the wheel value was non-zero, but it seems like there should be something more... generic? For example, would it be bad to change the order of events sent to WaitForMultipleObjectsEx and put mScrollWheelValue last? That way the mode change events would take priority.

Thanks for the detailed investigation. I should probably have the event handler run multiple times if something is found... Or maybe just check the wheel separately.