diff options
author | 2016-04-04 22:01:00 -0400 | |
---|---|---|
committer | 2016-04-04 22:01:00 -0400 | |
commit | 3dd8afd6c0b80a998758095b79d9dd5bd1cde017 (patch) | |
tree | 2e0d341a4c44699080353bd9fd323170af2e04e4 | |
parent | d5b01cf514212573c6490132cabf736b8b7ae9a8 (diff) |
Hybrid DirectInput/XInput module
-rw-r--r-- | scripts/src/osd/modules.lua | 4 | ||||
-rw-r--r-- | src/osd/modules/input/input_common.h | 28 | ||||
-rw-r--r-- | src/osd/modules/input/input_dinput.cpp | 572 | ||||
-rw-r--r-- | src/osd/modules/input/input_dinput.h | 177 | ||||
-rw-r--r-- | src/osd/modules/input/input_winhybrid.cpp | 372 | ||||
-rw-r--r-- | src/osd/modules/input/input_xinput.cpp | 406 | ||||
-rw-r--r-- | src/osd/modules/input/input_xinput.h | 140 | ||||
-rw-r--r-- | src/osd/modules/lib/osdobj_common.cpp | 1 |
8 files changed, 1121 insertions, 579 deletions
diff --git a/scripts/src/osd/modules.lua b/scripts/src/osd/modules.lua index f1bfab95b08..d50f12745d4 100644 --- a/scripts/src/osd/modules.lua +++ b/scripts/src/osd/modules.lua @@ -74,6 +74,7 @@ function osdmodulesbuild() MAME_DIR .. "src/osd/modules/input/input_common.cpp", MAME_DIR .. "src/osd/modules/input/input_common.h", MAME_DIR .. "src/osd/modules/input/input_dinput.cpp", + MAME_DIR .. "src/osd/modules/input/input_dinput.h", MAME_DIR .. "src/osd/modules/input/input_none.cpp", MAME_DIR .. "src/osd/modules/input/input_rawinput.cpp", MAME_DIR .. "src/osd/modules/input/input_win32.cpp", @@ -84,6 +85,8 @@ function osdmodulesbuild() MAME_DIR .. "src/osd/modules/input/input_windows.cpp", MAME_DIR .. "src/osd/modules/input/input_windows.h", MAME_DIR .. "src/osd/modules/input/input_xinput.cpp", + MAME_DIR .. "src/osd/modules/input/input_xinput.h", + MAME_DIR .. "src/osd/modules/input/input_winhybrid.cpp", } if _OPTIONS["targetos"]=="windows" then @@ -393,6 +396,7 @@ function osdmodulestargetconf() "gdi32", "dsound", "dxguid", + "oleaut32", } elseif _OPTIONS["targetos"]=="macosx" then links { diff --git a/src/osd/modules/input/input_common.h b/src/osd/modules/input/input_common.h index 7ea001dad4f..41a9d762838 100644 --- a/src/osd/modules/input/input_common.h +++ b/src/osd/modules/input/input_common.h @@ -352,19 +352,35 @@ public: // allocate the device object auto devinfo = std::make_unique<TActual>(machine, name, module); - // Add the device to the machine - devinfo->m_device = machine.input().device_class(devinfo->deviceclass()).add_device(devinfo->name(), devinfo.get()); + return add_device_internal(machine, name, module, std::move(devinfo)); + } - // append us to the list - m_list.push_back(std::move(devinfo)); + template <typename TActual, typename TArg> + TActual* create_device1(running_machine &machine, const char *name, input_module &module, TArg arg1) + { + // allocate the device object + auto devinfo = std::make_unique<TActual>(machine, name, module, arg1); - return (TActual*)m_list.back().get(); + return add_device_internal(machine, name, module, std::move(devinfo)); } template <class TActual> TActual* at(int index) { - return (TActual*)m_list.at(index).get(); + return static_cast<TActual*>(m_list.at(index).get()); + } + +private: + template <typename TActual> + TActual* add_device_internal(running_machine &machine, const char *name, input_module &module, std::unique_ptr<TActual> allocated) + { + // Add the device to the machine + allocated->m_device = machine.input().device_class(allocated->deviceclass()).add_device(allocated->name(), allocated.get()); + + // append us to the list + m_list.push_back(std::move(allocated)); + + return static_cast<TActual*>(m_list.back().get()); } }; diff --git a/src/osd/modules/input/input_dinput.cpp b/src/osd/modules/input/input_dinput.cpp index 1e4272e6317..ca3bad08026 100644 --- a/src/osd/modules/input/input_dinput.cpp +++ b/src/osd/modules/input/input_dinput.cpp @@ -14,6 +14,7 @@ // standard windows headers #define WIN32_LEAN_AND_MEAN #include <windows.h> +#include <initguid.h> #include <tchar.h> #include <wrl/client.h> @@ -31,17 +32,15 @@ #include "strconv.h" // MAMEOS headers -#include "winmain.h" #include "window.h" #include "winutil.h" #include "input_common.h" #include "input_windows.h" +#include "input_dinput.h" using namespace Microsoft::WRL; -#define STRUCTSIZE(x) (m_dinput_version == 0x0300) ? sizeof(x##_DX3) : sizeof(x) - static INT32 dinput_joystick_pov_get_state(void *device_internal, void *item_internal); //============================================================ @@ -58,255 +57,184 @@ static HRESULT dinput_set_dword_property(ComPtr<IDirectInputDevice> device, REFG dipdw.diph.dwHow = how; dipdw.dwData = value; - return IDirectInputDevice_SetProperty(device.Get(), property_guid, &dipdw.diph); + return device->SetProperty(property_guid, &dipdw.diph); } //============================================================ // dinput_device - base directinput device //============================================================ -// DirectInput-specific information about a device -struct dinput_api_state +dinput_device::dinput_device(running_machine &machine, const char *name, input_device_class deviceclass, input_module &module) + : device_info(machine, name, deviceclass, module), + dinput({nullptr}) { - ComPtr<IDirectInputDevice> device; - ComPtr<IDirectInputDevice2> device2; - DIDEVCAPS caps; - LPCDIDATAFORMAT format; -}; +} -class dinput_device : public device_info +dinput_device::~dinput_device() { -public: - dinput_api_state dinput; + if (dinput.device != nullptr) + dinput.device.Reset(); +} - dinput_device(running_machine &machine, const char *name, input_device_class deviceclass, input_module &module) - : device_info(machine, name, deviceclass, module), - dinput({nullptr}) - { - } +HRESULT dinput_device::poll_dinput(LPVOID pState) const +{ + HRESULT result; - virtual ~dinput_device() - { - if (dinput.device2 != nullptr) - dinput.device2.Reset(); + // first poll the device, then get the state + if (dinput.device2 != nullptr) + dinput.device2->Poll(); - if (dinput.device != nullptr) - dinput.device.Reset(); - } + // GetDeviceState returns the immediate state + result = dinput.device->GetDeviceState(dinput.format->dwDataSize, pState); -protected: - HRESULT poll_dinput(LPVOID pState) const + // handle lost inputs here + if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) { - HRESULT result; - - // first poll the device, then get the state - if (dinput.device2 != nullptr) - IDirectInputDevice2_Poll(dinput.device2.Get()); - - // GetDeviceState returns the immediate state - result = IDirectInputDevice_GetDeviceState(dinput.device.Get(), dinput.format->dwDataSize, pState); - - // handle lost inputs here - if (result == DIERR_INPUTLOST || result == DIERR_NOTACQUIRED) - { - result = IDirectInputDevice_Acquire(dinput.device.Get()); - if (result == DI_OK) - result = IDirectInputDevice_GetDeviceState(dinput.device.Get(), dinput.format->dwDataSize, pState); - } - - return result; + result = dinput.device->Acquire(); + if (result == DI_OK) + result = dinput.device->GetDeviceState(dinput.format->dwDataSize, pState); } -}; + + return result; +} //============================================================ // dinput_keyboard_device - directinput keyboard device //============================================================ -class dinput_keyboard_device : public dinput_device +dinput_keyboard_device::dinput_keyboard_device(running_machine &machine, const char *name, input_module &module) + : dinput_device(machine, name, DEVICE_CLASS_KEYBOARD, module), + keyboard({{0}}) { -private: - std::mutex m_device_lock; - -public: - keyboard_state keyboard; - - dinput_keyboard_device(running_machine &machine, const char *name, input_module &module) - : dinput_device(machine, name, DEVICE_CLASS_KEYBOARD, module), - keyboard({{0}}) - { - } +} - // Polls the direct input immediate state - void poll() override - { - std::lock_guard<std::mutex> scope_lock(m_device_lock); +// Polls the direct input immediate state +void dinput_keyboard_device::poll() +{ + std::lock_guard<std::mutex> scope_lock(m_device_lock); - // Poll the state - dinput_device::poll_dinput(&keyboard.state); - } + // Poll the state + dinput_device::poll_dinput(&keyboard.state); +} - void reset() override - { - memset(&keyboard.state, 0, sizeof(keyboard.state)); - } -}; +void dinput_keyboard_device::reset() +{ + memset(&keyboard.state, 0, sizeof(keyboard.state)); +} //============================================================ -// dinput_module - base directinput module +// dinput_api_helper - DirectInput API helper //============================================================ -class dinput_module : public wininput_module +dinput_api_helper::dinput_api_helper(int version) + : m_dinput(nullptr), + m_dinput_version(version), + m_pfn_DirectInputCreate("DirectInputCreateW", L"dinput.dll") { -private: - ComPtr<IDirectInput> m_dinput; - int m_dinput_version; +} -public: - dinput_module(const char* type, const char* name) - : wininput_module(type, name), - m_dinput(nullptr), - m_dinput_version(0) - { - } +dinput_api_helper::~dinput_api_helper() +{ + m_dinput.Reset(); +} - int init_internal() override - { - HRESULT result; +int dinput_api_helper::initialize() +{ + HRESULT result; -#if DIRECTINPUT_VERSION >= 0x800 - m_dinput_version = DIRECTINPUT_VERSION; + if (m_dinput_version >= 0x0800) + { result = DirectInput8Create(GetModuleHandleUni(), m_dinput_version, IID_IDirectInput8, reinterpret_cast<void **>(m_dinput.GetAddressOf()), nullptr); if (result != DI_OK) { m_dinput_version = 0; return result; } -#else - // first attempt to initialize DirectInput at the current version - m_dinput_version = DIRECTINPUT_VERSION; - result = DirectInputCreate(GetModuleHandleUni(), m_dinput_version, m_dinput.GetAddressOf(), nullptr); + } + else + { + result = m_pfn_DirectInputCreate.initialize(); + if (result != DI_OK) + return result; + + // first attempt to initialize DirectInput at v7 + m_dinput_version = 0x0700; + result = m_pfn_DirectInputCreate(GetModuleHandleUni(), m_dinput_version, m_dinput.GetAddressOf(), nullptr); if (result != DI_OK) { // if that fails, try version 5 m_dinput_version = 0x0500; - result = DirectInputCreate(GetModuleHandleUni(), m_dinput_version, m_dinput.GetAddressOf(), nullptr); + result = m_pfn_DirectInputCreate(GetModuleHandleUni(), m_dinput_version, m_dinput.GetAddressOf(), nullptr); if (result != DI_OK) { - // if that fails, try version 3 - m_dinput_version = 0x0300; - result = DirectInputCreate(GetModuleHandleUni(), m_dinput_version, m_dinput.GetAddressOf(), nullptr); - if (result != DI_OK) - { - m_dinput_version = 0; - return result; - } + m_dinput_version = 0; + return result; } } -#endif - - osd_printf_verbose("DirectInput: Using DirectInput %d\n", m_dinput_version >> 8); - return 0; } - void exit() override - { - m_dinput.Reset(); - wininput_module::exit(); - } + osd_printf_verbose("DirectInput: Using DirectInput %d\n", m_dinput_version >> 8); + return 0; +} - virtual BOOL device_enum_callback(LPCDIDEVICEINSTANCE instance, LPVOID ref) = 0; - struct dinput_callback_context - { - dinput_module * self; - running_machine * machine; - }; +HRESULT dinput_api_helper::enum_attached_devices(int devclass, device_enum_interface *enumerate_interface, void *state) const +{ + device_enum_interface::dinput_callback_context ctx; + ctx.self = enumerate_interface; + ctx.state = state; - static BOOL CALLBACK enum_callback(LPCDIDEVICEINSTANCE instance, LPVOID ref) - { - auto context = static_cast<dinput_callback_context*>(ref); - return context->self->device_enum_callback(instance, context->machine); - } + return m_dinput->EnumDevices(devclass, device_enum_interface::enum_callback, &ctx, DIEDFL_ATTACHEDONLY); +} - void input_init(running_machine &machine) override - { - dinput_callback_context context = { this, &machine }; - HRESULT result = IDirectInput_EnumDevices(m_dinput.Get(), dinput_devclass(), enum_callback, &context, DIEDFL_ATTACHEDONLY); - if (result != DI_OK) - fatalerror("DirectInput: Unable to enumerate keyboards (result=%08X)\n", static_cast<UINT32>(result)); - } +//============================================================ +// dinput_module - base directinput module +//============================================================ +class dinput_module : public wininput_module, public device_enum_interface +{ protected: - virtual int dinput_devclass() = 0; + std::unique_ptr<dinput_api_helper> m_dinput_helper; - template <class TDevice> - TDevice* create_dinput_device( - running_machine &machine, - LPCDIDEVICEINSTANCE instance, - LPCDIDATAFORMAT format1, - LPCDIDATAFORMAT format2, - DWORD cooperative_level) +public: + dinput_module(const char* type, const char* name) + : wininput_module(type, name), + m_dinput_helper(nullptr) { - HRESULT result; - - // convert instance name to utf8 - auto osd_deleter = [](void *ptr) { osd_free(ptr); }; - auto utf8_instance_name = std::unique_ptr<char, decltype(osd_deleter)>(utf8_from_tstring(instance->tszInstanceName), osd_deleter); - - // allocate memory for the device object - TDevice* devinfo = devicelist()->create_device<TDevice>(machine, utf8_instance_name.get(), *this); - - // attempt to create a device - result = IDirectInput_CreateDevice(m_dinput.Get(), WRAP_REFIID(instance->guidInstance), devinfo->dinput.device.GetAddressOf(), NULL); - if (result != DI_OK) - goto error; + } - // try to get a version 2 device for it - result = IDirectInputDevice_QueryInterface(devinfo->dinput.device.Get(), WRAP_REFIID(IID_IDirectInputDevice2), reinterpret_cast<void **>(devinfo->dinput.device2.GetAddressOf())); - if (result != DI_OK) - devinfo->dinput.device2 = nullptr; + int init_internal() override + { + m_dinput_helper = std::make_unique<dinput_api_helper>(DIRECTINPUT_VERSION); + int result = m_dinput_helper->initialize(); + if (result != 0) + return result; - // get the caps - devinfo->dinput.caps.dwSize = STRUCTSIZE(DIDEVCAPS); - result = IDirectInputDevice_GetCapabilities(devinfo->dinput.device.Get(), &devinfo->dinput.caps); - if (result != DI_OK) - goto error; + return 0; + } - // attempt to set the data format - devinfo->dinput.format = format1; - result = IDirectInputDevice_SetDataFormat(devinfo->dinput.device.Get(), devinfo->dinput.format); - if (result != DI_OK) - { - // use the secondary format if available - if (format2 != nullptr) - { - devinfo->dinput.format = format2; - result = IDirectInputDevice_SetDataFormat(devinfo->dinput.device.Get(), devinfo->dinput.format); - } - if (result != DI_OK) - goto error; - } + void exit() override + { + wininput_module::exit(); + m_dinput_helper.reset(); + } - // set the cooperative level - result = IDirectInputDevice_SetCooperativeLevel(devinfo->dinput.device.Get(), win_window_list->m_hwnd, cooperative_level); + void input_init(running_machine &machine) override + { + HRESULT result = m_dinput_helper->enum_attached_devices(dinput_devclass(), this, &machine); if (result != DI_OK) - goto error; - return devinfo; - - error: - devicelist()->free_device(devinfo); - return nullptr; + fatalerror("DirectInput: Unable to enumerate keyboards (result=%08X)\n", static_cast<UINT32>(result)); } - std::string device_item_name(dinput_device * devinfo, int offset, const char * defstring, const TCHAR * suffix) const + static std::string device_item_name(dinput_device * devinfo, int offset, const char * defstring, const TCHAR * suffix) { DIDEVICEOBJECTINSTANCE instance = { 0 }; HRESULT result; // query the key name - instance.dwSize = STRUCTSIZE(DIDEVICEOBJECTINSTANCE); - result = IDirectInputDevice_GetObjectInfo(devinfo->dinput.device.Get(), &instance, offset, DIPH_BYOFFSET); + instance.dwSize = sizeof(instance); + result = devinfo->dinput.device->GetObjectInfo(&instance, offset, DIPH_BYOFFSET); // if we got an error and have no default string, just return NULL if (result != DI_OK) @@ -342,6 +270,9 @@ protected: return std::string(combined.get()); } + +protected: + virtual int dinput_devclass() = 0; }; class keyboard_input_dinput : public dinput_module @@ -368,7 +299,7 @@ public: int keynum; // allocate and link in a new device - devinfo = create_dinput_device<dinput_keyboard_device>(machine, instance, &c_dfDIKeyboard, nullptr, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE); + devinfo = m_dinput_helper->create_device<dinput_keyboard_device>(machine, *this, instance, &c_dfDIKeyboard, nullptr, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE); if (devinfo == nullptr) goto exit; @@ -392,33 +323,28 @@ public: } }; -class dinput_mouse_device : public dinput_device -{ -public: - mouse_state mouse; - dinput_mouse_device(running_machine &machine, const char *name, input_module &module) - : dinput_device(machine, name, DEVICE_CLASS_MOUSE, module), - mouse({0}) - { - } +dinput_mouse_device::dinput_mouse_device(running_machine &machine, const char *name, input_module &module) + : dinput_device(machine, name, DEVICE_CLASS_MOUSE, module), + mouse({0}) +{ +} - void poll() override - { - // poll - dinput_device::poll_dinput(&mouse); +void dinput_mouse_device::poll() +{ + // poll + dinput_device::poll_dinput(&mouse); - // scale the axis data - mouse.lX *= INPUT_RELATIVE_PER_PIXEL; - mouse.lY *= INPUT_RELATIVE_PER_PIXEL; - mouse.lZ *= INPUT_RELATIVE_PER_PIXEL; - } + // scale the axis data + mouse.lX *= INPUT_RELATIVE_PER_PIXEL; + mouse.lY *= INPUT_RELATIVE_PER_PIXEL; + mouse.lZ *= INPUT_RELATIVE_PER_PIXEL; +} - void reset() override - { - memset(&mouse, 0, sizeof(mouse)); - } -}; +void dinput_mouse_device::reset() +{ + memset(&mouse, 0, sizeof(mouse)); +} class mouse_input_dinput : public dinput_module { @@ -445,7 +371,7 @@ public: HRESULT result; // allocate and link in a new device - devinfo = create_dinput_device<dinput_mouse_device>(machine, instance, &c_dfDIMouse2, &c_dfDIMouse, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE); + devinfo = m_dinput_helper->create_device<dinput_mouse_device>(machine, *this, instance, &c_dfDIMouse2, &c_dfDIMouse, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE); if (devinfo == nullptr) goto exit; @@ -489,46 +415,130 @@ public: } }; -// state information for a joystick; DirectInput state must be first element -struct dinput_joystick_state +dinput_joystick_device::dinput_joystick_device(running_machine &machine, const char *name, input_module &module) + : dinput_device(machine, name, DEVICE_CLASS_JOYSTICK, module), + joystick({{0}}) { - DIJOYSTATE state; - LONG rangemin[8]; - LONG rangemax[8]; -}; +} -class dinput_joystick_device : public dinput_device +void dinput_joystick_device::reset() { -public: - dinput_joystick_state joystick; + memset(&joystick.state, 0, sizeof(joystick.state)); +} + +void dinput_joystick_device::poll() +{ + int axisnum; + + // poll the device first + if (dinput_device::poll_dinput(&joystick.state) != ERROR_SUCCESS) + return; + + // normalize axis values + for (axisnum = 0; axisnum < 8; axisnum++) + { + LONG *axis = (&joystick.state.lX) + axisnum; + *axis = normalize_absolute_axis(*axis, joystick.rangemin[axisnum], joystick.rangemax[axisnum]); + } +} + +int dinput_joystick_device::configure() +{ + HRESULT result; + UINT32 axisnum, axiscount; + + auto devicelist = static_cast<input_module_base&>(module()).devicelist(); + + // temporary approximation of index + int devindex = devicelist->size(); + + // set absolute mode + result = dinput_set_dword_property(dinput.device, DIPROP_AXISMODE, 0, DIPH_DEVICE, DIPROPAXISMODE_ABS); + if (result != DI_OK && result != DI_PROPNOEFFECT) + osd_printf_warning("DirectInput: Unable to set absolute mode for joystick %d (%s)\n", devindex, name()); + + // turn off deadzone; we do our own calculations + result = dinput_set_dword_property(dinput.device, DIPROP_DEADZONE, 0, DIPH_DEVICE, 0); + if (result != DI_OK && result != DI_PROPNOEFFECT) + osd_printf_warning("DirectInput: Unable to reset deadzone for joystick %d (%s)\n", devindex, name()); - dinput_joystick_device(running_machine &machine, const char *name, input_module &module) - : dinput_device(machine, name, DEVICE_CLASS_JOYSTICK, module), - joystick({{0}}) + // turn off saturation; we do our own calculations + result = dinput_set_dword_property(dinput.device, DIPROP_SATURATION, 0, DIPH_DEVICE, 10000); + if (result != DI_OK && result != DI_PROPNOEFFECT) + osd_printf_warning("DirectInput: Unable to reset saturation for joystick %d (%s)\n", devindex, name()); + + // cap the number of axes, POVs, and buttons based on the format + dinput.caps.dwAxes = MIN(dinput.caps.dwAxes, 8); + dinput.caps.dwPOVs = MIN(dinput.caps.dwPOVs, 4); + dinput.caps.dwButtons = MIN(dinput.caps.dwButtons, 128); + + // populate the axes + for (axisnum = axiscount = 0; axiscount < dinput.caps.dwAxes && axisnum < 8; axisnum++) { + DIPROPRANGE dipr; + std::string name; + + // fetch the range of this axis + dipr.diph.dwSize = sizeof(dipr); + dipr.diph.dwHeaderSize = sizeof(dipr.diph); + dipr.diph.dwObj = offsetof(DIJOYSTATE2, lX) + axisnum * sizeof(LONG); + dipr.diph.dwHow = DIPH_BYOFFSET; + result = dinput.device->GetProperty(DIPROP_RANGE, &dipr.diph); + if (result != DI_OK) + continue; + + joystick.rangemin[axisnum] = dipr.lMin; + joystick.rangemax[axisnum] = dipr.lMax; + + // populate the item description as well + name = dinput_module::device_item_name(this, offsetof(DIJOYSTATE2, lX) + axisnum * sizeof(LONG), default_axis_name[axisnum], nullptr); + device()->add_item(name.c_str(), static_cast<input_item_id>(ITEM_ID_XAXIS + axisnum), generic_axis_get_state, &joystick.state.lX + axisnum); + + axiscount++; } - void reset() override + // populate the POVs + for (UINT32 povnum = 0; povnum < dinput.caps.dwPOVs; povnum++) { - memset(&joystick.state, 0, sizeof(joystick.state)); + std::string name; + + // left + name = dinput_module::device_item_name(this, offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), TEXT("L")); + device()->add_item(name.c_str(), ITEM_ID_OTHER_SWITCH, dinput_joystick_pov_get_state, reinterpret_cast<void *>(static_cast<FPTR>(povnum * 4 + POVDIR_LEFT))); + + // right + name = dinput_module::device_item_name(this, offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), TEXT("R")); + device()->add_item(name.c_str(), ITEM_ID_OTHER_SWITCH, dinput_joystick_pov_get_state, reinterpret_cast<void *>(static_cast<FPTR>(povnum * 4 + POVDIR_RIGHT))); + + // up + name = dinput_module::device_item_name(this, offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), TEXT("U")); + device()->add_item(name.c_str(), ITEM_ID_OTHER_SWITCH, dinput_joystick_pov_get_state, reinterpret_cast<void *>(static_cast<FPTR>(povnum * 4 + POVDIR_UP))); + + // down + name = dinput_module::device_item_name(this, offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), TEXT("D")); + device()->add_item(name.c_str(), ITEM_ID_OTHER_SWITCH, dinput_joystick_pov_get_state, reinterpret_cast<void *>(static_cast<FPTR>(povnum * 4 + POVDIR_DOWN))); } - void poll() override + // populate the buttons + for (UINT32 butnum = 0; butnum < dinput.caps.dwButtons; butnum++) { - int axisnum; + FPTR offset = reinterpret_cast<FPTR>(&static_cast<DIJOYSTATE2 *>(nullptr)->rgbButtons[butnum]); + std::string name = dinput_module::device_item_name(this, offset, default_button_name(butnum), nullptr); - // poll the device first - if (dinput_device::poll_dinput(&joystick.state) != ERROR_SUCCESS) - return; + input_item_id itemid; - // normalize axis values - for (axisnum = 0; axisnum < 8; axisnum++) - { - LONG *axis = (&joystick.state.lX) + axisnum; - *axis = normalize_absolute_axis(*axis, joystick.rangemin[axisnum], joystick.rangemax[axisnum]); - } + if (butnum < INPUT_MAX_BUTTONS) + itemid = static_cast<input_item_id>(ITEM_ID_BUTTON1 + butnum); + else if (butnum < INPUT_MAX_BUTTONS + INPUT_MAX_ADD_SWITCH) + itemid = static_cast<input_item_id>(ITEM_ID_ADD_SWITCH1 - INPUT_MAX_BUTTONS + butnum); + else + itemid = ITEM_ID_OTHER_SWITCH; + + device()->add_item(name.c_str(), itemid, generic_button_get_state, &joystick.state.rgbButtons[butnum]); } -}; + + return 0; +} class joystick_input_dinput : public dinput_module { @@ -550,102 +560,22 @@ public: BOOL device_enum_callback(LPCDIDEVICEINSTANCE instance, LPVOID ref) override { DWORD cooperative_level = DISCL_FOREGROUND | DISCL_NONEXCLUSIVE; - int axisnum, axiscount, povnum, butnum; running_machine &machine = *static_cast<running_machine *>(ref); dinput_joystick_device *devinfo; - HRESULT result; + int result = 0; - if (win_window_list != nullptr && win_window_list->win_has_menu()) { + if (win_window_list != nullptr && win_window_list->win_has_menu()) cooperative_level = DISCL_BACKGROUND | DISCL_NONEXCLUSIVE; - } + // allocate and link in a new device - devinfo = create_dinput_device<dinput_joystick_device>(machine, instance, &c_dfDIJoystick, nullptr, cooperative_level); + devinfo = m_dinput_helper->create_device<dinput_joystick_device>(machine, *this, instance, &c_dfDIJoystick, nullptr, cooperative_level); if (devinfo == nullptr) goto exit; - // set absolute mode - result = dinput_set_dword_property(devinfo->dinput.device, DIPROP_AXISMODE, 0, DIPH_DEVICE, DIPROPAXISMODE_ABS); - if (result != DI_OK && result != DI_PROPNOEFFECT) - osd_printf_warning("DirectInput: Unable to set absolute mode for joystick %d (%s)\n", devicelist()->size(), devinfo->name()); - - // turn off deadzone; we do our own calculations - result = dinput_set_dword_property(devinfo->dinput.device, DIPROP_DEADZONE, 0, DIPH_DEVICE, 0); - if (result != DI_OK && result != DI_PROPNOEFFECT) - osd_printf_warning("DirectInput: Unable to reset deadzone for joystick %d (%s)\n", devicelist()->size(), devinfo->name()); - - // turn off saturation; we do our own calculations - result = dinput_set_dword_property(devinfo->dinput.device, DIPROP_SATURATION, 0, DIPH_DEVICE, 10000); - if (result != DI_OK && result != DI_PROPNOEFFECT) - osd_printf_warning("DirectInput: Unable to reset saturation for joystick %d (%s)\n", devicelist()->size(), devinfo->name()); - - // cap the number of axes, POVs, and buttons based on the format - devinfo->dinput.caps.dwAxes = MIN(devinfo->dinput.caps.dwAxes, 8); - devinfo->dinput.caps.dwPOVs = MIN(devinfo->dinput.caps.dwPOVs, 4); - devinfo->dinput.caps.dwButtons = MIN(devinfo->dinput.caps.dwButtons, 128); - - // populate the axes - for (axisnum = axiscount = 0; axiscount < devinfo->dinput.caps.dwAxes && axisnum < 8; axisnum++) + result = devinfo->configure(); + if (result != 0) { - DIPROPRANGE dipr; - std::string name; - - // fetch the range of this axis - dipr.diph.dwSize = sizeof(dipr); - dipr.diph.dwHeaderSize = sizeof(dipr.diph); - dipr.diph.dwObj = offsetof(DIJOYSTATE2, lX) + axisnum * sizeof(LONG); - dipr.diph.dwHow = DIPH_BYOFFSET; - result = IDirectInputDevice_GetProperty(devinfo->dinput.device.Get(), DIPROP_RANGE, &dipr.diph); - if (result != DI_OK) - continue; - - devinfo->joystick.rangemin[axisnum] = dipr.lMin; - devinfo->joystick.rangemax[axisnum] = dipr.lMax; - - // populate the item description as well - name = device_item_name(devinfo, offsetof(DIJOYSTATE2, lX) + axisnum * sizeof(LONG), default_axis_name[axisnum], nullptr); - devinfo->device()->add_item(name.c_str(), static_cast<input_item_id>(ITEM_ID_XAXIS + axisnum), generic_axis_get_state, &devinfo->joystick.state.lX + axisnum); - - axiscount++; - } - - // populate the POVs - for (povnum = 0; povnum < devinfo->dinput.caps.dwPOVs; povnum++) - { - std::string name; - - // left - name = device_item_name(devinfo, offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), TEXT("L")); - devinfo->device()->add_item(name.c_str(), ITEM_ID_OTHER_SWITCH, dinput_joystick_pov_get_state, reinterpret_cast<void *>(static_cast<FPTR>(povnum * 4 + POVDIR_LEFT))); - - // right - name = device_item_name(devinfo, offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), TEXT("R")); - devinfo->device()->add_item(name.c_str(), ITEM_ID_OTHER_SWITCH, dinput_joystick_pov_get_state, reinterpret_cast<void *>(static_cast<FPTR>(povnum * 4 + POVDIR_RIGHT))); - - // up - name = device_item_name(devinfo, offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), TEXT("U")); - devinfo->device()->add_item(name.c_str(), ITEM_ID_OTHER_SWITCH, dinput_joystick_pov_get_state, reinterpret_cast<void *>(static_cast<FPTR>(povnum * 4 + POVDIR_UP))); - - // down - name = device_item_name(devinfo, offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), TEXT("D")); - devinfo->device()->add_item(name.c_str(), ITEM_ID_OTHER_SWITCH, dinput_joystick_pov_get_state, reinterpret_cast<void *>(static_cast<FPTR>(povnum * 4 + POVDIR_DOWN))); - } - - // populate the buttons - for (butnum = 0; butnum < devinfo->dinput.caps.dwButtons; butnum++) - { - FPTR offset = reinterpret_cast<FPTR>(&static_cast<DIJOYSTATE2 *>(nullptr)->rgbButtons[butnum]); - std::string name = device_item_name(devinfo, offset, default_button_name(butnum), nullptr); - - input_item_id itemid; - - if (butnum < INPUT_MAX_BUTTONS) - itemid = static_cast<input_item_id>(ITEM_ID_BUTTON1 + butnum); - else if (butnum < INPUT_MAX_BUTTONS + INPUT_MAX_ADD_SWITCH) - itemid = static_cast<input_item_id>(ITEM_ID_ADD_SWITCH1 - INPUT_MAX_BUTTONS + butnum); - else - itemid = ITEM_ID_OTHER_SWITCH; - - devinfo->device()->add_item(name.c_str(), itemid, generic_button_get_state, &devinfo->joystick.state.rgbButtons[butnum]); + osd_printf_error("Failed to configure DI Joystick device. Error 0x%x\n", static_cast<unsigned int>(result)); } exit: diff --git a/src/osd/modules/input/input_dinput.h b/src/osd/modules/input/input_dinput.h new file mode 100644 index 00000000000..6dacf8ebd77 --- /dev/null +++ b/src/osd/modules/input/input_dinput.h @@ -0,0 +1,177 @@ +#ifndef INPUT_DINPUT_H_ +#define INPUT_DINPUT_H_ + +#include "input_common.h" +#include "winutil.h" + +//============================================================ +// dinput_device - base directinput device +//============================================================ + +// DirectInput-specific information about a device +struct dinput_api_state +{ + Microsoft::WRL::ComPtr<IDirectInputDevice> device; + Microsoft::WRL::ComPtr<IDirectInputDevice2> device2; + DIDEVCAPS caps; + LPCDIDATAFORMAT format; +}; + +class device_enum_interface +{ +public: + struct dinput_callback_context + { + device_enum_interface * self; + void * state; + }; + + virtual ~device_enum_interface() + { + } + + static BOOL CALLBACK enum_callback(LPCDIDEVICEINSTANCE instance, LPVOID ref) + { + auto context = static_cast<dinput_callback_context*>(ref); + return context->self->device_enum_callback(instance, context->state); + } + + virtual BOOL device_enum_callback(LPCDIDEVICEINSTANCE instance, LPVOID ref) = 0; +}; + +typedef lazy_loaded_function_p4<HRESULT, HMODULE, int, IDirectInput **, LPUNKNOWN> pfn_dinput_create; + +class dinput_api_helper +{ +private: + Microsoft::WRL::ComPtr<IDirectInput> m_dinput; + int m_dinput_version; + pfn_dinput_create m_pfn_DirectInputCreate; + +public: + dinput_api_helper(int version); + virtual ~dinput_api_helper(); + int initialize(); + + template<class TDevice> + TDevice* create_device( + running_machine &machine, + input_module_base &module, + LPCDIDEVICEINSTANCE instance, + LPCDIDATAFORMAT format1, + LPCDIDATAFORMAT format2, + DWORD cooperative_level) + { + HRESULT result; + + // convert instance name to utf8 + auto osd_deleter = [](void *ptr) { osd_free(ptr); }; + auto utf8_instance_name = std::unique_ptr<char, decltype(osd_deleter)>(utf8_from_tstring(instance->tszInstanceName), osd_deleter); + + // allocate memory for the device object + TDevice* devinfo = module.devicelist()->create_device<TDevice>(machine, utf8_instance_name.get(), module); + + // attempt to create a device + result = m_dinput->CreateDevice(instance->guidInstance, devinfo->dinput.device.GetAddressOf(), nullptr); + if (result != DI_OK) + goto error; + + // try to get a version 2 device for it + result = devinfo->dinput.device.CopyTo(IID_IDirectInputDevice2, reinterpret_cast<void**>(devinfo->dinput.device2.GetAddressOf())); + if (result != DI_OK) + devinfo->dinput.device2 = nullptr; + + // get the caps + devinfo->dinput.caps.dwSize = sizeof(devinfo->dinput.caps); + result = devinfo->dinput.device->GetCapabilities(&devinfo->dinput.caps); + if (result != DI_OK) + goto error; + + // attempt to set the data format + devinfo->dinput.format = format1; + result = devinfo->dinput.device->SetDataFormat(devinfo->dinput.format); + if (result != DI_OK) + { + // use the secondary format if available + if (format2 != nullptr) + { + devinfo->dinput.format = format2; + result = devinfo->dinput.device->SetDataFormat(devinfo->dinput.format); + } + if (result != DI_OK) + goto error; + } + + // set the cooperative level + result = devinfo->dinput.device->SetCooperativeLevel(win_window_list->m_hwnd, cooperative_level); + if (result != DI_OK) + goto error; + + return devinfo; + + error: + module.devicelist()->free_device(devinfo); + return nullptr; + } + + HRESULT enum_attached_devices(int devclass, device_enum_interface *enumerate_interface, void *state) const; +}; + +class dinput_device : public device_info +{ +public: + dinput_api_state dinput; + + dinput_device(running_machine &machine, const char *name, input_device_class deviceclass, input_module &module); + virtual ~dinput_device(); + +protected: + HRESULT poll_dinput(LPVOID pState) const; +}; + +class dinput_keyboard_device : public dinput_device +{ +private: + std::mutex m_device_lock; + +public: + keyboard_state keyboard; + + dinput_keyboard_device(running_machine &machine, const char *name, input_module &module); + + void poll() override; + void reset() override; +}; + +class dinput_mouse_device : public dinput_device +{ +public: + mouse_state mouse; + +public: + dinput_mouse_device(running_machine &machine, const char *name, input_module &module); + void poll() override; + void reset() override; +}; + +// state information for a joystick; DirectInput state must be first element +struct dinput_joystick_state +{ + DIJOYSTATE state; + LONG rangemin[8]; + LONG rangemax[8]; +}; + +class dinput_joystick_device : public dinput_device +{ +public: + dinput_joystick_state joystick; +public: + dinput_joystick_device(running_machine &machine, const char *name, input_module &module); + void reset() override; + void poll() override; + int configure(); +}; + + +#endif diff --git a/src/osd/modules/input/input_winhybrid.cpp b/src/osd/modules/input/input_winhybrid.cpp new file mode 100644 index 00000000000..cf0f095cdfc --- /dev/null +++ b/src/osd/modules/input/input_winhybrid.cpp @@ -0,0 +1,372 @@ +// license:BSD-3-Clause +// copyright-holders:Brad Hughes +//============================================================ +// +// input_winhybrid.cpp - Windows hybrid DirectInput/Xinput +// +//============================================================ + +#include "input_module.h" +#include "modules/osdmodule.h" +#include <wbemcli.h> + +#if defined(OSD_WINDOWS) + +#include <list> + +// standard windows headers +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <wrl/client.h> + +// XInput/DirectInput +#include <xinput.h> +#include <dinput.h> + +#undef interface + +// MAME headers +#include "emu.h" +#include "osdepend.h" + +// MAMEOS headers +#include "strconv.h" +#include "winutil.h" +#include "winmain.h" + +#include "input_common.h" +#include "input_windows.h" +#include "input_xinput.h" +#include "input_dinput.h" + +using namespace Microsoft::WRL; + +#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=nullptr; } } + +struct bstr_deleter +{ + void operator () (BSTR bstr) const + { + if (bstr != nullptr) + SysFreeString(bstr); + } +}; + +typedef std::unique_ptr<OLECHAR, bstr_deleter> bstr_ptr; + +//============================================================ +// winhybrid_joystick_module +//============================================================ + +class winhybrid_joystick_module : public wininput_module, public device_enum_interface +{ +private: + std::shared_ptr<xinput_api_helper> m_xinput_helper; + std::unique_ptr<dinput_api_helper> m_dinput_helper; + std::list<DWORD> m_xinput_deviceids; + bool m_xinput_detect_failed; + +public: + winhybrid_joystick_module() + : wininput_module(OSD_JOYSTICKINPUT_PROVIDER, "winhybrid"), + m_xinput_helper(nullptr), + m_dinput_helper(nullptr), + m_xinput_detect_failed(false) + { + } + + bool probe() override + { + int status = init_helpers(); + if (status != 0) + { + osd_printf_verbose("Hybrid joystick module isn't supported, falling back.\n"); + return false; + } + + return true; + } + + int init(const osd_options &options) override + { + // Call the base + int status = wininput_module::init(options); + if (status != 0) + return status; + + // Create and initialize our helpers + status = init_helpers(); + if (status != 0) + return status; + + return 0; + } + + BOOL device_enum_callback(LPCDIDEVICEINSTANCE instance, LPVOID ref) override + { + DWORD cooperative_level = DISCL_FOREGROUND | DISCL_NONEXCLUSIVE; + running_machine &machine = *static_cast<running_machine *>(ref); + dinput_joystick_device *devinfo; + int result = 0; + + // First check if this device is XInput Compatible. If so, don't add it here + // as it'll be picked up by Xinput + if (!m_xinput_detect_failed && is_xinput_device(&instance->guidProduct)) + { + osd_printf_verbose("Skipping DirectInput for XInput compatible joystick %S.\n", instance->tszInstanceName); + goto exit; + } + + if (win_window_list != nullptr && win_window_list->win_has_menu()) + cooperative_level = DISCL_BACKGROUND | DISCL_NONEXCLUSIVE; + + // allocate and link in a new device + devinfo = m_dinput_helper->create_device<dinput_joystick_device>(machine, *this, instance, &c_dfDIJoystick, nullptr, cooperative_level); + if (devinfo == nullptr) + goto exit; + + result = devinfo->configure(); + if (result != 0) + { + osd_printf_error("Failed to configure DI Joystick device. Error 0x%x\n", static_cast<unsigned int>(result)); + } + + exit: + return DIENUM_CONTINUE; + } + + void exit() override + { + m_xinput_helper.reset(); + m_dinput_helper.reset(); + + wininput_module::exit(); + } + +protected: + virtual void input_init(running_machine &machine) override + { + HRESULT result = get_xinput_devices(m_xinput_deviceids); + if (result != 0) + { + m_xinput_detect_failed = true; + osd_printf_warning("XInput device detection failed. XInput won't be used. Error: 0x%X\n", static_cast<unsigned int>(result)); + } + + // Enumerate all the directinput joysticks and add them if they aren't xinput compatible + result = m_dinput_helper->enum_attached_devices(DI8DEVCLASS_GAMECTRL, this, &machine); + if (result != DI_OK) + fatalerror("DirectInput: Unable to enumerate keyboards (result=%08X)\n", static_cast<UINT32>(result)); + + xinput_joystick_device *devinfo; + + // now add all xinput devices + if (!m_xinput_detect_failed) + { + // Loop through each gamepad to determine if they are connected + for (UINT i = 0; i < XUSER_MAX_COUNT; i++) + { + XINPUT_STATE state = { 0 }; + + if (m_xinput_helper->XInputGetState(i, &state) == ERROR_SUCCESS) + { + // allocate and link in a new device + devinfo = m_xinput_helper->create_xinput_device(machine, i, *this); + if (devinfo == nullptr) + continue; + + // Configure each gamepad to add buttons and Axes, etc. + devinfo->configure(); + } + } + } + } + +private: + int init_helpers() + { + int status = 0; + + if (m_xinput_helper == nullptr) + { + m_xinput_helper = std::make_shared<xinput_api_helper>(); + status = m_xinput_helper->initialize(); + if (status != 0) + { + osd_printf_error("xinput_api_helper failed to initialize! Error: %u\n", static_cast<unsigned int>(status)); + return -1; + } + } + + if (m_dinput_helper == nullptr) + { + m_dinput_helper = std::make_unique<dinput_api_helper>(DIRECTINPUT_VERSION); + status = m_dinput_helper->initialize(); + if (status != DI_OK) + { + osd_printf_error("dinput_api_helper failed to initialize! Error: %u\n", static_cast<unsigned int>(status)); + return -1; + } + } + + return status; + } + + //----------------------------------------------------------------------------- + // Returns true if the DirectInput device is also an XInput device. + //----------------------------------------------------------------------------- + bool is_xinput_device(const GUID* pGuidProductFromDirectInput) + { + // Check each xinput device to see if this device's vid/pid matches + for (auto devid = m_xinput_deviceids.begin(); devid != m_xinput_deviceids.end(); ++devid) + { + if (*devid == pGuidProductFromDirectInput->Data1) + return true; + } + + return false; + } + + //----------------------------------------------------------------------------- + // Enum each PNP device using WMI and check each device ID to see if it contains + // "IG_" (ex. "VID_045E&PID_028E&IG_00"). If it does, then it’s an XInput device + // Unfortunately this information can not be found by just using DirectInput. + // Checking against a VID/PID of 0x028E/0x045E won't find 3rd party or future + // XInput devices. + //----------------------------------------------------------------------------- + HRESULT get_xinput_devices(std::list<DWORD> &xinput_id_list) const + { + ComPtr<IWbemServices> pIWbemServices; + ComPtr<IEnumWbemClassObject> pEnumDevices; + ComPtr<IWbemLocator> pIWbemLocator; + IWbemClassObject* pDevices[20]; + bstr_ptr bstrDeviceID; + bstr_ptr bstrClassName; + bstr_ptr bstrNamespace; + DWORD uReturned = 0; + UINT iDevice = 0; + VARIANT var; + HRESULT hr; + + // CoInit if needed + CoInitialize(nullptr); + + // Create WMI + hr = CoCreateInstance( + __uuidof(WbemLocator), + nullptr, + CLSCTX_INPROC_SERVER, + __uuidof(IWbemLocator), + reinterpret_cast<void**>(pIWbemLocator.GetAddressOf())); + + if (FAILED(hr) || pIWbemLocator == nullptr) + { + osd_printf_error("Creating WbemLocator failed. Error: 0x%X\n", static_cast<unsigned int>(hr)); + return hr; + } + + // Create BSTRs for WMI + bstrNamespace = bstr_ptr(SysAllocString(L"\\\\.\\root\\cimv2")); + bstrDeviceID = bstr_ptr(SysAllocString(L"DeviceID")); + bstrClassName = bstr_ptr(SysAllocString(L"Win32_PNPEntity")); + + // Connect to WMI + hr = pIWbemLocator->ConnectServer( + bstrNamespace.get(), + nullptr, + nullptr, + nullptr, + 0L, + nullptr, + nullptr, + pIWbemServices.GetAddressOf()); + + if (FAILED(hr) || pIWbemServices == nullptr) + { + osd_printf_error("Connecting to WMI Server failed. Error: 0x%X\n", static_cast<unsigned int>(hr)); + return hr; + } + + // Switch security level to IMPERSONATE + (void)CoSetProxyBlanket( + pIWbemServices.Get(), + RPC_C_AUTHN_WINNT, + RPC_C_AUTHZ_NONE, + nullptr, + RPC_C_AUTHN_LEVEL_CALL, + RPC_C_IMP_LEVEL_IMPERSONATE, + nullptr, + 0); + + // Get list of Win32_PNPEntity devices + hr = pIWbemServices->CreateInstanceEnum(bstrClassName.get(), 0, nullptr, pEnumDevices.GetAddressOf()); + if (FAILED(hr) || pEnumDevices == nullptr) + { + osd_printf_error("Getting list of Win32_PNPEntity devices failed. Error: 0x%X\n", static_cast<unsigned int>(hr)); + return hr; + } + + // Loop over all devices + for (; ; ) + { + // Get 20 at a time + hr = pEnumDevices->Next(10000, 20, pDevices, &uReturned); + if (FAILED(hr)) + { + osd_printf_error("Enumerating WMI classes failed. Error: 0x%X\n", static_cast<unsigned int>(hr)); + goto cleanup;; + } + + if (uReturned == 0) + break; + + for (iDevice = 0; iDevice < uReturned; iDevice++) + { + if (!pDevices[iDevice]) + continue; + + // For each device, get its device ID + hr = pDevices[iDevice]->Get(bstrDeviceID.get(), 0L, &var, nullptr, nullptr); + if (SUCCEEDED(hr) && var.vt == VT_BSTR && var.bstrVal != nullptr) + { + // Check if the device ID contains "IG_". If it does, then it’s an XInput device + // Unfortunately this information can not be found by just using DirectInput + if (wcsstr(var.bstrVal, L"IG_")) + { + // If it does, then get the VID/PID from var.bstrVal + DWORD dwPid = 0, dwVid = 0; + WCHAR* strVid = wcsstr(var.bstrVal, L"VID_"); + if (strVid && swscanf(strVid, L"VID_%4X", &dwVid) != 1) + dwVid = 0; + + WCHAR* strPid = wcsstr(var.bstrVal, L"PID_"); + if (strPid && swscanf(strPid, L"PID_%4X", &dwPid) != 1) + dwPid = 0; + + DWORD dwVidPid = MAKELONG(dwVid, dwPid); + + // Add the VID/PID to a linked list + xinput_id_list.push_back(dwVidPid); + } + } + } + } + + cleanup: + for (iDevice = 0; iDevice < 20; iDevice++) + { + SAFE_RELEASE(pDevices[iDevice]); + } + + if (SUCCEEDED(hr)) + hr = S_OK; + + return hr; + } +}; + +#else +MODULE_NOT_SUPPORTED(winhybrid_joystick_module, OSD_JOYSTICKINPUT_PROVIDER, "winhybrid") +#endif + +MODULE_DEFINITION(JOYSTICKINPUT_WINHYBRID, winhybrid_joystick_module) diff --git a/src/osd/modules/input/input_xinput.cpp b/src/osd/modules/input/input_xinput.cpp index 8c7800c8c35..753fd49a729 100644 --- a/src/osd/modules/input/input_xinput.cpp +++ b/src/osd/modules/input/input_xinput.cpp @@ -23,224 +23,199 @@ // MAME headers #include "emu.h" #include "osdepend.h" -#include "ui/ui.h" // MAMEOS headers #include "winutil.h" #include "winmain.h" -#include "window.h" #include "input_common.h" #include "input_windows.h" +#include "input_xinput.h" -#define XINPUT_MAX_POV 4 -#define XINPUT_MAX_BUTTONS 10 -#define XINPUT_MAX_AXIS 4 -#define XINPUT_AXIS_MINVALUE -32767 -#define XINPUT_AXIS_MAXVALUE 32767 - -// default axis names -static const char *const xinput_axis_name[] = +xinput_api_helper::xinput_api_helper() +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + : XInputGetState("XInputGetState", xinput_dll_names, ARRAY_LENGTH(xinput_dll_names)) + , XInputGetCapabilities("XInputGetCapabilities", xinput_dll_names, ARRAY_LENGTH(xinput_dll_names)) +#endif { - "LSX", - "LSY", - "RSX", - "RSY" -}; +} -static const input_item_id xinput_axis_ids[] = +int xinput_api_helper::initialize() { - ITEM_ID_XAXIS, - ITEM_ID_YAXIS, - ITEM_ID_RXAXIS, - ITEM_ID_RYAXIS -}; - -static const USHORT xinput_pov_dir[] = { - XINPUT_GAMEPAD_DPAD_UP, - XINPUT_GAMEPAD_DPAD_DOWN, - XINPUT_GAMEPAD_DPAD_LEFT, - XINPUT_GAMEPAD_DPAD_RIGHT -}; - -static const char *const xinput_pov_names[] = { - "DPAD Up", - "DPAD Down", - "DPAD Left", - "DPAD Right" -}; +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + int status; + status = XInputGetState.initialize(); + if (status != 0) + { + osd_printf_error("Failed to initialize function pointer for %s. Error: %d\n", XInputGetState.name(), status); + return -1; + } -static const USHORT xinput_buttons[] = { - XINPUT_GAMEPAD_A, - XINPUT_GAMEPAD_B, - XINPUT_GAMEPAD_X, - XINPUT_GAMEPAD_Y, - XINPUT_GAMEPAD_LEFT_SHOULDER, - XINPUT_GAMEPAD_RIGHT_SHOULDER, - XINPUT_GAMEPAD_START, - XINPUT_GAMEPAD_BACK, - XINPUT_GAMEPAD_LEFT_THUMB, - XINPUT_GAMEPAD_RIGHT_THUMB, -}; + status = XInputGetCapabilities.initialize(); + if (status != 0) + { + osd_printf_error("Failed to initialize function pointer for %s. Error: %d\n", XInputGetCapabilities.name(), status); + return -1; + } +#endif -static const char *const xinput_button_names[] = { - "A", - "B", - "X", - "Y", - "Left Shoulder", - "Right Shoulder", - "Start", - "Back", - "Left Thumb", - "Right Thumb" -}; + return 0; +} -struct gamepad_state +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +// Pass-through functions for Universal Windows +inline DWORD xinput_api_helper::XInputGetState(DWORD dwUserindex, XINPUT_STATE *pState) { - BYTE rgbButtons[XINPUT_MAX_BUTTONS]; - BYTE rgbPov[XINPUT_MAX_POV]; - BYTE bLeftTrigger; - BYTE bRightTrigger; - LONG sThumbLX; - LONG sThumbLY; - LONG sThumbRX; - LONG sThumbRY; -}; + return ::XInputGetState(dwUserindex, pState); +} -// state information for a gamepad; state must be first element -struct xinput_api_state +inline DWORD xinput_api_helper::XInputGetCapabilities(DWORD dwUserindex, DWORD dwFlags, XINPUT_CAPABILITIES* pCapabilities) { - UINT32 playerIndex; - XINPUT_STATE xstate; - XINPUT_CAPABILITIES caps; -}; + return ::XInputGetCapabilities(dwUserindex, dwFlags, pCapabilities); +} +#endif -// Typedef for pointers to XInput Functions -typedef lazy_loaded_function_p2<DWORD, DWORD, XINPUT_STATE*> xinput_get_state_fn; -typedef lazy_loaded_function_p3<DWORD, DWORD, DWORD, XINPUT_CAPABILITIES*> xinput_get_caps_fn; +//============================================================ +// create_xinput_device +//============================================================ -class xinput_interface +xinput_joystick_device * xinput_api_helper::create_xinput_device(running_machine &machine, UINT index, wininput_module &module) { -private: - const wchar_t* xinput_dll_names[2] = { L"xinput1_4.dll", L"xinput9_1_0.dll" }; + xinput_joystick_device *devinfo; -public: - xinput_get_state_fn XInputGetState; - xinput_get_caps_fn XInputGetCapabilities; - -private: - xinput_interface() - : XInputGetState("XInputGetState", xinput_dll_names, ARRAY_LENGTH(xinput_dll_names)), - XInputGetCapabilities("XInputGetCapabilities", xinput_dll_names, ARRAY_LENGTH(xinput_dll_names)) + XINPUT_CAPABILITIES caps = { 0 }; + if (FAILED(XInputGetCapabilities(index, 0, &caps))) { + // If we can't get the capabilities skip this device + return nullptr; } - int initialize() - { - int status; - status = XInputGetState.initialize(); - if (status != 0) - { - osd_printf_error("Failed to initialize function pointer for %s. Error: %d\n", XInputGetState.name(), status); - return -1; - } + char device_name[16]; + snprintf(device_name, sizeof(device_name), "XInput Player %u", index + 1); - status = XInputGetCapabilities.initialize(); - if (status != 0) - { - osd_printf_error("Failed to initialize function pointer for %s. Error: %d\n", XInputGetCapabilities.name(), status); - return -1; - } + // allocate the device object + devinfo = module.devicelist()->create_device1<xinput_joystick_device>(machine, device_name, module, shared_from_this()); - return 0; - } + // Set the player ID + devinfo->xinput_state.playerIndex = index; -public: - static DWORD get_interface(xinput_interface **ppinterface) - { - static xinput_interface s_instance; - DWORD error = s_instance.initialize(); - if (error == 0) - { - *ppinterface = &s_instance; - } + // Assign the caps we captured earlier + devinfo->xinput_state.caps = caps; - return error; - } -}; + return devinfo; +} //============================================================ // xinput_joystick_device //============================================================ -class xinput_joystick_device : public device_info +xinput_joystick_device::xinput_joystick_device(running_machine &machine, const char *name, input_module &module, std::shared_ptr<xinput_api_helper> helper) + : device_info(machine, name, DEVICE_CLASS_JOYSTICK, module), + gamepad({{0}}), + xinput_state({0}), + m_xinput_helper(helper), + m_configured(false) { -public: - gamepad_state gamepad; - xinput_api_state xinput_state; +} -private: - xinput_interface* xinput_api; +void xinput_joystick_device::poll() +{ + if (!m_configured) + return; -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}), - xinput_api(nullptr) + // poll the device first + HRESULT result = m_xinput_helper->XInputGetState(xinput_state.playerIndex, &xinput_state.xstate); + + // If we can't poll the device, skip + if (FAILED(result)) + return; + + // Copy the XState into State + // Start with the POV (DPAD) + for (int povindex = 0; povindex < XINPUT_MAX_POV; povindex++) { - // Attempt to get the xinput interface - xinput_interface::get_interface(&xinput_api); + int currentPov = xinput_pov_dir[povindex]; + gamepad.rgbPov[currentPov] = (xinput_state.xstate.Gamepad.wButtons & currentPov) ? 0xFF : 0; } - void poll() override + // Now do the buttons + for (int buttonindex = 0; buttonindex < XINPUT_MAX_BUTTONS; buttonindex++) { - // Nothing we can do if for some reason the API couldn't be loaded - if (xinput_api == nullptr) - return; + int currentButton = xinput_buttons[buttonindex]; + gamepad.rgbButtons[buttonindex] = (xinput_state.xstate.Gamepad.wButtons & currentButton) ? 0xFF : 0; + } - // poll the device first - HRESULT result = xinput_api->XInputGetState(xinput_state.playerIndex, &xinput_state.xstate); + // Now grab the axis values + // Each of the thumbstick axis members is a signed value between -32768 and 32767 describing the position of the thumbstick + // However, the Y axis values are inverted from what MAME expects, so multiply by -1 first + gamepad.sThumbLX = normalize_absolute_axis(xinput_state.xstate.Gamepad.sThumbLX, XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); + gamepad.sThumbLY = normalize_absolute_axis(xinput_state.xstate.Gamepad.sThumbLY * -1, XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); + gamepad.sThumbRX = normalize_absolute_axis(xinput_state.xstate.Gamepad.sThumbRX, XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); + gamepad.sThumbRY = normalize_absolute_axis(xinput_state.xstate.Gamepad.sThumbRY * -1, XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); - // If we can't poll the device, skip - if (FAILED(result)) - return; + // Now the triggers + gamepad.bLeftTrigger = xinput_state.xstate.Gamepad.bLeftTrigger; + gamepad.bRightTrigger = xinput_state.xstate.Gamepad.bRightTrigger; +} - // Copy the XState into State - // Start with the POV (DPAD) - for (int povindex = 0; povindex < XINPUT_MAX_POV; povindex++) - { - int currentPov = xinput_pov_dir[povindex]; - gamepad.rgbPov[currentPov] = (xinput_state.xstate.Gamepad.wButtons & currentPov) ? 0xFF : 0; - } +void xinput_joystick_device::reset() +{ + memset(&gamepad, 0, sizeof(gamepad)); +} - // Now do the buttons - for (int buttonindex = 0; buttonindex < XINPUT_MAX_BUTTONS; buttonindex++) - { - int currentButton = xinput_buttons[buttonindex]; - gamepad.rgbButtons[buttonindex] = (xinput_state.xstate.Gamepad.wButtons & currentButton) ? 0xFF : 0; - } +void xinput_joystick_device::configure() +{ + std::lock_guard<std::mutex> scope_lock(m_device_lock); + + if (m_configured) + return; - // Now grab the axis values - // Each of the thumbstick axis members is a signed value between -32768 and 32767 describing the position of the thumbstick - // However, the Y axis values are inverted from what MAME expects, so multiply by -1 first - gamepad.sThumbLX = normalize_absolute_axis(xinput_state.xstate.Gamepad.sThumbLX, XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); - gamepad.sThumbLY = normalize_absolute_axis(xinput_state.xstate.Gamepad.sThumbLY * -1, XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); - gamepad.sThumbRX = normalize_absolute_axis(xinput_state.xstate.Gamepad.sThumbRX, XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); - gamepad.sThumbRY = normalize_absolute_axis(xinput_state.xstate.Gamepad.sThumbRY * -1, XINPUT_AXIS_MINVALUE, XINPUT_AXIS_MAXVALUE); - - // Now the triggers - gamepad.bLeftTrigger = xinput_state.xstate.Gamepad.bLeftTrigger; - gamepad.bRightTrigger = xinput_state.xstate.Gamepad.bRightTrigger; + // Add the axes + for (int axisnum = 0; axisnum < XINPUT_MAX_AXIS; axisnum++) + { + device()->add_item( + xinput_axis_name[axisnum], + xinput_axis_ids[axisnum], + generic_axis_get_state, + &gamepad.sThumbLX + axisnum); } - void reset() override + // Populate the POVs + // For XBOX, we treat the DPAD as a hat switch + for (int povnum = 0; povnum < XINPUT_MAX_POV; povnum++) { - memset(&gamepad, 0, sizeof(gamepad)); + device()->add_item( + xinput_pov_names[povnum], + ITEM_ID_OTHER_SWITCH, + generic_button_get_state, + &gamepad.rgbPov[povnum]); } -}; + + // populate the buttons + for (int butnum = 0; butnum < XINPUT_MAX_BUTTONS; butnum++) + { + device()->add_item( + xinput_button_names[butnum], + static_cast<input_item_id>(ITEM_ID_BUTTON1 + butnum), + generic_button_get_state, + &gamepad.rgbButtons[butnum]); + } + + device()->add_item( + "Left Trigger", + static_cast<input_item_id>(ITEM_ID_BUTTON1 + XINPUT_MAX_BUTTONS), + generic_button_get_state, + &gamepad.bLeftTrigger); + + device()->add_item( + "Right Trigger", + static_cast<input_item_id>(ITEM_ID_BUTTON1 + XINPUT_MAX_BUTTONS + 1), + generic_button_get_state, + &gamepad.bRightTrigger); + + m_configured = true; +} //============================================================ // xinput_joystick_module @@ -249,12 +224,12 @@ public: class xinput_joystick_module : public wininput_module { private: - xinput_interface* xinput_api; + std::shared_ptr<xinput_api_helper> m_xinput_helper; public: xinput_joystick_module() : wininput_module(OSD_JOYSTICKINPUT_PROVIDER, "xinput"), - xinput_api(nullptr) + m_xinput_helper(nullptr) { } @@ -265,11 +240,12 @@ public: if (status != 0) return status; - // Make sure we can get the xinput API - status = xinput_interface::get_interface(&xinput_api); + // Create and initialize our helper + m_xinput_helper = std::make_shared<xinput_api_helper>(); + status = m_xinput_helper->initialize(); if (status != 0) { - osd_printf_error("xinput_joystick_module failed to get XInput interface! Error: %u\n", (unsigned int)status); + osd_printf_error("xinput_joystick_module failed to get XInput interface! Error: %u\n", static_cast<unsigned int>(status)); return -1; } @@ -279,9 +255,6 @@ public: 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 @@ -289,93 +262,22 @@ protected: { XINPUT_STATE state = {0}; - if (xinput_api->XInputGetState(i, &state) == ERROR_SUCCESS) + if (m_xinput_helper->XInputGetState(i, &state) == ERROR_SUCCESS) { // allocate and link in a new device - devinfo = create_xinput_device(machine, i); + devinfo = m_xinput_helper->create_xinput_device(machine, i, *this); if (devinfo == nullptr) continue; - // Add the axes - for (int axisnum = 0; axisnum < XINPUT_MAX_AXIS; axisnum++) - { - devinfo->device()->add_item( - xinput_axis_name[axisnum], - xinput_axis_ids[axisnum], - generic_axis_get_state, - &devinfo->gamepad.sThumbLX + axisnum); - } - - // Populate the POVs - // For XBOX, we treat the DPAD as a hat switch - for (int povnum = 0; povnum < XINPUT_MAX_POV; povnum++) - { - devinfo->device()->add_item( - xinput_pov_names[povnum], - ITEM_ID_OTHER_SWITCH, - generic_button_get_state, - &devinfo->gamepad.rgbPov[povnum]); - } - - // populate the buttons - for (int butnum = 0; butnum < XINPUT_MAX_BUTTONS; butnum++) - { - devinfo->device()->add_item( - xinput_button_names[butnum], - (input_item_id)(ITEM_ID_BUTTON1 + butnum), - generic_button_get_state, - &devinfo->gamepad.rgbButtons[butnum]); - } - - devinfo->device()->add_item( - "Left Trigger", - (input_item_id)(ITEM_ID_BUTTON1 + XINPUT_MAX_BUTTONS), - generic_button_get_state, - &devinfo->gamepad.bLeftTrigger); - - devinfo->device()->add_item( - "Right Trigger", - (input_item_id)(ITEM_ID_BUTTON1 + XINPUT_MAX_BUTTONS + 1), - generic_button_get_state, - &devinfo->gamepad.bRightTrigger); + // Configure each gamepad to add buttons and Axes, etc. + devinfo->configure(); } } } - - private: - //============================================================ - // create_xinput_device - //============================================================ - - xinput_joystick_device * create_xinput_device(running_machine &machine, UINT index) - { - xinput_joystick_device *devinfo; - - XINPUT_CAPABILITIES caps = {0}; - if (FAILED(xinput_api->XInputGetCapabilities(index, 0, &caps))) - { - // If we can't get the capabilities skip this device - return nullptr; - } - - char device_name[16]; - snprintf(device_name, sizeof(device_name), "XInput Player %u", index + 1); - - // allocate the device object - devinfo = devicelist()->create_device<xinput_joystick_device>(machine, device_name, *this); - - // Set the player ID - devinfo->xinput_state.playerIndex = index; - - // Assign the caps we captured earlier - devinfo->xinput_state.caps = caps; - - return devinfo; - } }; #else -MODULE_NOT_SUPPORTED(xinput_joystick_module, OSD_KEYBOARDINPUT_PROVIDER, "xinput") +MODULE_NOT_SUPPORTED(xinput_joystick_module, OSD_JOYSTICKINPUT_PROVIDER, "xinput") #endif MODULE_DEFINITION(JOYSTICKINPUT_XINPUT, xinput_joystick_module) diff --git a/src/osd/modules/input/input_xinput.h b/src/osd/modules/input/input_xinput.h new file mode 100644 index 00000000000..0a0e4ed0266 --- /dev/null +++ b/src/osd/modules/input/input_xinput.h @@ -0,0 +1,140 @@ +#ifndef INPUT_XINPUT_H_ +#define INPUT_XINPUT_H_ + +#include <mutex> + +#define XINPUT_MAX_POV 4 +#define XINPUT_MAX_BUTTONS 10 +#define XINPUT_MAX_AXIS 4 + +#define XINPUT_AXIS_MINVALUE -32767 +#define XINPUT_AXIS_MAXVALUE 32767 + +class xinput_joystick_device; +// default axis names +static const char *const xinput_axis_name[] = +{ + "LSX", + "LSY", + "RSX", + "RSY" +}; + +static const input_item_id xinput_axis_ids[] = +{ + ITEM_ID_XAXIS, + ITEM_ID_YAXIS, + ITEM_ID_RXAXIS, + ITEM_ID_RYAXIS +}; + +static const USHORT xinput_pov_dir[] = { + XINPUT_GAMEPAD_DPAD_UP, + XINPUT_GAMEPAD_DPAD_DOWN, + XINPUT_GAMEPAD_DPAD_LEFT, + XINPUT_GAMEPAD_DPAD_RIGHT +}; + +static const char *const xinput_pov_names[] = { + "DPAD Up", + "DPAD Down", + "DPAD Left", + "DPAD Right" +}; + +static const USHORT xinput_buttons[] = { + XINPUT_GAMEPAD_A, + XINPUT_GAMEPAD_B, + XINPUT_GAMEPAD_X, + XINPUT_GAMEPAD_Y, + XINPUT_GAMEPAD_LEFT_SHOULDER, + XINPUT_GAMEPAD_RIGHT_SHOULDER, + XINPUT_GAMEPAD_START, + XINPUT_GAMEPAD_BACK, + XINPUT_GAMEPAD_LEFT_THUMB, + XINPUT_GAMEPAD_RIGHT_THUMB, +}; + +static const char *const xinput_button_names[] = { + "A", + "B", + "X", + "Y", + "Left Shoulder", + "Right Shoulder", + "Start", + "Back", + "Left Thumb", + "Right Thumb" +}; + +struct gamepad_state +{ + BYTE rgbButtons[XINPUT_MAX_BUTTONS]; + BYTE rgbPov[XINPUT_MAX_POV]; + BYTE bLeftTrigger; + BYTE bRightTrigger; + LONG sThumbLX; + LONG sThumbLY; + LONG sThumbRX; + LONG sThumbRY; +}; + +// state information for a gamepad; state must be first element +struct xinput_api_state +{ + UINT32 playerIndex; + XINPUT_STATE xstate; + XINPUT_CAPABILITIES caps; +}; + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +// Typedef for pointers to XInput Functions +typedef lazy_loaded_function_p2<DWORD, DWORD, XINPUT_STATE*> xinput_get_state_fn; +typedef lazy_loaded_function_p3<DWORD, DWORD, DWORD, XINPUT_CAPABILITIES*> xinput_get_caps_fn; +#endif + +class xinput_api_helper : public std::enable_shared_from_this<xinput_api_helper> +{ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +private: + const wchar_t* xinput_dll_names[2] = { L"xinput1_4.dll", L"xinput9_1_0.dll" }; + +public: + xinput_get_state_fn XInputGetState; + xinput_get_caps_fn XInputGetCapabilities; +#endif + +public: + xinput_api_helper(); + + int initialize(); + xinput_joystick_device * create_xinput_device(running_machine &machine, UINT index, wininput_module &module); + +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + // Pass-through functions for Universal Windows + inline DWORD XInputGetState(DWORD dwUserindex, XINPUT_STATE *pState); + inline DWORD XInputGetCapabilities(DWORD dwUserindex, DWORD dwFlags, XINPUT_CAPABILITIES* pCapabilities); +#endif +}; + +class xinput_joystick_device : public device_info +{ +public: + gamepad_state gamepad; + xinput_api_state xinput_state; + +private: + std::shared_ptr<xinput_api_helper> m_xinput_helper; + std::mutex m_device_lock; + bool m_configured; + +public: + xinput_joystick_device(running_machine &machine, const char *name, input_module &module, std::shared_ptr<xinput_api_helper> helper); + + void poll() override; + void reset() override; + void configure(); +}; + +#endif diff --git a/src/osd/modules/lib/osdobj_common.cpp b/src/osd/modules/lib/osdobj_common.cpp index b4c0d68915e..04eb1f6da68 100644 --- a/src/osd/modules/lib/osdobj_common.cpp +++ b/src/osd/modules/lib/osdobj_common.cpp @@ -241,6 +241,7 @@ void osd_common_t::register_options() REGISTER_MODULE(m_mod_man, LIGHTGUN_NONE); REGISTER_MODULE(m_mod_man, JOYSTICKINPUT_SDL); + REGISTER_MODULE(m_mod_man, JOYSTICKINPUT_WINHYBRID); REGISTER_MODULE(m_mod_man, JOYSTICKINPUT_DINPUT); REGISTER_MODULE(m_mod_man, JOYSTICKINPUT_XINPUT); REGISTER_MODULE(m_mod_man, JOYSTICK_NONE); |