core: hid: Implement true multitouch support
This commit is contained in:
		| @@ -19,27 +19,26 @@ void EmulatedConsole::ReloadFromSettings() { | |||||||
| } | } | ||||||
|  |  | ||||||
| void EmulatedConsole::SetTouchParams() { | void EmulatedConsole::SetTouchParams() { | ||||||
|     // TODO(german77): Support any number of fingers |  | ||||||
|     std::size_t index = 0; |     std::size_t index = 0; | ||||||
|  |  | ||||||
|     // Hardcode mouse, touchscreen and cemuhook parameters |     // We can't use mouse as touch if native mouse is enabled | ||||||
|     if (!Settings::values.mouse_enabled) { |     if (!Settings::values.mouse_enabled) { | ||||||
|         // We can't use mouse as touch if native mouse is enabled |  | ||||||
|         touch_params[index++] = Common::ParamPackage{"engine:mouse,axis_x:10,axis_y:11,button:0"}; |         touch_params[index++] = Common::ParamPackage{"engine:mouse,axis_x:10,axis_y:11,button:0"}; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     touch_params[index++] = |     touch_params[index++] = | ||||||
|         Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0,touch_id:0"}; |         Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536"}; | ||||||
|     touch_params[index++] = |     touch_params[index++] = | ||||||
|         Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1,touch_id:1"}; |         Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072"}; | ||||||
|     touch_params[index++] = |  | ||||||
|         Common::ParamPackage{"engine:touch,axis_x:4,axis_y:5,button:2,touch_id:2"}; |     for (int i = 0; i < static_cast<int>(MaxActiveTouchInputs); i++) { | ||||||
|     touch_params[index++] = |         Common::ParamPackage touchscreen_param{}; | ||||||
|         Common::ParamPackage{"engine:touch,axis_x:6,axis_y:7,button:3,touch_id:3"}; |         touchscreen_param.Set("engine", "touch"); | ||||||
|     touch_params[index++] = |         touchscreen_param.Set("axis_x", i * 2); | ||||||
|         Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536,touch_id:0"}; |         touchscreen_param.Set("axis_y", (i * 2) + 1); | ||||||
|     touch_params[index++] = |         touchscreen_param.Set("button", i); | ||||||
|         Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072,touch_id:1"}; |         touch_params[index++] = touchscreen_param; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     const auto button_index = |     const auto button_index = | ||||||
|         static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue()); |         static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue()); | ||||||
| @@ -47,7 +46,7 @@ void EmulatedConsole::SetTouchParams() { | |||||||
|  |  | ||||||
|     // Map the rest of the fingers from touch from button configuration |     // Map the rest of the fingers from touch from button configuration | ||||||
|     for (const auto& config_entry : touch_buttons) { |     for (const auto& config_entry : touch_buttons) { | ||||||
|         if (index >= touch_params.size()) { |         if (index >= MaxTouchDevices) { | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|         Common::ParamPackage params{config_entry}; |         Common::ParamPackage params{config_entry}; | ||||||
| @@ -60,7 +59,6 @@ void EmulatedConsole::SetTouchParams() { | |||||||
|         touch_button_params.Set("button", params.Serialize()); |         touch_button_params.Set("button", params.Serialize()); | ||||||
|         touch_button_params.Set("x", x); |         touch_button_params.Set("x", x); | ||||||
|         touch_button_params.Set("y", y); |         touch_button_params.Set("y", y); | ||||||
|         touch_button_params.Set("touch_id", static_cast<int>(index)); |  | ||||||
|         touch_params[index] = touch_button_params; |         touch_params[index] = touch_button_params; | ||||||
|         index++; |         index++; | ||||||
|     } |     } | ||||||
| @@ -178,12 +176,38 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) { | |||||||
| } | } | ||||||
|  |  | ||||||
| void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index) { | void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index) { | ||||||
|     if (index >= console.touch_values.size()) { |     if (index >= MaxTouchDevices) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     std::unique_lock lock{mutex}; |     std::unique_lock lock{mutex}; | ||||||
|  |  | ||||||
|     console.touch_values[index] = TransformToTouch(callback); |     const auto touch_input = TransformToTouch(callback); | ||||||
|  |     auto touch_index = GetIndexFromFingerId(index); | ||||||
|  |     bool is_new_input = false; | ||||||
|  |  | ||||||
|  |     if (!touch_index.has_value() && touch_input.pressed.value) { | ||||||
|  |         touch_index = GetNextFreeIndex(); | ||||||
|  |         is_new_input = true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // No free entries or invalid state. Ignore input | ||||||
|  |     if (!touch_index.has_value()) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     auto& touch_value = console.touch_values[touch_index.value()]; | ||||||
|  |  | ||||||
|  |     if (is_new_input) { | ||||||
|  |         touch_value.pressed.value = true; | ||||||
|  |         touch_value.id = static_cast<u32>(index); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     touch_value.x = touch_input.x; | ||||||
|  |     touch_value.y = touch_input.y; | ||||||
|  |  | ||||||
|  |     if (!touch_input.pressed.value) { | ||||||
|  |         touch_value.pressed.value = false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (is_configuring) { |     if (is_configuring) { | ||||||
|         lock.unlock(); |         lock.unlock(); | ||||||
| @@ -191,11 +215,15 @@ void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, st | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // TODO(german77): Remap touch id in sequential order |     // Touch outside allowed range. Ignore input | ||||||
|     console.touch_state[index] = { |     if (touch_index.value() >= MaxActiveTouchInputs) { | ||||||
|         .position = {console.touch_values[index].x.value, console.touch_values[index].y.value}, |         return; | ||||||
|         .id = static_cast<u32>(console.touch_values[index].id), |     } | ||||||
|         .pressed = console.touch_values[index].pressed.value, |  | ||||||
|  |     console.touch_state[touch_index.value()] = { | ||||||
|  |         .position = {touch_value.x.value, touch_value.y.value}, | ||||||
|  |         .id = static_cast<u32>(touch_index.value()), | ||||||
|  |         .pressed = touch_input.pressed.value, | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     lock.unlock(); |     lock.unlock(); | ||||||
| @@ -222,6 +250,28 @@ TouchFingerState EmulatedConsole::GetTouch() const { | |||||||
|     return console.touch_state; |     return console.touch_state; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | std::optional<std::size_t> EmulatedConsole::GetIndexFromFingerId(std::size_t finger_id) const { | ||||||
|  |     for (std::size_t index = 0; index < MaxTouchDevices; ++index) { | ||||||
|  |         const auto& finger = console.touch_values[index]; | ||||||
|  |         if (!finger.pressed.value) { | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |         if (finger.id == static_cast<int>(finger_id)) { | ||||||
|  |             return index; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return std::nullopt; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::optional<std::size_t> EmulatedConsole::GetNextFreeIndex() const { | ||||||
|  |     for (std::size_t index = 0; index < MaxTouchDevices; ++index) { | ||||||
|  |         if (!console.touch_values[index].pressed.value) { | ||||||
|  |             return index; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return std::nullopt; | ||||||
|  | } | ||||||
|  |  | ||||||
| void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) { | void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) { | ||||||
|     std::scoped_lock lock{callback_mutex}; |     std::scoped_lock lock{callback_mutex}; | ||||||
|     for (const auto& poller_pair : callback_list) { |     for (const auto& poller_pair : callback_list) { | ||||||
|   | |||||||
| @@ -7,6 +7,7 @@ | |||||||
| #include <functional> | #include <functional> | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <mutex> | #include <mutex> | ||||||
|  | #include <optional> | ||||||
| #include <unordered_map> | #include <unordered_map> | ||||||
|  |  | ||||||
| #include "common/common_funcs.h" | #include "common/common_funcs.h" | ||||||
| @@ -20,6 +21,8 @@ | |||||||
| #include "core/hid/motion_input.h" | #include "core/hid/motion_input.h" | ||||||
|  |  | ||||||
| namespace Core::HID { | namespace Core::HID { | ||||||
|  | static constexpr std::size_t MaxTouchDevices = 32; | ||||||
|  | static constexpr std::size_t MaxActiveTouchInputs = 16; | ||||||
|  |  | ||||||
| struct ConsoleMotionInfo { | struct ConsoleMotionInfo { | ||||||
|     Common::Input::MotionStatus raw_status{}; |     Common::Input::MotionStatus raw_status{}; | ||||||
| @@ -27,13 +30,13 @@ struct ConsoleMotionInfo { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| using ConsoleMotionDevices = std::unique_ptr<Common::Input::InputDevice>; | using ConsoleMotionDevices = std::unique_ptr<Common::Input::InputDevice>; | ||||||
| using TouchDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, 16>; | using TouchDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, MaxTouchDevices>; | ||||||
|  |  | ||||||
| using ConsoleMotionParams = Common::ParamPackage; | using ConsoleMotionParams = Common::ParamPackage; | ||||||
| using TouchParams = std::array<Common::ParamPackage, 16>; | using TouchParams = std::array<Common::ParamPackage, MaxTouchDevices>; | ||||||
|  |  | ||||||
| using ConsoleMotionValues = ConsoleMotionInfo; | using ConsoleMotionValues = ConsoleMotionInfo; | ||||||
| using TouchValues = std::array<Common::Input::TouchStatus, 16>; | using TouchValues = std::array<Common::Input::TouchStatus, MaxTouchDevices>; | ||||||
|  |  | ||||||
| struct TouchFinger { | struct TouchFinger { | ||||||
|     u64 last_touch{}; |     u64 last_touch{}; | ||||||
| @@ -55,7 +58,7 @@ struct ConsoleMotion { | |||||||
|     bool is_at_rest{}; |     bool is_at_rest{}; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| using TouchFingerState = std::array<TouchFinger, 16>; | using TouchFingerState = std::array<TouchFinger, MaxActiveTouchInputs>; | ||||||
|  |  | ||||||
| struct ConsoleStatus { | struct ConsoleStatus { | ||||||
|     // Data from input_common |     // Data from input_common | ||||||
| @@ -166,6 +169,10 @@ private: | |||||||
|      */ |      */ | ||||||
|     void SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index); |     void SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index); | ||||||
|  |  | ||||||
|  |     std::optional<std::size_t> GetIndexFromFingerId(std::size_t finger_id) const; | ||||||
|  |  | ||||||
|  |     std::optional<std::size_t> GetNextFreeIndex() const; | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Triggers a callback that something has changed on the console status |      * Triggers a callback that something has changed on the console status | ||||||
|      * @param type Input type of the event to trigger |      * @param type Input type of the event to trigger | ||||||
|   | |||||||
| @@ -200,9 +200,6 @@ Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus& | |||||||
|     x = std::clamp(x, 0.0f, 1.0f); |     x = std::clamp(x, 0.0f, 1.0f); | ||||||
|     y = std::clamp(y, 0.0f, 1.0f); |     y = std::clamp(y, 0.0f, 1.0f); | ||||||
|  |  | ||||||
|     // Limit id to maximum number of fingers |  | ||||||
|     status.id = std::clamp(status.id, 0, 16); |  | ||||||
|  |  | ||||||
|     if (status.pressed.inverted) { |     if (status.pressed.inverted) { | ||||||
|         status.pressed.value = !status.pressed.value; |         status.pressed.value = !status.pressed.value; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -10,8 +10,8 @@ namespace InputCommon { | |||||||
| class TouchFromButtonDevice final : public Common::Input::InputDevice { | class TouchFromButtonDevice final : public Common::Input::InputDevice { | ||||||
| public: | public: | ||||||
|     using Button = std::unique_ptr<Common::Input::InputDevice>; |     using Button = std::unique_ptr<Common::Input::InputDevice>; | ||||||
|     TouchFromButtonDevice(Button button_, int touch_id_, float x_, float y_) |     TouchFromButtonDevice(Button button_, float x_, float y_) | ||||||
|         : button(std::move(button_)), touch_id(touch_id_), x(x_), y(y_) { |         : button(std::move(button_)), x(x_), y(y_) { | ||||||
|         last_button_value = false; |         last_button_value = false; | ||||||
|         button->SetCallback({ |         button->SetCallback({ | ||||||
|             .on_change = |             .on_change = | ||||||
| @@ -34,7 +34,6 @@ public: | |||||||
|             .pressed = button_status, |             .pressed = button_status, | ||||||
|             .x = {}, |             .x = {}, | ||||||
|             .y = {}, |             .y = {}, | ||||||
|             .id = touch_id, |  | ||||||
|         }; |         }; | ||||||
|         status.x.properties = properties; |         status.x.properties = properties; | ||||||
|         status.y.properties = properties; |         status.y.properties = properties; | ||||||
| @@ -62,7 +61,6 @@ public: | |||||||
| private: | private: | ||||||
|     Button button; |     Button button; | ||||||
|     bool last_button_value; |     bool last_button_value; | ||||||
|     const int touch_id; |  | ||||||
|     const float x; |     const float x; | ||||||
|     const float y; |     const float y; | ||||||
|     const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false}; |     const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false}; | ||||||
| @@ -73,10 +71,9 @@ std::unique_ptr<Common::Input::InputDevice> TouchFromButton::Create( | |||||||
|     const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize(); |     const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize(); | ||||||
|     auto button = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>( |     auto button = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>( | ||||||
|         params.Get("button", null_engine)); |         params.Get("button", null_engine)); | ||||||
|     const auto touch_id = params.Get("touch_id", 0); |  | ||||||
|     const float x = params.Get("x", 0.0f) / 1280.0f; |     const float x = params.Get("x", 0.0f) / 1280.0f; | ||||||
|     const float y = params.Get("y", 0.0f) / 720.0f; |     const float y = params.Get("y", 0.0f) / 720.0f; | ||||||
|     return std::make_unique<TouchFromButtonDevice>(std::move(button), touch_id, x, y); |     return std::make_unique<TouchFromButtonDevice>(std::move(button), x, y); | ||||||
| } | } | ||||||
|  |  | ||||||
| } // namespace InputCommon | } // namespace InputCommon | ||||||
|   | |||||||
| @@ -229,13 +229,12 @@ private: | |||||||
|  |  | ||||||
| class InputFromTouch final : public Common::Input::InputDevice { | class InputFromTouch final : public Common::Input::InputDevice { | ||||||
| public: | public: | ||||||
|     explicit InputFromTouch(PadIdentifier identifier_, int touch_id_, int button_, bool toggle_, |     explicit InputFromTouch(PadIdentifier identifier_, int button_, bool toggle_, bool inverted_, | ||||||
|                             bool inverted_, int axis_x_, int axis_y_, |                             int axis_x_, int axis_y_, Common::Input::AnalogProperties properties_x_, | ||||||
|                             Common::Input::AnalogProperties properties_x_, |  | ||||||
|                             Common::Input::AnalogProperties properties_y_, |                             Common::Input::AnalogProperties properties_y_, | ||||||
|                             InputEngine* input_engine_) |                             InputEngine* input_engine_) | ||||||
|         : identifier(identifier_), touch_id(touch_id_), button(button_), toggle(toggle_), |         : identifier(identifier_), button(button_), toggle(toggle_), inverted(inverted_), | ||||||
|           inverted(inverted_), axis_x(axis_x_), axis_y(axis_y_), properties_x(properties_x_), |           axis_x(axis_x_), axis_y(axis_y_), properties_x(properties_x_), | ||||||
|           properties_y(properties_y_), input_engine(input_engine_) { |           properties_y(properties_y_), input_engine(input_engine_) { | ||||||
|         UpdateCallback engine_callback{[this]() { OnChange(); }}; |         UpdateCallback engine_callback{[this]() { OnChange(); }}; | ||||||
|         const InputIdentifier button_input_identifier{ |         const InputIdentifier button_input_identifier{ | ||||||
| @@ -271,8 +270,7 @@ public: | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     Common::Input::TouchStatus GetStatus() const { |     Common::Input::TouchStatus GetStatus() const { | ||||||
|         Common::Input::TouchStatus status; |         Common::Input::TouchStatus status{}; | ||||||
|         status.id = touch_id; |  | ||||||
|         status.pressed = { |         status.pressed = { | ||||||
|             .value = input_engine->GetButton(identifier, button), |             .value = input_engine->GetButton(identifier, button), | ||||||
|             .inverted = inverted, |             .inverted = inverted, | ||||||
| @@ -307,7 +305,6 @@ public: | |||||||
|  |  | ||||||
| private: | private: | ||||||
|     const PadIdentifier identifier; |     const PadIdentifier identifier; | ||||||
|     const int touch_id; |  | ||||||
|     const int button; |     const int button; | ||||||
|     const bool toggle; |     const bool toggle; | ||||||
|     const bool inverted; |     const bool inverted; | ||||||
| @@ -919,7 +916,6 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTriggerDevice( | |||||||
|  |  | ||||||
| std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTouchDevice( | std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTouchDevice( | ||||||
|     const Common::ParamPackage& params) { |     const Common::ParamPackage& params) { | ||||||
|     const auto touch_id = params.Get("touch_id", 0); |  | ||||||
|     const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f); |     const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f); | ||||||
|     const auto range = std::clamp(params.Get("range", 1.0f), 0.25f, 1.50f); |     const auto range = std::clamp(params.Get("range", 1.0f), 0.25f, 1.50f); | ||||||
|     const auto threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f); |     const auto threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f); | ||||||
| @@ -954,8 +950,8 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTouchDevice( | |||||||
|     input_engine->PreSetAxis(identifier, axis_x); |     input_engine->PreSetAxis(identifier, axis_x); | ||||||
|     input_engine->PreSetAxis(identifier, axis_y); |     input_engine->PreSetAxis(identifier, axis_y); | ||||||
|     input_engine->PreSetButton(identifier, button); |     input_engine->PreSetButton(identifier, button); | ||||||
|     return std::make_unique<InputFromTouch>(identifier, touch_id, button, toggle, inverted, axis_x, |     return std::make_unique<InputFromTouch>(identifier, button, toggle, inverted, axis_x, axis_y, | ||||||
|                                             axis_y, properties_x, properties_y, input_engine.get()); |                                             properties_x, properties_y, input_engine.get()); | ||||||
| } | } | ||||||
|  |  | ||||||
| std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateBatteryDevice( | std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateBatteryDevice( | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user