summaryrefslogtreecommitdiffstatshomepage
path: root/src/osd/modules/input/input_dinput.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/osd/modules/input/input_dinput.cpp')
-rw-r--r--src/osd/modules/input/input_dinput.cpp912
1 files changed, 773 insertions, 139 deletions
diff --git a/src/osd/modules/input/input_dinput.cpp b/src/osd/modules/input/input_dinput.cpp
index 2ac7c7458fd..8642f693d03 100644
--- a/src/osd/modules/input/input_dinput.cpp
+++ b/src/osd/modules/input/input_dinput.cpp
@@ -1,22 +1,108 @@
// license:BSD-3-Clause
-// copyright-holders:Aaron Giles, Brad Hughes
+// copyright-holders:Aaron Giles, Brad Hughes, Vas Crabb
//============================================================
//
// input_dinput.cpp - Windows DirectInput support
//
//============================================================
+/*
+
+DirectInput joystick input is a bit of a mess. It gives eight axes
+called X, Y, Z, Rx, Ry, Rz, slider 0 and slider 1. The driver can
+assign arbitrary physical axes to these axes. Up to four hat switches
+are supported, giving a direction in hundredths of degrees. In theory,
+this supports dial-like controls with an arbitrary number of stops. In
+practice, it just makes dealing with 8-way hat switches more complicated
+and prevents contradictory inputs from being reported altogether.
+
+You may get a vague indication of the type of controller, and you can
+obtain usage information for HID controllers.
+
+The Windows HID driver supposedly uses the following mappings:
+
+0x01 Generic Desktop 0x30 X X
+0x01 Generic Desktop 0x31 Y Y
+0x01 Generic Desktop 0x32 Z Z
+0x01 Generic Desktop 0x33 Rx Rx
+0x01 Generic Desktop 0x34 Ry Ry
+0x01 Generic Desktop 0x35 Rz Rz
+0x01 Generic Desktop 0x36 Slider Slider
+0x01 Generic Desktop 0x37 Dial Slider
+0x01 Generic Desktop 0x39 Hat Switch POV Hat
+0x02 Simulation 0xBA Rudder Rz
+0x02 Simulation 0xBB Throttle Slider
+0x02 Simulation 0xC4 Accelerator Y
+0x02 Simulation 0xC5 Brake Rz
+0x02 Simulation 0xC8 Steering X
+
+Anything without an explicit mapping is treated as a button.
+
+The WinMM driver supposedly uses the following axis mappings:
+
+X X
+Y Y
+Z Slider
+R Rz
+U Slider
+V Slider
+
+The actual mapping used by various controllers doesn't match what you
+might expect from the HID mapping.
+
+Gamepads:
+
+Axis Logitech Xinput Switch
+X Left X Left X Left X
+Y Left Y Left Y Left Y
+Z Right X Triggers
+Rx Right X Right X
+Ry Right Y Right Y
+Rz Right Y
+
+Thrustmaster controllers:
+
+Axis HOTAS Side stick Throttle/Pedals Dual Throttles Triple Throttles Driving
+X Aileron Aileron Mini Stick X Left Throttle Right Brake Steering
+Y Elevator Elevator Mini stick Y Right Throttle Left Brake Brake
+Z Throttle Throttle Flaps Rudder
+Rx Right Brake Right Brake Right Brake Left Throttle
+Ry Left Brake Left Brake Left Brake Centre Throttle
+Rz Twist Rudder Rocker Air Brake Right Throttle Accelerator
+Slider 0 Rocker Throttle Antenna Rudder Clutch
+Slider 1 Rudder Rudder
+
+Logitech controllers:
+
+Axis Pro Wheels
+X Steering
+Y
+Z
+Rx Accelerator
+Ry Brake
+Rz Clutch
+Slider 0
+Slider 1
+
+MFG Crosswind pedals:
+
+X Left Brake
+Y Right Brake
+Rz Rudder
+
+*/
#include "modules/osdmodule.h"
#if defined(OSD_WINDOWS) || defined(SDLMAME_WIN32)
-// emu
-#include "emu.h" // put this here before Windows headers define interface as a macro
-
#include "input_dinput.h"
+#include "interface/inputseq.h"
#include "windows/winutil.h"
+// emu
+#include "inpttype.h"
+
// lib/util
#include "util/corestr.h"
@@ -26,6 +112,7 @@
#endif
#include <algorithm>
+#include <cmath>
#include <iterator>
#include <memory>
@@ -44,8 +131,180 @@ BOOL CALLBACK device_enum_interface_callback(LPCDIDEVICEINSTANCE instance, LPVOI
}
+std::string guid_to_string(GUID const &guid)
+{
+ // size of a GUID string with dashes plus NUL terminator
+ char guid_string[37];
+ snprintf(
+ guid_string, std::size(guid_string),
+ "%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ guid.Data1, guid.Data2, guid.Data3,
+ guid.Data4[0], guid.Data4[1], guid.Data4[2],
+ guid.Data4[3], guid.Data4[4], guid.Data4[5],
+ guid.Data4[6], guid.Data4[7]);
+
+ return guid_string;
+}
+
+
+
//============================================================
-// dinput_module - base directinput module
+// dinput_keyboard_device - DirectInput keyboard device
+//============================================================
+
+class dinput_keyboard_device : public dinput_device
+{
+public:
+ dinput_keyboard_device(
+ std::string &&name,
+ std::string &&id,
+ input_module &module,
+ Microsoft::WRL::ComPtr<IDirectInputDevice8> &&device,
+ DIDEVCAPS const &caps,
+ LPCDIDATAFORMAT format);
+
+ virtual void poll() override;
+ virtual void reset() override;
+ virtual void configure(input_device &device) override;
+
+private:
+ std::mutex m_device_lock;
+ keyboard_state m_keyboard;
+};
+
+dinput_keyboard_device::dinput_keyboard_device(
+ std::string &&name,
+ std::string &&id,
+ input_module &module,
+ Microsoft::WRL::ComPtr<IDirectInputDevice8> &&device,
+ DIDEVCAPS const &caps,
+ LPCDIDATAFORMAT format) :
+ dinput_device(std::move(name), std::move(id), module, std::move(device), caps, format),
+ m_keyboard({ { 0 } })
+{
+}
+
+void dinput_keyboard_device::poll()
+{
+ // poll the DirectInput immediate state
+ std::lock_guard<std::mutex> scope_lock(m_device_lock);
+ poll_dinput(&m_keyboard.state);
+}
+
+void dinput_keyboard_device::reset()
+{
+ memset(&m_keyboard.state, 0, sizeof(m_keyboard.state));
+}
+
+void dinput_keyboard_device::configure(input_device &device)
+{
+ // populate it
+ char defname[20];
+ for (int keynum = 0; keynum < MAX_KEYS; keynum++)
+ {
+ input_item_id itemid = keyboard_trans_table::instance().map_di_scancode_to_itemid(keynum);
+
+ // generate/fetch the name
+ snprintf(defname, std::size(defname), "Scan%03d", keynum);
+
+ // add the item to the device
+ device.add_item(
+ item_name(keynum, defname, nullptr),
+ strmakeupper(defname),
+ itemid,
+ generic_button_get_state<uint8_t>,
+ &m_keyboard.state[keynum]);
+ }
+}
+
+
+//============================================================
+// dinput_mouse_device - DirectInput mouse device
+//============================================================
+
+class dinput_mouse_device : public dinput_device
+{
+public:
+ dinput_mouse_device(
+ std::string &&name,
+ std::string &&id,
+ input_module &module,
+ Microsoft::WRL::ComPtr<IDirectInputDevice8> &&device,
+ DIDEVCAPS const &caps,
+ LPCDIDATAFORMAT format);
+
+ void poll() override;
+ void reset() override;
+ virtual void configure(input_device &device) override;
+
+private:
+ mouse_state m_mouse;
+};
+
+dinput_mouse_device::dinput_mouse_device(
+ std::string &&name,
+ std::string &&id,
+ input_module &module,
+ Microsoft::WRL::ComPtr<IDirectInputDevice8> &&device,
+ DIDEVCAPS const &caps,
+ LPCDIDATAFORMAT format) :
+ dinput_device(std::move(name), std::move(id), module, std::move(device), caps, format),
+ m_mouse({0})
+{
+ // cap the number of axes and buttons based on the format
+ m_caps.dwAxes = std::min(m_caps.dwAxes, DWORD(3));
+ m_caps.dwButtons = std::min(m_caps.dwButtons, DWORD((m_format == &c_dfDIMouse) ? 4 : 8));
+}
+
+void dinput_mouse_device::poll()
+{
+ // poll
+ if (poll_dinput(&m_mouse) == DI_OK)
+ {
+ // scale the axis data
+ m_mouse.lX *= input_device::RELATIVE_PER_PIXEL;
+ m_mouse.lY *= input_device::RELATIVE_PER_PIXEL;
+ m_mouse.lZ *= input_device::RELATIVE_PER_PIXEL;
+ }
+}
+
+void dinput_mouse_device::reset()
+{
+ memset(&m_mouse, 0, sizeof(m_mouse));
+}
+
+void dinput_mouse_device::configure(input_device &device)
+{
+ // populate the axes
+ for (int axisnum = 0; axisnum < m_caps.dwAxes; axisnum++)
+ {
+ // add to the mouse device and optionally to the gun device as well
+ device.add_item(
+ item_name(offsetof(DIMOUSESTATE, lX) + axisnum * sizeof(LONG), default_axis_name[axisnum], nullptr),
+ std::string_view(),
+ input_item_id(ITEM_ID_XAXIS + axisnum),
+ generic_axis_get_state<LONG>,
+ &m_mouse.lX + axisnum);
+ }
+
+ // populate the buttons
+ for (int butnum = 0; butnum < m_caps.dwButtons; butnum++)
+ {
+ auto offset = reinterpret_cast<uintptr_t>(&static_cast<DIMOUSESTATE *>(nullptr)->rgbButtons[butnum]);
+
+ // add to the mouse device
+ device.add_item(
+ item_name(offset, default_button_name(butnum), nullptr),
+ std::string_view(),
+ input_item_id(ITEM_ID_BUTTON1 + butnum),
+ generic_button_get_state<BYTE>,
+ &m_mouse.rgbButtons[butnum]);
+ }
+}
+
+
+//============================================================
+// dinput_module - base DirectInput module
//============================================================
class dinput_module : public input_module_impl<dinput_device, osd_common_t>, public device_enum_interface
@@ -220,7 +479,7 @@ public:
//============================================================
-// dinput_device - base directinput device
+// dinput_device - base DirectInput device
//============================================================
dinput_device::dinput_device(
@@ -282,122 +541,6 @@ std::string dinput_device::item_name(int offset, std::string_view defstring, con
//============================================================
-// dinput_keyboard_device - directinput keyboard device
-//============================================================
-
-dinput_keyboard_device::dinput_keyboard_device(
- std::string &&name,
- std::string &&id,
- input_module &module,
- Microsoft::WRL::ComPtr<IDirectInputDevice8> &&device,
- DIDEVCAPS const &caps,
- LPCDIDATAFORMAT format) :
- dinput_device(std::move(name), std::move(id), module, std::move(device), caps, format),
- m_keyboard({ { 0 } })
-{
-}
-
-void dinput_keyboard_device::poll()
-{
- // poll the DirectInput immediate state
- std::lock_guard<std::mutex> scope_lock(m_device_lock);
- poll_dinput(&m_keyboard.state);
-}
-
-void dinput_keyboard_device::reset()
-{
- memset(&m_keyboard.state, 0, sizeof(m_keyboard.state));
-}
-
-void dinput_keyboard_device::configure(input_device &device)
-{
- // populate it
- char defname[20];
- for (int keynum = 0; keynum < MAX_KEYS; keynum++)
- {
- input_item_id itemid = keyboard_trans_table::instance().map_di_scancode_to_itemid(keynum);
-
- // generate/fetch the name
- snprintf(defname, std::size(defname), "Scan%03d", keynum);
-
- // add the item to the device
- device.add_item(
- item_name(keynum, defname, nullptr),
- strmakeupper(defname),
- itemid,
- generic_button_get_state<std::uint8_t>,
- &m_keyboard.state[keynum]);
- }
-}
-
-
-//============================================================
-// dinput_mouse_device - directinput mouse device
-//============================================================
-
-dinput_mouse_device::dinput_mouse_device(
- std::string &&name,
- std::string &&id,
- input_module &module,
- Microsoft::WRL::ComPtr<IDirectInputDevice8> &&device,
- DIDEVCAPS const &caps,
- LPCDIDATAFORMAT format) :
- dinput_device(std::move(name), std::move(id), module, std::move(device), caps, format),
- m_mouse({0})
-{
- // cap the number of axes and buttons based on the format
- m_caps.dwAxes = std::min(m_caps.dwAxes, DWORD(3));
- m_caps.dwButtons = std::min(m_caps.dwButtons, DWORD((m_format == &c_dfDIMouse) ? 4 : 8));
-}
-
-void dinput_mouse_device::poll()
-{
- // poll
- if (poll_dinput(&m_mouse) == DI_OK)
- {
- // scale the axis data
- m_mouse.lX *= input_device::RELATIVE_PER_PIXEL;
- m_mouse.lY *= input_device::RELATIVE_PER_PIXEL;
- m_mouse.lZ *= input_device::RELATIVE_PER_PIXEL;
- }
-}
-
-void dinput_mouse_device::reset()
-{
- memset(&m_mouse, 0, sizeof(m_mouse));
-}
-
-void dinput_mouse_device::configure(input_device &device)
-{
- // populate the axes
- for (int axisnum = 0; axisnum < m_caps.dwAxes; axisnum++)
- {
- // add to the mouse device and optionally to the gun device as well
- device.add_item(
- item_name(offsetof(DIMOUSESTATE, lX) + axisnum * sizeof(LONG), default_axis_name[axisnum], nullptr),
- std::string_view(),
- input_item_id(ITEM_ID_XAXIS + axisnum),
- generic_axis_get_state<LONG>,
- &m_mouse.lX + axisnum);
- }
-
- // populate the buttons
- for (int butnum = 0; butnum < m_caps.dwButtons; butnum++)
- {
- auto offset = reinterpret_cast<uintptr_t>(&static_cast<DIMOUSESTATE *>(nullptr)->rgbButtons[butnum]);
-
- // add to the mouse device
- device.add_item(
- item_name(offset, default_button_name(butnum), nullptr),
- std::string_view(),
- input_item_id(ITEM_ID_BUTTON1 + butnum),
- generic_button_get_state<BYTE>,
- &m_mouse.rgbButtons[butnum]);
- }
-}
-
-
-//============================================================
// dinput_joystick_device - directinput joystick device
//============================================================
@@ -431,28 +574,57 @@ void dinput_joystick_device::poll()
// normalize axis values
for (int axisnum = 0; axisnum < 8; axisnum++)
{
- LONG *const axis = &m_joystick.state.lX + axisnum;
- *axis = normalize_absolute_axis(*axis, m_joystick.rangemin[axisnum], m_joystick.rangemax[axisnum]);
+ auto const range = m_joystick.rangemax[axisnum] - m_joystick.rangemin[axisnum];
+ if (range)
+ {
+ // assumes output range is symmetrical
+ LONG *const axis = &m_joystick.state.lX + axisnum;
+ double const offset = *axis - m_joystick.rangemin[axisnum];
+ double const scaled = offset * double(input_device::ABSOLUTE_MAX - input_device::ABSOLUTE_MIN) / double(range);
+ *axis = lround(std::clamp<double>(scaled + input_device::ABSOLUTE_MIN, input_device::ABSOLUTE_MIN, input_device::ABSOLUTE_MAX));
+ }
}
}
}
void dinput_joystick_device::configure(input_device &device)
{
+ input_device::assignment_vector assignments;
HRESULT result;
+ // get device information - it gives clues about axis usage
+ DIDEVICEINSTANCE info;
+ info.dwSize = sizeof(info);
+ result = m_device->GetDeviceInfo(&info);
+ bool hid = false;
+ uint8_t type = DI8DEVTYPE_DEVICE;
+ uint8_t subtype = 0;
+ if (result == DI_OK)
+ {
+ hid = (info.dwDevType & DIDEVTYPE_HID) != 0;
+ type = info.dwDevType & 0x00ff;
+ subtype = (info.dwDevType >> 8) & 0x00ff;
+ osd_printf_verbose(
+ "DirectInput: Device type=0x%02X subtype=0x%02X HID=%u\n",
+ type,
+ subtype,
+ hid ? "yes" : "no");
+ }
+
// turn off deadzone; we do our own calculations
result = dinput_api_helper::set_dword_property(m_device, DIPROP_DEADZONE, 0, DIPH_DEVICE, 0);
- if (result != DI_OK && result != DI_PROPNOEFFECT)
+ if ((result != DI_OK) && (result != DI_PROPNOEFFECT))
osd_printf_warning("DirectInput: Unable to reset deadzone for joystick %s.\n", name());
// turn off saturation; we do our own calculations
result = dinput_api_helper::set_dword_property(m_device, DIPROP_SATURATION, 0, DIPH_DEVICE, 10000);
- if (result != DI_OK && result != DI_PROPNOEFFECT)
+ if ((result != DI_OK) && (result != DI_PROPNOEFFECT))
osd_printf_warning("DirectInput: Unable to reset saturation for joystick %s.\n", name());
// populate the axes
- for (uint32_t axisnum = 0, axiscount = 0; axiscount < m_caps.dwAxes && axisnum < 8; axisnum++)
+ input_item_id axisitems[8];
+ std::fill(std::begin(axisitems), std::end(axisitems), ITEM_ID_INVALID);
+ for (uint32_t axisnum = 0, axiscount = 0; (axiscount < m_caps.dwAxes) && (axisnum < 8); axisnum++)
{
// fetch the range of this axis
DIPROPRANGE dipr;
@@ -463,6 +635,7 @@ void dinput_joystick_device::configure(input_device &device)
result = m_device->GetProperty(DIPROP_RANGE, &dipr.diph);
if (result != DI_OK)
{
+ // this is normal when axes are skipped, e.g. X/Y/Z present, rX/rY absent, rZ present
osd_printf_verbose("DirectInput: Unable to get properties for joystick %s axis %u.\n", name(), axisnum);
continue;
}
@@ -471,7 +644,7 @@ void dinput_joystick_device::configure(input_device &device)
m_joystick.rangemax[axisnum] = dipr.lMax;
// populate the item description as well
- device.add_item(
+ axisitems[axisnum] = device.add_item(
item_name(offsetof(DIJOYSTATE2, lX) + axisnum * sizeof(LONG), default_axis_name[axisnum], nullptr),
std::string_view(),
input_item_id(ITEM_ID_XAXIS + axisnum),
@@ -481,38 +654,114 @@ void dinput_joystick_device::configure(input_device &device)
axiscount++;
}
- // populate the POVs
+ // take a guess at which axes might be pedals depending on type and remap onto negative half of range
+ input_item_id pedalitems[3] = { ITEM_ID_INVALID, ITEM_ID_INVALID, ITEM_ID_INVALID };
+ if (DI8DEVTYPE_FLIGHT == type)
+ {
+ // Rx/Ry are often used for brakes
+ bool const rxpedal = (ITEM_ID_INVALID != axisitems[3]) && !m_joystick.rangemin[3] && (0 < m_joystick.rangemax[3]);
+ bool const rypedal = (ITEM_ID_INVALID != axisitems[4]) && !m_joystick.rangemin[4] && (0 < m_joystick.rangemax[4]);
+ if (rxpedal && rypedal)
+ {
+ pedalitems[0] = axisitems[3];
+ m_joystick.rangemin[3] = m_joystick.rangemax[3];
+ m_joystick.rangemax[3] = -m_joystick.rangemax[3];
+
+ pedalitems[1] = axisitems[4];
+ m_joystick.rangemin[4] = m_joystick.rangemax[4];
+ m_joystick.rangemax[4] = -m_joystick.rangemax[4];
+ }
+ }
+ else if (DI8DEVTYPE_DRIVING == type)
+ {
+ bool const ypedal = (ITEM_ID_INVALID != axisitems[1]) && !m_joystick.rangemin[1] && (0 < m_joystick.rangemax[1]);
+ bool const rxpedal = (ITEM_ID_INVALID != axisitems[3]) && !m_joystick.rangemin[3] && (0 < m_joystick.rangemax[3]);
+ bool const rypedal = (ITEM_ID_INVALID != axisitems[4]) && !m_joystick.rangemin[4] && (0 < m_joystick.rangemax[4]);
+ bool const rzpedal = (ITEM_ID_INVALID != axisitems[5]) && !m_joystick.rangemin[5] && (0 < m_joystick.rangemax[5]);
+ bool const s0pedal = (ITEM_ID_INVALID != axisitems[6]) && !m_joystick.rangemin[6] && (0 < m_joystick.rangemax[6]);
+ if (DI8DEVTYPEDRIVING_DUALPEDALS == subtype)
+ {
+ // dual pedals are usually Y and Rz
+ if (ypedal && rzpedal)
+ {
+ pedalitems[0] = axisitems[1];
+ m_joystick.rangemin[1] = m_joystick.rangemax[1];
+ m_joystick.rangemax[1] = -m_joystick.rangemax[1];
+
+ pedalitems[1] = axisitems[5];
+ m_joystick.rangemin[5] = m_joystick.rangemax[5];
+ m_joystick.rangemax[5] = -m_joystick.rangemax[5];
+ }
+ }
+ else if (DI8DEVTYPEDRIVING_THREEPEDALS == subtype)
+ {
+ // triple pedals may be Y, Rz and slider 0, or Rx, Ry and Rz
+ if (ypedal && rzpedal && s0pedal)
+ {
+ pedalitems[0] = axisitems[1];
+ m_joystick.rangemin[1] = m_joystick.rangemax[1];
+ m_joystick.rangemax[1] = -m_joystick.rangemax[1];
+
+ pedalitems[1] = axisitems[5];
+ m_joystick.rangemin[5] = m_joystick.rangemax[5];
+ m_joystick.rangemax[5] = -m_joystick.rangemax[5];
+
+ pedalitems[2] = axisitems[6];
+ m_joystick.rangemin[6] = m_joystick.rangemax[6];
+ m_joystick.rangemax[6] = -m_joystick.rangemax[6];
+ }
+ else if (rxpedal && rypedal && rzpedal)
+ {
+ pedalitems[0] = axisitems[3];
+ m_joystick.rangemin[3] = m_joystick.rangemax[3];
+ m_joystick.rangemax[3] = -m_joystick.rangemax[3];
+
+ pedalitems[1] = axisitems[4];
+ m_joystick.rangemin[4] = m_joystick.rangemax[4];
+ m_joystick.rangemax[4] = -m_joystick.rangemax[4];
+
+ pedalitems[2] = axisitems[5];
+ m_joystick.rangemin[5] = m_joystick.rangemax[5];
+ m_joystick.rangemax[5] = -m_joystick.rangemax[5];
+ }
+ }
+ }
+
+ // populate the POV hats
+ input_item_id povitems[4][4];
+ for (auto &pov : povitems)
+ std::fill(std::begin(pov), std::end(pov), ITEM_ID_INVALID);
for (uint32_t povnum = 0; povnum < m_caps.dwPOVs; povnum++)
{
// left
- device.add_item(
+ povitems[povnum][0] = device.add_item(
item_name(offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), "Left"),
std::string_view(),
- input_item_id(povnum * 4 + ITEM_ID_HAT1LEFT),
+ input_item_id((povnum * 4) + ITEM_ID_HAT1LEFT),
&dinput_joystick_device::pov_get_state,
reinterpret_cast<void *>(uintptr_t(povnum * 4 + POVDIR_LEFT)));
// right
- device.add_item(
+ povitems[povnum][1] = device.add_item(
item_name(offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), "Right"),
std::string_view(),
- input_item_id(povnum * 4 + ITEM_ID_HAT1RIGHT),
+ input_item_id((povnum * 4) + ITEM_ID_HAT1RIGHT),
&dinput_joystick_device::pov_get_state,
reinterpret_cast<void *>(uintptr_t(povnum * 4 + POVDIR_RIGHT)));
// up
- device.add_item(
+ povitems[povnum][2] = device.add_item(
item_name(offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), "Up"),
std::string_view(),
- input_item_id(povnum * 4 + ITEM_ID_HAT1UP),
+ input_item_id((povnum * 4) + ITEM_ID_HAT1UP),
&dinput_joystick_device::pov_get_state,
reinterpret_cast<void *>(uintptr_t(povnum * 4 + POVDIR_UP)));
// down
- device.add_item(
+ povitems[povnum][3] = device.add_item(
item_name(offsetof(DIJOYSTATE2, rgdwPOV) + povnum * sizeof(DWORD), default_pov_name(povnum), "Down"),
std::string_view(),
- input_item_id(povnum * 4 + ITEM_ID_HAT1DOWN),
+ input_item_id((povnum * 4) + ITEM_ID_HAT1DOWN),
&dinput_joystick_device::pov_get_state,
reinterpret_cast<void *>(uintptr_t(povnum * 4 + POVDIR_DOWN)));
}
@@ -530,13 +779,386 @@ void dinput_joystick_device::configure(input_device &device)
else
itemid = ITEM_ID_OTHER_SWITCH;
- device.add_item(
+ input_item_id const actual = device.add_item(
item_name(offset, default_button_name(butnum), nullptr),
std::string_view(),
itemid,
generic_button_get_state<BYTE>,
&m_joystick.state.rgbButtons[butnum]);
+
+ // there are sixteen action button types
+ if (butnum < 16)
+ {
+ input_seq const seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, actual));
+ assignments.emplace_back(ioport_type(IPT_BUTTON1 + butnum), SEQ_TYPE_STANDARD, seq);
+
+ // assign the first few buttons to UI actions and pedals
+ // TODO: don't map pedals for driving controls that have them present
+ switch (butnum)
+ {
+ case 0:
+ assignments.emplace_back(IPT_PEDAL, SEQ_TYPE_INCREMENT, seq);
+ assignments.emplace_back(IPT_UI_SELECT, SEQ_TYPE_STANDARD, seq);
+ break;
+ case 1:
+ assignments.emplace_back(IPT_PEDAL2, SEQ_TYPE_INCREMENT, seq);
+ assignments.emplace_back((3 > m_caps.dwButtons) ? IPT_UI_CLEAR : IPT_UI_BACK, SEQ_TYPE_STANDARD, seq);
+ break;
+ case 2:
+ assignments.emplace_back(IPT_PEDAL3, SEQ_TYPE_INCREMENT, seq);
+ assignments.emplace_back(IPT_UI_CLEAR, SEQ_TYPE_STANDARD, seq);
+ break;
+ case 3:
+ assignments.emplace_back(IPT_UI_HELP, SEQ_TYPE_STANDARD, seq);
+ break;
+ }
+ }
+ }
+
+ // add default assignments depending on type
+ if (DI8DEVTYPE_FLIGHT == type)
+ {
+ if (((ITEM_ID_INVALID == axisitems[0]) || (ITEM_ID_INVALID == axisitems[1])) && (1 <= m_caps.dwPOVs))
+ {
+ // X or Y missing, fall back to using POV hat for navigation
+ add_directional_assignments(
+ assignments,
+ axisitems[0],
+ axisitems[1],
+ povitems[0][0],
+ povitems[0][1],
+ povitems[0][2],
+ povitems[0][3]);
+
+ // try using throttle for zoom/focus
+ if (ITEM_ID_INVALID != axisitems[2])
+ {
+ input_seq const negseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, axisitems[2]));
+ input_seq const posseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, axisitems[2]));
+ if (ITEM_ID_INVALID != axisitems[5])
+ {
+ assignments.emplace_back(IPT_UI_ZOOM_IN, SEQ_TYPE_STANDARD, posseq);
+ assignments.emplace_back(IPT_UI_ZOOM_OUT, SEQ_TYPE_STANDARD, negseq);
+ assignments.emplace_back(IPT_UI_FOCUS_PREV, SEQ_TYPE_STANDARD, negseq);
+ assignments.emplace_back(IPT_UI_FOCUS_NEXT, SEQ_TYPE_STANDARD, posseq);
+ }
+ else
+ {
+ assignments.emplace_back(IPT_UI_PREV_GROUP, SEQ_TYPE_STANDARD, posseq);
+ assignments.emplace_back(IPT_UI_NEXT_GROUP, SEQ_TYPE_STANDARD, negseq);
+ }
+ }
+
+ // try using twist/rudder for next/previous group
+ if (ITEM_ID_INVALID != axisitems[5])
+ {
+ input_seq const negseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, axisitems[5]));
+ input_seq const posseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, axisitems[5]));
+ assignments.emplace_back(IPT_UI_PREV_GROUP, SEQ_TYPE_STANDARD, negseq);
+ assignments.emplace_back(IPT_UI_NEXT_GROUP, SEQ_TYPE_STANDARD, posseq);
+ }
+ }
+ else
+ {
+ // only use stick for primary navigation/movement
+ add_directional_assignments(
+ assignments,
+ axisitems[0],
+ axisitems[1],
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID);
+
+ // try using hat for secondary navigation functions
+ if (1 <= m_caps.dwPOVs)
+ {
+ input_seq const leftseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, povitems[0][0]));
+ input_seq const rightseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, povitems[0][1]));
+ input_seq const upseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, povitems[0][2]));
+ input_seq const downseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, povitems[0][3]));
+ if ((ITEM_ID_INVALID != axisitems[2]) || (ITEM_ID_INVALID != axisitems[5]))
+ {
+ assignments.emplace_back(IPT_UI_PREV_GROUP, SEQ_TYPE_STANDARD, leftseq);
+ assignments.emplace_back(IPT_UI_NEXT_GROUP, SEQ_TYPE_STANDARD, rightseq);
+ assignments.emplace_back(IPT_UI_PAGE_UP, SEQ_TYPE_STANDARD, upseq);
+ assignments.emplace_back(IPT_UI_PAGE_DOWN, SEQ_TYPE_STANDARD, downseq);
+ }
+ else
+ {
+ assignments.emplace_back(IPT_UI_FOCUS_PREV, SEQ_TYPE_STANDARD, leftseq);
+ assignments.emplace_back(IPT_UI_FOCUS_NEXT, SEQ_TYPE_STANDARD, rightseq);
+ assignments.emplace_back(IPT_UI_ZOOM_OUT, SEQ_TYPE_STANDARD, leftseq);
+ assignments.emplace_back(IPT_UI_ZOOM_IN, SEQ_TYPE_STANDARD, rightseq);
+ assignments.emplace_back(IPT_UI_PREV_GROUP, SEQ_TYPE_STANDARD, upseq);
+ assignments.emplace_back(IPT_UI_NEXT_GROUP, SEQ_TYPE_STANDARD, downseq);
+ }
+ }
+
+ // try using throttle for zoom
+ if (ITEM_ID_INVALID != axisitems[2])
+ {
+ input_seq const negseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, axisitems[2]));
+ input_seq const posseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, axisitems[2]));
+ if ((1 <= m_caps.dwPOVs) || (ITEM_ID_INVALID != axisitems[5]))
+ {
+ assignments.emplace_back(IPT_UI_ZOOM_IN, SEQ_TYPE_STANDARD, posseq);
+ assignments.emplace_back(IPT_UI_ZOOM_OUT, SEQ_TYPE_STANDARD, negseq);
+ if ((1 > m_caps.dwPOVs) || (ITEM_ID_INVALID == axisitems[5]))
+ {
+ assignments.emplace_back(IPT_UI_FOCUS_PREV, SEQ_TYPE_STANDARD, negseq);
+ assignments.emplace_back(IPT_UI_FOCUS_NEXT, SEQ_TYPE_STANDARD, posseq);
+ }
+ }
+ else
+ {
+ assignments.emplace_back(IPT_UI_PREV_GROUP, SEQ_TYPE_STANDARD, posseq);
+ assignments.emplace_back(IPT_UI_NEXT_GROUP, SEQ_TYPE_STANDARD, negseq);
+ }
+ }
+
+ // try using twist/rudder for focus next/previous
+ if (ITEM_ID_INVALID != axisitems[5])
+ {
+ input_seq const negseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, axisitems[5]));
+ input_seq const posseq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, axisitems[5]));
+ if (1 <= m_caps.dwPOVs)
+ {
+ assignments.emplace_back(IPT_UI_FOCUS_PREV, SEQ_TYPE_STANDARD, negseq);
+ assignments.emplace_back(IPT_UI_FOCUS_NEXT, SEQ_TYPE_STANDARD, posseq);
+ if (ITEM_ID_INVALID == axisitems[2])
+ {
+ assignments.emplace_back(IPT_UI_ZOOM_IN, SEQ_TYPE_STANDARD, posseq);
+ assignments.emplace_back(IPT_UI_ZOOM_OUT, SEQ_TYPE_STANDARD, negseq);
+ }
+ }
+ else
+ {
+ assignments.emplace_back(IPT_UI_PREV_GROUP, SEQ_TYPE_STANDARD, negseq);
+ assignments.emplace_back(IPT_UI_NEXT_GROUP, SEQ_TYPE_STANDARD, posseq);
+ }
+ }
+ }
+
+ // Z or slider 0 is usually the throttle - use one of them for joystick Z
+ add_assignment(
+ assignments,
+ IPT_AD_STICK_Z,
+ SEQ_TYPE_STANDARD,
+ ITEM_CLASS_ABSOLUTE,
+ ITEM_MODIFIER_NONE,
+ { axisitems[2], axisitems[6] });
+
+ // use Z for the first two pedals if present
+ if (ITEM_ID_INVALID != axisitems[2])
+ {
+ // TODO: use Rx/Ry as well if they appear to be brakes
+ input_seq const pedal1seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_POS, axisitems[2]));
+ input_seq const pedal2seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, axisitems[2]));
+ assignments.emplace_back(IPT_PEDAL, SEQ_TYPE_STANDARD, pedal1seq);
+ assignments.emplace_back(IPT_PEDAL2, SEQ_TYPE_STANDARD, pedal2seq);
+ }
+ }
+ else if (DI8DEVTYPE_DRIVING == type)
+ {
+ // use the wheel and D-pad for navigation and directional controls
+ add_directional_assignments(
+ assignments,
+ axisitems[0],
+ ITEM_ID_INVALID,
+ povitems[0][0],
+ povitems[0][1],
+ povitems[0][2],
+ povitems[0][3]);
+
+ // check subtype to determine how pedals should be assigned
+ if (DI8DEVTYPEDRIVING_COMBINEDPEDALS == subtype)
+ {
+ if (ITEM_ID_INVALID != axisitems[1])
+ {
+ // put first two pedals on opposite sides of Y axis
+ assignments.emplace_back(
+ IPT_PEDAL,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_POS, axisitems[1])));
+ assignments.emplace_back(
+ IPT_PEDAL2,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, axisitems[1])));
+
+ // use for previous/next group as well
+ assignments.emplace_back(
+ IPT_UI_NEXT_GROUP,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, axisitems[1])));
+ assignments.emplace_back(
+ IPT_UI_PREV_GROUP,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, axisitems[1])));
+ }
+ }
+ else
+ {
+ // see if we have individual pedals
+ if (ITEM_ID_INVALID != pedalitems[0])
+ {
+ assignments.emplace_back(
+ IPT_PEDAL,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, pedalitems[0])));
+ assignments.emplace_back(
+ IPT_UI_NEXT_GROUP,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, pedalitems[0])));
+ }
+ if (ITEM_ID_INVALID != pedalitems[1])
+ {
+ assignments.emplace_back(
+ IPT_PEDAL2,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, pedalitems[1])));
+ assignments.emplace_back(
+ IPT_UI_PREV_GROUP,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, pedalitems[1])));
+ }
+ if (ITEM_ID_INVALID != pedalitems[2])
+ {
+ assignments.emplace_back(
+ IPT_PEDAL3,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_ABSOLUTE, ITEM_MODIFIER_NEG, pedalitems[2])));
+ assignments.emplace_back(
+ IPT_UI_FOCUS_NEXT,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, pedalitems[2])));
+ }
+ }
+ }
+ else
+ {
+ // assume this is a gamepad - see if it looks like it has dual analog sticks
+ input_item_id stickaxes[2][2] = {
+ { axisitems[0], axisitems[1] },
+ { ITEM_ID_INVALID, ITEM_ID_INVALID } };
+ input_item_id pedalaxis = ITEM_ID_INVALID;
+ if ((ITEM_ID_INVALID != axisitems[3]) && (ITEM_ID_INVALID != axisitems[4]))
+ {
+ // assume Rx/Ry are right stick and Z is triggers if present
+ stickaxes[1][0] = axisitems[3];
+ stickaxes[1][1] = axisitems[4];
+ pedalaxis = axisitems[2];
+ add_twin_stick_assignments(
+ assignments,
+ stickaxes[0][0],
+ stickaxes[0][1],
+ stickaxes[1][0],
+ stickaxes[1][1],
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID);
+ }
+ else if ((ITEM_ID_INVALID != axisitems[2]) && (ITEM_ID_INVALID != axisitems[5]))
+ {
+ // assume Z/Rz are right stick
+ stickaxes[1][0] = axisitems[2];
+ stickaxes[1][1] = axisitems[5];
+ add_twin_stick_assignments(
+ assignments,
+ stickaxes[0][0],
+ stickaxes[0][1],
+ stickaxes[1][0],
+ stickaxes[1][1],
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID,
+ ITEM_ID_INVALID);
+ }
+ else
+ {
+ // if Z is present, use it as secondary Y
+ stickaxes[1][1] = axisitems[2];
+ }
+
+ // try to find a "complete" stick for primary movement controls
+ input_item_id diraxis[2][2];
+ choose_primary_stick(diraxis, stickaxes[0][0], stickaxes[0][1], stickaxes[1][0], stickaxes[1][1]);
+ add_directional_assignments(
+ assignments,
+ diraxis[0][0],
+ diraxis[0][1],
+ povitems[0][0],
+ povitems[0][1],
+ povitems[0][2],
+ povitems[0][3]);
+
+ // assign a secondary stick axis to joystick Z
+ add_assignment(
+ assignments,
+ IPT_AD_STICK_Z,
+ SEQ_TYPE_STANDARD,
+ ITEM_CLASS_ABSOLUTE,
+ ITEM_MODIFIER_NONE,
+ { diraxis[1][1], diraxis[1][0] });
+
+ // try to find a suitable axis to use for the first two pedals
+ add_assignment(
+ assignments,
+ IPT_PEDAL,
+ SEQ_TYPE_STANDARD,
+ ITEM_CLASS_ABSOLUTE,
+ ITEM_MODIFIER_NEG,
+ { pedalaxis, diraxis[1][1], diraxis[0][1] });
+ add_assignment(
+ assignments,
+ IPT_PEDAL2,
+ SEQ_TYPE_STANDARD,
+ ITEM_CLASS_ABSOLUTE,
+ ITEM_MODIFIER_POS,
+ { pedalaxis, diraxis[1][1], diraxis[0][1] });
+
+ // try to choose an axis for previous/next group
+ if (ITEM_ID_INVALID != pedalaxis)
+ {
+ // this is reversed because right trigger is negative direction
+ assignments.emplace_back(
+ IPT_UI_PREV_GROUP,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_POS, pedalaxis)));
+ assignments.emplace_back(
+ IPT_UI_NEXT_GROUP,
+ SEQ_TYPE_STANDARD,
+ input_seq(make_code(ITEM_CLASS_SWITCH, ITEM_MODIFIER_NEG, pedalaxis)));
+ pedalaxis = ITEM_ID_INVALID;
+ }
+ else if (consume_axis_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, diraxis[1][1]))
+ {
+ // took secondary Y
+ }
+ else if (consume_axis_pair(assignments, IPT_UI_PREV_GROUP, IPT_UI_NEXT_GROUP, diraxis[1][0]))
+ {
+ // took secondary X
+ }
+
+ // use secondary Y for page up/down if available
+ consume_axis_pair(assignments, IPT_UI_PAGE_UP, IPT_UI_PAGE_DOWN, diraxis[1][1]);
+
+ // put focus previous/next and zoom on secondary X if available
+ add_axis_pair_assignment(assignments, IPT_UI_FOCUS_PREV, IPT_UI_FOCUS_NEXT, diraxis[1][0]);
+ add_axis_pair_assignment(assignments, IPT_UI_ZOOM_OUT, IPT_UI_ZOOM_IN, diraxis[1][0]);
}
+
+ // set default assignments
+ device.set_default_assignments(std::move(assignments));
}
int32_t dinput_joystick_device::pov_get_state(void *device_internal, void *item_internal)
@@ -580,7 +1202,7 @@ dinput_api_helper::~dinput_api_helper()
int dinput_api_helper::initialize()
{
- HRESULT result = DirectInput8Create(GetModuleHandleUni(), DIRECTINPUT_VERSION, IID_IDirectInput8, reinterpret_cast<void **>(m_dinput.GetAddressOf()), nullptr);
+ HRESULT result = DirectInput8Create(GetModuleHandleUni(), DIRECTINPUT_VERSION, IID_IDirectInput8, &m_dinput, nullptr);
if (result != DI_OK)
{
return result;
@@ -683,6 +1305,18 @@ std::pair<Microsoft::WRL::ComPtr<IDirectInputDevice8>, LPCDIDATAFORMAT> dinput_a
return std::make_pair(std::move(device), format);
}
+
+std::string dinput_api_helper::make_id(LPCDIDEVICEINSTANCE instance)
+{
+ // use name, product GUID and instance GUID as identifier
+ return
+ text::from_tstring(instance->tszInstanceName) +
+ " product_" +
+ guid_to_string(instance->guidProduct) +
+ " instance_" +
+ guid_to_string(instance->guidInstance);
+}
+
} // namespace osd