1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
|
// Copyright 2010 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "InputCommon/ControllerInterface/XInput/XInput.h"
#ifndef XINPUT_GAMEPAD_GUIDE
#define XINPUT_GAMEPAD_GUIDE 0x0400
#endif
namespace ciface::XInput
{
struct ButtonDef
{
const char* name;
WORD bitmask;
};
static constexpr std::array<ButtonDef, 15> named_buttons{{
{"Button A", XINPUT_GAMEPAD_A},
{"Button B", XINPUT_GAMEPAD_B},
{"Button X", XINPUT_GAMEPAD_X},
{"Button Y", XINPUT_GAMEPAD_Y},
{"Pad N", XINPUT_GAMEPAD_DPAD_UP},
{"Pad S", XINPUT_GAMEPAD_DPAD_DOWN},
{"Pad W", XINPUT_GAMEPAD_DPAD_LEFT},
{"Pad E", XINPUT_GAMEPAD_DPAD_RIGHT},
{"Start", XINPUT_GAMEPAD_START},
{"Back", XINPUT_GAMEPAD_BACK},
{"Shoulder L", XINPUT_GAMEPAD_LEFT_SHOULDER},
{"Shoulder R", XINPUT_GAMEPAD_RIGHT_SHOULDER},
{"Guide", XINPUT_GAMEPAD_GUIDE},
{"Thumb L", XINPUT_GAMEPAD_LEFT_THUMB},
{"Thumb R", XINPUT_GAMEPAD_RIGHT_THUMB},
}};
static constexpr std::array named_triggers{"Trigger L", "Trigger R"};
static constexpr std::array named_axes{"Left X", "Left Y", "Right X", "Right Y"};
static constexpr std::array named_motors{"Motor L", "Motor R"};
class Button final : public Core::Device::Input
{
public:
Button(u8 index, const WORD& buttons) : m_buttons(buttons), m_index(index) {}
std::string GetName() const override { return named_buttons[m_index].name; }
ControlState GetState() const override
{
return (m_buttons & named_buttons[m_index].bitmask) > 0;
}
private:
const WORD& m_buttons;
const u8 m_index;
};
class Axis final : public Core::Device::Input
{
public:
Axis(u8 index, const SHORT& axis, SHORT range) : m_axis(axis), m_range(range), m_index(index) {}
std::string GetName() const override
{
return std::string(named_axes[m_index]) + (m_range < 0 ? '-' : '+');
}
ControlState GetState() const override { return ControlState(m_axis) / m_range; }
private:
const SHORT& m_axis;
const SHORT m_range;
const u8 m_index;
};
class Trigger final : public Core::Device::Input
{
public:
Trigger(u8 index, const BYTE& trigger, BYTE range)
: m_trigger(trigger), m_range(range), m_index(index)
{
}
std::string GetName() const override { return named_triggers[m_index]; }
ControlState GetState() const override { return ControlState(m_trigger) / m_range; }
private:
const BYTE& m_trigger;
const BYTE m_range;
const u8 m_index;
};
class Motor final : public Core::Device::Output
{
public:
Motor(u8 index, Device* parent, WORD& motor, WORD range)
: m_motor(motor), m_range(range), m_index(index), m_parent(parent)
{
}
std::string GetName() const override { return named_motors[m_index]; }
void SetState(ControlState state) override
{
const auto old_value = m_motor;
m_motor = (WORD)(state * m_range);
// Only update if the state changed.
if (m_motor != old_value)
m_parent->UpdateMotors();
}
private:
WORD& m_motor;
const WORD m_range;
const u8 m_index;
Device* m_parent;
};
class Battery final : public Core::Device::Input
{
public:
Battery(const ControlState* level) : m_level(*level) {}
std::string GetName() const override { return "Battery"; }
ControlState GetState() const override { return m_level; }
bool IsDetectable() const override { return false; }
private:
const ControlState& m_level;
};
static HMODULE hXInput = nullptr;
typedef decltype(&XInputGetCapabilities) XInputGetCapabilities_t;
typedef decltype(&XInputSetState) XInputSetState_t;
typedef decltype(&XInputGetState) XInputGetState_t;
typedef decltype(&XInputGetBatteryInformation) XInputGetBatteryInformation_t;
static XInputGetCapabilities_t PXInputGetCapabilities = nullptr;
static XInputSetState_t PXInputSetState = nullptr;
static XInputGetState_t PXInputGetState = nullptr;
static XInputGetBatteryInformation_t PXInputGetBatteryInformation = nullptr;
static bool s_have_guide_button = false;
void Init()
{
if (!hXInput)
{
// Try for the most recent version we were compiled against (will only work if running on Win8+)
hXInput = ::LoadLibrary(XINPUT_DLL);
if (!hXInput)
{
// Drop back to DXSDK June 2010 version. Requires DX June 2010 redist.
hXInput = ::LoadLibrary(TEXT("xinput1_3.dll"));
if (!hXInput)
{
return;
}
}
PXInputGetCapabilities =
(XInputGetCapabilities_t)::GetProcAddress(hXInput, "XInputGetCapabilities");
PXInputSetState = (XInputSetState_t)::GetProcAddress(hXInput, "XInputSetState");
PXInputGetBatteryInformation =
(XInputGetBatteryInformation_t)::GetProcAddress(hXInput, "XInputGetBatteryInformation");
// Ordinal 100 is the same as XInputGetState, except it doesn't dummy out the guide
// button info. Try loading it and fall back if needed.
PXInputGetState = (XInputGetState_t)::GetProcAddress(hXInput, (LPCSTR)100);
s_have_guide_button = PXInputGetState != nullptr;
if (!PXInputGetState)
PXInputGetState = (XInputGetState_t)::GetProcAddress(hXInput, "XInputGetState");
if (!PXInputGetCapabilities || !PXInputSetState || !PXInputGetState ||
!PXInputGetBatteryInformation)
{
::FreeLibrary(hXInput);
hXInput = nullptr;
return;
}
}
}
void PopulateDevices()
{
if (!hXInput)
return;
g_controller_interface.RemoveDevice([](const auto* dev) { return dev->GetSource() == "XInput"; });
XINPUT_CAPABILITIES caps;
for (int i = 0; i != 4; ++i)
if (ERROR_SUCCESS == PXInputGetCapabilities(i, 0, &caps))
g_controller_interface.AddDevice(std::make_shared<Device>(caps, i));
}
void DeInit()
{
if (hXInput)
{
::FreeLibrary(hXInput);
hXInput = nullptr;
}
}
Device::Device(const XINPUT_CAPABILITIES& caps, u8 index) : m_subtype(caps.SubType), m_index(index)
{
// XInputGetCaps can be broken on some devices, so we'll just ignore it
// and assume all gamepad + vibration capabilities are supported
// Buttons.
for (size_t i = 0; i != size(named_buttons); ++i)
{
// Only add guide button if we have the 100 ordinal XInputGetState.
if (named_buttons[i].bitmask == XINPUT_GAMEPAD_GUIDE && !s_have_guide_button)
continue;
AddInput(new Button(u8(i), m_state_in.Gamepad.wButtons));
}
// Triggers.
for (size_t i = 0; i != size(named_triggers); ++i)
AddInput(new Trigger(u8(i), (&m_state_in.Gamepad.bLeftTrigger)[i], 255));
// Axes.
for (size_t i = 0; i != size(named_axes); ++i)
{
const SHORT& ax = (&m_state_in.Gamepad.sThumbLX)[i];
// Each axis gets a negative and a positive input instance associated with it.
AddInput(new Axis(u8(i), ax, -32768));
AddInput(new Axis(u8(i), ax, 32767));
}
// Rumble motors.
for (size_t i = 0; i != size(named_motors); ++i)
AddOutput(new Motor(u8(i), this, (&m_state_out.wLeftMotorSpeed)[i], 65535));
AddInput(new Battery(&m_battery_level));
}
std::string Device::GetName() const
{
switch (m_subtype)
{
case XINPUT_DEVSUBTYPE_GAMEPAD:
return "Gamepad";
case XINPUT_DEVSUBTYPE_WHEEL:
return "Wheel";
case XINPUT_DEVSUBTYPE_ARCADE_STICK:
return "Arcade Stick";
case XINPUT_DEVSUBTYPE_FLIGHT_STICK:
return "Flight Stick";
case XINPUT_DEVSUBTYPE_DANCE_PAD:
return "Dance Pad";
case XINPUT_DEVSUBTYPE_GUITAR:
return "Guitar";
case XINPUT_DEVSUBTYPE_DRUM_KIT:
return "Drum Kit";
default:
return "Device";
}
}
std::string Device::GetSource() const
{
return "XInput";
}
Core::DeviceRemoval Device::UpdateInput()
{
PXInputGetState(m_index, &m_state_in);
XINPUT_BATTERY_INFORMATION battery_info = {};
if (SUCCEEDED(PXInputGetBatteryInformation(m_index, BATTERY_DEVTYPE_GAMEPAD, &battery_info)))
{
switch (battery_info.BatteryType)
{
case BATTERY_TYPE_DISCONNECTED:
case BATTERY_TYPE_UNKNOWN:
m_battery_level = 0;
break;
case BATTERY_TYPE_WIRED:
m_battery_level = BATTERY_INPUT_MAX_VALUE;
break;
default:
m_battery_level =
battery_info.BatteryLevel / ControlState(BATTERY_LEVEL_FULL) * BATTERY_INPUT_MAX_VALUE;
break;
}
}
return Core::DeviceRemoval::Keep;
}
void Device::UpdateMotors()
{
PXInputSetState(m_index, &m_state_out);
}
std::optional<int> Device::GetPreferredId() const
{
return m_index;
}
} // namespace ciface::XInput
|