From dd15183faf865bf53a6115f52a505beddeb5d849 Mon Sep 17 00:00:00 2001 From: Gliniak Date: Thu, 9 Feb 2023 20:56:32 +0100 Subject: [PATCH] [HID] Enable Keyboard Passthru Thanks Adrian for figuring out missing XINPUT_KEYSTROKE_VALIDUNICODE flag --- src/xenia/hid/input_system.cc | 5 ++ src/xenia/hid/sdl/sdl_input_driver.cc | 4 +- src/xenia/hid/winkey/winkey_input_driver.cc | 65 +++++++++++++++++---- src/xenia/hid/winkey/winkey_input_driver.h | 2 +- src/xenia/hid/xinput/xinput_input_driver.cc | 3 + src/xenia/kernel/xam/xam_input.cc | 26 --------- src/xenia/xbox.h | 6 ++ 7 files changed, 72 insertions(+), 39 deletions(-) diff --git a/src/xenia/hid/input_system.cc b/src/xenia/hid/input_system.cc index 878efb9a5..fdb809c99 100644 --- a/src/xenia/hid/input_system.cc +++ b/src/xenia/hid/input_system.cc @@ -131,10 +131,15 @@ X_RESULT InputSystem::GetKeystroke(uint32_t user_index, uint32_t flags, if (result != X_ERROR_DEVICE_NOT_CONNECTED) { any_connected = true; } + if (result == X_ERROR_SUCCESS || result == X_ERROR_EMPTY) { UpdateUsedSlot(driver.get(), user_index, any_connected); return result; } + + if (result == X_ERROR_EMPTY) { + continue; + } } UpdateUsedSlot(nullptr, user_index, any_connected); return any_connected ? X_ERROR_EMPTY : X_ERROR_DEVICE_NOT_CONNECTED; diff --git a/src/xenia/hid/sdl/sdl_input_driver.cc b/src/xenia/hid/sdl/sdl_input_driver.cc index cbbbd5f01..5d24a01f2 100644 --- a/src/xenia/hid/sdl/sdl_input_driver.cc +++ b/src/xenia/hid/sdl/sdl_input_driver.cc @@ -289,7 +289,9 @@ 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); diff --git a/src/xenia/hid/winkey/winkey_input_driver.cc b/src/xenia/hid/winkey/winkey_input_driver.cc index 601112c99..cc2dd5abc 100644 --- a/src/xenia/hid/winkey/winkey_input_driver.cc +++ b/src/xenia/hid/winkey/winkey_input_driver.cc @@ -28,10 +28,25 @@ DEFINE_int32(keyboard_user_index, 0, "Controller port that keyboard emulates", "HID.WinKey"); +DEFINE_int32(keyboard_passthru_user_index, -1, + "Allows keyboard to be assigned as virtual keyboard to user with " + "specific index. This also forces keyboard to be assigned to that " + "slot to be interpreted as controller. Possible values: -1 - " + "Disabled (Keyboard is in " + "gamepad mode), [0, 3] - Keyboard is assigned as VK for that user", + "HID"); + namespace xe { namespace hid { namespace winkey { +bool static IsPassThruForUserEnabled(uint32_t user_index) { + if (cvars::keyboard_passthru_user_index == -1) { + return false; + } + return user_index == cvars::keyboard_passthru_user_index; +} + bool __inline IsKeyToggled(uint8_t key) { return (GetKeyState(key) & 0x1) == 0x1; } @@ -126,7 +141,8 @@ X_RESULT WinKeyInputDriver::GetCapabilities(uint32_t user_index, uint32_t flags, X_RESULT WinKeyInputDriver::GetState(uint32_t user_index, X_INPUT_STATE* out_state) { - if (user_index != cvars::keyboard_user_index) { + if (!IsPassThruForUserEnabled(user_index) && + user_index != cvars::keyboard_user_index) { return X_ERROR_DEVICE_NOT_CONNECTED; } @@ -241,7 +257,8 @@ X_RESULT WinKeyInputDriver::GetState(uint32_t user_index, X_RESULT WinKeyInputDriver::SetState(uint32_t user_index, X_INPUT_VIBRATION* vibration) { - if (user_index != cvars::keyboard_user_index) { + if (!IsPassThruForUserEnabled(user_index) && + user_index != cvars::keyboard_user_index) { return X_ERROR_DEVICE_NOT_CONNECTED; } @@ -250,7 +267,8 @@ X_RESULT WinKeyInputDriver::SetState(uint32_t user_index, X_RESULT WinKeyInputDriver::GetKeystroke(uint32_t user_index, uint32_t flags, X_INPUT_KEYSTROKE* out_keystroke) { - if (user_index != cvars::keyboard_user_index) { + if (!IsPassThruForUserEnabled(user_index) && + user_index != cvars::keyboard_user_index) { return X_ERROR_DEVICE_NOT_CONNECTED; } @@ -278,23 +296,48 @@ X_RESULT WinKeyInputDriver::GetKeystroke(uint32_t user_index, uint32_t flags, } bool capital = IsKeyToggled(VK_CAPITAL) || IsKeyDown(VK_SHIFT); - for (const KeyBinding& b : key_bindings_) { - if (b.input_key == evt.virtual_key && - ((b.lowercase == b.uppercase) || (b.lowercase && !capital) || - (b.uppercase && capital))) { - xinput_virtual_key = b.output_key; + + if (!IsPassThruForUserEnabled(user_index)) { + for (const KeyBinding& b : key_bindings_) { + if (b.input_key == evt.virtual_key && + ((b.lowercase == b.uppercase) || (b.lowercase && !capital) || + (b.uppercase && capital))) { + xinput_virtual_key = b.output_key; + } + } + } else { + xinput_virtual_key = evt.virtual_key; + + if (capital) { + keystroke_flags |= 0x0008; // XINPUT_KEYSTROKE_SHIFT + } + + if (IsKeyToggled(VK_CONTROL)) { + keystroke_flags |= 0x0010; // XINPUT_KEYSTROKE_CTRL + } + + if (IsKeyToggled(VK_MENU)) { + keystroke_flags |= 0x0020; // XINPUT_KEYSTROKE_ALT } } if (xinput_virtual_key != ui::VirtualKey::kNone) { if (evt.transition == true) { keystroke_flags |= 0x0001; // XINPUT_KEYSTROKE_KEYDOWN + if (evt.prev_state == evt.transition) { + keystroke_flags |= 0x0004; // XINPUT_KEYSTROKE_REPEAT + } } else if (evt.transition == false) { keystroke_flags |= 0x0002; // XINPUT_KEYSTROKE_KEYUP } - if (evt.prev_state == evt.transition) { - keystroke_flags |= 0x0004; // XINPUT_KEYSTROKE_REPEAT + if (IsPassThruForUserEnabled(user_index)) { + WCHAR buf; + if (ToUnicode(uint8_t(xinput_virtual_key), 0, key_map_, &buf, 1, 0) == + 1) { + keystroke_flags |= 0x1000; // XINPUT_KEYSTROKE_VALIDUNICODE + unicode = buf; + } } result = X_ERROR_SUCCESS; @@ -303,7 +346,7 @@ X_RESULT WinKeyInputDriver::GetKeystroke(uint32_t user_index, uint32_t flags, out_keystroke->virtual_key = uint16_t(xinput_virtual_key); out_keystroke->unicode = unicode; out_keystroke->flags = keystroke_flags; - out_keystroke->user_index = 0; + out_keystroke->user_index = user_index; out_keystroke->hid_code = hid_code; // X_ERROR_EMPTY if no new keys diff --git a/src/xenia/hid/winkey/winkey_input_driver.h b/src/xenia/hid/winkey/winkey_input_driver.h index e11788acf..631c4c6a7 100644 --- a/src/xenia/hid/winkey/winkey_input_driver.h +++ b/src/xenia/hid/winkey/winkey_input_driver.h @@ -72,7 +72,7 @@ class WinKeyInputDriver final : public InputDriver { xe::global_critical_region global_critical_region_; std::queue key_events_; std::vector key_bindings_; - + uint8_t key_map_[256]; uint32_t packet_number_ = 1; }; diff --git a/src/xenia/hid/xinput/xinput_input_driver.cc b/src/xenia/hid/xinput/xinput_input_driver.cc index 7f1c9439e..2b61a5789 100644 --- a/src/xenia/hid/xinput/xinput_input_driver.cc +++ b/src/xenia/hid/xinput/xinput_input_driver.cc @@ -200,6 +200,9 @@ X_RESULT XInputInputDriver::GetKeystroke(uint32_t user_index, uint32_t flags, // flags is reserved on desktop. DWORD result; + if ((flags & XINPUT_FLAG_KEYBOARD) != 0) { + return X_ERROR_INVALID_PARAMETER; + } // XInputGetKeystroke on Windows has a bug where it will return // ERROR_SUCCESS (0) even if the device is not connected: // https://stackoverflow.com/questions/23669238/xinputgetkeystroke-returning-error-success-while-controller-is-unplugged diff --git a/src/xenia/kernel/xam/xam_input.cc b/src/xenia/kernel/xam/xam_input.cc index 58aee7d1d..661b81a47 100644 --- a/src/xenia/kernel/xam/xam_input.cc +++ b/src/xenia/kernel/xam/xam_input.cc @@ -25,12 +25,6 @@ using xe::hid::X_INPUT_KEYSTROKE; using xe::hid::X_INPUT_STATE; using xe::hid::X_INPUT_VIBRATION; -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; - dword_result_t XAutomationpUnbindController_entry(dword_t user_index) { if (user_index > 4) { return 0; @@ -73,11 +67,6 @@ dword_result_t XamInputGetCapabilitiesEx_entry( // should trap } - if ((flags & XINPUT_FLAG_ANYDEVICE) && (flags & XINPUT_FLAG_GAMEPAD) == 0) { - // Ignore any query for other types of devices. - return X_ERROR_DEVICE_NOT_CONNECTED; - } - uint32_t actual_user_index = user_index; if ((actual_user_index & 0xFF) == 0xFF || (flags & XINPUT_FLAG_ANY_USER)) { // Always pin user to 0. @@ -111,11 +100,6 @@ dword_result_t XamInputGetState_entry(dword_t user_index, dword_t flags, // Games call this with a NULL state ptr, probably as a query. - if ((flags & XINPUT_FLAG_ANYDEVICE) && (flags & XINPUT_FLAG_GAMEPAD) == 0) { - // Ignore any query for other types of devices. - return X_ERROR_DEVICE_NOT_CONNECTED; - } - uint32_t actual_user_index = user_index; // chrispy: change this, logic is not right if ((actual_user_index & 0xFF) == 0xFF || (flags & XINPUT_FLAG_ANY_USER)) { @@ -164,11 +148,6 @@ dword_result_t XamInputGetKeystroke_entry( return X_ERROR_BAD_ARGUMENTS; } - if ((flags & XINPUT_FLAG_ANYDEVICE) && (flags & XINPUT_FLAG_GAMEPAD) == 0) { - // Ignore any query for other types of devices. - return X_ERROR_DEVICE_NOT_CONNECTED; - } - uint32_t actual_user_index = user_index; if ((actual_user_index & 0xFF) == 0xFF || (flags & XINPUT_FLAG_ANY_USER)) { // Always pin user to 0. @@ -189,11 +168,6 @@ dword_result_t XamInputGetKeystrokeEx_entry( return X_ERROR_BAD_ARGUMENTS; } - if ((flags & XINPUT_FLAG_ANYDEVICE) && (flags & XINPUT_FLAG_GAMEPAD) == 0) { - // Ignore any query for other types of devices. - return X_ERROR_DEVICE_NOT_CONNECTED; - } - uint32_t user_index = *user_index_ptr; auto input_system = kernel_state()->emulator()->input_system(); auto lock = input_system->lock(); diff --git a/src/xenia/xbox.h b/src/xenia/xbox.h index b8f96dd22..e370d1b19 100644 --- a/src/xenia/xbox.h +++ b/src/xenia/xbox.h @@ -539,6 +539,12 @@ enum class XDeploymentType : uint32_t { kUnknown = 0xFF, }; +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