diff --git a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp index 2c510fb394..9d354a4b8e 100644 --- a/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp +++ b/Source/Core/Core/HW/WiimoteEmu/EmuSubroutines.cpp @@ -602,9 +602,9 @@ ExtensionNumber Wiimote::GetActiveExtensionNumber() const return m_active_extension; } -bool Wiimote::IsMotionPlusAttached() const +ControllerEmu::SubscribableSettingValue& Wiimote::GetMotionPlusSetting() { - return m_is_motion_plus_attached; + return m_motion_plus_setting; } } // namespace WiimoteEmu diff --git a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h index e364ccdb6c..e50a6efbc9 100644 --- a/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h +++ b/Source/Core/Core/HW/WiimoteEmu/WiimoteEmu.h @@ -173,7 +173,7 @@ public: // Active extension number is exposed for TAS. ExtensionNumber GetActiveExtensionNumber() const; - bool IsMotionPlusAttached() const; + ControllerEmu::SubscribableSettingValue& GetMotionPlusSetting(); static Common::Vec3 OverrideVec3(const ControllerEmu::ControlGroup* control_group, Common::Vec3 vec, @@ -308,7 +308,7 @@ private: ControllerEmu::SettingValue m_sideways_setting; ControllerEmu::SettingValue m_upright_setting; ControllerEmu::SettingValue m_battery_setting; - ControllerEmu::SettingValue m_motion_plus_setting; + ControllerEmu::SubscribableSettingValue m_motion_plus_setting; ControllerEmu::SettingValue m_fov_x_setting; ControllerEmu::SettingValue m_fov_y_setting; diff --git a/Source/Core/DolphinQt/TAS/WiiTASInputWindow.cpp b/Source/Core/DolphinQt/TAS/WiiTASInputWindow.cpp index 08c3431fd8..5afe374608 100644 --- a/Source/Core/DolphinQt/TAS/WiiTASInputWindow.cpp +++ b/Source/Core/DolphinQt/TAS/WiiTASInputWindow.cpp @@ -347,32 +347,6 @@ WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : TASInputWindow( layout->addWidget(m_settings_box); setLayout(layout); - - if (Core::IsRunning(Core::System::GetInstance())) - { - m_active_extension = GetWiimote()->GetActiveExtensionNumber(); - m_is_motion_plus_attached = GetWiimote()->IsMotionPlusAttached(); - } - else - { - Common::IniFile ini; - ini.Load(File::GetUserPath(D_CONFIG_IDX) + "WiimoteNew.ini"); - const std::string section_name = "Wiimote" + std::to_string(num + 1); - - std::string extension; - ini.GetIfExists(section_name, "Extension", &extension); - - if (extension == "Nunchuk") - m_active_extension = WiimoteEmu::ExtensionNumber::NUNCHUK; - else if (extension == "Classic") - m_active_extension = WiimoteEmu::ExtensionNumber::CLASSIC; - else - m_active_extension = WiimoteEmu::ExtensionNumber::NONE; - - m_is_motion_plus_attached = true; - ini.GetIfExists(section_name, "Extension/Attach MotionPlus", &m_is_motion_plus_attached); - } - UpdateExt(); } WiimoteEmu::Wiimote* WiiTASInputWindow::GetWiimote() @@ -392,7 +366,71 @@ WiimoteEmu::Extension* WiiTASInputWindow::GetExtension() GetAttachments()->GetAttachmentList()[m_active_extension].get()); } -void WiiTASInputWindow::UpdateExt() +void WiiTASInputWindow::UpdateExtension(const int extension) +{ + const auto new_extension = static_cast(extension); + if (new_extension == m_active_extension) + return; + + m_active_extension = new_extension; + + UpdateControlVisibility(); + UpdateInputOverrideFunction(); +} + +void WiiTASInputWindow::UpdateMotionPlus(const bool attached) +{ + if (attached == m_is_motion_plus_attached) + return; + + m_is_motion_plus_attached = attached; + + UpdateControlVisibility(); +} + +void WiiTASInputWindow::LoadExtensionAndMotionPlus() +{ + WiimoteEmu::Wiimote* const wiimote = GetWiimote(); + + if (Core::IsRunning(Core::System::GetInstance())) + { + m_active_extension = wiimote->GetActiveExtensionNumber(); + m_is_motion_plus_attached = wiimote->GetMotionPlusSetting().GetValue(); + } + else + { + Common::IniFile ini; + ini.Load(File::GetUserPath(D_CONFIG_IDX) + "WiimoteNew.ini"); + const std::string section_name = "Wiimote" + std::to_string(m_num + 1); + + std::string extension; + ini.GetIfExists(section_name, "Extension", &extension); + + if (extension == "Nunchuk") + m_active_extension = WiimoteEmu::ExtensionNumber::NUNCHUK; + else if (extension == "Classic") + m_active_extension = WiimoteEmu::ExtensionNumber::CLASSIC; + else + m_active_extension = WiimoteEmu::ExtensionNumber::NONE; + + m_is_motion_plus_attached = true; + ini.GetIfExists(section_name, "Extension/Attach MotionPlus", &m_is_motion_plus_attached); + } + + UpdateControlVisibility(); + UpdateInputOverrideFunction(); + + m_motion_plus_callback_id = + wiimote->GetMotionPlusSetting().AddCallback([this](const bool attached) { + QueueOnObject(this, [this, attached] { UpdateMotionPlus(attached); }); + }); + m_attachment_callback_id = + GetAttachments()->GetAttachmentSetting().AddCallback([this](const int extension_index) { + QueueOnObject(this, [this, extension_index] { UpdateExtension(extension_index); }); + }); +} + +void WiiTASInputWindow::UpdateControlVisibility() { if (m_active_extension == WiimoteEmu::ExtensionNumber::NUNCHUK) { @@ -451,17 +489,36 @@ void WiiTASInputWindow::UpdateExt() m_nunchuk_buttons_box->hide(); m_classic_buttons_box->hide(); } + + // Without these calls, switching between attachments can result in the Stick/IRWidgets being + // surrounded by large amounts of empty space in one dimension. + adjustSize(); + resize(sizeHint()); } -void WiiTASInputWindow::hideEvent(QHideEvent* event) +void WiiTASInputWindow::hideEvent(QHideEvent* const event) { - GetWiimote()->ClearInputOverrideFunction(); + WiimoteEmu::Wiimote* const wiimote = GetWiimote(); + + wiimote->ClearInputOverrideFunction(); + wiimote->GetMotionPlusSetting().RemoveCallback(m_motion_plus_callback_id); + GetExtension()->ClearInputOverrideFunction(); + GetAttachments()->GetAttachmentSetting().RemoveCallback(m_attachment_callback_id); + + TASInputWindow::hideEvent(event); } -void WiiTASInputWindow::showEvent(QShowEvent* event) +void WiiTASInputWindow::showEvent(QShowEvent* const event) { - WiimoteEmu::Wiimote* wiimote = GetWiimote(); + LoadExtensionAndMotionPlus(); + + TASInputWindow::showEvent(event); +} + +void WiiTASInputWindow::UpdateInputOverrideFunction() +{ + WiimoteEmu::Wiimote* const wiimote = GetWiimote(); if (m_active_extension != WiimoteEmu::ExtensionNumber::CLASSIC) wiimote->SetInputOverrideFunction(m_wiimote_overrider.GetInputOverrideFunction()); diff --git a/Source/Core/DolphinQt/TAS/WiiTASInputWindow.h b/Source/Core/DolphinQt/TAS/WiiTASInputWindow.h index a77c38bb4c..a7a413e864 100644 --- a/Source/Core/DolphinQt/TAS/WiiTASInputWindow.h +++ b/Source/Core/DolphinQt/TAS/WiiTASInputWindow.h @@ -34,15 +34,22 @@ public: void hideEvent(QHideEvent* event) override; void showEvent(QShowEvent* event) override; + void UpdateExtension(int extension); + void UpdateMotionPlus(bool attached); + private: WiimoteEmu::Wiimote* GetWiimote(); ControllerEmu::Attachments* GetAttachments(); WiimoteEmu::Extension* GetExtension(); - void UpdateExt(); + void LoadExtensionAndMotionPlus(); + void UpdateControlVisibility(); + void UpdateInputOverrideFunction(); WiimoteEmu::ExtensionNumber m_active_extension; + int m_attachment_callback_id = -1; bool m_is_motion_plus_attached; + int m_motion_plus_callback_id = -1; int m_num; InputOverrider m_wiimote_overrider; diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Attachments.cpp b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Attachments.cpp index 42e1acff5b..b4bb1699c0 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Attachments.cpp +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Attachments.cpp @@ -35,6 +35,11 @@ NumericSetting& Attachments::GetSelectionSetting() return m_selection_setting; } +SubscribableSettingValue& Attachments::GetAttachmentSetting() +{ + return m_selection_value; +} + const std::vector>& Attachments::GetAttachmentList() const { return m_attachments; diff --git a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Attachments.h b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Attachments.h index 7553639396..887231196b 100644 --- a/Source/Core/InputCommon/ControllerEmu/ControlGroup/Attachments.h +++ b/Source/Core/InputCommon/ControllerEmu/ControlGroup/Attachments.h @@ -29,11 +29,12 @@ public: void SetSelectedAttachment(u32 val); NumericSetting& GetSelectionSetting(); + SubscribableSettingValue& GetAttachmentSetting(); const std::vector>& GetAttachmentList() const; private: - SettingValue m_selection_value; + SubscribableSettingValue m_selection_value; // This is here and not added to the list of numeric_settings because it's serialized differently, // by string (to be independent from the enum), and visualized differently in the UI. // For the rest, it's treated similarly to other numeric_settings in the group. diff --git a/Source/Core/InputCommon/ControllerEmu/Setting/NumericSetting.h b/Source/Core/InputCommon/ControllerEmu/Setting/NumericSetting.h index 863e211cae..dbe52576e4 100644 --- a/Source/Core/InputCommon/ControllerEmu/Setting/NumericSetting.h +++ b/Source/Core/InputCommon/ControllerEmu/Setting/NumericSetting.h @@ -3,8 +3,13 @@ #pragma once +#include #include +#include +#include #include +#include +#include #include "Common/CommonTypes.h" #include "Common/IniFile.h" @@ -173,7 +178,9 @@ class SettingValue friend class NumericSetting; public: - ValueType GetValue() const + virtual ~SettingValue() = default; + + virtual ValueType GetValue() const { // Only update dynamic values when the input gate is enabled. // Otherwise settings will all change to 0 when window focus is lost. @@ -184,9 +191,11 @@ public: return m_value; } + ValueType GetCachedValue() const { return m_value; } + bool IsSimpleValue() const { return m_input.GetExpression().empty(); } - void SetValue(ValueType value) + virtual void SetValue(const ValueType value) { m_value = value; @@ -202,4 +211,78 @@ private: mutable InputReference m_input; }; +template +class SubscribableSettingValue final : public SettingValue +{ +public: + using Base = SettingValue; + + ValueType GetValue() const override + { + const ValueType cached_value = GetCachedValue(); + if (IsSimpleValue()) + return cached_value; + + const ValueType updated_value = Base::GetValue(); + if (updated_value != cached_value) + TriggerCallbacks(); + + return updated_value; + } + + void SetValue(const ValueType value) override + { + if (value != GetCachedValue()) + { + Base::SetValue(value); + TriggerCallbacks(); + } + else if (!IsSimpleValue()) + { + // The setting has an expression with a cached value equal to the one currently being set. + // Don't trigger the callbacks (since the value didn't change), but clear the expression and + // make the setting a simple value instead. + Base::SetValue(value); + } + } + + ValueType GetCachedValue() const { return Base::GetCachedValue(); } + bool IsSimpleValue() const { return Base::IsSimpleValue(); } + + using SettingChangedCallback = std::function; + + int AddCallback(const SettingChangedCallback& callback) + { + std::lock_guard lock(m_mutex); + const int callback_id = m_next_callback_id; + ++m_next_callback_id; + m_callback_pairs.emplace_back(callback_id, callback); + + return callback_id; + } + + void RemoveCallback(const int id) + { + std::lock_guard lock(m_mutex); + const auto iter = std::ranges::find(m_callback_pairs, id, &IDCallbackPair::first); + if (iter != m_callback_pairs.end()) + m_callback_pairs.erase(iter); + } + +private: + void TriggerCallbacks() const + { + std::lock_guard lock(m_mutex); + const ValueType value = Base::GetValue(); + for (const auto& pair : m_callback_pairs) + pair.second(value); + } + + using IDCallbackPair = std::pair; + std::vector m_callback_pairs; + int m_next_callback_id = 0; + + mutable std::mutex m_mutex; +}; + } // namespace ControllerEmu