123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
- // SPDX-License-Identifier: MIT
- #include <TestFramework.h>
- #include <Input/Mouse.h>
- #include <Renderer/Renderer.h>
- #include <Jolt/Core/Profiler.h>
- Mouse::Mouse()
- {
- Reset();
- }
- Mouse::~Mouse()
- {
- Shutdown();
- }
- void
- Mouse::Reset()
- {
- mDI = nullptr;
- mMouse = nullptr;
- mMousePos.x = 0;
- mMousePos.y = 0;
- ResetMouse();
- }
- void Mouse::ResetMouse()
- {
- memset(&mMouseState, 0, sizeof(mMouseState));
- mMousePosInitialized = false;
- memset(&mDOD, 0, sizeof(mDOD));
- mDODLength = 0;
- mTimeLeftButtonLastReleased = 0;
- mLeftButtonDoubleClicked = false;
- }
- bool Mouse::Initialize(Renderer *inRenderer)
- #ifdef JPH_COMPILER_CLANG
- // DIPROP_BUFFERSIZE is a pointer to 1 which causes UBSan: runtime error: reference binding to misaligned address 0x000000000001
- __attribute__((no_sanitize("alignment")))
- #endif
- {
- // Store renderer
- mRenderer = inRenderer;
- // Create direct input interface
- if (FAILED(CoCreateInstance(CLSID_DirectInput8, nullptr, CLSCTX_INPROC_SERVER, IID_IDirectInput8W, (void **)&mDI)))
- {
- Trace("Unable to create DirectInput interface, DirectX 8.0 is required");
- return false;
- }
-
- // Initialize direct input interface
- if (FAILED(mDI->Initialize((HINSTANCE)GetModuleHandle(nullptr), DIRECTINPUT_VERSION)))
- {
- Trace("Unable to initialize DirectInput interface, DirectX 8.0 is required");
- return false;
- }
- // Create Mouse device
- if (FAILED(mDI->CreateDevice(GUID_SysMouse, &mMouse, nullptr)))
- {
- Trace("Unable to get DirectInputDevice interface, DirectX 8.0 is required");
- return false;
- }
-
- // Set cooperative level for Mouse
- SetExclusive(false);
-
- // Set data format
- if (FAILED(mMouse->SetDataFormat(&c_dfDIMouse)))
- {
- Trace("Unable to set data format to mouse");
- return false;
- }
- // Create a mouse buffer
- DIPROPDWORD dipdw;
- dipdw.diph.dwSize = sizeof(DIPROPDWORD);
- dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
- dipdw.diph.dwObj = 0;
- dipdw.diph.dwHow = DIPH_DEVICE;
- dipdw.dwData = BUFFERSIZE;
- if (FAILED(mMouse->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph)))
- {
- Trace("Unable to set mouse buffer size");
- return false;
- }
- return true;
- }
- void Mouse::Shutdown()
- {
- if (mMouse)
- {
- mMouse->Unacquire();
- mMouse = nullptr;
- }
- mDI = nullptr;
- Reset();
- }
- void Mouse::Poll()
- {
- JPH_PROFILE_FUNCTION();
- // Remember last position
- POINT old_mouse_pos = mMousePos;
- // Get mouse position using the standard window call
- if (!GetCursorPos(&mMousePos))
- {
- ResetMouse();
- return;
- }
- // If we lost mouse before, we need to reset the old mouse pos to the current one
- if (!mMousePosInitialized)
- {
- old_mouse_pos = mMousePos;
- mMousePosInitialized = true;
- }
- // Convert to window space
- if (!ScreenToClient(mRenderer->GetWindowHandle(), &mMousePos))
- {
- ResetMouse();
- return;
- }
-
- // Get relative movement
- if (FAILED(mMouse->GetDeviceState(sizeof(mMouseState), &mMouseState)))
- {
- // Mouse input was lost, reacquire
- mMouse->Acquire();
-
- if (FAILED(mMouse->GetDeviceState(sizeof(mMouseState), &mMouseState)))
- {
- ResetMouse();
- return;
- }
- }
- // If we're connected through remote desktop then GetDeviceState returns faulty data for lX and lY so we need to use a fallback
- if (GetSystemMetrics(SM_REMOTESESSION))
- {
- // Just use the delta between the current and last mouse position.
- // Note that this has the disadvantage that you can no longer rotate any further if you're at the edge of the screen,
- // but unfortunately a RDP session doesn't allow capturing the mouse so there doesn't seem to be a workaround for this.
- mMouseState.lX = mMousePos.x - old_mouse_pos.x;
- mMouseState.lY = mMousePos.y - old_mouse_pos.y;
- }
-
- // Get the state in a buffer for checking doubleclicks
- if (FAILED(mMouse->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), mDOD, &mDODLength, 0)))
- {
- // We lost mMouse input, reacquire
- mMouse->Acquire();
-
- if (FAILED(mMouse->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), mDOD, &mDODLength, 0)))
- {
- // Unable to reacquire, reset button info
- mTimeLeftButtonLastReleased = 0;
- mLeftButtonDoubleClicked = false;
- return;
- }
- }
-
- // Check for double clicks
- for (DWORD d = 0; d < mDODLength; d++)
- {
- // Check if this means left button is pressed
- if (mDOD[d].dwOfs == DIMOFS_BUTTON0)
- {
- if (mDOD[d].dwData & 0x80)
- {
- if (mDOD[d].dwTimeStamp - mTimeLeftButtonLastReleased <= DCLICKTIME)
- {
- // This is a double click
- mTimeLeftButtonLastReleased = 0;
- mLeftButtonDoubleClicked = true;
- }
- }
- else // Remember last time button was released
- mTimeLeftButtonLastReleased = mDOD[d].dwTimeStamp;
- }
- }
- }
- void Mouse::HideCursor()
- {
- ::ShowCursor(false);
- }
- void Mouse::ShowCursor()
- {
- ::ShowCursor(true);
- }
- void Mouse::SetExclusive(bool inExclusive)
- {
- // Set cooperative level for Mouse
- if (FAILED(mMouse->SetCooperativeLevel(mRenderer->GetWindowHandle(), (inExclusive? DISCL_EXCLUSIVE : DISCL_NONEXCLUSIVE) | DISCL_FOREGROUND)))
- Trace("Failed to set cooperative level for mouse");
- }
|