From c0212fd185406321b5c7dbe824ee0b305768e07f Mon Sep 17 00:00:00 2001 From: mogemimi Date: Thu, 19 Feb 2015 05:19:36 +0900 Subject: [PATCH] Add gamepad support for Windows --- build/pomdog.gyp | 7 +- build/pomdog.xcodeproj/project.pbxproj | 10 - build/pomdog/CMakeLists.txt | 7 +- .../DeviceContextDirectInput.cpp | 45 -- .../DeviceContextDirectInput.hpp | 33 - .../GamepadDirectInput.cpp | 675 ++++++++++++++++++ .../GamepadDirectInput.hpp | 76 ++ src/InputSystem/GamepadMappings.cpp | 10 + src/InputSystem/InputDeviceCreator.hpp | 24 - src/InputSystem/InputDeviceFactory.cpp | 16 - src/InputSystem/InputDeviceFactory.hpp | 27 - src/Platform.Win32/Bootstrap.cpp | 21 +- src/Platform.Win32/GameHostWin32.cpp | 35 +- src/Platform.Win32/GameHostWin32.hpp | 4 +- 14 files changed, 797 insertions(+), 193 deletions(-) delete mode 100644 src/InputSystem.DirectInput/DeviceContextDirectInput.cpp delete mode 100644 src/InputSystem.DirectInput/DeviceContextDirectInput.hpp create mode 100644 src/InputSystem.DirectInput/GamepadDirectInput.cpp create mode 100644 src/InputSystem.DirectInput/GamepadDirectInput.hpp delete mode 100644 src/InputSystem/InputDeviceCreator.hpp delete mode 100644 src/InputSystem/InputDeviceFactory.cpp delete mode 100644 src/InputSystem/InputDeviceFactory.hpp diff --git a/build/pomdog.gyp b/build/pomdog.gyp index 6c7ecfbdb..0548e768f 100644 --- a/build/pomdog.gyp +++ b/build/pomdog.gyp @@ -275,9 +275,6 @@ '../src/InputSystem/GamepadHelper.hpp', '../src/InputSystem/GamepadMappings.cpp', '../src/InputSystem/GamepadMappings.hpp', - '../src/InputSystem/InputDeviceCreator.hpp', - '../src/InputSystem/InputDeviceFactory.cpp', - '../src/InputSystem/InputDeviceFactory.hpp', '../src/InputSystem/NativeGamepad.hpp', '../src/Logging/Log.cpp', '../src/Logging/LogChannel.cpp', @@ -519,8 +516,8 @@ '../src/SoundSystem.XAudio2/SoundEffectXAudio2.hpp', ], 'pomdog_library_directinput_sources': [ - '../src/InputSystem.DirectInput/DeviceContextDirectInput.cpp', - '../src/InputSystem.DirectInput/DeviceContextDirectInput.hpp', + '../src/InputSystem.DirectInput/GamepadDirectInput.cpp', + '../src/InputSystem.DirectInput/GamepadDirectInput.hpp', '../src/InputSystem.DirectInput/PrerequisitesDirectInput.hpp', ], 'pomdog_library_win32_sources': [ diff --git a/build/pomdog.xcodeproj/project.pbxproj b/build/pomdog.xcodeproj/project.pbxproj index a3691a2f3..a71875f08 100644 --- a/build/pomdog.xcodeproj/project.pbxproj +++ b/build/pomdog.xcodeproj/project.pbxproj @@ -94,7 +94,6 @@ 6AF7B562E9CA059894EB5359 /* MSWaveAudioLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6CF76618636C36F96145C2A9 /* MSWaveAudioLoader.cpp */; }; 6CAD968D14E4A83B87CE38BD /* GraphicsDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0732B0AF8F54DAE712BFE595 /* GraphicsDevice.cpp */; }; 6DED53A00BC530C5F59A2547 /* AudioEngineAL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2E6B1323537C7C990BE1436C /* AudioEngineAL.cpp */; }; - 6E7FC2384465DDDB5C5B0A25 /* InputDeviceFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B7379F84C54E861AD074AA1C /* InputDeviceFactory.cpp */; }; 6FA8A4ED33396758E9052EAD /* GraphicsCommandQueueImmediate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CA6CF7478713A3FBFCCEA57D /* GraphicsCommandQueueImmediate.cpp */; }; 70214CEF1A1CF63D334FD75E /* ErrorChecker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 74C791C392D2DF46A553FC0F /* ErrorChecker.cpp */; }; 70602D1132700C62F76B98C7 /* DepthStencilStateGL4.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 09B86E4752B8088B0C3A217A /* DepthStencilStateGL4.cpp */; }; @@ -243,7 +242,6 @@ D36D164E7998A51B8BC0C0DB /* Viewport.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A651CE22C9DE13920AB99E12 /* Viewport.cpp */; }; D5CAE3F16D482B36E58155CA /* LogChannel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4406B12793DCB512528775FC /* LogChannel.cpp */; }; D6B00FCEBABBF1A29ADD25A1 /* GraphicsDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0732B0AF8F54DAE712BFE595 /* GraphicsDevice.cpp */; }; - D7EAEFE80CEE8FFAA2DB9282 /* InputDeviceFactory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B7379F84C54E861AD074AA1C /* InputDeviceFactory.cpp */; }; D8E9871132965BADBA4BE734 /* FloatingPointMatrix3x3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 029A2B232511421D3FDB9FAC /* FloatingPointMatrix3x3.cpp */; }; DA4579E5970831E615EA005D /* Ray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2EB2111ED9B45715040451A9 /* Ray.cpp */; }; DC83323F55585B855108ED51 /* AudioClipLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9524A580BCEF4C5E9F56EB43 /* AudioClipLoader.cpp */; }; @@ -369,7 +367,6 @@ 1892782F88759D12052FFC2B /* InputElement.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; path = InputElement.hpp; sourceTree = ""; }; 1AEA83A100F195FE0816B39C /* PlayerIndex.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; path = PlayerIndex.hpp; sourceTree = ""; }; 1BED371B011B6BD6EDF26BB8 /* Texture2DLoader.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; path = Texture2DLoader.hpp; sourceTree = ""; }; - 1C7CBAD3B25BA6B0D155A5E6 /* InputDeviceCreator.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; path = InputDeviceCreator.hpp; sourceTree = ""; }; 1D9DD11937173B699F763FAA /* AudioClip.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; path = AudioClip.hpp; sourceTree = ""; }; 1EC5597A41696495C978EDC9 /* MakeFourCC.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; path = MakeFourCC.hpp; sourceTree = ""; }; 1F2D3469F0692959C9DCC878 /* FloatingPointMatrix3x2.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; path = FloatingPointMatrix3x2.hpp; sourceTree = ""; }; @@ -436,7 +433,6 @@ 54A7AF2C7D84D4B4AA3A6B53 /* FloatingPointMatrix3x3.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; path = FloatingPointMatrix3x3.hpp; sourceTree = ""; }; 55900864C39A4FB67890BF0E /* Signal.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; path = Signal.hpp; sourceTree = ""; }; 55D0921E3DA34A09344C0E0A /* AudioEngine.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; path = AudioEngine.hpp; sourceTree = ""; }; - 5672D00947C663520B460134 /* InputDeviceFactory.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; path = InputDeviceFactory.hpp; sourceTree = ""; }; 5698DDE55D53D7EAA02C2490 /* AudioChannels.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; path = AudioChannels.hpp; sourceTree = ""; }; 582304B2F09B38708AF61AFC /* ErrorChecker.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; path = ErrorChecker.hpp; sourceTree = ""; }; 58C01CA2BC12335C6CC85121 /* Texture2DGL4.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; path = Texture2DGL4.hpp; sourceTree = ""; }; @@ -657,7 +653,6 @@ B2AEB843C0EAE82FED23106B /* ConnectionList.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ConnectionList.cpp; sourceTree = ""; }; B5B53A56551DA75B254A8BF5 /* Version.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; path = Version.hpp; sourceTree = ""; }; B5C78F551ABA7E2DE11CF40F /* GraphicsDevice.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; path = GraphicsDevice.hpp; sourceTree = ""; }; - B7379F84C54E861AD074AA1C /* InputDeviceFactory.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = InputDeviceFactory.cpp; sourceTree = ""; }; B8D261783FA846F7C3A474EC /* GamepadState.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; path = GamepadState.hpp; sourceTree = ""; }; B908835B58A865ED1EC9B55F /* InputElementFormat.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; path = InputElementFormat.hpp; sourceTree = ""; }; B9B61FF78819BA1E5B2CBFE8 /* BlendDescription.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; path = BlendDescription.hpp; sourceTree = ""; }; @@ -949,9 +944,6 @@ A9E4B869210CD9C0003F87DC /* GamepadHelper.hpp */, A9E4B866210CD9C0003F87DC /* GamepadMappings.cpp */, A9E4B867210CD9C0003F87DC /* GamepadMappings.hpp */, - 1C7CBAD3B25BA6B0D155A5E6 /* InputDeviceCreator.hpp */, - B7379F84C54E861AD074AA1C /* InputDeviceFactory.cpp */, - 5672D00947C663520B460134 /* InputDeviceFactory.hpp */, A9E4B86E210CDCC7003F87DC /* NativeGamepad.hpp */, ); path = InputSystem; @@ -1898,7 +1890,6 @@ A992AF021C2B1168003170C9 /* Coordinate2D.cpp in Sources */, 96250C0AF1475E290503FBF7 /* HLSLCompiler.cpp in Sources */, 6AC6140DEE87DAE2DC52FFE7 /* KeyboardState.cpp in Sources */, - 6E7FC2384465DDDB5C5B0A25 /* InputDeviceFactory.cpp in Sources */, 1650793D0884E1424679277F /* Log.cpp in Sources */, D5CAE3F16D482B36E58155CA /* LogChannel.cpp in Sources */, 5FC19F99728314D0AB72DDB8 /* BoundingBox.cpp in Sources */, @@ -2027,7 +2018,6 @@ A992AF031C2B1168003170C9 /* Coordinate2D.cpp in Sources */, 10C3C865D4638E1512AD0274 /* HLSLCompiler.cpp in Sources */, 2C6A8330B306F09980C8EFE0 /* KeyboardState.cpp in Sources */, - D7EAEFE80CEE8FFAA2DB9282 /* InputDeviceFactory.cpp in Sources */, 1F0E6CB61ABDCD22F870F258 /* Log.cpp in Sources */, 2253F43FA13A0B293FE79749 /* LogChannel.cpp in Sources */, 15C4EF0C411554073735CCF7 /* BoundingBox.cpp in Sources */, diff --git a/build/pomdog/CMakeLists.txt b/build/pomdog/CMakeLists.txt index 783b3f7bb..2a1c230fc 100644 --- a/build/pomdog/CMakeLists.txt +++ b/build/pomdog/CMakeLists.txt @@ -286,9 +286,6 @@ set(POMDOG_SOURCES_CORE ${POMDOG_DIR}/src/InputSystem/GamepadHelper.hpp ${POMDOG_DIR}/src/InputSystem/GamepadMappings.cpp ${POMDOG_DIR}/src/InputSystem/GamepadMappings.hpp - ${POMDOG_DIR}/src/InputSystem/InputDeviceCreator.hpp - ${POMDOG_DIR}/src/InputSystem/InputDeviceFactory.cpp - ${POMDOG_DIR}/src/InputSystem/InputDeviceFactory.hpp ${POMDOG_DIR}/src/InputSystem/NativeGamepad.hpp ${POMDOG_DIR}/src/Logging/Log.cpp ${POMDOG_DIR}/src/Logging/LogChannel.cpp @@ -538,8 +535,8 @@ set(POMDOG_SOURCES_XAUDIO2 ) set(POMDOG_SOURCES_DIRECTINPUT - ${POMDOG_DIR}/src/InputSystem.DirectInput/DeviceContextDirectInput.cpp - ${POMDOG_DIR}/src/InputSystem.DirectInput/DeviceContextDirectInput.hpp + ${POMDOG_DIR}/src/InputSystem.DirectInput/GamepadDirectInput.cpp + ${POMDOG_DIR}/src/InputSystem.DirectInput/GamepadDirectInput.hpp ${POMDOG_DIR}/src/InputSystem.DirectInput/PrerequisitesDirectInput.hpp ) diff --git a/src/InputSystem.DirectInput/DeviceContextDirectInput.cpp b/src/InputSystem.DirectInput/DeviceContextDirectInput.cpp deleted file mode 100644 index cbbda454a..000000000 --- a/src/InputSystem.DirectInput/DeviceContextDirectInput.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2013-2018 mogemimi. Distributed under the MIT license. - -#include "DeviceContextDirectInput.hpp" -#include "../Platform.Win32/GameWindowWin32.hpp" -#include "Pomdog/Utility/Assert.hpp" -#include "Pomdog/Utility/Exception.hpp" - -namespace Pomdog { -namespace Detail { -namespace InputSystem { -namespace DirectInput { - -DeviceContextDirectInput::DeviceContextDirectInput(HINSTANCE hInstance, HWND windowHandleIn) - : windowHandle(windowHandleIn) -{ - POMDOG_ASSERT(hInstance); - POMDOG_ASSERT(windowHandle); - - auto hr = DirectInput8Create(hInstance, DIRECTINPUT_VERSION, IID_IDirectInput8, - &directInput, nullptr); - - if (FAILED(hr)) { - // error: FUS RO DAH! - ///@todo Not implemented - } -} - -DeviceContextDirectInput::~DeviceContextDirectInput() = default; - -HWND DeviceContextDirectInput::WindowHandle() const -{ - POMDOG_ASSERT(windowHandle); - return windowHandle; -} - -IDirectInput8* DeviceContextDirectInput::GetDirectInput() const -{ - POMDOG_ASSERT(directInput); - return directInput.Get(); -} - -} // namespace DirectInput -} // namespace InputSystem -} // namespace Detail -} // namespace Pomdog diff --git a/src/InputSystem.DirectInput/DeviceContextDirectInput.hpp b/src/InputSystem.DirectInput/DeviceContextDirectInput.hpp deleted file mode 100644 index 4db27195c..000000000 --- a/src/InputSystem.DirectInput/DeviceContextDirectInput.hpp +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2013-2018 mogemimi. Distributed under the MIT license. - -#pragma once - -#include "PrerequisitesDirectInput.hpp" -#include "../Utility/Noncopyable.hpp" -#include -#include - -namespace Pomdog { -namespace Detail { -namespace InputSystem { -namespace DirectInput { - -class DeviceContextDirectInput final : Noncopyable { -public: - DeviceContextDirectInput(HINSTANCE hInstance, HWND windowHandle); - - ~DeviceContextDirectInput(); - - HWND WindowHandle() const; - - IDirectInput8* GetDirectInput() const; - -private: - HWND windowHandle; - Microsoft::WRL::ComPtr directInput; -}; - -} // namespace DirectInput -} // namespace InputSystem -} // namespace Detail -} // namespace Pomdog diff --git a/src/InputSystem.DirectInput/GamepadDirectInput.cpp b/src/InputSystem.DirectInput/GamepadDirectInput.cpp new file mode 100644 index 000000000..cd0fbaa5e --- /dev/null +++ b/src/InputSystem.DirectInput/GamepadDirectInput.cpp @@ -0,0 +1,675 @@ +// Copyright (c) 2013-2018 mogemimi. Distributed under the MIT license. + +#include "GamepadDirectInput.hpp" +#include "../InputSystem/GamepadHelper.hpp" +#include "../Utility/ScopeGuard.hpp" +#include "Pomdog/Logging/Log.hpp" +#include "Pomdog/Utility/Assert.hpp" +#include "Pomdog/Utility/Exception.hpp" +#include +#include +#include +#include +#include + +namespace Pomdog { +namespace Detail { +namespace InputSystem { +namespace DirectInput { +namespace { + +constexpr LONG ThumbStickMinValue = -32768; +constexpr LONG ThumbStickMaxValue = 32767; + +BOOL CALLBACK EnumGamepadsCallback(LPCDIDEVICEINSTANCE deviceInstance, LPVOID context) +{ + auto input = reinterpret_cast(context); + POMDOG_ASSERT(input != nullptr); + return input->OnDeviceAttached(deviceInstance); +} + +BOOL CALLBACK EnumAxesCallback(const DIDEVICEOBJECTINSTANCE* deviceObject, VOID* context) +{ + auto gamepad = reinterpret_cast(context); + POMDOG_ASSERT(gamepad != nullptr); + + if ((deviceObject->dwType & DIDFT_BUTTON) != 0) { + auto& buttons = gamepad->mappings.buttons; + + constexpr int usageButton0 = 0x01; + auto buttonIndex = static_cast(deviceObject->wUsage) - usageButton0; + + if ((buttonIndex < 0) || (buttonIndex >= static_cast(buttons.size()))) { + return DIENUM_CONTINUE; + } + + POMDOG_ASSERT(buttonIndex >= 0); + POMDOG_ASSERT(buttonIndex < static_cast(buttons.size())); + + if (auto hasButton = HasButton(gamepad->caps, buttons, buttonIndex); hasButton != nullptr) { + (*hasButton) = true; + } + } + else if ((deviceObject->dwType & DIDFT_AXIS) != 0) { + DIPROPRANGE rangeProps; + rangeProps.diph.dwSize = sizeof(rangeProps); + rangeProps.diph.dwHeaderSize = sizeof(rangeProps.diph); + rangeProps.diph.dwHow = DIPH_BYID; + rangeProps.diph.dwObj = deviceObject->dwType; + rangeProps.lMin = ThumbStickMinValue; + rangeProps.lMax = ThumbStickMaxValue; + + auto inputDevice = gamepad->inputDevice; + POMDOG_ASSERT(inputDevice != nullptr); + + // NOTE: Set the range for the axis + HRESULT hr = inputDevice->SetProperty(DIPROP_RANGE, &rangeProps.diph); + if (FAILED(hr)) { + Log::Warning("Pomdog.InputSystem", "Failed to call IDirectInputDevice8::SetProperty"); + return DIENUM_STOP; + } + + constexpr int usageAxisX = 0x30; + const auto axisIndex = static_cast(deviceObject->wUsage) - usageAxisX; + + if ((axisIndex < 0) || (axisIndex >= static_cast(gamepad->mappings.axes.size()))) { + return DIENUM_CONTINUE; + } + + POMDOG_ASSERT(axisIndex >= 0); + POMDOG_ASSERT(axisIndex < static_cast(gamepad->mappings.axes.size())); + + auto& mapper = gamepad->mappings.axes[axisIndex]; + auto& caps = gamepad->caps; + + if (auto hasThumbStick = HasThumbStick(caps, mapper.thumbStick); hasThumbStick != nullptr) { + (*hasThumbStick) = true; + } + if (auto hasButton = HasButton(caps, mapper.positiveTrigger); hasButton != nullptr) { + (*hasButton) = true; + } + if (auto hasButton = HasButton(caps, mapper.negativeTrigger); hasButton != nullptr) { + (*hasButton) = true; + } + + POMDOG_ASSERT(axisIndex >= 0); + POMDOG_ASSERT(axisIndex < static_cast(gamepad->thumbStickInfos.size())); + + auto& info = gamepad->thumbStickInfos[axisIndex]; + + switch (mapper.thumbStick) { + case ThumbStickKind::LeftStickY: + case ThumbStickKind::RightStickY: + // Set to -1 to reverse the Y axis (vertical) + info.InvertDirection = -1; + break; + default: + info.InvertDirection = 1; + break; + } + } + + return DIENUM_CONTINUE; +} + +bool IsXInputDevice(const GUID& guidProduct) +{ + auto hr = ::CoInitialize(nullptr); + + const bool cleanupCOM = SUCCEEDED(hr); + Detail::ScopeGuard deferCleanupCOM([&] { + if (cleanupCOM) { + ::CoUninitialize(); + } + }); + + Microsoft::WRL::ComPtr wbemLocator = nullptr; + hr = ::CoCreateInstance( + __uuidof(WbemLocator), + nullptr, + CLSCTX_INPROC_SERVER, + __uuidof(IWbemLocator), + &wbemLocator); + if (FAILED(hr) || (wbemLocator == nullptr)) { + return false; + } + + const auto bstrNamespace = ::SysAllocString(L"\\\\.\\root\\cimv2"); + if (bstrNamespace == nullptr) { + return false; + } + Detail::ScopeGuard defer1([&] { ::SysFreeString(bstrNamespace); }); + + const auto bstrClassName = ::SysAllocString(L"Win32_PNPEntity"); + if (bstrClassName == nullptr) { + return false; + } + Detail::ScopeGuard defer2([&] { ::SysFreeString(bstrClassName); }); + + const auto bstrDeviceID = ::SysAllocString(L"DeviceID"); + if (bstrDeviceID == nullptr) { + return false; + } + Detail::ScopeGuard defer3([&] { ::SysFreeString(bstrDeviceID); }); + + Microsoft::WRL::ComPtr wbemServices = nullptr; + hr = wbemLocator->ConnectServer( + bstrNamespace, + nullptr, + nullptr, + 0L, + 0L, + nullptr, + nullptr, + &wbemServices); + if (FAILED(hr) || (wbemServices == nullptr)) { + return false; + } + + ::CoSetProxyBlanket( + wbemServices.Get(), + RPC_C_AUTHN_WINNT, + RPC_C_AUTHZ_NONE, + nullptr, + RPC_C_AUTHN_LEVEL_CALL, + RPC_C_IMP_LEVEL_IMPERSONATE, + nullptr, + EOAC_NONE); + + Microsoft::WRL::ComPtr enumDevices = nullptr; + hr = wbemServices->CreateInstanceEnum(bstrClassName, 0, nullptr, &enumDevices); + if (FAILED(hr) || enumDevices == nullptr) { + return false; + } + + for (;;) { + DWORD deviceCount = 0; + + std::array devices; + std::fill(std::begin(devices), std::end(devices), nullptr); + Detail::ScopeGuard defer4([&] { + for (auto& device : devices) { + if (device != nullptr) { + device->Release(); + device = nullptr; + } + } + }); + + hr = enumDevices->Next(10000, static_cast(devices.size()), devices.data(), &deviceCount); + if (FAILED(hr)) { + return false; + } + + if (deviceCount == 0) { + break; + } + + for (DWORD deviceIndex = 0; deviceIndex < deviceCount; ++deviceIndex) { + VARIANT var; + hr = devices[deviceIndex]->Get(bstrDeviceID, 0L, &var, nullptr, nullptr); + + if (SUCCEEDED(hr) && (var.vt == VT_BSTR) && (var.bstrVal != nullptr)) { + if (::wcsstr(var.bstrVal, L"IG_") == nullptr) { + continue; + } + + DWORD dwVid = 0; + const auto strVid = ::wcsstr(var.bstrVal, L"VID_"); + if ((strVid != nullptr) && (::swscanf_s(strVid, L"VID_%4X", &dwVid) != 1)) { + dwVid = 0; + } + + DWORD dwPid = 0; + const auto strPid = ::wcsstr(var.bstrVal, L"PID_"); + if ((strPid != nullptr) && (::swscanf_s(strPid, L"PID_%4X", &dwPid) != 1)) { + dwPid = 0; + } + + const auto vidPid = static_cast(MAKELONG(dwVid, dwPid)); + if (vidPid == guidProduct.Data1) { + return true; + } + } + } + } + + return false; +} + +bool GetCaps(GamepadDevice& gamepad) +{ + POMDOG_ASSERT(gamepad.inputDevice != nullptr); + + const auto inputDevice = gamepad.inputDevice; + auto& caps = gamepad.caps; + + DIDEVCAPS deviceCaps; + deviceCaps.dwSize = sizeof(deviceCaps); + auto hr = inputDevice->GetCapabilities(&deviceCaps); + if (FAILED(hr)) { + Log::Warning("Pomdog.InputSystem", "Failed to call IDirectInputDevice8::GetCapabilities"); + return false; + } + + if (deviceCaps.dwButtons <= 0) { + // NOTE: If there is no button in the device, it will be treated as a gamepad. + return false; + } + + DIDEVICEINSTANCE deviceInfo; + deviceInfo.dwSize = sizeof(deviceInfo); + hr = inputDevice->GetDeviceInfo(&deviceInfo); + if (FAILED(hr)) { + Log::Warning("Pomdog.InputSystem", "Failed to call IDirectInputDevice8::GetDeviceInfo"); + return false; + } + + if (std::memcmp(&deviceInfo.guidProduct.Data4[2], "PIDVID", 6) == 0) { + constexpr std::uint16_t busUSB = 0x03; + caps.DeviceUUID.BusType = busUSB; + caps.DeviceUUID.ProductID = static_cast((deviceInfo.guidProduct.Data1 & 0xffff0000) >> 16); + caps.DeviceUUID.VendorID = static_cast(deviceInfo.guidProduct.Data1 & 0x0000ffff); + caps.DeviceUUID.VersionNumber = 0; + } + else { + // FIXME: Not implemented here + caps.DeviceUUID.BusType = 0x05; + caps.DeviceUUID.ProductID = 0; + caps.DeviceUUID.VendorID = 0; + caps.DeviceUUID.VersionNumber = 0; + Log::Warning( + "Pomdog.InputSystem", + "Pomdog does not support bluetooth gamepads yet," + "please report a issue to GitHub: https://github.com/mogemimi/pomdog ."); + } + + auto uuidString = caps.DeviceUUID.ToString(); + if (gamepad.isXInputDevice) { + // NOTE: Rename uuid string for SDL GameController DB + // https://github.com/gabomdq/SDL_GameControllerDB/blob/6854c08b4643a857e23e1d22d55a41d86ff63150/data/SDL_gamecontrollerdb2.0.6.h#L35 + uuidString = "xinput"; + } + + std::tie(gamepad.mappings, caps.Name) = GetMappings(uuidString); + if (caps.Name.empty() || gamepad.isXInputDevice) { + caps.Name = deviceInfo.tszProductName; + } + + if (gamepad.isXInputDevice) { + // NOTE: According to the following page, on an XInput device, + // the left and right trigger buttons will act as a single button, not independently. + // https://docs.microsoft.com/en-us/windows/desktop/xinput/xinput-and-directinput + // FIXME: Use XInput instead of DirectInput + for (auto& axis : gamepad.mappings.axes) { + if (axis.positiveTrigger == ButtonKind::RightTrigger) { + axis.positiveTrigger = ButtonKind::None; + } + if ((axis.positiveTrigger == ButtonKind::LeftTrigger) && + (axis.negativeTrigger == ButtonKind::None)) { + axis.negativeTrigger = ButtonKind::RightTrigger; + } + } + } + + hr = inputDevice->EnumObjects( + EnumAxesCallback, + static_cast(&gamepad), + DIDFT_BUTTON | DIDFT_AXIS); + + if (FAILED(hr)) { + Log::Warning("Pomdog.InputSystem", "Failed to call IDirectInputDevice8::EnumObjects"); + return false; + } + + DIPROPDWORD props; + props.diph.dwSize = sizeof(props); + props.diph.dwHeaderSize = sizeof(props.diph); + props.diph.dwHow = DIPH_DEVICE; + props.diph.dwObj = 0; + props.dwData = DIPROPAXISMODE_ABS; + hr = inputDevice->SetProperty(DIPROP_AXISMODE, &props.diph); + if (FAILED(hr)) { + Log::Warning("Pomdog.InputSystem", "Failed to call IDirectInputDevice8::SetProperty"); + return false; + } + + return true; +} + +void OnNotAcquired(GamepadDevice& gamepad) +{ + POMDOG_ASSERT(gamepad.inputDevice != nullptr); + HRESULT hr = gamepad.inputDevice->Acquire(); + if (FAILED(hr)) { + if (DIERR_OTHERAPPHASPRIO != hr) { + Log::Warning("Pomdog.InputSystem", "Failed to call IDirectInputDevice8::Acquire"); + gamepad.state.IsConnected = false; + return; + } + gamepad.deviceState = GamepadStateDirectInput::NotAcquired; + return; + } + gamepad.deviceState = GamepadStateDirectInput::Acquired; +} + +float NormalizeAxisValue(LONG value, const ThumbStickInfo& info) +{ + static_assert(ThumbStickMaxValue >= ThumbStickMinValue, ""); + constexpr auto range = ThumbStickMaxValue - ThumbStickMinValue; + return static_cast(info.InvertDirection * ((value - ThumbStickMinValue) * 2 - range)) / range; +} + +} // unnamed namespace + +bool GamepadDevice::Open(IDirectInput8* directInput, HWND windowHandle, const ::GUID& guidInstance) +{ + POMDOG_ASSERT(inputDevice == nullptr); + POMDOG_ASSERT(directInput != nullptr); + + auto hr = directInput->CreateDevice(guidInstance, &inputDevice, nullptr); + if (FAILED(hr)) { + Log::Warning("Pomdog.InputSystem", "Failed to call IDirectInput8::CreateDevice"); + return false; + } + + POMDOG_ASSERT(inputDevice != nullptr); + + // TODO: Replace c_dfDIJoystick with c_dfDIJoystick2 + hr = inputDevice->SetDataFormat(&c_dfDIJoystick); + if (FAILED(hr)) { + Log::Warning("Pomdog.InputSystem", "Failed to call IDirectInputDevice8::SetDataFormat"); + Close(); + return false; + } + + hr = inputDevice->SetCooperativeLevel(windowHandle, DISCL_FOREGROUND | DISCL_EXCLUSIVE); + if (FAILED(hr)) { + Log::Warning("Pomdog.InputSystem", "Failed to call IDirectInputDevice8::SetCooperativeLevel"); + Close(); + return false; + } + + if (!GetCaps(*this)) { + Log::Warning("Pomdog.InputSystem", "Failed to get capabilities"); + Close(); + return false; + } + + deviceState = GamepadStateDirectInput::NotAcquired; + + hr = inputDevice->Acquire(); + if (FAILED(hr)) { + if (DIERR_OTHERAPPHASPRIO != hr) { + Log::Warning("Pomdog.InputSystem", "Failed to call IDirectInputDevice8::Acquire"); + Close(); + return false; + } + POMDOG_ASSERT(deviceState == GamepadStateDirectInput::NotAcquired); + } + else { + deviceState = GamepadStateDirectInput::Acquired; + } + + GamepadHelper::ClearState(state); + state.IsConnected = true; + + Log::Internal("Open gamepad: " + caps.Name); + + return true; +} + +void GamepadDevice::Close() +{ + if (inputDevice != nullptr) { + inputDevice->Unacquire(); + inputDevice.Reset(); + } + + GamepadHelper::ClearState(state); + state.IsConnected = false; + + GamepadCapabilities emptyCaps; + std::swap(caps, emptyCaps); + + deviceState = GamepadStateDirectInput::NotInitialized; + deviceGuid.Data1 = 0; + deviceGuid.Data2 = 0; + deviceGuid.Data3 = 0; + std::fill(std::begin(deviceGuid.Data4), std::end(deviceGuid.Data4), static_cast(0)); + isXInputDevice = false; +} + +void GamepadDevice::PollEvents() +{ + if (deviceState == GamepadStateDirectInput::NotInitialized) { + return; + } + + POMDOG_ASSERT(inputDevice != nullptr); + + switch (deviceState) { + case GamepadStateDirectInput::NotAcquired: + OnNotAcquired(*this); + break; + case GamepadStateDirectInput::Acquired: + break; + case GamepadStateDirectInput::NotInitialized: + break; + } + + if (deviceState != GamepadStateDirectInput::Acquired) { + return; + } + + auto hr = inputDevice->Poll(); + if (FAILED(hr)) { + deviceState = GamepadStateDirectInput::NotAcquired; + return; + } + + // TODO: Replace DIJOYSTATE with DIJOYSTATE2 + DIJOYSTATE joystate; + hr = inputDevice->GetDeviceState(sizeof(DIJOYSTATE), &joystate); + + if ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED)) { + hr = inputDevice->Acquire(); + if (FAILED(hr)) { + deviceState = GamepadStateDirectInput::NotAcquired; + return; + } + hr = inputDevice->GetDeviceState(sizeof(DIJOYSTATE), &joystate); + } + + switch (hr) { + case DI_OK: + break; + case DIERR_NOTACQUIRED: + deviceState = GamepadStateDirectInput::NotAcquired; + return; + default: + state.IsConnected = false; + return; + } + + for (int i = 0; i < static_cast(mappings.buttons.size()); ++i) { + if (auto button = GetButton(state, mappings.buttons, i); button != nullptr) { + (*button) = ((joystate.rgbButtons[i] & 0x80) != 0) + ? ButtonState::Pressed + : ButtonState::Released; + } + } + + const auto& pov = joystate.rgdwPOV[0] / 100; + state.DPad.Up = ButtonState::Released; + state.DPad.Down = ButtonState::Released; + state.DPad.Left = ButtonState::Released; + state.DPad.Right = ButtonState::Released; + + if (((0 <= pov) && (pov <= 45)) || ((315 <= pov) && (pov <= 360))) { + state.DPad.Up = ButtonState::Pressed; + } + else if ((135 <= pov) && (pov <= 225)) { + state.DPad.Down = ButtonState::Pressed; + } + + if ((45 <= pov) && (pov <= 135)) { + state.DPad.Right = ButtonState::Pressed; + } + else if ((225 <= pov) && (pov <= 315)) { + state.DPad.Left = ButtonState::Pressed; + } + + const std::array values = {{ + joystate.lX, + joystate.lY, + joystate.lZ, + joystate.lRx, + joystate.lRy, + joystate.lRz, + }}; + + for (int i = 0; i < static_cast(mappings.axes.size()); ++i) { + POMDOG_ASSERT(i < static_cast(values.size())); + POMDOG_ASSERT(i < static_cast(mappings.axes.size())); + POMDOG_ASSERT(i < static_cast(thumbStickInfos.size())); + + const auto& mapper = mappings.axes[i]; + + if (auto thumbStick = GetThumbStick(state, mapper.thumbStick); thumbStick != nullptr) { + (*thumbStick) = NormalizeAxisValue(values[i], thumbStickInfos[i]); + } + + if (auto button = GetButton(state, mapper.positiveTrigger); button != nullptr) { + const auto value = NormalizeAxisValue(values[i], thumbStickInfos[i]); + constexpr float threshold = 0.05f; + (*button) = (value > threshold) ? ButtonState::Pressed : ButtonState::Released; + } + + if (auto button = GetButton(state, mapper.negativeTrigger); button != nullptr) { + const auto value = NormalizeAxisValue(values[i], thumbStickInfos[i]); + constexpr float threshold = -0.05f; + (*button) = (value < threshold) ? ButtonState::Pressed : ButtonState::Released; + } + } +} + +GamepadDirectInput::GamepadDirectInput(HINSTANCE hInstance, HWND windowHandleIn) + : windowHandle(windowHandleIn) + , directInput(nullptr) +{ + POMDOG_ASSERT(hInstance != nullptr); + POMDOG_ASSERT(windowHandle != nullptr); + + gamepads[0].playerIndex = PlayerIndex::One; + gamepads[1].playerIndex = PlayerIndex::Two; + gamepads[2].playerIndex = PlayerIndex::Three; + gamepads[3].playerIndex = PlayerIndex::Four; + + auto hr = DirectInput8Create( + hInstance, + DIRECTINPUT_VERSION, + IID_IDirectInput8, + &directInput, + nullptr); + + if (FAILED(hr)) { + POMDOG_THROW_EXCEPTION(std::runtime_error, "Error: Failed to create DirectInput8 instance."); + } + + for (auto& gamepad : gamepads) { + gamepad.deviceState = GamepadStateDirectInput::NotInitialized; + } +} + +GamepadDirectInput::~GamepadDirectInput() +{ + for (auto& gamepad : gamepads) { + gamepad.Close(); + } + directInput.Reset(); + windowHandle = nullptr; +} + +void GamepadDirectInput::EnumerateDevices() +{ + POMDOG_ASSERT(directInput != nullptr); + POMDOG_ASSERT(windowHandle != nullptr); + + auto hr = directInput->EnumDevices( + DI8DEVCLASS_GAMECTRL, + EnumGamepadsCallback, + static_cast(this), + DIEDFL_ATTACHEDONLY); + + if (FAILED(hr)) { + POMDOG_THROW_EXCEPTION(std::runtime_error, "Error: Failed to enumerate any input devices."); + } +} + +BOOL GamepadDirectInput::OnDeviceAttached(LPCDIDEVICEINSTANCE deviceInstance) +{ + for (auto& gamepad : gamepads) { + auto iter = std::find_if(std::begin(gamepads), std::end(gamepads), [&](const GamepadDevice& dev) { + return std::memcmp(&dev.deviceGuid, &deviceInstance->guidInstance, sizeof(dev.deviceGuid)) == 0; + }); + if (iter != std::end(gamepads)) { + // The device is already opened. + break; + } + + if (gamepad.deviceState == GamepadStateDirectInput::NotInitialized) { + gamepad.isXInputDevice = false; + if (IsXInputDevice(deviceInstance->guidProduct)) { + // TODO: Use XInput instead of DirectInput for Xbox controllers. + gamepad.isXInputDevice = true; + } + if (!gamepad.Open(directInput.Get(), windowHandle, deviceInstance->guidInstance)) { + Log::Warning("Pomdog.InputSystem", "Failed to initialize gamepad"); + return DIENUM_CONTINUE; + } + std::memcpy(&gamepad.deviceGuid, &deviceInstance->guidInstance, sizeof(gamepad.deviceGuid)); + this->Connected(gamepad.playerIndex, gamepad.caps); + return DIENUM_CONTINUE; + } + } + return DIENUM_STOP; +} + +GamepadCapabilities +GamepadDirectInput::GetCapabilities(PlayerIndex playerIndex) const +{ + const auto index = GamepadHelper::ToInt(playerIndex); + POMDOG_ASSERT(index >= 0); + POMDOG_ASSERT(index < static_cast(gamepads.size())); + return gamepads[index].caps; +} + +GamepadState GamepadDirectInput::GetState(PlayerIndex playerIndex) const +{ + const auto index = GamepadHelper::ToInt(playerIndex); + POMDOG_ASSERT(index >= 0); + POMDOG_ASSERT(index < static_cast(gamepads.size())); + return gamepads[index].state; +} + +void GamepadDirectInput::PollEvents() +{ + for (auto& gamepad : gamepads) { + if (!gamepad.state.IsConnected) { + continue; + } + + gamepad.PollEvents(); + if (!gamepad.state.IsConnected) { + auto caps = gamepad.caps; + gamepad.Close(); + this->Disconnected(gamepad.playerIndex, caps); + } + } +} + +} // namespace DirectInput +} // namespace InputSystem +} // namespace Detail +} // namespace Pomdog diff --git a/src/InputSystem.DirectInput/GamepadDirectInput.hpp b/src/InputSystem.DirectInput/GamepadDirectInput.hpp new file mode 100644 index 000000000..40ed8c2da --- /dev/null +++ b/src/InputSystem.DirectInput/GamepadDirectInput.hpp @@ -0,0 +1,76 @@ +// Copyright (c) 2013-2018 mogemimi. Distributed under the MIT license. + +#pragma once + +#include "PrerequisitesDirectInput.hpp" +#include "../InputSystem/GamepadMappings.hpp" +#include "../InputSystem/NativeGamepad.hpp" +#include "Pomdog/Input/Gamepad.hpp" +#include "Pomdog/Input/GamepadCapabilities.hpp" +#include "Pomdog/Input/GamepadState.hpp" +#include +#include +#include +#include + +namespace Pomdog { +namespace Detail { +namespace InputSystem { +namespace DirectInput { + +enum class GamepadStateDirectInput : std::uint8_t { + NotInitialized, + NotAcquired, + Acquired, +}; + +struct ThumbStickInfo final { + std::int32_t InvertDirection = 1; +}; + +struct GamepadDevice final { + GamepadCapabilities caps; + GamepadState state; + GamepadStateDirectInput deviceState; + + Microsoft::WRL::ComPtr inputDevice; + ::GUID deviceGuid; + + GamepadMappings mappings; + std::array thumbStickInfos; + PlayerIndex playerIndex; + bool isXInputDevice; + + bool Open(IDirectInput8* directInput, HWND windowHandle, const ::GUID& guidInstance); + + void Close(); + + void PollEvents(); +}; + +class GamepadDirectInput final : public NativeGamepad { +public: + GamepadDirectInput(HINSTANCE hInstance, HWND windowHandle); + + ~GamepadDirectInput(); + + GamepadCapabilities GetCapabilities(PlayerIndex index) const override; + + GamepadState GetState(PlayerIndex index) const override; + + void PollEvents() override; + + void EnumerateDevices(); + + BOOL OnDeviceAttached(LPCDIDEVICEINSTANCE deviceInstance); + +private: + std::array gamepads; + HWND windowHandle; + Microsoft::WRL::ComPtr directInput; +}; + +} // namespace DirectInput +} // namespace InputSystem +} // namespace Detail +} // namespace Pomdog diff --git a/src/InputSystem/GamepadMappings.cpp b/src/InputSystem/GamepadMappings.cpp index a62cce5da..060133067 100644 --- a/src/InputSystem/GamepadMappings.cpp +++ b/src/InputSystem/GamepadMappings.cpp @@ -28,6 +28,16 @@ namespace { #define __LINUX__ 1 #endif #endif + +#ifdef POMDOG_PLATFORM_WIN32 +#ifndef SDL_JOYSTICK_XINPUT +#define SDL_JOYSTICK_XINPUT 1 +#endif +#ifndef SDL_JOYSTICK_DINPUT +#define SDL_JOYSTICK_DINPUT 1 +#endif +#endif + #include "SDL_gamecontrollerdb.h" std::tuple Parse(const char* source, char delimiter) diff --git a/src/InputSystem/InputDeviceCreator.hpp b/src/InputSystem/InputDeviceCreator.hpp deleted file mode 100644 index e4d404dd0..000000000 --- a/src/InputSystem/InputDeviceCreator.hpp +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2013-2018 mogemimi. Distributed under the MIT license. - -#pragma once - -#include - -namespace Pomdog { -namespace Detail { - -class SubsystemScheduler; - -namespace InputSystem { - -template -class InputDeviceCreator { -public: - virtual ~InputDeviceCreator() = default; - - virtual std::shared_ptr Create(SubsystemScheduler & scheduler) = 0; -}; - -} // namespace InputSystem -} // namespace Detail -} // namespace Pomdog diff --git a/src/InputSystem/InputDeviceFactory.cpp b/src/InputSystem/InputDeviceFactory.cpp deleted file mode 100644 index c2dfeac9c..000000000 --- a/src/InputSystem/InputDeviceFactory.cpp +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2013-2018 mogemimi. Distributed under the MIT license. - -#include "InputDeviceFactory.hpp" -#include "Pomdog/Utility/Assert.hpp" -#include "Pomdog/Utility/Exception.hpp" -#include - -namespace Pomdog { -namespace Detail { -namespace InputSystem { - -InputDeviceFactory::~InputDeviceFactory() = default; - -} // namespace InputSystem -} // namespace Detail -} // namespace Pomdog diff --git a/src/InputSystem/InputDeviceFactory.hpp b/src/InputSystem/InputDeviceFactory.hpp deleted file mode 100644 index c9ae78e1b..000000000 --- a/src/InputSystem/InputDeviceFactory.hpp +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2013-2018 mogemimi. Distributed under the MIT license. - -#pragma once - -#include "../Utility/Noncopyable.hpp" -#include - -namespace Pomdog { - -class Keyboard; -class Mouse; - -namespace Detail { -namespace InputSystem { - -class InputDeviceFactory final : Noncopyable { -public: - ~InputDeviceFactory(); - - //std::shared_ptr CreateGamepad(SubsystemScheduler & scheduler); - -private: -}; - -} // namespace InputSystem -} // namespace Detail -} // namespace Pomdog diff --git a/src/Platform.Win32/Bootstrap.cpp b/src/Platform.Win32/Bootstrap.cpp index a090b6463..ad77e3f85 100644 --- a/src/Platform.Win32/Bootstrap.cpp +++ b/src/Platform.Win32/Bootstrap.cpp @@ -3,13 +3,14 @@ #include "Pomdog/Platform/Win32/Bootstrap.hpp" #include "GameHostWin32.hpp" #include "GameWindowWin32.hpp" -#include "../InputSystem.DirectInput/DeviceContextDirectInput.hpp" -#include "../InputSystem/InputDeviceFactory.hpp" +#include "../InputSystem.DirectInput/GamepadDirectInput.hpp" #include "Pomdog/Application/Game.hpp" #include "Pomdog/Application/GameHost.hpp" #include "Pomdog/Graphics/PresentationParameters.hpp" #include "Pomdog/Logging/Log.hpp" +using Pomdog::Detail::InputSystem::DirectInput::GamepadDirectInput; + namespace Pomdog { namespace Win32 { @@ -94,19 +95,11 @@ void Bootstrap::Run( hInstance, cmdShow, icon, iconSmall, useOpenGL, eventQueue, presentationParameters); - auto inputDeviceFactory = std::make_unique(); - - //{ - // using namespace Detail::InputSystem::DirectInput; - // auto deviceContext = std::make_shared( - // hInstance, gameWindow->NativeWindowHandle()); - // - // auto gamepadCreator = std::make_unique(deviceContext); - // inputDeviceFactory->AddCreator(std::move(gamepadCreator)); - //} + auto gamepad = std::make_shared( + hInstance, gameWindow->NativeWindowHandle()); - auto gameHost = std::make_shared(gameWindow, - eventQueue, presentationParameters, std::move(inputDeviceFactory), useOpenGL); + auto gameHost = std::make_shared( + gameWindow, eventQueue, gamepad, presentationParameters, useOpenGL); POMDOG_ASSERT(createApp); auto game = createApp(gameHost); diff --git a/src/Platform.Win32/GameHostWin32.cpp b/src/Platform.Win32/GameHostWin32.cpp index a596b142c..1dc84336e 100644 --- a/src/Platform.Win32/GameHostWin32.cpp +++ b/src/Platform.Win32/GameHostWin32.cpp @@ -4,6 +4,7 @@ #include "GameWindowWin32.hpp" #include "KeyboardWin32.hpp" #include "MouseWin32.hpp" +#include "../InputSystem/NativeGamepad.hpp" #if !defined(POMDOG_DISABLE_GL4) #include "../RenderSystem/GraphicsCommandQueueImmediate.hpp" #include "../Platform.Win32/OpenGLContextWin32.hpp" @@ -17,7 +18,6 @@ #endif #include "../Application/SubsystemScheduler.hpp" #include "../Application/SystemEvents.hpp" -#include "../InputSystem/InputDeviceFactory.hpp" #include "../SoundSystem.XAudio2/AudioEngineXAudio2.hpp" #include "Pomdog/Application/Game.hpp" #include "Pomdog/Application/GameClock.hpp" @@ -38,6 +38,7 @@ #include using Pomdog::Detail::Win32::GameWindowWin32; +using Pomdog::Detail::InputSystem::NativeGamepad; #if !defined(POMDOG_DISABLE_DIRECT3D11) using Pomdog::Detail::Direct3D11::GraphicsContextDirect3D11; using Pomdog::Detail::Direct3D11::GraphicsDeviceDirect3D11; @@ -180,8 +181,8 @@ class GameHostWin32::Impl final { Impl( const std::shared_ptr& window, const std::shared_ptr& eventQueue, + const std::shared_ptr& gamepad, const PresentationParameters& presentationParameters, - std::unique_ptr && inputDeviceFactory, bool useOpenGL); ~Impl(); @@ -206,6 +207,8 @@ class GameHostWin32::Impl final { std::shared_ptr GetMouse(); + std::shared_ptr GetGamepad(); + SurfaceFormat GetBackBufferSurfaceFormat() const noexcept; DepthFormat GetBackBufferDepthStencilFormat() const noexcept; @@ -233,9 +236,9 @@ class GameHostWin32::Impl final { std::unique_ptr assetManager; std::shared_ptr audioEngine; - std::unique_ptr inputDeviceFactory; std::shared_ptr keyboard; std::shared_ptr mouse; + std::shared_ptr gamepad; Duration presentationInterval; SurfaceFormat backBufferSurfaceFormat; @@ -247,12 +250,11 @@ class GameHostWin32::Impl final { GameHostWin32::Impl::Impl( const std::shared_ptr& windowIn, const std::shared_ptr& eventQueueIn, + const std::shared_ptr& gamepadIn, const PresentationParameters& presentationParameters, - std::unique_ptr && inputDeviceFactoryIn, bool useOpenGL) : eventQueue(eventQueueIn) , window(windowIn) - , inputDeviceFactory(std::move(inputDeviceFactoryIn)) , backBufferSurfaceFormat(presentationParameters.BackBufferFormat) , backBufferDepthStencilFormat(presentationParameters.DepthStencilFormat) , exitRequest(false) @@ -285,9 +287,9 @@ GameHostWin32::Impl::Impl( audioEngine = std::make_shared(); - POMDOG_ASSERT(inputDeviceFactory); keyboard = std::make_shared(); mouse = std::make_shared(window->NativeWindowHandle()); + gamepad = gamepadIn; Detail::AssetLoaderContext loaderContext; loaderContext.RootDirectory = PathHelper::Join( @@ -300,9 +302,9 @@ GameHostWin32::Impl::~Impl() { eventQueue.reset(); assetManager.reset(); + gamepad.reset(); keyboard.reset(); mouse.reset(); - inputDeviceFactory.reset(); audioEngine.reset(); graphicsBridge.reset(); graphicsCommandQueue.reset(); @@ -319,6 +321,11 @@ void GameHostWin32::Impl::Run(Game & game) clock.Tick(); MessagePump(); DoEvents(); + constexpr int64_t gamepadDetectionInterval = 240; + if (((clock.GetFrameNumber() % gamepadDetectionInterval) == 16) && (clock.GetFrameRate() >= 30.0f)) { + gamepad->EnumerateDevices(); + } + gamepad->PollEvents(); subsystemScheduler.OnUpdate(); game.Update(); RenderFrame(game); @@ -441,6 +448,12 @@ std::shared_ptr GameHostWin32::Impl::GetMouse() return mouse; } +std::shared_ptr GameHostWin32::Impl::GetGamepad() +{ + POMDOG_ASSERT(gamepad); + return gamepad; +} + SurfaceFormat GameHostWin32::Impl::GetBackBufferSurfaceFormat() const noexcept { return backBufferSurfaceFormat; @@ -454,14 +467,14 @@ DepthFormat GameHostWin32::Impl::GetBackBufferDepthStencilFormat() const noexcep GameHostWin32::GameHostWin32( const std::shared_ptr& window, const std::shared_ptr& eventQueue, + const std::shared_ptr& gamepad, const PresentationParameters& presentationParameters, - std::unique_ptr && inputDeviceFactory, bool useOpenGL) : impl(std::make_unique( window, eventQueue, + gamepad, presentationParameters, - std::move(inputDeviceFactory), useOpenGL)) {} @@ -530,9 +543,7 @@ std::shared_ptr GameHostWin32::GetMouse() std::shared_ptr GameHostWin32::GetGamepad() { POMDOG_ASSERT(impl); - // FIXME: Add DirectInput support - // https://github.com/mogemimi/pomdog/pull/16 - return nullptr; + return impl->GetGamepad(); } SurfaceFormat GameHostWin32::GetBackBufferSurfaceFormat() const diff --git a/src/Platform.Win32/GameHostWin32.hpp b/src/Platform.Win32/GameHostWin32.hpp index b65205893..ad4d6e19f 100644 --- a/src/Platform.Win32/GameHostWin32.hpp +++ b/src/Platform.Win32/GameHostWin32.hpp @@ -14,7 +14,7 @@ struct PresentationParameters; namespace Detail { namespace InputSystem { -class InputDeviceFactory; +class NativeGamepad; } // namespace InputSystem namespace Win32 { @@ -26,8 +26,8 @@ class GameHostWin32 final : public GameHost { GameHostWin32( const std::shared_ptr& window, const std::shared_ptr& eventQueue, + const std::shared_ptr& gamepad, const PresentationParameters& presentationParameters, - std::unique_ptr && inputDeviceFactory, bool useOpenGL); ~GameHostWin32();