summaryrefslogtreecommitdiffstats
path: root/src/core/hid/emulated_devices.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/core/hid/emulated_devices.cpp349
1 files changed, 349 insertions, 0 deletions
diff --git a/src/core/hid/emulated_devices.cpp b/src/core/hid/emulated_devices.cpp
new file mode 100644
index 000000000..3caf90714
--- /dev/null
+++ b/src/core/hid/emulated_devices.cpp
@@ -0,0 +1,349 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included
+
+#include <fmt/format.h>
+
+#include "core/hid/emulated_devices.h"
+#include "core/hid/input_converter.h"
+
+namespace Core::HID {
+
+EmulatedDevices::EmulatedDevices() {}
+
+EmulatedDevices::~EmulatedDevices() = default;
+
+void EmulatedDevices::ReloadFromSettings() {
+ const auto& mouse = Settings::values.mouse_buttons;
+
+ for (std::size_t index = 0; index < mouse.size(); ++index) {
+ mouse_button_params[index] = Common::ParamPackage(mouse[index]);
+ }
+ ReloadInput();
+}
+
+void EmulatedDevices::ReloadInput() {
+ std::transform(mouse_button_params.begin() + Settings::NativeMouseButton::MOUSE_HID_BEGIN,
+ mouse_button_params.begin() + Settings::NativeMouseButton::MOUSE_HID_END,
+ mouse_button_devices.begin(), Input::CreateDevice<Input::InputDevice>);
+
+ std::transform(Settings::values.keyboard_keys.begin(), Settings::values.keyboard_keys.end(),
+ keyboard_devices.begin(), Input::CreateDeviceFromString<Input::InputDevice>);
+
+ std::transform(Settings::values.keyboard_mods.begin(), Settings::values.keyboard_mods.end(),
+ keyboard_modifier_devices.begin(),
+ Input::CreateDeviceFromString<Input::InputDevice>);
+
+ for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) {
+ if (!mouse_button_devices[index]) {
+ continue;
+ }
+ Input::InputCallback button_callback{
+ [this, index](Input::CallbackStatus callback) { SetMouseButton(callback, index); }};
+ mouse_button_devices[index]->SetCallback(button_callback);
+ }
+
+ for (std::size_t index = 0; index < keyboard_devices.size(); ++index) {
+ if (!keyboard_devices[index]) {
+ continue;
+ }
+ Input::InputCallback button_callback{
+ [this, index](Input::CallbackStatus callback) { SetKeyboardButton(callback, index); }};
+ keyboard_devices[index]->SetCallback(button_callback);
+ }
+
+ for (std::size_t index = 0; index < keyboard_modifier_devices.size(); ++index) {
+ if (!keyboard_modifier_devices[index]) {
+ continue;
+ }
+ Input::InputCallback button_callback{[this, index](Input::CallbackStatus callback) {
+ SetKeyboardModifier(callback, index);
+ }};
+ keyboard_modifier_devices[index]->SetCallback(button_callback);
+ }
+}
+
+void EmulatedDevices::UnloadInput() {
+ for (auto& button : mouse_button_devices) {
+ button.reset();
+ }
+ for (auto& button : keyboard_devices) {
+ button.reset();
+ }
+ for (auto& button : keyboard_modifier_devices) {
+ button.reset();
+ }
+}
+
+void EmulatedDevices::EnableConfiguration() {
+ is_configuring = true;
+ SaveCurrentConfig();
+}
+
+void EmulatedDevices::DisableConfiguration() {
+ is_configuring = false;
+}
+
+bool EmulatedDevices::IsConfiguring() const {
+ return is_configuring;
+}
+
+void EmulatedDevices::SaveCurrentConfig() {
+ if (!is_configuring) {
+ return;
+ }
+
+ auto& mouse = Settings::values.mouse_buttons;
+
+ for (std::size_t index = 0; index < mouse.size(); ++index) {
+ mouse[index] = mouse_button_params[index].Serialize();
+ }
+}
+
+void EmulatedDevices::RestoreConfig() {
+ if (!is_configuring) {
+ return;
+ }
+ ReloadFromSettings();
+}
+
+Common::ParamPackage EmulatedDevices::GetMouseButtonParam(std::size_t index) const {
+ if (index >= mouse_button_params.size()) {
+ return {};
+ }
+ return mouse_button_params[index];
+}
+
+void EmulatedDevices::SetButtonParam(std::size_t index, Common::ParamPackage param) {
+ if (index >= mouse_button_params.size()) {
+ return;
+ }
+ mouse_button_params[index] = param;
+ ReloadInput();
+}
+
+void EmulatedDevices::SetKeyboardButton(Input::CallbackStatus callback, std::size_t index) {
+ if (index >= device_status.keyboard_values.size()) {
+ return;
+ }
+ std::lock_guard lock{mutex};
+ bool value_changed = false;
+ const auto new_status = TransformToButton(callback);
+ auto& current_status = device_status.keyboard_values[index];
+ current_status.toggle = new_status.toggle;
+
+ // Update button status with current
+ if (!current_status.toggle) {
+ current_status.locked = false;
+ if (current_status.value != new_status.value) {
+ current_status.value = new_status.value;
+ value_changed = true;
+ }
+ } else {
+ // Toggle button and lock status
+ if (new_status.value && !current_status.locked) {
+ current_status.locked = true;
+ current_status.value = !current_status.value;
+ value_changed = true;
+ }
+
+ // Unlock button ready for next press
+ if (!new_status.value && current_status.locked) {
+ current_status.locked = false;
+ }
+ }
+
+ if (!value_changed) {
+ return;
+ }
+
+ if (is_configuring) {
+ TriggerOnChange(DeviceTriggerType::Keyboard);
+ return;
+ }
+
+ // TODO(german77): Do this properly
+ // switch (index) {
+ // case Settings::NativeKeyboard::A:
+ // interface_status.keyboard_state.a.Assign(current_status.value);
+ // break;
+ // ....
+ //}
+
+ TriggerOnChange(DeviceTriggerType::Keyboard);
+}
+
+void EmulatedDevices::SetKeyboardModifier(Input::CallbackStatus callback, std::size_t index) {
+ if (index >= device_status.keyboard_moddifier_values.size()) {
+ return;
+ }
+ std::lock_guard lock{mutex};
+ bool value_changed = false;
+ const auto new_status = TransformToButton(callback);
+ auto& current_status = device_status.keyboard_moddifier_values[index];
+ current_status.toggle = new_status.toggle;
+
+ // Update button status with current
+ if (!current_status.toggle) {
+ current_status.locked = false;
+ if (current_status.value != new_status.value) {
+ current_status.value = new_status.value;
+ value_changed = true;
+ }
+ } else {
+ // Toggle button and lock status
+ if (new_status.value && !current_status.locked) {
+ current_status.locked = true;
+ current_status.value = !current_status.value;
+ value_changed = true;
+ }
+
+ // Unlock button ready for next press
+ if (!new_status.value && current_status.locked) {
+ current_status.locked = false;
+ }
+ }
+
+ if (!value_changed) {
+ return;
+ }
+
+ if (is_configuring) {
+ TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
+ return;
+ }
+
+ switch (index) {
+ case Settings::NativeKeyboard::LeftControl:
+ case Settings::NativeKeyboard::RightControl:
+ device_status.keyboard_moddifier_state.control.Assign(current_status.value);
+ break;
+ case Settings::NativeKeyboard::LeftShift:
+ case Settings::NativeKeyboard::RightShift:
+ device_status.keyboard_moddifier_state.shift.Assign(current_status.value);
+ break;
+ case Settings::NativeKeyboard::LeftAlt:
+ device_status.keyboard_moddifier_state.left_alt.Assign(current_status.value);
+ break;
+ case Settings::NativeKeyboard::RightAlt:
+ device_status.keyboard_moddifier_state.right_alt.Assign(current_status.value);
+ break;
+ case Settings::NativeKeyboard::CapsLock:
+ device_status.keyboard_moddifier_state.caps_lock.Assign(current_status.value);
+ break;
+ case Settings::NativeKeyboard::ScrollLock:
+ device_status.keyboard_moddifier_state.scroll_lock.Assign(current_status.value);
+ break;
+ case Settings::NativeKeyboard::NumLock:
+ device_status.keyboard_moddifier_state.num_lock.Assign(current_status.value);
+ break;
+ }
+
+ TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
+}
+
+void EmulatedDevices::SetMouseButton(Input::CallbackStatus callback, std::size_t index) {
+ if (index >= device_status.mouse_button_values.size()) {
+ return;
+ }
+ std::lock_guard lock{mutex};
+ bool value_changed = false;
+ const auto new_status = TransformToButton(callback);
+ auto& current_status = device_status.mouse_button_values[index];
+ current_status.toggle = new_status.toggle;
+
+ // Update button status with current
+ if (!current_status.toggle) {
+ current_status.locked = false;
+ if (current_status.value != new_status.value) {
+ current_status.value = new_status.value;
+ value_changed = true;
+ }
+ } else {
+ // Toggle button and lock status
+ if (new_status.value && !current_status.locked) {
+ current_status.locked = true;
+ current_status.value = !current_status.value;
+ value_changed = true;
+ }
+
+ // Unlock button ready for next press
+ if (!new_status.value && current_status.locked) {
+ current_status.locked = false;
+ }
+ }
+
+ if (!value_changed) {
+ return;
+ }
+
+ if (is_configuring) {
+ TriggerOnChange(DeviceTriggerType::Mouse);
+ return;
+ }
+
+ switch (index) {
+ case Settings::NativeMouseButton::Left:
+ device_status.mouse_button_state.left.Assign(current_status.value);
+ break;
+ case Settings::NativeMouseButton::Right:
+ device_status.mouse_button_state.right.Assign(current_status.value);
+ break;
+ case Settings::NativeMouseButton::Middle:
+ device_status.mouse_button_state.middle.Assign(current_status.value);
+ break;
+ case Settings::NativeMouseButton::Forward:
+ device_status.mouse_button_state.forward.Assign(current_status.value);
+ break;
+ case Settings::NativeMouseButton::Back:
+ device_status.mouse_button_state.back.Assign(current_status.value);
+ break;
+ }
+
+ TriggerOnChange(DeviceTriggerType::Mouse);
+}
+
+MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const {
+ return device_status.mouse_button_values;
+}
+
+KeyboardKey EmulatedDevices::GetKeyboard() const {
+ return device_status.keyboard_state;
+}
+
+KeyboardModifier EmulatedDevices::GetKeyboardModifier() const {
+ return device_status.keyboard_moddifier_state;
+}
+
+MouseButton EmulatedDevices::GetMouseButtons() const {
+ return device_status.mouse_button_state;
+}
+
+MousePosition EmulatedDevices::GetMousePosition() const {
+ return device_status.mouse_position_state;
+}
+
+void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
+ for (const std::pair<int, InterfaceUpdateCallback> poller_pair : callback_list) {
+ const InterfaceUpdateCallback& poller = poller_pair.second;
+ if (poller.on_change) {
+ poller.on_change(type);
+ }
+ }
+}
+
+int EmulatedDevices::SetCallback(InterfaceUpdateCallback update_callback) {
+ std::lock_guard lock{mutex};
+ callback_list.insert_or_assign(last_callback_key, update_callback);
+ return last_callback_key++;
+}
+
+void EmulatedDevices::DeleteCallback(int key) {
+ std::lock_guard lock{mutex};
+ if (!callback_list.contains(key)) {
+ LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
+ return;
+ }
+ callback_list.erase(key);
+}
+} // namespace Core::HID