From 7e8e8164ed074ad90a61ed25e0d2b6677ae2cd73 Mon Sep 17 00:00:00 2001 From: nikolay-kyosev Date: Wed, 29 Jan 2025 13:23:56 +0100 Subject: [PATCH] [LINUX/HID] Added a generic keyboard input driver. --- premake5.lua | 4 + src/xenia/app/premake5.lua | 4 + src/xenia/app/xenia_main.cc | 6 + src/xenia/hid/hid_demo.cc | 16 + .../hid/keyboard/keyboard_binding_table.inc | 38 ++ src/xenia/hid/keyboard/keyboard_hid.cc | 26 ++ src/xenia/hid/keyboard/keyboard_hid.h | 28 ++ .../hid/keyboard/keyboard_input_driver.cc | 340 ++++++++++++++++++ .../hid/keyboard/keyboard_input_driver.h | 86 +++++ src/xenia/hid/keyboard/premake5.lua | 16 + 10 files changed, 564 insertions(+) create mode 100644 src/xenia/hid/keyboard/keyboard_binding_table.inc create mode 100644 src/xenia/hid/keyboard/keyboard_hid.cc create mode 100644 src/xenia/hid/keyboard/keyboard_hid.h create mode 100644 src/xenia/hid/keyboard/keyboard_input_driver.cc create mode 100644 src/xenia/hid/keyboard/keyboard_input_driver.h create mode 100644 src/xenia/hid/keyboard/premake5.lua diff --git a/premake5.lua b/premake5.lua index a5508ba75..ca269f7e5 100644 --- a/premake5.lua +++ b/premake5.lua @@ -327,6 +327,10 @@ workspace("xenia") include("src/xenia/hid/sdl") end + if not os.istarget("windows") then + include("src/xenia/hid/keyboard") + end + if os.istarget("windows") then include("src/xenia/apu/xaudio2") include("src/xenia/gpu/d3d12") diff --git a/src/xenia/app/premake5.lua b/src/xenia/app/premake5.lua index 11a7a0100..c7fae7a4f 100644 --- a/src/xenia/app/premake5.lua +++ b/src/xenia/app/premake5.lua @@ -97,6 +97,10 @@ project("xenia-app") "xenia-hid-sdl", }) + filter("platforms:not Windows") + links({ + "xenia-hid-keyboard" + }) filter("platforms:Linux") links({ "X11", diff --git a/src/xenia/app/xenia_main.cc b/src/xenia/app/xenia_main.cc index d90246936..8f459f0b7 100644 --- a/src/xenia/app/xenia_main.cc +++ b/src/xenia/app/xenia_main.cc @@ -56,6 +56,9 @@ #if !XE_PLATFORM_ANDROID #include "xenia/hid/sdl/sdl_hid.h" #endif // !XE_PLATFORM_ANDROID +#if !XE_PLATFORM_WIN32 +#include "xenia/hid/keyboard/keyboard_hid.h" +#endif #if XE_PLATFORM_WIN32 #include "xenia/hid/winkey/winkey_hid.h" #include "xenia/hid/xinput/xinput_hid.h" @@ -388,6 +391,9 @@ std::vector> EmulatorApp::CreateInputDrivers( #if !XE_PLATFORM_ANDROID factory.Add("sdl", xe::hid::sdl::Create); #endif // !XE_PLATFORM_ANDROID +#if !XE_PLATFORM_WIN32 + factory.Add("keyboard", xe::hid::keyboard::Create); +#endif #if XE_PLATFORM_WIN32 // WinKey input driver should always be the last input driver added! factory.Add("winkey", xe::hid::winkey::Create); diff --git a/src/xenia/hid/hid_demo.cc b/src/xenia/hid/hid_demo.cc index dd4198cb5..3e8df0bf4 100644 --- a/src/xenia/hid/hid_demo.cc +++ b/src/xenia/hid/hid_demo.cc @@ -40,6 +40,9 @@ #if !XE_PLATFORM_ANDROID #include "xenia/hid/sdl/sdl_hid.h" #endif // !XE_PLATFORM_ANDROID +#if !XE_PLATFORM_WIN32 +#include "xenia/hid/keyboard/keyboard_hid.h" +#endif #if XE_PLATFORM_WIN32 #include "xenia/hid/winkey/winkey_hid.h" #include "xenia/hid/xinput/xinput_hid.h" @@ -132,6 +135,13 @@ std::vector> HidDemoApp::CreateInputDrivers( drivers.emplace_back(std::move(driver)); } #endif // !XE_PLATFORM_ANDROID +#if !XE_PLATFORM_WIN32 + } else if (cvars::hid.compare("keyboard") == 0) { + auto driver = xe::hid::keyboard::Create(window, kZOrderHidInput); + if (driver && XSUCCEEDED(driver->Setup())) { + drivers.emplace_back(std::move(driver)); + } +#endif #if XE_PLATFORM_WIN32 } else if (cvars::hid.compare("winkey") == 0) { auto driver = xe::hid::winkey::Create(window, kZOrderHidInput); @@ -151,6 +161,12 @@ std::vector> HidDemoApp::CreateInputDrivers( drivers.emplace_back(std::move(sdl_driver)); } #endif // !XE_PLATFORM_ANDROID +#if !XE_PLATFORM_WIN32 + auto keyboard_driver = xe::hid::keyboard::Create(window, kZOrderHidInput); + if (keyboard_driver && XSUCCEEDED(keyboard_driver->Setup())) { + drivers.emplace_back(std::move(keyboard_driver)); + } +#endif #if XE_PLATFORM_WIN32 auto xinput_driver = xe::hid::xinput::Create(window, kZOrderHidInput); if (xinput_driver && XSUCCEEDED(xinput_driver->Setup())) { diff --git a/src/xenia/hid/keyboard/keyboard_binding_table.inc b/src/xenia/hid/keyboard/keyboard_binding_table.inc new file mode 100644 index 000000000..4e4db3d1c --- /dev/null +++ b/src/xenia/hid/keyboard/keyboard_binding_table.inc @@ -0,0 +1,38 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2022 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +// This is a partial file designed to be included by other files when +// constructing various tables. + +// clang-format off +XE_HID_KEYBOARD_BINDING(DpadLeft, "DPAD_LEFT" , keybind_dpad_left , "^A" ) +XE_HID_KEYBOARD_BINDING(DpadRight, "DPAD_RIGHT" , keybind_dpad_right , "^D" ) +XE_HID_KEYBOARD_BINDING(DpadDown, "DPAD_DOWN" , keybind_dpad_down , "^S" ) +XE_HID_KEYBOARD_BINDING(DpadUp, "DPAD_UP" , keybind_dpad_up , "^W" ) +XE_HID_KEYBOARD_BINDING(LThumbLeft, "LEFT_THUMB_LEFT" , keybind_left_thumb_left , "_A" ) +XE_HID_KEYBOARD_BINDING(LThumbRight, "LEFT_THUMB_RIGHT" , keybind_left_thumb_right , "_D" ) +XE_HID_KEYBOARD_BINDING(LThumbDown, "LEFT_THUMB_DOWN" , keybind_left_thumb_down , "_S" ) +XE_HID_KEYBOARD_BINDING(LThumbUp, "LEFT_THUMB_UP" , keybind_left_thumb_up , "_W" ) +XE_HID_KEYBOARD_BINDING(LThumbPress, "LEFT_THUMB_PRESSED" , keybind_left_thumb , "F" ) +XE_HID_KEYBOARD_BINDING(RThumbUp, "RIGHT_THUMB_UP" , keybind_right_thumb_up , "0x26") +XE_HID_KEYBOARD_BINDING(RThumbDown, "RIGHT_THUMB_DOWN" , keybind_right_thumb_down , "0x28") +XE_HID_KEYBOARD_BINDING(RThumbRight, "RIGHT_THUMB_RIGHT" , keybind_right_thumb_right, "0x27") +XE_HID_KEYBOARD_BINDING(RThumbLeft, "RIGHT_THUMB_LEFT" , keybind_right_thumb_left , "0x25") +XE_HID_KEYBOARD_BINDING(RThumbPress, "RIGHT_THUMB_PRESSED", keybind_right_thumb , "K" ) +XE_HID_KEYBOARD_BINDING(X, "X" , keybind_x , "L" ) +XE_HID_KEYBOARD_BINDING(B, "B" , keybind_b , "0xDE") +XE_HID_KEYBOARD_BINDING(A, "A" , keybind_a , "0xBA") +XE_HID_KEYBOARD_BINDING(Y, "Y" , keybind_y , "P" ) +XE_HID_KEYBOARD_BINDING(LTrigger, "LEFT_TRIGGER" , keybind_left_trigger , "Q I" ) +XE_HID_KEYBOARD_BINDING(RTrigger, "RIGHT_TRIGGER" , keybind_right_trigger , "E O" ) +XE_HID_KEYBOARD_BINDING(Back, "BACK" , keybind_back , "Z" ) +XE_HID_KEYBOARD_BINDING(Start, "START" , keybind_start , "X" ) +XE_HID_KEYBOARD_BINDING(LShoulder, "LEFT_SHOULDER" , keybind_left_shoulder , "1" ) +XE_HID_KEYBOARD_BINDING(RShoulder, "RIGHT_SHOULDER" , keybind_right_shoulder , "3" ) +// clang-format on diff --git a/src/xenia/hid/keyboard/keyboard_hid.cc b/src/xenia/hid/keyboard/keyboard_hid.cc new file mode 100644 index 000000000..983ff4ba0 --- /dev/null +++ b/src/xenia/hid/keyboard/keyboard_hid.cc @@ -0,0 +1,26 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2014 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/hid/keyboard/keyboard_hid.h" + +#include "xenia/hid/keyboard/keyboard_input_driver.h" + +namespace xe { +namespace hid { +namespace keyboard { + +std::unique_ptr Create(xe::ui::Window* window, + size_t window_z_order) { + return std::make_unique( + window, window_z_order); +} + +} // namespace keyboard +} // namespace hid +} // namespace xe diff --git a/src/xenia/hid/keyboard/keyboard_hid.h b/src/xenia/hid/keyboard/keyboard_hid.h new file mode 100644 index 000000000..5d8915b42 --- /dev/null +++ b/src/xenia/hid/keyboard/keyboard_hid.h @@ -0,0 +1,28 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2014 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_HID_KEYBOARD_KEYBOARD_HID_H_ +#define XENIA_HID_KEYBOARD_KEYBOARD_HID_H_ + +#include + +#include "xenia/hid/input_system.h" + +namespace xe { +namespace hid { +namespace keyboard { + +std::unique_ptr Create(xe::ui::Window* window, + size_t window_z_order); + +} // namespace keyboard +} // namespace hid +} // namespace xe + +#endif // XENIA_HID_WINKEY_WINKEY_HID_H_ diff --git a/src/xenia/hid/keyboard/keyboard_input_driver.cc b/src/xenia/hid/keyboard/keyboard_input_driver.cc new file mode 100644 index 000000000..184db383a --- /dev/null +++ b/src/xenia/hid/keyboard/keyboard_input_driver.cc @@ -0,0 +1,340 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2022 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/hid/keyboard/keyboard_input_driver.h" + +#include "xenia/base/logging.h" +#include "xenia/hid/hid_flags.h" +#include "xenia/hid/input_system.h" +#include "xenia/ui/virtual_key.h" +#include "xenia/ui/window.h" + +#define XE_HID_KEYBOARD_BINDING(button, description, cvar_name, \ + cvar_default_value) \ + DEFINE_string(cvar_name, cvar_default_value, \ + "List of keys to bind to " description \ + ", separated by spaces", \ + "HID.Key") +#include "keyboard_binding_table.inc" +#undef XE_HID_KEYBOARD_BINDING + +namespace xe { +namespace hid { +namespace keyboard { + +void KeyboardInputDriver::ParseKeyBinding( + ui::VirtualKey output_key, const std::string_view description, + const std::string_view source_tokens) { + for (const std::string_view source_token : + utf8::split(source_tokens, " ", true)) { + KeyBinding key_binding; + key_binding.output_key = output_key; + + std::string_view token = source_token; + + if (utf8::starts_with(token, "_")) { + key_binding.uppercase = false; + token = token.substr(1); + } else if (utf8::starts_with(token, "^")) { + key_binding.uppercase = true; + token = token.substr(1); + } + + if (utf8::starts_with(token, "0x")) { + token = token.substr(2); + key_binding.input_key = static_cast( + string_util::from_string(token, true)); + } else if (token.size() == 1 && (token[0] >= 'A' && token[0] <= 'Z') || + (token[0] >= '0' && token[0] <= '9')) { + key_binding.input_key = static_cast(token[0]); + } + + if (key_binding.input_key == ui::VirtualKey::kNone) { + XELOGW( + "keyboard HID: failed to parse binding \"{}\" for controller input " + "{}.", + source_token, description); + continue; + } + + key_bindings_.push_back(key_binding); + XELOGI("keyboard HID: \"{}\" binds key 0x{:X} to controller input {}.", + source_token, static_cast(key_binding.input_key), + description); + } +} + +KeyboardInputDriver::KeyboardInputDriver(xe::ui::Window* window, + size_t window_z_order) + : InputDriver(window, window_z_order), window_input_listener_(*this) { +#define XE_HID_KEYBOARD_BINDING(button, description, cvar_name, \ + cvar_default_value) \ + ParseKeyBinding(xe::ui::VirtualKey::kXInputPad##button, description, \ + cvars::cvar_name); +#include "keyboard_binding_table.inc" +#undef XE_HID_KEYBOARD_BINDING + + window->AddInputListener(&window_input_listener_, window_z_order); +} + +KeyboardInputDriver::~KeyboardInputDriver() { + window()->RemoveInputListener(&window_input_listener_); +} + +X_STATUS KeyboardInputDriver::Setup() { return X_STATUS_SUCCESS; } + +X_RESULT KeyboardInputDriver::GetCapabilities(uint32_t user_index, + uint32_t flags, + X_INPUT_CAPABILITIES* out_caps) { + if (user_index != 0) { + return X_ERROR_DEVICE_NOT_CONNECTED; + } + + // TODO(benvanik): confirm with a real XInput controller. + out_caps->type = 0x01; // XINPUT_DEVTYPE_GAMEPAD + out_caps->sub_type = 0x01; // XINPUT_DEVSUBTYPE_GAMEPAD + out_caps->flags = 0; + out_caps->gamepad.buttons = 0xFFFF; + out_caps->gamepad.left_trigger = 0xFF; + out_caps->gamepad.right_trigger = 0xFF; + out_caps->gamepad.thumb_lx = (int16_t)0xFFFFu; + out_caps->gamepad.thumb_ly = (int16_t)0xFFFFu; + out_caps->gamepad.thumb_rx = (int16_t)0xFFFFu; + out_caps->gamepad.thumb_ry = (int16_t)0xFFFFu; + out_caps->vibration.left_motor_speed = 0; + out_caps->vibration.right_motor_speed = 0; + return X_ERROR_SUCCESS; +} + +X_RESULT KeyboardInputDriver::GetState(uint32_t user_index, + X_INPUT_STATE* out_state) { + if (user_index != 0) { + return X_ERROR_DEVICE_NOT_CONNECTED; + } + + packet_number_++; + + uint16_t buttons = 0; + uint8_t left_trigger = 0; + uint8_t right_trigger = 0; + int16_t thumb_lx = 0; + int16_t thumb_ly = 0; + int16_t thumb_rx = 0; + int16_t thumb_ry = 0; + + if (window()->HasFocus() && is_active()) { + for (const KeyBinding& b : key_bindings_) { + if (b.is_pressed) { + switch (b.output_key) { + case ui::VirtualKey::kXInputPadA: + buttons |= X_INPUT_GAMEPAD_A; + break; + case ui::VirtualKey::kXInputPadY: + buttons |= X_INPUT_GAMEPAD_Y; + break; + case ui::VirtualKey::kXInputPadB: + buttons |= X_INPUT_GAMEPAD_B; + break; + case ui::VirtualKey::kXInputPadX: + buttons |= X_INPUT_GAMEPAD_X; + break; + case ui::VirtualKey::kXInputPadGuide: + buttons |= X_INPUT_GAMEPAD_GUIDE; + break; + case ui::VirtualKey::kXInputPadDpadLeft: + buttons |= X_INPUT_GAMEPAD_DPAD_LEFT; + break; + case ui::VirtualKey::kXInputPadDpadRight: + buttons |= X_INPUT_GAMEPAD_DPAD_RIGHT; + break; + case ui::VirtualKey::kXInputPadDpadDown: + buttons |= X_INPUT_GAMEPAD_DPAD_DOWN; + break; + case ui::VirtualKey::kXInputPadDpadUp: + buttons |= X_INPUT_GAMEPAD_DPAD_UP; + break; + case ui::VirtualKey::kXInputPadRThumbPress: + buttons |= X_INPUT_GAMEPAD_RIGHT_THUMB; + break; + case ui::VirtualKey::kXInputPadLThumbPress: + buttons |= X_INPUT_GAMEPAD_LEFT_THUMB; + break; + case ui::VirtualKey::kXInputPadBack: + buttons |= X_INPUT_GAMEPAD_BACK; + break; + case ui::VirtualKey::kXInputPadStart: + buttons |= X_INPUT_GAMEPAD_START; + break; + case ui::VirtualKey::kXInputPadLShoulder: + buttons |= X_INPUT_GAMEPAD_LEFT_SHOULDER; + break; + case ui::VirtualKey::kXInputPadRShoulder: + buttons |= X_INPUT_GAMEPAD_RIGHT_SHOULDER; + break; + case ui::VirtualKey::kXInputPadLTrigger: + left_trigger = 0xFF; + break; + case ui::VirtualKey::kXInputPadRTrigger: + right_trigger = 0xFF; + break; + case ui::VirtualKey::kXInputPadLThumbLeft: + thumb_lx += SHRT_MIN; + break; + case ui::VirtualKey::kXInputPadLThumbRight: + thumb_lx += SHRT_MAX; + break; + case ui::VirtualKey::kXInputPadLThumbDown: + thumb_ly += SHRT_MIN; + break; + case ui::VirtualKey::kXInputPadLThumbUp: + thumb_ly += SHRT_MAX; + break; + case ui::VirtualKey::kXInputPadRThumbUp: + thumb_ry += SHRT_MAX; + break; + case ui::VirtualKey::kXInputPadRThumbDown: + thumb_ry += SHRT_MIN; + break; + case ui::VirtualKey::kXInputPadRThumbRight: + thumb_rx += SHRT_MAX; + break; + case ui::VirtualKey::kXInputPadRThumbLeft: + thumb_rx += SHRT_MIN; + break; + default: + assert_unhandled_case(b.output_key); + } + } + } + } + + out_state->packet_number = packet_number_; + out_state->gamepad.buttons = buttons; + out_state->gamepad.left_trigger = left_trigger; + out_state->gamepad.right_trigger = right_trigger; + out_state->gamepad.thumb_lx = thumb_lx; + out_state->gamepad.thumb_ly = thumb_ly; + out_state->gamepad.thumb_rx = thumb_rx; + out_state->gamepad.thumb_ry = thumb_ry; + + return X_ERROR_SUCCESS; +} + +X_RESULT KeyboardInputDriver::SetState(uint32_t user_index, + X_INPUT_VIBRATION* vibration) { + if (user_index != 0) { + return X_ERROR_DEVICE_NOT_CONNECTED; + } + + return X_ERROR_SUCCESS; +} + +X_RESULT KeyboardInputDriver::GetKeystroke(uint32_t user_index, uint32_t flags, + X_INPUT_KEYSTROKE* out_keystroke) { + if (user_index != 0) { + return X_ERROR_DEVICE_NOT_CONNECTED; + } + + if (!is_active()) { + return X_ERROR_EMPTY; + } + + X_RESULT result = X_ERROR_EMPTY; + + ui::VirtualKey xinput_virtual_key = ui::VirtualKey::kNone; + uint16_t unicode = 0; + uint16_t keystroke_flags = 0; + uint8_t hid_code = 0; + + // Pop from the queue. + KeyEvent evt; + { + auto global_lock = global_critical_region_.Acquire(); + if (key_events_.empty()) { + // No keys! + return X_ERROR_EMPTY; + } + evt = key_events_.front(); + key_events_.pop(); + } + + for (const KeyBinding& b : key_bindings_) { + if (b.input_key == evt.virtual_key && b.uppercase == evt.is_capital) { + xinput_virtual_key = b.output_key; + } + } + + if (xinput_virtual_key != ui::VirtualKey::kNone) { + if (evt.transition == true) { + keystroke_flags |= 0x0001; // XINPUT_KEYSTROKE_KEYDOWN + } else if (evt.transition == false) { + keystroke_flags |= 0x0002; // XINPUT_KEYSTROKE_KEYUP + } + + if (evt.prev_state == evt.transition) { + keystroke_flags |= 0x0004; // XINPUT_KEYSTROKE_REPEAT + } + + result = X_ERROR_SUCCESS; + } + + 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->hid_code = hid_code; + + // X_ERROR_EMPTY if no new keys + // X_ERROR_DEVICE_NOT_CONNECTED if no device + // X_ERROR_SUCCESS if key + return result; +} + +void KeyboardInputDriver::KeyboardWindowInputListener::OnKeyDown( + ui::KeyEvent& e) { + driver_.OnKey(e, true); +} + +void KeyboardInputDriver::KeyboardWindowInputListener::OnKeyUp( + ui::KeyEvent& e) { + driver_.OnKey(e, false); +} + +void KeyboardInputDriver::OnKey(ui::KeyEvent& e, bool is_down) { + if (!is_active()) { + return; + } + + KeyEvent key; + key.virtual_key = e.virtual_key(); + key.transition = is_down; + key.prev_state = e.prev_state(); + key.repeat_count = e.repeat_count(); + key.is_capital = e.is_shift_pressed(); + + auto global_lock = global_critical_region_.Acquire(); + key_events_.push(key); + bool found = false; + for (auto& key_binding : key_bindings_) { + if (key_binding.input_key == key.virtual_key) { + found = true; + if (key_binding.uppercase == e.is_shift_pressed()) { + key_binding.is_pressed = is_down; + } + } + } +} + +InputType KeyboardInputDriver::GetInputType() const { + return InputType::Controller; +} + +} // namespace keyboard +} // namespace hid +} // namespace xe diff --git a/src/xenia/hid/keyboard/keyboard_input_driver.h b/src/xenia/hid/keyboard/keyboard_input_driver.h new file mode 100644 index 000000000..0ae55f5fd --- /dev/null +++ b/src/xenia/hid/keyboard/keyboard_input_driver.h @@ -0,0 +1,86 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2022 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_HID_KEYBOARD_KEYBOARD_INPUT_DRIVER_H_ +#define XENIA_HID_KEYBOARD_KEYBOARD_INPUT_DRIVER_H_ + +#include + +#include "xenia/base/mutex.h" +#include "xenia/hid/input_driver.h" +#include "xenia/ui/virtual_key.h" + +namespace xe { +namespace hid { +namespace keyboard { + +class KeyboardInputDriver final : public InputDriver { + public: + explicit KeyboardInputDriver(xe::ui::Window* window, size_t window_z_order); + ~KeyboardInputDriver() override; + + X_STATUS Setup() override; + + X_RESULT GetCapabilities(uint32_t user_index, uint32_t flags, + X_INPUT_CAPABILITIES* out_caps) override; + X_RESULT GetState(uint32_t user_index, X_INPUT_STATE* out_state) override; + 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: + class KeyboardWindowInputListener final : public ui::WindowInputListener { + public: + explicit KeyboardWindowInputListener(KeyboardInputDriver& driver) + : driver_(driver) {} + + void OnKeyDown(ui::KeyEvent& e) override; + void OnKeyUp(ui::KeyEvent& e) override; + + private: + KeyboardInputDriver& driver_; + }; + + struct KeyEvent { + ui::VirtualKey virtual_key = ui::VirtualKey::kNone; + int repeat_count = 0; + bool transition = false; // going up(false) or going down(true) + bool prev_state = false; // down(true) or up(false) + bool is_capital = false; + }; + + struct KeyBinding { + ui::VirtualKey input_key = ui::VirtualKey::kNone; + ui::VirtualKey output_key = ui::VirtualKey::kNone; + bool uppercase = false; + bool is_pressed = false; + }; + + void ParseKeyBinding(ui::VirtualKey virtual_key, + const std::string_view description, + const std::string_view binding); + + void OnKey(ui::KeyEvent& e, bool is_down); + + xe::global_critical_region global_critical_region_; + + std::queue key_events_; + std::vector key_bindings_; + + KeyboardWindowInputListener window_input_listener_; + + uint32_t packet_number_ = 1; +}; + +} // namespace keyboard +} // namespace hid +} // namespace xe + +#endif // XENIA_HID_WINKEY_WINKEY_INPUT_DRIVER_H_ diff --git a/src/xenia/hid/keyboard/premake5.lua b/src/xenia/hid/keyboard/premake5.lua new file mode 100644 index 000000000..1516da586 --- /dev/null +++ b/src/xenia/hid/keyboard/premake5.lua @@ -0,0 +1,16 @@ +project_root = "../../../.." +include(project_root.."/tools/build") + +group("src") +project("xenia-hid-keyboard") + uuid("ab5cfd8d-7877-44cd-9526-63f7c5738609") + kind("StaticLib") + language("C++") + links({ + "xenia-base", + "xenia-hid", + "xenia-ui", + }) + defines({ + }) + local_platform_files()