From 86e874516949d44696648f1be9208b1e24e27d39 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sat, 8 Feb 2020 15:02:54 -0600 Subject: [PATCH 1/2] InputCommon: Expose XInput battery level as an input. --- .../ControllerInterface/XInput/XInput.cpp | 39 ++++++++++++++++++- .../ControllerInterface/XInput/XInput.h | 3 +- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/Source/Core/InputCommon/ControllerInterface/XInput/XInput.cpp b/Source/Core/InputCommon/ControllerInterface/XInput/XInput.cpp index cda20a08db..6044c0bfd5 100644 --- a/Source/Core/InputCommon/ControllerInterface/XInput/XInput.cpp +++ b/Source/Core/InputCommon/ControllerInterface/XInput/XInput.cpp @@ -10,6 +10,17 @@ namespace ciface::XInput { +class Battery : 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; } + +private: + const ControlState& m_level; +}; + static const struct { const char* const name; @@ -41,10 +52,12 @@ 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 haveGuideButton = false; @@ -67,6 +80,8 @@ void Init() 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. @@ -76,7 +91,8 @@ void Init() else PXInputGetState = (XInputGetState_t)::GetProcAddress(hXInput, "XInputGetState"); - if (!PXInputGetCapabilities || !PXInputSetState || !PXInputGetState) + if (!PXInputGetCapabilities || !PXInputSetState || !PXInputGetState || + !PXInputGetBatteryInformation) { ::FreeLibrary(hXInput); hXInput = nullptr; @@ -142,7 +158,7 @@ Device::Device(const XINPUT_CAPABILITIES& caps, u8 index) : m_subtype(caps.SubTy AddOutput(new Motor(i, this, (&m_state_out.wLeftMotorSpeed)[i], 65535)); } - ZeroMemory(&m_state_in, sizeof(m_state_in)); + AddInput(new Battery(&m_battery_level)); } std::string Device::GetName() const @@ -178,6 +194,25 @@ std::string Device::GetSource() const void 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; + } + } } void Device::UpdateMotors() diff --git a/Source/Core/InputCommon/ControllerInterface/XInput/XInput.h b/Source/Core/InputCommon/ControllerInterface/XInput/XInput.h index 3c543fb18f..c64a5c33c1 100644 --- a/Source/Core/InputCommon/ControllerInterface/XInput/XInput.h +++ b/Source/Core/InputCommon/ControllerInterface/XInput/XInput.h @@ -97,9 +97,10 @@ public: void UpdateMotors(); private: - XINPUT_STATE m_state_in; + XINPUT_STATE m_state_in{}; XINPUT_VIBRATION m_state_out{}; XINPUT_VIBRATION m_current_state_out{}; + ControlState m_battery_level{}; const BYTE m_subtype; const u8 m_index; }; From e7400cafd2c3c708444cbc3dddaab872d0959d89 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sat, 8 Feb 2020 15:45:01 -0600 Subject: [PATCH 2/2] InputCommon: XInput cleanups. --- .../ControllerInterface/XInput/XInput.cpp | 233 ++++++++++-------- .../ControllerInterface/XInput/XInput.h | 72 +----- 2 files changed, 134 insertions(+), 171 deletions(-) diff --git a/Source/Core/InputCommon/ControllerInterface/XInput/XInput.cpp b/Source/Core/InputCommon/ControllerInterface/XInput/XInput.cpp index 6044c0bfd5..14462165fa 100644 --- a/Source/Core/InputCommon/ControllerInterface/XInput/XInput.cpp +++ b/Source/Core/InputCommon/ControllerInterface/XInput/XInput.cpp @@ -10,43 +10,120 @@ namespace ciface::XInput { -class Battery : public Core::Device::Input +struct ButtonDef +{ + const char* name; + WORD bitmask; +}; +static constexpr std::array 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() override { return false; } private: const ControlState& m_level; }; -static const struct -{ - const char* const name; - const WORD bitmask; -} 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 const char* const named_triggers[] = {"Trigger L", "Trigger R"}; - -static const char* const named_axes[] = {"Left X", "Left Y", "Right X", "Right Y"}; - -static const char* const named_motors[] = {"Motor L", "Motor R"}; - static HMODULE hXInput = nullptr; typedef decltype(&XInputGetCapabilities) XInputGetCapabilities_t; @@ -59,7 +136,7 @@ static XInputSetState_t PXInputSetState = nullptr; static XInputGetState_t PXInputGetState = nullptr; static XInputGetBatteryInformation_t PXInputGetBatteryInformation = nullptr; -static bool haveGuideButton = false; +static bool s_have_guide_button = false; void Init() { @@ -86,9 +163,10 @@ void Init() // 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); - if (PXInputGetState) - haveGuideButton = true; - else + + s_have_guide_button = PXInputGetState != nullptr; + + if (!PXInputGetState) PXInputGetState = (XInputGetState_t)::GetProcAddress(hXInput, "XInputGetState"); if (!PXInputGetCapabilities || !PXInputSetState || !PXInputGetState || @@ -128,35 +206,33 @@ Device::Device(const XINPUT_CAPABILITIES& caps, u8 index) : m_subtype(caps.SubTy // XInputGetCaps can be broken on some devices, so we'll just ignore it // and assume all gamepad + vibration capabilities are supported - // get supported buttons - for (int i = 0; i != sizeof(named_buttons) / sizeof(*named_buttons); ++i) + // 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) || haveGuideButton) - AddInput(new Button(i, m_state_in.Gamepad.wButtons)); + // 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)); } - // get supported triggers - for (int i = 0; i != sizeof(named_triggers) / sizeof(*named_triggers); ++i) - { - AddInput(new Trigger(i, (&m_state_in.Gamepad.bLeftTrigger)[i], 255)); - } + // Triggers. + for (size_t i = 0; i != size(named_triggers); ++i) + AddInput(new Trigger(u8(i), (&m_state_in.Gamepad.bLeftTrigger)[i], 255)); - // get supported axes - for (int i = 0; i != sizeof(named_axes) / sizeof(*named_axes); ++i) + // 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(i, ax, -32768)); - AddInput(new Axis(i, ax, 32767)); + // 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)); } - // get supported motors - for (int i = 0; i != sizeof(named_motors) / sizeof(*named_motors); ++i) - { - AddOutput(new Motor(i, this, (&m_state_out.wLeftMotorSpeed)[i], 65535)); - } + // 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)); } @@ -189,8 +265,6 @@ std::string Device::GetSource() const return "XInput"; } -// Update I/O - void Device::UpdateInput() { PXInputGetState(m_index, &m_state_in); @@ -217,14 +291,7 @@ void Device::UpdateInput() void Device::UpdateMotors() { - // this if statement is to make rumble work better when multiple ControllerInterfaces are using - // the device - // only calls XInputSetState if the state changed - if (memcmp(&m_state_out, &m_current_state_out, sizeof(m_state_out))) - { - m_current_state_out = m_state_out; - PXInputSetState(m_index, &m_state_out); - } + PXInputSetState(m_index, &m_state_out); } std::optional Device::GetPreferredId() const @@ -232,48 +299,4 @@ std::optional Device::GetPreferredId() const return m_index; } -// GET name/source/id - -std::string Device::Button::GetName() const -{ - return named_buttons[m_index].name; -} - -std::string Device::Axis::GetName() const -{ - return std::string(named_axes[m_index]) + (m_range < 0 ? '-' : '+'); -} - -std::string Device::Trigger::GetName() const -{ - return named_triggers[m_index]; -} - -std::string Device::Motor::GetName() const -{ - return named_motors[m_index]; -} - -// GET / SET STATES - -ControlState Device::Button::GetState() const -{ - return (m_buttons & named_buttons[m_index].bitmask) > 0; -} - -ControlState Device::Trigger::GetState() const -{ - return ControlState(m_trigger) / m_range; -} - -ControlState Device::Axis::GetState() const -{ - return ControlState(m_axis) / m_range; -} - -void Device::Motor::SetState(ControlState state) -{ - m_motor = (WORD)(state * m_range); - m_parent->UpdateMotors(); -} } // namespace ciface::XInput diff --git a/Source/Core/InputCommon/ControllerInterface/XInput/XInput.h b/Source/Core/InputCommon/ControllerInterface/XInput/XInput.h index c64a5c33c1..5417f350b5 100644 --- a/Source/Core/InputCommon/ControllerInterface/XInput/XInput.h +++ b/Source/Core/InputCommon/ControllerInterface/XInput/XInput.h @@ -24,82 +24,22 @@ void Init(); void PopulateDevices(); void DeInit(); -class Device : public Core::Device +class Device final : public Core::Device { -private: - class Button : public Core::Device::Input - { - public: - Button(u8 index, const WORD& buttons) : m_buttons(buttons), m_index(index) {} - std::string GetName() const override; - ControlState GetState() const override; - - private: - const WORD& m_buttons; - u8 m_index; - }; - - class Axis : 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; - ControlState GetState() const override; - - private: - const SHORT& m_axis; - const SHORT m_range; - const u8 m_index; - }; - - class Trigger : 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; - ControlState GetState() const override; - - private: - const BYTE& m_trigger; - const BYTE m_range; - const u8 m_index; - }; - - class Motor : 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; - void SetState(ControlState state) override; - - private: - WORD& m_motor; - const WORD m_range; - const u8 m_index; - Device* m_parent; - }; - public: - void UpdateInput() override; - Device(const XINPUT_CAPABILITIES& capabilities, u8 index); - std::string GetName() const final override; - std::string GetSource() const final override; - std::optional GetPreferredId() const final override; + std::string GetName() const override; + std::string GetSource() const override; + std::optional GetPreferredId() const override; + + void UpdateInput() override; void UpdateMotors(); private: XINPUT_STATE m_state_in{}; XINPUT_VIBRATION m_state_out{}; - XINPUT_VIBRATION m_current_state_out{}; ControlState m_battery_level{}; const BYTE m_subtype; const u8 m_index;