190 lines
4.4 KiB
C++
190 lines
4.4 KiB
C++
|
|
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
||
|
|
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
|
||
|
|
// SPDX-License-Identifier: MIT
|
||
|
|
|
||
|
|
#include <TestFramework.h>
|
||
|
|
|
||
|
|
#include <Input/Win/MouseWin.h>
|
||
|
|
#include <Window/ApplicationWindowWin.h>
|
||
|
|
#include <Jolt/Core/Profiler.h>
|
||
|
|
|
||
|
|
MouseWin::MouseWin()
|
||
|
|
{
|
||
|
|
Reset();
|
||
|
|
}
|
||
|
|
|
||
|
|
MouseWin::~MouseWin()
|
||
|
|
{
|
||
|
|
Shutdown();
|
||
|
|
}
|
||
|
|
|
||
|
|
void MouseWin::Reset()
|
||
|
|
{
|
||
|
|
mDI = nullptr;
|
||
|
|
mMouse = nullptr;
|
||
|
|
mMousePos.x = 0;
|
||
|
|
mMousePos.y = 0;
|
||
|
|
|
||
|
|
ResetMouse();
|
||
|
|
}
|
||
|
|
|
||
|
|
void MouseWin::ResetMouse()
|
||
|
|
{
|
||
|
|
memset(&mMouseState, 0, sizeof(mMouseState));
|
||
|
|
mMousePosInitialized = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
void MouseWin::DetectParsecRunning()
|
||
|
|
{
|
||
|
|
mIsParsecRunning = false;
|
||
|
|
|
||
|
|
if (SC_HANDLE manager = OpenSCManager(nullptr, nullptr, SC_MANAGER_CONNECT))
|
||
|
|
{
|
||
|
|
if (SC_HANDLE service = OpenServiceA(manager, "Parsec", SERVICE_QUERY_STATUS))
|
||
|
|
{
|
||
|
|
SERVICE_STATUS status;
|
||
|
|
if (QueryServiceStatus(service, &status))
|
||
|
|
{
|
||
|
|
mIsParsecRunning = status.dwCurrentState == SERVICE_RUNNING;
|
||
|
|
}
|
||
|
|
CloseServiceHandle(service);
|
||
|
|
}
|
||
|
|
CloseServiceHandle(manager);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
bool MouseWin::Initialize(ApplicationWindow *inWindow)
|
||
|
|
#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 window
|
||
|
|
mWindow = static_cast<ApplicationWindowWin *>(inWindow);
|
||
|
|
|
||
|
|
// 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
|
||
|
|
if (FAILED(mMouse->SetCooperativeLevel(mWindow->GetWindowHandle(), DISCL_NONEXCLUSIVE | DISCL_FOREGROUND)))
|
||
|
|
Trace("Failed to set cooperative level for mouse");
|
||
|
|
|
||
|
|
// 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;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check if the parsec service is running
|
||
|
|
DetectParsecRunning();
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
void MouseWin::Shutdown()
|
||
|
|
{
|
||
|
|
if (mMouse)
|
||
|
|
{
|
||
|
|
mMouse->Unacquire();
|
||
|
|
mMouse = nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
mDI = nullptr;
|
||
|
|
|
||
|
|
Reset();
|
||
|
|
}
|
||
|
|
|
||
|
|
void MouseWin::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(mWindow->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 or Parsec then GetDeviceState returns faulty data for lX and lY so we need to use a fallback
|
||
|
|
if (GetSystemMetrics(SM_REMOTESESSION) || mIsParsecRunning)
|
||
|
|
{
|
||
|
|
// 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;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void MouseWin::HideCursor()
|
||
|
|
{
|
||
|
|
::ShowCursor(false);
|
||
|
|
}
|
||
|
|
|
||
|
|
void MouseWin::ShowCursor()
|
||
|
|
{
|
||
|
|
::ShowCursor(true);
|
||
|
|
}
|