ControllerInterface: Implement ChangeWindow on DInput without recreating the devices

Also polished DInput code in general to try and mitigate issue 11702.
Added a lot of logging and comments.
This commit is contained in:
Filoppi
2021-05-15 12:20:20 +03:00
parent dcc345400e
commit a0ecca1a84
8 changed files with 140 additions and 37 deletions

View File

@ -16,6 +16,8 @@
namespace ciface::DInput
{
static IDirectInput8* s_idi8 = nullptr;
BOOL CALLBACK DIEnumDeviceObjectsCallback(LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pvRef)
{
((std::list<DIDEVICEOBJECTINSTANCE>*)pvRef)->push_back(*lpddoi);
@ -42,29 +44,57 @@ std::string GetDeviceName(const LPDIRECTINPUTDEVICE8 device)
}
else
{
ERROR_LOG_FMT(PAD, "GetProperty(DIPROP_PRODUCTNAME) failed.");
ERROR_LOG_FMT(CONTROLLERINTERFACE, "GetProperty(DIPROP_PRODUCTNAME) failed.");
}
return result;
}
// Assumes hwnd had not changed from the previous call
void PopulateDevices(HWND hwnd)
{
// Remove unplugged devices.
g_controller_interface.RemoveDevice(
[](const auto* dev) { return dev->GetSource() == DINPUT_SOURCE_NAME && !dev->IsValid(); });
IDirectInput8* idi8;
if (FAILED(DirectInput8Create(GetModuleHandle(nullptr), DIRECTINPUT_VERSION, IID_IDirectInput8,
(LPVOID*)&idi8, nullptr)))
if (!s_idi8 && FAILED(DirectInput8Create(GetModuleHandle(nullptr), DIRECTINPUT_VERSION,
IID_IDirectInput8, (LPVOID*)&s_idi8, nullptr)))
{
ERROR_LOG_FMT(PAD, "DirectInput8Create failed.");
ERROR_LOG_FMT(CONTROLLERINTERFACE, "DirectInput8Create failed.");
return;
}
InitKeyboardMouse(idi8, hwnd);
InitJoystick(idi8, hwnd);
// Remove old (invalid) devices. No need to ever remove the KeyboardMouse device.
// Note that if we have 2+ DInput controllers, not fully repopulating devices
// will mean that a device with index "2" could persist while there is no device with index "0".
// This is slightly inconsistent as when we refresh all devices, they will instead reset, and
// that happens a lot (for uncontrolled reasons, like starting/stopping the emulation).
g_controller_interface.RemoveDevice(
[](const auto* dev) { return dev->GetSource() == DINPUT_SOURCE_NAME && !dev->IsValid(); });
idi8->Release();
InitKeyboardMouse(s_idi8, hwnd);
InitJoystick(s_idi8, hwnd);
}
void ChangeWindow(HWND hwnd)
{
if (s_idi8) // Has init? Ignore if called before the first PopulateDevices()
{
// The KeyboardMouse device is marked as virtual device, so we avoid removing it.
// We need to force all the DInput joysticks to be destroyed now, or recreation would fail.
g_controller_interface.RemoveDevice(
[](const auto* dev) {
return dev->GetSource() == DINPUT_SOURCE_NAME && !dev->IsVirtualDevice();
},
true);
SetKeyboardMouseWindow(hwnd);
InitJoystick(s_idi8, hwnd);
}
}
void DeInit()
{
if (s_idi8)
{
s_idi8->Release();
s_idi8 = nullptr;
}
}
} // namespace ciface::DInput

View File

@ -20,4 +20,6 @@ BOOL CALLBACK DIEnumDevicesCallback(LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef);
std::string GetDeviceName(const LPDIRECTINPUTDEVICE8 device);
void PopulateDevices(HWND hwnd);
void ChangeWindow(HWND hwnd);
void DeInit();
} // namespace ciface::DInput

View File

@ -6,7 +6,8 @@
#include <algorithm>
#include <fmt/format.h>
#include "Common/Logging/Log.h"
#include "Core/Core.h"
#include "InputCommon/ControllerInterface/ControllerInterface.h"
#include "InputCommon/ControllerInterface/DInput/DInput.h"
@ -54,15 +55,18 @@ static const struct
#include "InputCommon/ControllerInterface/DInput/NamedKeys.h" // NOLINT
};
// Prevent duplicate keyboard/mouse devices.
static bool s_keyboard_mouse_exists = false;
// Prevent duplicate keyboard/mouse devices. Modified by more threads.
static bool s_keyboard_mouse_exists;
static HWND s_hwnd;
void InitKeyboardMouse(IDirectInput8* const idi8, HWND hwnd)
{
if (s_keyboard_mouse_exists)
return;
// mouse and keyboard are a combined device, to allow shift+click and stuff
s_hwnd = hwnd;
// Mouse and keyboard are a combined device, to allow shift+click and stuff
// if that's dumb, I will make a VirtualDevice class that just uses ranges of inputs/outputs from
// other devices
// so there can be a separated Keyboard and mouse, as well as combined KeyboardMouse
@ -70,6 +74,8 @@ void InitKeyboardMouse(IDirectInput8* const idi8, HWND hwnd)
LPDIRECTINPUTDEVICE8 kb_device = nullptr;
LPDIRECTINPUTDEVICE8 mo_device = nullptr;
// These are "virtual" system devices, so they are always there even if we have no physical
// mouse and keyboard plugged into the computer
if (SUCCEEDED(idi8->CreateDevice(GUID_SysKeyboard, &kb_device, nullptr)) &&
SUCCEEDED(kb_device->SetDataFormat(&c_dfDIKeyboard)) &&
SUCCEEDED(kb_device->SetCooperativeLevel(nullptr, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE)) &&
@ -77,16 +83,23 @@ void InitKeyboardMouse(IDirectInput8* const idi8, HWND hwnd)
SUCCEEDED(mo_device->SetDataFormat(&c_dfDIMouse2)) &&
SUCCEEDED(mo_device->SetCooperativeLevel(nullptr, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE)))
{
g_controller_interface.AddDevice(std::make_shared<KeyboardMouse>(kb_device, mo_device, hwnd));
g_controller_interface.AddDevice(std::make_shared<KeyboardMouse>(kb_device, mo_device));
return;
}
ERROR_LOG_FMT(CONTROLLERINTERFACE, "KeyboardMouse device failed to be created");
if (kb_device)
kb_device->Release();
if (mo_device)
mo_device->Release();
}
void SetKeyboardMouseWindow(HWND hwnd)
{
s_hwnd = hwnd;
}
KeyboardMouse::~KeyboardMouse()
{
s_keyboard_mouse_exists = false;
@ -105,9 +118,8 @@ KeyboardMouse::~KeyboardMouse()
}
KeyboardMouse::KeyboardMouse(const LPDIRECTINPUTDEVICE8 kb_device,
const LPDIRECTINPUTDEVICE8 mo_device, HWND hwnd)
: m_kb_device(kb_device), m_mo_device(mo_device), m_hwnd(hwnd), m_last_update(GetTickCount()),
m_state_in()
const LPDIRECTINPUTDEVICE8 mo_device)
: m_kb_device(kb_device), m_mo_device(mo_device), m_last_update(GetTickCount()), m_state_in()
{
s_keyboard_mouse_exists = true;
@ -160,11 +172,11 @@ void KeyboardMouse::UpdateCursorInput()
// Get the cursor position relative to the upper left corner of the current window
// (separate or render to main)
ScreenToClient(m_hwnd, &point);
ScreenToClient(s_hwnd, &point);
// Get the size of the current window. (In my case Rect.top and Rect.left was zero.)
// Get the size of the current window (in my case Rect.top and Rect.left was zero).
RECT rect;
GetClientRect(m_hwnd, &rect);
GetClientRect(s_hwnd, &rect);
// Width and height are the size of the rendering window. They could be 0
const auto win_width = std::max(rect.right - rect.left, 1l);
@ -236,6 +248,11 @@ int KeyboardMouse::GetSortPriority() const
return 5;
}
bool KeyboardMouse::IsVirtualDevice() const
{
return true;
}
// names
std::string KeyboardMouse::Key::GetName() const
{

View File

@ -16,6 +16,7 @@ namespace ciface::DInput
void InitKeyboardMouse(IDirectInput8* const idi8, HWND hwnd);
using RelativeMouseState = RelativeInputState<Common::TVec3<LONG>>;
void SetKeyboardMouseWindow(HWND hwnd);
class KeyboardMouse : public Core::Device
{
@ -96,13 +97,13 @@ private:
public:
void UpdateInput() override;
KeyboardMouse(const LPDIRECTINPUTDEVICE8 kb_device, const LPDIRECTINPUTDEVICE8 mo_device,
HWND hwnd);
KeyboardMouse(const LPDIRECTINPUTDEVICE8 kb_device, const LPDIRECTINPUTDEVICE8 mo_device);
~KeyboardMouse();
std::string GetName() const override;
std::string GetSource() const override;
int GetSortPriority() const override;
bool IsVirtualDevice() const override;
private:
void UpdateCursorInput();
@ -110,8 +111,6 @@ private:
const LPDIRECTINPUTDEVICE8 m_kb_device;
const LPDIRECTINPUTDEVICE8 m_mo_device;
const HWND m_hwnd;
DWORD m_last_update;
State m_state_in;
};