#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2013 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #pragma endregion // CPL License // If the library is compiled as a DLL, this ensures symbols are exported #define NUCLEX_INPUT_SOURCE 1 #include "WindowsInputManager.Impl.h" #include "XInputDeviceEnumerator.h" #include "../Devices/XInput/XInputGamePad.h" #include "../Devices/Windows/DirectInputGamePad.h" namespace { // ------------------------------------------------------------------------------------------- // /// Determines if the specified handle is valid /// Handle the will be checked /// True if the handle is a valid handle, otherwise false bool isValidHandle(HMODULE handle) { return (handle != nullptr) && (handle != INVALID_HANDLE_VALUE); } // ------------------------------------------------------------------------------------------- // /// Adds informations about a DirectInput device to a vector /// Device informations that will be added to the vector /// Vector the device informations will be added to /// DIENUM_CONTINUE, always BOOL CALLBACK addDeviceInstance(const DIDEVICEINSTANCE *deviceInstance, void *userdata) { typedef std::vector DeviceInstanceVector; DeviceInstanceVector *deviceInstances = reinterpret_cast(userdata); deviceInstances->push_back(*deviceInstance); return DIENUM_CONTINUE; } // ------------------------------------------------------------------------------------------- // } // anonymous namespace namespace Nuclex { namespace Input { // ------------------------------------------------------------------------------------------- // WindowsInputManager::Impl::Impl(HWND windowHandle, HINSTANCE instanceHandle) : windowHandle(windowHandle), instanceHandle(instanceHandle), Mouse(windowHandle) { this->mouseEventTrackData.cbSize = sizeof(this->mouseEventTrackData); this->mouseEventTrackData.dwFlags = TME_LEAVE; this->mouseEventTrackData.dwHoverTime = 0; this->mouseEventTrackData.hwndTrack = windowHandle; tryInitializeDirectInput(); subclassWindow(); for(std::size_t playerIndex = 0; playerIndex < 4; ++playerIndex) { this->GamePads.push_back(new Devices::XInputGamePad(playerIndex)); } for(std::size_t playerIndex = 0; playerIndex < 4; ++playerIndex) { this->VirtualGamePads.push_back(new Devices::VirtualGamePad()); } std::vector deviceInstances = EnumerateDirectInputGamePads(); for(std::size_t index = 0; index < 4; ++index) { if(index >= deviceInstances.size()) { break; } this->VirtualGamePads[index]->SetForwardingGamePad( new Devices::DirectInputGamePad(this->directInput, deviceInstances[index]) ); } } // ------------------------------------------------------------------------------------------- // WindowsInputManager::Impl::~Impl() { for(std::size_t playerIndex = 0; playerIndex < 4; ++playerIndex) { Devices::GamePad *gamePad = this->VirtualGamePads[playerIndex]->GetForwardingGamePad(); delete this->VirtualGamePads[playerIndex]; delete gamePad; } for(std::size_t playerIndex = 0; playerIndex < 4; ++playerIndex) { delete this->GamePads[playerIndex]; } unsubclassWindow(); } // ------------------------------------------------------------------------------------------- // void WindowsInputManager::Impl::tryInitializeDirectInput() { // Try to load dinput8.dll. If it isn't on the system (or cannot be loaded for // some reason), we'll get a null handle. This allows a clean fallback instead // of an ugly error message to the user. HMODULE dinputDllHandle = ::LoadLibrary(L"dinput8.dll"); // If we could load dinput8.dll, we can attempt to initialize DirectInput if(isValidHandle(dinputDllHandle)) { ::FreeLibrary(dinputDllHandle); // Ugly COM stuff, creates the DirectInput COM object and returns it under // the interface we requested. HRESULT result = ::DirectInput8Create( this->instanceHandle, DIRECTINPUT_VERSION, IID_IDirectInput8, reinterpret_cast(&directInput), nullptr ); if(FAILED(result)) { directInput = nullptr; } } } // ------------------------------------------------------------------------------------------- // std::vector WindowsInputManager::Impl::EnumerateDirectInputGamePads() const { std::vector deviceInstances; // Let DirectInput fill our vector through the callback HRESULT result = this->directInput->EnumDevices( DI8DEVCLASS_GAMECTRL, addDeviceInstance, &deviceInstances, DIEDFL_ALLDEVICES ); if(FAILED(result)) { throw std::runtime_error("Error enumerating game controllers via DirectInput"); } #if TESTING_ONLY_DONT_FILTER_XINPUT_DEVICES return deviceInstances; // Only for testing, do not ship with this statement enabled! #endif // Remove all devices which are also XINPUT devices so we don't have them under both APIs std::set vidPids = XInputDeviceEnumerator::GetXInputVidPidPairs(); for(std::size_t index = 0; index < deviceInstances.size();) { DWORD deviceVidPid = deviceInstances[index].guidProduct.Data1; if(vidPids.find(deviceVidPid) == vidPids.end()) { ++index; } else { deviceInstances.erase(deviceInstances.begin() + index); } } return deviceInstances; } // ------------------------------------------------------------------------------------------- // void WindowsInputManager::Impl::subclassWindow() { BOOL result = ::SetPropA( this->windowHandle, "Nuclex::Input::WindowsInputManager::Impl", this ); if(result == 0) { throw std::runtime_error("Could not assign property to window"); } this->oldWindowProc = reinterpret_cast( ::SetWindowLongPtrA( this->windowHandle, GWLP_WNDPROC, reinterpret_cast(&WindowsInputManager::Impl::windowProcedure) ) ); } // ------------------------------------------------------------------------------------------- // void WindowsInputManager::Impl::unsubclassWindow() { ::SetWindowLongPtrA( this->windowHandle, GWLP_WNDPROC, reinterpret_cast(this->oldWindowProc) ); } // ------------------------------------------------------------------------------------------- // LRESULT CALLBACK WindowsInputManager::Impl::windowProcedure( HWND windowHandle, UINT message, WPARAM parameter1, LPARAM parameter2 ) { WindowsInputManager::Impl *self = reinterpret_cast( ::GetPropA(windowHandle, "Nuclex::Input::WindowsInputManager::Impl") ); if(self == nullptr) { return ::DefWindowProcA(windowHandle, message, parameter1, parameter2); } else { if(!self->Keyboard.ProcessWindowMessage(message, parameter1, parameter2)) { if(!self->Mouse.ProcessWindowMessage(message, parameter1, parameter2)) { return ::CallWindowProcA( self->oldWindowProc, windowHandle, message, parameter1, parameter2 ); } } return 0; } } // ------------------------------------------------------------------------------------------- // }} // namespace Nuclex::Input