@@ -0,0 +1,135 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#include "InputCommon/ControllerInterface/Win32/Win32.h"

#include <windows.h>

#include <thread>

#include "Common/Event.h"
#include "Common/Logging/Log.h"
#include "Common/ScopeGuard.h"
#include "InputCommon/ControllerInterface/DInput/DInput.h"
#include "InputCommon/ControllerInterface/XInput/XInput.h"

constexpr UINT WM_DOLPHIN_STOP = WM_USER;

static Common::Event s_done_populating;
static std::atomic<HWND> s_hwnd;
static HWND s_message_window;
static std::thread s_thread;

static LRESULT CALLBACK WindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
{
if (message == WM_INPUT_DEVICE_CHANGE)
{
ciface::DInput::PopulateDevices(s_hwnd);
ciface::XInput::PopulateDevices();
s_done_populating.Set();
}

return DefWindowProc(hwnd, message, wparam, lparam);
}

void ciface::Win32::Init(void* hwnd)
{
s_hwnd = static_cast<HWND>(hwnd);
XInput::Init();

s_thread = std::thread([] {
if (FAILED(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED)))
{
ERROR_LOG(SERIALINTERFACE, "CoInitializeEx failed: %i", GetLastError());
return;
}
Common::ScopeGuard uninit([] { CoUninitialize(); });

WNDCLASSEX window_class_info{};
window_class_info.cbSize = sizeof(window_class_info);
window_class_info.lpfnWndProc = WindowProc;
window_class_info.hInstance = GetModuleHandle(nullptr);
window_class_info.lpszClassName = L"Message";

ATOM window_class = RegisterClassEx(&window_class_info);
if (!window_class)
{
NOTICE_LOG(SERIALINTERFACE, "RegisterClassEx failed: %i", GetLastError());
return;
}
Common::ScopeGuard unregister([&window_class] {
if (!UnregisterClass(MAKEINTATOM(window_class), GetModuleHandle(nullptr)))
ERROR_LOG(SERIALINTERFACE, "UnregisterClass failed: %i", GetLastError());
});

s_message_window = CreateWindowEx(0, L"Message", nullptr, 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr,
nullptr, nullptr);
if (!s_message_window)
{
ERROR_LOG(SERIALINTERFACE, "CreateWindowEx failed: %i", GetLastError());
return;
}
Common::ScopeGuard destroy([] {
if (!DestroyWindow(s_message_window))
ERROR_LOG(SERIALINTERFACE, "DestroyWindow failed: %i", GetLastError());
s_message_window = nullptr;
});

std::array<RAWINPUTDEVICE, 2> devices;
// game pad devices
devices[0].usUsagePage = 0x01;
devices[0].usUsage = 0x05;
devices[0].dwFlags = RIDEV_DEVNOTIFY;
devices[0].hwndTarget = s_message_window;
// joystick devices
devices[1].usUsagePage = 0x01;
devices[1].usUsage = 0x04;
devices[1].dwFlags = RIDEV_DEVNOTIFY;
devices[1].hwndTarget = s_message_window;

if (!RegisterRawInputDevices(devices.data(), static_cast<UINT>(devices.size()),
static_cast<UINT>(sizeof(decltype(devices)::value_type))))
{
ERROR_LOG(SERIALINTERFACE, "RegisterRawInputDevices failed: %i", GetLastError());
return;
}

MSG msg;
while (GetMessage(&msg, nullptr, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
if (msg.message == WM_DOLPHIN_STOP)
break;
}
});
}

void ciface::Win32::PopulateDevices(void* hwnd)
{
if (s_thread.joinable())
{
s_hwnd = static_cast<HWND>(hwnd);
s_done_populating.Reset();
PostMessage(s_message_window, WM_INPUT_DEVICE_CHANGE, 0, 0);
if (!s_done_populating.WaitFor(std::chrono::seconds(10)))
ERROR_LOG(SERIALINTERFACE, "win32 timed out when trying to populate devices");
}
else
{
ERROR_LOG(SERIALINTERFACE, "win32 asked to populate devices, but device thread isn't running");
}
}

void ciface::Win32::DeInit()
{
NOTICE_LOG(SERIALINTERFACE, "win32 DeInit");
if (s_thread.joinable())
{
PostMessage(s_message_window, WM_DOLPHIN_STOP, 0, 0);
s_thread.join();
}

XInput::DeInit();
}
@@ -0,0 +1,15 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once

namespace ciface
{
namespace Win32
{
void Init(void* hwnd);
void PopulateDevices(void* hwnd);
void DeInit();
} // namespace Win32
} // namespace ciface
@@ -92,6 +92,8 @@ void PopulateDevices()
if (!hXInput)
return;

g_controller_interface.RemoveDevice([](const auto* dev) { return dev->GetSource() == "XInput"; });

XINPUT_CAPABILITIES caps;
for (int i = 0; i != 4; ++i)
if (ERROR_SUCCESS == PXInputGetCapabilities(i, 0, &caps))
@@ -192,6 +194,11 @@ void Device::UpdateMotors()
}
}

std::optional<int> Device::GetPreferredId() const
{
return m_index;
}

// GET name/source/id

std::string Device::Button::GetName() const
@@ -92,8 +92,9 @@ class Device : public Core::Device

Device(const XINPUT_CAPABILITIES& capabilities, u8 index);

std::string GetName() const override;
std::string GetSource() const override;
std::string GetName() const final override;
std::string GetSource() const final override;
std::optional<int> GetPreferredId() const final override;

void UpdateMotors();

@@ -104,5 +105,5 @@ class Device : public Core::Device
const BYTE m_subtype;
const u8 m_index;
};
}
}
} // namespace XInput
} // namespace ciface
@@ -63,6 +63,7 @@
<ClCompile Include="ControlReference\ControlReference.cpp" />
<ClCompile Include="ControlReference\ExpressionParser.cpp" />
<ClCompile Include="ControllerInterface\ForceFeedback\ForceFeedbackDevice.cpp" />
<ClCompile Include="ControllerInterface\Win32\Win32.cpp" />
<ClCompile Include="ControllerInterface\XInput\XInput.cpp" />
<ClCompile Include="GCAdapter.cpp">
<!--
@@ -104,6 +105,7 @@
<ClInclude Include="ControlReference\ControlReference.h" />
<ClInclude Include="ControlReference\ExpressionParser.h" />
<ClInclude Include="ControllerInterface\ForceFeedback\ForceFeedbackDevice.h" />
<ClInclude Include="ControllerInterface\Win32\Win32.h" />
<ClInclude Include="ControllerInterface\XInput\XInput.h" />
<ClInclude Include="GCAdapter.h" />
<ClInclude Include="GCPadStatus.h" />
@@ -4,12 +4,6 @@
<Filter Include="ControllerInterface">
<UniqueIdentifier>{3a755a86-0efa-4396-bf79-bb3a1910764d}</UniqueIdentifier>
</Filter>
<Filter Include="ControllerInterface\DInput">
<UniqueIdentifier>{0289ef91-50f5-4c16-9fa4-ff4c4d8208e7}</UniqueIdentifier>
</Filter>
<Filter Include="ControllerInterface\XInput">
<UniqueIdentifier>{07bad1aa-7e03-4f5c-ade2-a44857c5cbc3}</UniqueIdentifier>
</Filter>
<Filter Include="ControllerInterface\ForceFeedback">
<UniqueIdentifier>{e10ce316-283c-4be0-848d-578dec2b6404}</UniqueIdentifier>
</Filter>
@@ -25,6 +19,15 @@
<Filter Include="ControllerEmu\Setting">
<UniqueIdentifier>{ce661cb4-f23f-4ab2-952d-402d381735e5}</UniqueIdentifier>
</Filter>
<Filter Include="ControllerInterface\Win32">
<UniqueIdentifier>{6ca06b20-d8f6-4622-97ab-eefbc66edbd5}</UniqueIdentifier>
</Filter>
<Filter Include="ControllerInterface\DInput">
<UniqueIdentifier>{0289ef91-50f5-4c16-9fa4-ff4c4d8208e7}</UniqueIdentifier>
</Filter>
<Filter Include="ControllerInterface\XInput">
<UniqueIdentifier>{07bad1aa-7e03-4f5c-ade2-a44857c5cbc3}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="GCAdapter.cpp" />
@@ -104,6 +107,9 @@
<ClCompile Include="ControllerInterface\DInput\XInputFilter.cpp">
<Filter>ControllerInterface\DInput</Filter>
</ClCompile>
<ClCompile Include="ControllerInterface\Win32\Win32.cpp">
<Filter>ControllerInterface\Win32</Filter>
</ClCompile>
<ClCompile Include="ControlReference\ExpressionParser.cpp">
<Filter>ControllerInterface</Filter>
</ClCompile>
@@ -200,6 +206,9 @@
<ClInclude Include="ControllerInterface\DInput\XInputFilter.h">
<Filter>ControllerInterface\DInput</Filter>
</ClInclude>
<ClInclude Include="ControllerInterface\Win32\Win32.h">
<Filter>ControllerInterface\Win32</Filter>
</ClInclude>
<ClInclude Include="ControlReference\ExpressionParser.h">
<Filter>ControllerInterface</Filter>
</ClInclude>