summaryrefslogtreecommitdiffstats
path: root/src/hid_core/frontend/emulated_console.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/hid_core/frontend/emulated_console.cpp')
-rw-r--r--src/hid_core/frontend/emulated_console.cpp324
1 files changed, 324 insertions, 0 deletions
diff --git a/src/hid_core/frontend/emulated_console.cpp b/src/hid_core/frontend/emulated_console.cpp
new file mode 100644
index 000000000..114c22fb7
--- /dev/null
+++ b/src/hid_core/frontend/emulated_console.cpp
@@ -0,0 +1,324 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/settings.h"
+#include "hid_core/frontend/emulated_console.h"
+#include "hid_core/frontend/input_converter.h"
+
+namespace Core::HID {
+EmulatedConsole::EmulatedConsole() = default;
+
+EmulatedConsole::~EmulatedConsole() = default;
+
+void EmulatedConsole::ReloadFromSettings() {
+ // Using first motion device from player 1. No need to assign any unique config at the moment
+ const auto& player = Settings::values.players.GetValue()[0];
+ motion_params[0] = Common::ParamPackage(player.motions[0]);
+
+ ReloadInput();
+}
+
+void EmulatedConsole::SetTouchParams() {
+ std::size_t index = 0;
+
+ // We can't use mouse as touch if native mouse is enabled
+ if (!Settings::values.mouse_enabled) {
+ touch_params[index++] =
+ Common::ParamPackage{"engine:mouse,axis_x:0,axis_y:1,button:0,port:2"};
+ }
+
+ touch_params[index++] =
+ Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536"};
+ touch_params[index++] =
+ Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072"};
+
+ for (int i = 0; i < static_cast<int>(MaxActiveTouchInputs); i++) {
+ Common::ParamPackage touchscreen_param{};
+ touchscreen_param.Set("engine", "touch");
+ touchscreen_param.Set("axis_x", i * 2);
+ touchscreen_param.Set("axis_y", (i * 2) + 1);
+ touchscreen_param.Set("button", i);
+ touch_params[index++] = std::move(touchscreen_param);
+ }
+
+ if (Settings::values.touch_from_button_maps.empty()) {
+ LOG_WARNING(Input, "touch_from_button_maps is unset by frontend config");
+ return;
+ }
+
+ const auto button_index =
+ static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue());
+ const auto& touch_buttons = Settings::values.touch_from_button_maps[button_index].buttons;
+
+ // Map the rest of the fingers from touch from button configuration
+ for (const auto& config_entry : touch_buttons) {
+ if (index >= MaxTouchDevices) {
+ continue;
+ }
+ Common::ParamPackage params{config_entry};
+ Common::ParamPackage touch_button_params;
+ const int x = params.Get("x", 0);
+ const int y = params.Get("y", 0);
+ params.Erase("x");
+ params.Erase("y");
+ touch_button_params.Set("engine", "touch_from_button");
+ touch_button_params.Set("button", params.Serialize());
+ touch_button_params.Set("x", x);
+ touch_button_params.Set("y", y);
+ touch_params[index] = std::move(touch_button_params);
+ index++;
+ }
+}
+
+void EmulatedConsole::ReloadInput() {
+ // If you load any device here add the equivalent to the UnloadInput() function
+ SetTouchParams();
+
+ motion_params[1] = Common::ParamPackage{"engine:virtual_gamepad,port:8,motion:0"};
+
+ for (std::size_t index = 0; index < motion_devices.size(); ++index) {
+ motion_devices[index] = Common::Input::CreateInputDevice(motion_params[index]);
+ if (!motion_devices[index]) {
+ continue;
+ }
+ motion_devices[index]->SetCallback({
+ .on_change =
+ [this](const Common::Input::CallbackStatus& callback) { SetMotion(callback); },
+ });
+ }
+
+ // Restore motion state
+ auto& emulated_motion = console.motion_values.emulated;
+ auto& motion = console.motion_state;
+ emulated_motion.ResetRotations();
+ emulated_motion.ResetQuaternion();
+ motion.accel = emulated_motion.GetAcceleration();
+ motion.gyro = emulated_motion.GetGyroscope();
+ motion.rotation = emulated_motion.GetRotations();
+ motion.orientation = emulated_motion.GetOrientation();
+ motion.is_at_rest = !emulated_motion.IsMoving(motion_sensitivity);
+
+ // Unique index for identifying touch device source
+ std::size_t index = 0;
+ for (auto& touch_device : touch_devices) {
+ touch_device = Common::Input::CreateInputDevice(touch_params[index]);
+ if (!touch_device) {
+ continue;
+ }
+ touch_device->SetCallback({
+ .on_change =
+ [this, index](const Common::Input::CallbackStatus& callback) {
+ SetTouch(callback, index);
+ },
+ });
+ index++;
+ }
+}
+
+void EmulatedConsole::UnloadInput() {
+ for (auto& motion : motion_devices) {
+ motion.reset();
+ }
+ for (auto& touch : touch_devices) {
+ touch.reset();
+ }
+}
+
+void EmulatedConsole::EnableConfiguration() {
+ is_configuring = true;
+ SaveCurrentConfig();
+}
+
+void EmulatedConsole::DisableConfiguration() {
+ is_configuring = false;
+}
+
+bool EmulatedConsole::IsConfiguring() const {
+ return is_configuring;
+}
+
+void EmulatedConsole::SaveCurrentConfig() {
+ if (!is_configuring) {
+ return;
+ }
+}
+
+void EmulatedConsole::RestoreConfig() {
+ if (!is_configuring) {
+ return;
+ }
+ ReloadFromSettings();
+}
+
+Common::ParamPackage EmulatedConsole::GetMotionParam() const {
+ return motion_params[0];
+}
+
+void EmulatedConsole::SetMotionParam(Common::ParamPackage param) {
+ motion_params[0] = std::move(param);
+ ReloadInput();
+}
+
+void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
+ std::unique_lock lock{mutex};
+ auto& raw_status = console.motion_values.raw_status;
+ auto& emulated = console.motion_values.emulated;
+
+ raw_status = TransformToMotion(callback);
+ emulated.SetAcceleration(Common::Vec3f{
+ raw_status.accel.x.value,
+ raw_status.accel.y.value,
+ raw_status.accel.z.value,
+ });
+ emulated.SetGyroscope(Common::Vec3f{
+ raw_status.gyro.x.value,
+ raw_status.gyro.y.value,
+ raw_status.gyro.z.value,
+ });
+ emulated.UpdateRotation(raw_status.delta_timestamp);
+ emulated.UpdateOrientation(raw_status.delta_timestamp);
+
+ if (is_configuring) {
+ lock.unlock();
+ TriggerOnChange(ConsoleTriggerType::Motion);
+ return;
+ }
+
+ auto& motion = console.motion_state;
+ motion.accel = emulated.GetAcceleration();
+ motion.gyro = emulated.GetGyroscope();
+ motion.rotation = emulated.GetRotations();
+ motion.orientation = emulated.GetOrientation();
+ motion.quaternion = emulated.GetQuaternion();
+ motion.gyro_bias = emulated.GetGyroBias();
+ motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
+ // Find what is this value
+ motion.verticalization_error = 0.0f;
+
+ lock.unlock();
+ TriggerOnChange(ConsoleTriggerType::Motion);
+}
+
+void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index) {
+ if (index >= MaxTouchDevices) {
+ return;
+ }
+ std::unique_lock lock{mutex};
+
+ 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<int>(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) {
+ lock.unlock();
+ TriggerOnChange(ConsoleTriggerType::Touch);
+ return;
+ }
+
+ // Touch outside allowed range. Ignore input
+ if (touch_index.value() >= MaxActiveTouchInputs) {
+ return;
+ }
+
+ 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();
+ TriggerOnChange(ConsoleTriggerType::Touch);
+}
+
+ConsoleMotionValues EmulatedConsole::GetMotionValues() const {
+ std::scoped_lock lock{mutex};
+ return console.motion_values;
+}
+
+TouchValues EmulatedConsole::GetTouchValues() const {
+ std::scoped_lock lock{mutex};
+ return console.touch_values;
+}
+
+ConsoleMotion EmulatedConsole::GetMotion() const {
+ std::scoped_lock lock{mutex};
+ return console.motion_state;
+}
+
+TouchFingerState EmulatedConsole::GetTouch() const {
+ std::scoped_lock lock{mutex};
+ 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) {
+ std::scoped_lock lock{callback_mutex};
+ for (const auto& poller_pair : callback_list) {
+ const ConsoleUpdateCallback& poller = poller_pair.second;
+ if (poller.on_change) {
+ poller.on_change(type);
+ }
+ }
+}
+
+int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) {
+ std::scoped_lock lock{callback_mutex};
+ callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
+ return last_callback_key++;
+}
+
+void EmulatedConsole::DeleteCallback(int key) {
+ std::scoped_lock lock{callback_mutex};
+ const auto& iterator = callback_list.find(key);
+ if (iterator == callback_list.end()) {
+ LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
+ return;
+ }
+ callback_list.erase(iterator);
+}
+} // namespace Core::HID