From 630edadf9c351c5e1a4b36ea78167653848bd0fd Mon Sep 17 00:00:00 2001 From: Brad Hughes Date: Tue, 1 Mar 2016 11:08:05 -0500 Subject: [PATCH] Dynamically load xinput --- scripts/src/osd/windows.lua | 1 - src/osd/modules/input/input_xinput.cpp | 145 ++++++++++++++++++++++++- 2 files changed, 141 insertions(+), 5 deletions(-) diff --git a/scripts/src/osd/windows.lua b/scripts/src/osd/windows.lua index 7ff677eb1ba..950e15d1e46 100644 --- a/scripts/src/osd/windows.lua +++ b/scripts/src/osd/windows.lua @@ -46,7 +46,6 @@ function maintargetosdoptions(_target,_subtarget) "comctl32", "comdlg32", "psapi", - "xinput", } end diff --git a/src/osd/modules/input/input_xinput.cpp b/src/osd/modules/input/input_xinput.cpp index 292f9ee5d35..588ac0a9e5f 100644 --- a/src/osd/modules/input/input_xinput.cpp +++ b/src/osd/modules/input/input_xinput.cpp @@ -116,6 +116,107 @@ struct xinput_api_state XINPUT_CAPABILITIES caps; }; +// Typedef for pointers to XInput Functions +typedef DWORD(__stdcall* PFN_XINPUTGETSTATE)(DWORD, XINPUT_STATE*); +typedef DWORD(__stdcall* PFN_XINPUTGETCAPS)(DWORD, DWORD, XINPUT_CAPABILITIES*); + +class xinput_interface +{ +private: + HMODULE m_xinput_module; + PFN_XINPUTGETSTATE m_pfnXInputGetState; + PFN_XINPUTGETCAPS m_pfnXInputGetCapabilities; + bool m_initialized; + + xinput_interface() + : m_xinput_module(NULL), + m_pfnXInputGetState(nullptr), + m_pfnXInputGetCapabilities(nullptr), + m_initialized(false) + { + } + + int initialize() + { + if (m_initialized) + return 0; + + m_xinput_module = load_xinput_library(); + if (m_xinput_module == NULL) + { + osd_printf_error("Failed to load xinput library. Error: %u\n", (unsigned int)GetLastError()); + return -1; + } + + if (load_xinput_exports() != 0) + { + osd_printf_error("Failed to load exported XInput functions from library\n"); + return -1; + } + + m_initialized = true; + return 0; + } + + DWORD load_xinput_exports() + { + m_pfnXInputGetState = (PFN_XINPUTGETSTATE)GetProcAddress(m_xinput_module, "XInputGetState"); + m_pfnXInputGetCapabilities = (PFN_XINPUTGETCAPS)GetProcAddress(m_xinput_module, "XInputGetCapabilities"); + + if (m_pfnXInputGetState == nullptr + || m_pfnXInputGetCapabilities == nullptr) + { + return ERROR_NOT_FOUND; + } + + return 0; + } + + static HMODULE load_xinput_library() + { + // Attempt to load the Windows 8/10 version of Xinput first + HMODULE module = LoadLibrary(L"xinput1_4.dll"); + + // Fall back to the one from DX SDK + if (module == NULL) + { + module = LoadLibrary(L"xinput9_1_0.dll"); + } + + // If we still didn't load it, fail + if (module == NULL) + { + osd_printf_warning("Could not load any verions of the XInput Library.\n"); + SetLastError(ERROR_DLL_NOT_FOUND); + } + + return module; + } + +public: + inline DWORD XInputGetState(DWORD dwUserIndex, XINPUT_STATE* pState) + { + return m_pfnXInputGetState(dwUserIndex, pState); + } + + inline DWORD XInputGetCapabilities(DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES* pCapabilities) + { + return m_pfnXInputGetCapabilities(dwUserIndex, dwFlags, pCapabilities); + } + + static DWORD get_interface(xinput_interface **ppinterface) + { + static xinput_interface s_instance; + DWORD error = s_instance.initialize(); + if (error == 0) + { + *ppinterface = &s_instance; + } + + return error; + } +}; + //============================================================ // xinput_joystick_device //============================================================ @@ -126,17 +227,28 @@ public: gamepad_state gamepad; xinput_api_state xinput_state; +private: + xinput_interface* xinput_api; + +public: xinput_joystick_device(running_machine &machine, const char *name, input_module &module) : device_info(machine, name, DEVICE_CLASS_JOYSTICK, module), gamepad({0}), xinput_state({0}) { + // Attempt to get the xinput interface + xinput_api = nullptr; + xinput_interface::get_interface(&xinput_api); } void poll() override { + // Nothing we can do if for some reason the API couldn't be loaded + if (xinput_api == nullptr) + return; + // poll the device first - HRESULT result = XInputGetState(xinput_state.playerIndex, &xinput_state.xstate); + HRESULT result = xinput_api->XInputGetState(xinput_state.playerIndex, &xinput_state.xstate); // If we can't poll the device, skip if (FAILED(result)) @@ -182,15 +294,40 @@ public: class xinput_joystick_module : public wininput_module { +private: + xinput_interface* xinput_api; + public: xinput_joystick_module() - : wininput_module(OSD_JOYSTICKINPUT_PROVIDER, "xinput") + : wininput_module(OSD_JOYSTICKINPUT_PROVIDER, "xinput"), + xinput_api(nullptr) { } + int init(const osd_options &options) override + { + // Call the base + int status = wininput_module::init(options); + if (status != 0) + return status; + + // Make sure we can get the xinput API + status = xinput_interface::get_interface(&xinput_api); + if (status != 0) + { + osd_printf_error("xinput_joystick_module failed to get XInput interface! Error: %u\n", (unsigned int)status); + return -1; + } + + return 0; + } + protected: virtual void input_init(running_machine &machine) override { + // The Xinput API should have been obtained already + assert(xinput_api != nullptr); + xinput_joystick_device *devinfo; // Loop through each gamepad to determine if they are connected @@ -198,7 +335,7 @@ protected: { XINPUT_STATE state = {0}; - if (XInputGetState(i, &state) == ERROR_SUCCESS) + if (xinput_api->XInputGetState(i, &state) == ERROR_SUCCESS) { // allocate and link in a new device devinfo = create_xinput_device(machine, i); @@ -261,7 +398,7 @@ protected: xinput_joystick_device *devinfo; XINPUT_CAPABILITIES caps = {0}; - if (FAILED(XInputGetCapabilities(index, 0, &caps))) + if (FAILED(xinput_api->XInputGetCapabilities(index, 0, &caps))) { // If we can't get the capabilities skip this device return nullptr;