From 2e521383c2157dc814b06a2f8974563a26a875f8 Mon Sep 17 00:00:00 2001 From: Gliniak Date: Mon, 23 Dec 2024 10:43:27 +0100 Subject: [PATCH] [HID] Fixed some issues with controller/passthrough visibility. - Moved X_INPUT flags from kernel to HID - Added ability to return input type from driver --- src/xenia/app/xenia_main.cc | 12 +++++ src/xenia/hid/input.h | 6 +++ src/xenia/hid/input_driver.h | 4 ++ src/xenia/hid/input_system.cc | 59 +++++++++++---------- src/xenia/hid/input_system.h | 3 +- src/xenia/hid/nop/nop_input_driver.cc | 2 + src/xenia/hid/nop/nop_input_driver.h | 1 + src/xenia/hid/sdl/sdl_input_driver.cc | 6 +-- src/xenia/hid/sdl/sdl_input_driver.h | 1 + src/xenia/hid/winkey/winkey_input_driver.cc | 37 +++++++++---- src/xenia/hid/winkey/winkey_input_driver.h | 1 + src/xenia/hid/xinput/xinput_input_driver.cc | 4 ++ src/xenia/hid/xinput/xinput_input_driver.h | 1 + src/xenia/kernel/xam/xam_input.cc | 19 ++++--- src/xenia/xbox.h | 6 --- 15 files changed, 110 insertions(+), 52 deletions(-) diff --git a/src/xenia/app/xenia_main.cc b/src/xenia/app/xenia_main.cc index 0a2dcdb26..c1be293f6 100644 --- a/src/xenia/app/xenia_main.cc +++ b/src/xenia/app/xenia_main.cc @@ -186,6 +186,18 @@ class EmulatorApp final : public xe::ui::WindowedApp { instances.emplace_back(std::move(instance)); } } +#if XE_PLATFORM_WIN32 + // Always add winkey for passthrough + it = std::find_if( + creators_.cbegin(), creators_.cend(), + [&name](const auto& f) { return f.name.compare("winkey") == 0; }); + if (it != creators_.cend() && (*it).is_available()) { + auto instance = (*it).instantiate(std::forward(args)...); + if (instance) { + instances.emplace_back(std::move(instance)); + } + } +#endif } else { for (const auto& creator : creators_) { if (!creator.is_available()) continue; diff --git a/src/xenia/hid/input.h b/src/xenia/hid/input.h index 82202a391..ebfeb7eeb 100644 --- a/src/xenia/hid/input.h +++ b/src/xenia/hid/input.h @@ -26,6 +26,12 @@ enum X_INPUT_CAPS { enum X_INPUT_FLAG { X_INPUT_FLAG_GAMEPAD = 0x00000001, + X_INPUT_FLAG_KEYBOARD = 0x00000002, + X_INPUT_FLAG_UNKNOWN = 0x00000004, + X_INPUT_FLAG_UNKNOWN2 = 0x00000008, + X_INPUT_FLAG_MIC = 0x00000020, + X_INPUT_FLAG_ANYDEVICE = 0x000000FF, + X_INPUT_FLAG_ANY_USER = 1 << 30 }; enum X_INPUT_GAMEPAD_BUTTON { diff --git a/src/xenia/hid/input_driver.h b/src/xenia/hid/input_driver.h index 6b0ff3472..a5c05af03 100644 --- a/src/xenia/hid/input_driver.h +++ b/src/xenia/hid/input_driver.h @@ -28,6 +28,8 @@ namespace hid { class InputSystem; +enum InputType { None, Controller = 1, Keyboard = 2, Other = 4 }; + class InputDriver { public: virtual ~InputDriver() = default; @@ -42,6 +44,8 @@ class InputDriver { virtual X_RESULT GetKeystroke(uint32_t user_index, uint32_t flags, X_INPUT_KEYSTROKE* out_keystroke) = 0; + virtual InputType GetInputType() const = 0; + void set_is_active_callback(std::function is_active_callback) { is_active_callback_ = is_active_callback; } diff --git a/src/xenia/hid/input_system.cc b/src/xenia/hid/input_system.cc index bb4cfbd9c..6912deb3d 100644 --- a/src/xenia/hid/input_system.cc +++ b/src/xenia/hid/input_system.cc @@ -45,6 +45,11 @@ void InputSystem::UpdateUsedSlot(InputDriver* driver, uint8_t slot, return; } + // Do not report passthrough as a controller. + if (driver && driver->GetInputType() == InputType::Keyboard) { + return; + } + if (connected_slots.test(slot) == connected) { // No state change, so nothing to do. return; @@ -70,36 +75,42 @@ void InputSystem::UpdateUsedSlot(InputDriver* driver, uint8_t slot, } } +std::vector InputSystem::FilterDrivers(uint32_t flags) { + std::vector filtered_drivers; + for (auto& driver : drivers_) { + if (driver->GetInputType() == InputType::None) { + continue; + } + + if ((flags & driver->GetInputType()) != 0) { + filtered_drivers.push_back(driver.get()); + } + } + return filtered_drivers; +} + X_RESULT InputSystem::GetCapabilities(uint32_t user_index, uint32_t flags, X_INPUT_CAPABILITIES* out_caps) { SCOPE_profile_cpu_f("hid"); - bool any_connected = false; - for (auto& driver : drivers_) { + std::vector filtered_drivers = FilterDrivers(flags); + + for (auto& driver : filtered_drivers) { X_RESULT result = driver->GetCapabilities(user_index, flags, out_caps); - if (result != X_ERROR_DEVICE_NOT_CONNECTED) { - any_connected = true; - } if (result == X_ERROR_SUCCESS) { - UpdateUsedSlot(driver.get(), user_index, any_connected); return result; } } - UpdateUsedSlot(nullptr, user_index, any_connected); - return any_connected ? X_ERROR_EMPTY : X_ERROR_DEVICE_NOT_CONNECTED; + return X_ERROR_DEVICE_NOT_CONNECTED; } X_RESULT InputSystem::GetState(uint32_t user_index, X_INPUT_STATE* out_state) { SCOPE_profile_cpu_f("hid"); - bool any_connected = false; for (auto& driver : drivers_) { X_RESULT result = driver->GetState(user_index, out_state); - if (result != X_ERROR_DEVICE_NOT_CONNECTED) { - any_connected = true; - } if (result == X_ERROR_SUCCESS) { - UpdateUsedSlot(driver.get(), user_index, any_connected); + UpdateUsedSlot(driver.get(), user_index, true); AdjustDeadzoneLevels(user_index, &out_state->gamepad); if (out_state->gamepad.buttons != 0) { @@ -108,35 +119,31 @@ X_RESULT InputSystem::GetState(uint32_t user_index, X_INPUT_STATE* out_state) { return result; } } - UpdateUsedSlot(nullptr, user_index, any_connected); - return any_connected ? X_ERROR_EMPTY : X_ERROR_DEVICE_NOT_CONNECTED; + UpdateUsedSlot(nullptr, user_index, false); + return X_ERROR_DEVICE_NOT_CONNECTED; } X_RESULT InputSystem::SetState(uint32_t user_index, X_INPUT_VIBRATION* vibration) { SCOPE_profile_cpu_f("hid"); X_INPUT_VIBRATION modified_vibration = ModifyVibrationLevel(vibration); - bool any_connected = false; for (auto& driver : drivers_) { X_RESULT result = driver->SetState(user_index, &modified_vibration); - if (result != X_ERROR_DEVICE_NOT_CONNECTED) { - any_connected = true; - } if (result == X_ERROR_SUCCESS) { - UpdateUsedSlot(driver.get(), user_index, any_connected); return result; } } - UpdateUsedSlot(nullptr, user_index, any_connected); - return any_connected ? X_ERROR_EMPTY : X_ERROR_DEVICE_NOT_CONNECTED; + return X_ERROR_DEVICE_NOT_CONNECTED; } X_RESULT InputSystem::GetKeystroke(uint32_t user_index, uint32_t flags, X_INPUT_KEYSTROKE* out_keystroke) { SCOPE_profile_cpu_f("hid"); + std::vector filtered_drivers = FilterDrivers(flags); + bool any_connected = false; - for (auto& driver : drivers_) { + for (auto& driver : filtered_drivers) { // connected_slots X_RESULT result = driver->GetKeystroke(user_index, flags, out_keystroke); if (result == X_ERROR_INVALID_PARAMETER || @@ -146,10 +153,8 @@ X_RESULT InputSystem::GetKeystroke(uint32_t user_index, uint32_t flags, any_connected = true; - if (result == X_ERROR_SUCCESS || result == X_ERROR_EMPTY) { - if (result == X_ERROR_SUCCESS) { - last_used_slot = user_index; - } + if (result == X_ERROR_SUCCESS) { + last_used_slot = user_index; return result; } diff --git a/src/xenia/hid/input_system.h b/src/xenia/hid/input_system.h index b1c0db52d..895e7a8cd 100644 --- a/src/xenia/hid/input_system.h +++ b/src/xenia/hid/input_system.h @@ -46,7 +46,6 @@ class InputSystem { X_INPUT_KEYSTROKE* out_keystroke); bool GetVibrationCvar(); - void ToggleVibration(); const std::bitset GetConnectedSlots() const { @@ -68,6 +67,8 @@ class InputSystem { void AdjustDeadzoneLevels(const uint8_t slot, X_INPUT_GAMEPAD* gamepad); X_INPUT_VIBRATION ModifyVibrationLevel(X_INPUT_VIBRATION* vibration); + std::vector FilterDrivers(uint32_t flags); + xe::ui::Window* window_ = nullptr; std::vector> drivers_; diff --git a/src/xenia/hid/nop/nop_input_driver.cc b/src/xenia/hid/nop/nop_input_driver.cc index da6897459..e17f7464a 100644 --- a/src/xenia/hid/nop/nop_input_driver.cc +++ b/src/xenia/hid/nop/nop_input_driver.cc @@ -45,6 +45,8 @@ X_RESULT NopInputDriver::GetKeystroke(uint32_t user_index, uint32_t flags, return X_ERROR_DEVICE_NOT_CONNECTED; } +InputType NopInputDriver::GetInputType() const { return InputType::Other; } + } // namespace nop } // namespace hid } // namespace xe diff --git a/src/xenia/hid/nop/nop_input_driver.h b/src/xenia/hid/nop/nop_input_driver.h index 361b63a7f..f65cc584f 100644 --- a/src/xenia/hid/nop/nop_input_driver.h +++ b/src/xenia/hid/nop/nop_input_driver.h @@ -29,6 +29,7 @@ class NopInputDriver final : public InputDriver { X_RESULT SetState(uint32_t user_index, X_INPUT_VIBRATION* vibration) override; X_RESULT GetKeystroke(uint32_t user_index, uint32_t flags, X_INPUT_KEYSTROKE* out_keystroke) override; + virtual InputType GetInputType() const override; }; } // namespace nop diff --git a/src/xenia/hid/sdl/sdl_input_driver.cc b/src/xenia/hid/sdl/sdl_input_driver.cc index d0bd0ca93..e622f31ac 100644 --- a/src/xenia/hid/sdl/sdl_input_driver.cc +++ b/src/xenia/hid/sdl/sdl_input_driver.cc @@ -288,9 +288,7 @@ X_RESULT SDLInputDriver::GetKeystroke(uint32_t users, uint32_t flags, if (!out_keystroke) { return X_ERROR_BAD_ARGUMENTS; } - if ((flags & XINPUT_FLAG_KEYBOARD) != 0) { - return X_ERROR_INVALID_PARAMETER; - } + // The order of this list is also the order in which events are send if // multiple buttons change at once. static_assert(sizeof(X_INPUT_GAMEPAD::buttons) == 2); @@ -431,6 +429,8 @@ X_RESULT SDLInputDriver::GetKeystroke(uint32_t users, uint32_t flags, return X_ERROR_EMPTY; } +InputType SDLInputDriver::GetInputType() const { return InputType::Controller; } + void SDLInputDriver::HandleEvent(const SDL_Event& event) { // This callback will likely run on the thread that posts the event, which // may be a dedicated thread SDL has created for the joystick subsystem. diff --git a/src/xenia/hid/sdl/sdl_input_driver.h b/src/xenia/hid/sdl/sdl_input_driver.h index 1b8d2e9b1..462374d82 100644 --- a/src/xenia/hid/sdl/sdl_input_driver.h +++ b/src/xenia/hid/sdl/sdl_input_driver.h @@ -44,6 +44,7 @@ class SDLInputDriver final : public InputDriver { X_RESULT SetState(uint32_t user_index, X_INPUT_VIBRATION* vibration) override; X_RESULT GetKeystroke(uint32_t user_index, uint32_t flags, X_INPUT_KEYSTROKE* out_keystroke) override; + virtual InputType GetInputType() const override; private: struct ControllerState { diff --git a/src/xenia/hid/winkey/winkey_input_driver.cc b/src/xenia/hid/winkey/winkey_input_driver.cc index 078f80c7b..b1d4af1eb 100644 --- a/src/xenia/hid/winkey/winkey_input_driver.cc +++ b/src/xenia/hid/winkey/winkey_input_driver.cc @@ -25,15 +25,17 @@ #include "winkey_binding_table.inc" #undef XE_HID_WINKEY_BINDING -DEFINE_int32(keyboard_mode, 1, +DEFINE_int32(keyboard_mode, 0, "Allows user do specify keyboard working mode. Possible values: 0 " - "- Disabled, 1 - Enabled, 2 - Passthrough", + "- Disabled, 1 - Enabled, 2 - Passthrough. Passthrough requires " + "controller being connected!", "HID"); -DEFINE_int32(keyboard_user_index, 0, - "Controller port that keyboard emulates. -1 - Keyboard usage " - "disabled, [0, 3] - Keyboard is assigned to selected slot.", - "HID"); +DEFINE_int32( + keyboard_user_index, 0, + "Controller port that keyboard emulates. [0, 3] - Keyboard is assigned to " + "selected slot. Passthrough does not require assigning slot.", + "HID"); namespace xe { namespace hid { @@ -126,7 +128,7 @@ X_STATUS WinKeyInputDriver::Setup() { return X_STATUS_SUCCESS; } X_RESULT WinKeyInputDriver::GetCapabilities(uint32_t user_index, uint32_t flags, X_INPUT_CAPABILITIES* out_caps) { - if (!IsKeyboardForUserEnabled(user_index)) { + if (!IsKeyboardForUserEnabled(user_index) && !IsPassthroughEnabled()) { return X_ERROR_DEVICE_NOT_CONNECTED; } @@ -258,12 +260,16 @@ X_RESULT WinKeyInputDriver::GetState(uint32_t user_index, out_state->gamepad.thumb_rx = thumb_rx; out_state->gamepad.thumb_ry = thumb_ry; + if (IsPassthroughEnabled()) { + memset(out_state, 0, sizeof(out_state)); + } + return X_ERROR_SUCCESS; } X_RESULT WinKeyInputDriver::SetState(uint32_t user_index, X_INPUT_VIBRATION* vibration) { - if (!IsKeyboardForUserEnabled(user_index)) { + if (!IsKeyboardForUserEnabled(user_index) && !IsPassthroughEnabled()) { return X_ERROR_DEVICE_NOT_CONNECTED; } @@ -279,7 +285,6 @@ X_RESULT WinKeyInputDriver::GetKeystroke(uint32_t user_index, uint32_t flags, if (!IsKeyboardForUserEnabled(user_index) && !IsPassthroughEnabled()) { return X_ERROR_DEVICE_NOT_CONNECTED; } - // Pop from the queue. KeyEvent evt; { @@ -387,6 +392,20 @@ void WinKeyInputDriver::OnKey(ui::KeyEvent& e, bool is_down) { key_events_.push(key); } +InputType WinKeyInputDriver::GetInputType() const { + switch (static_cast(cvars::keyboard_mode)) { + case KeyboardMode::Disabled: + return InputType::None; + case KeyboardMode::Enabled: + return InputType::Controller; + case KeyboardMode::Passthrough: + return InputType::Keyboard; + default: + break; + } + return InputType::Controller; +} + } // namespace winkey } // namespace hid } // namespace xe diff --git a/src/xenia/hid/winkey/winkey_input_driver.h b/src/xenia/hid/winkey/winkey_input_driver.h index 541ef864b..7bdba0a31 100644 --- a/src/xenia/hid/winkey/winkey_input_driver.h +++ b/src/xenia/hid/winkey/winkey_input_driver.h @@ -35,6 +35,7 @@ class WinKeyInputDriver final : public InputDriver { X_RESULT SetState(uint32_t user_index, X_INPUT_VIBRATION* vibration) override; X_RESULT GetKeystroke(uint32_t user_index, uint32_t flags, X_INPUT_KEYSTROKE* out_keystroke) override; + virtual InputType GetInputType() const override; protected: struct KeyEvent { diff --git a/src/xenia/hid/xinput/xinput_input_driver.cc b/src/xenia/hid/xinput/xinput_input_driver.cc index 62e659e1c..421734562 100644 --- a/src/xenia/hid/xinput/xinput_input_driver.cc +++ b/src/xenia/hid/xinput/xinput_input_driver.cc @@ -239,6 +239,10 @@ X_RESULT XInputInputDriver::GetKeystroke(uint32_t user_index, uint32_t flags, return result; } +InputType XInputInputDriver::GetInputType() const { + return InputType::Controller; +} + } // namespace xinput } // namespace hid } // namespace xe diff --git a/src/xenia/hid/xinput/xinput_input_driver.h b/src/xenia/hid/xinput/xinput_input_driver.h index 520a8bbe5..597d2d992 100644 --- a/src/xenia/hid/xinput/xinput_input_driver.h +++ b/src/xenia/hid/xinput/xinput_input_driver.h @@ -29,6 +29,7 @@ class XInputInputDriver final : public InputDriver { X_RESULT SetState(uint32_t user_index, X_INPUT_VIBRATION* vibration) override; X_RESULT GetKeystroke(uint32_t user_index, uint32_t flags, X_INPUT_KEYSTROKE* out_keystroke) override; + virtual InputType GetInputType() const override; private: void* module_; diff --git a/src/xenia/kernel/xam/xam_input.cc b/src/xenia/kernel/xam/xam_input.cc index cef58c839..070dbd6c1 100644 --- a/src/xenia/kernel/xam/xam_input.cc +++ b/src/xenia/kernel/xam/xam_input.cc @@ -21,6 +21,7 @@ namespace kernel { namespace xam { using xe::hid::X_INPUT_CAPABILITIES; +using xe::hid::X_INPUT_FLAG; using xe::hid::X_INPUT_KEYSTROKE; using xe::hid::X_INPUT_STATE; using xe::hid::X_INPUT_VIBRATION; @@ -59,7 +60,7 @@ dword_result_t XamInputGetCapabilitiesEx_entry( caps.Zero(); - if ((flags & XINPUT_FLAG_ANY_USER) != 0) { + if ((flags & X_INPUT_FLAG::X_INPUT_FLAG_ANY_USER) != 0) { // should trap } @@ -69,14 +70,20 @@ dword_result_t XamInputGetCapabilitiesEx_entry( uint32_t actual_user_index = user_index; if ((actual_user_index & XUserIndexAny) == XUserIndexAny || - (flags & XINPUT_FLAG_ANY_USER)) { + (flags & X_INPUT_FLAG::X_INPUT_FLAG_ANY_USER)) { // Always pin user to 0. actual_user_index = 0; } + uint32_t actual_flags = flags; + if (!flags) { + actual_flags = X_INPUT_FLAG::X_INPUT_FLAG_GAMEPAD | + X_INPUT_FLAG::X_INPUT_FLAG_KEYBOARD; + } + auto input_system = kernel_state()->emulator()->input_system(); auto lock = input_system->lock(); - return input_system->GetCapabilities(actual_user_index, flags, caps); + return input_system->GetCapabilities(actual_user_index, actual_flags, caps); } DECLARE_XAM_EXPORT1(XamInputGetCapabilitiesEx, kInput, kSketchy); @@ -104,7 +111,7 @@ dword_result_t XamInputGetState_entry(dword_t user_index, dword_t flags, uint32_t actual_user_index = user_index; // chrispy: change this, logic is not right if ((actual_user_index & XUserIndexAny) == XUserIndexAny || - (flags & XINPUT_FLAG_ANY_USER)) { + (flags & X_INPUT_FLAG::X_INPUT_FLAG_ANY_USER)) { // Always pin user to 0. actual_user_index = 0; } @@ -146,7 +153,7 @@ dword_result_t XamInputGetKeystroke_entry( uint32_t actual_user_index = user_index; if ((actual_user_index & XUserIndexAny) == XUserIndexAny || - (flags & XINPUT_FLAG_ANY_USER)) { + (flags & X_INPUT_FLAG::X_INPUT_FLAG_ANY_USER)) { // Always pin user to 0. actual_user_index = 0; } @@ -173,7 +180,7 @@ dword_result_t XamInputGetKeystrokeEx_entry( user_index = 0; } - if (flags & XINPUT_FLAG_ANY_USER) { + if (flags & X_INPUT_FLAG::X_INPUT_FLAG_ANY_USER) { // That flag means we should iterate over every connected controller and // check which one have pending request. auto result = X_ERROR_DEVICE_NOT_CONNECTED; diff --git a/src/xenia/xbox.h b/src/xenia/xbox.h index 05d95a6ec..e7bedd73b 100644 --- a/src/xenia/xbox.h +++ b/src/xenia/xbox.h @@ -663,12 +663,6 @@ struct X_PROFILEENUMRESULT { }; static_assert_size(X_PROFILEENUMRESULT, 0x188); -constexpr uint32_t XINPUT_FLAG_GAMEPAD = 0x01; -constexpr uint32_t XINPUT_FLAG_KEYBOARD = 0x02; -constexpr uint32_t XINPUT_FLAG_MIC = 0x20; // Based on "karaoke" titles -constexpr uint32_t XINPUT_FLAG_ANYDEVICE = 0xFF; -constexpr uint32_t XINPUT_FLAG_ANY_USER = 1 << 30; - } // namespace xe // clang-format on