Merge pull request #13208 from Dentomologist/wiitasinputwindow_update_on_attachment_change

WiiTASInputWindow: Update controls when attachment changes
This commit is contained in:
Admiral H. Curtiss 2025-02-02 18:02:58 +01:00 committed by GitHub
commit 77056ba7b7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 192 additions and 39 deletions

View File

@ -602,9 +602,9 @@ ExtensionNumber Wiimote::GetActiveExtensionNumber() const
return m_active_extension; return m_active_extension;
} }
bool Wiimote::IsMotionPlusAttached() const ControllerEmu::SubscribableSettingValue<bool>& Wiimote::GetMotionPlusSetting()
{ {
return m_is_motion_plus_attached; return m_motion_plus_setting;
} }
} // namespace WiimoteEmu } // namespace WiimoteEmu

View File

@ -173,7 +173,7 @@ public:
// Active extension number is exposed for TAS. // Active extension number is exposed for TAS.
ExtensionNumber GetActiveExtensionNumber() const; ExtensionNumber GetActiveExtensionNumber() const;
bool IsMotionPlusAttached() const; ControllerEmu::SubscribableSettingValue<bool>& GetMotionPlusSetting();
static Common::Vec3 static Common::Vec3
OverrideVec3(const ControllerEmu::ControlGroup* control_group, Common::Vec3 vec, OverrideVec3(const ControllerEmu::ControlGroup* control_group, Common::Vec3 vec,
@ -308,7 +308,7 @@ private:
ControllerEmu::SettingValue<bool> m_sideways_setting; ControllerEmu::SettingValue<bool> m_sideways_setting;
ControllerEmu::SettingValue<bool> m_upright_setting; ControllerEmu::SettingValue<bool> m_upright_setting;
ControllerEmu::SettingValue<double> m_battery_setting; ControllerEmu::SettingValue<double> m_battery_setting;
ControllerEmu::SettingValue<bool> m_motion_plus_setting; ControllerEmu::SubscribableSettingValue<bool> m_motion_plus_setting;
ControllerEmu::SettingValue<double> m_fov_x_setting; ControllerEmu::SettingValue<double> m_fov_x_setting;
ControllerEmu::SettingValue<double> m_fov_y_setting; ControllerEmu::SettingValue<double> m_fov_y_setting;

View File

@ -347,32 +347,6 @@ WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : TASInputWindow(
layout->addWidget(m_settings_box); layout->addWidget(m_settings_box);
setLayout(layout); 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() WiimoteEmu::Wiimote* WiiTASInputWindow::GetWiimote()
@ -392,7 +366,71 @@ WiimoteEmu::Extension* WiiTASInputWindow::GetExtension()
GetAttachments()->GetAttachmentList()[m_active_extension].get()); GetAttachments()->GetAttachmentList()[m_active_extension].get());
} }
void WiiTASInputWindow::UpdateExt() void WiiTASInputWindow::UpdateExtension(const int extension)
{
const auto new_extension = static_cast<WiimoteEmu::ExtensionNumber>(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) if (m_active_extension == WiimoteEmu::ExtensionNumber::NUNCHUK)
{ {
@ -451,17 +489,36 @@ void WiiTASInputWindow::UpdateExt()
m_nunchuk_buttons_box->hide(); m_nunchuk_buttons_box->hide();
m_classic_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(); 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) if (m_active_extension != WiimoteEmu::ExtensionNumber::CLASSIC)
wiimote->SetInputOverrideFunction(m_wiimote_overrider.GetInputOverrideFunction()); wiimote->SetInputOverrideFunction(m_wiimote_overrider.GetInputOverrideFunction());

View File

@ -34,15 +34,22 @@ public:
void hideEvent(QHideEvent* event) override; void hideEvent(QHideEvent* event) override;
void showEvent(QShowEvent* event) override; void showEvent(QShowEvent* event) override;
void UpdateExtension(int extension);
void UpdateMotionPlus(bool attached);
private: private:
WiimoteEmu::Wiimote* GetWiimote(); WiimoteEmu::Wiimote* GetWiimote();
ControllerEmu::Attachments* GetAttachments(); ControllerEmu::Attachments* GetAttachments();
WiimoteEmu::Extension* GetExtension(); WiimoteEmu::Extension* GetExtension();
void UpdateExt(); void LoadExtensionAndMotionPlus();
void UpdateControlVisibility();
void UpdateInputOverrideFunction();
WiimoteEmu::ExtensionNumber m_active_extension; WiimoteEmu::ExtensionNumber m_active_extension;
int m_attachment_callback_id = -1;
bool m_is_motion_plus_attached; bool m_is_motion_plus_attached;
int m_motion_plus_callback_id = -1;
int m_num; int m_num;
InputOverrider m_wiimote_overrider; InputOverrider m_wiimote_overrider;

View File

@ -35,6 +35,11 @@ NumericSetting<int>& Attachments::GetSelectionSetting()
return m_selection_setting; return m_selection_setting;
} }
SubscribableSettingValue<int>& Attachments::GetAttachmentSetting()
{
return m_selection_value;
}
const std::vector<std::unique_ptr<EmulatedController>>& Attachments::GetAttachmentList() const const std::vector<std::unique_ptr<EmulatedController>>& Attachments::GetAttachmentList() const
{ {
return m_attachments; return m_attachments;

View File

@ -29,11 +29,12 @@ public:
void SetSelectedAttachment(u32 val); void SetSelectedAttachment(u32 val);
NumericSetting<int>& GetSelectionSetting(); NumericSetting<int>& GetSelectionSetting();
SubscribableSettingValue<int>& GetAttachmentSetting();
const std::vector<std::unique_ptr<EmulatedController>>& GetAttachmentList() const; const std::vector<std::unique_ptr<EmulatedController>>& GetAttachmentList() const;
private: private:
SettingValue<int> m_selection_value; SubscribableSettingValue<int> m_selection_value;
// This is here and not added to the list of numeric_settings because it's serialized differently, // 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. // 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. // For the rest, it's treated similarly to other numeric_settings in the group.

View File

@ -3,8 +3,13 @@
#pragma once #pragma once
#include <algorithm>
#include <atomic> #include <atomic>
#include <functional>
#include <mutex>
#include <string> #include <string>
#include <utility>
#include <vector>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/IniFile.h" #include "Common/IniFile.h"
@ -173,7 +178,9 @@ class SettingValue
friend class NumericSetting<T>; friend class NumericSetting<T>;
public: public:
ValueType GetValue() const virtual ~SettingValue() = default;
virtual ValueType GetValue() const
{ {
// Only update dynamic values when the input gate is enabled. // Only update dynamic values when the input gate is enabled.
// Otherwise settings will all change to 0 when window focus is lost. // Otherwise settings will all change to 0 when window focus is lost.
@ -184,9 +191,11 @@ public:
return m_value; return m_value;
} }
ValueType GetCachedValue() const { return m_value; }
bool IsSimpleValue() const { return m_input.GetExpression().empty(); } bool IsSimpleValue() const { return m_input.GetExpression().empty(); }
void SetValue(ValueType value) virtual void SetValue(const ValueType value)
{ {
m_value = value; m_value = value;
@ -202,4 +211,78 @@ private:
mutable InputReference m_input; mutable InputReference m_input;
}; };
template <typename ValueType>
class SubscribableSettingValue final : public SettingValue<ValueType>
{
public:
using Base = SettingValue<ValueType>;
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<void(ValueType)>;
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<int, SettingChangedCallback>;
std::vector<IDCallbackPair> m_callback_pairs;
int m_next_callback_id = 0;
mutable std::mutex m_mutex;
};
} // namespace ControllerEmu } // namespace ControllerEmu